private List<LookupElement> getSubCompletions(PsiElement psiElement) { JSProperty property = PsiTreeUtil.getParentOfType(psiElement, JSProperty.class); if (property == null) { return Collections.emptyList(); } String name = property.getQualifiedName(); if (name == null) { return Collections.emptyList(); } Setting setting = CompletionPreloader .getCompletions() .getSetting(name); return setting != null ? setting.getSubCompletionVariants() : Collections.emptyList(); }
@Override public void visitJSProperty(JSProperty node) { if (done.contains(node.getQualifiedName())) { return; } if (!CompletionPreloader.isRocConfigFile(node.getContainingFile())) { this.stopWalking(); return; } inspectProperty(node); }
private static PsiElementPattern.Capture<JSLiteralExpression> literalInProperty(final String propertyName) { return PlatformPatterns.psiElement(JSLiteralExpression.class).and(new FilterPattern(new ElementFilter() { @Override public boolean isAcceptable(Object element, @Nullable PsiElement context) { if (element instanceof JSLiteralExpression) { final JSLiteralExpression literal = (JSLiteralExpression)element; if (literal.isQuotedLiteral()) { final PsiElement parent = literal.getParent(); if (parent instanceof JSProperty && propertyName.equals(((JSProperty)parent).getName())) { return EmberIndexUtil.hasEmberJS(literal.getProject()); } } } return false; } @Override public boolean isClassAcceptable(Class hintClass) { return true; } })); }
@Override public boolean processDeclarations(@NotNull final PsiScopeProcessor processor, @NotNull final ResolveState state, final PsiElement lastParent, @NotNull final PsiElement place) { if(lastParent == null || !(place instanceof JSProperty)) { return true; } final JSProperty[] props = getProperties(); for(JSProperty property : props) { if(!processor.execute(property, state)) { return false; } } return true; }
@Override @RequiredReadAction public boolean isReferenceTo(PsiElement element) { final PsiElement element2 = resolve(); boolean proxyExpanded = false; if(element instanceof JSDefinitionExpression) { final JSExpression expression = ((JSDefinitionExpression) element).getExpression(); if(expression instanceof JSReferenceExpression) { return ((JSReferenceExpression) expression).isReferenceTo(element2); } } if(element != element2 && element instanceof JSProperty && element2 instanceof JSProperty) { return ((JSProperty) element).getName().equals(((JSProperty) element2).getName()); } return proxyExpanded && element == element2; }
@Override @RequiredReadAction public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String propertyName = JomUtil.getJsonGetPropertyName(method); if(propertyName != null) { JSProperty jsProperty = findProperty(myObjectLiteralExpression, propertyName); if(jsProperty == null) { return JomValueConverter.getDefaultValueForType(method.getReturnType()); } else { try { return JomValueConverter.convertToObject(method.getReturnType(), method.getGenericReturnType(), jsProperty.getValue()); } catch(JomBadValueExpressionException e) { return JomValueConverter.getDefaultValueForType(method.getReturnType()); } } } return null; }
@Nullable private static JSProperty findProperty(@Nullable JSObjectLiteralExpression objectLiteralExpression, String name) { if(objectLiteralExpression == null) { return null; } for(JSProperty property : objectLiteralExpression.getProperties()) { if(Comparing.equal(property.getName(), name)) { return property; } } return null; }
static List<SettingLookupElement> buildCompletions(CompletionParameters parameters, String prefix) { PsiElement position = parameters.getPosition(); JSProperty property = PsiTreeUtil.getParentOfType(position, JSProperty.class); // Unable to determine where in roc.settings.js we are. if (property == null) { return Collections.emptyList(); } String namespace = property .getJSNamespace() .toString(); // Wrong file, or wrong structure. if (!namespace.startsWith(SettingContainer.ROOT_NAMESPACE)) { return Collections.emptyList(); } SettingContainer settingContainer = CompletionPreloader.getCompletions(); Map<String, JSProperty> existingSettings = findExistingSettings(parameters, prefix); List<Setting> settings = settingContainer.getSettings(namespace, existingSettings); return settings .stream() .map(setting -> new SettingLookupElement(setting, namespace)) .collect(Collectors.toCollection(ArrayList::new)); }
static private Map<String, JSProperty> findExistingSettings(CompletionParameters parameters, String prefix) { PsiFile file = parameters.getOriginalFile(); StringBuffer buffer; // User was typing and probably screwed up the syntax in the process :-/ if (parameters.isAutoPopup()) { PsiElement position = parameters.getPosition(); Integer startOffset = position.getTextOffset(); Integer endOffset = startOffset + prefix.length(); buffer = new StringBuffer(file.getText()); buffer = buffer.replace(startOffset, endOffset, ""); file = PsiFileFactory .getInstance(position.getProject()) .createFileFromText(position.getLanguage(), buffer.toString()); } try { RecursivePropertyWalker walker = new RecursivePropertyWalker(); walker.visitFile(file); return walker.getExistingProperties(); } catch (Exception e) { return Collections.emptyMap(); } }
private JSProperty getTargetProperty(PsiFile configFile) { // Braaaaaaiiins!!! RecursivePropertyWalker walker = new RecursivePropertyWalker(); walker.visitFile(configFile); Map<String, JSProperty> existingProperties = walker.getExistingProperties(); ArrayList<String> parts = Lists.newArrayList(setting.getNamespace().split("\\.")); JSProperty property; // Begin at the bottom and locate the nearest property. while (true) { String propertyNamespace = String.join(".", parts); property = existingProperties.get(propertyNamespace); if (property != null || parts.size() == 0) { break; } parts.remove(parts.size() - 1); } return property; }
@Nullable @Override public List<String> getUrlFor(PsiElement psiElement, PsiElement originalElement) { List<String> urls = new ArrayList<>(); if (!CompletionPreloader.isRocConfigFile(psiElement.getContainingFile())) { return urls; } try { JSProperty property = (JSProperty)psiElement; String namespace = property .getJSNamespace() .toString(); if (!namespace.startsWith(SettingContainer.ROOT_NAMESPACE)) { return urls; } urls.add(property.getQualifiedName()); } catch (Exception ignored) {} return urls; }
@NotNull @Override public ThreeState shouldSkipAutopopup(@NotNull PsiElement contextElement, @NotNull PsiFile psiFile, int offset) { // Wrong file. if (!CompletionPreloader.isRocConfigFile(psiFile)) { return ThreeState.UNSURE; } JSProperty property = PsiTreeUtil.getParentOfType(contextElement, JSProperty.class); // Wrong place in file. if (property == null) { return ThreeState.UNSURE; } Setting setting = CompletionPreloader .getCompletions() .getSetting(property.getQualifiedName()); // Not a roc-setting. if (setting == null) { return ThreeState.UNSURE; } return setting.getSubCompletionVariants().size() > 1 ? ThreeState.NO : ThreeState.UNSURE; }
public String toRelativeJsNotation(JSProperty property, String quote) { String remainder = getNamespace().replaceFirst(property.getQualifiedName() + ".", ""); // This is the js-properties we need to create. List<String> remainderParts = Lists.newArrayList(remainder.split("\\.")); String jsObject = "%s"; // Create all needed object-levels. for (int i = 0; i < remainderParts.size() - 1; i++) { String remainderPart = getFormattedRemainderPart(remainderParts, i, quote); String jsPart = remainderPart + ": { %s }"; jsObject = String.format(jsObject, jsPart); } String valueName = getFormattedRemainderPart(remainderParts, remainderParts.size() - 1, quote); // Insert an "intelligent" default-value. String valuePart = String.format("%s: %s,", valueName, defaultValue.getTargetValue(quote)); jsObject = String.format(jsObject, valuePart); // If we needed to create additional levels, odds are we need a comma. if (remainderParts.size() > 1) { jsObject += ","; } return jsObject; }
public List<Setting> getSettings(String namespace, Map<String, JSProperty> existingSettings) { return flatList .subMap(namespace, namespace + Character.MAX_VALUE) .values() .stream() .filter(setting -> !existingSettings.containsKey(setting.getNamespace())) .collect(Collectors.toList()); }
@Nullable public static JSProperty getProperty(@NotNull PsiElement position) { JSProperty property = PsiTreeUtil.getParentOfType(position, JSProperty.class, false); if (property != null) { JSObjectLiteralExpression objectLiteralExpression = ObjectUtils.tryCast(property.getParent(), JSObjectLiteralExpression.class); if (objectLiteralExpression != null) { return property; } } return null; }
@Nullable public static PsiElement getStringLiteral(@NotNull JSProperty property) { PsiElement firstElement = property.getFirstChild(); if (firstElement != null && isStringLiteral(firstElement)) { return firstElement; } return null; }
@Override public JSProperty[] getProperties() { List<JSProperty> props = new ArrayList<JSProperty>(); for(Map.Entry<String, String> entry : properties.entrySet()) { JSProperty property = mock(JSProperty.class); // TODO props.add(property); } return props.toArray(new JSProperty[0]); }
@Override public JSProperty[] getProperties() { final ASTNode[] nodes = getNode().getChildren(PROPERTIES_FILTER); final JSProperty[] properties = new JSProperty[nodes.length]; for(int i = 0; i < properties.length; i++) { properties[i] = (JSProperty) nodes[i].getPsi(); } return properties; }
@RequiredReadAction @Nullable @Override public PsiReference getReference(@NotNull JSProperty property) { PsiElement nameIdentifier = property.getNameIdentifier(); assert nameIdentifier != null; return new JSPropertyNameReference(property, nameIdentifier); }
@Override public boolean isSafeDeleteAvailable(PsiElement element) { boolean simpleElement = element instanceof JSFunction || element instanceof JSVariable || element instanceof JSDefinitionExpression || element instanceof JSProperty || element instanceof JSClass; return simpleElement && ((JSNamedElement) element).getName() != null; }
@Nullable @RequiredReadAction public static JsonPropertyDescriptor findPropertyDescriptor(@NotNull final JSProperty jsProperty) { return CachedValuesManager.getManager(jsProperty.getProject()).createCachedValue(new CachedValueProvider<JsonPropertyDescriptor>() { @Nullable @Override @RequiredReadAction public Result<JsonPropertyDescriptor> compute() { return Result.create(findPropertyDescriptorImpl(jsProperty), jsProperty, PsiModificationTracker.OUT_OF_CODE_BLOCK_MODIFICATION_COUNT); } }, false).getValue(); }
@RequiredReadAction @Override public boolean isReferenceTo(PsiElement element) { if(element instanceof JSProperty) { JsonPropertyDescriptor propertyDescriptor = PropertyValidationInspection.findPropertyDescriptor((JSProperty) element); return myPropertyDescriptor == propertyDescriptor; } else if(element == myPropertyDescriptor.getNavigationElement()) { return true; } return false; }
@RequiredReadAction @Nullable @Override public PsiReference getReference(@NotNull JSProperty property) { JsonPropertyDescriptor propertyDescriptor = PropertyValidationInspection.findPropertyDescriptor(property); if(propertyDescriptor == null) { return null; } PsiElement nameIdentifier = property.getNameIdentifier(); assert nameIdentifier != null; return new JsonPropertyNameReference(property, nameIdentifier, propertyDescriptor); }
@RequiredReadAction @Override @SuppressWarnings("unchecked") public Map parseValue(@NotNull Class type, @NotNull Type genericType, @NotNull PsiElement value) throws JomBadValueExpressionException { if(!(value instanceof JSObjectLiteralExpression)) { throw new JomBadValueExpressionException(); } Pair<Class, Type> valueType = JomCollectionValue.findValueTypeInsideGeneric(genericType, 1); // K, V JSProperty[] properties = ((JSObjectLiteralExpression) value).getProperties(); Map map = new LinkedHashMap(properties.length); for(JSProperty property : properties) { String name = property.getName(); if(name == null) { continue; } try { Object object = JomValueConverter.convertToObject(valueType.getFirst(), valueType.getSecond(), property.getValue()); map.put(name, object); } catch(JomBadValueExpressionException e) { // we dont interest in bad value } } return map; }
@Override public void visitJSObjectLiteralExpression(JSObjectLiteralExpression jsObjectLiteralExpression) { super.visitJSObjectLiteralExpression(jsObjectLiteralExpression); final JSProperty[] properties = jsObjectLiteralExpression.getProperties(); final boolean[] matched = new boolean[properties.length]; Arrays.fill(matched, false); for (int i = 0; i < properties.length; i++) { if (matched[i]) { continue; } final JSProperty property1 = properties[i]; for (int j = i + 1; j < properties.length; j++) { if (matched[j]) { continue; } final JSProperty property2 = properties[j]; final String property1Name = property1.getName(); final String property2Name = property2.getName(); if(property1Name !=null && property2Name!=null && property1Name.equals(property2Name)) { registerError(property2.getFirstChild()); if (!matched[i]) { registerError(property1.getFirstChild()); } matched[i] = true; matched[j] = true; } } } }
@Override public void handleInsert(InsertionContext context) { // Remove the text the user typed. context .getDocument() .deleteString(context.getStartOffset(), context.getTailOffset()); context.commitDocument(); // Grab the deepest property available for insertion... hø hø hø! JSProperty targetProperty = getTargetProperty(context.getFile()); // Generate the remaining jsNotation. String quote = JSCodeStyleSettings.getQuote(targetProperty); String jsNotation = setting.toRelativeJsNotation(targetProperty, quote); // Create a new file to initiate all lexers, parsers grumpkins and snarks... PsiFile psiContainer = PsiFileFactory .getInstance(context.getProject()) .createFileFromText(targetProperty.getLanguage(), jsNotation); JSExpression value = targetProperty.getValue(); if (value == null) { return; } PsiElement[] childrenForInsertion = psiContainer.getChildren(); // Insert in reverse order. Probably an easier way, but this works just fine... ArrayUtils.reverse(childrenForInsertion); // Grab all created elements in the temporary file and insert them into the document. for (PsiElement completion : childrenForInsertion) { value.addAfter(completion, value.getFirstChild()); } PsiElement formattedValue = CodeStyleManager .getInstance(context.getProject()) .reformat(value); value.replace(formattedValue); List<LookupElement> subCompletions = setting.getSubCompletionVariants(); // User does not need to edit bools or enums with single value. if (setting.getType().equals("Boolean") || subCompletions.size() == 1) { return; } context.commitDocument(); moveCursor(context); if (subCompletions.size() > 1) { AutoPopupController.getInstance(context.getProject()).scheduleAutoPopup(context.getEditor()); } }
Map<String, JSProperty> getExistingProperties() { return existingProperties; }
@Override public void visitJSProperty(JSProperty node) { existingProperties.put(node.getQualifiedName(), node); super.visitJSProperty(node); }
private JSProperty getParentProperty(PsiElement psiElement) { return PsiTreeUtil.getParentOfType(psiElement, JSProperty.class); }
private void inspectProperty(JSProperty node) { String namespace = node.getQualifiedName(); done.add(namespace); Setting setting = CompletionPreloader .getCompletions() .getSetting(namespace); JSExpression nodeValue = node.getValue(); if (setting == null || nodeValue == null) { return; } String value; // We don't want the quotes included. try { JSLiteralExpression literalExpression = (JSLiteralExpression)nodeValue; value = (String)literalExpression.getValue(); } catch (Exception e) { value = nodeValue.getText(); } String defaultValue = setting.getDefaultValue(); if (value == null) { return; } // Can't get hold of CodeStyleManager here because not dispatch. Manual labor ahead. List<String> valueCandidates = generateCandidates(value); List<String> defaultValueCandidates = generateCandidates(defaultValue); Boolean someSortOfMatch = false; for (String valueCandidate : valueCandidates) { if (defaultValueCandidates.contains(valueCandidate)) { someSortOfMatch = true; break; } } if (!someSortOfMatch) { return; } holder.registerProblem(node, "Value equals default-value", new QuickFix()); }
@Nullable public PsiFile findTemplateFromDeclare(@Nullable DeclareStatementItems statement) { if(statement == null) { return null; } for(JSProperty property : statement.getMethodsToConvert()) { // just continue if this property is invalid for some reason if(property == null || property.getName() == null || property.getValue() == null) { continue; } /** * have to account for these scenarios * templateString: <reference to an imported template> * templateString: 'inline template' * templateString: 'inline template ' + * ' spanning multiple lines ' */ if(property.getName().equals("templateString")) { String template = property.getValue().getText(); if(property.getValue() instanceof JSLiteralExpression || property.getValue() instanceof JSBinaryExpression) { return property.getContainingFile(); } else { // find the parameter and define that matches the template parameter PsiElement relevantDefine = AMDPsiUtil.getDefineForVariable(file, template); if(relevantDefine == null) { // we couldn't find the module that templateString reference for whatever reason // (it could be an invalid reference to a module) return null; } String templatePath = relevantDefine.getText().substring(relevantDefine.getText().lastIndexOf('!') + 1); // now open the file and find the reference in it VirtualFile htmlFile = SourcesLocator.getAMDImportFile(relevantDefine.getProject(), templatePath, relevantDefine.getContainingFile().getContainingDirectory()); // if we can't resolve it return null for now TODO if(htmlFile == null) { return null; } PsiFile templateFile = PsiManager.getInstance(file.getProject()).findFile(htmlFile); return templateFile; } } } return null; }
public DeclareStatementItems(JSExpression[] expressionsToMixin, JSProperty[] methodsToConvert, JSElement returnStatement) { this.expressionsToMixin = expressionsToMixin; this.methodsToConvert = methodsToConvert; this.declareContainingStatement = returnStatement; }
public DeclareStatementItems(JSLiteralExpression className, JSExpression[] expressionsToMixin, JSProperty[] methodsToConvert, JSElement declareContainingStatement) { this(expressionsToMixin, methodsToConvert, declareContainingStatement); this.className = className; }
public JSProperty[] getMethodsToConvert() { return methodsToConvert; }
public JSPropertyNameReference(JSProperty property, PsiElement nameIdentifier) { this.myProperty = property; myNameIdentifier = nameIdentifier; }
@Nullable private static JSArgumentList fillSignaturesForArgumentList(final CreateParameterInfoContext context, final @NotNull JSArgumentList argList) { final PsiElement psiElement = argList.getParent(); if(!(psiElement instanceof JSCallExpression)) { return null; } final JSCallExpression parent = (JSCallExpression) psiElement; final JSExpression methodExpression = parent.getMethodExpression(); if(methodExpression instanceof JSReferenceExpression) { final ResolveResult[] resolveResults = ((JSReferenceExpression) methodExpression).multiResolve(true); if(resolveResults.length > 0) { List<JSFunction> items = new ArrayList<JSFunction>(resolveResults.length); Set<String> availableSignatures = new HashSet<String>(); for(ResolveResult r : resolveResults) { PsiElement element = r.getElement(); if(element instanceof JSProperty) { element = ((JSProperty) element).getValue(); } doAddSignature(items, availableSignatures, element); } context.setItemsToShow(ArrayUtil.toObjectArray(items)); return argList; } } else if(methodExpression instanceof JSSuperExpression) { final PsiElement clazz = methodExpression.getReference().resolve(); if(clazz instanceof JSFunction) { context.setItemsToShow(new Object[]{clazz}); return argList; } } return null; }