From 180272e4cbcf936d709295ae36898157e36aa392 Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Thu, 19 Feb 2015 12:36:34 +0300 Subject: [PATCH] JS: underline js() errors for non-string-literal constants --- .../idea/highlighter/IdeErrorMessages.java | 5 +- .../diagnostics/DefaultErrorMessagesJs.kt | 6 +- .../js/resolve/diagnostics/jsRenderers.kt | 125 ++++++++++++++++++ 3 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/jsRenderers.kt diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java index 34e18d31490..cdf96774d3f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/highlighter/IdeErrorMessages.java @@ -22,6 +22,8 @@ import org.jetbrains.kotlin.diagnostics.Diagnostic; import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages; import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap; import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticRenderer; +import org.jetbrains.kotlin.js.resolve.diagnostics.ErrorsJs; +import org.jetbrains.kotlin.js.resolve.diagnostics.JsCallDataHtmlRenderer; import org.jetbrains.kotlin.renderer.DescriptorRenderer; import static org.jetbrains.kotlin.diagnostics.Errors.*; @@ -125,7 +127,8 @@ public class IdeErrorMessages { MAP.put(CONFLICTING_JVM_DECLARATIONS, "Platform declaration clash: {0}", HTML_CONFLICTING_JVM_DECLARATIONS_DATA); MAP.put(ACCIDENTAL_OVERRIDE, "Accidental override: {0}", HTML_CONFLICTING_JVM_DECLARATIONS_DATA); - + MAP.put(ErrorsJs.JSCODE_ERROR, "JavaScript: {0}", JsCallDataHtmlRenderer.INSTANCE$); + MAP.put(ErrorsJs.JSCODE_WARNING, "JavaScript: {0}", JsCallDataHtmlRenderer.INSTANCE$); MAP.setImmutable(); } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt index 3ed9b748307..154cdb319e4 100644 --- a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/DefaultErrorMessagesJs.kt @@ -31,9 +31,9 @@ private val DIAGNOSTIC_FACTORY_TO_RENDERER by Delegates.lazy { put(ErrorsJs.NATIVE_GETTER_RETURN_TYPE_SHOULD_BE_NULLABLE, "Native getter''s return type should be nullable") put(ErrorsJs.NATIVE_SETTER_WRONG_RETURN_TYPE, "Native setter''s return type should be ''Unit'' or a supertype of the second parameter''s type") put(ErrorsJs.NATIVE_INDEXER_WRONG_PARAMETER_COUNT, "Expected {0} parameters for native {1}", Renderers.TO_STRING, Renderers.STRING) - put(ErrorsJs.JSCODE_ERROR, "JavaScript: {0}", Renderers.TO_STRING, Renderers.TO_STRING) - put(ErrorsJs.JSCODE_WARNING, "JavaScript: {0}", Renderers.TO_STRING, Renderers.TO_STRING) - put(ErrorsJs.JSCODE_ARGUMENT_SHOULD_BE_LITERAL, "Argument must be string literal") + put(ErrorsJs.JSCODE_ERROR, "JavaScript: {0}", JsCallDataTextRenderer) + put(ErrorsJs.JSCODE_WARNING, "JavaScript: {0}", JsCallDataTextRenderer) + put(ErrorsJs.JSCODE_ARGUMENT_SHOULD_BE_CONSTANT, "Argument must be string constant") this } diff --git a/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/jsRenderers.kt b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/jsRenderers.kt new file mode 100644 index 00000000000..c07e265ec5d --- /dev/null +++ b/js/js.frontend/src/org/jetbrains/kotlin/js/resolve/diagnostics/jsRenderers.kt @@ -0,0 +1,125 @@ +/* + * Copyright 2010-2015 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.js.resolve.diagnostics + +import org.jetbrains.kotlin.renderer.Renderer +import org.jetbrains.kotlin.resolve.diagnostics.JsCallData +import org.jetbrains.kotlin.resolve.diagnostics.JsCallDataWithCode +import com.google.gwt.dev.js.rhino.Utils.isEndOfLine + +abstract class JsCallDataRenderer : Renderer { + protected abstract fun format(data: JsCallDataWithCode): String + + override fun render(data: JsCallData?): String = + when (data) { + is JsCallDataWithCode -> format(data) + is JsCallData -> data.message + else -> throw AssertionError("Cannot render null data") + } +} + +object JsCallDataTextRenderer : JsCallDataRenderer() { + override fun format(data: JsCallDataWithCode): String { + val codeRange = data.codeRange + val code = data.code.underlineAsText(codeRange.getStartOffset(), codeRange.getEndOffset()) + return "${data.message} in code:\n${code}" + } +} + +public object JsCallDataHtmlRenderer : JsCallDataRenderer() { + override fun format(data: JsCallDataWithCode): String { + val codeRange = data.codeRange + val code = data.code.underlineAsHtml(codeRange.getStartOffset(), codeRange.getEndOffset()) + return "${data.message} in code:
${code}
" + } +} + +/** + * Underlines string in given rage. + * + * For example: + * var = 10; + * ^^^^ + */ +public fun String.underlineAsText(from: Int, to: Int): String { + val lines = StringBuilder() + var marks = StringBuilder() + var lineWasMarked = false + + for (i in indices) { + val c = charAt(i) + val mark: Char + + mark = when (i) { + in from..to -> '^' + else -> ' ' + } + + lines.append(c) + marks.append(mark) + lineWasMarked = lineWasMarked || mark != ' ' + + if (isEndOfLine(c.toInt())) { + if (lineWasMarked) { + lines.appendln(marks.toString().trimTrailing()) + lineWasMarked = false + } + + marks = StringBuilder() + } + } + + if (lineWasMarked) { + lines.appendln() + lines.append(marks.toString()) + } + + return lines.toString() +} + +public fun String.underlineAsHtml(from: Int, to: Int): String { + val lines = StringBuilder() + var openMarker = false + val underlineStart = "" + val underlineEnd = "" + + for (i in indices) { + val c = charAt(i) + + val mark = when (i) { + from -> { + openMarker = true + underlineStart + } + to -> { + openMarker = false + underlineEnd + } + else -> "" + } + + lines.append(mark) + + if (isEndOfLine(c.toInt()) && openMarker) { + lines.append(underlineEnd + c + underlineStart) + } else { + lines.append(c) + } + } + + return lines.toString() +} \ No newline at end of file