links-notation 0.27.1 → 0.30.0

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/src/comments.js CHANGED
@@ -13,23 +13,35 @@
13
13
  */
14
14
 
15
15
  import { DelimitedReferences, QUOTES, readReference } from './quotes.js';
16
+ import { referenceLiteralEnd } from './ReferenceLiteral.js';
16
17
 
17
18
  /** The character that starts a comment. */
18
19
  export const COMMENT = '#';
19
20
 
20
- /** What a delimited reference may start after. */
21
- const BEFORE_REFERENCE = [' ', '\t', '\n', '\r', '(', ':'];
21
+ /**
22
+ * What ends a bare reference. A token starts after one of these, at the start
23
+ * of the document, and right after a delimited reference or a versioned
24
+ * literal: the places the grammar reads a reference from.
25
+ */
26
+ const SEPARATORS = [' ', '\t', '\n', '\r', '(', ':', ')'];
22
27
 
23
28
  /** What a comment may start after. */
24
29
  const BEFORE_COMMENT = [' ', '\t', '\n', '\r'];
25
30
 
26
31
  /**
27
32
  * The document with every comment blanked out.
33
+ *
34
+ * The document is read token by token, the way the grammar reads it, so a `#`
35
+ * inside a delimited reference that starts right after a group, another
36
+ * reference or a literal is content, as it is in one that starts after a
37
+ * space. References are read in the document as written, as the grammar
38
+ * reads them.
28
39
  * @param {string} document - The document to read
29
40
  * @returns {string} The document, of the same length, without its comments
30
41
  * @example
31
42
  * stripComments('a: b # why\n'); // 'a: b \n'
32
43
  * stripComments('"# kept"\n'); // '"# kept"\n'
44
+ * stripComments('(a)" #kept"\n'); // '(a)" #kept"\n'
33
45
  * stripComments('issue#1047\n'); // 'issue#1047\n'
34
46
  */
