├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── META-INF
└── plugin.xml
├── README.md
├── idea-php-oxid.iml
└── src
├── de
└── espend
│ └── idea
│ └── oxid
│ ├── OxidPluginIcons.java
│ ├── OxidProjectComponent.java
│ ├── completion
│ ├── PhpCompletionProvider.java
│ └── SmartyFileCompletionProvider.java
│ ├── dict
│ └── metadata
│ │ ├── MetadataBlock.java
│ │ └── MetadataSetting.java
│ ├── icons
│ ├── line_marker_oxid.png
│ ├── line_marker_oxid@2.png
│ ├── oxid.png
│ └── oxid@2.png
│ ├── inspection
│ └── OxidMetadataFileInspection.java
│ ├── navigation
│ ├── PhpGoToHandler.java
│ ├── SmartyGoToHandler.java
│ └── SmartyTemplateLineMarkerProvider.java
│ ├── stub
│ └── OxidContentIdentIndexer.java
│ ├── types
│ └── OxidFactoryTypeProvider.java
│ └── utils
│ ├── MetadataUtil.java
│ ├── ModuleUtil.java
│ ├── OxidUtil.java
│ ├── PhpMetadataUtil.java
│ ├── SmartyBlockUtil.java
│ ├── SmartyPattern.java
│ ├── TemplateUtil.java
│ └── TranslationUtil.java
└── inspectionDescriptions
└── OxidMetadataFile.html
/.gitignore:
--------------------------------------------------------------------------------
1 | out/
2 | /.idea
3 | /.idea/misc.xml
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.5
2 | * Move OxidMetadataFileInspection into PsiElementVisitor
3 | * Migration to PhpTypeProvider3 for PhpStorm 2017.3 [#12](https://github.com/Haehnchen/idea-php-oxid-plugin/issues/12)
4 | * Fix com.intellij.openapi.project.IndexNotReadyException in custom trait generator for class overwrites [#10](https://github.com/Haehnchen/idea-php-oxid-plugin/issues/10)
5 |
6 | ## 0.4
7 | * Fix NullPointerException in visitMetadataKey [#5](https://github.com/Haehnchen/idea-php-oxid-plugin/issues/5)
8 | * fix modules without vendor are not in .phpstorm-oxid.meta.php [#2](https://github.com/Haehnchen/idea-php-oxid-plugin/issues/2)
9 | * Add cache for metadata getExtendsList, as this one is the slowest stuff
10 |
11 | ## 0.3.1
12 | * Support core "lang.php" for translations
13 | * Fix repeating File Cache Conflict since [#4](https://github.com/Haehnchen/idea-php-oxid-plugin/issues/4)
14 |
15 | ## 0.3
16 | * Implement traits generator for extend metadata class, so every module class knows foreign inheritance
17 | * Add support oxmultilang::ident and oxcontent::ident in smarty
18 |
19 | ## 0.2.1
20 | * Fix lookup elements for factory methods where collected in nowhere
21 |
22 | ## 0.2
23 | * proxy getArrayKeyValueMap for npe fix [#1](https://github.com/Haehnchen/idea-php-oxid-plugin/issues/1)
24 | * Recursive search for module or vendor module structure [#2](https://github.com/Haehnchen/idea-php-oxid-plugin/issues/2)
25 | * Support array key of "extend" in metadata [#3](https://github.com/Haehnchen/idea-php-oxid-plugin/issues/3)
26 | * Some performance improvements
27 |
28 | ## 0.1
29 | * Initial release with dep on [Symfony2 Plugin](http://plugins.jetbrains.com/plugin/7219)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2015 Daniel Espendiller
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19 | THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/META-INF/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 | de.espend.idea.oxid
3 | OXID Plugin
4 | 0.5
5 | Daniel Espendiller
6 |
7 | OXID Plugin
9 |
10 | Issues | Donate
11 |
12 |
13 | Because of code sharing install Symfony Plugin and enable it per project
14 |
15 |
16 | Dependencies
17 |
20 |
21 | Features
22 |
23 | - PHP: Periodically scans module metadata for class inheritance and exports them to ".phpstorm-oxid.meta.php" in your project root
24 | - PHP: Metadata file exists inspector in array values
25 | - PHP: oxLang::translateString support
26 | - PHP: TypeProvider, references for factory function: oxNew, oxRegistry::get
27 | - PHP: Custom class inheritance TypeProvider for factories that a extended by metafile
28 | - PHP: oxConfig::*ConfigParam
29 | - PHP: Completion and navigation in all metadata files; array key and value context
30 | - Smarty: Block references and linemarker
31 | - Smarty: File includes references
32 | - Smarty: File metadata parser for template related stuff
33 | - Smarty: Ident oxmultilang, oxcontent
34 |
35 |
36 | ]]>
37 |
38 |
40 | Move OxidMetadataFileInspection into PsiElementVisitor
41 | Migration to PhpTypeProvider3 for PhpStorm 2017.3 #12
42 | Fix com.intellij.openapi.project.IndexNotReadyException in custom trait generator for class overwrites #10
43 |
44 |
45 |
46 | see full changelog | Donate
47 | ]]>
48 |
49 |
50 |
51 |
52 |
53 |
55 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 | com.jetbrains.php
84 | com.intellij.modules.platform
85 | fr.adrienbrault.idea.symfony2plugin
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | de.espend.idea.oxid.OxidProjectComponent
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OXID Plugin
2 | ==========================
3 | [](https://plugins.jetbrains.com/plugin/7853)
4 | [](https://plugins.jetbrains.com/plugin/7853)
5 | [](https://plugins.jetbrains.com/plugin/7853)
6 | [](https://www.paypal.me/DanielEspendiller)
7 |
8 | IntelliJ IDEA / PhpStorm Plugin for [OXID](http://www.oxid-esales.com/ "OXID")
9 |
10 | Key | Value
11 | ----------- | -----------
12 | Plugin Url | https://plugins.jetbrains.com/plugin/7853
13 | ID | de.espend.idea.oxid
14 | Changelog | [CHANGELOG](CHANGELOG.md)
15 |
16 | Install
17 | ---------------------
18 | * Install the plugin by going to `Settings -> Plugins -> Browse repositories` and then search for `OXID Plugin`.
19 | * Enabled [Symfony Plugin](https://plugins.jetbrains.com/plugin/7219) per project (File > Settings > Symfony Plugin )
20 |
21 | Screenshots
22 | ---------------------
23 |
24 | 
25 | 
26 | 
--------------------------------------------------------------------------------
/idea-php-oxid.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/OxidPluginIcons.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid;
2 |
3 | import com.intellij.openapi.util.IconLoader;
4 |
5 | import javax.swing.*;
6 |
7 | /**
8 | * @author Daniel Espendiller
9 | */
10 | public class OxidPluginIcons {
11 | public static final Icon OXID = IconLoader.getIcon("icons/oxid.png");
12 | public static final Icon OXID_LINEMARKER = IconLoader.getIcon("icons/line_marker_oxid.png");
13 | }
14 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/OxidProjectComponent.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid;
2 |
3 | import com.intellij.openapi.components.ProjectComponent;
4 | import com.intellij.openapi.progress.ProgressIndicator;
5 | import com.intellij.openapi.progress.Task;
6 | import com.intellij.openapi.project.DumbService;
7 | import com.intellij.openapi.project.Project;
8 | import com.intellij.psi.PsiElement;
9 | import de.espend.idea.oxid.utils.OxidUtil;
10 | import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
11 | import org.jetbrains.annotations.NotNull;
12 | import org.jetbrains.annotations.Nullable;
13 |
14 | import java.util.Timer;
15 | import java.util.TimerTask;
16 |
17 | /**
18 | * @author Daniel Espendiller
19 | */
20 | public class OxidProjectComponent implements ProjectComponent {
21 |
22 | public static final int DUMPER_PERIODE = 600 * 1000;
23 |
24 | private final Project project;
25 | private Timer timer;
26 |
27 | public OxidProjectComponent(Project project) {
28 | this.project = project;
29 | }
30 |
31 | @Override
32 | public void projectOpened() {
33 |
34 | DumbService.getInstance(this.project).smartInvokeLater(() -> {
35 | if(PhpElementsUtil.getClassInterface(project, "\\oxArticle") == null) {
36 | return;
37 | }
38 |
39 | new Task.Backgroundable(project, "OXID extends dumper", true) {
40 | @Override
41 | public void run(@NotNull ProgressIndicator indicator) {
42 | if(DumbService.getInstance(project).isDumb()) {
43 | return;
44 | }
45 |
46 | OxidUtil.buildClassMetadataFile(project);
47 | }
48 | }.queue();
49 |
50 |
51 | timer = new Timer();
52 |
53 | timer.schedule(new TimerTask() {
54 | public void run() {
55 |
56 | if(DumbService.getInstance(project).isDumb()) {
57 | return;
58 | }
59 |
60 | OxidUtil.buildClassMetadataFile(project);
61 | }
62 | }, DUMPER_PERIODE, DUMPER_PERIODE);
63 |
64 | });
65 |
66 |
67 | }
68 |
69 | @Override
70 | public void projectClosed() {
71 | if(this.timer != null) {
72 | this.timer.cancel();
73 | timer.purge();
74 | timer = null;
75 | }
76 | }
77 |
78 | @Override
79 | public void initComponent() {
80 |
81 | }
82 |
83 | @Override
84 | public void disposeComponent() {
85 |
86 | }
87 |
88 | @NotNull
89 | @Override
90 | public String getComponentName() {
91 | return "OXID Plugin";
92 | }
93 |
94 | public static boolean isValidForProject(@Nullable PsiElement psiElement) {
95 | if(psiElement == null) {
96 | return false;
97 | }
98 |
99 | // @TODO: plugin switch
100 | // VfsUtil.findRelativeFile(psiElement.getProject().getBaseDir(), "engine", "Shopware", "Kernel.php") != null
101 | // PhpElementsUtil.getClassInterface(psiElement.getProject(), "\\Enlight_Controller_Action") != null
102 |
103 | return true;
104 | }
105 |
106 | }
107 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/completion/PhpCompletionProvider.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.completion;
2 |
3 | import com.intellij.codeInsight.completion.*;
4 | import com.intellij.codeInsight.completion.impl.CamelHumpMatcher;
5 | import com.intellij.codeInsight.lookup.LookupElement;
6 | import com.intellij.codeInsight.lookup.LookupElementBuilder;
7 | import com.intellij.openapi.fileTypes.FileType;
8 | import com.intellij.openapi.vfs.VfsUtil;
9 | import com.intellij.openapi.vfs.VirtualFile;
10 | import com.intellij.openapi.vfs.VirtualFileVisitor;
11 | import com.intellij.patterns.PlatformPatterns;
12 | import com.intellij.psi.PsiDirectory;
13 | import com.intellij.psi.PsiElement;
14 | import com.intellij.psi.PsiFile;
15 | import com.intellij.psi.PsiManager;
16 | import com.intellij.psi.search.FilenameIndex;
17 | import com.intellij.psi.search.GlobalSearchScope;
18 | import com.intellij.psi.util.PsiTreeUtil;
19 | import com.intellij.util.ProcessingContext;
20 | import com.jetbrains.php.PhpIndex;
21 | import com.jetbrains.php.completion.PhpClassLookupElement;
22 | import com.jetbrains.php.lang.PhpFileType;
23 | import com.jetbrains.php.lang.parser.PhpElementTypes;
24 | import com.jetbrains.php.lang.psi.PhpFile;
25 | import com.jetbrains.php.lang.psi.PhpPsiUtil;
26 | import com.jetbrains.php.lang.psi.elements.*;
27 | import com.jetbrains.smarty.SmartyFileType;
28 | import de.espend.idea.oxid.OxidPluginIcons;
29 | import de.espend.idea.oxid.OxidProjectComponent;
30 | import de.espend.idea.oxid.dict.metadata.MetadataSetting;
31 | import de.espend.idea.oxid.utils.*;
32 | import fr.adrienbrault.idea.symfony2plugin.Symfony2Icons;
33 | import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
34 | import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
35 | import fr.adrienbrault.idea.symfony2plugin.util.completion.PhpClassReferenceInsertHandler;
36 | import org.apache.commons.lang.StringUtils;
37 | import org.jetbrains.annotations.NotNull;
38 |
39 | import java.util.*;
40 |
41 | /**
42 | * @author Daniel Espendiller
43 | */
44 | public class PhpCompletionProvider extends CompletionContributor {
45 |
46 | public PhpCompletionProvider() {
47 |
48 | // ['extend' => ['key' => ''] ]
49 | extend(
50 | CompletionType.BASIC, MetadataUtil.getMetadataFilePattern(),
51 | new CompletionProvider() {
52 | @Override
53 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
54 |
55 | PsiElement originalPosition = parameters.getOriginalPosition();
56 | if(originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
57 | return;
58 | }
59 |
60 | PsiElement parent = originalPosition.getParent();
61 | if(!(parent instanceof StringLiteralExpression) || !PhpMetadataUtil.isModuleKeyInFlatArray((StringLiteralExpression) parent, "extend")) {
62 | return;
63 | }
64 |
65 | ModuleUtil.visitModuleFile(originalPosition.getContainingFile(), new ModuleUtil.ModuleFileVisitor() {
66 | @Override
67 | public void visit(@NotNull VirtualFile virtualFile, @NotNull String relativePath) {
68 |
69 | if (virtualFile.getFileType() != PhpFileType.INSTANCE || !relativePath.endsWith(".php")) {
70 | return;
71 | }
72 |
73 | result.addElement(LookupElementBuilder.create(relativePath.substring(0, relativePath.length() - 4)).withIcon(virtualFile.getFileType().getIcon()));
74 | }
75 | });
76 |
77 | }
78 | }
79 | );
80 |
81 |
82 | // ['extend' => ['file' => ''] ]
83 | extend(
84 | CompletionType.BASIC, MetadataUtil.getMetadataFilePattern(),
85 | new CompletionProvider() {
86 | @Override
87 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
88 |
89 | PsiElement originalPosition = parameters.getOriginalPosition();
90 | if(originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
91 | return;
92 | }
93 |
94 | PsiElement parent = originalPosition.getParent();
95 | if(!(parent instanceof StringLiteralExpression) || !PhpMetadataUtil.isInTemplateWithKey((StringLiteralExpression) parent, "file")) {
96 | return;
97 | }
98 |
99 | ModuleUtil.visitModuleTemplatesInMetadataScope(originalPosition.getContainingFile(), new ModuleUtil.ModuleFileVisitor() {
100 | @Override
101 | public void visit(@NotNull VirtualFile virtualFile, @NotNull String relativePath) {
102 | result.addElement(LookupElementBuilder.create(relativePath).withIcon(virtualFile.getFileType().getIcon()));
103 | }
104 | });
105 |
106 | }
107 | }
108 | );
109 |
110 | // ['files' => [...] ]
111 | extend(
112 | CompletionType.BASIC, MetadataUtil.getMetadataFilePattern(),
113 | new ModuleFileParametersCompletionProvider("files", PhpFileType.INSTANCE)
114 | );
115 |
116 | // ['templates' => [...] ]
117 | extend(
118 | CompletionType.BASIC, MetadataUtil.getMetadataFilePattern(),
119 | new ModuleFileParametersCompletionProvider("templates", SmartyFileType.INSTANCE)
120 | );
121 |
122 | // "blocks" => [{"template" => 'foo'}]
123 | extend(
124 | CompletionType.BASIC, MetadataUtil.getMetadataFilePattern(),
125 | new CompletionProvider() {
126 | @Override
127 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
128 |
129 | PsiElement originalPosition = parameters.getOriginalPosition();
130 | if(originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
131 | return;
132 | }
133 |
134 | PsiElement parent = originalPosition.getParent();
135 | if(!(parent instanceof StringLiteralExpression) || !PhpMetadataUtil.isInTemplateWithKey((StringLiteralExpression) parent, "template")) {
136 | return;
137 | }
138 |
139 | TemplateUtil.collectFiles(parameters.getPosition().getProject(), new TemplateUtil.SmartyTemplateVisitor() {
140 | @Override
141 | public void visitFile(VirtualFile virtualFile, String fileName) {
142 | result.addElement(LookupElementBuilder.create(fileName).withIcon(virtualFile.getFileType().getIcon()));
143 | }
144 | });
145 |
146 | }
147 | }
148 | );
149 |
150 | // "blocks" => [{"block" => 'foo'}]
151 | extend(
152 | CompletionType.BASIC, MetadataUtil.getMetadataFilePattern(),
153 | new CompletionProvider() {
154 | @Override
155 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
156 |
157 | PsiElement originalPosition = parameters.getOriginalPosition();
158 | if(originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
159 | return;
160 | }
161 |
162 | PsiElement parent = originalPosition.getParent();
163 | if(!(parent instanceof StringLiteralExpression) || !PhpMetadataUtil.isInTemplateWithKey((StringLiteralExpression) parent, "block")) {
164 | return;
165 | }
166 |
167 | ArrayCreationExpression arrayCreation = PsiTreeUtil.getParentOfType(originalPosition, ArrayCreationExpression.class);
168 | if(arrayCreation == null) {
169 | return;
170 | }
171 |
172 | String template = PhpElementsUtil.getArrayValueString(arrayCreation, "template");
173 | if(template == null) {
174 | return;
175 | }
176 |
177 | result.addAllElements(TemplateUtil.getBlockFileLookupElements(parameters.getPosition().getProject(), template));
178 | }
179 | }
180 | );
181 |
182 | // \oxConfig::getConfigParam()
183 | // \oxConfig::setConfigParam()
184 | extend(
185 | CompletionType.BASIC, PlatformPatterns.psiElement(),
186 | new CompletionProvider() {
187 | @Override
188 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
189 |
190 | PsiElement originalPosition = parameters.getOriginalPosition();
191 | if(originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
192 | return;
193 | }
194 |
195 | if(new MethodMatcher.StringParameterRecursiveMatcher(originalPosition.getContext(), 0)
196 | .withSignature("\\oxConfig", "getConfigParam")
197 | .withSignature("\\oxConfig", "getConfigParam")
198 | .match() == null) {
199 |
200 | return;
201 | }
202 |
203 | Set settings = new HashSet();
204 |
205 | for (PsiFile psiFile : FilenameIndex.getFilesByName(originalPosition.getProject(), "metadata.php", GlobalSearchScope.allScope(originalPosition.getProject()))) {
206 | for (MetadataSetting setting : MetadataUtil.getSettings(psiFile)) {
207 | settings.add(setting.getName());
208 | }
209 | }
210 |
211 | for (String setting : settings) {
212 | result.addElement(LookupElementBuilder.create(setting).withIcon(OxidPluginIcons.OXID));
213 | }
214 | }
215 | }
216 | );
217 |
218 | // oxRegistry::get, oxNew
219 | extend(
220 | CompletionType.BASIC, PlatformPatterns.psiElement(),
221 | new CompletionProvider() {
222 | @Override
223 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
224 |
225 | PsiElement originalPosition = parameters.getOriginalPosition();
226 | if (originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
227 | return;
228 | }
229 |
230 | PsiElement parent = originalPosition.getContext();
231 | if(!(parent instanceof StringLiteralExpression)) {
232 | return;
233 | }
234 |
235 | if(!OxidUtil.isFactory((StringLiteralExpression) parent)) {
236 | return;
237 | }
238 |
239 | String contents = ((StringLiteralExpression) parent).getContents();
240 | result.addAllElements(OxidUtil.getOverloadAbleClasses(parent.getProject(), contents));
241 | }
242 | }
243 | );
244 |
245 | // \oxLang::translateString()
246 | extend(
247 | CompletionType.BASIC, PlatformPatterns.psiElement(),
248 | new CompletionProvider() {
249 | @Override
250 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
251 |
252 | PsiElement originalPosition = parameters.getOriginalPosition();
253 | if (originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
254 | return;
255 | }
256 |
257 | if (new MethodMatcher.StringParameterRecursiveMatcher(originalPosition.getContext(), 0)
258 | .withSignature("\\oxLang", "translateString")
259 | .match() == null) {
260 |
261 | return;
262 | }
263 |
264 | result.addAllElements(TranslationUtil.getTranslationLookupElements(originalPosition.getProject()));
265 | }
266 | }
267 | );
268 |
269 |
270 | // ['extend' => ['key'] ]
271 | extend(
272 | CompletionType.BASIC, MetadataUtil.getMetadataFilePattern(),
273 | new CompletionProvider() {
274 | @Override
275 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
276 |
277 | PsiElement originalPosition = parameters.getOriginalPosition();
278 | if (originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
279 | return;
280 | }
281 |
282 | PsiElement parent = originalPosition.getParent();
283 | if(!(parent instanceof StringLiteralExpression) || !PhpMetadataUtil.isExtendKey((StringLiteralExpression) parent)) {
284 | return;
285 | }
286 |
287 | result.addAllElements(OxidUtil.getOverloadAbleClasses(parent.getProject(), ((StringLiteralExpression) parent).getContents()));
288 | }
289 | }
290 | );
291 |
292 | }
293 |
294 | private static class ModuleFileParametersCompletionProvider extends CompletionProvider {
295 |
296 | @NotNull
297 | private final String key;
298 |
299 | @NotNull
300 | private final FileType fileType;
301 |
302 | public ModuleFileParametersCompletionProvider(@NotNull String key, @NotNull FileType fileType) {
303 | this.key = key;
304 | this.fileType = fileType;
305 | }
306 |
307 | @Override
308 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
309 |
310 | PsiElement originalPosition = parameters.getOriginalPosition();
311 | if(originalPosition == null || !OxidProjectComponent.isValidForProject(originalPosition)) {
312 | return;
313 | }
314 |
315 | PsiElement parent = originalPosition.getParent();
316 | if(!(parent instanceof StringLiteralExpression) || !PhpMetadataUtil.isModuleKeyInFlatArray((StringLiteralExpression) parent, key)) {
317 | return;
318 | }
319 |
320 | ModuleUtil.visitModuleFile(originalPosition.getContainingFile(), new ModuleUtil.ModuleFileVisitor() {
321 | @Override
322 | public void visit(@NotNull VirtualFile virtualFile, @NotNull String relativePath) {
323 |
324 | if (virtualFile.getFileType() != fileType) {
325 | return;
326 | }
327 |
328 | result.addElement(LookupElementBuilder.create(relativePath).withIcon(virtualFile.getFileType().getIcon()));
329 | }
330 | });
331 |
332 | }
333 | }
334 | }
335 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/completion/SmartyFileCompletionProvider.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.completion;
2 |
3 | import com.intellij.codeInsight.completion.*;
4 | import com.intellij.codeInsight.lookup.LookupElementBuilder;
5 | import com.intellij.openapi.vfs.VirtualFile;
6 | import com.intellij.psi.PsiElement;
7 | import com.intellij.util.ProcessingContext;
8 | import de.espend.idea.oxid.OxidPluginIcons;
9 | import de.espend.idea.oxid.OxidProjectComponent;
10 | import de.espend.idea.oxid.utils.SmartyPattern;
11 | import de.espend.idea.oxid.utils.TemplateUtil;
12 | import de.espend.idea.oxid.utils.TranslationUtil;
13 | import org.jetbrains.annotations.NotNull;
14 |
15 | import java.util.Set;
16 |
17 | /**
18 | * @author Daniel Espendiller
19 | */
20 | public class SmartyFileCompletionProvider extends CompletionContributor {
21 |
22 | public SmartyFileCompletionProvider() {
23 |
24 | extend(
25 | CompletionType.BASIC, SmartyPattern.getFilePattern(),
26 | new CompletionProvider() {
27 | @Override
28 | protected void addCompletions(final @NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
29 |
30 | if(!OxidProjectComponent.isValidForProject(parameters.getOriginalPosition())) {
31 | return;
32 | }
33 |
34 | attachTemplateFiles(parameters, result);
35 | }
36 | }
37 | );
38 |
39 | extend(
40 | CompletionType.BASIC, SmartyPattern.getBlockPattern(),
41 | new CompletionProvider() {
42 | @Override
43 | protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
44 |
45 | PsiElement originalPosition = parameters.getOriginalPosition();
46 | if(!OxidProjectComponent.isValidForProject(originalPosition)) {
47 | return;
48 | }
49 |
50 | Set templateNames = TemplateUtil.getTemplateNames(originalPosition.getProject(), originalPosition.getContainingFile().getVirtualFile());
51 | if(templateNames.size() == 0) {
52 | return;
53 | }
54 |
55 | result.addAllElements(TemplateUtil.getBlockFileLookupElements(parameters.getPosition().getProject(), templateNames.toArray(new String[templateNames.size()])));
56 | }
57 |
58 | }
59 |
60 | );
61 |
62 | extend(
63 | CompletionType.BASIC, SmartyPattern.getAttributeInsideTagPattern("ident", "oxmultilang"),
64 | new CompletionProvider() {
65 | @Override
66 | protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
67 |
68 | PsiElement originalPosition = parameters.getOriginalPosition();
69 | if(!OxidProjectComponent.isValidForProject(originalPosition)) {
70 | return;
71 | }
72 |
73 | result.addAllElements(TranslationUtil.getTranslationLookupElements(originalPosition.getProject()));
74 | }
75 |
76 | }
77 |
78 | );
79 |
80 | extend(
81 | CompletionType.BASIC, SmartyPattern.getAttributeInsideTagPattern("ident", "oxcontent"),
82 | new CompletionProvider() {
83 | @Override
84 | protected void addCompletions(@NotNull CompletionParameters parameters, ProcessingContext context, final @NotNull CompletionResultSet result) {
85 |
86 | PsiElement originalPosition = parameters.getOriginalPosition();
87 | if(!OxidProjectComponent.isValidForProject(originalPosition)) {
88 | return;
89 | }
90 |
91 | for (String s : TemplateUtil.getContentIdents(originalPosition.getProject())) {
92 | result.addElement(LookupElementBuilder.create(s).withIcon(OxidPluginIcons.OXID));
93 | }
94 |
95 | }
96 |
97 | }
98 |
99 | );
100 |
101 | }
102 |
103 | private void attachTemplateFiles(CompletionParameters parameters, final CompletionResultSet result) {
104 | TemplateUtil.collectFiles(parameters.getPosition().getProject(), new TemplateUtil.SmartyTemplateVisitor() {
105 | @Override
106 | public void visitFile(VirtualFile virtualFile, String fileName) {
107 | result.addElement(LookupElementBuilder.create(fileName).withIcon(virtualFile.getFileType().getIcon()));
108 | }
109 | });
110 | }
111 |
112 | }
113 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/dict/metadata/MetadataBlock.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.dict.metadata;
2 |
3 | import org.jetbrains.annotations.NotNull;
4 | import org.jetbrains.annotations.Nullable;
5 |
6 | import java.util.Map;
7 |
8 | /**
9 | * @author Daniel Espendiller
10 | */
11 | public class MetadataBlock {
12 |
13 | @Nullable
14 | private final String template;
15 |
16 | @Nullable
17 | private final String block;
18 |
19 | @Nullable
20 | private final String file;
21 |
22 | public MetadataBlock(@Nullable String template, @Nullable String block, @Nullable String file) {
23 | this.template = template;
24 | this.block = block;
25 | this.file = file;
26 | }
27 |
28 | @Nullable
29 | public String getTemplate() {
30 | return template;
31 | }
32 |
33 | @Nullable
34 | public String getBlock() {
35 | return block;
36 | }
37 |
38 | @Nullable
39 | public String getFile() {
40 | return file;
41 | }
42 |
43 | @Nullable
44 | public static MetadataBlock create(@NotNull Map map) {
45 |
46 | if(!map.containsKey("template") && !map.containsKey("block") && !map.containsKey("file")) {
47 | return null;
48 | }
49 |
50 | return new MetadataBlock(
51 | map.get("template"),
52 | map.get("block"),
53 | map.get("file")
54 | );
55 | }
56 |
57 | }
58 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/dict/metadata/MetadataSetting.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.dict.metadata;
2 |
3 | import com.intellij.psi.PsiElement;
4 | import org.jetbrains.annotations.NotNull;
5 | import org.jetbrains.annotations.Nullable;
6 |
7 | import java.util.Map;
8 |
9 | /**
10 | * @author Daniel Espendiller
11 | */
12 | public class MetadataSetting {
13 |
14 | @NotNull
15 | private final PsiElement source;
16 | @Nullable
17 | private final String group;
18 |
19 | @Nullable
20 | private final String name;
21 |
22 | @Nullable
23 | private final String type;
24 |
25 | private MetadataSetting(@NotNull PsiElement source, @Nullable String group, @Nullable String name, @Nullable String type) {
26 | this.source = source;
27 | this.group = group;
28 | this.name = name;
29 | this.type = type;
30 | }
31 |
32 | @Nullable
33 | public String getGroup() {
34 | return group;
35 | }
36 |
37 | @Nullable
38 | public String getName() {
39 | return name;
40 | }
41 |
42 | @Nullable
43 | public String getType() {
44 | return type;
45 | }
46 |
47 | @NotNull
48 | public PsiElement getSource() {
49 | return source;
50 | }
51 |
52 | @Nullable
53 | public static MetadataSetting create(@NotNull PsiElement source, @NotNull Map map) {
54 |
55 | if(!map.containsKey("group") && !map.containsKey("name") && !map.containsKey("type")) {
56 | return null;
57 | }
58 |
59 | return new MetadataSetting(
60 | source,
61 | map.get("group"),
62 | map.get("name"),
63 | map.get("type")
64 | );
65 | }
66 |
67 |
68 | }
69 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/icons/line_marker_oxid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Haehnchen/idea-php-oxid-plugin/edb86e6b41e422957cc86ea78293f7427ff1e764/src/de/espend/idea/oxid/icons/line_marker_oxid.png
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/icons/line_marker_oxid@2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Haehnchen/idea-php-oxid-plugin/edb86e6b41e422957cc86ea78293f7427ff1e764/src/de/espend/idea/oxid/icons/line_marker_oxid@2.png
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/icons/oxid.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Haehnchen/idea-php-oxid-plugin/edb86e6b41e422957cc86ea78293f7427ff1e764/src/de/espend/idea/oxid/icons/oxid.png
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/icons/oxid@2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Haehnchen/idea-php-oxid-plugin/edb86e6b41e422957cc86ea78293f7427ff1e764/src/de/espend/idea/oxid/icons/oxid@2.png
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/inspection/OxidMetadataFileInspection.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.inspection;
2 |
3 | import com.intellij.codeInspection.LocalInspectionTool;
4 | import com.intellij.codeInspection.ProblemHighlightType;
5 | import com.intellij.codeInspection.ProblemsHolder;
6 | import com.intellij.psi.PsiElement;
7 | import com.intellij.psi.PsiElementVisitor;
8 | import com.intellij.psi.PsiFile;
9 | import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
10 | import com.jetbrains.php.lang.PhpFileType;
11 | import com.jetbrains.php.lang.psi.elements.StringLiteralExpression;
12 | import com.jetbrains.smarty.SmartyFileType;
13 | import de.espend.idea.oxid.navigation.PhpGoToHandler;
14 | import de.espend.idea.oxid.utils.PhpMetadataUtil;
15 | import org.jetbrains.annotations.NotNull;
16 |
17 | import java.util.ArrayList;
18 | import java.util.Collection;
19 |
20 | /**
21 | * @author Daniel Espendiller
22 | */
23 | public class OxidMetadataFileInspection extends LocalInspectionTool {
24 |
25 | @NotNull
26 | @Override
27 | public PsiElementVisitor buildVisitor(final @NotNull ProblemsHolder holder, boolean isOnTheFly) {
28 |
29 | PsiFile psiFile = holder.getFile();
30 |
31 | String name = psiFile.getName();
32 | if(!name.toLowerCase().contains("metadata.php")) {
33 | return super.buildVisitor(holder, isOnTheFly);
34 | }
35 |
36 | return new MyPsiRecursiveElementWalkingVisitor(holder);
37 | }
38 |
39 | private static class MyPsiRecursiveElementWalkingVisitor extends PsiElementVisitor {
40 | @NotNull
41 | private final ProblemsHolder holder;
42 |
43 | private MyPsiRecursiveElementWalkingVisitor(@NotNull ProblemsHolder holder) {
44 | this.holder = holder;
45 | }
46 |
47 | @Override
48 | public void visitElement(PsiElement element) {
49 |
50 | if(element instanceof StringLiteralExpression) {
51 | visitLiteralExpression((StringLiteralExpression) element);
52 | }
53 |
54 | super.visitElement(element);
55 | }
56 |
57 | private void visitLiteralExpression(StringLiteralExpression element) {
58 |
59 | // @TODO: refactor in providers
60 | if(PhpMetadataUtil.isModuleKeyInFlatArray(element, "extend")) {
61 | Collection psiElements = new ArrayList();
62 | PhpGoToHandler.attachMetadataExtends(element, psiElements);
63 | if(psiElements.size() == 0) {
64 | holder.registerProblem(element, "File not found", ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
65 | }
66 | } else if(PhpMetadataUtil.isModuleKeyInFlatArray(element, "files")) {
67 | Collection psiElements = new ArrayList();
68 | PhpGoToHandler.attachAllFileTypes(element, psiElements, PhpFileType.INSTANCE);
69 | if(psiElements.size() == 0) {
70 | holder.registerProblem(element, "File not found", ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
71 | }
72 | } else if(PhpMetadataUtil.isModuleKeyInFlatArray(element, "templates")) {
73 | Collection psiElements = new ArrayList();
74 | PhpGoToHandler.attachAllFileTypes(element, psiElements, SmartyFileType.INSTANCE);
75 | if(psiElements.size() == 0) {
76 | holder.registerProblem(element, "File not found", ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
77 | }
78 | } else if(PhpMetadataUtil.isInTemplateWithKey(element, "file")) {
79 | Collection psiElements = new ArrayList();
80 | PhpGoToHandler.attachTemplateFileTypes(element, psiElements);
81 | if(psiElements.size() == 0) {
82 | holder.registerProblem(element, "File not found", ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
83 | }
84 | }
85 |
86 | }
87 |
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/navigation/PhpGoToHandler.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.navigation;
2 |
3 | import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
4 | import com.intellij.openapi.actionSystem.DataContext;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.openapi.fileTypes.FileType;
7 | import com.intellij.openapi.vfs.VirtualFile;
8 | import com.intellij.psi.PsiElement;
9 | import com.intellij.psi.PsiFile;
10 | import com.intellij.psi.PsiManager;
11 | import com.intellij.psi.search.FilenameIndex;
12 | import com.intellij.psi.search.GlobalSearchScope;
13 | import com.intellij.psi.util.PsiTreeUtil;
14 | import com.jetbrains.php.lang.PhpFileType;
15 | import com.jetbrains.php.lang.psi.elements.*;
16 | import com.jetbrains.smarty.SmartyFileType;
17 | import de.espend.idea.oxid.OxidProjectComponent;
18 | import de.espend.idea.oxid.dict.metadata.MetadataSetting;
19 | import de.espend.idea.oxid.utils.*;
20 | import fr.adrienbrault.idea.symfony2plugin.util.MethodMatcher;
21 | import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
22 | import org.apache.commons.lang.StringUtils;
23 | import org.jetbrains.annotations.NotNull;
24 | import org.jetbrains.annotations.Nullable;
25 |
26 | import java.util.ArrayList;
27 | import java.util.Collection;
28 |
29 | /**
30 | * @author Daniel Espendiller
31 | */
32 | public class PhpGoToHandler implements GotoDeclarationHandler {
33 |
34 | @Nullable
35 | @Override
36 | public PsiElement[] getGotoDeclarationTargets(PsiElement psiElement, int i, Editor editor) {
37 |
38 | if(!OxidProjectComponent.isValidForProject(psiElement)) {
39 | return new PsiElement[0];
40 | }
41 |
42 | Collection psiElements = new ArrayList();
43 |
44 | PsiElement parent = psiElement.getParent();
45 | if(parent instanceof StringLiteralExpression) {
46 |
47 | // in metadata.php
48 | if(MetadataUtil.getMetadataFilePattern().accepts(parent)) {
49 |
50 | // ['extend' => ['key' => ''] ]
51 | if(PhpMetadataUtil.isModuleKeyInFlatArray((StringLiteralExpression) parent, "extend")) {
52 | attachMetadataExtends((StringLiteralExpression) parent, psiElements);
53 | }
54 |
55 | // ['files' => [...] ]
56 | if(PhpMetadataUtil.isModuleKeyInFlatArray((StringLiteralExpression) parent, "files")) {
57 | attachAllFileTypes((StringLiteralExpression) parent, psiElements, PhpFileType.INSTANCE);
58 | }
59 |
60 | // ['templates' => [...] ]
61 | if(PhpMetadataUtil.isModuleKeyInFlatArray((StringLiteralExpression) parent, "templates")) {
62 | attachAllFileTypes((StringLiteralExpression) parent, psiElements, SmartyFileType.INSTANCE);
63 | }
64 |
65 | // ['extend' => ['file' => ''] ]
66 | if(PhpMetadataUtil.isInTemplateWithKey((StringLiteralExpression) parent, "file")) {
67 | attachTemplateFileTypes((StringLiteralExpression) parent, psiElements);
68 | }
69 |
70 | // "blocks" => [{"template" => 'foo'}]
71 | if(PhpMetadataUtil.isInTemplateWithKey((StringLiteralExpression) parent, "template")) {
72 | attachTemplateFile((StringLiteralExpression) parent, psiElements);
73 | }
74 |
75 | // "blocks" => [{"block" => 'foo'}]
76 | if(PhpMetadataUtil.isInTemplateWithKey((StringLiteralExpression) parent, "block")) {
77 | attachTemplateBlocks((StringLiteralExpression) parent, psiElements);
78 | }
79 |
80 | // ['extend' => ['key'] ]
81 | if (PhpMetadataUtil.isExtendKey((StringLiteralExpression) parent)) {
82 | attachFactoryClasses((StringLiteralExpression) parent, psiElements);
83 | }
84 |
85 | }
86 |
87 | // \oxConfig::getConfigParam()
88 | // \oxConfig::setConfigParam()
89 | if(new MethodMatcher.StringParameterRecursiveMatcher(parent, 0)
90 | .withSignature("\\oxConfig", "getConfigParam")
91 | .withSignature("\\oxConfig", "getConfigParam").match() != null) {
92 |
93 | attachConfigs((StringLiteralExpression) parent, psiElements);
94 | }
95 |
96 | // \oxLang::translateString()
97 | if (new MethodMatcher.StringParameterRecursiveMatcher(parent, 0)
98 | .withSignature("\\oxLang", "translateString")
99 | .match() != null) {
100 |
101 | attachTranslations((StringLiteralExpression) parent, psiElements);
102 | }
103 |
104 | // oxNew();
105 | // oxRegistry::get();
106 | if (OxidUtil.isFactory((StringLiteralExpression) parent)) {
107 | attachFactoryClasses((StringLiteralExpression) parent, psiElements);
108 | }
109 |
110 | }
111 |
112 | return psiElements.toArray(new PsiElement[psiElements.size()]);
113 | }
114 |
115 | private void attachFactoryClasses(StringLiteralExpression parent, Collection psiElements) {
116 |
117 | String contents = parent.getContents();
118 | if(StringUtils.isBlank(contents)) {
119 | return;
120 | }
121 |
122 | PhpClass classInterface = PhpElementsUtil.getClassInterface(parent.getProject(), contents);
123 | if(classInterface == null) {
124 | return;
125 | }
126 |
127 | psiElements.add(classInterface);
128 | }
129 |
130 | private void attachTranslations(@NotNull StringLiteralExpression parent, @NotNull Collection psiElements) {
131 |
132 | String contents = parent.getContents();
133 | if(StringUtils.isBlank(contents)) {
134 | return;
135 | }
136 |
137 | psiElements.addAll(TranslationUtil.getTranslationTargets(parent.getProject(), contents));
138 | }
139 |
140 | private void attachConfigs(@NotNull StringLiteralExpression parent, @NotNull Collection psiElements) {
141 |
142 | String contents = parent.getContents();
143 | if(StringUtils.isBlank(contents)) {
144 | return;
145 | }
146 |
147 | for (PsiFile psiFile : FilenameIndex.getFilesByName(parent.getProject(), "metadata.php", GlobalSearchScope.allScope(parent.getProject()))) {
148 | for (MetadataSetting setting : MetadataUtil.getSettings(psiFile)) {
149 | if (contents.equals(setting.getName())) {
150 | psiElements.add(setting.getSource());
151 | }
152 | }
153 | }
154 | }
155 |
156 | private void attachTemplateBlocks(@NotNull final StringLiteralExpression psiElement, @NotNull final Collection psiElements) {
157 |
158 | final String contents = getPathFormattedString(psiElement);
159 | if (contents == null) {
160 | return;
161 | }
162 |
163 |
164 | ArrayCreationExpression arrayCreation = PsiTreeUtil.getParentOfType(psiElement, ArrayCreationExpression.class);
165 | if(arrayCreation == null) {
166 | return;
167 | }
168 |
169 | String template = PhpElementsUtil.getArrayValueString(arrayCreation, "template");
170 | if(template == null) {
171 | return;
172 | }
173 |
174 | for (SmartyBlockUtil.SmartyBlock block : TemplateUtil.getBlocksTemplateName(psiElement.getProject(), template)) {
175 | if(block.getName().equals(contents)) {
176 | psiElements.add(block.getElement());
177 | }
178 | }
179 |
180 | }
181 |
182 | private void attachTemplateFile(@NotNull final StringLiteralExpression psiElement, @NotNull final Collection psiElements) {
183 |
184 | final String contents = getPathFormattedString(psiElement);
185 | if (contents == null) {
186 | return;
187 | }
188 |
189 | TemplateUtil.collectFiles(psiElement.getProject(), new TemplateUtil.SmartyTemplateVisitor() {
190 | @Override
191 | public void visitFile(VirtualFile virtualFile, String fileName) {
192 | if(contents.equalsIgnoreCase(fileName)) {
193 |
194 | PsiFile file = PsiManager.getInstance(psiElement.getProject()).findFile(virtualFile);
195 | if(file != null) {
196 | psiElements.add(file);
197 | }
198 |
199 | }
200 | }
201 | });
202 |
203 | }
204 |
205 | public static void attachTemplateFileTypes(StringLiteralExpression psiElement, Collection psiElements) {
206 |
207 | final String contents = getPathFormattedString(psiElement);
208 | if (contents == null) {
209 | return;
210 | }
211 |
212 | ModuleUtil.visitModuleTemplatesInMetadataScope(psiElement.getContainingFile(), new ContentEqualModuleFileVisitor(SmartyFileType.INSTANCE, contents, psiElement, psiElements));
213 | }
214 |
215 | public static void attachMetadataExtends(@NotNull final StringLiteralExpression psiElement, final @NotNull Collection psiElements) {
216 |
217 | final String contents = getPathFormattedString(psiElement);
218 | if (contents == null) {
219 | return;
220 | }
221 |
222 | ModuleUtil.visitModuleFile(psiElement.getContainingFile(), new ModuleUtil.ModuleFileVisitor() {
223 | @Override
224 | public void visit(@NotNull VirtualFile virtualFile, @NotNull String relativePath) {
225 |
226 | if (virtualFile.getFileType() != PhpFileType.INSTANCE || !relativePath.toLowerCase().endsWith(".php") || !contents.equalsIgnoreCase(relativePath.substring(0, relativePath.length() - 4))) {
227 | return;
228 | }
229 |
230 | PsiFile file = PsiManager.getInstance(psiElement.getProject()).findFile(virtualFile);
231 | if(file != null) {
232 | psiElements.add(file);
233 | }
234 |
235 | }
236 | });
237 |
238 | }
239 |
240 | public static void attachAllFileTypes(@NotNull final StringLiteralExpression psiElement, final @NotNull Collection psiElements, final FileType fileType) {
241 |
242 | final String contents = getPathFormattedString(psiElement);
243 | if (contents == null) {
244 | return;
245 | }
246 |
247 | ModuleUtil.visitModuleFile(psiElement.getContainingFile(), new ContentEqualModuleFileVisitor(fileType, contents, psiElement, psiElements));
248 | }
249 |
250 | @Nullable
251 | private static String getPathFormattedString(@NotNull StringLiteralExpression psiElement) {
252 | String contents = psiElement.getContents();
253 | if(StringUtils.isBlank(contents)) {
254 | return null;
255 | }
256 |
257 | if(contents.startsWith("/")) {
258 | contents = contents.substring(1);
259 | }
260 |
261 | return contents;
262 | }
263 |
264 | @Nullable
265 | @Override
266 | public String getActionText(DataContext dataContext) {
267 | return null;
268 | }
269 |
270 | private static class ContentEqualModuleFileVisitor implements ModuleUtil.ModuleFileVisitor {
271 | private final FileType fileType;
272 | private final String contents;
273 | private final StringLiteralExpression psiElement;
274 | private final Collection psiElements;
275 |
276 | public ContentEqualModuleFileVisitor(FileType fileType, String contents, StringLiteralExpression psiElement, Collection psiElements) {
277 | this.fileType = fileType;
278 | this.contents = contents;
279 | this.psiElement = psiElement;
280 | this.psiElements = psiElements;
281 | }
282 |
283 | @Override
284 | public void visit(@NotNull VirtualFile virtualFile, @NotNull String relativePath) {
285 |
286 | if (virtualFile.getFileType() != fileType || !contents.equalsIgnoreCase(relativePath)) {
287 | return;
288 | }
289 |
290 | PsiFile file = PsiManager.getInstance(psiElement.getProject()).findFile(virtualFile);
291 | if(file != null) {
292 | psiElements.add(file);
293 | }
294 |
295 | }
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/navigation/SmartyGoToHandler.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.navigation;
2 |
3 | import com.intellij.codeInsight.navigation.actions.GotoDeclarationHandler;
4 | import com.intellij.openapi.actionSystem.DataContext;
5 | import com.intellij.openapi.editor.Editor;
6 | import com.intellij.openapi.project.Project;
7 | import com.intellij.openapi.vfs.VirtualFile;
8 | import com.intellij.psi.PsiElement;
9 | import com.intellij.psi.PsiFile;
10 | import com.intellij.psi.PsiManager;
11 | import de.espend.idea.oxid.OxidProjectComponent;
12 | import de.espend.idea.oxid.utils.SmartyBlockUtil;
13 | import de.espend.idea.oxid.utils.SmartyPattern;
14 | import de.espend.idea.oxid.utils.TemplateUtil;
15 | import de.espend.idea.oxid.utils.TranslationUtil;
16 | import org.apache.commons.lang.StringUtils;
17 | import org.jetbrains.annotations.NotNull;
18 | import org.jetbrains.annotations.Nullable;
19 |
20 | import java.util.ArrayList;
21 | import java.util.Collection;
22 |
23 | /**
24 | * @author Daniel Espendiller
25 | */
26 | public class SmartyGoToHandler implements GotoDeclarationHandler {
27 | @Nullable
28 | @Override
29 | public PsiElement[] getGotoDeclarationTargets(@Nullable PsiElement psiElement, int i, Editor editor) {
30 |
31 | if(!OxidProjectComponent.isValidForProject(psiElement)) {
32 | return new PsiElement[0];
33 | }
34 |
35 | Collection psiElements = new ArrayList();
36 |
37 | if(SmartyPattern.getFilePattern().accepts(psiElement)) {
38 | attachFiles(psiElements, psiElement);
39 | }
40 |
41 | if(SmartyPattern.getBlockPattern().accepts(psiElement)) {
42 | attachBlocks(psiElements, psiElement);
43 | }
44 |
45 | if(SmartyPattern.getAttributeInsideTagPattern("ident", "oxmultilang").accepts(psiElement)) {
46 | attachTranslations(psiElements, psiElement);
47 | }
48 |
49 | if(SmartyPattern.getAttributeInsideTagPattern("ident", "oxcontent").accepts(psiElement)) {
50 | attachContentIdent(psiElements, psiElement);
51 | }
52 |
53 | return psiElements.toArray(new PsiElement[psiElements.size()]);
54 | }
55 |
56 | private void attachContentIdent(@NotNull Collection psiElements, @NotNull PsiElement psiElement) {
57 | final String contents = psiElement.getText();
58 | if(StringUtils.isBlank(contents)) {
59 | return;
60 | }
61 |
62 | psiElements.addAll(TemplateUtil.getContentIdentsTargets(psiElement.getProject(), psiElement.getContainingFile().getVirtualFile(), contents));
63 | }
64 |
65 | private void attachTranslations(@NotNull Collection psiElements, @NotNull PsiElement psiElement) {
66 |
67 | final String contents = psiElement.getText();
68 | if(StringUtils.isBlank(contents)) {
69 | return;
70 | }
71 |
72 | psiElements.addAll(TranslationUtil.getTranslationTargets(psiElement.getProject(), contents));
73 | }
74 |
75 | private void attachBlocks(@NotNull Collection psiElements, @NotNull PsiElement psiElement) {
76 |
77 | final String contents = psiElement.getText();
78 | if(StringUtils.isBlank(contents)) {
79 | return;
80 | }
81 |
82 | for (String templateName: TemplateUtil.getTemplateNames(psiElement.getProject(), psiElement.getContainingFile().getVirtualFile())) {
83 | for (SmartyBlockUtil.SmartyBlock block : TemplateUtil.getBlocksTemplateName(psiElement.getProject(), templateName)) {
84 | if(block.getName().equals(contents)) {
85 | psiElements.add(block.getElement());
86 | }
87 | }
88 | }
89 | }
90 |
91 | private void attachFiles(@NotNull Collection psiElements, @NotNull PsiElement psiElement) {
92 |
93 | final String text = psiElement.getText();
94 | if(StringUtils.isBlank(text)) {
95 | return;
96 | }
97 |
98 | attachTemplateFiles(psiElement.getProject(), text, psiElements);
99 | }
100 |
101 | @Nullable
102 | @Override
103 | public String getActionText(DataContext dataContext) {
104 | return null;
105 | }
106 |
107 | private void attachTemplateFiles(final Project project, final @NotNull String s, final @NotNull Collection psiElements) {
108 | TemplateUtil.collectFiles(project, new TemplateUtil.SmartyTemplateVisitor() {
109 | @Override
110 | public void visitFile(VirtualFile virtualFile, String fileName) {
111 | if(s.equals(fileName)) {
112 | PsiFile file = PsiManager.getInstance(project).findFile(virtualFile);
113 | if(file != null) {
114 | psiElements.add(file);
115 | }
116 | }
117 | }
118 | });
119 | }
120 |
121 | }
122 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/navigation/SmartyTemplateLineMarkerProvider.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.navigation;
2 |
3 | import com.intellij.codeInsight.daemon.LineMarkerInfo;
4 | import com.intellij.codeInsight.daemon.LineMarkerProvider;
5 | import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo;
6 | import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder;
7 | import com.intellij.navigation.GotoRelatedItem;
8 | import com.intellij.openapi.vfs.VfsUtil;
9 | import com.intellij.openapi.vfs.VirtualFile;
10 | import com.intellij.psi.PsiElement;
11 | import com.intellij.psi.PsiFile;
12 | import com.intellij.psi.PsiManager;
13 | import com.intellij.util.ConstantFunction;
14 | import com.jetbrains.php.PhpIcons;
15 | import com.jetbrains.smarty.SmartyFile;
16 | import de.espend.idea.oxid.OxidPluginIcons;
17 | import de.espend.idea.oxid.OxidProjectComponent;
18 | import de.espend.idea.oxid.utils.SmartyBlockUtil;
19 | import de.espend.idea.oxid.utils.SmartyPattern;
20 | import de.espend.idea.oxid.utils.TemplateUtil;
21 | import fr.adrienbrault.idea.symfony2plugin.dic.RelatedPopupGotoLineMarker;
22 | import org.apache.commons.lang.StringUtils;
23 | import org.jetbrains.annotations.NotNull;
24 | import org.jetbrains.annotations.Nullable;
25 |
26 | import javax.swing.*;
27 | import java.util.*;
28 |
29 | /**
30 | * @author Daniel Espendiller
31 | */
32 | public class SmartyTemplateLineMarkerProvider implements LineMarkerProvider {
33 |
34 | @Nullable
35 | @Override
36 | public LineMarkerInfo getLineMarkerInfo(@NotNull PsiElement psiElement) {
37 | return null;
38 | }
39 |
40 | @Override
41 | public void collectSlowLineMarkers(@NotNull List psiElements, @NotNull Collection collection) {
42 |
43 | if(psiElements.size() == 0 || !OxidProjectComponent.isValidForProject(psiElements.get(0))) {
44 | return;
45 | }
46 |
47 | for(PsiElement psiElement: psiElements) {
48 |
49 | if (psiElement instanceof SmartyFile) {
50 | attachFileContextMaker((SmartyFile) psiElement, collection);
51 | }
52 |
53 | if(SmartyPattern.getBlockPattern().accepts(psiElement)) {
54 | attachBlocks(psiElement, collection);
55 | }
56 |
57 | }
58 | }
59 |
60 | private void attachBlocks(PsiElement psiElement, Collection lineMarkerInfos) {
61 |
62 | final String contents = psiElement.getText();
63 | if(StringUtils.isBlank(contents)) {
64 | return;
65 | }
66 |
67 | VirtualFile virtualFile = psiElement.getContainingFile().getVirtualFile();
68 |
69 | Collection psiElements = new ArrayList();
70 |
71 | for (String templateName: TemplateUtil.getTemplateNames(psiElement.getProject(), psiElement.getContainingFile().getVirtualFile())) {
72 | for (SmartyBlockUtil.SmartyBlock block : TemplateUtil.getBlocksTemplateName(psiElement.getProject(), templateName)) {
73 | if(block.getName().equals(contents)) {
74 | if(!virtualFile.equals(block.getElement().getContainingFile().getVirtualFile())) {
75 | psiElements.add(block.getElement());
76 | }
77 | }
78 | }
79 | }
80 |
81 | if(psiElements.size() == 0) {
82 | return;
83 | }
84 |
85 | NavigationGutterIconBuilder builder = NavigationGutterIconBuilder.create(PhpIcons.OVERRIDES).
86 | setTargets(psiElements).
87 | setTooltipText("Navigate to block");
88 |
89 | lineMarkerInfos.add(builder.createLineMarkerInfo(psiElement));
90 | }
91 |
92 | private void attachFileContextMaker(SmartyFile smartyFile, @NotNull Collection lineMarkerInfo) {
93 |
94 | final VirtualFile virtualFile = smartyFile.getVirtualFile();
95 |
96 | final Set templates = TemplateUtil.getTemplateNames(smartyFile.getProject(), virtualFile);
97 | if(templates.size() == 0) {
98 | return;
99 | }
100 |
101 | List gotoRelatedItems = new ArrayList();
102 |
103 | for (String template : templates) {
104 | for (VirtualFile file : TemplateUtil.getFilesByTemplateName(smartyFile.getProject(), template)) {
105 |
106 | if(file.equals(virtualFile)) {
107 | continue;
108 | }
109 |
110 | PsiFile psiFile = PsiManager.getInstance(smartyFile.getProject()).findFile(file);
111 | if(psiFile != null) {
112 | String templateName = getPresentableTemplateName(smartyFile, file);
113 | gotoRelatedItems.add(new RelatedPopupGotoLineMarker.PopupGotoRelatedItem(psiFile, templateName).withIcon(file.getFileType().getIcon(), OxidPluginIcons.OXID_LINEMARKER));
114 | }
115 | }
116 | }
117 |
118 | if(gotoRelatedItems.size() == 0) {
119 | return;
120 | }
121 |
122 | // only one item dont need popover
123 | if(gotoRelatedItems.size() == 1) {
124 | lineMarkerInfo.add(getSingleLineMarker(smartyFile, lineMarkerInfo, gotoRelatedItems.get(0)));
125 | return;
126 | }
127 |
128 | if(gotoRelatedItems.size() == 0) {
129 | return;
130 | }
131 |
132 | lineMarkerInfo.add(getRelatedPopover("Overwrite", "Overwrites", smartyFile, gotoRelatedItems));
133 | }
134 |
135 | private LineMarkerInfo getRelatedPopover(String singleItemTitle, String singleItemTooltipPrefix, PsiElement lineMarkerTarget, List gotoRelatedItems) {
136 |
137 | // single item has no popup
138 | String title = singleItemTitle;
139 | if(gotoRelatedItems.size() == 1) {
140 | String customName = gotoRelatedItems.get(0).getCustomName();
141 | if(customName != null) {
142 | title = String.format(singleItemTooltipPrefix, customName);
143 | }
144 | }
145 |
146 | return new LineMarkerInfo(lineMarkerTarget, lineMarkerTarget.getTextOffset(), OxidPluginIcons.OXID_LINEMARKER, 6, new ConstantFunction(title), new fr.adrienbrault.idea.symfony2plugin.dic.RelatedPopupGotoLineMarker.NavigationHandler(gotoRelatedItems));
147 | }
148 |
149 | public static RelatedItemLineMarkerInfo getSingleLineMarker(SmartyFile smartyFile, Collection lineMarkerInfos, GotoRelatedItem gotoRelatedItem) {
150 |
151 | // hell: find any possible small icon
152 | Icon icon = null;
153 | if(gotoRelatedItem instanceof RelatedPopupGotoLineMarker.PopupGotoRelatedItem) {
154 | icon = ((RelatedPopupGotoLineMarker.PopupGotoRelatedItem) gotoRelatedItem).getSmallIcon();
155 | }
156 |
157 | if(icon == null) {
158 | icon = OxidPluginIcons.OXID_LINEMARKER;
159 | }
160 |
161 | NavigationGutterIconBuilder builder = NavigationGutterIconBuilder.create(icon).
162 | setTargets(gotoRelatedItem.getElement());
163 |
164 | String customName = gotoRelatedItem.getCustomName();
165 | if(customName != null) {
166 | builder.setTooltipText(customName);
167 | }
168 |
169 | return builder.createLineMarkerInfo(smartyFile);
170 | }
171 |
172 | private String getPresentableTemplateName(SmartyFile smartyFile, VirtualFile file) {
173 | String templateName = file.getPath();
174 |
175 | String relativePath = VfsUtil.getRelativePath(file, smartyFile.getProject().getBaseDir());
176 | if(relativePath != null) {
177 | templateName = relativePath;
178 | }
179 |
180 | int i = templateName.indexOf("/views/");
181 | if(i > 0) {
182 | templateName = templateName.substring(i + "/views/".length());
183 | }
184 |
185 | if(templateName.length() > 50) {
186 | templateName = "..." + templateName.substring(templateName.length() - 50);
187 | }
188 | return templateName;
189 | }
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/stub/OxidContentIdentIndexer.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.stub;
2 |
3 | import com.intellij.psi.PsiElement;
4 | import com.intellij.psi.PsiFile;
5 | import com.intellij.psi.PsiRecursiveElementWalkingVisitor;
6 | import com.intellij.util.indexing.*;
7 | import com.intellij.util.io.DataExternalizer;
8 | import com.intellij.util.io.EnumeratorStringDescriptor;
9 | import com.intellij.util.io.KeyDescriptor;
10 | import com.intellij.util.io.VoidDataExternalizer;
11 | import com.jetbrains.smarty.SmartyFileType;
12 | import de.espend.idea.oxid.utils.SmartyPattern;
13 | import gnu.trove.THashMap;
14 | import org.apache.commons.lang.StringUtils;
15 | import org.jetbrains.annotations.NotNull;
16 |
17 | import java.util.Map;
18 |
19 | /**
20 | * @author Daniel Espendiller
21 | */
22 | public class OxidContentIdentIndexer extends FileBasedIndexExtension {
23 |
24 | public static final ID KEY = ID.create("de.espend.idea.oxid.oxid_content_ident");
25 | private final KeyDescriptor myKeyDescriptor = new EnumeratorStringDescriptor();
26 |
27 | @NotNull
28 | @Override
29 | public ID getName() {
30 | return KEY;
31 | }
32 |
33 | @NotNull
34 | @Override
35 | public DataIndexer getIndexer() {
36 |
37 | return new DataIndexer() {
38 | @NotNull
39 | @Override
40 | public Map map(@NotNull FileContent inputData) {
41 |
42 | PsiFile psiFile = inputData.getPsiFile();
43 | final Map map = new THashMap<>();
44 |
45 | psiFile.acceptChildren(new PsiRecursiveElementWalkingVisitor() {
46 | @Override
47 | public void visitElement(PsiElement element) {
48 |
49 | if(SmartyPattern.getAttributeInsideTagPattern("ident", "oxcontent").accepts(element)) {
50 | String content = element.getText();
51 | if(StringUtils.isNotBlank(content)) {
52 | map.put(content, null);
53 | }
54 | }
55 |
56 | super.visitElement(element);
57 | }
58 |
59 | });
60 |
61 | return map;
62 | }
63 | };
64 |
65 | }
66 |
67 | @NotNull
68 | @Override
69 | public KeyDescriptor getKeyDescriptor() {
70 | return this.myKeyDescriptor;
71 | }
72 |
73 | @NotNull
74 | @Override
75 | public DataExternalizer getValueExternalizer() {
76 | return VoidDataExternalizer.INSTANCE;
77 | }
78 |
79 | @NotNull
80 | @Override
81 | public FileBasedIndex.InputFilter getInputFilter() {
82 | return file -> file.getFileType() == SmartyFileType.INSTANCE;
83 | }
84 |
85 | @Override
86 | public boolean dependsOnFileContent() {
87 | return true;
88 | }
89 |
90 | @Override
91 | public int getVersion() {
92 | return 1;
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/types/OxidFactoryTypeProvider.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.types;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.vfs.VirtualFile;
5 | import com.intellij.psi.PsiElement;
6 | import com.intellij.psi.PsiFile;
7 | import com.intellij.psi.PsiManager;
8 | import com.jetbrains.php.PhpIndex;
9 | import com.jetbrains.php.lang.psi.PhpFile;
10 | import com.jetbrains.php.lang.psi.PhpPsiUtil;
11 | import com.jetbrains.php.lang.psi.elements.*;
12 | import com.jetbrains.php.lang.psi.resolve.types.PhpType;
13 | import com.jetbrains.php.lang.psi.resolve.types.PhpTypeProvider3;
14 | import de.espend.idea.oxid.utils.ModuleUtil;
15 | import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
16 | import fr.adrienbrault.idea.symfony2plugin.util.PhpTypeProviderUtil;
17 | import org.apache.commons.lang.StringUtils;
18 | import org.jetbrains.annotations.Nullable;
19 |
20 | import java.util.*;
21 |
22 | public class OxidFactoryTypeProvider implements PhpTypeProvider3 {
23 | final private static char TRIM_KEY = '\u0180';
24 |
25 | @Override
26 | public char getKey() {
27 | return '\u0169';
28 | }
29 |
30 | @Nullable
31 | @Override
32 | public PhpType getType(PsiElement e) {
33 | if(e instanceof FunctionReference && "oxNew".equalsIgnoreCase(((FunctionReference) e).getName())) {
34 | PsiElement[] parameters = ((FunctionReference) e).getParameters();
35 | if(parameters.length > 0 && parameters[0] instanceof StringLiteralExpression) {
36 | String contents = ((StringLiteralExpression) parameters[0]).getContents();
37 | if(StringUtils.isNotBlank(contents)) {
38 | String signature = PhpTypeProviderUtil.getReferenceSignatureByFirstParameter((FunctionReference) e, TRIM_KEY);
39 | return signature == null ? null : new PhpType().add("#" + this.getKey() + signature);
40 | }
41 | }
42 | }
43 |
44 | // container calls are only on "get" methods
45 | if(e instanceof MethodReference && PhpElementsUtil.isMethodWithFirstStringOrFieldReference(e, "get")) {
46 | String signature = PhpTypeProviderUtil.getReferenceSignatureByFirstParameter((FunctionReference) e, TRIM_KEY);
47 | return signature == null ? null : new PhpType().add("#" + this.getKey() + signature);
48 | }
49 |
50 | return null;
51 | }
52 |
53 | @Override
54 | public Collection extends PhpNamedElement> getBySignature(String expression, Set visited, int depth, Project project) {
55 |
56 | int endIndex = expression.lastIndexOf(TRIM_KEY);
57 | if(endIndex == -1) {
58 | return Collections.emptySet();
59 | }
60 |
61 | String originalSignature = expression.substring(0, endIndex);
62 | String parameter = expression.substring(endIndex + 1);
63 |
64 | // search for called method
65 | PhpIndex phpIndex = PhpIndex.getInstance(project);
66 | Collection extends PhpNamedElement> phpNamedElementCollections = phpIndex.getBySignature(originalSignature, null, 0);
67 | if(phpNamedElementCollections.size() == 0) {
68 | return Collections.emptySet();
69 | }
70 |
71 | // get first matched item
72 | PhpNamedElement phpNamedElement = phpNamedElementCollections.iterator().next();
73 | if(!(phpNamedElement instanceof Function)) {
74 | return phpNamedElementCollections;
75 | }
76 |
77 | parameter = PhpTypeProviderUtil.getResolvedParameter(phpIndex, parameter);
78 | if(parameter == null) {
79 | return phpNamedElementCollections;
80 | }
81 |
82 | PhpClass phpClass = PhpElementsUtil.getClassInterface(project, parameter);
83 | if(phpClass != null) {
84 |
85 |
86 | Collection phpClasses = new ArrayList();
87 | phpClasses.add(phpClass);
88 |
89 | addExtendsClasses(project, parameter, phpClasses);
90 |
91 | return phpClasses;
92 | }
93 |
94 | return null;
95 | }
96 |
97 | /**
98 | *
99 | * We support "extends" on module metadata
100 | *
101 | * TODO: use index, this in a performance issue
102 | */
103 | private void addExtendsClasses(Project project, String parameter, Collection phpClasses) {
104 |
105 | for (Map.Entry> entry : ModuleUtil.getExtendsList(project).entrySet()) {
106 |
107 | // ignore cases, so we need an each
108 | if(!entry.getKey().equalsIgnoreCase(parameter)) {
109 | continue;
110 | }
111 |
112 | for (VirtualFile virtualFile : entry.getValue()) {
113 |
114 | PsiFile file = PsiManager.getInstance(project).findFile(virtualFile);
115 | if (!(file instanceof PhpFile)) {
116 | continue;
117 | }
118 |
119 | Collection allClasses = PhpPsiUtil.findAllClasses((PhpFile) file);
120 | if (allClasses.size() > 0) {
121 | phpClasses.add(allClasses.iterator().next());
122 | }
123 |
124 | }
125 | }
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/utils/MetadataUtil.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.utils;
2 |
3 | import com.intellij.openapi.vfs.VirtualFile;
4 | import com.intellij.patterns.PlatformPatterns;
5 | import com.intellij.patterns.PsiElementPattern;
6 | import com.intellij.psi.PsiElement;
7 | import com.intellij.psi.PsiFile;
8 | import com.intellij.psi.util.PsiTreeUtil;
9 | import com.jetbrains.php.lang.parser.PhpElementTypes;
10 | import com.jetbrains.php.lang.psi.elements.ArrayCreationExpression;
11 | import com.jetbrains.php.lang.psi.elements.GroupStatement;
12 | import com.jetbrains.php.lang.psi.elements.PhpPsiElement;
13 | import com.jetbrains.php.lang.psi.elements.Statement;
14 | import com.jetbrains.php.lang.psi.elements.impl.AssignmentExpressionImpl;
15 | import de.espend.idea.oxid.dict.metadata.MetadataBlock;
16 | import de.espend.idea.oxid.dict.metadata.MetadataSetting;
17 | import fr.adrienbrault.idea.symfony2plugin.util.PhpElementsUtil;
18 | import org.apache.commons.lang.StringUtils;
19 | import org.jetbrains.annotations.NotNull;
20 | import org.jetbrains.annotations.Nullable;
21 |
22 | import java.util.ArrayList;
23 | import java.util.Collection;
24 | import java.util.HashMap;
25 | import java.util.Map;
26 |
27 | /**
28 | * @author Daniel Espendiller
29 | */
30 | public class MetadataUtil {
31 |
32 | public static PsiElementPattern.Capture getMetadataFilePattern() {
33 | return PlatformPatterns.psiElement().inVirtualFile(PlatformPatterns.virtualFile().withName("metadata.php"));
34 | }
35 |
36 | public static Collection getSettings(@NotNull PsiFile psiFile) {
37 |
38 | final Collection blocks = new ArrayList();
39 |
40 | visitMetadataKey(psiFile, "settings", new MetadataKeyVisitor() {
41 | @Override
42 | public void visit(@NotNull ArrayCreationExpression arrayCreationExpression) {
43 |
44 | for (PhpPsiElement phpPsiElement : PsiTreeUtil.getChildrenOfTypeAsList(arrayCreationExpression, PhpPsiElement.class)) {
45 |
46 | if(phpPsiElement.getNode().getElementType() != PhpElementTypes.ARRAY_VALUE) {
47 | continue;
48 | }
49 |
50 | PhpPsiElement firstPsiChild = phpPsiElement.getFirstPsiChild();
51 | if(!(firstPsiChild instanceof ArrayCreationExpression)) {
52 | continue;
53 | }
54 |
55 | MetadataSetting block = MetadataSetting.create(firstPsiChild, getArrayKeyValueMapProxy((ArrayCreationExpression) firstPsiChild));
56 | if(block != null) {
57 | blocks.add(block);
58 | }
59 |
60 | }
61 |
62 | }
63 | });
64 |
65 | return blocks;
66 | }
67 |
68 | public static Collection getBlocks(@NotNull PsiFile psiFile) {
69 |
70 | final Collection blocks = new ArrayList();
71 |
72 | visitMetadataKey(psiFile, "blocks", new MetadataKeyVisitor() {
73 | @Override
74 | public void visit(@NotNull ArrayCreationExpression arrayCreationExpression) {
75 |
76 | for (PhpPsiElement phpPsiElement : PsiTreeUtil.getChildrenOfTypeAsList(arrayCreationExpression, PhpPsiElement.class)) {
77 |
78 | if(phpPsiElement.getNode().getElementType() != PhpElementTypes.ARRAY_VALUE) {
79 | continue;
80 | }
81 |
82 | PhpPsiElement firstPsiChild = phpPsiElement.getFirstPsiChild();
83 | if(!(firstPsiChild instanceof ArrayCreationExpression)) {
84 | continue;
85 | }
86 |
87 | MetadataBlock block = MetadataBlock.create(getArrayKeyValueMapProxy((ArrayCreationExpression) firstPsiChild));
88 | if(block != null) {
89 | blocks.add(block);
90 | }
91 |
92 | }
93 |
94 | }
95 | });
96 |
97 | return blocks;
98 | }
99 |
100 | public static Map getMetadataKeyMap(@NotNull PsiFile psiFile, @NotNull String key) {
101 |
102 | final Map values = new HashMap();
103 |
104 | visitMetadataKey(psiFile, key, new MetadataKeyVisitor() {
105 | @Override
106 | public void visit(@NotNull ArrayCreationExpression arrayCreationExpression) {
107 | values.putAll(getArrayKeyValueMapProxy(arrayCreationExpression));
108 | }
109 | });
110 |
111 | return values;
112 | }
113 |
114 |
115 | /**
116 | * GetArrayKeyValueMap adds null Value; proxy until fixed external
117 | */
118 | @NotNull
119 | private static Map getArrayKeyValueMapProxy(@NotNull ArrayCreationExpression arrayCreationExpression) {
120 |
121 | Map map = new HashMap();
122 |
123 | for (Map.Entry entry : PhpElementsUtil.getArrayKeyValueMap(arrayCreationExpression).entrySet()) {
124 | String value = entry.getValue();
125 | String key = entry.getKey();
126 | if(key != null && StringUtils.isNotBlank(key) && value != null && StringUtils.isNotBlank(value)) {
127 | map.put(entry.getKey(), entry.getValue());
128 | }
129 | }
130 |
131 | return map;
132 | }
133 |
134 | private static void visitMetadataKey(@NotNull PsiFile psiFile, @NotNull String key, @NotNull MetadataKeyVisitor visitor) {
135 |
136 | PsiElement childOfType = PsiTreeUtil.getChildOfType(psiFile, GroupStatement.class);
137 | if(childOfType == null) {
138 | return;
139 | }
140 |
141 | for (Statement statement : PsiTreeUtil.getChildrenOfTypeAsList(childOfType, Statement.class)) {
142 | PsiElement assignmentExpr = statement.getFirstPsiChild();
143 | if(assignmentExpr instanceof AssignmentExpressionImpl) {
144 | PhpPsiElement variable = ((AssignmentExpressionImpl) assignmentExpr).getVariable();
145 | if(variable != null && "aModule".equals(variable.getName())) {
146 |
147 | PhpPsiElement value = ((AssignmentExpressionImpl) assignmentExpr).getValue();
148 | if(value instanceof ArrayCreationExpression) {
149 | PhpPsiElement arrayCreationKeyMap = PhpElementsUtil.getArrayValue((ArrayCreationExpression) value, key);
150 | if(arrayCreationKeyMap instanceof ArrayCreationExpression) {
151 | visitor.visit((ArrayCreationExpression) arrayCreationKeyMap);
152 | }
153 | }
154 | }
155 | }
156 | }
157 | }
158 |
159 | public static void visitTranslationKey(@NotNull PsiFile psiFile, @NotNull TranslationKeyVisitor visitor) {
160 |
161 | PsiElement childOfType = PsiTreeUtil.getChildOfType(psiFile, GroupStatement.class);
162 | if(childOfType == null) {
163 | return;
164 | }
165 |
166 | Statement[] childrenOfType = PsiTreeUtil.getChildrenOfType(childOfType, Statement.class);
167 | if(childrenOfType == null) {
168 | return;
169 | }
170 |
171 | for (Statement statement : childrenOfType) {
172 | PsiElement assignmentExpr = statement.getFirstPsiChild();
173 | if(assignmentExpr instanceof AssignmentExpressionImpl) {
174 | PhpPsiElement variable = ((AssignmentExpressionImpl) assignmentExpr).getVariable();
175 | if(variable != null && "aLang".equals(variable.getName())) {
176 |
177 | PhpPsiElement value = ((AssignmentExpressionImpl) assignmentExpr).getValue();
178 | if(value instanceof ArrayCreationExpression) {
179 | for (Map.Entry entry : PhpElementsUtil.getArrayCreationKeyMap((ArrayCreationExpression) value).entrySet()) {
180 | visitor.visit(entry.getKey(), entry.getValue());
181 | }
182 | }
183 | }
184 | }
185 | }
186 | }
187 |
188 | public interface TranslationKeyVisitor {
189 | void visit(@NotNull String name, @NotNull PsiElement value);
190 | }
191 |
192 | public interface MetadataKeyVisitor {
193 | void visit(@NotNull ArrayCreationExpression arrayCreationExpression);
194 | }
195 |
196 | /**
197 | * Find modules folder on "modules" or vendor structure
198 | *
199 | * @param file metadata file
200 | */
201 | @Nullable
202 | public static VirtualFile getModuleVendorFolderFromMetadata(@NotNull VirtualFile file) {
203 |
204 | // save previous we
205 | VirtualFile current = file;
206 |
207 | for (VirtualFile parent = file.getParent(); parent != null; parent = parent.getParent()) {
208 |
209 | if(parent.getName().equals("modules")) {
210 | return current;
211 | }
212 |
213 | if(parent.isDirectory() && parent.findChild("vendormetadata.php") != null) {
214 | return parent;
215 | }
216 |
217 | // we need to return previous PsiElement
218 | current = parent;
219 | }
220 |
221 | return null;
222 | }
223 |
224 | /**
225 | * getModuleVendorFolderFromMetadata delivers our topmost "vendor module" folder
226 | * We need to get the parent here, this should be our main path root
227 | */
228 | @Nullable
229 | public static VirtualFile getModuleDirectoryOnMetadata(@NotNull VirtualFile file) {
230 |
231 | VirtualFile modulesDir = getModuleVendorFolderFromMetadata(file);
232 | if(modulesDir == null) {
233 | return null;
234 | }
235 |
236 | // should be "modules" folder
237 | return modulesDir.getParent();
238 | }
239 |
240 | }
241 |
--------------------------------------------------------------------------------
/src/de/espend/idea/oxid/utils/ModuleUtil.java:
--------------------------------------------------------------------------------
1 | package de.espend.idea.oxid.utils;
2 |
3 | import com.intellij.openapi.project.Project;
4 | import com.intellij.openapi.util.Key;
5 | import com.intellij.openapi.vfs.VfsUtil;
6 | import com.intellij.openapi.vfs.VirtualFile;
7 | import com.intellij.openapi.vfs.VirtualFileVisitor;
8 | import com.intellij.psi.PsiDirectory;
9 | import com.intellij.psi.PsiFile;
10 | import com.intellij.psi.search.FilenameIndex;
11 | import com.intellij.psi.search.GlobalSearchScope;
12 | import com.intellij.psi.util.CachedValue;
13 | import com.intellij.psi.util.CachedValueProvider;
14 | import com.intellij.psi.util.CachedValuesManager;
15 | import com.intellij.psi.util.PsiModificationTracker;
16 | import com.jetbrains.smarty.SmartyFileType;
17 | import org.apache.commons.lang.StringUtils;
18 | import org.jetbrains.annotations.NotNull;
19 | import org.jetbrains.annotations.Nullable;
20 |
21 | import java.util.HashMap;
22 | import java.util.HashSet;
23 | import java.util.Map;
24 | import java.util.Set;
25 |
26 | /**
27 | * @author Daniel Espendiller
28 | */
29 | public class ModuleUtil {
30 |
31 | private static final Key>>> EXTEND_LIST_CACHE = new Key>>>("OXID_EXTEND_LIST_CACHE");
32 |
33 | public static void visitModuleTemplatesInMetadataScope(@NotNull PsiFile metaFile, final @NotNull ModuleFileVisitor visitor) {
34 |
35 | ModuleUtil.visitModuleFile(metaFile, new ModuleUtil.ModuleFileVisitor() {
36 | @Override
37 | public void visit(@NotNull VirtualFile virtualFile, @NotNull String relativePath) {
38 |
39 | if (virtualFile.getFileType() != SmartyFileType.INSTANCE) {
40 | return;
41 | }
42 |
43 | String[] split = relativePath.split("/");
44 | if(split.length <= 2) {
45 | return;
46 | }
47 |
48 | int n = split.length - 2;
49 | String[] newArray = new String[n];
50 | System.arraycopy(split, 2, newArray, 0, n);
51 |
52 | visitor.visit(virtualFile, StringUtils.join(newArray, "/"));
53 | }
54 | });
55 | }
56 |
57 | public static void visitModuleFile(@NotNull PsiFile metaFile, final @NotNull ModuleFileVisitor visitor) {
58 |
59 | final VirtualFile moduleFolder = MetadataUtil.getModuleVendorFolderFromMetadata(metaFile.getVirtualFile());
60 | if(moduleFolder == null) {
61 | return;
62 | }
63 |
64 | PsiDirectory parent = metaFile.getParent();
65 | if(parent == null) {
66 | return;
67 | }
68 |
69 | VfsUtil.visitChildrenRecursively(parent.getVirtualFile(), new VirtualFileVisitor() {
70 | @Override
71 | public boolean visitFile(@NotNull VirtualFile file) {
72 |
73 | if (file.isDirectory()) {
74 | return super.visitFile(file);
75 | }
76 |
77 | String relativePath = VfsUtil.getRelativePath(file, moduleFolder.getParent(), '/');
78 | if(relativePath == null) {
79 | return super.visitFile(file);
80 | }
81 |
82 | visitor.visit(file, relativePath);
83 |
84 | return super.visitFile(file);
85 | }
86 | });
87 | }
88 |
89 | public interface ModuleFileVisitor {
90 | void visit(@NotNull VirtualFile virtualFile, @NotNull String relativePath);
91 | }
92 |
93 | @NotNull
94 | synchronized public static Map> getExtendsList(@NotNull final Project project) {
95 |
96 | CachedValue