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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
- Added: Module hover information
- Added: Added extension config fields for enabling/disabling completions, definitions and hovers
- Added: acl.xml indexer, definitions, autocomplete and hovers
- Added: template file indexer, definitions and autocomplete
- Added: Index data persistance
- Changed: Adjusted namespace indexer logic

Expand Down
68 changes: 3 additions & 65 deletions src/command/CopyMagentoPathCommand.ts
Original file line number Diff line number Diff line change
@@ -1,82 +1,20 @@
import { Command } from 'command/Command';
import IndexManager from 'indexer/IndexManager';
import ModuleIndexer from 'indexer/module/ModuleIndexer';
import GetMagentoPath from 'common/GetMagentoPath';
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 magentoPath = GetMagentoPath.getMagentoPath(file);

const paths = this.getPaths(file);

if (!paths) {
if (!magentoPath) {
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;
}
}
8 changes: 4 additions & 4 deletions src/common/Context.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { commands, TextEditor } from 'vscode';
import PhpDocumentParser from './php/PhpDocumentParser';
import CopyMagentoPathCommand from 'command/CopyMagentoPathCommand';
import GetMagentoPath from './GetMagentoPath';

export interface EditorContext {
canGeneratePlugin: boolean;
Expand Down Expand Up @@ -47,9 +47,9 @@ class Context {
canGeneratePlugin: false,
canGeneratePreference: false,
supportedMagentoPathExtensions: [
...CopyMagentoPathCommand.TEMPLATE_EXTENSIONS,
...CopyMagentoPathCommand.WEB_EXTENSIONS,
...CopyMagentoPathCommand.IMAGE_EXTENSIONS,
...GetMagentoPath.TEMPLATE_EXTENSIONS,
...GetMagentoPath.WEB_EXTENSIONS,
...GetMagentoPath.IMAGE_EXTENSIONS,
],
};
}
Expand Down
78 changes: 78 additions & 0 deletions src/common/GetMagentoPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import IndexManager from 'indexer/IndexManager';
import ModuleIndexer from 'indexer/module/ModuleIndexer';
import { Uri } from 'vscode';