35
47
  export function stripComments(document) {
@@ -37,20 +49,13 @@ export function stripComments(document) {
37
49
  // parse error reports are both counted in.
38
50
  let blanked = null;
39
51
  let position = 0;
52
+ // Whether a token may start at `position`.
53
+ let tokenStart = true;
40
54
  const references = new DelimitedReferences(document);
41
55
 
42
56
  while (position < document.length) {
43
57
  const character = document[position];
44
58
 
45
- if (
46
- QUOTES.includes(character) &&
47
- follows(document, position, BEFORE_REFERENCE)
48
- ) {
49
- const end = references.endAt(position);
50
- position = end === null ? position + 1 : end;
51
- continue;
52
- }
53
-
54
59
  if (character === COMMENT && follows(document, position, BEFORE_COMMENT)) {
55
60
  blanked = blanked ?? document.split('');
56
61
  while (
@@ -64,12 +69,46 @@ export function stripComments(document) {
64
69
  continue;
65
70
  }
66
71
 
72
+ if (SEPARATORS.includes(character)) {
73
+ tokenStart = true;
74
+ position++;
75
+ continue;
76
+ }
77
+
78
+ if (tokenStart) {
79
+ const end = delimitedTokenEnd(document, position, references);
80
+ if (end !== null) {
81
+ position = end;
82
+ continue;
83
+ }
84
+ }
85
+
86
+ // Anything else is part of a bare reference, which runs to a separator.
87
+ tokenStart = false;
67
88
  position++;
68
89
  }
69
90
 
70
91
  return blanked === null ? document : blanked.join('');
71
92
  }
72
93
 
94
+ /**
95
+ * The position just past the delimited reference or versioned literal that
96
+ * starts the token at `start`, or `null` when the token there is a bare
97
+ * reference: a delimiter run that never closes is read as one, as the grammar
98
+ * reads it.
99
+ * @param {string} document - The document being read
100
+ * @param {number} start - Position where a token starts
101
+ * @param {DelimitedReferences} references - The references of the document
102
+ * @returns {number|null} The position just past the token
103
+ */
104
+ export function delimitedTokenEnd(document, start, references) {
105
+ const literal = referenceLiteralEnd(document, start);
106
+ if (literal !== -1) {
107
+ return literal;
108
+ }
109
+ return QUOTES.includes(document[start]) ? references.endAt(start) : null;
110
+ }
111
+
73
112
  /**
74
113
  * Whether the character before the given position is one the caller allows,
75
114
  * the start of the document counting as allowed.
package/src/grammar.pegjs CHANGED
@@ -16,6 +16,8 @@
16
16
  // until the stack overflows.
17
17
  let contextDepth = 0;
18
18
  const maxDepth = options.maxDepth ?? Infinity;
19
+ // The document as the caller wrote it, comments included.
20
+ const source = options.source ?? input;
19
21
  // Lines already found unreadable, keyed by where they start and whether they
20
22
  // are inside a parenthesised group. Reading a line depends on nothing else: a
21
23
  // group starts a fresh indentation context, and indentation only decides
@@ -115,8 +117,10 @@
115
117
  }
116
118
 
117
119
  // Delimited references of this document, each read once however many
118
- // alternatives ask for it (see quotes.js for how a reference is read)
119
- const delimitedReferences = new DelimitedReferences(input);
120
+ // alternatives ask for it (see quotes.js for how a reference is read). They
121
+ // are read in the document as written, as the comment reader reads them, so
122
+ // blanking a comment never changes how far a reference reaches (issue #377).
123
+ const delimitedReferences = new DelimitedReferences(source);
120
124
 
121
125
  // Universal procedural parser for N-quote strings (any N >= 1)
122
126
  // Parses from the given position in the input string
@@ -164,7 +168,7 @@ element = &{ return !unreadableLines.has(lineKey(offset())); }
164
168
  // succeed without consuming input.
165
169
  / &{ unreadableLines.add(lineKey(offset())); return false; } .
166
170
 
167
- referenceOrLink = l:multiLineAnyLink { return l; } / i:reference { return { id: i }; }
171
+ referenceOrLink = l:multiLineAnyLink { return l; } / r:reference { return { id: r.value, rawId: r.raw }; }
168
172
 
169
173
  // A line that starts with a parenthesised group reads that group once and then
170
174
  // branches on what follows it: the end of the line makes the group the whole
@@ -210,15 +214,24 @@ singleLineValueAndWhitespace = __ value:referenceOrLink { return value; }
210
214
 
211
215
  singleLineValues = list:singleLineValueAndWhitespace+ { return list; }
212
216
 
213
- singleLineLink = __ id:reference __ ":" v:singleLineValues { return { id: id, values: v }; }
217
+ singleLineLink = __ id:reference __ ":" v:singleLineValues { return { id: id.value, rawId: id.raw, values: v }; }
214
218
 
215
219
  singleLineValueLink = v:singleLineValues { return { values: v }; }
216
220
 
217
- indentedIdLink = id:reference __ ":" eol { return { id: id, values: [] }; }
221
+ indentedIdLink = id:reference __ ":" eol { return { id: id.value, rawId: id.raw, values: [] }; }
222
+
223
+ // A reference is read as its value together with the text it was written as,
224
+ // so a parsed link can say whether its ID was bare, quoted or a literal. The
225
+ // text is taken from the document as the caller wrote it, which keeps the same
226
+ // offsets as the document with its comments blanked out.
227
+ reference = value:referenceValue {
228
+ const { start, end } = range();
229
+ return { value: value, raw: source.slice(start, end) };
230
+ }
218
231
 
219
232
  // Reference can be quoted (with any number of quotes N >= 1) or simple unquoted
220
233
  // Universal approach: use procedural parsing for all quote types and counts
221
- reference = referenceLiteral / quotedReference / simpleReference
234
+ referenceValue = referenceLiteral / quotedReference / simpleReference
222
235
 
223
236
  // Versioned literals reserve ~<version>{<UTF-8 hex>} at a reference boundary.
224
237
  // A malformed reserved token must fail rather than fall back to a bare name.
package/src/index.js CHANGED
@@ -1,5 +1,10 @@
1
1
  export { Link, formatLinks } from './Link.js';
2
- export { escapeReference, unescapeReference } from './references.js';
2
+ export {
3
+ escapeReference,
4
+ unescapeReference,
5
+ referenceQuote,
6
+ } from './references.js';
7
+ export { CstKind, CstNode } from './Cst.js';
3
8
  export {
4
9
  parseIndentedDocument,
5
10
  formatIndentedDocument,