unhandled) {
+ unhandled.addAll(context.parserResult.getDiagnostics());
+ }
+
+ /**
+ * Cancel in-progress processing of hints.
+ */
+ @Override
+ public void cancel() {
+ //not required
+ }
+
+ /**
+ *
+ * Optional builtin Rules. Typically you don't use this; you register your
+ * rules in your filesystem layer in the gsf-hints/mimetype1/mimetype2
+ * folder, for example gsf-hints/text/x-ruby/. Error hints should go in the
+ * "errors" folder, selection hints should go in the "selection" folder, and
+ * all other hints should go in the "hints" folder (but note that you can
+ * create localized folders and organize them under hints; these categories
+ * are shown in the hints options panel. Hints returned from this method
+ * will be placed in the "general" folder.
+ *
+ *
+ * This method is primarily intended for rules that should be added
+ * dynamically, for example for Rules that have a many different flavors yet
+ * a single implementation class (such as JavaScript's StrictWarning rule
+ * which wraps a number of builtin parser warnings.)
+ *
+ * @return A list of rules that are builtin, or null or an empty list when
+ * there are no builtins
+ */
+ @Override
+ public List getBuiltinRules() {
+ return Collections.emptyList();
+ }
+
+ /**
+ * Create a RuleContext object specific to this HintsProvider. This lets
+ * implementations of this interface created subclasses of the RuleContext
+ * that can be passed around to all the executed rules.
+ *
+ * @return A new instance of a RuleContext object
+ */
+ @Override
+ public RuleContext createRuleContext() {
+ return new BladeRuleContext();
+ }
+
+ private static final class BladeRule implements ErrorRule {
+
+ private final HintSeverity severity;
+
+ private BladeRule(HintSeverity severity) {
+ this.severity = severity;
+ }
+
+ @Override
+ public Set> getCodes() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public boolean appliesTo(RuleContext context) {
+ return true;
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "blade"; //NOI18N
+ }
+
+ @Override
+ public boolean showInTasklist() {
+ return true;
+ }
+
+ @Override
+ public HintSeverity getDefaultSeverity() {
+ return severity;
+ }
+ }
+
+ public class BladeRuleContext extends RuleContext {
+
+ private BladeParserResult bladeParserResult = null;
+
+ public BladeParserResult getJsParserResult() {
+ if (bladeParserResult == null) {
+ bladeParserResult = (BladeParserResult) parserResult;
+ }
+ return bladeParserResult;
+ }
+
+ public boolean isCancelled() {
+ return false;
+ }
+
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/Bundle.properties b/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/Bundle.properties
new file mode 100644
index 000000000000..eac084f5fafe
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/Bundle.properties
@@ -0,0 +1,27 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+
+UnknownDirectiveHintMsg=Unknown directive. Try adding the provider class file using Project -> Properties -> Custom Directives
+BladeViewNotFound=Blade path not found.\
+For custom blade context you can try to set the root folder using:\
+Project -> Properties -> Laravel Blade -> Views Folder
+
+AST_Rule_UnknownDirective=Unknown Directive
+AST_Rule_UnknownDirectiveDescription=Unknown Directive. The directive my not be in the base laravel blade compiler and was not found in the custom list.
+
+AST_Rule_PhpSyntaxError=`@php` directive syntax error
+AST_Rule_PhpSyntaxErrorDescription=Php Syntax error for `@php` directives
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/HintsControllerFactory.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/HintsControllerFactory.java
new file mode 100644
index 000000000000..57b23a072f74
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/HintsControllerFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.hints;
+
+import org.netbeans.modules.csl.api.HintsProvider;
+import org.netbeans.modules.php.blade.editor.BladeLanguage;
+import org.netbeans.spi.options.OptionsPanelController;
+import org.openide.util.NbBundle;
+
+/**
+ *
+ * @author bogdan
+ */
+public class HintsControllerFactory {
+
+ public HintsControllerFactory() {
+ }
+
+ @OptionsPanelController.SubRegistration(
+ id = "BladeHints",
+ location = "Blade/Hints",
+ displayName = "#HintsControllerFactory.name"
+ )
+ @NbBundle.Messages("HintsControllerFactory.name=Blade Hints")
+ public static OptionsPanelController createOptions() {
+ HintsProvider.HintsManager manager = HintsProvider.HintsManager.getManagerForMimeType(BladeLanguage.MIME_TYPE);
+ assert manager != null;
+
+ return manager.getOptionsController();
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/UnknownDirective.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/UnknownDirective.java
new file mode 100644
index 000000000000..7bb522aa035d
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/hints/UnknownDirective.java
@@ -0,0 +1,87 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.hints;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import java.util.prefs.Preferences;
+import javax.swing.JComponent;
+import javax.swing.text.BadLocationException;
+import org.netbeans.modules.csl.api.Hint;
+import org.netbeans.modules.csl.api.HintSeverity;
+import org.netbeans.modules.csl.api.HintsProvider;
+import org.netbeans.modules.csl.api.Rule;
+import org.netbeans.modules.csl.api.RuleContext;
+
+/**
+ *
+ * @author bogdan
+ */
+public class UnknownDirective implements Rule.AstRule {
+
+ public void computeHints(RuleContext context, List hints, int offset, HintsProvider.HintsManager manager) throws BadLocationException {
+
+ }
+
+ @Override
+ public boolean getDefaultEnabled() {
+ return true;
+ }
+
+ @Override
+ public JComponent getCustomizer(Preferences node) {
+ return null;
+ }
+
+ @Override
+ public boolean appliesTo(RuleContext context) {
+ return context instanceof BladeHintsProvider.BladeRuleContext;
+ }
+
+ @Override
+ public boolean showInTasklist() {
+ return true;
+ }
+
+ @Override
+ public HintSeverity getDefaultSeverity() {
+ return HintSeverity.WARNING;
+ }
+
+ @Override
+ public Set> getKinds() {
+ return Collections.singleton("blade.option.directive.hints");
+ }
+
+ @Override
+ public String getId() {
+ return "blade.hint.unknown_directive";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Unknown Directive. The directive my not be in the base laravel blade compiler and was not found in the custom list.";
+ }
+
+ @Override
+ public String getDisplayName() {
+ return "Unknown Directive";
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/BladeIndex.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/BladeIndex.java
new file mode 100644
index 000000000000..4859e7f55b18
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/BladeIndex.java
@@ -0,0 +1,321 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.indexing;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import java.util.concurrent.ExecutionException;
+import org.netbeans.api.project.Project;
+import org.netbeans.api.project.ui.OpenProjects;
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.netbeans.modules.parsing.spi.indexing.support.IndexResult;
+import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
+import org.netbeans.modules.php.blade.editor.parser.BladeParserResult.Reference;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author bhaidu
+ */
+public class BladeIndex {
+
+ private final QuerySupport querySupport;
+ private static final Map INDEXES = new WeakHashMap<>();
+ private static boolean areProjectsOpen = false;
+
+ private BladeIndex(QuerySupport querySupport) throws IOException {
+ this.querySupport = querySupport;
+ }
+
+ public QuerySupport getQuerySupport() {
+ return querySupport;
+ }
+
+ public static BladeIndex get(Project project) throws IOException {
+ if (project == null) {
+ return null;
+ }
+ synchronized (INDEXES) {
+ BladeIndex index = INDEXES.get(project);
+ if (index == null) {
+ if (!areProjectsOpen) {
+ try {
+ // just be sure that the projects are open
+ OpenProjects.getDefault().openProjects().get();
+ } catch (InterruptedException | ExecutionException ex) {
+ Exceptions.printStackTrace(ex);
+ } finally {
+ areProjectsOpen = true;
+ }
+ }
+ Collection sourceRoots = QuerySupport.findRoots(project,
+ null /* all source roots */,
+ Collections.emptyList(),
+ Collections.emptyList());
+ QuerySupport querySupport = QuerySupport.forRoots(BladeIndexer.Factory.NAME, BladeIndexer.Factory.VERSION, sourceRoots.toArray(new FileObject[]{}));
+ index = new BladeIndex(querySupport);
+ if (!sourceRoots.isEmpty()) {
+ INDEXES.put(project, index);
+ }
+ }
+ return index;
+ }
+ }
+
+ public List queryYieldIds(String prefix) {
+ return queryIndexedReferenceId(prefix, BladeIndexer.YIELD_ID);
+ }
+
+ public List queryStacksIndexedReferences(String prefix) {
+ return queryIndexedReferenceId(prefix, BladeIndexer.STACK_ID);
+ }
+
+ private List queryIndexedReferenceId(String prefix, String indexKey) {
+ List indexedReferences = new ArrayList<>();
+
+ try {
+ Collection extends IndexResult> result = querySupport.query(indexKey, prefix, QuerySupport.Kind.PREFIX, indexKey);
+
+ if (result == null || result.isEmpty()) {
+ return indexedReferences;
+ }
+
+ for (IndexResult indexResult : result) {
+ String[] values = indexResult.getValues(indexKey);
+ for (String value : values) {
+ if (value.startsWith(prefix)) {
+ indexedReferences.add(new IndexedReferenceId(value, indexResult.getFile()));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+
+ return indexedReferences;
+ }
+
+ public List queryYieldIndexedReferences(String prefix) {
+ return queryIndexedReferences(prefix,
+ BladeIndexer.YIELD_REFERENCE,
+ new IndexReferenceCallback() {
+ @Override
+ public Reference createIndexReference(String value) {
+ return BladeIndexer.extractYieldDataFromIndex(value);
+ }
+ }
+ );
+ }
+
+ public List queryStacksIdsReference(String prefix) {
+ return queryIndexedReferences(prefix,
+ BladeIndexer.STACK_REFERENCE,
+ new IndexReferenceCallback() {
+ @Override
+ public Reference createIndexReference(String value) {
+ return BladeIndexer.extractStackDataFromIndex(value);
+ }
+ }
+ );
+ }
+
+ private List queryIndexedReferences(String prefix, String indexKey, IndexReferenceCallback callback) {
+ List references = new ArrayList<>();
+ try {
+ Collection extends IndexResult> result = querySupport.query(indexKey,
+ prefix, QuerySupport.Kind.PREFIX, indexKey);
+
+ if (result == null || result.isEmpty()) {
+ return references;
+ }
+
+ for (IndexResult indexResult : result) {
+ String[] values = indexResult.getValues(indexKey);
+ for (String value : values) {
+ if (value.startsWith(prefix)) {
+ references.add(new IndexedReference(callback.createIndexReference(value), indexResult.getFile()));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+
+ return references;
+ }
+
+ public List findYieldIndexedReferences(String prefix) {
+ return findIndexedReferences(prefix,
+ BladeIndexer.YIELD_ID,
+ new String[]{BladeIndexer.YIELD_ID, BladeIndexer.YIELD_REFERENCE},
+ BladeIndexer.YIELD_REFERENCE,
+ new IndexReferenceCallback() {
+ @Override
+ public Reference createIndexReference(String value) {
+ return BladeIndexer.extractYieldDataFromIndex(value);
+ }
+ }
+ );
+ }
+
+ public List findStackIdIndexedReferences(String prefix) {
+ return findIndexedReferences(prefix,
+ BladeIndexer.STACK_ID,
+ new String[]{BladeIndexer.STACK_ID, BladeIndexer.STACK_REFERENCE},
+ BladeIndexer.STACK_REFERENCE,
+ new IndexReferenceCallback() {
+ @Override
+ public Reference createIndexReference(String value) {
+ return BladeIndexer.extractStackDataFromIndex(value);
+ }
+ }
+ );
+ }
+
+ private List findIndexedReferences(String prefix,
+ String indexKey, String[] valuesKeys, String valueKey,
+ IndexReferenceCallback callback) {
+ List references = new ArrayList<>();
+ try {
+ Collection extends IndexResult> result = querySupport.query(indexKey,
+ prefix, QuerySupport.Kind.EXACT, valuesKeys);
+
+ if (result == null || result.isEmpty()) {
+ return references;
+ }
+
+ for (IndexResult indexResult : result) {
+ String[] values = indexResult.getValues(valueKey);
+ for (String value : values) {
+ String name = BladeIndexer.getIdFromSignature(value);
+ if (name != null && name.equals(prefix)) {
+ references.add(
+ new IndexedReference(callback.createIndexReference(value),
+ indexResult.getFile()));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+
+ return references;
+ }
+
+ public List getIncludePaths(String prefix) {
+ List references = new ArrayList<>();
+ Collection extends IndexResult> result;
+ try {
+ result = querySupport.query(BladeIndexer.INCLUDE_PATH, prefix, QuerySupport.Kind.PREFIX, BladeIndexer.INCLUDE_PATH);
+
+ if (result == null || result.isEmpty()) {
+ return references;
+ }
+
+ for (IndexResult indexResult : result) {
+ String[] values = indexResult.getValues(BladeIndexer.INCLUDE_PATH);
+ for (String value : values) {
+ Reference templatePathRef = BladeIndexer.extractTemplatePathDataFromIndex(value);
+ if (!templatePathRef.identifier.equals(prefix)) {
+ continue;
+ }
+ references.add(new IndexedOffsetReference(templatePathRef.identifier, indexResult.getFile(), templatePathRef.defOffset));
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return references;
+ }
+
+ public static interface IndexReferenceCallback {
+
+ public Reference createIndexReference(String value);
+ }
+
+ public static class IndexedReferenceId {
+
+ private final String identifier;
+ private final FileObject originFile;
+
+ public IndexedReferenceId(String identifier, FileObject originFile) {
+ this.identifier = identifier;
+ this.originFile = originFile;
+ }
+
+ public String getIdenfiier() {
+ return this.identifier;
+ }
+
+ public FileObject getOriginFile() {
+ return this.originFile;
+ }
+ }
+
+ public static class IndexedReference {
+
+ private final Reference reference;
+ private final FileObject originFile;
+
+ public IndexedReference(Reference reference, FileObject originFile) {
+ this.reference = reference;
+ this.originFile = originFile;
+ }
+
+ public Reference getReference() {
+ return this.reference;
+ }
+
+ public FileObject getOriginFile() {
+ return this.originFile;
+ }
+ }
+
+ public static class IndexedOffsetReference {
+
+ private final String identifier;
+ private final FileObject originFile;
+ private final OffsetRange range;
+
+ public IndexedOffsetReference(String identifier,
+ FileObject originFile, OffsetRange range) {
+ this.identifier = identifier;
+ this.originFile = originFile;
+ this.range = range;
+ }
+
+ public String getReference() {
+ return this.identifier;
+ }
+
+ public FileObject getOriginFile() {
+ return this.originFile;
+ }
+
+ public int getStart() {
+ return this.range.getStart();
+ }
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/BladeIndexer.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/BladeIndexer.java
new file mode 100644
index 000000000000..923cfd351097
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/BladeIndexer.java
@@ -0,0 +1,303 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.indexing;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import org.netbeans.api.editor.mimelookup.MimeRegistration;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.netbeans.modules.parsing.api.Snapshot;
+import org.netbeans.modules.parsing.spi.Parser;
+import org.netbeans.modules.parsing.spi.indexing.Context;
+import org.netbeans.modules.parsing.spi.indexing.EmbeddingIndexer;
+import org.netbeans.modules.parsing.spi.indexing.EmbeddingIndexerFactory;
+import org.netbeans.modules.parsing.spi.indexing.Indexable;
+import org.netbeans.modules.parsing.spi.indexing.support.IndexingSupport;
+import org.netbeans.modules.parsing.spi.indexing.support.IndexDocument;
+import org.netbeans.modules.php.blade.editor.BladeLanguage;
+import org.netbeans.modules.php.blade.editor.parser.BladeParserResult;
+import org.netbeans.modules.php.blade.editor.parser.BladeParserResult.Reference;
+import org.netbeans.modules.php.blade.editor.parser.BladeParserResult.ReferenceType;
+import org.netbeans.modules.php.blade.editor.parser.BladeReferenceIdsCollection;
+import org.netbeans.modules.php.blade.editor.path.BladePathUtils;
+import org.netbeans.modules.php.blade.project.ProjectUtils;
+import org.netbeans.modules.php.blade.syntax.StringUtils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+/**
+ * move to language EmbeddingIndexerFactory getIndexerFactory ?
+ *
+ * @author bhaidu
+ */
+public class BladeIndexer extends EmbeddingIndexer {
+
+ private static final Logger LOGGER = Logger.getLogger(BladeIndexer.class.getSimpleName());
+ public static final String BLADE_INDEXED = "indexed"; //NOI18N
+ public static final String YIELD_REFERENCE = "yield"; //NOI18N
+ public static final String YIELD_ID = "yieldid"; //NOI18N
+ public static final String STACK_REFERENCE = "stack"; //NOI18N
+ public static final String STACK_ID = "stackid"; //NOI18N
+ public static final String INCLUDE_PATH = "include"; //NOI18N
+ public static final String BLADE_PATH = "path"; //NOI18N
+ public static final String INFO_SEPARATOR = "#"; //NOI18N
+ public static final String RANGE_SEPARATOR = ";"; //NOI18N
+
+ @Override
+ protected void index(Indexable indxbl, Parser.Result result, Context context) {
+ long startTime = System.currentTimeMillis();
+
+ BladeParserResult parserResult;
+
+ if (!(result instanceof BladeParserResult)) {
+ return;
+ }
+
+ parserResult = (BladeParserResult) result;
+
+ if (!parserResult.getDiagnostics().isEmpty()) {
+ return;
+ }
+
+ try {
+ IndexingSupport support = IndexingSupport.getInstance(context);
+
+ support.removeDocuments(indxbl);
+ IndexDocument document = support.createDocument(indxbl);
+
+ BladeReferenceIdsCollection referenceCollection = parserResult.getBladeReferenceIdsCollection();
+ if (!referenceCollection.getYieldIdOccurences().isEmpty()) {
+ storeYieldReferences(referenceCollection.getYieldIdOccurences(), document);
+ }
+
+ if (!referenceCollection.getStackIdOccurences().isEmpty()) {
+ storeStackReferences(referenceCollection.getStackIdOccurences(), document);
+ }
+
+ if (!referenceCollection.getIncludePathsOccurences().isEmpty()) {
+ storeIncludePathReferences(referenceCollection.getIncludePathsOccurences(), document);
+ }
+
+ storeFilePathAsBladePath(parserResult.getSnapshot().getSource().getFileObject(), document);
+
+ document.addPair(BLADE_INDEXED, Boolean.TRUE.toString(), true, true);
+
+ support.addDocument(document);
+ long time = System.currentTimeMillis() - startTime;
+
+ if (time > 2000) {
+ LOGGER.log(Level.INFO, "Indexer for " + context.getIndexFolder().getName() + " finished in {0} ms", System.currentTimeMillis() - startTime);
+ }
+ } catch (IOException ex) {
+ LOGGER.log(Level.WARNING, null, ex);
+ Exceptions.printStackTrace(ex);
+ } catch (Exception ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ }
+
+ private void storeYieldReferences(Map yields, IndexDocument document) {
+
+ for (Map.Entry entry : yields.entrySet()) {
+ StringBuilder sb = new StringBuilder();
+ OffsetRange range = entry.getValue();
+ //used for completion
+ document.addPair(YIELD_ID, entry.getKey(), true, true);
+ sb.append(entry.getKey()).append(INFO_SEPARATOR).append(range.getStart()).append(RANGE_SEPARATOR).append(range.getEnd());
+ //used for declaration finder
+ document.addPair(YIELD_REFERENCE, sb.toString(), true, true);
+ }
+ }
+
+ private void storeStackReferences(Map stacks, IndexDocument document) {
+
+ for (Map.Entry entry : stacks.entrySet()) {
+ StringBuilder sb = new StringBuilder();
+ OffsetRange range = entry.getValue();
+ //used for completion
+ document.addPair(STACK_ID, entry.getKey(), true, true);
+
+ sb.append(entry.getKey()).append(INFO_SEPARATOR).append(range.getStart()).append(RANGE_SEPARATOR).append(range.getEnd());
+ //used for declaration finder
+ document.addPair(STACK_REFERENCE, sb.toString(), true, true);
+ }
+ }
+
+ private void storeFilePathAsBladePath(FileObject fo, IndexDocument document) {
+ Project project = ProjectUtils.getMainOwner(fo);
+ if (project == null) {
+ return;
+ }
+ List roots = BladePathUtils.getCustomViewsRoots(project, fo);
+ String filePath = fo.getPath();
+
+ for (FileObject root : roots) {
+ String rootPath = root.getPath();
+ if (filePath.startsWith(rootPath)) {
+ String bladeFormatPath = BladePathUtils.toBladeViewPath(filePath.replace(rootPath, "")); //NOI18N
+ if (bladeFormatPath.startsWith(StringUtils.DOT)) {
+ bladeFormatPath = bladeFormatPath.substring(1, bladeFormatPath.length());
+ }
+ document.addPair(BLADE_PATH, bladeFormatPath, true, true);
+ }
+ }
+ }
+
+ public static Reference extractYieldDataFromIndex(String index) {
+ String[] mainElements = index.split(INFO_SEPARATOR);
+
+ if (mainElements.length == 0) {
+ return null;
+ }
+
+ String name = mainElements[0];
+ String offsets[] = mainElements[1].split(RANGE_SEPARATOR);
+ int start = 0;
+ int end = 1;
+
+ if (offsets.length > 0) {
+ start = Integer.parseInt(offsets[0]);
+ end = Integer.parseInt(offsets[1]);
+ }
+
+ return new Reference(ReferenceType.YIELD, name, new OffsetRange(start, end));
+ }
+
+ public static Reference extractStackDataFromIndex(String index) {
+ String[] mainElements = index.split(INFO_SEPARATOR);
+
+ if (mainElements.length == 0) {
+ return null;
+ }
+
+ String name = mainElements[0];
+ String offsets[] = mainElements[1].split(RANGE_SEPARATOR);
+ int start = 0;
+ int end = 1;
+
+ if (offsets.length > 0) {
+ start = Integer.parseInt(offsets[0]);
+ end = Integer.parseInt(offsets[1]);
+ }
+
+ return new Reference(ReferenceType.STACK, name, new OffsetRange(start, end));
+ }
+
+ public static Reference extractTemplatePathDataFromIndex(String indexInfo) {
+ String[] mainElements = indexInfo.split(INFO_SEPARATOR);
+
+ if (mainElements.length == 0) {
+ return null;
+ }
+
+ String name = mainElements[0];
+ String offsets[] = mainElements[1].split(RANGE_SEPARATOR);
+ int start = 0;
+ int end = 1;
+
+ if (offsets.length > 0) {
+ start = Integer.parseInt(offsets[0]);
+ end = start + name.length();
+ }
+
+ return new Reference(ReferenceType.TEMPLATE_PATH, name, new OffsetRange(start, end));
+ }
+
+ public static String getIdFromSignature(String value) {
+ String[] mainElements = value.split(INFO_SEPARATOR);
+ if (mainElements.length == 0) {
+ return null;
+ }
+
+ return mainElements[0];
+ }
+
+ private void storeIncludePathReferences(Map> includes, IndexDocument document) {
+ for (Map.Entry> entry : includes.entrySet()) {
+ StringBuilder sb = new StringBuilder();
+
+ sb.append(entry.getKey()).append(INFO_SEPARATOR);
+ for (OffsetRange range : entry.getValue()) {
+ sb.append(range.getStart());
+ sb.append(RANGE_SEPARATOR);
+ }
+
+ document.addPair(INCLUDE_PATH, sb.toString(), true, true);
+ }
+ }
+
+ @MimeRegistration(mimeType = BladeLanguage.MIME_TYPE, service = EmbeddingIndexerFactory.class, position = 500) //NOI18N
+ public static class Factory extends EmbeddingIndexerFactory {
+
+ public static final String NAME = "blade"; //NOI18N
+ public static final int VERSION = 3;
+
+ @Override
+ public EmbeddingIndexer createIndexer(Indexable indxbl, Snapshot snapshot) {
+ if (isIndexable(snapshot)) {
+ return new BladeIndexer();
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ public void filesDeleted(Iterable extends Indexable> deleted, Context context) {
+ try {
+ IndexingSupport is = IndexingSupport.getInstance(context);
+ for (Indexable i : deleted) {
+ is.removeDocuments(i);
+ }
+ } catch (IOException ioe) {
+ LOGGER.log(Level.WARNING, null, ioe);
+ }
+ }
+
+ @Override
+ public void filesDirty(Iterable extends Indexable> dirty, org.netbeans.modules.parsing.spi.indexing.Context context) {
+ try {
+ IndexingSupport is = IndexingSupport.getInstance(context);
+ for (Indexable i : dirty) {
+ is.markDirtyDocuments(i);
+ }
+ } catch (IOException ioe) {
+ LOGGER.log(Level.WARNING, null, ioe);
+ }
+ }
+
+ @Override
+ public String getIndexerName() {
+ return NAME;
+ }
+
+ @Override
+ public int getIndexVersion() {
+ return VERSION;
+ }
+
+ private boolean isIndexable(Snapshot snapshot) {
+ return BladeLanguage.MIME_TYPE.equals(snapshot.getMimeType());
+ }
+ }
+
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/IndexManager.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/IndexManager.java
new file mode 100644
index 000000000000..276562426772
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/IndexManager.java
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.indexing;
+
+import java.io.File;
+import java.util.Enumeration;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.parsing.api.indexing.IndexingManager;
+import org.netbeans.modules.php.blade.project.BladeProjectProperties;
+import org.openide.filesystems.FileObject;
+import org.openide.filesystems.FileUtil;
+
+/**
+ *
+ * @author bogdan
+ */
+public class IndexManager {
+
+ public static void reindexProjectViews(Project project) {
+ assert project != null;
+ String[] views = BladeProjectProperties.getInstance(project).getViewsFolderPathList();
+
+ if (views.length > 0) {
+ for (String view : views) {
+ if (view.length() == 0) {
+ continue;
+ }
+ File viewPath = new File(view);
+ if (viewPath.exists()) {
+ FileObject fileObj = FileUtil.toFileObject(viewPath);
+ Enumeration extends FileObject> children = fileObj.getChildren(true);
+ while (children.hasMoreElements()) {
+ FileObject file = children.nextElement();
+ if (file.isFolder()) {
+ continue;
+ }
+ IndexingManager.getDefault().refreshAllIndices(file);
+ }
+ }
+ }
+ } else {
+ //falback
+ String projectDir = project.getProjectDirectory().getPath();
+ File viewPath = new File(projectDir + "/views"); // NOI18N
+ if (viewPath.exists()) {
+ FileObject fileObj = FileUtil.toFileObject(viewPath);
+ Enumeration extends FileObject> children = fileObj.getChildren(true);
+ while (children.hasMoreElements()) {
+ FileObject file = children.nextElement();
+ IndexingManager.getDefault().refreshAllIndices(file);
+ }
+ }
+ }
+ }
+
+ public static void reindexFolder(File viewPath) {
+ FileObject fileObj = FileUtil.toFileObject(viewPath);
+ Enumeration extends FileObject> children = fileObj.getChildren(true);
+ while (children.hasMoreElements()) {
+ FileObject file = children.nextElement();
+ if (file.isFolder()) {
+ continue;
+ }
+ IndexingManager.getDefault().refreshAllIndices(file);
+ }
+ }
+
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexFunctionResult.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexFunctionResult.java
new file mode 100644
index 000000000000..439b3bad1f33
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexFunctionResult.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.indexing;
+
+import java.util.List;
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.openide.filesystems.FileObject;
+
+/**
+ *
+ * @author bogdan
+ */
+public class PhpIndexFunctionResult extends PhpIndexResult {
+
+ protected final List params;
+ protected final String classNamespace;
+
+ public PhpIndexFunctionResult(String name, FileObject fo,
+ PhpIndexFunctionResult.Type type,
+ OffsetRange range, String classNamespace, List params) {
+ super(name, fo, type, range);
+ this.params = params;
+ this.classNamespace = classNamespace;
+ }
+
+ public String getParamsAsString() {
+ if (params == null || params.isEmpty()){
+ return "()";
+ }
+ return "(" + String.join(", ", params) + ")";
+ }
+
+ public List getParams(){
+ return params;
+ }
+
+ public String getClassNamespace(){
+ return classNamespace;
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexResult.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexResult.java
new file mode 100644
index 000000000000..7db56cacdf88
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexResult.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.indexing;
+
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.openide.filesystems.FileObject;
+
+/**
+ *
+ * @author bogdan
+ */
+public class PhpIndexResult {
+ public static enum Type{
+ CLASS,
+ FUNCTION,
+ NAMESPACE,
+ CONSTANT
+ };
+
+ public String name;
+ public String namespace;
+ public FileObject declarationFile;
+ public PhpIndexResult.Type type;
+ public OffsetRange range;
+
+ public PhpIndexResult(String name, FileObject fo,
+ PhpIndexResult.Type type,
+ OffsetRange range){
+ this.name = name;
+ this.declarationFile = fo;
+ this.type = type;
+ this.range = range;
+ }
+
+ public PhpIndexResult(String name, String qualifiedName, FileObject fo,
+ PhpIndexResult.Type type,
+ OffsetRange range){
+ this.name = name;
+ this.namespace = qualifiedName;
+ this.declarationFile = fo;
+ this.type = type;
+ this.range = range;
+ }
+
+ public int getStartOffset(){
+ return this.range.getStart();
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexUtils.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexUtils.java
new file mode 100644
index 000000000000..1821ad404f8f
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/PhpIndexUtils.java
@@ -0,0 +1,785 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.indexing;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.netbeans.modules.parsing.spi.indexing.support.IndexResult;
+import org.netbeans.modules.parsing.spi.indexing.support.QuerySupport;
+import static org.netbeans.modules.php.blade.editor.EditorStringUtils.NAMESPACE_SEPARATOR;
+import org.netbeans.modules.php.blade.editor.cache.QueryCache;
+import org.netbeans.modules.php.editor.api.QuerySupportFactory;
+import org.netbeans.modules.php.editor.index.PHPIndexer;
+import org.netbeans.modules.php.editor.index.Signature;
+import org.netbeans.spi.project.ui.support.ProjectConvertors;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+/**
+ * TODO needs simplification & refactor
+ *
+ * @author bhaidu
+ */
+public class PhpIndexUtils {
+
+ public final static String ESCAPED_NAMESPACE_SEPARATOR = "\\\\"; // NOI18N
+ private final static QueryCache> cache = new QueryCache();
+ private final static QueryCache> functionCache = new QueryCache();
+ private final static int SIGN_NAME_POS = 1;
+ private final static int SIGN_METHOD_OFFSET_POS = 2;
+ private final static int SIGN_METHOD_PARAMS_POS = 3;
+ private final static int SIGN_NAMESPACE_ROOT_POS = 2;
+ private final static int SIGN_CLASS_NAMESPACE_POS = 4;
+ private final static String PHP_INDEX_INFO_SEPARATOR = ";";
+
+ private final static int MIN_NAMESPACE_LENGTH = 3;
+
+ private static final Map QUERY_SUPPORT_INSTANCES = new WeakHashMap<>();
+
+ public enum FieldAccessType {
+ STATIC,
+ DIRECT
+ }
+
+ private PhpIndexUtils() {
+
+ }
+
+ /**
+ * class query without namespace
+ *
+ * @param fo
+ * @param prefix
+ * @return
+ */
+ public static Collection queryClass(FileObject fo, String prefix) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+
+ QueryCache> selfCache = getCache(fo, prefix);
+ if (selfCache != null && selfCache.containsKey(prefix)) {
+ return selfCache.get(prefix).get();
+ }
+ Collection results = new ArrayList<>();
+ String queryPrefix = prefix.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_CLASS,
+ queryPrefix, QuerySupport.Kind.PREFIX, new String[]{
+ PHPIndexer.FIELD_CLASS,});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_CLASS);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String fullName = sig.string(SIGN_NAME_POS);
+ String namespace = sig.string(SIGN_CLASS_NAMESPACE_POS);
+
+ if (fullName.length() > 0 && fullName.startsWith(prefix)) {
+ results.add(new PhpIndexResult(fullName, namespace,
+ indexFile,
+ PhpIndexResult.Type.CLASS, new OffsetRange(0, 1)));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+
+ if (selfCache != null && !results.isEmpty()) {
+ selfCache.put(prefix, results);
+ }
+ return results;
+ }
+
+ public static Collection queryNamespaceClassesName(FileObject fo, String prefix,
+ String namespace) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ String queryPrefix = prefix.toLowerCase() + ".*" + namespace.replace(NAMESPACE_SEPARATOR, ESCAPED_NAMESPACE_SEPARATOR) + ";.*";// NOI18N
+
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(
+ PHPIndexer.FIELD_CLASS, queryPrefix, QuerySupport.Kind.REGEXP, new String[]{
+ PHPIndexer.FIELD_CLASS, PHPIndexer.FIELD_FIELD});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_CLASS);
+
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String fullName = sig.string(SIGN_NAME_POS);
+ String classNamespace = sig.string(SIGN_CLASS_NAMESPACE_POS);
+ if (fullName.length() > 0
+ && classNamespace.length() > 0
+ && classNamespace.startsWith(namespace)) {
+ results.add(new PhpIndexResult(fullName,
+ classNamespace + NAMESPACE_SEPARATOR + fullName, indexFile, PhpIndexResult.Type.CLASS, new OffsetRange(0, 1)));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryExactNamespaceClasses(String identifier,
+ String namespace, FileObject fo) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(
+ PHPIndexer.FIELD_TOP_LEVEL, namespace.toLowerCase(), QuerySupport.Kind.EXACT,
+ new String[]{
+ PHPIndexer.FIELD_NAMESPACE
+ });
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+
+ if (!indexFile.getName().equals(identifier)){
+ continue;
+ }
+
+ String namespaceValue = indexResult.getValue(PHPIndexer.FIELD_NAMESPACE);
+
+ if (namespaceValue == null){
+ continue;
+ }
+
+ results.add(new PhpIndexResult(namespace + NAMESPACE_SEPARATOR + identifier, indexFile, PhpIndexResult.Type.CLASS, new OffsetRange(0, 1)));
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryComponentClass(String identifier,
+ String namespace, FileObject fo) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(
+ PHPIndexer.FIELD_TOP_LEVEL, namespace.toLowerCase(), QuerySupport.Kind.PREFIX,
+ new String[]{
+ PHPIndexer.FIELD_NAMESPACE
+ });
+ for (IndexResult indexResult : indexResults) {
+ String namespaceValue = indexResult.getValue(PHPIndexer.FIELD_NAMESPACE);
+ if (namespaceValue == null){
+ continue;
+ }
+ Signature sig = Signature.get(namespaceValue);
+ String name = sig.string(SIGN_NAME_POS);
+ String domainName = sig.string(SIGN_NAMESPACE_ROOT_POS);
+ FileObject indexFile = indexResult.getFile();
+ if (indexFile.getName().equals(identifier)) {
+ results.add(new PhpIndexResult(domainName + NAMESPACE_SEPARATOR + name + NAMESPACE_SEPARATOR + indexFile.getName(),
+ indexFile, PhpIndexResult.Type.CLASS, new OffsetRange(0, 1)));
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryExactClass(String identifier, FileObject fo) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ QueryCache> selfCache = getCache(fo, identifier);
+ if (selfCache != null && selfCache.containsKey(identifier)) {
+ return selfCache.get(identifier).get();
+ }
+ Collection results = new ArrayList<>();
+ String queryPrefix = identifier.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_CLASS,
+ queryPrefix, QuerySupport.Kind.PREFIX, new String[]{PHPIndexer.FIELD_CLASS});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_CLASS);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(SIGN_NAME_POS);
+ if (name.length() > 0 && name.equals(identifier)) {
+ results.add(new PhpIndexResult(name, indexFile, PhpIndexResult.Type.CLASS, new OffsetRange(0, 1)));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ if (selfCache != null && !results.isEmpty()) {
+ selfCache.put(identifier, results);
+ }
+ return results;
+ }
+
+ public static Collection queryFunctions(FileObject fo, String prefix) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ String queryPrefix = prefix.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_BASE, queryPrefix, QuerySupport.Kind.PREFIX, new String[]{PHPIndexer.FIELD_BASE});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ //internal php index
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_BASE);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(SIGN_NAME_POS);
+
+ if (name.length() > 0 && name.startsWith(prefix)) {
+ Integer offset = sig.integer(SIGN_METHOD_OFFSET_POS);
+ String params = sig.string(SIGN_METHOD_PARAMS_POS);
+ results.add(new PhpIndexFunctionResult(
+ name, indexFile,
+ PhpIndexResult.Type.FUNCTION,
+ new OffsetRange(offset, offset + name.length()),
+ null,
+ parseParameters(params)
+ ));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryExactFunctions(FileObject fo, String prefix) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ QueryCache> selfCache = getFunctionCache(fo, prefix);
+ if (selfCache != null && selfCache.containsKey(prefix)) {
+ return selfCache.get(prefix).get();
+ }
+ String queryPrefix = prefix.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_BASE, queryPrefix, QuerySupport.Kind.PREFIX, new String[]{PHPIndexer.FIELD_BASE});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ //internal php index
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_BASE);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(1);
+
+ if (name.length() > 0 && name.equals(prefix)) {
+ Integer offset = sig.integer(SIGN_METHOD_OFFSET_POS);
+ String params = sig.string(SIGN_METHOD_PARAMS_POS);
+ results.add(new PhpIndexFunctionResult(name,
+ indexFile, PhpIndexResult.Type.FUNCTION,
+ new OffsetRange(offset, offset + name.length()),
+ null,
+ parseParameters(params)
+ ));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ if (selfCache != null && !results.isEmpty()) {
+ selfCache.put(prefix, results);
+ }
+ return results;
+ }
+
+ public static Collection queryExactClassMethods(FileObject fo,
+ String method, String className, String queryNamespace) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+
+ String regexQuery = className.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_CLASS, regexQuery,
+ QuerySupport.Kind.PREFIX, new String[]{PHPIndexer.FIELD_CLASS, PHPIndexer.FIELD_METHOD});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ //internal php index
+ String[] classValues = indexResult.getValues(PHPIndexer.FIELD_CLASS);
+
+ Signature classSignature = null;
+ String classNamespace = null;
+
+ for (String classValue : classValues) {
+ Signature sig = Signature.get(classValue);
+ String name = sig.string(SIGN_NAME_POS);
+ String namespace = sig.string(SIGN_CLASS_NAMESPACE_POS);
+ if (name.length() > 0 && name.equals(className)
+ ) {
+ if (queryNamespace != null && !namespace.equals(queryNamespace)){
+ continue;
+ }
+ classSignature = sig;
+
+ if (namespace.length() > 0){
+ classNamespace = namespace + NAMESPACE_SEPARATOR + className;
+ }
+ }
+ }
+
+ if (classSignature == null){
+ continue;
+ }
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_METHOD);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(SIGN_NAME_POS);
+
+ if (name.length() > 0 && name.equals(method)) {
+ Integer offset = sig.integer(SIGN_METHOD_OFFSET_POS);
+ String params = sig.string(SIGN_METHOD_PARAMS_POS);
+ results.add(new PhpIndexFunctionResult(name,
+ indexFile, PhpIndexResult.Type.FUNCTION,
+ new OffsetRange(offset, offset + name.length()),
+ classNamespace,
+ parseParameters(params)
+ ));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ /**
+ *
+ *
+ * @param fo
+ * @param method
+ * @param className
+ * @param queryNamespace
+ * @return
+ */
+ public static Collection queryClassMethods(FileObject fo,
+ String method, String className, String queryNamespace, FieldAccessType accessType) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+
+ if (queryNamespace != null && queryNamespace.length() > MIN_NAMESPACE_LENGTH){
+ int startOffset = queryNamespace.startsWith(NAMESPACE_SEPARATOR) ? 1 : 0;
+ int endOffset = queryNamespace.endsWith(NAMESPACE_SEPARATOR) ? 1 : 0;
+ queryNamespace = queryNamespace.substring(startOffset, queryNamespace.length() - endOffset);
+ }
+ //should query the class befoe
+ //for the moment a quick hack
+ //maybe send the classNamePath directly?
+ String regexQuery = className.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_CLASS, regexQuery,
+ QuerySupport.Kind.PREFIX, new String[]{PHPIndexer.FIELD_CLASS, PHPIndexer.FIELD_METHOD});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ String[] classValues = indexResult.getValues(PHPIndexer.FIELD_CLASS);
+
+ Signature classSignature = null;
+ String classNamespace = null;
+
+ for (String classValue : classValues) {
+ Signature sig = Signature.get(classValue);
+ String name = sig.string(SIGN_NAME_POS);
+ if (name.length() > 0 && name.equals(className)) {
+ classSignature = sig;
+ String namespace = sig.string(SIGN_CLASS_NAMESPACE_POS);
+
+ if (queryNamespace != null && !namespace.equals(queryNamespace) ){
+ classSignature = null;
+ continue;
+ }
+
+ if (namespace.length() > 0){
+ classNamespace = namespace + NAMESPACE_SEPARATOR + className;
+ }
+ }
+ }
+
+ if (classSignature == null){
+ continue;
+ }
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_METHOD);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(SIGN_NAME_POS);
+
+ Integer funcAccessType = sig.integer(5);
+ //todo find where does the value 9 come from
+ if (accessType.equals(FieldAccessType.STATIC) && funcAccessType != 9){
+ //only public static methods
+ continue;
+ }
+
+ if (name.length() > 0 && name.startsWith(method)) {
+ Integer offset = sig.integer(SIGN_METHOD_OFFSET_POS);
+ String params = sig.string(SIGN_METHOD_PARAMS_POS);
+ results.add(new PhpIndexFunctionResult(name,
+ indexFile, PhpIndexResult.Type.FUNCTION,
+ new OffsetRange(offset, offset + name.length()),
+ classNamespace,
+ parseParameters(params)
+ ));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryConstants(FileObject fo, String prefix) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ String queryPrefix = prefix.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_CONST, queryPrefix, QuerySupport.Kind.PREFIX, new String[]{PHPIndexer.FIELD_CONST});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ //internal php index
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_CONST);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(SIGN_NAME_POS);
+
+ if (name.length() > 0 && name.startsWith(prefix)) {
+ Integer offset = sig.integer(SIGN_METHOD_OFFSET_POS);
+ results.add(new PhpIndexResult(name, indexFile, PhpIndexResult.Type.CONSTANT, new OffsetRange(offset, offset + name.length())));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryNamespace(FileObject fo, String prefix) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ Collection namespaces = new ArrayList<>();
+ //subfolders with lowercase ; rootFolder
+ //third signature namespace
+ //the first el is the folder
+ String originalPrefix = prefix;
+
+ if (prefix.endsWith(ESCAPED_NAMESPACE_SEPARATOR)) {
+ return results;
+ }
+
+ String[] queryItems = prefix.split(ESCAPED_NAMESPACE_SEPARATOR);
+
+ if (queryItems.length == 0) {
+ return results;
+ }
+
+ String queryPrefix = prefix.toLowerCase();
+
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(
+ PHPIndexer.FIELD_TOP_LEVEL, queryPrefix, QuerySupport.Kind.PREFIX, new String[]{
+ PHPIndexer.FIELD_NAMESPACE, PHPIndexer.FIELD_TOP_LEVEL});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ String topFieldValue = indexResult.getValue(PHPIndexer.FIELD_TOP_LEVEL);
+ //internal php index
+ if (topFieldValue.startsWith(prefix.toLowerCase())) {
+ String firstValue = indexResult.getValue(PHPIndexer.FIELD_NAMESPACE);
+ if (firstValue == null || firstValue.isEmpty()) {
+ continue;
+ }
+ Signature sig = Signature.get(firstValue);
+
+ String name = sig.string(SIGN_NAME_POS);
+ String namespace = sig.string(SIGN_NAMESPACE_ROOT_POS);
+
+ String fullNamespace = ""; // NOI18N
+
+ if (!namespace.isEmpty()) {
+ fullNamespace = namespace + NAMESPACE_SEPARATOR;
+ }
+
+ fullNamespace += name;
+
+ //just one namespace is enough
+ if (fullNamespace.startsWith(originalPrefix) && !namespaces.contains(fullNamespace)) {
+ namespaces.add(fullNamespace);
+ results.add(new PhpIndexResult(fullNamespace, indexFile, PhpIndexResult.Type.NAMESPACE, new OffsetRange(0, 1)));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryNamespaces(FileObject fo, String namespace,
+ QuerySupport.Kind queryType) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ String queryPrefix = namespace.toLowerCase();
+
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(
+ PHPIndexer.FIELD_TOP_LEVEL, queryPrefix, queryType,
+ new String[]{
+ PHPIndexer.FIELD_NAMESPACE,
+ PHPIndexer.FIELD_TOP_LEVEL
+ });
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ String namespaceValue = indexResult.getValue(PHPIndexer.FIELD_NAMESPACE);
+ //no namespace found
+ if (namespaceValue == null){
+ continue;
+ }
+ results.add(new PhpIndexResult(namespaceValue, indexFile, PhpIndexResult.Type.NAMESPACE, new OffsetRange(0, 1)));
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ /**
+ * a optimized hack solution
+ * assuming that the name of the class is the same with the file
+ *
+ * @param fo
+ * @param namespace
+ * @return
+ */
+ public static Collection queryAllNamespaceClasses(FileObject fo, String namespace) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ String queryPrefix = namespace.toLowerCase();
+
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(
+ PHPIndexer.FIELD_TOP_LEVEL, queryPrefix, QuerySupport.Kind.PREFIX,
+ new String[]{
+ PHPIndexer.FIELD_NAMESPACE,
+ PHPIndexer.FIELD_TOP_LEVEL
+ });
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ String namespaceValue = indexResult.getValue(PHPIndexer.FIELD_NAMESPACE);
+ //no namespace found
+ if (namespaceValue == null){
+ continue;
+ }
+ results.add(new PhpIndexResult(indexFile.getName(), indexFile, PhpIndexResult.Type.CLASS, new OffsetRange(0, 1)));
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryClassConstants(FileObject fo, String prefix, String ownerClass) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ String queryPrefix = prefix.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_CLASS_CONST, queryPrefix, QuerySupport.Kind.PREFIX, new String[]{
+ PHPIndexer.FIELD_CLASS_CONST, PHPIndexer.FIELD_CLASS});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ //internal php index
+
+ String classOwnerName = indexResult.getValue(PHPIndexer.FIELD_CLASS);
+ if (classOwnerName == null || !classOwnerName.startsWith(ownerClass.toLowerCase())) {
+ continue;
+ }
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_CLASS_CONST);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(SIGN_NAME_POS);
+
+ if (name.length() > 0 && name.startsWith(prefix)) {
+ Integer offset = sig.integer(SIGN_METHOD_OFFSET_POS);
+ results.add(new PhpIndexResult(name, indexFile, PhpIndexResult.Type.CONSTANT, new OffsetRange(offset, offset + name.length())));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryExactClassConstants(String prefix, String ownerClass, FileObject fo) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ String queryPrefix = prefix.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_CLASS_CONST, queryPrefix, QuerySupport.Kind.PREFIX, new String[]{
+ PHPIndexer.FIELD_CLASS_CONST, PHPIndexer.FIELD_CLASS});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ //internal php index
+
+ String classOwnerName = indexResult.getValue(PHPIndexer.FIELD_CLASS);
+ if (!classOwnerName.startsWith(ownerClass.toLowerCase() + PHP_INDEX_INFO_SEPARATOR)) {
+ continue;
+ }
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_CLASS_CONST);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(SIGN_NAME_POS);
+
+ if (name.length() > 0 && name.equals(prefix)) {
+ Integer offset = sig.integer(SIGN_METHOD_OFFSET_POS);
+ results.add(new PhpIndexResult(name, indexFile, PhpIndexResult.Type.CONSTANT, new OffsetRange(offset, offset + name.length())));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ public static Collection queryExactConstants(FileObject fo, String prefix) {
+ QuerySupport phpindex = QuerySupportFactory.get(fo);
+ Collection results = new ArrayList<>();
+ String queryPrefix = prefix.toLowerCase();
+ try {
+ Collection extends IndexResult> indexResults = phpindex.query(PHPIndexer.FIELD_CONST, queryPrefix, QuerySupport.Kind.PREFIX, new String[]{PHPIndexer.FIELD_CONST});
+ for (IndexResult indexResult : indexResults) {
+ FileObject indexFile = indexResult.getFile();
+ //internal php index
+
+ String[] values = indexResult.getValues(PHPIndexer.FIELD_CONST);
+ for (String value : values) {
+ Signature sig = Signature.get(value);
+ String name = sig.string(SIGN_NAME_POS);
+
+ if (name.length() > 0 && name.equals(prefix)) {
+ Integer offset = sig.integer(SIGN_METHOD_OFFSET_POS);
+ results.add(new PhpIndexResult(name, indexFile, PhpIndexResult.Type.FUNCTION, new OffsetRange(offset, offset + name.length())));
+ }
+ }
+ }
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return results;
+ }
+
+ static List parseParameters(final String signature) {
+ List retval = new ArrayList<>();
+ if (signature != null && signature.length() > 0) {
+ final String regexp = String.format("\\%s", ","); //NOI18N
+
+ for (String sign : signature.split(regexp)) {
+ try {
+ final String param = parseOneParameter(sign);
+ if (param != null) {
+ retval.add(param);
+ }
+ } catch (NumberFormatException originalException) {
+ final String message = String.format("%s [for signature: %s]", originalException.getMessage(), signature); //NOI18N
+ final NumberFormatException formatException = new NumberFormatException(message);
+ formatException.initCause(originalException);
+ throw formatException;
+ }
+ }
+ }
+ return retval;
+ }
+
+ /**
+ * See more info on org.netbeans.modules.php.editor.model.impl.ParameterImpl for signature structure
+ *
+ * @param sig
+ * @return
+ */
+ static String parseOneParameter(String sig) {
+ String retval = null;
+ final String regexp = String.format("\\%s", ":"); //NOI18N
+ String[] parts = sig.split(regexp);
+ if (parts.length > 0) {
+ String paramName = parts[0];
+ retval = paramName;
+ }
+ return retval;
+ }
+
+ protected static QueryCache> getCache(FileObject fo, String prefix) {
+ QueryCache> selfCache = new QueryCache<>();
+ Project projectOwner = ProjectConvertors.getNonConvertorOwner(fo);
+ if (projectOwner == null) {
+ return null;
+ }
+ int pathHash = projectOwner.getProjectDirectory().toString().hashCode();
+ if (PhpIndexUtils.QUERY_SUPPORT_INSTANCES.containsKey(pathHash)) {
+ PhpIndexUtils indexUtils = QUERY_SUPPORT_INSTANCES.get(pathHash);
+ selfCache = indexUtils.getQueryCache();
+
+ } else {
+ QUERY_SUPPORT_INSTANCES.put(pathHash, new PhpIndexUtils());
+ }
+ return selfCache;
+ }
+
+ protected static QueryCache> getFunctionCache(FileObject fo, String prefix) {
+ QueryCache> selfCache = new QueryCache<>();
+ Project projectOwner = ProjectConvertors.getNonConvertorOwner(fo);
+ if (projectOwner == null) {
+ return null;
+ }
+ int pathHash = projectOwner.getProjectDirectory().toString().hashCode();
+ if (PhpIndexUtils.QUERY_SUPPORT_INSTANCES.containsKey(pathHash)) {
+ PhpIndexUtils indexUtils = QUERY_SUPPORT_INSTANCES.get(pathHash);
+ selfCache = indexUtils.getFunctionQueryCache();
+
+ } else {
+ QUERY_SUPPORT_INSTANCES.put(pathHash, new PhpIndexUtils());
+ }
+ return selfCache;
+ }
+
+ public QueryCache> getQueryCache() {
+ return cache;
+ }
+
+ public QueryCache> getFunctionQueryCache() {
+ return functionCache;
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/QueryUtils.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/QueryUtils.java
new file mode 100644
index 000000000000..18e27e90c94f
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/indexing/QueryUtils.java
@@ -0,0 +1,74 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.indexing;
+
+import java.io.IOException;
+import java.util.List;
+import org.netbeans.api.annotations.common.CheckForNull;
+import org.netbeans.api.project.Project;
+import org.netbeans.modules.php.blade.project.ProjectUtils;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
+
+/**
+ *
+ * @author bogdan
+ */
+public final class QueryUtils {
+
+ private QueryUtils() {
+
+ }
+
+ public static List findYieldReferences(String prefix, FileObject fo) {
+ BladeIndex bladeIndex = getIndex(fo);
+ if (bladeIndex == null) {
+ return List.of();
+ }
+ return bladeIndex.findYieldIndexedReferences(prefix);
+ }
+
+ public static List findStacksReferences(String prefix, FileObject fo) {
+ BladeIndex bladeIndex = getIndex(fo);
+ if (bladeIndex == null) {
+ return List.of();
+ }
+ return bladeIndex.findStackIdIndexedReferences(prefix);
+ }
+
+ public static List getIncludePathReferences(String prefix, FileObject fo) {
+ BladeIndex bladeIndex = getIndex(fo);
+ if (bladeIndex == null) {
+ return List.of();
+ }
+ return bladeIndex.getIncludePaths(prefix);
+ }
+
+ @CheckForNull
+ public static BladeIndex getIndex(FileObject fo) {
+ Project project = ProjectUtils.getMainOwner(fo);
+
+ try {
+ return BladeIndex.get(project);
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ return null;
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeLexer.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeLexer.java
new file mode 100644
index 000000000000..55f9a4574459
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeLexer.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.lexer;
+
+import org.netbeans.api.lexer.Token;
+import static org.netbeans.modules.php.blade.editor.lexer.BladeTokenId.*;
+import org.netbeans.modules.php.blade.syntax.antlr4.v10.BladeAntlrColoringLexer;
+import org.netbeans.spi.lexer.LexerRestartInfo;
+import org.netbeans.spi.lexer.antlr4.AbstractAntlrLexerBridge;
+
+/**
+ *
+ * @author bogdan
+ */
+public class BladeLexer extends AbstractAntlrLexerBridge {
+
+ public BladeLexer(LexerRestartInfo info) {
+ super(info, BladeAntlrColoringLexer::new);
+ }
+
+ @Override
+ public Object state() {
+ return new State(lexer);
+ }
+
+ @Override
+ protected Token mapToken(org.antlr.v4.runtime.Token antlrToken) {
+
+ return switch (antlrToken.getType()) {
+ case BladeAntlrColoringLexer.HTML_TAG, BladeAntlrColoringLexer.HTML -> groupToken(HTML, BladeAntlrColoringLexer.HTML);
+ case BladeAntlrColoringLexer.COMPONENT_ATTR -> groupToken(BLADE_COMPONENT_ATTRIBUTE, BladeAntlrColoringLexer.COMPONENT_ATTR);
+ case BladeAntlrColoringLexer.DIRECTIVE, BladeAntlrColoringLexer.D_PHP, BladeAntlrColoringLexer.D_ENDPHP -> token(BLADE_DIRECTIVE);
+ case BladeAntlrColoringLexer.D_CUSTOM -> token(BLADE_CUSTOM_DIRECTIVE);
+ case BladeAntlrColoringLexer.RAW_TAG, BladeAntlrColoringLexer.CONTENT_TAG -> token(BLADE_ECHO_DELIMITOR);
+ case BladeAntlrColoringLexer.BLADE_PHP_ECHO_EXPR -> token(PHP_BLADE_ECHO_EXPR);
+ case BladeAntlrColoringLexer.D_UNKNOWN, BladeAntlrColoringLexer.D_AT -> groupToken(BLADE_DIRECTIVE_UNKNOWN, BladeAntlrColoringLexer.D_UNKNOWN);
+ case BladeAntlrColoringLexer.BLADE_PAREN -> token(BLADE_PAREN);
+ case BladeAntlrColoringLexer.BLADE_COMMENT_START -> token(BLADE_COMMENT_START);
+ case BladeAntlrColoringLexer.BLADE_COMMENT -> groupToken(BLADE_COMMENT, BladeAntlrColoringLexer.BLADE_COMMENT);
+ case BladeAntlrColoringLexer.BLADE_COMMENT_END -> token(BLADE_COMMENT_END);
+ case BladeAntlrColoringLexer.PHP_INLINE -> token(PHP_INLINE);
+ case BladeAntlrColoringLexer.PHP_EXPRESSION -> groupToken(PHP_BLADE_EXPRESSION, BladeAntlrColoringLexer.PHP_EXPRESSION);
+ case BladeAntlrColoringLexer.BLADE_PHP_INLINE -> groupToken(PHP_BLADE_INLINE_CODE, BladeAntlrColoringLexer.BLADE_PHP_INLINE);
+ case BladeAntlrColoringLexer.ERROR -> token(WS_D);
+ default -> token(OTHER);
+ };
+ }
+
+ private static class State extends AbstractAntlrLexerBridge.LexerState {
+
+ final int rParenBalance;
+ final int compAttrQuoteBalance;
+ final boolean insideComponentTag;
+
+ public State(BladeAntlrColoringLexer lexer) {
+ super(lexer);
+ this.rParenBalance = lexer.rParenBalance;
+ this.compAttrQuoteBalance = lexer.compAttrQuoteBalance;
+ this.insideComponentTag = lexer.insideComponentTag;
+ }
+
+ @Override
+ public void restore(BladeAntlrColoringLexer lexer) {
+ super.restore(lexer);
+ lexer.rParenBalance = rParenBalance;
+ lexer.compAttrQuoteBalance = compAttrQuoteBalance;
+ lexer.insideComponentTag = insideComponentTag;
+ }
+
+ }
+
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeLexerUtils.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeLexerUtils.java
new file mode 100644
index 000000000000..6ba9eea6d89d
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeLexerUtils.java
@@ -0,0 +1,160 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.lexer;
+
+import java.util.List;
+import javax.swing.text.Document;
+import org.netbeans.api.html.lexer.HTMLTokenId;
+import org.netbeans.api.lexer.Language;
+import org.netbeans.api.lexer.Token;
+import org.netbeans.api.lexer.TokenHierarchy;
+import org.netbeans.api.lexer.TokenSequence;
+import org.netbeans.editor.BaseDocument;
+import org.netbeans.modules.php.blade.editor.BladeLanguage;
+import org.netbeans.modules.php.editor.lexer.PHPTokenId;
+
+/**
+ *
+ * @author bogdan
+ */
+public final class BladeLexerUtils {
+
+ private BladeLexerUtils() {
+
+ }
+
+ public static TokenSequence getLockedPhpTokenSequence(Document doc, int offset) {
+ BaseDocument baseDoc = (BaseDocument) doc;
+ TokenSequence tokenSequence = null;
+ baseDoc.readLock();
+ try {
+ TokenHierarchy hierarchy = TokenHierarchy.get(baseDoc);
+ tokenSequence = hierarchy.tokenSequence(PHPTokenId.language());
+ } finally {
+ baseDoc.readUnlock();
+ }
+ if (tokenSequence != null) {
+ tokenSequence.move(offset);
+ tokenSequence.moveNext();
+ }
+ return tokenSequence;
+
+ }
+
+ public static TokenSequence extends PHPTokenId> getPhpTokenSequence(TokenHierarchy th, final int offset) {
+ return getTokenSequence(th, offset, PHPTokenId.language());
+ }
+
+
+ public static TokenSequence extends PHPTokenId> getPhpTokenSequence(final Document document, final int offset) {
+ TokenHierarchy th = TokenHierarchy.get(document);
+ return getTokenSequence(th, offset, PHPTokenId.language());
+ }
+
+ public static TokenSequence extends HTMLTokenId> getHtmlTokenSequence(TokenHierarchy> th, final int offset) {
+ return getTokenSequence(th, offset, HTMLTokenId.language());
+ }
+
+ public static TokenSequence extends HTMLTokenId> getHtmlTokenSequence(final Document document, final int offset) {
+ TokenHierarchy th = TokenHierarchy.get(document);
+ return getTokenSequence(th, offset, HTMLTokenId.language());
+ }
+
+ public static Token extends HTMLTokenId> getHtmlToken(TokenHierarchy> th, final int offset) {
+ TokenSequence extends HTMLTokenId> tsHtml = BladeLexerUtils.getHtmlTokenSequence(th, offset);
+ if (tsHtml == null) {
+ return null;
+ }
+ tsHtml.move(offset);
+
+ if (!tsHtml.moveNext() && !tsHtml.movePrevious()) {
+ return null;
+ }
+
+ return tsHtml.token();
+ }
+
+ public static TokenSequence getTokenSequence(final Document document, final int offset) {
+ return getBladeTokenSequence(TokenHierarchy.get(document), offset);
+ }
+
+ public static TokenSequence getBladeTokenSequence(TokenHierarchy> th, int offset) {
+ BladeLanguage lang = new BladeLanguage();
+ TokenSequence ts = th.tokenSequence(lang.getLexerLanguage());
+
+ return ts;
+ }
+
+ public static TokenSequence getBladeTokenSequenceDoc(TokenHierarchy> th, int offset) {
+ BladeLanguage lang = new BladeLanguage();
+ TokenSequence ts = th.tokenSequence(lang.getLexerLanguage());
+
+ return ts;
+ }
+
+ public static Token getBladeToken(final Document document, final int offset){
+ TokenSequence ts = getTokenSequence(document, offset);
+ return getBladeToken(ts, offset);
+ }
+
+ public static Token getBladeToken(TokenHierarchy> th, final int offset){
+ TokenSequence ts = getBladeTokenSequence(th, offset);
+ return getBladeToken(ts, offset);
+ }
+
+ public static Token getBladeToken(TokenSequence ts, final int offset){
+ if (ts == null){
+ return null;
+ }
+
+ ts.move(offset);
+
+ if (!ts.moveNext() && !ts.movePrevious()) {
+ return null;
+ }
+
+ return ts.token();
+ }
+
+ @SuppressWarnings({"unchecked", "rawtypes"})
+ public static TokenSequence extends T> getTokenSequence(final TokenHierarchy> th,
+ final int offset, final Language language) {
+ TokenSequence extends T> ts = th.tokenSequence(language);
+ if (ts == null) {
+ List> list = th.embeddedTokenSequences(offset, true);
+ for (TokenSequence t : list) {
+ if (t.language() == language) {
+ ts = t;
+ break;
+ }
+ }
+ if (ts == null) {
+ list = th.embeddedTokenSequences(offset, false);
+ for (TokenSequence t : list) {
+ if (t.language() == language) {
+ ts = t;
+ break;
+ }
+ }
+ }
+ }
+ return ts;
+ }
+
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeTokenId.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeTokenId.java
new file mode 100644
index 000000000000..54674dc957c4
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/lexer/BladeTokenId.java
@@ -0,0 +1,103 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.lexer;
+
+import java.util.Collection;
+import java.util.EnumSet;
+import org.netbeans.api.html.lexer.HTMLTokenId;
+import org.netbeans.api.lexer.InputAttributes;
+import org.netbeans.api.lexer.Language;
+import org.netbeans.api.lexer.LanguagePath;
+import org.netbeans.api.lexer.Token;
+import org.netbeans.api.lexer.TokenId;
+import org.netbeans.spi.lexer.LanguageEmbedding;
+import org.netbeans.spi.lexer.LanguageHierarchy;
+import org.netbeans.modules.php.editor.lexer.PHPTokenId;
+
+/**
+ *
+ * @author bogdan
+ */
+public enum BladeTokenId implements TokenId {
+ BLADE_COMMENT_START("blade_comment"), // NOI18N
+ BLADE_COMMENT("blade_comment"), // NOI18N
+ BLADE_COMMENT_END("blade_comment"), // NOI18N
+ BLADE_DIRECTIVE("blade_directive"), // NOI18N
+ BLADE_CUSTOM_DIRECTIVE("blade_directive"), // NOI18N
+ BLADE_ECHO_DELIMITOR("blade_echo_delimiters"), // NOI18N
+ BLADE_TAG_ERROR("html"), // NOI18N
+ BLADE_PAREN("token"), // NOI18N
+ BLADE_COMPONENT_ATTRIBUTE("blade_comp_attribute"), // NOI18N
+ HTML("html"), // NOI18N
+ WS_D("html"), // NOI18N
+ BLADE_DIRECTIVE_UNKNOWN("at_string"), // NOI18N
+ PHP_BLADE_EXPRESSION("blade_php"), // NOI18N
+ PHP_BLADE_ECHO_EXPR("blade_php"), // NOI18N
+ PHP_BLADE_INLINE_CODE("blade_php"), // NOI18N
+ PHP_INLINE("php"), // NOI18N
+ OTHER("error"); // NOI18N
+ private final String category;
+
+ BladeTokenId(String category) {
+ this.category = category;
+ }
+
+ @Override
+ public String primaryCategory() {
+ return category;
+ }
+
+ public static abstract class BladeLanguageHierarchy extends LanguageHierarchy {
+
+ @Override
+ protected Collection createTokenIds() {
+ return EnumSet.allOf(BladeTokenId.class);
+ }
+
+ @Override
+ protected LanguageEmbedding extends TokenId> embedding(Token token,
+ LanguagePath languagePath, InputAttributes inputAttributes) {
+
+ if (token.text() == null) {
+ return null;
+ }
+
+ switch (token.id()) {
+ case PHP_BLADE_EXPRESSION:
+ case PHP_BLADE_INLINE_CODE:
+ case PHP_BLADE_ECHO_EXPR: {
+ //php code without wrapping tags
+ Language extends TokenId> langInPhp = PHPTokenId.languageInPHP();
+ return langInPhp != null ? LanguageEmbedding.create(langInPhp, 0, 0, false) : null;
+ }
+ case PHP_INLINE: {
+ //generic inline ?php ... ?> wrapping tags
+ Language extends TokenId> phpLanguageCode = PHPTokenId.language();
+ return phpLanguageCode != null ? LanguageEmbedding.create(phpLanguageCode, 0, 0, false) : null;
+ }
+ case HTML: {
+ return LanguageEmbedding.create(HTMLTokenId.language(), 0, 0, true);
+ }
+ default: {
+ return null;
+ }
+ }
+ }
+ }
+}
diff --git a/php/php.blade/src/org/netbeans/modules/php/blade/editor/navigator/BladeStructureItem.java b/php/php.blade/src/org/netbeans/modules/php/blade/editor/navigator/BladeStructureItem.java
new file mode 100644
index 000000000000..0e14ecc5e01f
--- /dev/null
+++ b/php/php.blade/src/org/netbeans/modules/php/blade/editor/navigator/BladeStructureItem.java
@@ -0,0 +1,218 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.netbeans.modules.php.blade.editor.navigator;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+import javax.swing.ImageIcon;
+import org.netbeans.modules.csl.api.ElementHandle;
+import org.netbeans.modules.csl.api.ElementKind;
+import org.netbeans.modules.csl.api.HtmlFormatter;
+import org.netbeans.modules.csl.api.Modifier;
+import org.netbeans.modules.csl.api.OffsetRange;
+import org.netbeans.modules.csl.api.StructureItem;
+import org.netbeans.modules.csl.spi.ParserResult;
+import org.netbeans.modules.php.blade.editor.ResourceUtilities;
+import org.openide.filesystems.FileObject;
+
+/**
+ *
+ * @author bhaidu
+ */
+public abstract class BladeStructureItem implements ElementHandle, StructureItem {
+
+ final String name;
+ final FileObject source;
+ final int startOffset;
+ final int stopOffset;
+
+ public BladeStructureItem(String name, FileObject source, int startOffset, int stopOffset) {
+ this.name = name;
+ this.source = source;
+ this.startOffset = startOffset;
+ this.stopOffset = stopOffset;
+ }
+
+ @Override
+ public String getSortText() {
+ return String.format("[%8d]", this.startOffset).replace(' ', '0');
+ }
+
+ @Override
+ public String getHtml(HtmlFormatter formatter) {
+ formatter.appendText(name);
+ return formatter.getText();
+ }
+
+ @Override
+ public ElementHandle getElementHandle() {
+ return this;
+ }
+
+ @Override
+ public long getPosition() {
+ return startOffset;
+ }
+
+ @Override
+ public long getEndPosition() {
+ return stopOffset;
+ }
+
+ @Override
+ public ImageIcon getCustomIcon() {
+ return null;
+ }
+
+ @Override
+ public FileObject getFileObject() {
+ return source;
+ }
+
+ @Override
+ public String getMimeType() {
+ return source.getMIMEType();
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getIn() {
+ return null;
+ }
+
+ @Override
+ public Set getModifiers() {
+ return Collections.emptySet();
+ }
+
+ @Override
+ public boolean signatureEquals(ElementHandle handle) {
+ return false;
+ }
+
+ @Override
+ public OffsetRange getOffsetRange(ParserResult result) {
+ return new OffsetRange(startOffset, stopOffset);
+ }
+
+ public static final class DirectiveBlockStructureItem extends BladeStructureItem {
+
+ private String identifier;
+
+ public final List nestedItems = new ArrayList<>();
+
+ public DirectiveBlockStructureItem(String name, FileObject source, int startOffset, int stopOffset) {
+ super(name, source, startOffset, stopOffset);
+ }
+
+ public DirectiveBlockStructureItem(String name, String identifier, FileObject source, int startOffset, int stopOffset) {
+ super(name, source, startOffset, stopOffset);
+ this.identifier = identifier;
+ }
+
+ @Override
+ public boolean isLeaf() {
+ return nestedItems.isEmpty();
+ }
+
+ @Override
+ public List extends StructureItem> getNestedItems() {
+ return nestedItems;
+ }
+
+ @Override
+ public ElementKind getKind() {
+ return ElementKind.CLASS;
+ }
+
+ @Override
+ public String getHtml(HtmlFormatter formatter) {
+ formatter.appendText(name);
+ if (identifier != null) {
+ formatter.appendText(" ");
+ formatter.appendHtml("