clang: lib/Format/SortJavaScriptImports.cpp Source File (original) (raw)

72

81

83

84

86

88

90

91

93

94

96

97

99};

108

109

110

111

112 return false;

113 }

114

115 if (LHS.URL.empty() != RHS.URL.empty())

116 return LHS.URL.empty() < RHS.URL.empty();

117 if (int Res = LHS.URL.compare_insensitive(RHS.URL))

118 return Res < 0;

119

120 if (LHS.Prefix.empty() != RHS.Prefix.empty())

121 return LHS.Prefix.empty() < RHS.Prefix.empty();

124 return false;

125}

131public:

134 FileContents(Env.getSourceManager().getBufferData(Env.getFileID())) {

135

136 invalidToken.Tok.startToken();

137 }

138

139 std::pair<tooling::Replacements, unsigned>

145

149 std::tie(References, FirstNonImportLine) =

150 parseModuleReferences(Keywords, AnnotatedLines);

151

152 if (References.empty())

154

155

156 SourceRange InsertionPoint = References[0].Range;

157 InsertionPoint.setEnd(References[References.size() - 1].Range.getEnd());

158

159 References = sortModuleReferences(References);

160

161 std::string ReferencesText;

162 for (unsigned I = 0, E = References.size(); I != E; ++I) {

164 appendReference(ReferencesText, Reference);

165 if (I + 1 < E) {

166

167 ReferencesText += "\n";

168

169

171 (Reference.IsExport != References[I + 1].IsExport ||

172 Reference.Category != References[I + 1].Category)) {

173 ReferencesText += "\n";

174 }

175 }

176 }

177 StringRef PreviousText = getSourceText(InsertionPoint);

178 if (ReferencesText == PreviousText)

180

181

182

183

184

185

186

187

188 unsigned PreviousSize = PreviousText.size();

189 while (ReferencesText.size() < PreviousSize)

190 ReferencesText += " ";

191

192

193 if (FirstNonImportLine && FirstNonImportLine->First->NewlinesBefore < 2 &&

194 !(FirstNonImportLine->First->is(tok::comment) &&

196 ReferencesText += "\n";

197 }

198

199 LLVM_DEBUG(llvm::dbgs() << "Replacing imports:\n"

200 << PreviousText << "\nwith:\n"

201 << ReferencesText << "\n");

204 ReferencesText));

205

206

207 if (Err) {

208 llvm::errs() << toString(std::move(Err)) << "\n";

209 assert(false);

210 }

211

213 }

214

215private:

218

220

221 StringRef FileContents;

222

223 void skipComments() { Current = skipComments(Current); }

224

226 while (Tok && Tok->is(tok::comment))

228 return Tok;

229 }

230

231 void nextToken() {

232 Current = Current->Next;

233 skipComments();

234 if (!Current || Current == LineEnd->Next) {

235

236

237 Current = &invalidToken;

238 }

239 }

240

241 StringRef getSourceText(SourceRange Range) {

242 return getSourceText(Range.getBegin(), Range.getEnd());

243 }

244

245 StringRef getSourceText(SourceLocation Begin, SourceLocation End) {

246 const SourceManager &SM = Env.getSourceManager();

247 return FileContents.substr(SM.getFileOffset(Begin),

248 SM.getFileOffset(End) - SM.getFileOffset(Begin));

249 }

250

251

252

253

254

255 SmallVector<JsModuleReference, 16>

256 sortModuleReferences(const SmallVector<JsModuleReference, 16> &References) {

257

258

259

260

261 const auto *Start = References.begin();

262 SmallVector<JsModuleReference, 16> ReferencesSorted;

263 while (Start != References.end()) {

264 while (Start != References.end() && Start->FormattingOff) {

265

266 ReferencesSorted.push_back(*Start);

267 ++Start;

268 }

269 SmallVector<JsModuleReference, 16> SortChunk;

270 while (Start != References.end() && !Start->FormattingOff) {

271

272 SortChunk.push_back(*Start);

273 ++Start;

274 }

275 stable_sort(SortChunk);

276 mergeModuleReferences(SortChunk);

277 llvm::append_range(ReferencesSorted, SortChunk);

278 }

279 return ReferencesSorted;

280 }

281

282

283

284

285

286

287

288

289

290

