Skip to content

Commit 282fc4c

Browse files
ctf0ctf0
ctf0
authored and
ctf0
committed
043
1 parent 762fc99 commit 282fc4c

File tree

4 files changed

+74
-39
lines changed

4 files changed

+74
-39
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,9 @@ All notable changes to the "php-namespace-resolver" extension will be documented
7878
## 0.4.2
7979

8080
- fix not parsing of other types than class
81+
82+
## 0.4.3
83+
84+
- fix giving error when opening invalid workspace
85+
- fix not generating namespace
86+
- add new option `namespaceResolver.forceReplaceSimilarImports` to replace similar class import instead of keeping both (old & new)

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
"name": "php-namespace-resolver",
33
"displayName": "PHP Namespace Resolver",
44
"description": "Import and expand php namespaces",
5-
"version": "0.4.2",
5+
"version": "0.4.3",
66
"publisher": "ctf0",
77
"author": "ctf0",
88
"repository": "https://github.com/ctf0/PHP-Namespace-Resolver",
@@ -177,6 +177,11 @@
177177
"default": true,
178178
"description": "Expand class with leading namespace separator"
179179
},
180+
"namespaceResolver.forceReplaceSimilarImports": {
181+
"type": "boolean",
182+
"default": true,
183+
"markdownDescription": "when `false` both new & old will be kept, when `true` old will be replaced with new"
184+
},
180185
"namespaceResolver.php.command": {
181186
"type": "string",
182187
"default": "php",
@@ -249,7 +254,7 @@
249254
"@types/fs-extra": "^11.0.1",
250255
"@types/node": "^18.11.18",
251256
"@types/vscode": "^1.68.0",
252-
"esbuild": "^0.17.3",
257+
"esbuild": "^0.17.5",
253258
"typescript": "^4.9.4"
254259
},
255260
"dependencies": {

src/Parser.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ export function buildClassASTFromContent(content: string) {
1616
try {
1717
const AST = Parser.parseCode(content, '*.php');
1818

19-
const _tag: any = AST.tokens?.find((item: any) => item[0] == 'T_OPEN_TAG');
20-
const _declare: any = AST.children?.find((item: any) => item.kind == 'declare');
21-
const _namespace: any = AST.children?.find((item: any) => item.kind == 'namespace');
22-
const _use: any = (_namespace || AST).children?.filter((item: any) => item.kind == 'usegroup');
23-
const _class: any = (_namespace || AST).children?.find((item: any) => ['class', 'enum', 'interface', 'trait'].includes(item.kind));
24-
const _trait: any = _class?.body?.find((item: any) => item.kind == 'traituse')?.traits;
19+
const _tag: any = AST.tokens!.find((item: any) => item[0] == 'T_OPEN_TAG');
20+
const _declare: any = AST.children!.find((item: any) => item.kind == 'declare');
21+
const _namespace: any = AST.children!.find((item: any) => item.kind == 'namespace');
22+
const _use: any = (_namespace || AST).children!.filter((item: any) => item.kind == 'usegroup');
23+
const _class: any = (_namespace || AST).children!.find((item: any) => ['class', 'enum', 'interface', 'trait'].includes(item.kind));
24+
const _trait: any = _class!.body!.find((item: any) => item.kind == 'traituse')?.traits;
2525

2626
return {
2727
_openTag: {
@@ -37,7 +37,7 @@ export function buildClassASTFromContent(content: string) {
3737
},
3838
},
3939
_declare : _declare,
40-
_namespace : _namespace ? getNamespaceLoc(_namespace, _use[0] || _class) : null,
40+
_namespace : _namespace ? getNamespaceLoc(_namespace, _use![0] || _class) : null,
4141
_class : _class,
4242
_use : _use,
4343
_trait : _trait,

src/Resolver.ts

Lines changed: 54 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,11 @@ export default class Resolver {
1818
PKG_NAME = 'namespaceResolver';
1919

2020
public constructor() {
21-
this.CWD = vscode.workspace.workspaceFolders![0].uri.fsPath || '';
21+
try {
22+
this.CWD = vscode.workspace.workspaceFolders![0].uri.fsPath;
23+
} catch (error) {
24+
this.CWD = '';
25+
}
2226

2327
this.getPHPClassList();
2428
}
@@ -27,7 +31,7 @@ export default class Resolver {
2731
const className = this.resolving(selection);
2832

2933
if (className === undefined) {
30-
return this.showErrorMessage('No class is selected.');
34+
return this.showMessage('No class is selected.', true);
3135
}
3236

3337
let fqcn;
@@ -159,7 +163,7 @@ export default class Resolver {
159163
}
160164

161165
if (this.hasAliasConflict(useStatements, classBaseName)) {
162-
return this.showErrorMessage(`class : '${classBaseName}' is used as alias.`);
166+
return this.showMessage(`class : '${classBaseName}' is used as alias.`, true);
163167
}
164168

165169
if (replaceClassAfterImport) {
@@ -168,7 +172,7 @@ export default class Resolver {
168172

169173
return this.insert(fqcn, declarationLines);
170174
} catch (error) {
171-
return this.showErrorMessage(error.message);
175+
return this.showMessage(error.message, true);
172176
}
173177
}
174178

@@ -209,7 +213,7 @@ export default class Resolver {
209213
}
210214

211215
if (this.hasAliasConflict(useStatements, alias)) {
212-
await this.showErrorMessage(`alias : '${alias}' is already in use.`);
216+
await this.showMessage(`alias : '${alias}' is already in use.`, true);
213217

214218
return this.insertAsAlias(selection, fqcn, useStatements, declarationLines);
215219
}
@@ -219,22 +223,38 @@ export default class Resolver {
219223

220224
async insertNewUseStatement(selection, fqcn, useStatements, declarationLines) {
221225
if (useStatements.find((use) => use.text == fqcn)) {
222-
await this.showErrorMessage(`'${fqcn}' already exists`);
226+
return this.showMessage(`'${fqcn}' already exists`, true);
223227
}
224228

225229
const editor = this.EDITOR;
226-
const className = fqcn.replace(/\w+\\/g, '');
227-
const similarImport = useStatements.find((use) => use.text.endsWith(className) || fqcn.startsWith(use.text));
230+
const classBaseName = fqcn.match(/(\w+)/g).pop();
231+
const similarImport = useStatements.find((use) => use.text.endsWith(classBaseName) || fqcn.startsWith(use.text));
228232

229233
if (similarImport) {
230-
this.showErrorMessage(`use statement '${similarImport.text}' already exists`);
234+
if (this.config('forceReplaceSimilarImports')) {
235+
let useCall = `use ${fqcn}`;
236+
237+
if (similarImport.alias) {
238+
useCall = `${useCall} as ${similarImport.alias}`;
239+
}
240+
241+
return editor.edit((textEdit) => {
242+
textEdit.replace(
243+
// @ts-ignore
244+
editor.document.lineAt(similarImport.line).range,
245+
`${useCall};`,
246+
);
247+
}, { undoStopBefore: false, undoStopAfter: false });
248+
} else {
249+
return this.showMessage(`use statement '${similarImport.text}' already exists`, true);
250+
}
231251
}
232252

233253
await editor.edit((textEdit) => {
234254
textEdit.replace(
235255
// @ts-ignore
236256
editor.document.getWordRangeAtPosition(selection.active, regexWordWithNamespace),
237-
className,
257+
classBaseName,
238258
);
239259
}, { undoStopBefore: false, undoStopAfter: false });
240260

@@ -252,7 +272,7 @@ export default class Resolver {
252272
let className = resolving;
253273

254274
if (resolving === null) {
255-
return this.showErrorMessage('No class is selected.');
275+
return this.showMessage('No class is selected.', true);
256276
}
257277

258278
if (/\w+\\/.test(resolving)) {
@@ -301,7 +321,7 @@ export default class Resolver {
301321

302322
await this.showMessage('$(check) Imports are sorted.');
303323
} catch (error) {
304-
return this.showErrorMessage(error.message);
324+
return this.showMessage(error.message, true);
305325
}
306326
}
307327

@@ -319,7 +339,7 @@ export default class Resolver {
319339
);
320340

321341
if (parsedNamespaces.length === 0) {
322-
return this.showErrorMessage('$(circle-slash) The class is not found.');
342+
return this.showMessage('$(circle-slash) The class is not found.', true);
323343
}
324344

325345
return parsedNamespaces;
@@ -551,7 +571,7 @@ export default class Resolver {
551571
return document.getText(wordRange);
552572
}
553573

554-
async showMessage(message, error = false) {
574+
async showMessage(message, error = false): Promise<string | vscode.Disposable | undefined> {
555575
if (this.config('showMessageOnStatusBar')) {
556576
return vscode.window.setStatusBarMessage(message, 3000);
557577
}
@@ -563,10 +583,6 @@ export default class Resolver {
563583
: vscode.window.showInformationMessage(`PHP Namespace Resolver: ${message}`);
564584
}
565585

566-
showErrorMessage(message) {
567-
return this.showMessage(message, true);
568-
}
569-
570586
/**
571587
* @param uri: ?vscode.Uri
572588
*/
@@ -585,12 +601,19 @@ export default class Resolver {
585601
try {
586602
composerFile = await this.findComposerFileByUri(currentUri, returnDontInsert);
587603
psr4 = await this.getComposerFileData(composerFile, returnDontInsert);
588-
ns = await this.createNamespace(currentUri, psr4, returnDontInsert);
604+
ns = await this.createNamespace(
605+
currentUri,
606+
{
607+
psrData : psr4,
608+
composerFilePath : composerFile,
609+
},
610+
returnDontInsert,
611+
);
589612
} catch (error) {
590613
return undefined;
591614
}
592615

593-
const namespace = '\n' + 'namespace ' + ns + ';' + '\n\n';
616+
const namespace = '\n' + 'namespace ' + ns + ';' + '\n';
594617

595618
if (returnDontInsert) {
596619
return namespace;
@@ -619,7 +642,7 @@ export default class Resolver {
619642
);
620643
}
621644
} catch (error) {
622-
await this.showErrorMessage(error.message);
645+
await this.showMessage(error.message, true);
623646

624647
return undefined;
625648
}
@@ -630,7 +653,7 @@ export default class Resolver {
630653

631654
if (!composerFile) {
632655
if (!ignoreError) {
633-
await this.showErrorMessage('No composer.json file found');
656+
await this.showMessage('No composer.json file found', true);
634657
}
635658

636659
throw new Error();
@@ -647,7 +670,7 @@ export default class Resolver {
647670
psr4 = composerJson['autoload']['psr-4'];
648671
} catch (error) {
649672
if (!ignoreError) {
650-
await this.showErrorMessage('No psr-4 key in composer.json autoload object');
673+
await this.showMessage('No psr-4 key in composer.json autoload object', true);
651674
}
652675

653676
throw new Error();
@@ -664,12 +687,13 @@ export default class Resolver {
664687
return psr4;
665688
}
666689

667-
async createNamespace(currentUri: vscode.Uri, psr4, ignoreError = true): Promise<any> {
690+
async createNamespace(currentUri: vscode.Uri, composer: { psrData?: any; composerFilePath: string; }, ignoreError = true): Promise<any> {
668691
const currentFilePath = currentUri?.path;
669-
const workspaceFolder = vscode.workspace.getWorkspaceFolder(currentUri)?.uri.fsPath;
692+
const composerFileDir = this.getFileDirFromPath(composer.composerFilePath);
670693
const currentFileDir = this.getFileDirFromPath(currentFilePath);
694+
const psr4 = composer.psrData;
671695

672-
let currentRelativePath: any = currentFileDir.replace(`${workspaceFolder}/`, '');
696+
let currentRelativePath: any = currentFileDir.replace(`${composerFileDir}/`, '');
673697

674698
// this is a way to always match with psr-4 entries
675699
if (!currentRelativePath.endsWith('/')) {
@@ -680,7 +704,7 @@ export default class Resolver {
680704

681705
if (!namespaceBase) {
682706
if (!ignoreError) {
683-
await this.showErrorMessage('path parent directory not found under composer.json autoload object');
707+
await this.showMessage('path parent directory is not found under composer.json autoload object', true);
684708
}
685709

686710
throw new Error();
@@ -701,16 +725,16 @@ export default class Resolver {
701725

702726
if (!namespaceBase) {
703727
if (!ignoreError) {
704-
await this.showErrorMessage('no namespace found for current file parent directory');
728+
await this.showMessage('no namespace found for current file parent directory', true);
705729
}
706730

707731
throw new Error();
708732
}
709733

710734
let ns: any = null;
711-
const lower = namespaceBase.toLowerCase();
735+
const namespaceBaseLower = namespaceBase.toLowerCase();
712736

713-
if (!currentRelativePath || currentRelativePath == lower) { // dir already namespaced
737+
if (!currentRelativePath || currentRelativePath == namespaceBaseLower) { // dir already namespaced
714738
ns = namespaceBase;
715739
} else { // add parent dir/s to base namespace
716740
ns = `${namespaceBase}\\${currentRelativePath}`;

0 commit comments

Comments
 (0)