0
# Utility Functions
1
2
Prettier provides extensive utility functions for text processing, AST navigation, position tracking, and comment manipulation. These utilities are essential for plugin development and advanced formatting scenarios.
3
4
## Importing Utilities
5
6
```javascript { .api }
7
import { util } from 'prettier';
8
9
// Access all utilities
10
const { getStringWidth, skipWhitespace, addLeadingComment } = util;
11
```
12
13
## String and Text Processing
14
15
### getStringWidth
16
```javascript { .api }
17
function getStringWidth(text: string): number
18
```
19
20
Get the display width of a string, accounting for Unicode characters, emoji, and ANSI escape codes.
21
22
**Example:**
23
```javascript { .api }
24
const width1 = util.getStringWidth('hello'); // 5
25
const width2 = util.getStringWidth('你好'); // 4 (CJK characters are width 2)
26
const width3 = util.getStringWidth('🎉'); // 2 (emoji width)
27
```
28
29
### getMaxContinuousCount
30
```javascript { .api }
31
function getMaxContinuousCount(text: string, searchString: string): number
32
```
33
34
Count the maximum continuous occurrences of a substring in text.
35
36
**Example:**
37
```javascript { .api }
38
const maxQuotes = util.getMaxContinuousCount('"""hello"""', '"'); // 3
39
const maxSpaces = util.getMaxContinuousCount('a b c', ' '); // 4
40
```
41
42
### getAlignmentSize
43
```javascript { .api }
44
function getAlignmentSize(text: string, tabWidth: number, startIndex?: number): number
45
```
46
47
Calculate the alignment size for a text string, considering tabs and spaces.
48
49
**Parameters:**
50
- `text` (string): Text to measure
51
- `tabWidth` (number): Width of tab characters
52
- `startIndex` (number, optional): Starting position (default: 0)
53
54
**Example:**
55
```javascript { .api }
56
const size1 = util.getAlignmentSize(' text', 2); // 2
57
const size2 = util.getAlignmentSize('\ttext', 4); // 4
58
const size3 = util.getAlignmentSize(' \t text', 4); // 5 (1 space + 4 tab - 1 for alignment)
59
```
60
61
### getIndentSize
62
```javascript { .api }
63
function getIndentSize(value: string, tabWidth: number): number
64
```
65
66
Calculate the indentation size of a string.
67
68
**Example:**
69
```javascript { .api }
70
const indent1 = util.getIndentSize(' code', 2); // 4
71
const indent2 = util.getIndentSize('\t\tcode', 4); // 8
72
const indent3 = util.getIndentSize(' \t code', 4); // 5
73
```
74
75
## Quote Handling
76
77
### makeString
78
```javascript { .api }
79
function makeString(
80
rawText: string,
81
enclosingQuote: "'" | '"',
82
unescapeUnnecessaryEscapes?: boolean
83
): string
84
```
85
86
Create a properly quoted and escaped string literal.
87
88
**Parameters:**
89
- `rawText` (string): Raw text content
90
- `enclosingQuote` (Quote): Quote character to use
91
- `unescapeUnnecessaryEscapes` (boolean, optional): Remove unnecessary escapes
92
93
**Example:**
94
```javascript { .api }
95
const quoted1 = util.makeString('hello "world"', "'"); // "'hello \"world\"'"
96
const quoted2 = util.makeString("it's nice", '"'); // '"it\'s nice"'
97
const quoted3 = util.makeString('simple', '"'); // '"simple"'
98
```
99
100
### getPreferredQuote
101
```javascript { .api }
102
function getPreferredQuote(
103
text: string,
104
preferredQuoteOrPreferSingleQuote: "'" | '"' | boolean
105
): "'" | '"'
106
```
107
108
Determine the preferred quote character for a string to minimize escaping.
109
110
**Parameters:**
111
- `text` (string): Text content to quote
112
- `preferredQuoteOrPreferSingleQuote` (Quote | boolean): Preference setting
113
114
**Example:**
115
```javascript { .api }
116
const quote1 = util.getPreferredQuote('hello "world"', "'"); // "'" (no escaping needed)
117
const quote2 = util.getPreferredQuote("it's nice", '"'); // '"' (no escaping needed)
118
const quote3 = util.getPreferredQuote('simple', true); // "'" (prefer single)
119
```
120
121
## Position and Navigation
122
123
### Skip Functions
124
125
All skip functions follow this pattern:
126
- Take `text` and `startIndex` parameters
127
- Return new index position or `false` if pattern not found
128
- Support optional `backwards` direction
129
130
```javascript { .api }
131
interface SkipOptions {
132
backwards?: boolean; // Skip in reverse direction
133
}
134
```
135
136
### skipWhitespace
137
```javascript { .api }
138
const skipWhitespace: (
139
text: string,
140
startIndex: number | false,
141
options?: SkipOptions
142
) => number | false
143
```
144
145
Skip whitespace characters (spaces, tabs, newlines).
146
147
**Example:**
148
```javascript { .api }
149
const text = 'code \n more';
150
const nextPos = util.skipWhitespace(text, 4); // 10 (after whitespace)
151
const prevPos = util.skipWhitespace(text, 10, { backwards: true }); // 4
152
```
153
154
### skipSpaces
155
```javascript { .api }
156
const skipSpaces: (
157
text: string,
158
startIndex: number | false,
159
options?: SkipOptions
160
) => number | false
161
```
162
163
Skip space characters only (not tabs or newlines).
164
165
**Example:**
166
```javascript { .api }
167
const text = 'code \tmore';
168
const nextPos = util.skipSpaces(text, 4); // 7 (stops at tab)
169
```
170
171
### skipNewline
172
```javascript { .api }
173
function skipNewline(
174
text: string,
175
startIndex: number | false,
176
options?: SkipOptions
177
): number | false
178
```
179
180
Skip a single newline character (LF, CRLF, or CR).
181
182
**Example:**
183
```javascript { .api }
184
const text = 'line1\nline2';
185
const nextPos = util.skipNewline(text, 5); // 6 (after newline)
186
```
187
188
### skipToLineEnd
189
```javascript { .api }
190
const skipToLineEnd: (
191
text: string,
192
startIndex: number | false,
193
options?: SkipOptions
194
) => number | false
195
```
196
197
Skip to the end of the current line.
198
199
### skipEverythingButNewLine
200
```javascript { .api }
201
const skipEverythingButNewLine: (
202
text: string,
203
startIndex: number | false,
204
options?: SkipOptions
205
) => number | false
206
```
207
208
Skip all characters except newlines.
209
210
### Generic skip Function
211
```javascript { .api }
212
function skip(characters: string | RegExp): (
213
text: string,
214
startIndex: number | false,
215
options?: SkipOptions
216
) => number | false
217
```
218
219
Create a custom skip function for specific characters or patterns.
220
221
**Example:**
222
```javascript { .api }
223
const skipDigits = util.skip(/\d/);
224
const skipPunctuation = util.skip('.,;:!?');
225
226
const text = '123abc';
227
const afterDigits = skipDigits(text, 0); // 3
228
229
const text2 = '...text';
230
const afterPuncts = skipPunctuation(text2, 0); // 3
231
```
232
233
## Comment Processing
234
235
### skipInlineComment
236
```javascript { .api }
237
function skipInlineComment(text: string, startIndex: number | false): number | false
238
```
239
240
Skip JavaScript-style inline comments (`// comment`).
241
242
**Example:**
243
```javascript { .api }
244
const text = 'code // comment\nmore';
245
const afterComment = util.skipInlineComment(text, 5); // 15 (after comment)
246
```
247
248
### skipTrailingComment
249
```javascript { .api }
250
function skipTrailingComment(text: string, startIndex: number | false): number | false
251
```
252
253
Skip JavaScript-style block comments (`/* comment */`).
254
255
**Example:**
256
```javascript { .api }
257
const text = 'code /* comment */ more';
258
const afterComment = util.skipTrailingComment(text, 5); // 18 (after comment)
259
```
260
261
## Character and Position Testing
262
263
### hasNewline
264
```javascript { .api }
265
function hasNewline(text: string, startIndex: number, options?: SkipOptions): boolean
266
```
267
268
Check if there's a newline at the specified position.
269
270
**Example:**
271
```javascript { .api }
272
const text = 'line1\nline2';
273
const hasNL = util.hasNewline(text, 5); // true
274
```
275
276
### hasNewlineInRange
277
```javascript { .api }
278
function hasNewlineInRange(text: string, startIndex: number, endIndex: number): boolean
279
```
280
281
Check if there's a newline within a character range.
282
283
**Example:**
284
```javascript { .api }
285
const text = 'no newline here';
286
const hasNL = util.hasNewlineInRange(text, 0, 10); // false
287
```
288
289
### hasSpaces
290
```javascript { .api }
291
function hasSpaces(text: string, startIndex: number, options?: SkipOptions): boolean
292
```
293
294
Check if there are spaces at the specified position.
295
296
### getNextNonSpaceNonCommentCharacterIndex
297
```javascript { .api }
298
function getNextNonSpaceNonCommentCharacterIndex(text: string, startIndex: number): number | false
299
```
300
301
Find the index of the next significant character, skipping whitespace and comments.
302
303
**Example:**
304
```javascript { .api }
305
const text = 'code /* comment */ more';
306
const nextChar = util.getNextNonSpaceNonCommentCharacterIndex(text, 4); // 20 ('m' in 'more')
307
```
308
309
### getNextNonSpaceNonCommentCharacter
310
```javascript { .api }
311
function getNextNonSpaceNonCommentCharacter(text: string, startIndex: number): string
312
```
313
314
Get the next significant character, skipping whitespace and comments.
315
316
**Example:**
317
```javascript { .api }
318
const text = 'code /* comment */ more';
319
const nextChar = util.getNextNonSpaceNonCommentCharacter(text, 4); // 'm'
320
```
321
322
### isNextLineEmpty
323
```javascript { .api }
324
function isNextLineEmpty(text: string, startIndex: number): boolean
325
```
326
327
Check if the next line after the given position is empty.
328
329
**Example:**
330
```javascript { .api }
331
const text = 'line1\n\nline3';
332
const isEmpty = util.isNextLineEmpty(text, 5); // true (line after line1 is empty)
333
```
334
335
### isPreviousLineEmpty
336
```javascript { .api }
337
function isPreviousLineEmpty(text: string, startIndex: number): boolean
338
```
339
340
Check if the previous line before the given position is empty.
341
342
**Example:**
343
```javascript { .api }
344
const text = 'line1\n\nline3';
345
const isEmpty = util.isPreviousLineEmpty(text, 8); // true (line before line3 is empty)
346
```
347
348
## Comment Manipulation
349
350
### addLeadingComment
351
```javascript { .api }
352
function addLeadingComment(node: any, comment: any): void
353
```
354
355
Add a comment before an AST node.
356
357
**Example:**
358
```javascript { .api }
359
const node = { type: 'Identifier', name: 'x' };
360
const comment = { type: 'Line', value: ' This is x' };
361
util.addLeadingComment(node, comment);
362
// node.leadingComments = [comment]
363
```
364
365
### addTrailingComment
366
```javascript { .api }
367
function addTrailingComment(node: any, comment: any): void
368
```
369
370
Add a comment after an AST node.
371
372
**Example:**
373
```javascript { .api }
374
const node = { type: 'Identifier', name: 'x' };
375
const comment = { type: 'Line', value: ' End of x' };
376
util.addTrailingComment(node, comment);
377
// node.trailingComments = [comment]
378
```
379
380
### addDanglingComment
381
```javascript { .api }
382
function addDanglingComment(node: any, comment: any, marker: any): void
383
```
384
385
Add a comment inside an AST node (not leading or trailing).
386
387
**Parameters:**
388
- `node` (any): AST node to add comment to
389
- `comment` (any): Comment object to add
390
- `marker` (any): Marker for comment placement
391
392
**Example:**
393
```javascript { .api }
394
const node = { type: 'ObjectExpression', properties: [] };
395
const comment = { type: 'Block', value: ' empty object ' };
396
util.addDanglingComment(node, comment, 'inside');
397
// node.comments = [comment]
398
```
399
400
## Usage Patterns
401
402
### Text Processing Pipeline
403
```javascript { .api }
404
function processText(text, startIndex) {
405
// Skip initial whitespace
406
let pos = util.skipWhitespace(text, startIndex);
407
if (pos === false) return null;
408
409
// Skip comments
410
pos = util.skipInlineComment(text, pos) || pos;
411
pos = util.skipTrailingComment(text, pos) || pos;
412
413
// Get next significant character
414
const nextChar = util.getNextNonSpaceNonCommentCharacter(text, pos);
415
416
return {
417
position: pos,
418
character: nextChar,
419
width: util.getStringWidth(text.slice(startIndex, pos))
420
};
421
}
422
```
423
424
### String Quoting Logic
425
```javascript { .api }
426
function smartQuote(text, preferSingle = false) {
427
const preferred = preferSingle ? "'" : '"';
428
const optimal = util.getPreferredQuote(text, preferred);
429
return util.makeString(text, optimal, true);
430
}
431
```
432
433
### Position Navigation
434
```javascript { .api }
435
function findStatementEnd(text, startIndex) {
436
let pos = startIndex;
437
438
while (pos < text.length) {
439
// Skip whitespace and comments
440
pos = util.skipWhitespace(text, pos) || pos;
441
pos = util.skipInlineComment(text, pos) || pos;
442
pos = util.skipTrailingComment(text, pos) || pos;
443
444
// Check for statement terminator
445
if (text[pos] === ';' || util.hasNewline(text, pos)) {
446
return pos;
447
}
448
449
pos++;
450
}
451
452
return pos;
453
}
454
```
455
456
### Comment Processing
457
```javascript { .api }
458
function attachComments(ast, comments) {
459
for (const comment of comments) {
460
const beforeNode = findNodeBefore(ast, comment.start);
461
const afterNode = findNodeAfter(ast, comment.end);
462
463
if (beforeNode && isOnSameLine(beforeNode.end, comment.start)) {
464
util.addTrailingComment(beforeNode, comment);
465
} else if (afterNode && isOnSameLine(comment.end, afterNode.start)) {
466
util.addLeadingComment(afterNode, comment);
467
} else {
468
// Dangling comment
469
const parentNode = findParentNode(ast, comment);
470
if (parentNode) {
471
util.addDanglingComment(parentNode, comment, 'inside');
472
}
473
}
474
}
475
}
476
```
477
478
### Indentation Calculation
479
```javascript { .api }
480
function calculateIndentation(line, tabWidth) {
481
const trimmed = line.trimStart();
482
const indentText = line.slice(0, line.length - trimmed.length);
483
484
return {
485
size: util.getIndentSize(indentText, tabWidth),
486
alignment: util.getAlignmentSize(indentText, tabWidth),
487
text: indentText,
488
content: trimmed
489
};
490
}
491
```
492
493
## Deprecated Functions
494
495
### isNextLineEmptyAfterIndex (Deprecated)
496
```javascript { .api }
497
function isNextLineEmptyAfterIndex(text: string, startIndex: number): boolean
498
```
499
500
**⚠️ Deprecated**: This function will be removed in v4. Use `isNextLineEmpty` instead.
501
502
Legacy function that checks if the next line after the given index is empty.
503
504
**Example:**
505
```javascript { .api }
506
// Deprecated usage
507
const isEmpty = util.isNextLineEmptyAfterIndex(text, 5);
508
509
// Modern equivalent
510
const isEmpty = util.isNextLineEmpty(text, 5);
511
```
512
513
## Performance Notes
514
515
- Skip functions return early with `false` if `startIndex` is `false`
516
- String width calculation caches results for repeated calls
517
- Comment utilities modify AST nodes in place
518
- Position functions work with zero-based indices
519
- All functions handle edge cases (empty strings, invalid positions)