291 void mergeModuleReferences(SmallVector<JsModuleReference, 16> &References) {

292 if (References.empty())

293 return;

294 JsModuleReference *PreviousReference = References.begin();

295 auto *Reference = std::next(References.begin());

296 while (Reference != References.end()) {

297

298

299

300

301

304 Reference->IsExport != PreviousReference->IsExport ||

305 Reference->IsTypeOnly != PreviousReference->IsTypeOnly ||

306 !PreviousReference->Prefix.empty() || Reference->Prefix.empty() ||

307 !PreviousReference->DefaultImport.empty() ||

309 PreviousReference->URL != Reference->URL) {

312 continue;

313 }

314

315 PreviousReference->Symbols.append(Reference->Symbols);

316 PreviousReference->SymbolsMerged = true;

317

319 }

320 }

321

322

323 void appendReference(std::string &Buffer, JsModuleReference &Reference) {

325 Buffer +=

327 return;

328 }

329

330

331 SmallVector<JsImportedSymbol, 1> Symbols = Reference.Symbols;

332 stable_sort(Symbols,

333 [&](const JsImportedSymbol &LHS, const JsImportedSymbol &RHS) {

334 return LHS.Symbol.compare_insensitive(RHS.Symbol) < 0;

335 });

337

338 StringRef ReferenceStmt = getSourceText(Reference.Range);

339 Buffer += ReferenceStmt;

340 return;

341 }

342

343 Buffer += getSourceText(Reference.Range.getBegin(), Reference.SymbolsStart);

344

345 if (!Symbols.empty()) {

346 Buffer += getSourceText(Symbols.front().Range);

347 for (const JsImportedSymbol &Symbol : drop_begin(Symbols)) {

348 Buffer += ",";

349 Buffer += getSourceText(Symbol.Range);

350 }

351 }

352

353 Buffer += getSourceText(Reference.SymbolsEnd, Reference.Range.getEnd());

354 }

355

356

357

358

359 std::pair<SmallVector<JsModuleReference, 16>, AnnotatedLine *>

360 parseModuleReferences(const AdditionalKeywords &Keywords,

361 SmallVectorImpl<AnnotatedLine *> &AnnotatedLines) {

362 SmallVector<JsModuleReference, 16> References;

363 SourceLocation Start;

364 AnnotatedLine *FirstNonImportLine = nullptr;

365 bool AnyImportAffected = false;

366 bool FormattingOff = false;

367 for (auto *Line : AnnotatedLines) {

368 assert(Line->First);

369 Current = Line->First;

370 LineEnd = Line->Last;

371

372

373 while (Current && Current->is(tok::comment)) {

374 StringRef CommentText = Current->TokenText.trim();

376 FormattingOff = true;

378 FormattingOff = false;

379

380

381

382

383 if (!References.empty()) {

384 References.back().Range.setEnd(Current->Tok.getEndLoc());

385 Start = Current->Tok.getEndLoc().getLocWithOffset(1);

386 }

387 }

388

389 Current = Current->Next;

390 }

391 skipComments();

392 if (Start.isInvalid() || References.empty()) {

393

394

395

396 Start = Line->First->Tok.getLocation();

397 }

398 if (!Current) {

399

400 FirstNonImportLine = Line;

401 continue;

402 }

404 Reference.FormattingOff = FormattingOff;

406

408 if (!parseModuleReference(Keywords, Reference)) {

409 if (!FirstNonImportLine)

410 FirstNonImportLine = Line;

411 break;

412 }

413 FirstNonImportLine = nullptr;

414 AnyImportAffected = AnyImportAffected || Line->Affected;

415 Reference.Range.setEnd(LineEnd->Tok.getEndLoc());

416 LLVM_DEBUG({

417 llvm::dbgs() << "JsModuleReference: {"

418 << "formatting_off: " << Reference.FormattingOff

419 << ", is_export: " << Reference.IsExport

420 << ", cat: " << Reference.Category

422 << ", prefix: " << Reference.Prefix;

423 for (const JsImportedSymbol &Symbol : Reference.Symbols)

424 llvm::dbgs() << ", " << Symbol.Symbol << " as " << Symbol.Alias;

425 llvm::dbgs() << ", text: " << getSourceText(Reference.Range);

426 llvm::dbgs() << "}\n";

427 });

429 Start = SourceLocation();

430 }

431

432 if (!AnyImportAffected)

433 References.clear();

434 return std::make_pair(References, FirstNonImportLine);

