/* * Copyright 2011-present Greg Shrago * * Licensed 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.intellij.grammar; import com.intellij.concurrency.AsyncFutureFactory; import com.intellij.concurrency.AsyncFutureFactoryImpl; import com.intellij.concurrency.JobLauncher; import com.intellij.concurrency.JobLauncherImpl; import com.intellij.ide.startup.impl.StartupManagerImpl; import com.intellij.lang.*; import com.intellij.lang.impl.PsiBuilderFactoryImpl; import com.intellij.lang.impl.PsiBuilderImpl; import com.intellij.lang.injection.InjectedLanguageManager; import com.intellij.lexer.Lexer; import com.intellij.mock.*; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.editor.EditorFactory; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.extensions.ExtensionsArea; import com.intellij.openapi.fileEditor.FileDocumentManager; import com.intellij.openapi.fileEditor.impl.FileDocumentManagerImpl; import com.intellij.openapi.fileTypes.FileTypeFactory; import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.fileTypes.PlainTextLanguage; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.progress.impl.ProgressManagerImpl; import com.intellij.openapi.project.DumbService; import com.intellij.openapi.project.DumbServiceImpl; import com.intellij.openapi.project.Project; import com.intellij.openapi.startup.StartupManager; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.vfs.encoding.EncodingManager; import com.intellij.openapi.vfs.encoding.EncodingManagerImpl; import com.intellij.psi.*; import com.intellij.psi.impl.PsiCachedValuesFactory; import com.intellij.psi.impl.PsiFileFactoryImpl; import com.intellij.psi.impl.search.CachesBasedRefSearcher; import com.intellij.psi.impl.search.PsiSearchHelperImpl; import com.intellij.psi.impl.source.CharTableImpl; import com.intellij.psi.impl.source.resolve.ResolveCache; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry; import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl; import com.intellij.psi.impl.source.tree.injected.InjectedLanguageManagerImpl; import com.intellij.psi.search.PsiSearchHelper; import com.intellij.psi.util.CachedValuesManager; import com.intellij.testFramework.LightVirtualFile; import com.intellij.util.CachedValuesManagerImpl; import com.intellij.util.messages.MessageBus; import com.intellij.util.messages.MessageBusFactory; import org.intellij.grammar.java.JavaHelper; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.picocontainer.*; import org.picocontainer.defaults.AbstractComponentAdapter; import java.io.*; import java.lang.reflect.Modifier; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * @author greg * @noinspection UseOfSystemOutOrSystemErr */ public class LightPsi { private static final MyParsing ourParsing; static { try { ourParsing = new MyParsing(); } catch (Exception e) { throw new RuntimeException(e); } } @Nullable public static PsiFile parseFile(@NotNull File file, @NotNull ParserDefinition parserDefinition) throws IOException { String name = file.getName(); String text = FileUtil.loadFile(file); return parseFile(name, text, parserDefinition); } @Nullable public static PsiFile parseFile(@NotNull String name, @NotNull String text, @NotNull ParserDefinition parserDefinition) { return ourParsing.createFile(name, text, parserDefinition); } @NotNull public static ASTNode parseText(@NotNull String text, @NotNull ParserDefinition parserDefinition) { return ourParsing.createAST(text, parserDefinition); } @NotNull public static SyntaxTraverser<LighterASTNode> parseLight(@NotNull String text, @NotNull ParserDefinition parserDefinition) { return ourParsing.parseLight(text, parserDefinition); } /* * Builds light-psi-all.jar from JVM class loader log (-verbose:class option) */ public static void main(String[] args) throws Throwable { if (args.length < 2) { System.out.println("Usage: Main <output-dir> <classes.log.txt>"); return; } File dir = new File(args[0]); File file = new File(args[1]); File out = new File(dir, "light-psi-all.jar"); int count = mainImpl(file, out); System.out.println(StringUtil.formatFileSize(out.length()) + " and " + count + " classes written to " + out.getName()); } private static int mainImpl(File classesFile, File outJarFile) throws Throwable { BufferedReader reader = new BufferedReader(new FileReader(classesFile)); Pattern pattern = Pattern.compile("\\[Loaded (.*) from (?:file:)?(.*)\\]"); JarOutputStream jar = new JarOutputStream(new FileOutputStream(outJarFile)); int count = 0; String s; addJarEntry(jar, "misc/registry.properties"); while ((s = reader.readLine()) != null) { Matcher matcher = pattern.matcher(s); if (!matcher.matches()) continue; String className = matcher.group(1); String path = matcher.group(2); if (!shouldAddEntry(path)) continue; addJarEntry(jar, className.replace(".", "/") + ".class"); count ++; } jar.close(); return count; } private static boolean shouldAddEntry(String path) { if (!path.startsWith("/")) return false; if (path.contains("/grammar-kit/")) return false; return path.contains("/out/classes/production/") || path.contains("extensions.jar") || path.contains("openapi.jar") || path.contains("idea.jar"); } private static void addJarEntry(JarOutputStream jarFile, String resourceName) throws IOException { InputStream stream = LightPsi.class.getClassLoader().getResourceAsStream(resourceName); if (stream == null) { System.err.println("Skipping missing " + resourceName); } else { jarFile.putNextEntry(new JarEntry(resourceName)); FileUtil.copy(stream, jarFile); jarFile.closeEntry(); } } private static class MyParsing implements Disposable { private final MockProject myProject; MyParsing() throws Exception { myProject = Init.initAppAndProject(this); Init.initExtensions(myProject); } @Nullable protected PsiFile createFile(@NotNull String name, @NotNull String text, @NotNull ParserDefinition definition) { Language language = definition.getFileNodeType().getLanguage(); Init.addExplicitExtension(getProject(), LanguageParserDefinitions.INSTANCE, language, definition); return ((PsiFileFactoryImpl)PsiFileFactory.getInstance(myProject)).trySetupPsiForFile(new LightVirtualFile(name, language, text), language, true, false); } @NotNull protected ASTNode createAST(@NotNull String text, @NotNull ParserDefinition definition) { PsiParser parser = definition.createParser(getProject()); Lexer lexer = definition.createLexer(getProject()); PsiBuilderImpl psiBuilder = new PsiBuilderImpl(getProject(), null, definition, lexer, new CharTableImpl(), text, null, null); return parser.parse(definition.getFileNodeType(), psiBuilder); } @NotNull protected SyntaxTraverser<LighterASTNode> parseLight(@NotNull String text, @NotNull ParserDefinition definition) { LightPsiParser parser = (LightPsiParser)definition.createParser(getProject()); Lexer lexer = definition.createLexer(getProject()); PsiBuilderImpl psiBuilder = new PsiBuilderImpl(getProject(), null, definition, lexer, new CharTableImpl(), text, null, null); parser.parseLight(definition.getFileNodeType(), psiBuilder); return SyntaxTraverser.lightTraverser(psiBuilder); } private MockProject getProject() { return myProject; } @Override public void dispose() { } } public static class Init { public static void initExtensions(@NotNull MockProject project) { Extensions.getRootArea().registerExtensionPoint("com.intellij.referencesSearch", "com.intellij.util.QueryExecutor"); Extensions.getRootArea().registerExtensionPoint("com.intellij.useScopeEnlarger", "com.intellij.psi.search.UseScopeEnlarger"); Extensions.getRootArea().registerExtensionPoint("com.intellij.useScopeOptimizer", "com.intellij.psi.search.UseScopeOptimizer"); Extensions.getRootArea().registerExtensionPoint("com.intellij.languageInjector", "com.intellij.psi.LanguageInjector"); Extensions.getArea(project).registerExtensionPoint("com.intellij.multiHostInjector", "com.intellij.lang.injection.MultiHostInjector"); Extensions.getRootArea().registerExtensionPoint("com.intellij.codeInsight.containerProvider", "com.intellij.codeInsight.ContainerProvider"); Extensions.getRootArea().getExtensionPoint("com.intellij.referencesSearch").registerExtension(new CachesBasedRefSearcher()); registerApplicationService(project, PsiReferenceService.class, PsiReferenceServiceImpl.class); registerApplicationService(project, JobLauncher.class, JobLauncherImpl.class); registerApplicationService(project, AsyncFutureFactory.class, AsyncFutureFactoryImpl.class); project.registerService(PsiSearchHelper.class, PsiSearchHelperImpl.class); project.registerService(DumbService.class, DumbServiceImpl.class); project.registerService(ResolveCache.class, ResolveCache.class); project.registerService(PsiFileFactory.class, PsiFileFactoryImpl.class); try { project.registerService(JavaHelper.class, new JavaHelper.AsmHelper()); } catch (LinkageError e) { System.out.println("ASM not available, using reflection helper: " + e); project.registerService(JavaHelper.class, new JavaHelper.ReflectionHelper()); } project.registerService(InjectedLanguageManager.class, InjectedLanguageManagerImpl.class); ProgressManager.getInstance(); } private static <T, S extends T> void registerApplicationService(Project project, Class<T> intfClass, Class<S> implClass) { final MockApplicationEx application = (MockApplicationEx)ApplicationManager.getApplication(); application.registerService(intfClass, implClass); Disposer.register(project, () -> application.getPicoContainer().unregisterComponent(intfClass.getName())); } public static MockProject initAppAndProject(Disposable rootDisposable) { final MockApplicationEx application = initApplication(rootDisposable); ComponentAdapter component = application.getPicoContainer().getComponentAdapter(ProgressManager.class.getName()); if (component == null) { application.getPicoContainer().registerComponent(new AbstractComponentAdapter(ProgressManager.class.getName(), Object.class) { @Override public Object getComponentInstance(PicoContainer container) throws PicoInitializationException, PicoIntrospectionException { return new ProgressManagerImpl(); } @Override public void verify(PicoContainer container) throws PicoIntrospectionException { } }); } Extensions.registerAreaClass("IDEA_PROJECT", null); MockProjectEx project = new MockProjectEx(rootDisposable); MutablePicoContainer appContainer = application.getPicoContainer(); registerComponentInstance(appContainer, MessageBus.class, MessageBusFactory.newMessageBus(application)); final MockEditorFactory editorFactory = new MockEditorFactory(); registerComponentInstance(appContainer, EditorFactory.class, editorFactory); registerComponentInstance( appContainer, FileDocumentManager.class, new MockFileDocumentManagerImpl(editorFactory::createDocument, FileDocumentManagerImpl.HARD_REF_TO_DOCUMENT_KEY) ); registerComponentInstance(appContainer, PsiDocumentManager.class, new MockPsiDocumentManager()); registerComponentInstance(appContainer, FileTypeManager.class, new MockFileTypeManager(new MockLanguageFileType(PlainTextLanguage.INSTANCE, "txt"))); registerApplicationService(project, PsiBuilderFactory.class, PsiBuilderFactoryImpl.class); registerApplicationService(project, DefaultASTFactory.class, DefaultASTFactoryImpl.class); registerApplicationService(project, ReferenceProvidersRegistry.class, ReferenceProvidersRegistryImpl.class); project.registerService(PsiManager.class, MockPsiManager.class); project.registerService(PsiFileFactory.class, PsiFileFactoryImpl.class); project.registerService(StartupManager.class, StartupManagerImpl.class); project.registerService(CachedValuesManager.class, new CachedValuesManagerImpl(project, new PsiCachedValuesFactory(PsiManager.getInstance(project)))); registerExtensionPoint(FileTypeFactory.FILE_TYPE_FACTORY_EP, FileTypeFactory.class); registerExtensionPoint(MetaLanguage.EP_NAME, MetaLanguage.class); return project; } public static MockApplicationEx initApplication(Disposable rootDisposable) { MockApplicationEx instance = new MockApplicationEx(rootDisposable); ApplicationManager.setApplication(instance, FileTypeManager::getInstance, rootDisposable); instance.registerService(EncodingManager.class, EncodingManagerImpl.class); return instance; } public static <T> void registerExtensionPoint(ExtensionPointName<T> extensionPointName, final Class<T> aClass) { registerExtensionPoint(Extensions.getRootArea(), extensionPointName, aClass); } public static <T> void registerExtensionPoint(ExtensionsArea area, ExtensionPointName<T> extensionPointName, Class<? extends T> aClass) { final String name = extensionPointName.getName(); if (!area.hasExtensionPoint(name)) { ExtensionPoint.Kind kind = aClass.isInterface() || (aClass.getModifiers() & Modifier.ABSTRACT) != 0 ? ExtensionPoint.Kind.INTERFACE : ExtensionPoint.Kind.BEAN_CLASS; area.registerExtensionPoint(name, aClass.getName(), kind); } } public static <T> void registerComponentInstance(MutablePicoContainer container, Class<T> key, T implementation) { container.unregisterComponent(key); container.registerComponentInstance(key, implementation); } public static <T> void addExplicitExtension(Project project, final LanguageExtension<T> instance, final Language language, final T object) { instance.addExplicitExtension(language, object); Disposer.register(project, () -> instance.removeExplicitExtension(language, object)); } } }