Merge remote-tracking branch 'origin/master'
This commit is contained in:
Generated
+1
-1
@@ -1,7 +1,7 @@
|
||||
<component name="libraryTable">
|
||||
<library name="asm">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-util-3.3.1.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/asm-all-3.3.1.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Module> modules = loadModuleScript(moduleFile);
|
||||
public boolean compileModuleScript(String moduleFile, @Nullable String jarPath, @Nullable String outputDir, boolean jarRuntime) {
|
||||
List<Module> 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<Module> 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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
public boolean analyze(@NotNull PrintStream out, @NotNull MessageRenderer renderer) {
|
||||
MessageCollector collector = new MessageCollector(renderer);
|
||||
|
||||
for (String error : myErrors) {
|
||||
collector.report(Severity.ERROR, error, null, -1, -1);
|
||||
}
|
||||
|
||||
final ErrorCollector errorCollector = new ErrorCollector();
|
||||
reportSyntaxErrors(collector);
|
||||
analyzeAndReportSemanticErrors(collector);
|
||||
|
||||
reportSyntaxErrors(errorCollector);
|
||||
analyzeAndReportSemanticErrors(errorCollector);
|
||||
collector.printTo(out);
|
||||
|
||||
|
||||
boolean hasIncompleteHierarchyErrors;
|
||||
Collection<ClassDescriptor> 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;
|
||||
}
|
||||
|
||||
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<PsiFile> filesToAnalyzeCompletely =
|
||||
stubs ? Predicates.<PsiFile>alwaysFalse() : Predicates.<PsiFile>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<ClassDescriptor> 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();
|
||||
|
||||
+17
-17
@@ -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<PsiFile, Diagnostic> maps = LinkedHashMultimap.create();
|
||||
/*package*/ class MessageCollector {
|
||||
// File path (nullable) -> error message
|
||||
private final Multimap<String, String> 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<Diagnostic> 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<String> diagnostics = groupedMessages.get(path);
|
||||
for (String diagnostic : diagnostics) {
|
||||
out.println(diagnostic);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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<PsiElement> 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 + ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
<orderEntry type="module" module-name="stdlib" />
|
||||
<orderEntry type="module" module-name="cli" />
|
||||
<orderEntry type="library" name="idea-full" level="project" />
|
||||
<orderEntry type="library" name="asm" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
|
||||
@@ -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<File> javaFilesInDir(File dir) {
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, CompilerMessageCategory> categories = new HashMap<String, CompilerMessageCategory>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -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<TemplateImpl> templates = listApplicableTemplates(file, offset);
|
||||
|
||||
+1
-1
@@ -30,7 +30,7 @@ import org.jetbrains.jet.plugin.JetBundle;
|
||||
public class JetSuggestVariableNameMacro extends Macro {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "suggestVariableName";
|
||||
return "kotlinSuggestVariableName";
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user