435 }

436

437

438

439

440 bool parseModuleReference(const AdditionalKeywords &Keywords,

442 if (!Current || Current->isNoneOf(Keywords.kw_import, tok::kw_export))

443 return false;

444 Reference.IsExport = Current->is(tok::kw_export);

445

446 nextToken();

447 if (Current->isStringLiteral() && Reference.IsExport) {

448

451 Current->TokenText.substr(1, Current->TokenText.size() - 2);

452 return true;

453 }

454

455 if (!parseModuleBindings(Keywords, Reference))

456 return false;

457

458 if (Current->is(Keywords.kw_from)) {

459

460 nextToken();

461 if (!Current->isStringLiteral())

462 return false;

463

465 Current->TokenText.substr(1, Current->TokenText.size() - 2);

466 if (Reference.URL.starts_with("..")) {

469 } else if (Reference.URL.starts_with(".")) {

471 } else {

473 }

474 }

475 return true;

476 }

477

478 bool parseModuleBindings(const AdditionalKeywords &Keywords,

480 if (parseStarBinding(Keywords, Reference))

481 return true;

482 return parseNamedBindings(Keywords, Reference);

483 }

484

485 bool parseStarBinding(const AdditionalKeywords &Keywords,

487

488 if (Current->is(Keywords.kw_type) && Current->Next &&

489 Current->Next->is(tok::star)) {

491 nextToken();

492 }

493 if (Current->isNot(tok::star))

494 return false;

495 nextToken();

496 if (Current->isNot(Keywords.kw_as))

497 return false;

498 nextToken();

499 if (Current->isNot(tok::identifier))

500 return false;

501 Reference.Prefix = Current->TokenText;

502 nextToken();

503 return true;

504 }

505

506 bool parseNamedBindings(const AdditionalKeywords &Keywords,

508 if (Current->is(Keywords.kw_type) && Current->Next &&

509 Current->Next->isOneOf(tok::identifier, tok::l_brace)) {

511 nextToken();

512 }

513

514

515 if (Reference.IsExport && Current->is(tok::identifier)) {

516 Reference.DefaultImport = Current->TokenText;

517 nextToken();

518 if (Current->is(Keywords.kw_from))

519 return true;

520

521 if (Current->is(tok::equal)) {

523 nextToken();

524 while (Current->is(tok::identifier)) {

525 nextToken();

526 if (Current->is(tok::semi))

527 return true;

528 if (Current->isNot(tok::period))

529 return false;

530 nextToken();

531 }

532 }

533 if (Current->isNot(tok::comma))

534 return false;

535 nextToken();

536 }

537 if (Current->isNot(tok::l_brace))

538 return false;

539

540

541 Reference.SymbolsStart = Current->Tok.getEndLoc();

542 while (Current->isNot(tok::r_brace)) {

543 nextToken();

544 if (Current->is(tok::r_brace))

545 break;

546 auto IsIdentifier = [](const auto *Tok) {

547 return Tok->isOneOf(tok::identifier, tok::kw_default, tok::kw_template);

548 };

549 bool isTypeOnly = Current->is(Keywords.kw_type) && Current->Next &&

550 IsIdentifier(Current->Next);

551 if (!isTypeOnly && !IsIdentifier(Current))

552 return false;

553

554 JsImportedSymbol Symbol;

555

556 Symbol.Range.setBegin(

557 Current->getPreviousNonComment()->Next->WhitespaceRange.getBegin());

558 if (isTypeOnly)

559 nextToken();

560 Symbol.Symbol = Current->TokenText;

561 nextToken();

562

563 if (Current->is(Keywords.kw_as)) {

564 nextToken();

565 if (!IsIdentifier(Current))

566 return false;

567 Symbol.Alias = Current->TokenText;

568 nextToken();

569 }

570 Symbol.Range.setEnd(Current->Tok.getLocation());

571 Reference.Symbols.push_back(Symbol);

572

573 if (Current->isNoneOf(tok::r_brace, tok::comma))

574 return false;

575 }

576 Reference.SymbolsEnd = Current->Tok.getLocation();

577

578

579 if (Current->Previous->is(tok::comma))

580 Reference.SymbolsEnd = Current->Previous->Tok.getLocation();

581 nextToken();

582 return true;

583 }

584};

587 StringRef Code,

590

592 if (!Env)

593 return {};

595}