diff --git a/package-lock.json b/package-lock.json index 847fe66..e0e6b11 100644 --- a/package-lock.json +++ b/package-lock.json @@ -928,6 +928,29 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.1.tgz", + "integrity": "sha512-WMz71T1JS624nWj2n2fnYAuPovhv7EUhk69R6i9dsVyzxt5eM3bjwvgk9L+APE1TRscGysAVMANkB0jh0LQZrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1177,6 +1200,7 @@ "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", @@ -1429,6 +1453,7 @@ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", @@ -1808,6 +1833,7 @@ "integrity": "sha512-mNtXP9LTVBy14ZF3o7JG69gRPBK/2QWtQd0j0oH26HcY/foyJJau6pNUez7QrM5UHnSvwlQcJXKsk0I99B9pOA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.26.0", "@typescript-eslint/types": "8.26.0", @@ -2201,15 +2227,16 @@ ] }, "node_modules/@vscode/vsce/node_modules/glob": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.1.tgz", - "integrity": "sha512-zrQDm8XPnYEKawJScsnM0QzobJxlT/kHOOlRTio8IH/GrmxRE5fjllkzdaHclIuNjUQTJYH2xHNIGfdpJkDJUw==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^2.0.0" @@ -2225,13 +2252,13 @@ } }, "node_modules/@vscode/vsce/node_modules/glob/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "version": "10.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.2.tgz", + "integrity": "sha512-fu656aJ0n2kcXwsnwnv9g24tkU5uSmOlTjd6WyyaKm2Z+h1qmY6bAjrcaIxF/BslFqbZ8UBtbJi7KgQOZD2PTw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.1" }, "engines": { "node": "20 || >=22" @@ -2241,9 +2268,9 @@ } }, "node_modules/@vscode/vsce/node_modules/jackspeak": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.0.tgz", - "integrity": "sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { @@ -2313,6 +2340,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3526,9 +3554,9 @@ } }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.2.tgz", + "integrity": "sha512-vtcDfH3TOjP8UekytvnHH1o1P4FcUdt4eQ1Y+Abap1tk/OB2MWQvcwS2ClCd1zuIhc3JKOx6p3kod8Vfys3E+A==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -3975,6 +4003,7 @@ "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -4052,6 +4081,7 @@ "integrity": "sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "eslint-config-prettier": "bin/cli.js" }, @@ -4137,6 +4167,7 @@ "integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@rtsao/scc": "^1.1.0", "array-includes": "^3.1.9", @@ -4269,6 +4300,7 @@ "integrity": "sha512-6TyDmZ1HXoFQXnhCTUjVFULReoBPOAjpuiKELMkeP40yffI/1ZRO+d9ug/VC6fqISo2WkuIBk3cvuRPALaWlOQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "builtins": "^5.0.1", @@ -4382,6 +4414,7 @@ "integrity": "sha512-57Zzfw8G6+Gq7axm2Pdo3gW/Rx3h9Yywgn61uE/3elTCOePEHVrn2i5CdfBwA1BLK0Q0WqctICIUSqXZW/VprQ==", "dev": true, "license": "ISC", + "peer": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, @@ -5093,9 +5126,10 @@ "optional": true }, "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { @@ -6128,9 +6162,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { @@ -6212,25 +6246,25 @@ } }, "node_modules/jsonwebtoken/node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dev": true, "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jsonwebtoken/node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "dev": true, "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -6264,25 +6298,25 @@ } }, "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "dev": true, "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "dev": true, "license": "MIT", "dependencies": { - "jwa": "^2.0.0", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, @@ -7830,6 +7864,7 @@ "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -7905,9 +7940,9 @@ } }, "node_modules/qs": { - "version": "6.14.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", - "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -9421,6 +9456,7 @@ "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -9463,9 +9499,9 @@ "license": "MIT" }, "node_modules/undici": { - "version": "6.21.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.21.3.tgz", - "integrity": "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw==", + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.23.0.tgz", + "integrity": "sha512-VfQPToRA5FZs/qJxLIinmU59u0r7LXqoJkCzinq3ckNJp3vKEh7jTWN589YQ5+aoAC/TGRLyJLCPKcLQbM8r9g==", "dev": true, "license": "MIT", "engines": { diff --git a/src/app/features/FileRenameFeature.ts b/src/app/features/FileRenameFeature.ts index ff58203..660a8a6 100644 --- a/src/app/features/FileRenameFeature.ts +++ b/src/app/features/FileRenameFeature.ts @@ -1,6 +1,5 @@ import { MissingClassImporter } from '@app/services/MissingClassImporter'; import { NamespaceBatchUpdater } from '@app/services/NamespaceBatchUpdater'; -import { ImportRemover } from '@app/services/remove/ImportRemover'; import { ConfigKeys } from '@domain/workspace/ConfigurationLocator'; import { FeatureFlagManager } from '@domain/workspace/FeatureFlagManager'; import { inject, injectable } from 'tsyringe'; @@ -14,7 +13,6 @@ interface Props extends ReadonlyArray<{ @injectable() export class FileRenameFeature { constructor( - @inject(ImportRemover) private importRemover: ImportRemover, @inject(MissingClassImporter) private missingClassImporter: MissingClassImporter, @inject(NamespaceBatchUpdater) private namespaceBatchUpdater: NamespaceBatchUpdater, @inject(FeatureFlagManager) private featureFlagManager: FeatureFlagManager, @@ -35,8 +33,6 @@ export class FileRenameFeature { newUri, }); } - - await this.importRemover.execute({ uri: newUri }); } catch (error) { // eslint-disable-next-line no-undef console.error('Error processing file rename:', error); diff --git a/src/app/services/MissingClassImporter.ts b/src/app/services/MissingClassImporter.ts index 5592081..c806315 100644 --- a/src/app/services/MissingClassImporter.ts +++ b/src/app/services/MissingClassImporter.ts @@ -35,7 +35,7 @@ export class MissingClassImporter { } try { - const { document, text } = await this.textDocumentOpener.execute({ uri: newUri }); + const { document, text } = await this.textDocumentOpener.execute({ uri: newUri, useCache: true }); const imports = await this.useStatementCreator.multiple({ classesUsed: this.unusedImportDetector.execute({ diff --git a/src/app/services/NamespaceBatchUpdater.ts b/src/app/services/NamespaceBatchUpdater.ts index d87309a..bc25107 100644 --- a/src/app/services/NamespaceBatchUpdater.ts +++ b/src/app/services/NamespaceBatchUpdater.ts @@ -1,3 +1,4 @@ +import { DocumentCache } from '@app/services/cache/DocumentCache'; import { Namespace, NamespaceCreator } from '@domain/namespace/NamespaceCreator'; import { inject, injectable } from 'tsyringe'; import { Uri } from 'vscode'; @@ -14,6 +15,7 @@ interface Props { @injectable() export class NamespaceBatchUpdater { constructor( + @inject(DocumentCache) private documentCache: DocumentCache, @inject(MovedFileNamespaceUpdater) private movedFileNamespaceUpdater: MovedFileNamespaceUpdater, @inject(MultiFileReferenceUpdater) private multiFileReferenceUpdater: MultiFileReferenceUpdater, @inject(NamespaceCreator) private namespaceCreator: NamespaceCreator, @@ -21,33 +23,37 @@ export class NamespaceBatchUpdater { ) {} public async execute({ newUri, oldUri }: Props) { - const { namespace, fullNamespace } = await this.getNamespace(newUri); - - if (!namespace) { - return; - } - - const { namespace: old, fullNamespace: oldFullNamespace } = await this.getNamespace(oldUri); - - if (namespace === old && fullNamespace !== oldFullNamespace) { - this.classNameUpdater.execute({ newUri }); + try { + const { namespace, fullNamespace } = await this.getNamespace(newUri); + + if (!namespace) { + return; + } + + const { namespace: old, fullNamespace: oldFullNamespace } = await this.getNamespace(oldUri); + + if (namespace === old && fullNamespace !== oldFullNamespace) { + this.classNameUpdater.execute({ newUri }); + } + + const isUpdated = await this.movedFileNamespaceUpdater.execute({ + newNamespace: namespace, + newUri, + }); + + if (!isUpdated) { + return; + } + + await this.multiFileReferenceUpdater.execute({ + useOldNamespace: oldFullNamespace, + useNewNamespace: fullNamespace, + newUri, + oldUri, + }); + } finally { + this.documentCache.clear(); } - - const isUpdated = await this.movedFileNamespaceUpdater.execute({ - newNamespace: namespace, - newUri, - }); - - if (!isUpdated) { - return; - } - - await this.multiFileReferenceUpdater.execute({ - useOldNamespace: oldFullNamespace, - useNewNamespace: fullNamespace, - newUri, - oldUri, - }); } private async getNamespace(uri: Uri): Promise { diff --git a/src/app/services/TextDocumentOpener.ts b/src/app/services/TextDocumentOpener.ts index 19f0559..7115c5e 100644 --- a/src/app/services/TextDocumentOpener.ts +++ b/src/app/services/TextDocumentOpener.ts @@ -1,8 +1,10 @@ -import { injectable } from 'tsyringe'; +import { DocumentCache } from '@app/services/cache/DocumentCache'; +import { inject, injectable } from 'tsyringe'; import { TextDocument, Uri, workspace } from 'vscode'; interface Props { uri: Uri + useCache?: boolean } type OpenTextDocument = { @@ -12,7 +14,15 @@ type OpenTextDocument = { @injectable() export class TextDocumentOpener { - public async execute({ uri }: Props): Promise { + constructor( + @inject(DocumentCache) private documentCache: DocumentCache, + ) {} + + public async execute({ uri, useCache = false }: Props): Promise { + if (useCache) { + return this.documentCache.getOrOpen(uri); + } + const document = await workspace.openTextDocument(uri.fsPath); return { document, diff --git a/src/app/services/cache/DocumentCache.ts b/src/app/services/cache/DocumentCache.ts new file mode 100644 index 0000000..567bb9c --- /dev/null +++ b/src/app/services/cache/DocumentCache.ts @@ -0,0 +1,37 @@ +import { injectable } from 'tsyringe'; +import { TextDocument, Uri, workspace } from 'vscode'; + +interface CachedDocument { + document: TextDocument + text: string +} + +@injectable() +export class DocumentCache { + private cache: Map = new Map(); + + public async getOrOpen(uri: Uri): Promise { + const key = uri.fsPath; + + if (this.cache.has(key)) { + return this.cache.get(key)!; + } + + const document = await workspace.openTextDocument(uri.fsPath); + const cached: CachedDocument = { + document, + text: document.getText(), + }; + + this.cache.set(key, cached); + return cached; + } + + public clear(): void { + this.cache.clear(); + } + + public getSize(): number { + return this.cache.size; + } +} diff --git a/src/app/services/remove/ImportRemover.ts b/src/app/services/remove/ImportRemover.ts index 74b81b8..4745d78 100644 --- a/src/app/services/remove/ImportRemover.ts +++ b/src/app/services/remove/ImportRemover.ts @@ -45,7 +45,7 @@ export class ImportRemover { } try { - const { document } = await this.textDocumentOpener.execute({ uri }); + const { document } = await this.textDocumentOpener.execute({ uri, useCache: true }); await this.removeImports({ document, fileNames, @@ -58,7 +58,7 @@ export class ImportRemover { await Promise.all(otherFiles.map(async (file) => { try { - const { document } = await this.textDocumentOpener.execute({ uri: file }); + const { document } = await this.textDocumentOpener.execute({ uri: file, useCache: true }); await this.removeImports({ document, fileNames: [className], diff --git a/src/app/services/update/ClassNameUpdater.ts b/src/app/services/update/ClassNameUpdater.ts index 0fc6320..122ee35 100644 --- a/src/app/services/update/ClassNameUpdater.ts +++ b/src/app/services/update/ClassNameUpdater.ts @@ -18,7 +18,7 @@ export class ClassNameUpdater { ) {} public async execute({ newUri }: Props): Promise { - const { document, text } = await this.textDocumentOpener.execute({ uri: newUri }); + const { document, text } = await this.textDocumentOpener.execute({ uri: newUri, useCache: true }); const match = PHP_CLASS_DECLARATION_REGEX.exec(text); if (!match) { diff --git a/src/app/services/update/MovedFileNamespaceUpdater.ts b/src/app/services/update/MovedFileNamespaceUpdater.ts index 0b22ca2..026e1d8 100644 --- a/src/app/services/update/MovedFileNamespaceUpdater.ts +++ b/src/app/services/update/MovedFileNamespaceUpdater.ts @@ -14,7 +14,7 @@ export class MovedFileNamespaceUpdater { ) {} public async execute({ newNamespace, newUri }: Props) { - const { document, text } = await this.textDocumentOpener.execute({ uri: newUri }); + const { document, text } = await this.textDocumentOpener.execute({ uri: newUri, useCache: true }); const namespaceRegex = /^\s*namespace\s+[\w\\]+;/m; const match = text.match(namespaceRegex); diff --git a/src/app/services/update/MultiFileReferenceUpdater.ts b/src/app/services/update/MultiFileReferenceUpdater.ts index 495b9f5..c10f504 100644 --- a/src/app/services/update/MultiFileReferenceUpdater.ts +++ b/src/app/services/update/MultiFileReferenceUpdater.ts @@ -1,12 +1,11 @@ import { ImportRemover } from '@app/services/remove/ImportRemover'; import { TextDocumentOpener } from '@app/services/TextDocumentOpener'; -import { WorkspaceFileFinder } from '@app/services/workspace/WorkspaceFileFinder'; import { UseStatementCreator } from '@domain/namespace/UseStatementCreator'; import { UseStatementInjector } from '@domain/namespace/UseStatementInjector'; import { UseStatementLocator } from '@domain/namespace/UseStatementLocator'; import { WorkspacePathResolver } from '@domain/workspace/WorkspacePathResolver'; import { inject, injectable } from 'tsyringe'; -import { Uri, workspace, WorkspaceEdit } from 'vscode'; +import { RelativePattern, Uri, workspace, WorkspaceEdit } from 'vscode'; interface Props { useOldNamespace: string @@ -21,7 +20,6 @@ export class MultiFileReferenceUpdater { @inject(WorkspacePathResolver) private workspacePathResolver: WorkspacePathResolver, @inject(ImportRemover) private importRemover: ImportRemover, @inject(UseStatementCreator) private useStatementCreator: UseStatementCreator, - @inject(WorkspaceFileFinder) private workspaceFileFinder: WorkspaceFileFinder, @inject(TextDocumentOpener) private textDocumentOpener: TextDocumentOpener, @inject(UseStatementLocator) private useStatementLocator: UseStatementLocator, @inject(UseStatementInjector) private useStatementInjector: UseStatementInjector, @@ -38,62 +36,64 @@ export class MultiFileReferenceUpdater { const useImport = this.useStatementCreator.single({ fullNamespace: useNewNamespace }); - const ignoreFile = newUri.fsPath; + await this.updateNamespaceReferences(useOldNamespace, useNewNamespace, newUri); + await this.addUseStatementsToSameDirectory(directoryPath, newUri, useImport, className); + await this.importRemover.execute({ uri: newUri }); + } - const files = await this.workspaceFileFinder.execute(); + private async updateNamespaceReferences( + useOldNamespace: string, + useNewNamespace: string, + newUri: Uri, + ): Promise { + const filesWithOldNamespace = await workspace.findFiles('**/*.php', '**/vendor/**'); - const filesToProcess = files.filter(file => ignoreFile !== file.fsPath); + await Promise.all(filesWithOldNamespace.map(async (file) => { + if (file.fsPath === newUri.fsPath) { + return; + } - await Promise.all(filesToProcess.map(async (file) => { try { - const fileStream = workspace.fs; - - await fileStream.stat(file); - - const fileContent = await fileStream.readFile(file); + const fileContent = await workspace.fs.readFile(file); let text = Buffer.from(fileContent).toString(); if (!text.includes(useOldNamespace)) { - await this.updateInFile( - file, - directoryPath, - useImport, - className, - ); - return; } text = text.replace(useOldNamespace, useNewNamespace); - await fileStream.writeFile(file, Buffer.from(text)); - - await this.updateInFile( - file, - directoryPath, - useImport, - className, - ); + await workspace.fs.writeFile(file, Buffer.from(text)); } catch (_) { return; } })); + } - await this.importRemover.execute({ uri: newUri }); + private async addUseStatementsToSameDirectory( + directoryPath: string, + newUri: Uri, + useImport: string, + className: string, + ): Promise { + const pattern = new RelativePattern(Uri.file(directoryPath), '*.php'); + const sameDirectoryFiles = await workspace.findFiles(pattern); + + await Promise.all(sameDirectoryFiles.map(async (file) => { + if (file.fsPath === newUri.fsPath) { + return; + } + + await this.addUseStatementToFile(file, useImport, className); + })); } - private async updateInFile( + private async addUseStatementToFile( file: Uri, - oldDirectoryPath: string, useImport: string, className: string, ): Promise { - const currentDir = this.workspacePathResolver.extractDirectoryFromPath(file.fsPath); - if (oldDirectoryPath !== currentDir) { - return; - } - try { - const { document, text } = await this.textDocumentOpener.execute({ uri: file }); + const { document, text } = await this.textDocumentOpener.execute({ uri: file, useCache: true }); if (!text.includes(className)) { return; diff --git a/src/extension.ts b/src/extension.ts index c04bd62..3a20d4e 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -5,13 +5,17 @@ import { RenameHandler } from '@app/commands/RenameHandler'; import { ConfigKeys, ConfigurationLocator } from '@domain/workspace/ConfigurationLocator'; import { FeatureFlagManager } from '@domain/workspace/FeatureFlagManager'; import { COMPOSER_FILE, WORKSPACE_ROOT_PATH } from '@infra/utils/constants'; -import * as fs from 'fs'; +import { promises as fs } from 'fs'; import { container } from 'tsyringe'; import { commands, FileRenameEvent, window, workspace } from 'vscode'; -export function activate() { - const files = fs.readdirSync(WORKSPACE_ROOT_PATH); - if (!files.includes(COMPOSER_FILE)) { +export async function activate() { + try { + const files = await fs.readdir(WORKSPACE_ROOT_PATH); + if (!files.includes(COMPOSER_FILE)) { + return; + } + } catch { return; }