mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Merge branch 'idea-plugin'
This commit is contained in:
commit
aeca4c2df0
|
@ -222,7 +222,7 @@ public class TeaVMTool implements BaseTeaVMTool {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getClasses() {
|
public Collection<String> getClasses() {
|
||||||
return vm != null ? vm.getClasses() : Collections.<String>emptyList();
|
return vm != null ? vm.getClasses() : Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<String> getUsedResources() {
|
public Collection<String> getUsedResources() {
|
||||||
|
@ -361,6 +361,7 @@ public class TeaVMTool implements BaseTeaVMTool {
|
||||||
TeaVMProblemRenderer.describeProblems(vm, log);
|
TeaVMProblemRenderer.describeProblems(vm, log);
|
||||||
}
|
}
|
||||||
if (debugInformationGenerated) {
|
if (debugInformationGenerated) {
|
||||||
|
assert debugEmitter != null;
|
||||||
DebugInformation debugInfo = debugEmitter.getDebugInformation();
|
DebugInformation debugInfo = debugEmitter.getDebugInformation();
|
||||||
try (OutputStream debugInfoOut = new FileOutputStream(new File(targetDirectory,
|
try (OutputStream debugInfoOut = new FileOutputStream(new File(targetDirectory,
|
||||||
targetFileName + ".teavmdbg"))) {
|
targetFileName + ".teavmdbg"))) {
|
||||||
|
@ -369,6 +370,7 @@ public class TeaVMTool implements BaseTeaVMTool {
|
||||||
log.info("Debug information successfully written");
|
log.info("Debug information successfully written");
|
||||||
}
|
}
|
||||||
if (sourceMapsFileGenerated) {
|
if (sourceMapsFileGenerated) {
|
||||||
|
assert debugEmitter != null;
|
||||||
DebugInformation debugInfo = debugEmitter.getDebugInformation();
|
DebugInformation debugInfo = debugEmitter.getDebugInformation();
|
||||||
String sourceMapsFileName = targetFileName + ".map";
|
String sourceMapsFileName = targetFileName + ".map";
|
||||||
writer.append("\n//# sourceMappingURL=").append(sourceMapsFileName);
|
writer.append("\n//# sourceMappingURL=").append(sourceMapsFileName);
|
||||||
|
|
|
@ -25,13 +25,16 @@ import java.io.Reader;
|
||||||
import java.net.MalformedURLException;
|
import java.net.MalformedURLException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLClassLoader;
|
import java.net.URLClassLoader;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Queue;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
@ -50,6 +53,9 @@ import org.jetbrains.jps.model.module.JpsLibraryDependency;
|
||||||
import org.jetbrains.jps.model.module.JpsModule;
|
import org.jetbrains.jps.model.module.JpsModule;
|
||||||
import org.jetbrains.jps.model.module.JpsModuleDependency;
|
import org.jetbrains.jps.model.module.JpsModuleDependency;
|
||||||
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
|
import org.jetbrains.jps.model.module.JpsModuleSourceRoot;
|
||||||
|
import org.teavm.callgraph.CallGraph;
|
||||||
|
import org.teavm.callgraph.CallGraphNode;
|
||||||
|
import org.teavm.callgraph.CallSite;
|
||||||
import org.teavm.common.IntegerArray;
|
import org.teavm.common.IntegerArray;
|
||||||
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
import org.teavm.diagnostics.DefaultProblemTextConsumer;
|
||||||
import org.teavm.diagnostics.Problem;
|
import org.teavm.diagnostics.Problem;
|
||||||
|
@ -71,7 +77,7 @@ import org.teavm.vm.TeaVMPhase;
|
||||||
import org.teavm.vm.TeaVMProgressFeedback;
|
import org.teavm.vm.TeaVMProgressFeedback;
|
||||||
import org.teavm.vm.TeaVMProgressListener;
|
import org.teavm.vm.TeaVMProgressListener;
|
||||||
|
|
||||||
public class TeaVMBuild {
|
class TeaVMBuild {
|
||||||
private final CompileContext context;
|
private final CompileContext context;
|
||||||
private final TeaVMStorageProvider storageProvider = new TeaVMStorageProvider();
|
private final TeaVMStorageProvider storageProvider = new TeaVMStorageProvider();
|
||||||
private final List<String> classPathEntries = new ArrayList<>();
|
private final List<String> classPathEntries = new ArrayList<>();
|
||||||
|
@ -82,12 +88,12 @@ public class TeaVMBuild {
|
||||||
private final Map<File, int[]> fileLineCache = new HashMap<>();
|
private final Map<File, int[]> fileLineCache = new HashMap<>();
|
||||||
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
|
private final List<SourceFileProvider> sourceFileProviders = new ArrayList<>();
|
||||||
|
|
||||||
public TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant) {
|
TeaVMBuild(CompileContext context, TeaVMBuilderAssistant assistant) {
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.assistant = assistant;
|
this.assistant = assistant;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException {
|
boolean perform(JpsModule module, ModuleBuildTarget target) throws IOException {
|
||||||
storage = context.getProjectDescriptor().dataManager.getStorage(target, storageProvider);
|
storage = context.getProjectDescriptor().dataManager.getStorage(target, storageProvider);
|
||||||
|
|
||||||
TeaVMJpsConfiguration config = TeaVMJpsConfiguration.get(module);
|
TeaVMJpsConfiguration config = TeaVMJpsConfiguration.get(module);
|
||||||
|
@ -130,12 +136,13 @@ public class TeaVMBuild {
|
||||||
updateStorage(tool);
|
updateStorage(tool);
|
||||||
}
|
}
|
||||||
|
|
||||||
reportProblems(tool.getProblemProvider());
|
CallGraph callGraph = tool.getDependencyInfo().getCallGraph();
|
||||||
|
reportProblems(tool.getProblemProvider(), callGraph);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void reportProblems(ProblemProvider problemProvider) {
|
private void reportProblems(ProblemProvider problemProvider, CallGraph callGraph) {
|
||||||
for (Problem problem : problemProvider.getProblems()) {
|
for (Problem problem : problemProvider.getProblems()) {
|
||||||
BuildMessage.Kind kind;
|
BuildMessage.Kind kind;
|
||||||
switch (problem.getSeverity()) {
|
switch (problem.getSeverity()) {
|
||||||
|
@ -149,47 +156,119 @@ public class TeaVMBuild {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
String path = null;
|
|
||||||
File file = null;
|
|
||||||
int line = -1;
|
|
||||||
long startOffset = -1;
|
|
||||||
long endOffset = -1;
|
|
||||||
|
|
||||||
if (problem.getLocation() != null) {
|
|
||||||
CallLocation callLocation = problem.getLocation();
|
|
||||||
InstructionLocation insnLocation = problem.getLocation().getSourceLocation();
|
|
||||||
if (insnLocation != null) {
|
|
||||||
path = insnLocation.getFileName();
|
|
||||||
line = insnLocation.getLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (line <= 0 && assistant != null && callLocation != null && callLocation.getMethod() != null) {
|
|
||||||
MethodReference method = callLocation.getMethod();
|
|
||||||
try {
|
|
||||||
TeaVMElementLocation location = assistant.getMethodLocation(method.getClassName(),
|
|
||||||
method.getName(), ValueType.methodTypeToString(method.getSignature()));
|
|
||||||
line = location.getLine();
|
|
||||||
startOffset = location.getStartOffset();
|
|
||||||
endOffset = location.getEndOffset();
|
|
||||||
file = new File(location.getPath());
|
|
||||||
|
|
||||||
if (line <= 0) {
|
|
||||||
int[] lines = getLineOffsets(file);
|
|
||||||
if (lines != null) {
|
|
||||||
line = Arrays.binarySearch(lines, (int) startOffset + 1);
|
|
||||||
if (line < 0) {
|
|
||||||
line = -line - 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
// just don't fill location fields
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
DefaultProblemTextConsumer textConsumer = new DefaultProblemTextConsumer();
|
DefaultProblemTextConsumer textConsumer = new DefaultProblemTextConsumer();
|
||||||
problem.render(textConsumer);
|
problem.render(textConsumer);
|
||||||
|
String baseText = textConsumer.getText();
|
||||||
|
|
||||||
|
List<ProblemToReport> problemsToReport = resolveProblemLocation(problem, callGraph);
|
||||||
|
|
||||||
|
for (ProblemToReport problemToReport : problemsToReport) {
|
||||||
|
String text = baseText + buildCallStack(problemToReport.locations);
|
||||||
|
context.processMessage(new CompilerMessage("TeaVM", kind, text, problemToReport.path,
|
||||||
|
problemToReport.startOffset, problemToReport.endOffset, problemToReport.startOffset,
|
||||||
|
problemToReport.line, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private class ProblemToReport {
|
||||||
|
int line;
|
||||||
|
int startOffset;
|
||||||
|
int endOffset;
|
||||||
|
String path;
|
||||||
|
CallLocationList locations;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class CallLocationList {
|
||||||
|
final CallLocation value;
|
||||||
|
final CallLocationList next;
|
||||||
|
|
||||||
|
private CallLocationList(CallLocation value, CallLocationList next) {
|
||||||
|
this.value = value;
|
||||||
|
this.next = next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ProblemToReport> resolveProblemLocation(Problem problem, CallGraph callGraph) {
|
||||||
|
class Step {
|
||||||
|
private final CallLocationList locationList;
|
||||||
|
private final CallLocation location;
|
||||||
|
|
||||||
|
private Step(CallLocationList locationList, CallLocation location) {
|
||||||
|
this.locationList = locationList;
|
||||||
|
this.location = location;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ProblemToReport> problemsToReport = new ArrayList<>();
|
||||||
|
Set<MethodReference> visited = new HashSet<>();
|
||||||
|
Queue<Step> workList = new ArrayDeque<>();
|
||||||
|
workList.add(new Step(null, problem.getLocation()));
|
||||||
|
|
||||||
|
while (!workList.isEmpty()) {
|
||||||
|
Step step = workList.remove();
|
||||||
|
if (step.location == null || !visited.add(step.location.getMethod())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
ProblemToReport result = getProblemLocation(step.location);
|
||||||
|
|
||||||
|
CallGraphNode node = callGraph.getNode(step.location.getMethod());
|
||||||
|
if (node.getCallerCallSites().isEmpty() || isValid(result)) {
|
||||||
|
result.locations = step.locationList;
|
||||||
|
problemsToReport.add(result);
|
||||||
|
} else {
|
||||||
|
for (CallSite callSite : node.getCallerCallSites()) {
|
||||||
|
CallLocation nextLocation = new CallLocation(callSite.getCaller().getMethod(),
|
||||||
|
callSite.getLocation());
|
||||||
|
workList.add(new Step(new CallLocationList(step.location, step.locationList), nextLocation));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return problemsToReport;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isValid(ProblemToReport problemToReport) {
|
||||||
|
return problemToReport.path != null && (problemToReport.line >= 0 || problemToReport.startOffset >= 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ProblemToReport getProblemLocation(CallLocation callLocation) {
|
||||||
|
String path = null;
|
||||||
|
File file = null;
|
||||||
|
int line = -1;
|
||||||
|
int startOffset = -1;
|
||||||
|
int endOffset = -1;
|
||||||
|
|
||||||
|
if (callLocation != null) {
|
||||||
|
InstructionLocation insnLocation = callLocation.getSourceLocation();
|
||||||
|
if (insnLocation != null) {
|
||||||
|
path = insnLocation.getFileName();
|
||||||
|
line = insnLocation.getLine();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (line <= 0 && assistant != null && callLocation.getMethod() != null) {
|
||||||
|
MethodReference method = callLocation.getMethod();
|
||||||
|
try {
|
||||||
|
TeaVMElementLocation location = assistant.getMethodLocation(method.getClassName(),
|
||||||
|
method.getName(), ValueType.methodTypeToString(method.getSignature()));
|
||||||
|
line = location.getLine();
|
||||||
|
startOffset = location.getStartOffset();
|
||||||
|
endOffset = location.getEndOffset();
|
||||||
|
file = new File(location.getPath());
|
||||||
|
|
||||||
|
if (line <= 0) {
|
||||||
|
int[] lines = getLineOffsets(file);
|
||||||
|
if (lines != null) {
|
||||||
|
line = Arrays.binarySearch(lines, startOffset + 1);
|
||||||
|
if (line < 0) {
|
||||||
|
line = -line - 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
// just don't fill location fields
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (file == null) {
|
if (file == null) {
|
||||||
if (path != null) {
|
if (path != null) {
|
||||||
|
@ -207,10 +286,32 @@ public class TeaVMBuild {
|
||||||
endOffset = lines[line] - 1;
|
endOffset = lines[line] - 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context.processMessage(new CompilerMessage("TeaVM", kind, textConsumer.getText(), path,
|
|
||||||
startOffset, endOffset, startOffset, line, 0));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ProblemToReport result = new ProblemToReport();
|
||||||
|
result.line = line;
|
||||||
|
result.startOffset = startOffset;
|
||||||
|
result.endOffset = endOffset;
|
||||||
|
result.path = path;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private String buildCallStack(CallLocationList callLocationList) {
|
||||||
|
List<CallLocation> locations = new ArrayList<>();
|
||||||
|
while (callLocationList != null) {
|
||||||
|
locations.add(callLocationList.value);
|
||||||
|
callLocationList = callLocationList.next;
|
||||||
|
}
|
||||||
|
Collections.reverse(locations);
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (CallLocation location : locations) {
|
||||||
|
sb.append("\n at ").append(location.getMethod());
|
||||||
|
if (location.getSourceLocation() != null) {
|
||||||
|
sb.append("(").append(location.getSourceLocation()).append(")");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private File lookupSource(String relativePath) {
|
private File lookupSource(String relativePath) {
|
||||||
|
|
|
@ -0,0 +1,107 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2016 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* 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.teavm.idea.maven;
|
||||||
|
|
||||||
|
import com.intellij.openapi.diagnostic.Logger;
|
||||||
|
import com.intellij.openapi.externalSystem.service.project.IdeModifiableModelsProvider;
|
||||||
|
import com.intellij.openapi.module.Module;
|
||||||
|
import com.intellij.openapi.module.ModuleServiceManager;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.jdom.Element;
|
||||||
|
import org.jetbrains.idea.maven.importing.MavenImporter;
|
||||||
|
import org.jetbrains.idea.maven.importing.MavenRootModelAdapter;
|
||||||
|
import org.jetbrains.idea.maven.model.MavenPlugin;
|
||||||
|
import org.jetbrains.idea.maven.project.MavenProject;
|
||||||
|
import org.jetbrains.idea.maven.project.MavenProjectChanges;
|
||||||
|
import org.jetbrains.idea.maven.project.MavenProjectsProcessorTask;
|
||||||
|
import org.jetbrains.idea.maven.project.MavenProjectsTree;
|
||||||
|
import org.teavm.idea.TeaVMConfigurationStorage;
|
||||||
|
import org.teavm.idea.jps.model.TeaVMJpsConfiguration;
|
||||||
|
|
||||||
|
public class TeaVMMavenImporter extends MavenImporter {
|
||||||
|
private static final Logger logger = Logger.getInstance(TeaVMMavenImporter.class);
|
||||||
|
|
||||||
|
public TeaVMMavenImporter() {
|
||||||
|
super("org.teavm", "teavm-maven-plugin");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void preProcess(Module module, MavenProject mavenProject, MavenProjectChanges changes,
|
||||||
|
IdeModifiableModelsProvider modifiableModelsProvider) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void process(IdeModifiableModelsProvider modifiableModelsProvider, Module module,
|
||||||
|
MavenRootModelAdapter rootModel, MavenProjectsTree mavenModel, MavenProject mavenProject,
|
||||||
|
MavenProjectChanges changes, Map<MavenProject, String> mavenProjectToModuleName,
|
||||||
|
List<MavenProjectsProcessorTask> postTasks) {
|
||||||
|
TeaVMConfigurationStorage configurationStorage = ModuleServiceManager.getService(module,
|
||||||
|
TeaVMConfigurationStorage.class);
|
||||||
|
if (configurationStorage == null) {
|
||||||
|
logger.warn("Could not load component to retrieve TeaVM build configuration");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
TeaVMJpsConfiguration configuration = configurationStorage.getState();
|
||||||
|
|
||||||
|
for (MavenPlugin mavenPlugin : mavenProject.getPlugins()) {
|
||||||
|
if (mavenPlugin.getGroupId().equals(myPluginGroupID)
|
||||||
|
&& mavenPlugin.getArtifactId().equals(myPluginArtifactID)) {
|
||||||
|
updateConfiguration(mavenPlugin, configuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
configurationStorage.loadState(configuration);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateConfiguration(MavenPlugin plugin, TeaVMJpsConfiguration configuration) {
|
||||||
|
if (plugin.getConfigurationElement() != null) {
|
||||||
|
updateConfiguration(plugin.getConfigurationElement(), configuration);
|
||||||
|
}
|
||||||
|
for (MavenPlugin.Execution execution : plugin.getExecutions()) {
|
||||||
|
if (execution.getGoals().contains("compile")) {
|
||||||
|
if (execution.getConfigurationElement() != null) {
|
||||||
|
updateConfiguration(execution.getConfigurationElement(), configuration);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateConfiguration(Element source, TeaVMJpsConfiguration configuration) {
|
||||||
|
configuration.setEnabled(true);
|
||||||
|
for (Element child : source.getChildren()) {
|
||||||
|
switch (child.getName()) {
|
||||||
|
case "sourceFilesCopied":
|
||||||
|
configuration.setSourceFilesCopied(Boolean.parseBoolean(child.getTextTrim()));
|
||||||
|
break;
|
||||||
|
case "sourceMapsGenerated":
|
||||||
|
configuration.setSourceMapsFileGenerated(Boolean.parseBoolean(child.getTextTrim()));
|
||||||
|
break;
|
||||||
|
case "minifying":
|
||||||
|
configuration.setMinifying(Boolean.parseBoolean(child.getTextTrim()));
|
||||||
|
break;
|
||||||
|
case "targetDirectory":
|
||||||
|
configuration.setTargetDirectory(child.getTextTrim());
|
||||||
|
break;
|
||||||
|
case "mainClass":
|
||||||
|
configuration.setMainClass(child.getTextTrim());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,6 +4,8 @@
|
||||||
<version>1.0</version>
|
<version>1.0</version>
|
||||||
<vendor email="info@teavm.org" url="http://teavm.org">TeaVM community</vendor>
|
<vendor email="info@teavm.org" url="http://teavm.org">TeaVM community</vendor>
|
||||||
|
|
||||||
|
<depends>org.jetbrains.idea.maven</depends>
|
||||||
|
|
||||||
<description><![CDATA[
|
<description><![CDATA[
|
||||||
Enter short description for your plugin here.<br>
|
Enter short description for your plugin here.<br>
|
||||||
<em>most HTML tags may be used</em>
|
<em>most HTML tags may be used</em>
|
||||||
|
@ -30,4 +32,8 @@
|
||||||
<compileServer.plugin classpath="jps/teavm-jps-plugin.jar;teavm-all.jar"/>
|
<compileServer.plugin classpath="jps/teavm-jps-plugin.jar;teavm-all.jar"/>
|
||||||
<buildProcess.parametersProvider implementation="org.teavm.idea.TeaVMJPSConfigurator"/>
|
<buildProcess.parametersProvider implementation="org.teavm.idea.TeaVMJPSConfigurator"/>
|
||||||
</extensions>
|
</extensions>
|
||||||
|
|
||||||
|
<extensions defaultExtensionNs="org.jetbrains.idea.maven">
|
||||||
|
<importer implementation="org.teavm.idea.maven.TeaVMMavenImporter"/>
|
||||||
|
</extensions>
|
||||||
</idea-plugin>
|
</idea-plugin>
|
Loading…
Reference in New Issue
Block a user