or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

configuration.mdcore-formatting.mddocument-builders.mdfile-analysis.mdindex.mdplugin-development.mdutilities.md

utilities.mddocs/

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)