public void injectAll(Disposable parent) { injectVariousStuffEverywhere(parent, myPsiManager); Project project = myPsiManager.getProject(); Language ql = Language.findLanguageByID("JPAQL"); Language js = Language.findLanguageByID("JavaScript"); registerForStringVarInitializer(parent, project, ql, "ql", null, null); registerForStringVarInitializer(parent, project, ql, "qlPrefixed", "xxx", null); registerForStringVarInitializer(parent, project, js, "js", null, null); registerForStringVarInitializer(parent, project, js, "jsSeparated", " + ", " + 'separator'"); registerForStringVarInitializer(parent, project, js, "jsBrokenPrefix", "xx ", ""); registerForStringVarInitializer( parent, project, Language.findLanguageByID("Oracle"), "oracle", null, null); registerForParameterValue(parent, project, Language.findLanguageByID("Groovy"), "groovy"); }
public AndroidLayoutPreviewToolWindowManager( final Project project, final FileEditorManager fileEditorManager) { myProject = project; myFileEditorManager = fileEditorManager; myToolWindowUpdateQueue = new MergingUpdateQueue("android.layout.preview", 100, true, null, project); final MessageBusConnection connection = project.getMessageBus().connect(project); connection.subscribe( FileEditorManagerListener.FILE_EDITOR_MANAGER, new MyFileEditorManagerListener()); connection.subscribe(ProjectTopics.PROJECT_ROOTS, new MyAndroidPlatformListener(project)); PsiManager.getInstance(project) .addPsiTreeChangeListener( new PsiTreeChangeAdapter() { boolean myIgnoreChildrenChanged; @Override public void beforeChildrenChange(@NotNull PsiTreeChangeEvent event) { myIgnoreChildrenChanged = false; } @Override public void childrenChanged(@NotNull PsiTreeChangeEvent event) { // See ResourceFolderManager#PsiListener#childrenChanged if (isRelevant(event) && !myIgnoreChildrenChanged && event.getParent() != event.getChild()) { update(event); } } @Override public void childAdded(@NotNull PsiTreeChangeEvent event) { myIgnoreChildrenChanged = true; if (isRelevant(event)) { PsiElement child = event.getChild(); PsiElement parent = event.getParent(); if (child instanceof XmlAttribute && parent instanceof XmlTag) { // Typing in a new attribute. Don't need to do any rendering until there // is an actual value if (((XmlAttribute) child).getValueElement() == null) { return; } } else if (parent instanceof XmlAttribute && child instanceof XmlAttributeValue) { XmlAttributeValue attributeValue = (XmlAttributeValue) child; if (attributeValue.getValue() == null || attributeValue.getValue().isEmpty()) { // Just added a new blank attribute; nothing to render yet return; } } else if (parent instanceof XmlAttributeValue && child instanceof XmlToken && event.getOldChild() == null) { // Just added attribute value String text = child.getText(); // See if this is an attribute that takes a resource! if (text.startsWith(PREFIX_RESOURCE_REF)) { if (text.equals(PREFIX_RESOURCE_REF) || text.equals(ANDROID_PREFIX)) { // Using code completion to insert resource reference; not yet done return; } ResourceUrl url = ResourceUrl.parse(text); if (url != null && url.name.isEmpty()) { // Using code completion to insert resource reference; not yet done return; } } } update(event); } } @Override public void childReplaced(@NotNull PsiTreeChangeEvent event) { myIgnoreChildrenChanged = true; if (isRelevant(event)) { PsiElement child = event.getChild(); PsiElement parent = event.getParent(); if (parent instanceof XmlAttribute && child instanceof XmlToken) { // Typing in attribute name. Don't need to do any rendering until there // is an actual value XmlAttributeValue valueElement = ((XmlAttribute) parent).getValueElement(); if (valueElement == null || valueElement.getValue() == null || valueElement.getValue().isEmpty()) { return; } } else if (parent instanceof XmlAttributeValue && child instanceof XmlToken && event.getOldChild() != null) { String newText = child.getText(); String prevText = event.getOldChild().getText(); // See if user is working on an incomplete URL, and is still not complete, e.g. // typing in @string/foo manually if (newText.startsWith(PREFIX_RESOURCE_REF)) { ResourceUrl prevUrl = ResourceUrl.parse(prevText); ResourceUrl newUrl = ResourceUrl.parse(newText); if (prevUrl != null && prevUrl.name.isEmpty()) { prevUrl = null; } if (newUrl != null && newUrl.name.isEmpty()) { newUrl = null; } if (prevUrl == null && newUrl == null) { return; } } } update(event); } } @Override public void childRemoved(@NotNull PsiTreeChangeEvent event) { myIgnoreChildrenChanged = true; if (isRelevant(event)) { PsiElement child = event.getChild(); PsiElement parent = event.getParent(); if (parent instanceof XmlAttribute && child instanceof XmlToken) { // Typing in attribute name. Don't need to do any rendering until there // is an actual value XmlAttributeValue valueElement = ((XmlAttribute) parent).getValueElement(); if (valueElement == null || valueElement.getValue() == null || valueElement.getValue().isEmpty()) { return; } } update(event); } } }, project); ProjectBuilder.getInstance(project) .addAfterProjectBuildTask( new ProjectBuilder.AfterProjectBuildListener() { @Override protected void buildFinished() { if (myToolWindowForm != null && myToolWindowReady && !myToolWindowDisposed) { ApplicationManager.getApplication() .invokeLater( new Runnable() { @Override public void run() { render(); } }); } } }); }
private static void injectVariousStuffEverywhere(Disposable parent, final PsiManager psiManager) { final Language ql = Language.findLanguageByID("JPAQL"); final Language js = Language.findLanguageByID("JavaScript"); final Language html = Language.findLanguageByID("HTML"); if (ql == null || js == null) return; final Language ecma4 = Language.findLanguageByID("ECMA Script Level 4"); final MultiHostInjector myMultiHostInjector = new MultiHostInjector() { @Override public void getLanguagesToInject( @NotNull MultiHostRegistrar registrar, @NotNull PsiElement context) { XmlAttributeValue value = (XmlAttributeValue) context; PsiElement parent = value.getParent(); if (parent instanceof XmlAttribute) { @NonNls String attrName = ((XmlAttribute) parent).getLocalName(); if ("jsInBraces".equals(attrName)) { registrar.startInjecting(js); String text = value.getText(); int index = 0; while (text.indexOf('{', index) != -1) { int lbrace = text.indexOf('{', index); int rbrace = text.indexOf('}', index); registrar.addPlace( "", "", (PsiLanguageInjectionHost) value, new TextRange(lbrace + 1, rbrace)); index = rbrace + 1; } registrar.doneInjecting(); } } } @Override @NotNull public List<? extends Class<? extends PsiElement>> elementsToInjectIn() { return Arrays.asList(XmlAttributeValue.class); } }; InjectedLanguageManager.getInstance(psiManager.getProject()) .registerMultiHostInjector(myMultiHostInjector); Disposer.register( parent, new Disposable() { @Override public void dispose() { boolean b = InjectedLanguageManager.getInstance(psiManager.getProject()) .unregisterMultiHostInjector(myMultiHostInjector); assert b; } }); final LanguageInjector myInjector = new LanguageInjector() { @Override public void getLanguagesToInject( @NotNull PsiLanguageInjectionHost host, @NotNull InjectedLanguagePlaces placesToInject) { if (host instanceof XmlAttributeValue) { XmlAttributeValue value = (XmlAttributeValue) host; PsiElement parent = value.getParent(); if (parent instanceof XmlAttribute) { @NonNls String attrName = ((XmlAttribute) parent).getLocalName(); if ("ql".equals(attrName)) { inject(host, placesToInject, ql); return; } if ("js".equals(attrName)) { inject(host, placesToInject, js); return; } if ("jsprefix".equals(attrName)) { inject(host, placesToInject, js, "function foo(doc, window){", "}"); return; } } } if (host instanceof XmlText) { // inject to xml tags named 'ql' final XmlText xmlText = (XmlText) host; XmlTag tag = xmlText.getParentTag(); if (tag == null) return; if ("ql".equals(tag.getLocalName())) { inject(host, placesToInject, ql); return; } if ("js".equals(tag.getLocalName())) { inject(host, placesToInject, js); return; } if ("htmlInject".equals(tag.getLocalName())) { inject(host, placesToInject, html); return; } if (ecma4 != null && "ecma4".equals(tag.getLocalName())) { inject(host, placesToInject, ecma4); return; } if ("jsprefix".equals(tag.getLocalName())) { inject(host, placesToInject, js, "function foo(doc, window){", "}"); return; } if ("jsInHash".equals(tag.getLocalName())) { String text = xmlText.getText(); if (text.contains("#")) { int start = text.indexOf('#'); int end = text.lastIndexOf('#'); if (start != end && start != -1) { placesToInject.addPlace(js, new TextRange(start + 1, end), null, null); return; } } } } if (host instanceof PsiComment && ((PsiComment) host).getTokenType() == JavaTokenType.C_STYLE_COMMENT) { /* {{{ * js code * js code * }}} */ String text = host.getText(); String prefix = "/*\n * {{{\n"; String suffix = " }}}\n */"; if (text.startsWith(prefix) && text.endsWith(suffix)) { String s = StringUtil.trimEnd(StringUtil.trimStart(text, prefix), suffix); int off = 0; while (!s.isEmpty()) { String t = s.trim(); if (t.startsWith("*")) t = t.substring(1).trim(); int i = s.length() - t.length(); off += i; int endOfLine = t.indexOf('\n'); if (endOfLine == -1) endOfLine = t.length(); placesToInject.addPlace( js, TextRange.from(prefix.length() + off, endOfLine), "", "\n"); off += endOfLine; s = s.substring(i + endOfLine); } return; } } if (host instanceof PsiCommentImpl) { String text = host.getText(); if (text.startsWith("/*--{") && text.endsWith("}--*/")) { TextRange textRange = new TextRange(4, text.length() - 4); if (!(host.getParent() instanceof PsiMethod)) return; PsiMethod method = (PsiMethod) host.getParent(); if (!method.hasModifierProperty(PsiModifier.NATIVE) || !method.hasModifierProperty(PsiModifier.PUBLIC)) return; String paramList = ""; for (PsiParameter parameter : method.getParameterList().getParameters()) { if (!paramList.isEmpty()) paramList += ","; paramList += parameter.getName(); } @NonNls String header = "function " + method.getName() + "(" + paramList + ") {"; Language gwt = Language.findLanguageByID("GWT JavaScript"); placesToInject.addPlace(gwt, textRange, header, "}"); return; } PsiElement parent = host.getParent(); if (parent instanceof PsiMethod && ((PsiMethod) parent).getName().equals("xml")) { placesToInject.addPlace( StdLanguages.XML, new TextRange(2, host.getTextLength() - 2), null, null); return; } } // inject to all string literal initializers of variables named 'ql' if (host instanceof PsiLiteralExpression && ((PsiLiteralExpression) host).getValue() instanceof String) { PsiVariable variable = PsiTreeUtil.getParentOfType(host, PsiVariable.class); if (variable == null) return; if (host.getParent() instanceof PsiPolyadicExpression) return; if ("ql".equals(variable.getName())) { placesToInject.addPlace(ql, textRangeToInject(host), null, null); } if ("xml".equals(variable.getName())) { placesToInject.addPlace(StdLanguages.XML, textRangeToInject(host), null, null); } if ("js".equals(variable.getName())) { // with prefix/suffix placesToInject.addPlace( js, textRangeToInject(host), "function foo(doc,window) {", "}"); } if ("lang".equals(variable.getName())) { // various lang depending on field "languageID" content PsiClass aClass = PsiTreeUtil.getParentOfType(variable, PsiClass.class); aClass = aClass.findInnerClassByName("Language", false); String text = aClass .getInitializers()[0] .getBody() .getFirstBodyElement() .getNextSibling() .getText() .substring(2); Language language = Language.findLanguageByID(text); if (language != null) { placesToInject.addPlace(language, textRangeToInject(host), "", ""); } } } } }; final ExtensionPoint<LanguageInjector> extensionPoint = Extensions.getRootArea().getExtensionPoint(LanguageInjector.EXTENSION_POINT_NAME); extensionPoint.registerExtension(myInjector); Disposer.register( parent, new Disposable() { @Override public void dispose() { extensionPoint.unregisterExtension(myInjector); } }); }