export class GetMagentoPath {
public readonly TEMPLATE_EXTENSIONS = ['.phtml'];
public readonly WEB_EXTENSIONS = ['.css', '.js'];
public readonly IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg', '.gif', '.svg'];

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

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

public getMagentoPath(file: Uri): string | undefined {
if (!file) {
return;
}

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

if (!moduleIndexData) {
return;
}

const module = moduleIndexData.getModuleByUri(file, false);

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}`;

return magentoPath;
}

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

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

return undefined;
}
}

export default new GetMagentoPath();
2 changes: 2 additions & 0 deletions src/completion/XmlCompletionProviderProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CancellationToken } from 'vscode';
import { ModuleCompletionProvider } from './xml/ModuleCompletionProvider';
import { NamespaceCompletionProvider } from './xml/NamespaceCompletionProvider';
import { AclCompletionProvider } from './xml/AclCompletionProvider';
import { TemplateCompletionProvider } from './xml/TemplateCompletionProvider';

export class XmlCompletionProviderProcessor
extends XmlSuggestionProviderProcessor<CompletionItem>
Expand All @@ -17,6 +18,7 @@ export class XmlCompletionProviderProcessor
new ModuleCompletionProvider(),
new NamespaceCompletionProvider(),
new AclCompletionProvider(),
new TemplateCompletionProvider(),
]);
}

Expand Down
52 changes: 52 additions & 0 deletions src/completion/xml/TemplateCompletionProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { CompletionItem, Uri, Range, TextDocument } from 'vscode';
import IndexManager from 'indexer/IndexManager';
import { XmlSuggestionProvider, CombinedCondition } from 'common/xml/XmlSuggestionProvider';
import { XMLElement, XMLAttribute } from '@xml-tools/ast';
import { AttributeNameMatches } from 'common/xml/suggestion/condition/AttributeNameMatches';
import TemplateIndexer from 'indexer/template/TemplateIndexer';
import { ElementAttributeMatches } from 'common/xml/suggestion/condition/ElementAttributeMatches';

export class TemplateCompletionProvider extends XmlSuggestionProvider<CompletionItem> {
public getFilePatterns(): string[] {
return ['**/view/**/layout/*.xml', '**/etc/**/di.xml'];
}

public getAttributeValueConditions(): CombinedCondition[] {
return [[new AttributeNameMatches('template')]];
}

public getElementContentMatches(): CombinedCondition[] {
return [
[
new ElementAttributeMatches('name', 'template'),
new ElementAttributeMatches('xsi:type', 'string'),
],
];
}

public getConfigKey(): string | undefined {
return 'provideXmlDefinitions';
}

public getSuggestionItems(
value: string,
range: Range,
document: TextDocument,
element: XMLElement,
attribute?: XMLAttribute
): CompletionItem[] {
const templateIndexData = IndexManager.getIndexData(TemplateIndexer.KEY);

if (!templateIndexData) {
return [];
}

const templates = templateIndexData.getTemplatesByPrefix(value);

return templates.map(template => {
const item = new CompletionItem(template.magentoPath);
item.range = range;
return item;
});
}
}
7 changes: 6 additions & 1 deletion src/definition/XmlDefinitionProviderProcessor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,18 @@ import {
import { XmlSuggestionProviderProcessor } from 'common/xml/XmlSuggestionProviderProcessor';
import { AclDefinitionProvider } from './xml/AclDefinitionProvider';
import { ModuleDefinitionProvider } from './xml/ModuleDefinitionProvider';
import { TemplateDefinitionProvider } from './xml/TemplateDefinitionProvider';

export class XmlDefinitionProviderProcessor
extends XmlSuggestionProviderProcessor<LocationLink>
implements DefinitionProvider
{
public constructor() {
super([new AclDefinitionProvider(), new ModuleDefinitionProvider()]);
super([
new AclDefinitionProvider(),
new ModuleDefinitionProvider(),
new TemplateDefinitionProvider(),
]);
}

public async provideDefinition(
Expand Down
60 changes: 60 additions & 0 deletions src/definition/xml/TemplateDefinitionProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import { LocationLink, Uri, Range, TextDocument } from 'vscode';
import IndexManager from 'indexer/IndexManager';
import { XmlSuggestionProvider, CombinedCondition } from 'common/xml/XmlSuggestionProvider';
import { XMLElement, XMLAttribute } from '@xml-tools/ast';
import { AttributeNameMatches } from 'common/xml/suggestion/condition/AttributeNameMatches';
import TemplateIndexer from 'indexer/template/TemplateIndexer';
import { ElementAttributeMatches } from 'common/xml/suggestion/condition/ElementAttributeMatches';

export class TemplateDefinitionProvider extends XmlSuggestionProvider<LocationLink> {
public getFilePatterns(): string[] {
return ['**/view/**/layout/*.xml', '**/etc/**/di.xml'];
}

public getAttributeValueConditions(): CombinedCondition[] {
return [[new AttributeNameMatches('template')]];
}

public getElementContentMatches(): CombinedCondition[] {
return [
[
new ElementAttributeMatches('name', 'template'),
new ElementAttributeMatches('xsi:type', 'string'),
],
];
}

public getConfigKey(): string | undefined {
return 'provideXmlDefinitions';
}

public getSuggestionItems(
value: string,
range: Range,
document: TextDocument,
element: XMLElement,
attribute?: XMLAttribute
): LocationLink[] {
const templateIndexData = IndexManager.getIndexData(TemplateIndexer.KEY);

if (!templateIndexData) {
return [];
}

const templates = templateIndexData.getTemplatesByPrefix(value);

if (!templates.length) {
return [];
}

return templates.map(template => {
const templateUri = Uri.file(template.path);

return {
targetUri: templateUri,
targetRange: new Range(0, 0, 0, 0),
originSelectionRange: range,
};
});
}
}
10 changes: 9 additions & 1 deletion src/indexer/IndexManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,24 @@ import Logger from 'util/Logger';
import { IndexerKey } from 'types/indexer';
import AclIndexer from './acl/AclIndexer';
import { AclIndexData } from './acl/AclIndexData';
import TemplateIndexer from './template/TemplateIndexer';
import { TemplateIndexData } from './template/TemplateIndexData';

type IndexerInstance =
| DiIndexer
| ModuleIndexer
| AutoloadNamespaceIndexer
| EventsIndexer
| AclIndexer;
| AclIndexer
| TemplateIndexer;

type IndexerDataMap = {
[DiIndexer.KEY]: DiIndexData;
[ModuleIndexer.KEY]: ModuleIndexData;
[AutoloadNamespaceIndexer.KEY]: AutoloadNamespaceIndexData;
[EventsIndexer.KEY]: EventsIndexData;
[AclIndexer.KEY]: AclIndexData;
[TemplateIndexer.KEY]: TemplateIndexData;
};

class IndexManager {
Expand All @@ -43,6 +47,7 @@ class IndexManager {
new AutoloadNamespaceIndexer(),
new EventsIndexer(),
new AclIndexer(),
new TemplateIndexer(),
];
this.indexStorage = new IndexStorage();
}
Expand Down Expand Up @@ -173,6 +178,9 @@ class IndexManager {
case AclIndexer.KEY:
return new AclIndexData(data) as IndexerDataMap[T];

case TemplateIndexer.KEY:
return new TemplateIndexData(data) as IndexerDataMap[T];

default:
return undefined;
}
Expand Down
Loading