Skip to content

Commit 3ac7241

Browse files
Merge pull request #41 from raideer/v2-wip
V2 wip
2 parents fc7a1ae + 35e807e commit 3ac7241

File tree

66 files changed

+1995
-535
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+1995
-535
lines changed

.github/workflows/lint.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
name: lint
2+
on: [push, pull_request]
3+
4+
jobs:
5+
lint:
6+
runs-on: ubuntu-latest
7+
8+
steps:
9+
- uses: actions/checkout@v3
10+
- uses: actions/setup-node@v3
11+
with:
12+
node-version: '22'
13+
- run: npm install
14+
- run: npm run lint

.nvmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
22

package.json

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
{
22
"name": "magento-toolbox",
3-
"displayName": "magento-toolbox",
4-
"description": "",
5-
"version": "0.0.1",
3+
"displayName": "Magento Toolbox",
4+
"description": "Magento 2 code generation, inspection and utility tools",
5+
"publisher": "magebit",
6+
"version": "1.0.0",
67
"engines": {
78
"vscode": "^1.93.1"
89
},
@@ -16,6 +17,16 @@
1617
],
1718
"main": "./dist/extension.js",
1819
"contributes": {
20+
"configuration": {
21+
"title": "Magento Toolbox",
22+
"properties": {
23+
"magento-toolbox.magentoCliPath": {
24+
"type": "string",
25+
"default": "bin/magento",
26+
"description": "Path to Magento CLI tool. Relative to workspace root or absolute path."
27+
}
28+
}
29+
},
1930
"submenus": [
2031
{
2132
"id": "magento-toolbox.submenu",
@@ -38,13 +49,29 @@
3849
{
3950
"command": "magento-toolbox.generateContextPlugin",
4051
"title": "Magento Toolbox: Generate Plugin"
52+
},
53+
{
54+
"command": "magento-toolbox.copyMagentoPath",
55+
"title": "Magento Toolbox: Copy Magento Path"
56+
},
57+
{
58+
"command": "magento-toolbox.generateXmlCatalog",
59+
"title": "Magento Toolbox: Generate XML URN Catalog"
60+
},
61+
{
62+
"command": "magento-toolbox.generateObserver",
63+
"title": "Magento Toolbox: Generate Observer"
4164
}
4265
],
4366
"menus": {
4467
"commandPalette": [
4568
{
4669
"command": "magento-toolbox.generateContextPlugin",
4770
"when": "false"
71+
},
72+
{
73+
"command": "magento-toolbox.copyMagentoPath",
74+
"when": "false"
4875
}
4976
],
5077
"editor/context": [
@@ -64,6 +91,13 @@
6491
"command": "magento-toolbox.generateContextPlugin",
6592
"when": "magento-toolbox.canGeneratePlugin"
6693
}
94+
],
95+
"magento-toolbox.explorer-submenu": [
96+
{
97+
"command": "magento-toolbox.copyMagentoPath",
98+
"title": "Magento Toolbox: Copy Magento Path",
99+
"when": "resourceExtname in magento-toolbox.supportedMagentoPathExtensions && resourcePath =~ /view/"
100+
}
67101
]
68102
}
69103
},

resources/icons/observer.svg

