Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: lint
on: [push, pull_request]

jobs:
lint:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '22'
- run: npm install
- run: npm run lint
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
22
40 changes: 37 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
{
"name": "magento-toolbox",
"displayName": "magento-toolbox",
"description": "",
"version": "0.0.1",
"displayName": "Magento Toolbox",
"description": "Magento 2 code generation, inspection and utility tools",
"publisher": "magebit",
"version": "1.0.0",
"engines": {
"vscode": "^1.93.1"
},
Expand All @@ -16,6 +17,16 @@
],
"main": "./dist/extension.js",
"contributes": {
"configuration": {
"title": "Magento Toolbox",
"properties": {
"magento-toolbox.magentoCliPath": {
"type": "string",
"default": "bin/magento",
"description": "Path to Magento CLI tool. Relative to workspace root or absolute path."
}
}
},
"submenus": [
{
"id": "magento-toolbox.submenu",
Expand All @@ -38,13 +49,29 @@
{
"command": "magento-toolbox.generateContextPlugin",
"title": "Magento Toolbox: Generate Plugin"
},
{
"command": "magento-toolbox.copyMagentoPath",
"title": "Magento Toolbox: Copy Magento Path"
},
{
"command": "magento-toolbox.generateXmlCatalog",
"title": "Magento Toolbox: Generate XML URN Catalog"
},
{
"command": "magento-toolbox.generateObserver",
"title": "Magento Toolbox: Generate Observer"
}
],
"menus": {
"commandPalette": [
{
"command": "magento-toolbox.generateContextPlugin",
"when": "false"
},
{
"command": "magento-toolbox.copyMagentoPath",
"when": "false"
}
],
"editor/context": [
Expand All @@ -64,6 +91,13 @@
"command": "magento-toolbox.generateContextPlugin",
"when": "magento-toolbox.canGeneratePlugin"
}
],
"magento-toolbox.explorer-submenu": [
{
"command": "magento-toolbox.copyMagentoPath",
"title": "Magento Toolbox: Copy Magento Path",
"when": "resourceExtname in magento-toolbox.supportedMagentoPathExtensions && resourcePath =~ /view/"
}
]
}
},
Expand Down
6 changes: 6 additions & 0 deletions resources/icons/observer.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions src/codelens/ObserverCodelensProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import ObserverClassInfo from 'common/php/ObserverClassInfo';
import PhpDocumentParser from 'common/php/PhpDocumentParser';
import EventsIndexer from 'indexer/events/EventsIndexer';
import IndexManager from 'indexer/IndexManager';
import { NodeKind } from 'parser/php/Parser';
import { Call } from 'php-parser';
import Position from 'util/Position';
import { CodeLens, CodeLensProvider, TextDocument } from 'vscode';

export default class ObserverCodelensProvider implements CodeLensProvider {
public async provideCodeLenses(document: TextDocument): Promise<CodeLens[]> {
const codelenses: CodeLens[] = [];

const phpFile = await PhpDocumentParser.parse(document);

const observerClassInfo = new ObserverClassInfo(phpFile);

const eventDispatchCalls = observerClassInfo.getEventDispatchCalls();

if (eventDispatchCalls.length === 0) {
return codelenses;
}

codelenses.push(...this.getEventDispatchCodeLenses(eventDispatchCalls));

return codelenses;
}

private getEventDispatchCodeLenses(eventDispatchCalls: Call[]): CodeLens[] {
const eventsIndexData = IndexManager.getIndexData(EventsIndexer.KEY);

if (!eventsIndexData) {
return [];
}

const codelenses: CodeLens[] = [];

for (const eventDispatchCall of eventDispatchCalls) {
const args = eventDispatchCall.arguments;

if (args.length === 0) {
continue;
}

const firstArg = args[0];

if (!firstArg || firstArg.kind !== NodeKind.String || !firstArg.loc) {
continue;
}

const eventName = (firstArg as any).value;

const event = eventsIndexData.getEventByName(eventName);

if (!event) {
continue;
}

const range = Position.phpAstLocationToVsCodeRange(firstArg.loc);

const codelens = new CodeLens(range, {
title: 'Create an Observer',
command: 'magento-toolbox.generateObserver',
arguments: [event.name],
});

codelenses.push(codelens);
}

return codelenses;
}
}
82 changes: 82 additions & 0 deletions src/command/CopyMagentoPathCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { Command } from 'command/Command';
import IndexManager from 'indexer/IndexManager';
import ModuleIndexer from 'indexer/module/ModuleIndexer';
import { Uri, window, env } from 'vscode';

