@limaformat/lima 0.7.1 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -12,6 +12,23 @@ carry their own.
12
12
 
13
13
  ## [Unreleased]
14
14
 
15
+ ## [0.8.1] — 2026-11-06
16
+
17
+ ### Changed
18
+
19
+ - `outline()` takes the positions of keys and comments from the parser's own readers as they read, instead of
20
+ matching the parsed tree to the lines afterwards. It grows linearly with its input (2,048 malformed lines in 15 ms
21
+ instead of 527); `parse` and `parseCore` keep their speed. For a few malformed inputs the result is now right where
22
+ the old matching took a key from another line.
23
+
24
+ ## [0.8.0] — 2026-11-05
25
+
26
+ ### Added
27
+
28
+ - `outline(input)` returns decoded key paths, key and separator spans, comments,
29
+ and parser diagnostics for editors. Positions use UTF-16 code units in the
30
+ BOM-free physical input, with tolerant recovery for unfinished documents.
31
+
15
32
  ## [0.7.1] — 2026-10-31
16
33
 
17
34
  ### Security
package/README.md CHANGED
@@ -35,8 +35,9 @@ tags:
35
35
  `parse` accepts the raw content **between** the frontmatter delimiters
36
36
  (`---`) — stripping them is the caller's job.
37
37
 
38
- Three functions are exported:
38
+ Four functions are exported:
39
39
 
40
+ - `outline(input)` — source positions for editor keys, comments and diagnostics.
40
41
  - `parseCore(input, options?)` — the base format only.
41
42
  - `parse(input, options?)` — the primary References <!-- facts:language.references_version -->2.1<!-- /facts --> parser; adds
42
43
  `${key}` document references and `$(key)` external partials.
@@ -49,6 +50,29 @@ parse(frontmatter, { partials: { author: 'Alice' } })
49
50
  parse(frontmatter, { mode: 'core' }) // same reference-unaware path as parseCore
50
51
  ```
51
52
 
53
+ ## Editor outlines
54
+
55
+ ```ts
56
+ import { outline } from '@limaformat/lima'
57
+
58
+ const { keys, comments, diagnostics } = outline('author: {name: Ada} # byline')
59
+ // keys: paths ['author'] and ['author', 'name'], with key and separator spans
60
+ ```
61
+
62
+ `outline` tolerates unfinished content and returns keys in source order, including
63
+ all duplicate occurrences. Sequence paths include string indices, such as
64
+ `['authors', '0', 'name']`. Quoted keys are decoded; their spans include the quotes.
65
+ Comments run from `#` through the physical line's end, excluding its terminator.
66
+ Block scalar content contributes no keys or comments.
67
+
68
+ Spans are half-open. Lines and columns start at 1; columns and offsets count
69
+ UTF-16 code units (`offsetUnit: 'utf16-code-unit'`). Positions refer to the raw
70
+ input with a leading BOM removed; CRLF and tabs keep their original widths.
71
+ Diagnostics contain strict `parse`'s first error, followed by non-strict duplicate
72
+ warnings. A resource limit produces diagnostics and no spans. Structure remains
73
+ reference-inert: keys copied by reference substitution have no source occurrence
74
+ and are omitted; only the diagnostic pass evaluates references, without partials.
75
+
52
76
  The **[guide](https://github.com/limaformat/lima/blob/main/docs/guide.md)**
53
77
  covers Core <!-- facts:language.core_version -->1.2<!-- /facts --> and References <!-- facts:language.references_version -->2.1<!-- /facts --> syntax, resource limits, strict mode, and the
54
78
  public API. The normative rules are defined in the
@@ -1,3 +1,4 @@
1
+ import type { SourcePositions } from './builder.js';
1
2
  /**
2
3
  * Allocation-free physical-line cursor over one top-level block extent.
3
4
  * Positions are UTF-16 code-unit offsets. Indentation is the run of spaces
@@ -20,6 +21,20 @@ export declare class BlockCursor {
20
21
  private nextStart;
21
22
  constructor(source: string, rangeStart: number, rangeEnd: number);
22
23
  next(): boolean;
24
+ /** Consume the physical body of a block scalar, leaving the first dedented line. */
25
+ scalarLines(keyIndent: number): string[];
26
+ protected scalarBody(keyIndent: number): string[];
23
27
  get empty(): boolean;
24
28
  get firstCode(): number;
25
29
  }
30
+ /** Reports comments only as lines are consumed, never inside a scalar body. */
31
+ export declare class SourceBlockCursor extends BlockCursor {
32
+ private readonly positions;
33
+ private commentFrom;
34
+ private scalar;
35
+ constructor(source: string, start: number, end: number, positions: SourcePositions);
36
+ /** Exclude comment markers before this normalized UTF-16 offset on the current line. */
37
+ excludeCommentsBefore(offset: number): void;
38
+ next(): boolean;
39
+ protected scalarBody(keyIndent: number): string[];
40
+ }
@@ -1,4 +1,5 @@
1
1
  import { isTrimWhitespace } from './chars.js';
