From 7b3125705e9744821d68f3d1339ec7aafd8e4194 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82a=C5=BCej=20Kardy=C5=9B?= Date: Tue, 20 Aug 2019 14:01:14 +0200 Subject: [PATCH] Adding incremental compilation --- .../kotlin/core/compiler/KotlinCompiler.java | 240 ------------------ .../kotlin/core/compiler/KotlinCompiler.kt | 227 +++++++++++++++++ .../kotlin/core/compiler/KotlinCompiler2.java | 1 + .../core/compiler/KotlinCompilerUtils.java | 11 +- .../kotlin/core/model/KotlinEnvironment.kt | 6 + .../preferences/KotlinBuildingProperties.kt | 17 ++ ... Kotlin Plugin with Equinox Weaving.launch | 2 +- kotlin-eclipse-ui/plugin.xml | 30 +++ .../building/ProjectBuildingPropertyPage.kt | 41 +++ .../building/WorkspaceBuildingPropertyPage.kt | 26 ++ .../views/BuildingPropertiesView.kt | 41 +++ .../ui/builder/BaseKotlinBuilderElement.kt | 128 ++++++++++ .../ui/builder/CompileKotlinClassesAction.kt | 27 ++ .../IncrementalKotlinBuilderElement.kt | 83 ++++++ .../kotlin/ui/builder/KotlinBuilder.kt | 221 +--------------- .../kotlin/ui/builder/KotlinBuilderElement.kt | 93 +++++++ ...KotlinScriptLaunchConfigurationDelegate.kt | 2 +- 17 files changed, 739 insertions(+), 457 deletions(-) delete mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.java create mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt create mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler2.java create mode 100644 kotlin-eclipse-core/src/org/jetbrains/kotlin/core/preferences/KotlinBuildingProperties.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/building/ProjectBuildingPropertyPage.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/building/WorkspaceBuildingPropertyPage.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/views/BuildingPropertiesView.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/BaseKotlinBuilderElement.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/CompileKotlinClassesAction.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/IncrementalKotlinBuilderElement.kt create mode 100644 kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilderElement.kt diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.java b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.java deleted file mode 100644 index d8b3731a9..000000000 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.java +++ /dev/null @@ -1,240 +0,0 @@ -/******************************************************************************* - * Copyright 2010-2014 JetBrains s.r.o. - * - * 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.jetbrains.kotlin.core.compiler; - -import java.io.BufferedReader; -import java.io.ByteArrayOutputStream; -import java.io.File; -import java.io.PrintStream; -import java.io.Reader; -import java.io.StringReader; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.stream.Collectors; - -import org.eclipse.core.runtime.CoreException; -import org.eclipse.jdt.core.IJavaProject; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation; -import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity; -import org.jetbrains.kotlin.cli.common.messages.MessageCollector; -import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler; -import org.jetbrains.kotlin.core.launch.CompilerOutputData; -import org.jetbrains.kotlin.core.launch.CompilerOutputElement; -import org.jetbrains.kotlin.core.launch.CompilerOutputParser; -import org.jetbrains.kotlin.core.launch.KotlinCLICompiler; -import org.jetbrains.kotlin.core.model.KotlinEnvironment; -import org.jetbrains.kotlin.core.preferences.CompilerPlugin; -import org.jetbrains.kotlin.core.preferences.KotlinProperties; -import org.jetbrains.kotlin.core.utils.ProjectUtils; - -import kotlin.Pair; -import kotlin.text.StringsKt; -import org.jetbrains.kotlin.utils.PathUtil; - -public class KotlinCompiler { - public final static KotlinCompiler INSTANCE = new KotlinCompiler(); - - private KotlinCompiler() { - } - - @NotNull - public KotlinCompilerResult compileKotlinFiles(@NotNull IJavaProject javaProject) throws CoreException { - return ProjectUtils.getSrcOutDirectories(javaProject) - .stream() - .collect(Collectors.groupingBy(Pair::component2)) - .entrySet() - .stream() - .map(outSrcOut -> { - File out = outSrcOut.getKey(); - List srcs = outSrcOut.getValue() - .stream() - .map(pair -> pair.component1()) - .collect(Collectors.toList()); - return new Pair>(out, srcs); - }) - .map(outSrcs -> { - File out = outSrcs.component1(); - List srcs = outSrcs.component2(); - try { - String[] arguments = configureCompilerArguments(javaProject, out.getAbsolutePath(), srcs); - return execKotlinCompiler(arguments); - } catch (CoreException ce) { - throw new RuntimeException(ce); - } - }) - .reduce(new KotlinCompilerResult(true, new CompilerOutputData()), (leftResult, rightResult) -> { - CompilerOutputData mergedData = new CompilerOutputData(); - List mergedList = leftResult.compilerOutput.getList(); - mergedList.addAll(rightResult.compilerOutput.getList()); - mergedList.forEach(outElement -> { - mergedData.add(outElement.getMessageSeverity(), outElement.getMessage(), outElement.getMessageLocation()); - }); - return new KotlinCompilerResult(leftResult.result && rightResult.result, mergedData); - }); - } - - public KotlinCompilerResult execKotlinCompiler(@NotNull String[] arguments) { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - PrintStream out = new PrintStream(outputStream); - - KotlinCLICompiler.doMain(new K2JVMCompiler(), out, arguments); - - BufferedReader reader = new BufferedReader(new StringReader(outputStream.toString())); - return parseCompilerOutput(reader); - } - - private String[] configureCompilerArguments(@NotNull IJavaProject javaProject, @NotNull String outputDir, - @NotNull List sourceDirs) throws CoreException { - KotlinProperties kotlinProperties = - KotlinEnvironment.getEnvironment(javaProject.getProject()).getCompilerProperties(); - - List command = new ArrayList<>(); - command.add("-kotlin-home"); - command.add(ProjectUtils.getKtHome()); - - boolean jdkHomeUndefined = kotlinProperties.isJDKHomUndefined(); - if (jdkHomeUndefined) { - command.add("-no-jdk"); - } else { - command.add("-jdk-home"); - command.add(kotlinProperties.getJdkHome()); - } - command.add("-no-stdlib"); // Because we add runtime into the classpath - - command.add("-jvm-target"); - command.add(kotlinProperties.getJvmTarget().getDescription()); - - command.add("-language-version"); - command.add(kotlinProperties.getLanguageVersion().getVersionString()); - - command.add("-api-version"); - command.add(kotlinProperties.getApiVersion().getVersionString()); - - for (CompilerPlugin plugin : kotlinProperties.getCompilerPlugins().getEntries()) { - command.addAll(configurePlugin(plugin)); - } - command.add(configureScriptingPlugin()); - - StringBuilder classPath = new StringBuilder(); - String pathSeparator = System.getProperty("path.separator"); - - for (File file : ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkHomeUndefined)) { - classPath.append(file.getAbsolutePath()).append(pathSeparator); - } - - String additionalFlags = kotlinProperties.getCompilerFlags(); - if (additionalFlags != null && !StringsKt.isBlank(additionalFlags)) { - for (String flag : additionalFlags.split("\\s+")) { - command.add(flag); - } - } - - command.add("-classpath"); - command.add(classPath.toString()); - - command.add("-d"); - command.add(outputDir); - - for (File srcDirectory : sourceDirs) { - command.add(srcDirectory.getAbsolutePath()); - } - - return command.toArray(new String[0]); - } - - private Collection configurePlugin(CompilerPlugin plugin) { - List result = new ArrayList<>(); - String jarPath = plugin.getJarPath(); - if (plugin.getActive() && jarPath != null) { - String replacedPath = jarPath.replace("$KOTLIN_HOME", ProjectUtils.getKtHome()); - result.add("-Xplugin=" + replacedPath); - - for (String arg : plugin.getArgs()) { - result.add("-P"); - result.add("plugin:" + arg); - } - } - return result; - } - - private String configureScriptingPlugin() { - return "-Xplugin=" + ProjectUtils.buildLibPath(PathUtil.KOTLIN_SCRIPTING_COMPILER_PLUGIN_NAME); - } - - @NotNull - private KotlinCompilerResult parseCompilerOutput(Reader reader) { - final CompilerOutputData compilerOutput = new CompilerOutputData(); - - final List severities = new ArrayList(); - CompilerOutputParser.parseCompilerMessagesFromReader(new MessageCollector() { - private boolean hasErrors = false; - - @Override - public void report(@NotNull CompilerMessageSeverity messageSeverity, @NotNull String message, - @Nullable CompilerMessageLocation messageLocation) { - hasErrors = hasErrors || messageSeverity.isError(); - severities.add(messageSeverity); - compilerOutput.add(messageSeverity, message, messageLocation); - } - - @Override - public boolean hasErrors() { - return hasErrors; - } - - @Override - public void clear() { - hasErrors = false; - - } - }, reader); - - boolean result = true; - for (CompilerMessageSeverity severity : severities) { - if (severity.equals(CompilerMessageSeverity.ERROR) || severity.equals(CompilerMessageSeverity.EXCEPTION)) { - result = false; - break; - } - } - - return new KotlinCompilerResult(result, compilerOutput); - } - - public static class KotlinCompilerResult { - public static KotlinCompilerResult EMPTY = new KotlinCompilerResult(false, new CompilerOutputData()); - - private final boolean result; - private final CompilerOutputData compilerOutput; - - private KotlinCompilerResult(boolean result, @NotNull CompilerOutputData compilerOutput) { - this.result = result; - this.compilerOutput = compilerOutput; - } - - public boolean compiledCorrectly() { - return result; - } - - @NotNull - public CompilerOutputData getCompilerOutput() { - return compilerOutput; - } - } -} diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt new file mode 100644 index 000000000..b63d2b244 --- /dev/null +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler.kt @@ -0,0 +1,227 @@ +package org.jetbrains.kotlin.core.compiler + +import com.intellij.openapi.util.Disposer +import org.eclipse.jdt.core.IJavaProject +import org.jetbrains.kotlin.cli.common.CLIConfigurationKeys +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageLocation +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.ERROR +import org.jetbrains.kotlin.cli.common.messages.CompilerMessageSeverity.EXCEPTION +import org.jetbrains.kotlin.cli.common.messages.MessageCollector +import org.jetbrains.kotlin.cli.jvm.K2JVMCompiler +import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment +import org.jetbrains.kotlin.config.CompilerConfiguration +import org.jetbrains.kotlin.config.JVMConfigurationKeys +import org.jetbrains.kotlin.core.launch.CompilerOutputData +import org.jetbrains.kotlin.core.launch.CompilerOutputParser +import org.jetbrains.kotlin.core.launch.KotlinCLICompiler +import org.jetbrains.kotlin.core.model.KOTLIN_COMPILER_PATH +import org.jetbrains.kotlin.core.model.KotlinEnvironment +import org.jetbrains.kotlin.core.preferences.CompilerPlugin +import org.jetbrains.kotlin.core.utils.ProjectUtils +import org.jetbrains.kotlin.incremental.makeIncrementally +import java.io.BufferedReader +import java.io.ByteArrayOutputStream +import java.io.File +import java.io.PrintStream +import java.io.Reader +import java.io.StringReader + +object KotlinCompiler { + + private fun compileKotlinFiles( + javaProject: IJavaProject, + compilation: (IJavaProject, File, List) -> KotlinCompilerResult + ): KotlinCompilerResult = + ProjectUtils.getSrcOutDirectories(javaProject) + .groupingBy { it.second }.fold(mutableListOf()) { list, key -> + list.apply { add(key.first) } + }.map { (out, sources) -> + compilation(javaProject, out, sources) + }.fold(KotlinCompilerResult(true, CompilerOutputData())) { previous, current -> + KotlinCompilerResult(previous.result and current.result, CompilerOutputData().apply { + previous.compilerOutput.list.union(current.compilerOutput.list).forEach { + add(it.messageSeverity, it.message, it.messageLocation) + } + }) + } + + @JvmStatic + fun compileKotlinFiles(javaProject: IJavaProject): KotlinCompilerResult = + compileKotlinFiles(javaProject) { project, path, sources -> + execKotlinCompiler(configureCompilerArguments(project, path.absolutePath, sources)) + } + + @JvmStatic + fun compileIncrementallyFiles( + javaProject: IJavaProject + ): KotlinCompilerResult = + compileKotlinFiles(javaProject) { project, path, sources -> + execIncrementalKotlinCompiler(project, path.absoluteFile, sources) + } + + private fun execIncrementalKotlinCompiler( + javaProject: IJavaProject, + outputDir: File, + sourceDirs: List + ): KotlinCompilerResult { + val arguments = getCompilerArguments(javaProject, outputDir) + val messageCollector = CompilerMessageCollector() + val disposable = Disposer.newDisposable() + val config = CompilerConfiguration().apply { + put(JVMConfigurationKeys.NO_JDK, true) + put(CLIConfigurationKeys.MESSAGE_COLLECTOR_KEY, MessageCollector.NONE) + put(CLIConfigurationKeys.INTELLIJ_PLUGIN_ROOT, KOTLIN_COMPILER_PATH) + } + KotlinCoreEnvironment.getOrCreateApplicationEnvironmentForProduction(disposable, config) + var cacheDir = File("${outputDir.parentFile.absolutePath}/cache").also { it.mkdirs() } + makeIncrementally(cacheDir, sourceDirs, arguments, messageCollector) + return messageCollector.getCompilerResult() + } + + private fun execKotlinCompiler(arguments: Array): KotlinCompilerResult = with(ByteArrayOutputStream()) { + KotlinCLICompiler.doMain(K2JVMCompiler(), PrintStream(this), arguments) + parseCompilerOutput(BufferedReader(StringReader(this.toString()))) + } + + private fun getCompilerArguments(javaProject: IJavaProject, outputDir: File) = K2JVMCompilerArguments().apply { + val kotlinProperties = + KotlinEnvironment.getEnvironment(javaProject.project).compilerProperties + + kotlinHome = ProjectUtils.ktHome + destination = outputDir.absolutePath + moduleName = "kotlin-eclipse-plugin" + + val jdkUndefined = kotlinProperties.isJDKHomUndefined() + kotlinProperties.jdkHome?.takeUnless { jdkUndefined }?.let { jdkHomePath -> + jdkHome = jdkHomePath + } ?: { + noJdk = true + }() + + noStdlib = true + jvmTarget = kotlinProperties.jvmTarget.description + intellijPluginRoot = KOTLIN_COMPILER_PATH + languageVersion = kotlinProperties.languageVersion.versionString + apiVersion = kotlinProperties.apiVersion.versionString + + val pluginClasspathsList = mutableListOf() + val pluginOptionsList = mutableListOf() + + kotlinProperties.compilerPlugins.entries.forEach { plugin -> + plugin.jarPath?.takeIf { plugin.active }?.let { jarPath -> + pluginClasspathsList.add(jarPath.replace("\$KOTLIN_HOME", ProjectUtils.ktHome)) + plugin.args.forEach { arg -> + pluginOptionsList.add("plugin: $arg") + } + } + } + + pluginClasspaths = pluginClasspathsList.toTypedArray() + pluginOptions = pluginOptionsList.toTypedArray() + + classpath = ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkUndefined) + .joinToString(separator = System.getProperty("path.separator")) { it.absolutePath } + + + } + + private fun configureCompilerArguments( + javaProject: IJavaProject, outputDir: String, sourceDirs: List + ): Array = with(mutableListOf()) { + val kotlinProperties = + KotlinEnvironment.getEnvironment(javaProject.project).compilerProperties + + add("-kotlin-home") + add(ProjectUtils.ktHome) + + val jdkUndefined = kotlinProperties.isJDKHomUndefined() + kotlinProperties.jdkHome?.takeUnless { jdkUndefined }?.let { jdkHomePath -> + add("-jdk-home") + add(jdkHomePath) + } ?: add("-no-jdk") + + + add("-no-stdlib") // Because we add runtime into the classpath + + add("-jvm-target") + add(kotlinProperties.jvmTarget.description) + + add("-language-version") + add(kotlinProperties.languageVersion.versionString) + + add("-api-version") + add(kotlinProperties.apiVersion.versionString) + + kotlinProperties.compilerPlugins.entries.forEach { plugin -> + addAll(configurePlugin(plugin)) + } + + kotlinProperties.compilerFlags?.takeUnless { it.isBlank() }?.split("\\s+".toRegex())?.let { + addAll(it) + } + + add("-classpath") + ProjectUtils.collectClasspathWithDependenciesForLaunch(javaProject, jdkUndefined) + .joinToString(separator = System.getProperty("path.separator")) { it.absolutePath } + .let { add(it) } + + add("-d") + add(outputDir) + + addAll(sourceDirs.map { + it.absolutePath + }) + + toTypedArray() + } + + private fun configurePlugin(plugin: CompilerPlugin): Collection = mutableListOf().apply { + plugin.jarPath?.takeIf { plugin.active }?.let { jarPath -> + add("-Xplugin=${jarPath.replace("\$KOTLIN_HOME", ProjectUtils.ktHome)}") + plugin.args.forEach { arg -> + add("-P") + add("plugin: $arg") + } + } + } + + private class CompilerMessageCollector : MessageCollector { + var hasErrors = false + val severities: MutableList = mutableListOf() + val compilerOutput = CompilerOutputData() + + override fun report( + severity: CompilerMessageSeverity, + message: String, + location: CompilerMessageLocation? + ) { + hasErrors == hasErrors || severity.isError + severities.add(severity) + compilerOutput.add(severity, message, location) + } + + override fun hasErrors(): Boolean = hasErrors + + override fun clear() { + hasErrors = false + } + + fun getCompilerResult(): KotlinCompilerResult = + KotlinCompilerResult(severities.firstOrNull { it == ERROR || it == EXCEPTION } == null, compilerOutput) + } + + private fun parseCompilerOutput(reader: Reader): KotlinCompilerResult { + val messageCollector = CompilerMessageCollector() + + CompilerOutputParser.parseCompilerMessagesFromReader(messageCollector, reader) + + return messageCollector.getCompilerResult() + } +} + +class KotlinCompilerResult(val result: Boolean, val compilerOutput: CompilerOutputData) { + + fun compiledCorrectly() = result +} \ No newline at end of file diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler2.java b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler2.java new file mode 100644 index 000000000..c2b8a5225 --- /dev/null +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompiler2.java @@ -0,0 +1 @@ +package org.jetbrains.kotlin.core.compiler; diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.java b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.java index 590588c31..3286b9d2a 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.java +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/compiler/KotlinCompilerUtils.java @@ -23,19 +23,22 @@ import org.eclipse.jdt.core.IJavaProject; import org.jetbrains.annotations.NotNull; import org.jetbrains.kotlin.core.Activator; -import org.jetbrains.kotlin.core.compiler.KotlinCompiler.KotlinCompilerResult; import org.jetbrains.kotlin.core.launch.CompilerOutputData; public class KotlinCompilerUtils { - @NotNull + public static KotlinCompilerResult compileWholeProject(@NotNull IJavaProject javaProject) throws CoreException { - return KotlinCompiler.INSTANCE.compileKotlinFiles(javaProject); + return KotlinCompiler.compileKotlinFiles(javaProject); } + public static KotlinCompilerResult compileProjectIncrementally(@NotNull IJavaProject javaProject) { + return KotlinCompiler.compileIncrementallyFiles(javaProject); + } + public static void handleCompilerOutput(@NotNull CompilerOutputData compilerOutput) throws CoreException { IStatus status = new Status(IStatus.ERROR, Activator.PLUGIN_ID, 1, "", null); IStatusHandler handler = DebugPlugin.getDefault().getStatusHandler(status); - + if (handler != null) { handler.handleStatus(status, compilerOutput); } diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinEnvironment.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinEnvironment.kt index b7e2c54e9..794369685 100644 --- a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinEnvironment.kt +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/model/KotlinEnvironment.kt @@ -54,6 +54,7 @@ import org.jetbrains.kotlin.core.builder.KotlinPsiManager import org.jetbrains.kotlin.core.filesystem.KotlinLightClassManager import org.jetbrains.kotlin.core.log.KotlinLogger import org.jetbrains.kotlin.core.preferences.CompilerPlugin +import org.jetbrains.kotlin.core.preferences.KotlinBuildingProperties import org.jetbrains.kotlin.core.preferences.KotlinProperties import org.jetbrains.kotlin.core.resolve.lang.kotlin.EclipseVirtualFileFinderFactory import org.jetbrains.kotlin.core.utils.ProjectUtils @@ -303,6 +304,11 @@ class KotlinEnvironment private constructor(val eclipseProject: IProject, dispos val compilerProperties: KotlinProperties get() = projectCompilerProperties.takeIf { it.globalsOverridden } ?: KotlinProperties.workspaceInstance + val projectBuildingProperties: KotlinBuildingProperties = KotlinBuildingProperties(ProjectScope(eclipseProject)) + + val buildingProperties: KotlinBuildingProperties + get() = projectBuildingProperties.takeIf { it.globalsOverridden } ?: KotlinBuildingProperties.workspaceInstance + val index by lazy { JvmDependenciesIndexImpl(getRoots().toList()) } init { diff --git a/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/preferences/KotlinBuildingProperties.kt b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/preferences/KotlinBuildingProperties.kt new file mode 100644 index 000000000..8b9528a72 --- /dev/null +++ b/kotlin-eclipse-core/src/org/jetbrains/kotlin/core/preferences/KotlinBuildingProperties.kt @@ -0,0 +1,17 @@ +package org.jetbrains.kotlin.core.preferences + +import org.eclipse.core.runtime.preferences.IScopeContext +import org.eclipse.core.runtime.preferences.InstanceScope +import org.jetbrains.kotlin.core.Activator + +class KotlinBuildingProperties(scope: IScopeContext = InstanceScope.INSTANCE) : + Preferences(scope, Activator.PLUGIN_ID) { + + var globalsOverridden by BooleanPreference() + + var useIncremental by BooleanPreference() + + companion object { + val workspaceInstance by lazy { KotlinBuildingProperties() } + } +} \ No newline at end of file diff --git a/kotlin-eclipse-ui/Run Kotlin Plugin with Equinox Weaving.launch b/kotlin-eclipse-ui/Run Kotlin Plugin with Equinox Weaving.launch index edc7e1929..12b165c82 100644 --- a/kotlin-eclipse-ui/Run Kotlin Plugin with Equinox Weaving.launch +++ b/kotlin-eclipse-ui/Run Kotlin Plugin with Equinox Weaving.launch @@ -13,7 +13,7 @@ - + diff --git a/kotlin-eclipse-ui/plugin.xml b/kotlin-eclipse-ui/plugin.xml index a7e10d6d2..d2dffc685 100644 --- a/kotlin-eclipse-ui/plugin.xml +++ b/kotlin-eclipse-ui/plugin.xml @@ -222,6 +222,12 @@ id="kotlin-eclipse-ui.preferences.compiler" name="Compiler"> + + @@ -922,6 +928,16 @@ + + + + + + @@ -932,4 +948,18 @@ name="Kotlin script environments"> + + + + + + diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/building/ProjectBuildingPropertyPage.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/building/ProjectBuildingPropertyPage.kt new file mode 100644 index 000000000..6073de1c0 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/building/ProjectBuildingPropertyPage.kt @@ -0,0 +1,41 @@ +package org.jetbrains.kotlin.preferences.building + +import org.eclipse.core.resources.IProject +import org.eclipse.core.resources.ProjectScope +import org.eclipse.swt.widgets.Composite +import org.eclipse.swt.widgets.Control +import org.eclipse.ui.IWorkbenchPropertyPage +import org.jetbrains.kotlin.core.preferences.KotlinBuildingProperties +import org.jetbrains.kotlin.preferences.BasePropertyPage +import org.jetbrains.kotlin.preferences.views.buildingPropertiesView +import org.jetbrains.kotlin.swt.builders.View +import org.jetbrains.kotlin.swt.builders.asView +import org.jetbrains.kotlin.swt.builders.checkbox +import org.jetbrains.kotlin.swt.builders.enabled +import org.jetbrains.kotlin.swt.builders.layout +import org.jetbrains.kotlin.swt.builders.separator +import org.jetbrains.kotlin.utils.LazyObservable + +class ProjectBuildingPropertyPage : BasePropertyPage(), IWorkbenchPropertyPage { + // project must be lazy initialized, because getElement() called during construction of page object returns null + val project: IProject by lazy { element.getAdapter(IProject::class.java) } + + private var overrideFlag by LazyObservable({ properties.globalsOverridden }) { _, _, v -> + properties.globalsOverridden = v + settingsView.enabled = v + } + + private lateinit var settingsView: View + + override val properties by lazy { KotlinBuildingProperties(ProjectScope(project)) } + + override fun createUI(parent: Composite): Control = + parent.asView.apply { + checkbox(::overrideFlag, "Enable project speciffic settings") + separator { layout(horizontalGrab = true) } + settingsView = buildingPropertiesView(properties) { + enabled = properties.globalsOverridden + } + }.control +} + diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/building/WorkspaceBuildingPropertyPage.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/building/WorkspaceBuildingPropertyPage.kt new file mode 100644 index 000000000..7f6b660cd --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/building/WorkspaceBuildingPropertyPage.kt @@ -0,0 +1,26 @@ +package org.jetbrains.kotlin.preferences.building + +import org.eclipse.swt.widgets.Composite +import org.eclipse.swt.widgets.Control +import org.eclipse.ui.IWorkbench +import org.eclipse.ui.IWorkbenchPreferencePage +import org.jetbrains.kotlin.core.preferences.KotlinBuildingProperties +import org.jetbrains.kotlin.preferences.BasePropertyPage +import org.jetbrains.kotlin.preferences.views.buildingPropertiesView +import org.jetbrains.kotlin.swt.builders.asView + +class WorkspaceBuildingPropertyPage : BasePropertyPage(), IWorkbenchPreferencePage { + + override val properties = KotlinBuildingProperties.workspaceInstance + + override fun init(workbench: IWorkbench?) { + } + + override fun createUI(parent: Composite): Control = + parent.asView + .buildingPropertiesView(properties) { + onIsValidChanged = { setValid(it) } + } + .control + +} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/views/BuildingPropertiesView.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/views/BuildingPropertiesView.kt new file mode 100644 index 000000000..6f252d9c4 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/preferences/views/BuildingPropertiesView.kt @@ -0,0 +1,41 @@ +package org.jetbrains.kotlin.preferences.views + +import org.eclipse.swt.widgets.Composite +import org.jetbrains.kotlin.core.preferences.KotlinBuildingProperties +import org.jetbrains.kotlin.swt.builders.View +import org.jetbrains.kotlin.swt.builders.checkbox +import org.jetbrains.kotlin.swt.builders.gridContainer +import kotlin.properties.Delegates + +fun View.buildingPropertiesView( + kotlinBuildingProperties: KotlinBuildingProperties, + operations: BuildingPropertiesView.() -> Unit = {} +) = + BuildingPropertiesView(this, kotlinBuildingProperties) + .apply(operations) + +class BuildingPropertiesView( + parent: View, + kotlinBuildingProperties: KotlinBuildingProperties +) : View, Validable { + + override val control: Composite + + override var isValid: Boolean = true + private set(value) { + field = value + onIsValidChanged(value) + } + + override var onIsValidChanged: (Boolean) -> Unit = {} + + private var useIncremental by Delegates.observable(kotlinBuildingProperties.useIncremental) { _, _, value -> + kotlinBuildingProperties.useIncremental = value + } + + init { + control = parent.gridContainer { + checkbox(::useIncremental, "Use incremental compiler (experimental)") + }.control + } +} diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/BaseKotlinBuilderElement.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/BaseKotlinBuilderElement.kt new file mode 100644 index 000000000..505709d79 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/BaseKotlinBuilderElement.kt @@ -0,0 +1,128 @@ +package org.jetbrains.kotlin.ui.builder + +import org.eclipse.core.resources.IFile +import org.eclipse.core.resources.IProject +import org.eclipse.core.resources.IResourceDelta +import org.eclipse.core.resources.ResourcesPlugin +import org.eclipse.jdt.core.IJavaProject +import org.eclipse.ui.PlatformUI +import org.jetbrains.kotlin.core.asJava.KotlinLightClassGeneration +import org.jetbrains.kotlin.core.builder.KotlinPsiManager +import org.jetbrains.kotlin.core.model.KotlinScriptEnvironment +import org.jetbrains.kotlin.core.resolve.lang.java.structure.EclipseJavaElementUtil +import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil +import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics +import org.jetbrains.kotlin.ui.editors.KotlinFileEditor +import org.jetbrains.kotlin.ui.editors.annotations.AnnotationManager +import org.jetbrains.kotlin.ui.editors.annotations.DiagnosticAnnotation +import org.jetbrains.kotlin.ui.editors.annotations.DiagnosticAnnotationUtil +import org.jetbrains.kotlin.ui.editors.quickfix.removeMarkers + +abstract class BaseKotlinBuilderElement { + + private val fileFilters = listOf(ScriptFileFilter, FileFromOutputFolderFilter, FileFromKotlinBinFolderFilter) + + abstract fun build(project: IProject, delta: IResourceDelta?, kind: Int): Array? + + protected fun isAllFilesApplicableForFilters(files: Set, javaProject: IJavaProject): Boolean { + return files.all { file -> + fileFilters.any { filter -> + filter.isApplicable(file, javaProject) + } + } + } + + protected fun makeClean(javaProject: IJavaProject) { + val kotlinFiles = KotlinPsiManager.getFilesByProject(javaProject.project) + val existingFiles = kotlinFiles.filter { it.exists() } + + commitFiles(existingFiles) + + clearProblemAnnotationsFromOpenEditorsExcept(emptyList()) + clearMarkersFromFiles(existingFiles) + + runCancellableAnalysisFor(javaProject) { analysisResult -> + updateLineMarkers(analysisResult.bindingContext.diagnostics, existingFiles) + KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinFiles) + } + } + + protected fun commitFiles(files: Collection) { + files.forEach { KotlinPsiManager.commitFile(it, EditorUtil.getDocument(it)) } + } + + protected fun getAllAffectedFiles(resourceDelta: IResourceDelta): Set { + val affectedFiles = hashSetOf() + resourceDelta.accept { delta -> + if (delta.kind == IResourceDelta.NO_CHANGE) return@accept false + + val resource = delta.resource + if (resource is IFile) { + affectedFiles.add(resource) + } else { + return@accept true + } + + false + } + + return affectedFiles + } + + protected fun updateLineMarkers(diagnostics: Diagnostics, affectedFiles: List) { + clearMarkersFromFiles(affectedFiles) + addMarkersToProject(DiagnosticAnnotationUtil.INSTANCE.handleDiagnostics(diagnostics), affectedFiles) + } +} + +private fun clearMarkersFromFiles(files: List) { + files.forEach { it.removeMarkers() } +} + +fun clearProblemAnnotationsFromOpenEditorsExcept(affectedFiles: List) { + for (window in PlatformUI.getWorkbench().getWorkbenchWindows()) { + for (page in window.pages) { + page.editorReferences + .map { it.getEditor(false) } + .filterIsInstance(KotlinFileEditor::class.java) + .filterNot { it.eclipseFile in affectedFiles } + .forEach { + AnnotationManager.removeAnnotations(it, AnnotationManager.ANNOTATION_ERROR_TYPE) + AnnotationManager.removeAnnotations(it, AnnotationManager.ANNOTATION_WARNING_TYPE) + } + } + } +} + +private fun addMarkersToProject(annotations: Map>, affectedFiles: List) { + for (file in affectedFiles) { + DiagnosticAnnotationUtil.INSTANCE.addParsingDiagnosticAnnotations(file, annotations) + annotations[file]?.forEach { AnnotationManager.addProblemMarker(it, file) } + } +} + +interface KotlinFileFilterForBuild { + fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean +} + +object ScriptFileFilter : KotlinFileFilterForBuild { + override fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean { + return KotlinScriptEnvironment.isScript(file) + } +} + +object FileFromOutputFolderFilter : KotlinFileFilterForBuild { + override fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean { + val workspaceLocation = ResourcesPlugin.getWorkspace().getRoot().getFullPath() + val outputLocation = javaProject.outputLocation + + val filePathLocation = file.fullPath.makeRelativeTo(workspaceLocation) + return outputLocation.isPrefixOf(filePathLocation) + } +} + +object FileFromKotlinBinFolderFilter : KotlinFileFilterForBuild { + override fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean { + return EclipseJavaElementUtil.isFromKotlinBinFolder(file) + } +} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/CompileKotlinClassesAction.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/CompileKotlinClassesAction.kt new file mode 100644 index 000000000..a62112311 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/CompileKotlinClassesAction.kt @@ -0,0 +1,27 @@ +package org.jetbrains.kotlin.ui.builder + +import org.eclipse.core.resources.IncrementalProjectBuilder.INCREMENTAL_BUILD +import org.eclipse.core.resources.ResourcesPlugin +import org.eclipse.jface.action.IAction +import org.eclipse.jface.viewers.ISelection +import org.eclipse.ui.IWorkbenchWindow +import org.eclipse.ui.IWorkbenchWindowActionDelegate +import org.jetbrains.kotlin.preferences.compiler.RebuildJob + +class CompileKotlinClassesAction : IWorkbenchWindowActionDelegate { + + override fun run(action: IAction?) { + RebuildJob { monitor -> + ResourcesPlugin.getWorkspace().build(INCREMENTAL_BUILD, monitor) + }.schedule() + } + + override fun selectionChanged(action: IAction?, selection: ISelection?) { + } + + override fun init(window: IWorkbenchWindow?) { + } + + override fun dispose() { + } +} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/IncrementalKotlinBuilderElement.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/IncrementalKotlinBuilderElement.kt new file mode 100644 index 000000000..2e868e923 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/IncrementalKotlinBuilderElement.kt @@ -0,0 +1,83 @@ +package org.jetbrains.kotlin.ui.builder + +import org.eclipse.core.resources.IProject +import org.eclipse.core.resources.IResourceDelta +import org.eclipse.core.resources.IncrementalProjectBuilder +import org.eclipse.core.runtime.Status +import org.eclipse.core.runtime.jobs.Job +import org.eclipse.jdt.core.IJavaProject +import org.eclipse.jdt.core.JavaCore +import org.jetbrains.kotlin.core.asJava.KotlinLightClassGeneration +import org.jetbrains.kotlin.core.builder.KotlinPsiManager +import org.jetbrains.kotlin.core.compiler.KotlinCompilerResult +import org.jetbrains.kotlin.core.compiler.KotlinCompilerUtils +import org.jetbrains.kotlin.core.model.runJob +import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer +import org.jetbrains.kotlin.ui.KotlinPluginUpdater + +class IncrementalKotlinBuilderElement : BaseKotlinBuilderElement() { + + override fun build(project: IProject, delta: IResourceDelta?, kind: Int): Array? { + val javaProject = JavaCore.create(project) + + if (kind == IncrementalProjectBuilder.FULL_BUILD) { + makeClean(javaProject) + return null + } + + compileKotlinFilesIncrementally(javaProject) + + val allAffectedFiles = if (delta != null) getAllAffectedFiles(delta) else emptySet() + + if (allAffectedFiles.isNotEmpty()) { + if (isAllFilesApplicableForFilters(allAffectedFiles, javaProject)) { + return null + } + } + + val kotlinAffectedFiles = + allAffectedFiles + .filter { KotlinPsiManager.isKotlinSourceFile(it, javaProject) } + .toSet() + + val existingAffectedFiles = kotlinAffectedFiles.filter { it.exists() } + + commitFiles(existingAffectedFiles) + + KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinAffectedFiles) + if (kotlinAffectedFiles.isNotEmpty()) { + + runJob("Checking for update", Job.DECORATE) { + KotlinPluginUpdater.kotlinFileEdited() + Status.OK_STATUS + } + } + + val ktFiles = existingAffectedFiles.map { KotlinPsiManager.getParsedFile(it) } + + val analysisResultWithProvider = if (ktFiles.isEmpty()) + KotlinAnalyzer.analyzeProject(project) + else + KotlinAnalyzer.analyzeFiles(ktFiles) + + clearProblemAnnotationsFromOpenEditorsExcept(existingAffectedFiles) + updateLineMarkers(analysisResultWithProvider.analysisResult.bindingContext.diagnostics, existingAffectedFiles) + + runCancellableAnalysisFor(javaProject) { analysisResult -> + val projectFiles = KotlinPsiManager.getFilesByProject(javaProject.project) + updateLineMarkers( + analysisResult.bindingContext.diagnostics, + (projectFiles - existingAffectedFiles).toList() + ) + } + + return null + } + + private fun compileKotlinFilesIncrementally(javaProject: IJavaProject) { + val compilerResult: KotlinCompilerResult = KotlinCompilerUtils.compileProjectIncrementally(javaProject) + if (!compilerResult.compiledCorrectly()) { + KotlinCompilerUtils.handleCompilerOutput(compilerResult.compilerOutput) + } + } +} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilder.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilder.kt index 266f4a6ec..8337c94c9 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilder.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilder.kt @@ -16,33 +16,11 @@ *******************************************************************************/ package org.jetbrains.kotlin.ui.builder -import org.eclipse.core.resources.IFile import org.eclipse.core.resources.IProject -import org.eclipse.core.resources.IResourceDelta import org.eclipse.core.resources.IncrementalProjectBuilder -import org.eclipse.core.resources.ResourcesPlugin import org.eclipse.core.runtime.IProgressMonitor -import org.eclipse.core.runtime.Status -import org.eclipse.core.runtime.jobs.Job -import org.eclipse.debug.core.model.LaunchConfigurationDelegate -import org.eclipse.jdt.core.IJavaProject import org.eclipse.jdt.core.JavaCore -import org.eclipse.ui.PlatformUI -import org.jetbrains.kotlin.core.asJava.KotlinLightClassGeneration -import org.jetbrains.kotlin.core.builder.KotlinPsiManager -import org.jetbrains.kotlin.core.compiler.KotlinCompilerUtils -import org.jetbrains.kotlin.core.model.KotlinScriptEnvironment -import org.jetbrains.kotlin.core.model.runJob -import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer -import org.jetbrains.kotlin.core.resolve.lang.java.structure.EclipseJavaElementUtil -import org.jetbrains.kotlin.eclipse.ui.utils.EditorUtil -import org.jetbrains.kotlin.resolve.diagnostics.Diagnostics -import org.jetbrains.kotlin.ui.KotlinPluginUpdater -import org.jetbrains.kotlin.ui.editors.KotlinFileEditor -import org.jetbrains.kotlin.ui.editors.annotations.AnnotationManager -import org.jetbrains.kotlin.ui.editors.annotations.DiagnosticAnnotation -import org.jetbrains.kotlin.ui.editors.annotations.DiagnosticAnnotationUtil -import org.jetbrains.kotlin.ui.editors.quickfix.removeMarkers +import org.jetbrains.kotlin.core.model.KotlinEnvironment class KotlinBuilder : IncrementalProjectBuilder() { @@ -53,71 +31,23 @@ class KotlinBuilder : IncrementalProjectBuilder() { private const val RESOURCE_COPY_EXCLUSION_FILTER_VALUE = "*.kt" } - private val fileFilters = listOf(ScriptFileFilter, FileFromOuputFolderFilter, FileFromKotlinBinFolderFilter) + private val oldBuilderElement: KotlinBuilderElement by lazy { KotlinBuilderElement() } + + private val incrementalBuilder: IncrementalKotlinBuilderElement by lazy { IncrementalKotlinBuilderElement() } override fun build(kind: Int, args: Map?, monitor: IProgressMonitor?): Array? { val javaProject = JavaCore.create(project) - if (isBuildingForLaunch()) { - compileKotlinFiles(javaProject) - return null - } - - if (kind == FULL_BUILD) { - makeClean(javaProject) - return null - } - val delta = getDelta(project) - val allAffectedFiles = if (delta != null) getAllAffectedFiles(delta) else emptySet() - - if (allAffectedFiles.isNotEmpty()) { - if (isAllFilesApplicableForFilters(allAffectedFiles, javaProject)) { - return null - } - } - - val kotlinAffectedFiles = - allAffectedFiles - .filter { KotlinPsiManager.isKotlinSourceFile(it, javaProject) } - .toSet() - - val existingAffectedFiles = kotlinAffectedFiles.filter { it.exists() } - - commitFiles(existingAffectedFiles) - - KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinAffectedFiles) - if (kotlinAffectedFiles.isNotEmpty()) { - - runJob("Checking for update", Job.DECORATE) { - KotlinPluginUpdater.kotlinFileEdited() - Status.OK_STATUS - } - } - - val ktFiles = existingAffectedFiles.map { KotlinPsiManager.getParsedFile(it) } - - val analysisResultWithProvider = if (ktFiles.isEmpty()) - KotlinAnalyzer.analyzeProject(project) - else - KotlinAnalyzer.analyzeFiles(ktFiles) - - clearProblemAnnotationsFromOpenEditorsExcept(existingAffectedFiles) - updateLineMarkers(analysisResultWithProvider.analysisResult.bindingContext.diagnostics, existingAffectedFiles) - - runCancellableAnalysisFor(javaProject) { analysisResult -> - val projectFiles = KotlinPsiManager.getFilesByProject(javaProject.project) - updateLineMarkers( - analysisResult.bindingContext.diagnostics, - (projectFiles - existingAffectedFiles).toList() - ) - } - - return null + return if (KotlinEnvironment.getEnvironment(javaProject.project).buildingProperties.useIncremental) { + incrementalBuilder + } else { + oldBuilderElement + }.build(project, delta, kind) } override fun startupOnInitialize() { super.startupOnInitialize() - with (JavaCore.create(project)) { + with(JavaCore.create(project)) { (getOption(RESOURCE_COPY_EXCLUSION_FILTER_NAME, false) ?: "").let { value -> if (!RESOURCE_COPY_EXCLUSION_FILTER_VALUE_PATTERN.matches(value)) setOption( @@ -127,135 +57,4 @@ class KotlinBuilder : IncrementalProjectBuilder() { } } } - - private fun isAllFilesApplicableForFilters(files: Set, javaProject: IJavaProject): Boolean { - return files.all { file -> - fileFilters.any { filter -> - filter.isApplicable(file, javaProject) - } - } - } - - private fun makeClean(javaProject: IJavaProject) { - val kotlinFiles = KotlinPsiManager.getFilesByProject(javaProject.project) - val existingFiles = kotlinFiles.filter { it.exists() } - - commitFiles(existingFiles) - - clearProblemAnnotationsFromOpenEditorsExcept(emptyList()) - clearMarkersFromFiles(existingFiles) - - runCancellableAnalysisFor(javaProject) { analysisResult -> - updateLineMarkers(analysisResult.bindingContext.diagnostics, existingFiles) - KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinFiles) - } - } - - private fun commitFiles(files: Collection) { - files.forEach { KotlinPsiManager.commitFile(it, EditorUtil.getDocument(it)) } - } - - private fun isAllScripts(files: Set): Boolean { - return files.all { KotlinScriptEnvironment.isScript(it) } - } - - private fun isAllFromOutputFolder(files: Set, javaProject: IJavaProject): Boolean { - val workspaceLocation = ResourcesPlugin.getWorkspace().getRoot().getFullPath() - val outputLocation = javaProject.outputLocation - for (file in files) { - val filePathLocation = file.getFullPath().makeRelativeTo(workspaceLocation) - if (!outputLocation.isPrefixOf(filePathLocation)) { - return false - } - } - - return true - } - - private fun getAllAffectedFiles(resourceDelta: IResourceDelta): Set { - val affectedFiles = hashSetOf() - resourceDelta.accept { delta -> - if (delta.getKind() == IResourceDelta.NO_CHANGE) return@accept false - - val resource = delta.getResource() - if (resource is IFile) { - affectedFiles.add(resource) - } else { - return@accept true - } - - false - } - - return affectedFiles - } - - private fun isBuildingForLaunch(): Boolean { - val launchDelegateFQName = LaunchConfigurationDelegate::class.java.getCanonicalName() - return Thread.currentThread().getStackTrace().find { it.className == launchDelegateFQName } != null - } - - private fun compileKotlinFiles(javaProject: IJavaProject) { - val compilerResult = KotlinCompilerUtils.compileWholeProject(javaProject) - if (!compilerResult.compiledCorrectly()) { - KotlinCompilerUtils.handleCompilerOutput(compilerResult.getCompilerOutput()) - } - } -} - -fun updateLineMarkers(diagnostics: Diagnostics, affectedFiles: List) { - clearMarkersFromFiles(affectedFiles) - addMarkersToProject(DiagnosticAnnotationUtil.INSTANCE.handleDiagnostics(diagnostics), affectedFiles) -} - -private fun clearMarkersFromFiles(files: List) { - files.forEach { it.removeMarkers() } -} - -private fun clearProblemAnnotationsFromOpenEditorsExcept(affectedFiles: List) { - for (window in PlatformUI.getWorkbench().getWorkbenchWindows()) { - for (page in window.getPages()) { - page.getEditorReferences() - .map { it.getEditor(false) } - .filterIsInstance(KotlinFileEditor::class.java) - .filterNot { it.eclipseFile in affectedFiles } - .forEach { - AnnotationManager.removeAnnotations(it, AnnotationManager.ANNOTATION_ERROR_TYPE) - AnnotationManager.removeAnnotations(it, AnnotationManager.ANNOTATION_WARNING_TYPE) - } - } - } -} - -private fun addMarkersToProject(annotations: Map>, affectedFiles: List) { - for (file in affectedFiles) { - DiagnosticAnnotationUtil.INSTANCE.addParsingDiagnosticAnnotations(file, annotations) - annotations[file]?.forEach { AnnotationManager.addProblemMarker(it, file) } - } -} - -interface KotlinFileFilterForBuild { - fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean -} - -object ScriptFileFilter : KotlinFileFilterForBuild { - override fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean { - return KotlinScriptEnvironment.isScript(file) - } -} - -object FileFromOuputFolderFilter : KotlinFileFilterForBuild { - override fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean { - val workspaceLocation = ResourcesPlugin.getWorkspace().getRoot().getFullPath() - val outputLocation = javaProject.outputLocation - - val filePathLocation = file.getFullPath().makeRelativeTo(workspaceLocation) - return outputLocation.isPrefixOf(filePathLocation) - } -} - -object FileFromKotlinBinFolderFilter : KotlinFileFilterForBuild { - override fun isApplicable(file: IFile, javaProject: IJavaProject): Boolean { - return EclipseJavaElementUtil.isFromKotlinBinFolder(file) - } } \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilderElement.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilderElement.kt new file mode 100644 index 000000000..4818651a9 --- /dev/null +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/builder/KotlinBuilderElement.kt @@ -0,0 +1,93 @@ +package org.jetbrains.kotlin.ui.builder + +import org.eclipse.core.resources.IProject +import org.eclipse.core.resources.IResourceDelta +import org.eclipse.core.resources.IncrementalProjectBuilder.FULL_BUILD +import org.eclipse.core.runtime.Status +import org.eclipse.core.runtime.jobs.Job +import org.eclipse.debug.core.model.LaunchConfigurationDelegate +import org.eclipse.jdt.core.IJavaProject +import org.eclipse.jdt.core.JavaCore +import org.jetbrains.kotlin.core.asJava.KotlinLightClassGeneration +import org.jetbrains.kotlin.core.builder.KotlinPsiManager +import org.jetbrains.kotlin.core.compiler.KotlinCompilerResult +import org.jetbrains.kotlin.core.compiler.KotlinCompilerUtils +import org.jetbrains.kotlin.core.model.runJob +import org.jetbrains.kotlin.core.resolve.KotlinAnalyzer +import org.jetbrains.kotlin.ui.KotlinPluginUpdater + +class KotlinBuilderElement : BaseKotlinBuilderElement() { + + override fun build(project: IProject, delta: IResourceDelta?, kind: Int): Array? { + val javaProject = JavaCore.create(project) + if (isBuildingForLaunch()) { + compileKotlinFiles(javaProject) + return null + } + + if (kind == FULL_BUILD) { + makeClean(javaProject) + return null + } + + val allAffectedFiles = if (delta != null) getAllAffectedFiles(delta) else emptySet() + + if (allAffectedFiles.isNotEmpty()) { + if (isAllFilesApplicableForFilters(allAffectedFiles, javaProject)) { + return null + } + } + + val kotlinAffectedFiles = + allAffectedFiles + .filter { KotlinPsiManager.isKotlinSourceFile(it, javaProject) } + .toSet() + + val existingAffectedFiles = kotlinAffectedFiles.filter { it.exists() } + + commitFiles(existingAffectedFiles) + + KotlinLightClassGeneration.updateLightClasses(javaProject.project, kotlinAffectedFiles) + if (kotlinAffectedFiles.isNotEmpty()) { + + runJob("Checking for update", Job.DECORATE) { + KotlinPluginUpdater.kotlinFileEdited() + Status.OK_STATUS + } + } + + val ktFiles = existingAffectedFiles.map { KotlinPsiManager.getParsedFile(it) } + + val analysisResultWithProvider = if (ktFiles.isEmpty()) + KotlinAnalyzer.analyzeProject(project) + else + KotlinAnalyzer.analyzeFiles(ktFiles) + + clearProblemAnnotationsFromOpenEditorsExcept(existingAffectedFiles) + updateLineMarkers(analysisResultWithProvider.analysisResult.bindingContext.diagnostics, existingAffectedFiles) + + runCancellableAnalysisFor(javaProject) { analysisResult -> + val projectFiles = KotlinPsiManager.getFilesByProject(javaProject.project) + updateLineMarkers( + analysisResult.bindingContext.diagnostics, + (projectFiles - existingAffectedFiles).toList() + ) + } + + return null + } + + private fun isBuildingForLaunch(): Boolean { + val launchDelegateFQName = LaunchConfigurationDelegate::class.java.canonicalName + return Thread.currentThread().stackTrace.find { + it.className == launchDelegateFQName + } != null + } + + private fun compileKotlinFiles(javaProject: IJavaProject) { + val compilerResult: KotlinCompilerResult = KotlinCompilerUtils.compileWholeProject(javaProject) + if (!compilerResult.compiledCorrectly()) { + KotlinCompilerUtils.handleCompilerOutput(compilerResult.compilerOutput) + } + } +} \ No newline at end of file diff --git a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/KotlinScriptLaunchConfigurationDelegate.kt b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/KotlinScriptLaunchConfigurationDelegate.kt index 1a3dc42c9..58eb25f3f 100644 --- a/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/KotlinScriptLaunchConfigurationDelegate.kt +++ b/kotlin-eclipse-ui/src/org/jetbrains/kotlin/ui/launch/KotlinScriptLaunchConfigurationDelegate.kt @@ -59,7 +59,7 @@ class KotlinScriptLaunchConfigurationDelegate : AbstractJavaLaunchConfigurationD monitor.subTask("Building project...") val javaProject = JavaCore.create(scriptFile.project) - KotlinCompiler.INSTANCE.compileKotlinFiles(javaProject) + KotlinCompiler.compileKotlinFiles(javaProject) if (monitor.isCanceled) return monitor.worked(3)