export default class CopyMagentoPathCommand extends Command {
public static readonly TEMPLATE_EXTENSIONS = ['.phtml'];
public static readonly WEB_EXTENSIONS = ['.css', '.js'];
public static readonly IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg'];

private static readonly TEMPLATE_PATHS = [
'view/frontend/templates/',
'view/adminhtml/templates/',
'view/base/templates/',
'templates/',
];

private static readonly WEB_PATHS = [
'view/frontend/web/',
'view/adminhtml/web/',
'view/base/web/',
'web/',
];

constructor() {
super('magento-toolbox.copyMagentoPath');
}

public async execute(file: Uri): Promise<void> {
if (!file) {
return;
}

const moduleIndexData = IndexManager.getIndexData(ModuleIndexer.KEY);

if (!moduleIndexData) {
return;
}

const module = moduleIndexData.getModuleByUri(file);

if (!module) {
return;
}

const paths = this.getPaths(file);

if (!paths) {
return;
}

const pathIndex = paths.findIndex(p => file.fsPath.lastIndexOf(p) !== -1);

if (pathIndex === -1) {
return;
}

const endIndex = file.fsPath.lastIndexOf(paths[pathIndex]);
const offset = paths[pathIndex].length;
const relativePath = file.fsPath.slice(offset + endIndex);

const magentoPath = `${module.name}::${relativePath}`;

await env.clipboard.writeText(magentoPath);
window.showInformationMessage(`Copied: ${magentoPath}`);
}

private getPaths(file: Uri): string[] | undefined {
if (CopyMagentoPathCommand.TEMPLATE_EXTENSIONS.some(ext => file.fsPath.endsWith(ext))) {
return CopyMagentoPathCommand.TEMPLATE_PATHS;
}

if (
CopyMagentoPathCommand.WEB_EXTENSIONS.some(ext => file.fsPath.endsWith(ext)) ||
CopyMagentoPathCommand.IMAGE_EXTENSIONS.some(ext => file.fsPath.endsWith(ext))
) {
return CopyMagentoPathCommand.WEB_PATHS;
}

return undefined;
}
}
10 changes: 8 additions & 2 deletions src/command/GenerateContextPluginCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import PluginClassGenerator from 'generator/plugin/PluginClassGenerator';
import PluginDiGenerator from 'generator/plugin/PluginDiGenerator';
import PhpParser from 'parser/php/Parser';
import { PhpMethod } from 'parser/php/PhpMethod';
import Common from 'util/Common';
import * as vscode from 'vscode';
import PluginContextWizard from 'wizard/PluginContextWizard';

Expand All @@ -22,6 +23,13 @@ export default class GenerateContextPluginCommand extends Command {
return;
}

const workspaceFolder = Common.getActiveWorkspaceFolder();

if (!workspaceFolder) {
vscode.window.showErrorMessage('No active workspace folder');
return;
}

const selection = editor?.selection;
if (!editor || !selection) {
vscode.window.showErrorMessage('No selection');
Expand Down Expand Up @@ -65,8 +73,6 @@ export default class GenerateContextPluginCommand extends Command {
new PluginDiGenerator(data, classlike, method),
]);

const workspaceFolder = vscode.workspace.workspaceFolders![0];

await manager.generate(workspaceFolder.uri);
await manager.writeFiles();
await manager.refreshIndex(workspaceFolder);
Expand Down
25 changes: 21 additions & 4 deletions src/command/GenerateModuleCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import ModuleXmlGenerator from 'generator/module/ModuleXmlGenerator';
import ModuleRegistrationGenerator from 'generator/module/ModuleRegistrationGenerator';
import ModuleComposerGenerator from 'generator/module/ModuleComposerGenerator';
import ModuleLicenseGenerator from 'generator/module/ModuleLicenseGenerator';
import ModuleWizard from 'wizard/ModuleWizard';
import ModuleWizard, { ModuleWizardData, ModuleWizardComposerData } from 'wizard/ModuleWizard';
import FileGeneratorManager from 'generator/FileGeneratorManager';
import { workspace } from 'vscode';
import { window } from 'vscode';
import Common from 'util/Common';
import WizzardClosedError from 'webview/error/WizzardClosedError';

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

const data = await moduleWizard.show();
let data: ModuleWizardData | ModuleWizardComposerData;

try {
data = await moduleWizard.show();
} catch (error) {
if (error instanceof WizzardClosedError) {
return;
}

throw error;
}

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

const workspaceFolder = workspace.workspaceFolders![0];
const workspaceFolder = Common.getActiveWorkspaceFolder();

if (!workspaceFolder) {
window.showErrorMessage('No active workspace folder');
return;
}

await manager.generate(workspaceFolder.uri);
await manager.writeFiles();
Expand Down
47 changes: 47 additions & 0 deletions src/command/GenerateObserverCommand.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { Command } from 'command/Command';
import ObserverWizard, { ObserverWizardData } from 'wizard/ObserverWizard';
import WizzardClosedError from 'webview/error/WizzardClosedError';
import FileGeneratorManager from 'generator/FileGeneratorManager';
import Common from 'util/Common';
import { window } from 'vscode';
import ObserverClassGenerator from 'generator/observer/ObserverClassGenerator';
import ObserverEventsGenerator from 'generator/observer/ObserverEventsGenerator';

export default class GenerateObserverCommand extends Command {
constructor() {
super('magento-toolbox.generateObserver');
}

public async execute(eventName?: string): Promise<void> {
const observerWizard = new ObserverWizard();

let data: ObserverWizardData;

try {
data = await observerWizard.show(eventName);
} catch (error) {
if (error instanceof WizzardClosedError) {
return;
}

throw error;
}

const manager = new FileGeneratorManager([
new ObserverClassGenerator(data),
new ObserverEventsGenerator(data),
]);

const workspaceFolder = Common.getActiveWorkspaceFolder();

if (!workspaceFolder) {
window.showErrorMessage('No active workspace folder');
return;
}

await manager.generate(workspaceFolder.uri);
await manager.writeFiles();
await manager.refreshIndex(workspaceFolder);
manager.openFirstFile();
}
}
Loading