2
+ import { commentStart } from './scalars.js';
2
3
  /**
3
4
  * Allocation-free physical-line cursor over one top-level block extent.
4
5
  * Positions are UTF-16 code-unit offsets. Indentation is the run of spaces
@@ -66,6 +67,21 @@ export class BlockCursor {
66
67
  this.nextStart = newline === -1 || newline >= this.rangeEnd ? this.rangeEnd : newline + 1;
67
68
  return true;
68
69
  }
70
+ /** Consume the physical body of a block scalar, leaving the first dedented line. */
71
+ scalarLines(keyIndent) {
72
+ this.next();
73
+ return this.scalarBody(keyIndent);
74
+ }
75
+ scalarBody(keyIndent) {
76
+ const lines = [];
77
+ while (this.valid) {
78
+ if (this.lineEnd - this.lineStart > this.asciiIndent && this.asciiIndent <= keyIndent)
79
+ break;
80
+ lines.push(this.source.slice(this.lineStart, this.lineEnd));
81
+ this.next();
82
+ }
83
+ return lines;
84
+ }
69
85
  get empty() {
70
86
  return this.contentStart === this.lineEnd;
71
87
  }
@@ -73,3 +89,33 @@ export class BlockCursor {
73
89
  return this.source.charCodeAt(this.contentStart);
74
90
  }
75
91
  }
92
+ /** Reports comments only as lines are consumed, never inside a scalar body. */
93
+ export class SourceBlockCursor extends BlockCursor {
94
+ positions;
95
+ commentFrom = 0;
96
+ scalar = false;
97
+ constructor(source, start, end, positions) {
98
+ super(source, start, end);
99
+ this.positions = positions;
100
+ }
101
+ /** Exclude comment markers before this normalized UTF-16 offset on the current line. */
102
+ excludeCommentsBefore(offset) {
103
+ this.commentFrom = offset;
104
+ }
105
+ next() {
106
+ if (this.valid && !this.scalar) {
107
+ const hash = commentStart(this.source.slice(this.commentFrom, this.lineEnd));
108
+ if (hash !== -1)
109
+ this.positions.comment(this.commentFrom + hash, this.lineEnd);
110
+ }
111
+ const valid = super.next();
112
+ this.commentFrom = this.contentStart;
113
+ return valid;
114
+ }
115
+ scalarBody(keyIndent) {
116
+ this.scalar = true;
117
+ const lines = super.scalarBody(keyIndent);
118
+ this.scalar = false;
119
+ return lines;
120
+ }
121
+ }
package/dist/block.d.ts CHANGED
@@ -9,10 +9,21 @@
9
9
  * never resolves a reference in the first place).
10
10
  */
11
11
  import { type ParseContext } from './normalize.js';
12
+ import { parseFlowOrScalarValue } from './flow.js';
12
13
  import type { ValueBuilder } from './builder.js';
14
+ import { BlockCursor } from './block-cursor.js';
15
+ /**
16
+ * An inline value, or a block scalar when `marker`: `|` alone (§6.1.5;
17
+ * `| # c` is the string `|`). Both leave the cursor on the next unread line.
18
+ */
19
+ export declare const parseInlineValue: <V, M>(raw: string, marker: boolean, keyIndent: number, keyLine: number, valueCol: number, rawUndecoded: string, cursor: BlockCursor, ctx: ParseContext, builder: ValueBuilder<V, M>, readValue: typeof parseFlowOrScalarValue<V, M>) => V;
13
20
  export declare const findKeySep: (s: string) => number;
21
+ declare const nativeBlockParser: <V, M>(cursor: BlockCursor, baseIndent: number, ctx: ParseContext, baseLine: number, builder: ValueBuilder<V, M>, compact?: boolean) => V | null;
22
+ /** Shared block grammar consuming one mutable physical-line cursor. */
23
+ export declare const parseCursorBlock: typeof nativeBlockParser;
14
24
  /** Complete block grammar over one UTF-16 source range. */
15
25
  export type BlockDepthRisk = {
16
26
  mayExceed: boolean;
17
27
  };
18
- export declare const parseBlockRange: <V, M>(source: string, start: number, end: number, ctx: ParseContext, baseLine: number, builder: ValueBuilder<V, M>, depthRisk: BlockDepthRisk) => V | null;
28
+ export declare const parseBlockRange: <V, M>(source: string, start: number, end: number, ctx: ParseContext, baseLine: number, builder: ValueBuilder<V, M>, depthRisk: BlockDepthRisk, cursor?: BlockCursor) => V | null;
29
+ export {};