diff --git a/.idea/libraries/asm.xml b/.idea/libraries/asm.xml
index afc4bcc3b627848973842068e51ff0acaffaede2..d343b26b41660e932816875477d5b845f3e496c1 100644
--- a/.idea/libraries/asm.xml
+++ b/.idea/libraries/asm.xml
@@ -1,7 +1,7 @@
-
+
diff --git a/compiler/cli/src/org/jetbrains/jet/cli/KotlinCompiler.java b/compiler/cli/src/org/jetbrains/jet/cli/KotlinCompiler.java
index 62d0e459c6b96f7d1f827acb46cbbcab53ed069f..9738e1958e2e89740c2a6dd5ae69c802a7059ab2 100644
--- a/compiler/cli/src/org/jetbrains/jet/cli/KotlinCompiler.java
+++ b/compiler/cli/src/org/jetbrains/jet/cli/KotlinCompiler.java
@@ -19,12 +19,8 @@ package org.jetbrains.jet.cli;
import com.sampullara.cli.Args;
import com.sampullara.cli.Argument;
import org.jetbrains.annotations.NotNull;
-import org.jetbrains.jet.compiler.CompileEnvironment;
-import org.jetbrains.jet.compiler.CompileEnvironmentException;
-import org.jetbrains.jet.compiler.CompilerPlugin;
-import org.jetbrains.jet.compiler.FileNameTransformer;
+import org.jetbrains.jet.compiler.*;
-import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
/**
@@ -72,6 +68,9 @@ public class KotlinCompiler {
@Argument(value = "stubs", description = "Compile stubs: ignore function bodies")
public boolean stubs;
+ @Argument(value = "tags", description = "Demarcate each compilation message (error, warning, etc) with an open and close tag")
+ public boolean tags;
+
@Argument(value = "transformNamesToJava", description = "Transform Kotlin file names to *.java. This option is needed for compiling kotlinized Java library headers")
public boolean transformNamesToJava;
}
@@ -108,7 +107,8 @@ public class KotlinCompiler {
return 1;
}
catch (Throwable t) {
- t.printStackTrace();
+ // Always use tags
+ errStream.println(MessageRenderer.TAGS.renderException(t));
return 1;
}
@@ -117,7 +117,11 @@ public class KotlinCompiler {
return 0;
}
- CompileEnvironment environment = new CompileEnvironment(arguments.transformNamesToJava ? ANY_EXTENSION_TO_JAVA : FileNameTransformer.IDENTITY);
+ FileNameTransformer fileNameTransformer = arguments.transformNamesToJava
+ ? ANY_EXTENSION_TO_JAVA
+ : FileNameTransformer.IDENTITY;
+ MessageRenderer messageRenderer = arguments.tags ? MessageRenderer.TAGS : MessageRenderer.PLAIN;
+ CompileEnvironment environment = new CompileEnvironment(fileNameTransformer, messageRenderer);
try {
environment.setIgnoreErrors(false);
environment.setErrorStream(errStream);
@@ -138,16 +142,18 @@ public class KotlinCompiler {
if (arguments.module != null) {
environment.compileModuleScript(arguments.module, arguments.jar, arguments.outputDir, arguments.includeRuntime);
- return 0;
}
else {
- if (!environment.compileBunchOfSources(arguments.src, arguments.jar, arguments.outputDir, arguments.includeRuntime)) {
- return 1;
- }
+ environment.compileBunchOfSources(arguments.src, arguments.jar, arguments.outputDir, arguments.includeRuntime);
}
return 0;
- } finally {
+ }
+ catch (Throwable t) {
+ errStream.println(messageRenderer.renderException(t));
+ return 1;
+ }
+ finally {
environment.dispose();
}
}
diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/CompileEnvironment.java b/compiler/cli/src/org/jetbrains/jet/compiler/CompileEnvironment.java
index a1dd8251b1758eb73f2038c5e80f3e89586a1901..833bbf8a885e6b761184e018cd8ac083069b455b 100644
--- a/compiler/cli/src/org/jetbrains/jet/compiler/CompileEnvironment.java
+++ b/compiler/cli/src/org/jetbrains/jet/compiler/CompileEnvironment.java
@@ -55,7 +55,9 @@ import java.util.jar.*;
public class CompileEnvironment {
private JetCoreEnvironment myEnvironment;
private final Disposable myRootDisposable;
- private PrintStream myErrorStream = System.out;
+ private final MessageRenderer myMessageRenderer;
+ private PrintStream myErrorStream = System.err;
+
private final FileNameTransformer myFileNameTransformer;
private URL myStdlib;
@@ -63,10 +65,10 @@ public class CompileEnvironment {
private boolean stubs = false;
public CompileEnvironment() {
- this(FileNameTransformer.IDENTITY);
+ this(FileNameTransformer.IDENTITY, MessageRenderer.PLAIN);
}
- public CompileEnvironment(FileNameTransformer fileNameTransformer) {
+ public CompileEnvironment(FileNameTransformer fileNameTransformer, MessageRenderer messageRenderer) {
myRootDisposable = new Disposable() {
@Override
public void dispose() {
@@ -74,6 +76,7 @@ public class CompileEnvironment {
};
myEnvironment = new JetCoreEnvironment(myRootDisposable);
myFileNameTransformer = fileNameTransformer;
+ myMessageRenderer = messageRenderer;
}
public void setErrorStream(PrintStream errorStream) {
@@ -145,7 +148,7 @@ public class CompileEnvironment {
File rtJar = findRtJar(javaHome);
if (rtJar == null || !rtJar.exists()) {
- throw new CompileEnvironmentException("No JDK rt.jar found under" + javaHome);
+ throw new CompileEnvironmentException("No JDK rt.jar found under " + javaHome);
}
return rtJar;
@@ -164,8 +167,8 @@ public class CompileEnvironment {
return null;
}
- public void compileModuleScript(String moduleFile, @Nullable String jarPath, @Nullable String outputDir, boolean jarRuntime) {
- final List modules = loadModuleScript(moduleFile);
+ public boolean compileModuleScript(String moduleFile, @Nullable String jarPath, @Nullable String outputDir, boolean jarRuntime) {
+ List modules = loadModuleScript(moduleFile);
if (modules == null) {
throw new CompileEnvironmentException("Module script " + moduleFile + " compilation failed");
@@ -178,20 +181,22 @@ public class CompileEnvironment {
final String directory = new File(moduleFile).getParent();
for (Module moduleBuilder : modules) {
ClassFileFactory moduleFactory = compileModule(moduleBuilder, directory);
- if (moduleFactory != null) {
- if (outputDir != null) {
- writeToOutputDirectory(moduleFactory, outputDir);
- }
- else {
- String path = jarPath != null ? jarPath : new File(directory, moduleBuilder.getModuleName() + ".jar").getPath();
- try {
- writeToJar(moduleFactory, new FileOutputStream(path), null, jarRuntime);
- } catch (FileNotFoundException e) {
- throw new CompileEnvironmentException("Invalid jar path " + path, e);
- }
+ if (moduleFactory == null) {
+ return false;
+ }
+ if (outputDir != null) {
+ writeToOutputDirectory(moduleFactory, outputDir);
+ }
+ else {
+ String path = jarPath != null ? jarPath : new File(directory, moduleBuilder.getModuleName() + ".jar").getPath();
+ try {
+ writeToJar(moduleFactory, new FileOutputStream(path), null, jarRuntime);
+ } catch (FileNotFoundException e) {
+ throw new CompileEnvironmentException("Invalid jar path " + path, e);
}
}
}
+ return true;
}
public List loadModuleScript(String moduleFile) {
@@ -199,7 +204,7 @@ public class CompileEnvironment {
scriptCompileSession.addSources(moduleFile);
ensureRuntime();
- if (!scriptCompileSession.analyze(myErrorStream)) {
+ if (!scriptCompileSession.analyze(myErrorStream, myMessageRenderer)) {
return null;
}
final ClassFileFactory factory = scriptCompileSession.generate();
@@ -258,7 +263,7 @@ public class CompileEnvironment {
ensureRuntime();
- if (!moduleCompileSession.analyze(myErrorStream) && !ignoreErrors) {
+ if (!moduleCompileSession.analyze(myErrorStream, myMessageRenderer) && !ignoreErrors) {
return null;
}
return moduleCompileSession.generate();
@@ -345,7 +350,7 @@ public class CompileEnvironment {
CompileSession session = new CompileSession(myEnvironment, myFileNameTransformer);
session.addSources(new LightVirtualFile("script" + LocalTimeCounter.currentTime() + ".kt", JetLanguage.INSTANCE, code));
- if (!session.analyze(myErrorStream) && !ignoreErrors) {
+ if (!session.analyze(myErrorStream, myMessageRenderer) && !ignoreErrors) {
return null;
}
@@ -370,7 +375,7 @@ public class CompileEnvironment {
ensureRuntime();
- if (!session.analyze(myErrorStream) && !ignoreErrors) {
+ if (!session.analyze(myErrorStream, myMessageRenderer) && !ignoreErrors) {
return false;
}
diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/CompileSession.java b/compiler/cli/src/org/jetbrains/jet/compiler/CompileSession.java
index 641376b40b4d6a24ff78bbbe1232f58d9e29f526..7a63ab18823b7b5893f784a35344a2169fda489e 100644
--- a/compiler/cli/src/org/jetbrains/jet/compiler/CompileSession.java
+++ b/compiler/cli/src/org/jetbrains/jet/compiler/CompileSession.java
@@ -34,6 +34,7 @@ import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.ModuleDescriptor;
import org.jetbrains.jet.lang.diagnostics.Diagnostic;
import org.jetbrains.jet.lang.diagnostics.DiagnosticFactory;
+import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils;
import org.jetbrains.jet.lang.diagnostics.Severity;
import org.jetbrains.jet.lang.psi.JetFile;
import org.jetbrains.jet.lang.resolve.BindingContext;
@@ -41,8 +42,7 @@ import org.jetbrains.jet.lang.resolve.java.AnalyzerFacade;
import org.jetbrains.jet.lang.resolve.java.JavaDescriptorResolver;
import org.jetbrains.jet.plugin.JetFileType;
-import java.io.File;
-import java.io.PrintStream;
+import java.io.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
@@ -84,11 +84,11 @@ public class CompileSession {
VirtualFile vFile = myEnvironment.getLocalFileSystem().findFileByPath(path);
if (vFile == null) {
- myErrors.add("ERROR: File/directory not found: " + path);
+ myErrors.add("File/directory not found: " + path);
return;
}
if (!vFile.isDirectory() && vFile.getFileType() != JetFileType.INSTANCE) {
- myErrors.add("ERROR: Not a Kotlin file: " + path);
+ myErrors.add("Not a Kotlin file: " + path);
return;
}
@@ -135,40 +135,26 @@ public class CompileSession {
return mySourceFiles;
}
- public boolean analyze(final PrintStream out) {
- if (!myErrors.isEmpty()) {
- for (String error : myErrors) {
- out.println(error);
- }
- return false;
- }
-
- final ErrorCollector errorCollector = new ErrorCollector();
+ public boolean analyze(@NotNull PrintStream out, @NotNull MessageRenderer renderer) {
+ MessageCollector collector = new MessageCollector(renderer);
- reportSyntaxErrors(errorCollector);
- analyzeAndReportSemanticErrors(errorCollector);
+ for (String error : myErrors) {
+ collector.report(Severity.ERROR, error, null, -1, -1);
+ }
+ reportSyntaxErrors(collector);
+ analyzeAndReportSemanticErrors(collector);
- boolean hasIncompleteHierarchyErrors;
- Collection incompletes = myBindingContext.getKeys(BindingContext.INCOMPLETE_HIERARCHY);
- if (!incompletes.isEmpty()) {
- out.println(Severity.ERROR + ":: The following classes have incomplete hierarchies:");
- for (ClassDescriptor incomplete : incompletes) {
- out.println(Severity.ERROR + ":: " + fqName(incomplete));
- }
- hasIncompleteHierarchyErrors = true;
- } else {
- hasIncompleteHierarchyErrors = false;
- }
+ collector.printTo(out);
- errorCollector.flushTo(out);
- return !errorCollector.hasErrors() && !hasIncompleteHierarchyErrors;
+ return !collector.hasErrors();
}
/**
* @see JetTypeMapper#getFQName(DeclarationDescriptor)
+ * TODO possibly duplicates DescriptorUtils#getFQName(DeclarationDescriptor)
*/
- private String fqName(ClassOrNamespaceDescriptor descriptor) {
+ private static String fqName(ClassOrNamespaceDescriptor descriptor) {
DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
if (containingDeclaration == null || containingDeclaration instanceof ModuleDescriptor || containingDeclaration.getName().equals(JavaDescriptorResolver.JAVA_ROOT)) {
return descriptor.getName();
@@ -177,18 +163,31 @@ public class CompileSession {
}
}
- private void analyzeAndReportSemanticErrors(ErrorCollector errorCollector) {
+ private void analyzeAndReportSemanticErrors(MessageCollector collector) {
Predicate filesToAnalyzeCompletely =
stubs ? Predicates.alwaysFalse() : Predicates.alwaysTrue();
myBindingContext = AnalyzerFacade.analyzeFilesWithJavaIntegration(
myEnvironment.getProject(), mySourceFiles, filesToAnalyzeCompletely, JetControlFlowDataTraceFactory.EMPTY);
for (Diagnostic diagnostic : myBindingContext.getDiagnostics()) {
- errorCollector.report(diagnostic);
+ reportDiagnostic(collector, diagnostic);
+ }
+
+ reportIncompleteHierarchies(collector);
+ }
+
+ private void reportIncompleteHierarchies(MessageCollector collector) {
+ Collection incompletes = myBindingContext.getKeys(BindingContext.INCOMPLETE_HIERARCHY);
+ if (!incompletes.isEmpty()) {
+ StringBuilder message = new StringBuilder("The following classes have incomplete hierarchies:\n");
+ for (ClassDescriptor incomplete : incompletes) {
+ message.append(" ").append(fqName(incomplete)).append("\n");
+ }
+ collector.report(Severity.ERROR, message.toString(), null, -1, -1);
}
}
- private void reportSyntaxErrors(final ErrorCollector errorCollector) {
+ private void reportSyntaxErrors(final MessageCollector messageCollector) {
for (JetFile file : mySourceFiles) {
file.accept(new PsiRecursiveElementWalkingVisitor() {
@Override
@@ -196,12 +195,19 @@ public class CompileSession {
String description = element.getErrorDescription();
String message = StringUtil.isEmpty(description) ? "Syntax error" : description;
Diagnostic diagnostic = DiagnosticFactory.create(Severity.ERROR, message).on(element);
- errorCollector.report(diagnostic);
+ reportDiagnostic(messageCollector, diagnostic);
}
});
}
}
+ private static void reportDiagnostic(MessageCollector collector, Diagnostic diagnostic) {
+ DiagnosticUtils.LineAndColumn lineAndColumn = DiagnosticUtils.getLineAndColumn(diagnostic);
+ VirtualFile virtualFile = diagnostic.getPsiFile().getVirtualFile();
+ String path = virtualFile == null ? null : virtualFile.getPath();
+ collector.report(diagnostic.getSeverity(), diagnostic.getMessage(), path, lineAndColumn.getLine(), lineAndColumn.getColumn());
+ }
+
@NotNull
public ClassFileFactory generate() {
Project project = myEnvironment.getProject();
diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/ErrorCollector.java b/compiler/cli/src/org/jetbrains/jet/compiler/MessageCollector.java
similarity index 51%
rename from compiler/cli/src/org/jetbrains/jet/compiler/ErrorCollector.java
rename to compiler/cli/src/org/jetbrains/jet/compiler/MessageCollector.java
index 8bf506a613b4d6af23111333de41372d923db8e6..397716a7c598c8e88fe22f9c8ec4a50bd8c84d65 100644
--- a/compiler/cli/src/org/jetbrains/jet/compiler/ErrorCollector.java
+++ b/compiler/cli/src/org/jetbrains/jet/compiler/MessageCollector.java
@@ -18,9 +18,8 @@ package org.jetbrains.jet.compiler;
import com.google.common.collect.LinkedHashMultimap;
import com.google.common.collect.Multimap;
-import com.intellij.psi.PsiFile;
-import org.jetbrains.jet.lang.diagnostics.Diagnostic;
-import org.jetbrains.jet.lang.diagnostics.DiagnosticUtils;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.diagnostics.Severity;
import java.io.PrintStream;
@@ -29,27 +28,28 @@ import java.util.Collection;
/**
* @author alex.tkachman
*/
-class ErrorCollector {
- private final Multimap maps = LinkedHashMultimap.create();
+/*package*/ class MessageCollector {
+ // File path (nullable) -> error message
+ private final Multimap groupedMessages = LinkedHashMultimap.create();
+ private final MessageRenderer renderer;
private boolean hasErrors;
- public ErrorCollector() {
+ public MessageCollector(@NotNull MessageRenderer renderer) {
+ this.renderer = renderer;
}
- public void report(Diagnostic diagnostic) {
- hasErrors |= diagnostic.getSeverity() == Severity.ERROR;
- maps.put(diagnostic.getPsiFile(), diagnostic);
+ public void report(@NotNull Severity severity, @NotNull String message, @Nullable String path, int line, int column) {
+ hasErrors |= severity == Severity.ERROR;
+ groupedMessages.put(path, renderer.render(severity, message, path, line, column));
}
- public void flushTo(final PrintStream out) {
- if(!maps.isEmpty()) {
- for (PsiFile psiFile : maps.keySet()) {
- String path = psiFile.getVirtualFile().getPath();
- Collection diagnostics = maps.get(psiFile);
- for (Diagnostic diagnostic : diagnostics) {
- String position = DiagnosticUtils.formatPosition(diagnostic);
- out.println(diagnostic.getSeverity().toString() + ": " + path + ":" + position + " " + diagnostic.getMessage());
+ public void printTo(@NotNull PrintStream out) {
+ if (!groupedMessages.isEmpty()) {
+ for (String path : groupedMessages.keySet()) {
+ Collection diagnostics = groupedMessages.get(path);
+ for (String diagnostic : diagnostics) {
+ out.println(diagnostic);
}
}
}
diff --git a/compiler/cli/src/org/jetbrains/jet/compiler/MessageRenderer.java b/compiler/cli/src/org/jetbrains/jet/compiler/MessageRenderer.java
new file mode 100644
index 0000000000000000000000000000000000000000..4573f3d77eb2f05d1e0ffab68eacf44c9dabfeb5
--- /dev/null
+++ b/compiler/cli/src/org/jetbrains/jet/compiler/MessageRenderer.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2000-2012 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.jet.compiler;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jet.lang.diagnostics.Severity;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * @author abreslav
+ */
+public interface MessageRenderer {
+
+ MessageRenderer TAGS = new MessageRenderer() {
+ private String renderWithStringSeverity(String severityString, String message, String path, int line, int column) {
+ StringBuilder out = new StringBuilder();
+ out.append("<").append(severityString);
+ if (path != null) {
+ out.append(" path=\"").append(path).append("\"");
+ out.append(" line=\"").append(line).append("\"");
+ out.append(" column=\"").append(column).append("\"");
+ }
+ out.append(">\n");
+
+ out.append(message);
+
+ out.append("").append(severityString).append(">\n");
+ return out.toString();
+ }
+
+ @Override
+ public String render(@NotNull Severity severity, @NotNull String message, @Nullable String path, int line, int column) {
+ return renderWithStringSeverity(severity.toString(), message, path, line, column);
+ }
+
+ @Override
+ public String renderException(@NotNull Throwable e) {
+ return renderWithStringSeverity("EXCEPTION", PLAIN.renderException(e), null, -1, -1);
+ }
+ };
+
+ MessageRenderer PLAIN = new MessageRenderer() {
+ @Override
+ public String render(@NotNull Severity severity, @NotNull String message, @Nullable String path, int line, int column) {
+ String position = path == null ? "" : path + ": (" + (line + ", " + column) + ") ";
+ return severity + ": " + position + message;
+ }
+
+ @Override
+ public String renderException(@NotNull Throwable e) {
+ StringWriter out = new StringWriter();
+ e.printStackTrace(new PrintWriter(out));
+ return out.toString();
+ }
+ };
+
+ String render(@NotNull Severity severity, @NotNull String message, @Nullable String path, int line, int column);
+ String renderException(@NotNull Throwable e);
+}
diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java
index a06b0c9a3a05431819a41dbbc0ec1a0aecb11e66..1a1a6b655ad61cab61738e0dde77ebc7e0cf0f7c 100644
--- a/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java
+++ b/compiler/frontend/src/org/jetbrains/jet/lang/diagnostics/DiagnosticUtils.java
@@ -43,7 +43,7 @@ public class DiagnosticUtils {
if (element != null) {
return atLocation(element.getContainingFile(), element.getTextRange());
}
- return "' at offset " + startOffset + " (line and file unknown)";
+ return "' at offset " + startOffset + " (line and file unknown: no PSI element)";
}
@Nullable
@@ -63,7 +63,7 @@ public class DiagnosticUtils {
@NotNull
public static String atLocation(@NotNull PsiFile file, @NotNull TextRange textRange) {
- Document document = file.getViewProvider().getDocument();//PsiDocumentManager.getInstance(file.getProject()).getDocument(file);
+ Document document = file.getViewProvider().getDocument();
return atLocation(file, textRange, document);
}
@@ -72,31 +72,28 @@ public class DiagnosticUtils {
int offset = textRange.getStartOffset();
VirtualFile virtualFile = file.getVirtualFile();
String pathSuffix = virtualFile == null ? "" : " in " + virtualFile.getPath();
- if (document != null) {
- int lineNumber = document.getLineNumber(offset);
- int lineStartOffset = document.getLineStartOffset(lineNumber);
- int column = offset - lineStartOffset;
-
- return "' at line " + (lineNumber + 1) + ":" + (column + 1) + pathSuffix;
- }
- else {
- return "' at offset " + offset + " (line unknown)" + pathSuffix;
- }
+ return offsetToLineAndColumn(document, offset).toString() + pathSuffix;
}
- public static String formatPosition(Diagnostic diagnostic) {
+ @NotNull
+ public static LineAndColumn getLineAndColumn(@NotNull Diagnostic extends PsiElement> diagnostic) {
PsiFile file = diagnostic.getPsiFile();
Document document = file.getViewProvider().getDocument();
TextRange firstRange = diagnostic.getTextRanges().iterator().next();
- int offset = firstRange.getStartOffset();
- if (document != null) {
- int lineNumber = document.getLineNumber(offset);
- int lineStartOffset = document.getLineStartOffset(lineNumber);
- int column = offset - lineStartOffset;
+ return offsetToLineAndColumn(document, firstRange.getStartOffset());
+ }
- return "(" + (lineNumber + 1) + "," + (column + 1) + ")";
+ @NotNull
+ public static LineAndColumn offsetToLineAndColumn(Document document, int offset) {
+ if (document == null) {
+ return new LineAndColumn(-1, offset);
}
- return "(offset: " + offset + " line unknown)";
+
+ int lineNumber = document.getLineNumber(offset);
+ int lineStartOffset = document.getLineStartOffset(lineNumber);
+ int column = offset - lineStartOffset;
+
+ return new LineAndColumn(lineNumber + 1, column + 1);
}
public static void throwIfRunningOnServer(Throwable e) {
@@ -111,4 +108,31 @@ public class DiagnosticUtils {
throw new RuntimeException(e);
}
}
+
+ public static final class LineAndColumn {
+ private final int line;
+ private final int column;
+
+ public LineAndColumn(int line, int column) {
+ this.line = line;
+ this.column = column;
+ }
+
+ public int getLine() {
+ return line;
+ }
+
+ public int getColumn() {
+ return column;
+ }
+
+ // NOTE: This method is used for presenting positions to the user
+ @Override
+ public String toString() {
+ if (line < 0) {
+ return "(offset: " + column + " line unknown)";
+ }
+ return "(" + line + "," + column + ")";
+ }
+ }
}
diff --git a/compiler/tests/compiler-tests.iml b/compiler/tests/compiler-tests.iml
index e3a58fc3f6ec9c475f002f319f292204fa46888a..eee45ec38be9128c473ed1d41505b0cb7b0dc769 100644
--- a/compiler/tests/compiler-tests.iml
+++ b/compiler/tests/compiler-tests.iml
@@ -13,6 +13,7 @@
+
diff --git a/compiler/tests/org/jetbrains/jet/codegen/ForTestCompileStdlib.java b/compiler/tests/org/jetbrains/jet/codegen/ForTestCompileStdlib.java
index 422c9720536d30e4d6eeb7557626af93e1964a55..12c982495dfd090af361c2a7702f1b6078fcf08d 100644
--- a/compiler/tests/org/jetbrains/jet/codegen/ForTestCompileStdlib.java
+++ b/compiler/tests/org/jetbrains/jet/codegen/ForTestCompileStdlib.java
@@ -99,7 +99,7 @@ public class ForTestCompileStdlib {
private static void compileKotlinPartOfStdlib(File destdir) throws IOException {
// lame
- KotlinCompiler.exec("-output", destdir.getPath(), "-src", "./stdlib/ktSrc", "-stdlib", destdir.getAbsolutePath());
+ KotlinCompiler.exec(System.err, "-output", destdir.getPath(), "-src", "./stdlib/ktSrc", "-stdlib", destdir.getAbsolutePath());
}
private static List javaFilesInDir(File dir) {
diff --git a/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java b/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java
index c64bc07a62b1b5315d02e7a67f6fbd0180f522c8..2336c3e013db97bf5aae2c5f2b137deda97821b6 100644
--- a/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java
+++ b/compiler/tests/org/jetbrains/jet/codegen/TestlibTest.java
@@ -22,6 +22,7 @@ import junit.framework.Test;
import junit.framework.TestCase;
import junit.framework.TestSuite;
import org.jetbrains.jet.compiler.CompileSession;
+import org.jetbrains.jet.compiler.MessageRenderer;
import org.jetbrains.jet.lang.descriptors.ClassDescriptor;
import org.jetbrains.jet.lang.psi.JetClass;
import org.jetbrains.jet.lang.psi.JetDeclaration;
@@ -84,7 +85,7 @@ public class TestlibTest extends CodegenTestCase {
session.addSources(localFileSystem.findFileByPath(JetParsingTest.getTestDataDir() + "/../../testlib/test"));
session.addSources(localFileSystem.findFileByPath(JetParsingTest.getTestDataDir() + "/../../kunit/src"));
- if (!session.analyze(System.err)) {
+ if (!session.analyze(System.err, MessageRenderer.PLAIN)) {
throw new RuntimeException("There were compilation errors");
}
diff --git a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java
index bd42098adecae5073a8ec2e1032fbeec98e6ac3e..04c0a6bebe7b4e66373114bcfbd78b7fe8808df6 100644
--- a/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java
+++ b/idea/src/org/jetbrains/jet/plugin/compiler/JetCompiler.java
@@ -37,6 +37,8 @@ import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.util.Chunk;
import com.intellij.util.SystemProperties;
import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jetbrains.jet.lang.diagnostics.Severity;
import org.jetbrains.jet.plugin.JetFileType;
import java.io.*;
@@ -199,10 +201,10 @@ public class JetCompiler implements TranslatingCompiler {
try {
String line = reader.readLine();
if (line == null) break;
- listener.onTextAvailable(new ProcessEvent(NullProcessHandler.INSTANCE, line), ProcessOutputTypes.STDOUT);
+ listener.onTextAvailable(new ProcessEvent(NullProcessHandler.INSTANCE, line + "\n"), ProcessOutputTypes.STDERR);
} catch (IOException e) {
// Can't be
- throw new RuntimeException(e);
+ throw new IllegalStateException(e);
}
}
@@ -218,7 +220,7 @@ public class JetCompiler implements TranslatingCompiler {
Class> kompiler = Class.forName(compilerClassName, true, loader);
Method exec = kompiler.getDeclaredMethod("exec", PrintStream.class, String[].class);
- String[] arguments = { "-module", scriptFile.getAbsolutePath(), "-output", path(outputDir) };
+ String[] arguments = { "-module", scriptFile.getAbsolutePath(), "-output", path(outputDir), "-tags" };
context.addMessage(INFORMATION, "Using kotlinHome=" + kotlinHome, "", -1, -1);
context.addMessage(INFORMATION, "Invoking in-process compiler " + compilerClassName + " with arguments " + Arrays.asList(arguments), "", -1, -1);
@@ -228,7 +230,7 @@ public class JetCompiler implements TranslatingCompiler {
return ((Integer) rc).intValue();
}
else {
- throw new RuntimeException("Unexpected return: " + rc);
+ throw new IllegalStateException("Unexpected return: " + rc);
}
} catch (Throwable e) {
LOG.error(e);
@@ -267,6 +269,7 @@ public class JetCompiler implements TranslatingCompiler {
params.setMainClass("org.jetbrains.jet.cli.KotlinCompiler");
params.getProgramParametersList().add("-module", scriptFile.getAbsolutePath());
params.getProgramParametersList().add("-output", path(outputDir));
+ params.getProgramParametersList().add("-tags");
for (File jar : kompilerClasspath(kotlinHome, compileContext)) {
params.getClassPath().add(jar);
@@ -302,77 +305,7 @@ public class JetCompiler implements TranslatingCompiler {
}
private static ProcessAdapter createProcessListener(final CompileContext compileContext) {
- return new ProcessAdapter() {
- StringBuilder stderr = null;
-
- @Override
- public void onTextAvailable(ProcessEvent event, Key outputType) {
- String text = event.getText();
- String levelCode = parsePrefix(text);
- if (outputType == ProcessOutputTypes.STDERR) {
- if (stderr == null) {
- stderr = new StringBuilder();
- }
- stderr.append(text);
- return;
- }
- if (levelCode != null) {
- CompilerMessageCategory category = categories.get(levelCode);
- text = text.substring(levelCode.length());
-
- String path = "";
- int line = -1;
- int column = -1;
- int colonIndex = text.indexOf(':');
- if (text.startsWith(":")) {
- text = text.substring(1);
- } else if (colonIndex > 0) {
- path = "file://" + text.substring(0, colonIndex).trim();
- text = text.substring(colonIndex + 1);
-
- Pattern position = Pattern.compile("\\((\\d+),\\s*(\\d+)\\)");
-
- Matcher matcher = position.matcher(text);
- if (matcher.find()) {
- line = Integer.parseInt(matcher.group(1));
- column = Integer.parseInt(matcher.group(2));
- text = text.substring(matcher.group(0).length());
- }
- }
-
- compileContext.addMessage(category, text, path, line, column);
- }
- else {
- compileContext.addMessage(INFORMATION, text, "", -1, -1);
- }
- }
-
- @Override
- public void processTerminated(ProcessEvent event) {
- if (event.getExitCode() != 0) {
- compileContext.addMessage(ERROR, "Compiler terminated with exit code: " + event.getExitCode(), "", -1, -1);
- }
- // By alex.tkachman:
- if (stderr != null) {
- compileContext.addMessage(ERROR, "stderr output:\r\n" + stderr.toString(), "", -1, -1);
- }
- }
- };
- }
-
- private static String[] messagePrefixes = new String[] {"ERROR:", "WARNING:", "INFO:"} ;
- private static Map categories = new HashMap();
- static {
- categories.put("ERROR:", ERROR);
- categories.put("WARNING:", WARNING);
- categories.put("INFORMATION:", INFORMATION);
- }
-
- private static String parsePrefix(String message) {
- for (String prefix : messagePrefixes) {
- if (message.startsWith(prefix)) return prefix;
- }
- return null;
+ return new CompilerProcessListener(compileContext);
}
private static String path(VirtualFile root) {
@@ -406,4 +339,167 @@ public class JetCompiler implements TranslatingCompiler {
throw new UnsupportedOperationException("getProcessInput is not implemented");
}
}
+
+ private static class CompilerProcessListener extends ProcessAdapter {
+ private static final Pattern DIAGNOSTIC_PATTERN = Pattern.compile("<(ERROR|WARNING|INFO|EXCEPTION)", Pattern.MULTILINE);
+ private static final Pattern OPEN_TAG_END_PATTERN = Pattern.compile(">", Pattern.MULTILINE | Pattern.DOTALL);
+ private static final Pattern ATTRIBUTE_PATTERN = Pattern.compile("\\s*(path|line|column)\\s*=\\s*\"(.*?)\"", Pattern.MULTILINE | Pattern.DOTALL);
+ private static final Pattern MESSAGE_PATTERN = Pattern.compile("(.*?)(ERROR|WARNING|INFO|EXCEPTION)>", Pattern.MULTILINE | Pattern.DOTALL);
+
+ private enum State {
+ WAITING, ATTRIBUTES, MESSAGE
+ }
+
+ private static class CompilerMessage {
+ private CompilerMessageCategory messageCategory;
+ private boolean isException;
+ private @Nullable String url;
+ private @Nullable Integer line;
+ private @Nullable Integer column;
+ private String message;
+
+ public void setMessageCategoryFromString(String tagName) {
+ boolean exception = "EXCEPTION".equals(tagName);
+ if (Severity.ERROR.toString().equals(tagName) || exception) {
+ messageCategory = ERROR;
+ isException = exception;
+ }
+ else if (Severity.WARNING.toString().equals(tagName)) {
+ messageCategory = WARNING;
+ }
+ else {
+ messageCategory = INFORMATION;
+ }
+ }
+
+ public void setAttributeFromStrings(String name, String value) {
+ if ("path".equals(name)) {
+ url = "file://" + value.trim();
+ }
+ else if ("line".equals(name)) {
+ line = safeParseInt(value);
+ }
+ else if ("column".equals(name)) {
+ column = safeParseInt(value);
+ }
+ }
+
+ @Nullable
+ private static Integer safeParseInt(String value) {
+ try {
+ return Integer.parseInt(value.trim());
+ }
+ catch (NumberFormatException e) {
+ return null;
+ }
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public void reportTo(CompileContext compileContext) {
+ compileContext.addMessage(messageCategory, message, url == null ? "" : url, line == null ? -1 : line, column == null ? -1 : column);
+ if (isException) {
+ LOG.error(message);
+ }
+ }
+ }
+
+ private final CompileContext compileContext;
+ private final StringBuilder output = new StringBuilder();
+ private int firstUnprocessedIndex = 0;
+ private State state = State.WAITING;
+ private CompilerMessage currentCompilerMessage;
+
+ public CompilerProcessListener(CompileContext compileContext) {
+ this.compileContext = compileContext;
+ }
+
+ @Override
+ public void onTextAvailable(ProcessEvent event, Key outputType) {
+ String text = event.getText();
+ if (outputType == ProcessOutputTypes.STDERR) {
+ output.append(text);
+
+ // We loop until the state stabilizes
+ State lastState;
+ do {
+ lastState = state;
+ switch (state) {
+ case WAITING: {
+ Matcher matcher = matcher(DIAGNOSTIC_PATTERN);
+ if (find(matcher)) {
+ currentCompilerMessage = new CompilerMessage();
+ currentCompilerMessage.setMessageCategoryFromString(matcher.group(1));
+ state = State.ATTRIBUTES;
+ }
+ break;
+ }
+ case ATTRIBUTES: {
+ Matcher matcher = matcher(ATTRIBUTE_PATTERN);
+ int indexDelta = 0;
+ while (matcher.find()) {
+ handleSkippedOutput(output.subSequence(firstUnprocessedIndex + indexDelta, firstUnprocessedIndex + matcher.start()));
+ currentCompilerMessage.setAttributeFromStrings(matcher.group(1), matcher.group(2));
+ indexDelta = matcher.end();
+
+ }
+ firstUnprocessedIndex += indexDelta;
+
+ Matcher endMatcher = matcher(OPEN_TAG_END_PATTERN);
+ if (find(endMatcher)) {
+ state = State.MESSAGE;
+ }
+ break;
+ }
+ case MESSAGE: {
+ Matcher matcher = matcher(MESSAGE_PATTERN);
+ if (find(matcher)) {
+ currentCompilerMessage.setMessage(matcher.group(1));
+ currentCompilerMessage.reportTo(compileContext);
+ state = State.WAITING;
+ }
+ break;
+ }
+ }
+ }
+ while (state != lastState);
+
+ }
+ else {
+ compileContext.addMessage(INFORMATION, text, "", -1, -1);
+ }
+ }
+
+ private boolean find(Matcher matcher) {
+ boolean result = matcher.find();
+ if (result) {
+ handleSkippedOutput(output.subSequence(firstUnprocessedIndex, firstUnprocessedIndex + matcher.start()));
+ firstUnprocessedIndex += matcher.end();
+ }
+ return result;
+ }
+
+ private Matcher matcher(Pattern pattern) {
+ return pattern.matcher(output.subSequence(firstUnprocessedIndex, output.length()));
+ }
+
+ @Override
+ public void processTerminated(ProcessEvent event) {
+ if (firstUnprocessedIndex < output.length()) {
+ handleSkippedOutput(output.substring(firstUnprocessedIndex).trim());
+ }
+ if (event.getExitCode() != 0) {
+ compileContext.addMessage(ERROR, "Compiler terminated with exit code: " + event.getExitCode(), "", -1, -1);
+ }
+ }
+
+ private void handleSkippedOutput(CharSequence substring) {
+ String message = substring.toString();
+ if (!message.trim().isEmpty()) {
+ compileContext.addMessage(ERROR, message, "", -1, -1);
+ }
+ }
+ }
}
diff --git a/idea/src/org/jetbrains/jet/plugin/liveTemplates/JetLiveTemplateCompletionContributor.java b/idea/src/org/jetbrains/jet/plugin/liveTemplates/JetLiveTemplateCompletionContributor.java
index 9b843f235f7b3eac184dec910132abff3c4a92c3..e7388dcc7d1543d43997f0553e2595557f29dd53 100644
--- a/idea/src/org/jetbrains/jet/plugin/liveTemplates/JetLiveTemplateCompletionContributor.java
+++ b/idea/src/org/jetbrains/jet/plugin/liveTemplates/JetLiveTemplateCompletionContributor.java
@@ -46,6 +46,9 @@ public class JetLiveTemplateCompletionContributor extends CompletionContributor
protected void addCompletions(@NotNull CompletionParameters parameters,
ProcessingContext context,
@NotNull final CompletionResultSet result) {
+ if (parameters.getInvocationCount() == 0) {
+ return;
+ }
final PsiFile file = parameters.getPosition().getContainingFile();
final int offset = parameters.getOffset();
final List templates = listApplicableTemplates(file, offset);
diff --git a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/JetSuggestVariableNameMacro.java b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/JetSuggestVariableNameMacro.java
index 2bb9ebdcd176825ba60d40bd51b2f90aa51a044c..1e0183f1776f8e72aa6bae00ca94fdf619307579 100644
--- a/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/JetSuggestVariableNameMacro.java
+++ b/idea/src/org/jetbrains/jet/plugin/liveTemplates/macro/JetSuggestVariableNameMacro.java
@@ -30,7 +30,7 @@ import org.jetbrains.jet.plugin.JetBundle;
public class JetSuggestVariableNameMacro extends Macro {
@Override
public String getName() {
- return "suggestVariableName";
+ return "kotlinSuggestVariableName";
}
@Override
diff --git a/lib/asm-all-3.3.1.jar b/lib/asm-all-3.3.1.jar
new file mode 100644
index 0000000000000000000000000000000000000000..df03b326614ffafd517a4ecba14054855f21a9b2
Binary files /dev/null and b/lib/asm-all-3.3.1.jar differ
diff --git a/lib/asm-util-3.3.1.jar b/lib/asm-util-3.3.1.jar
deleted file mode 100644
index 0230bbcfb71bf24e7ac9b49e32a84b7eceaeb5a9..0000000000000000000000000000000000000000
Binary files a/lib/asm-util-3.3.1.jar and /dev/null differ