Lines changed: 6 additions & 0 deletions
Loading
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import ObserverClassInfo from 'common/php/ObserverClassInfo';
2+
import PhpDocumentParser from 'common/php/PhpDocumentParser';
3+
import EventsIndexer from 'indexer/events/EventsIndexer';
4+
import IndexManager from 'indexer/IndexManager';
5+
import { NodeKind } from 'parser/php/Parser';
6+
import { Call } from 'php-parser';
7+
import Position from 'util/Position';
8+
import { CodeLens, CodeLensProvider, TextDocument } from 'vscode';
9+
10+
export default class ObserverCodelensProvider implements CodeLensProvider {
11+
public async provideCodeLenses(document: TextDocument): Promise<CodeLens[]> {
12+
const codelenses: CodeLens[] = [];
13+
14+
const phpFile = await PhpDocumentParser.parse(document);
15+
16+
const observerClassInfo = new ObserverClassInfo(phpFile);
17+
18+
const eventDispatchCalls = observerClassInfo.getEventDispatchCalls();
19+
20+
if (eventDispatchCalls.length === 0) {
21+
return codelenses;
22+
}
23+
24+
codelenses.push(...this.getEventDispatchCodeLenses(eventDispatchCalls));
25+
26+
return codelenses;
27+
}
28+
29+
private getEventDispatchCodeLenses(eventDispatchCalls: Call[]): CodeLens[] {
30+
const eventsIndexData = IndexManager.getIndexData(EventsIndexer.KEY);
31+
32+
if (!eventsIndexData) {
33+
return [];
34+
}
35+
36+
const codelenses: CodeLens[] = [];
37+
38+
for (const eventDispatchCall of eventDispatchCalls) {
39+
const args = eventDispatchCall.arguments;
40+
41+
if (args.length === 0) {
42+
continue;
43+
}
44+
45+
const firstArg = args[0];
46+
47+
if (!firstArg || firstArg.kind !== NodeKind.String || !firstArg.loc) {
48+
continue;
49+
}
50+
51+
const eventName = (firstArg as any).value;
52+
53+
const event = eventsIndexData.getEventByName(eventName);
54+
55+
if (!event) {
56+
continue;
57+
}
58+
59+
const range = Position.phpAstLocationToVsCodeRange(firstArg.loc);
60+
61+
const codelens = new CodeLens(range, {
62+
title: 'Create an Observer',
63+
command: 'magento-toolbox.generateObserver',
64+
arguments: [event.name],
65+
});
66+
67+
codelenses.push(codelens);
68+
}
69+
70+
return codelenses;
71+
}
72+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { Command } from 'command/Command';
2+
import IndexManager from 'indexer/IndexManager';
3+
import ModuleIndexer from 'indexer/module/ModuleIndexer';
4+
import { Uri, window, env } from 'vscode';
5+
6+
export default class CopyMagentoPathCommand extends Command {
7+
public static readonly TEMPLATE_EXTENSIONS = ['.phtml'];
8+
public static readonly WEB_EXTENSIONS = ['.css', '.js'];
9+
public static readonly IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg'];
10+
11+
private static readonly TEMPLATE_PATHS = [
12+
'view/frontend/templates/',
13+
'view/adminhtml/templates/',
14+
'view/base/templates/',
15+
'templates/',
16+
];
17+
18+
private static readonly WEB_PATHS = [
19+
'view/frontend/web/',
20+
'view/adminhtml/web/',
21+
'view/base/web/',
22+
'web/',
23+
];
24+
25+
constructor() {
26+
super('magento-toolbox.copyMagentoPath');
27+
}
28+
29+
public async execute(file: Uri): Promise<void> {
30+
if (!file) {
31+
return;
32+
}
33+
34+
const moduleIndexData = IndexManager.getIndexData(ModuleIndexer.KEY);
35+
36+
if (!moduleIndexData) {
37+
return;
38+
}
39+
40+
const module = moduleIndexData.getModuleByUri(file);
41+
42+
if (!module) {
43+
return;
44+
}
45+
46+
const paths = this.getPaths(file);
47+
48+
if (!paths) {
49+
return;
50+
}
51+
52+
const pathIndex = paths.findIndex(p => file.fsPath.lastIndexOf(p) !== -1);
53+
54+
if (pathIndex === -1) {
55+
return;
56+
}
57+
58+
const endIndex = file.fsPath.lastIndexOf(paths[pathIndex]);
59+
const offset = paths[pathIndex].length;
60+
const relativePath = file.fsPath.slice(offset + endIndex);
61+
62+
const magentoPath = `${module.name}::${relativePath}`;
63+
64+
await env.clipboard.writeText(magentoPath);
65+
window.showInformationMessage(`Copied: ${magentoPath}`);
66+
}
67+
68+
private getPaths(file: Uri): string[] | undefined {
69+
if (CopyMagentoPathCommand.TEMPLATE_EXTENSIONS.some(ext => file.fsPath.endsWith(ext))) {
70+
return CopyMagentoPathCommand.TEMPLATE_PATHS;
71+
}
72+
73+
if (
74+
CopyMagentoPathCommand.WEB_EXTENSIONS.some(ext => file.fsPath.endsWith(ext)) ||
75+
CopyMagentoPathCommand.IMAGE_EXTENSIONS.some(ext => file.fsPath.endsWith(ext))
76+
) {
77+
return CopyMagentoPathCommand.WEB_PATHS;
78+
}
79+
80+
return undefined;
81+
}
82+
}

src/command/GenerateContextPluginCommand.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import PluginClassGenerator from 'generator/plugin/PluginClassGenerator';
66
import PluginDiGenerator from 'generator/plugin/PluginDiGenerator';
77
import PhpParser from 'parser/php/Parser';
88
import { PhpMethod } from 'parser/php/PhpMethod';
9+
import Common from 'util/Common';
910
import * as vscode from 'vscode';
1011
import PluginContextWizard from 'wizard/PluginContextWizard';
1112

@@ -22,6 +23,13 @@ export default class GenerateContextPluginCommand extends Command {
2223
return;
2324
}
2425

26+
const workspaceFolder = Common.getActiveWorkspaceFolder();
27+
28+
if (!workspaceFolder) {
29+
vscode.window.showErrorMessage('No active workspace folder');
30+
return;
31+
}
32+
2533
const selection = editor?.selection;
2634
if (!editor || !selection) {
2735
vscode.window.showErrorMessage('No selection');
@@ -65,8 +73,6 @@ export default class GenerateContextPluginCommand extends Command {
6573
new PluginDiGenerator(data, classlike, method),
6674
]);
6775

68-
const workspaceFolder = vscode.workspace.workspaceFolders![0];
69-
7076
await manager.generate(workspaceFolder.uri);
7177
await manager.writeFiles();
7278
await manager.refreshIndex(workspaceFolder);

src/command/GenerateModuleCommand.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ import ModuleXmlGenerator from 'generator/module/ModuleXmlGenerator';
33
import ModuleRegistrationGenerator from 'generator/module/ModuleRegistrationGenerator';
44
import ModuleComposerGenerator from 'generator/module/ModuleComposerGenerator';
55
import ModuleLicenseGenerator from 'generator/module/ModuleLicenseGenerator';
6-
import ModuleWizard from 'wizard/ModuleWizard';
6+
import ModuleWizard, { ModuleWizardData, ModuleWizardComposerData } from 'wizard/ModuleWizard';
77
import FileGeneratorManager from 'generator/FileGeneratorManager';
8-
import { workspace } from 'vscode';
8+
import { window } from 'vscode';
9+
import Common from 'util/Common';
10+
import WizzardClosedError from 'webview/error/WizzardClosedError';
911

1012
export default class GenerateModuleCommand extends Command {
1113
constructor() {
@@ -15,7 +17,17 @@ export default class GenerateModuleCommand extends Command {
1517
public async execute(...args: any[]): Promise<void> {
1618
const moduleWizard = new ModuleWizard();
1719

18-
const data = await moduleWizard.show();
20+
let data: ModuleWizardData | ModuleWizardComposerData;
21+
22+
try {
23+
data = await moduleWizard.show();
24+
} catch (error) {
25+
if (error instanceof WizzardClosedError) {
26+
return;
27+
}
28+
29+
throw error;
30+
}
1931

2032
const manager = new FileGeneratorManager([
2133
new ModuleXmlGenerator(data),
@@ -30,7 +42,12 @@ export default class GenerateModuleCommand extends Command {
3042
manager.addGenerator(new ModuleLicenseGenerator(data));
3143
}
3244

33-
const workspaceFolder = workspace.workspaceFolders![0];
45+
const workspaceFolder = Common.getActiveWorkspaceFolder();
46+
47+
if (!workspaceFolder) {
48+
window.showErrorMessage('No active workspace folder');
49+
return;
50+
}
3451

3552
await manager.generate(workspaceFolder.uri);
3653
await manager.writeFiles();
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { Command } from 'command/Command';
2+
import ObserverWizard, { ObserverWizardData } from 'wizard/ObserverWizard';
3+
import WizzardClosedError from 'webview/error/WizzardClosedError';
4+
import FileGeneratorManager from 'generator/FileGeneratorManager';
5+
import Common from 'util/Common';
6+
import { window } from 'vscode';
7+
import ObserverClassGenerator from 'generator/observer/ObserverClassGenerator';
8+
import ObserverEventsGenerator from 'generator/observer/ObserverEventsGenerator';
9+
10+
export default class GenerateObserverCommand extends Command {
11+
constructor() {
12+
super('magento-toolbox.generateObserver');
13+
}
14+
15+
public async execute(eventName?: string): Promise<void> {
16+
const observerWizard = new ObserverWizard();
17+
18+
let data: ObserverWizardData;
19+
20+
try {
21+
data = await observerWizard.show(eventName);
22+
} catch (error) {
23+
if (error instanceof WizzardClosedError) {
24+
return;
25+
}
26+
27+
throw error;
28+
}
29+
30+
const manager = new FileGeneratorManager([
31+
new ObserverClassGenerator(data),
32+
new ObserverEventsGenerator(data),
33+
]);
34+
35+
const workspaceFolder = Common.getActiveWorkspaceFolder();
36+
37+
if (!workspaceFolder) {
38+
window.showErrorMessage('No active workspace folder');
39+
return;
40+
}
41+
42+
await manager.generate(workspaceFolder.uri);
43+
await manager.writeFiles();
44+
await manager.refreshIndex(workspaceFolder);
45+
manager.openFirstFile();
46+
}
47+
}

0 commit comments

Comments
 (0)