[cli-repl][ide-console] Correct escaping
This commit is contained in:
committed by
Pavel V. Talanov
parent
e4ffe8b145
commit
e23441907c
@@ -25,7 +25,7 @@ import jline.console.history.FileHistory;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.cli.common.KotlinVersion;
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.FromIdeXmlMessagesParser;
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.IdeXmlMessagesParser;
|
||||
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplSystemOutWrapper;
|
||||
import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.utils.UtilsPackage;
|
||||
@@ -42,6 +42,7 @@ public class ReplFromTerminal {
|
||||
|
||||
private final boolean ideMode;
|
||||
private final ReplSystemOutWrapper replWriter;
|
||||
private final IdeXmlMessagesParser ideXmlParser = new IdeXmlMessagesParser();
|
||||
|
||||
private final ConsoleReader consoleReader;
|
||||
|
||||
@@ -186,7 +187,7 @@ public class ReplFromTerminal {
|
||||
private String readLine(@Nullable String prompt) throws IOException {
|
||||
String line = consoleReader.readLine(prompt);
|
||||
if (ideMode && line != null) {
|
||||
line = FromIdeXmlMessagesParser.parse(line);
|
||||
line = ideXmlParser.parse(line);
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.cli.jvm.repl.messages
|
||||
|
||||
public object EscapeConstants {
|
||||
public val XML_PREAMBLE: String = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
|
||||
// using '#' to avoid collisions with xml escaping
|
||||
public val SOURCE_CHARS: Array<String> = arrayOf("\n", "#")
|
||||
public val XML_REPLACEMENTS: Array<String> = arrayOf("#n", "#diez")
|
||||
}
|
||||
+3
-4
@@ -21,18 +21,17 @@ import org.w3c.dom.Element
|
||||
import org.xml.sax.InputSource
|
||||
import java.io.ByteArrayInputStream
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
import kotlin.platform.platformStatic
|
||||
|
||||
public object FromIdeXmlMessagesParser {
|
||||
public class IdeXmlMessagesParser {
|
||||
private fun strToSource(s: String) = InputSource(ByteArrayInputStream(s.toByteArray()))
|
||||
|
||||
platformStatic fun parse(inputMessage: String): String {
|
||||
fun parse(inputMessage: String): String {
|
||||
val docFactory = DocumentBuilderFactory.newInstance()
|
||||
val docBuilder = docFactory.newDocumentBuilder()
|
||||
val input = docBuilder.parse(strToSource(inputMessage))
|
||||
|
||||
val root = input.firstChild as Element
|
||||
val inputContent = StringUtil.unescapeStringCharacters(root.textContent).trim()
|
||||
val inputContent = StringUtil.replace(root.textContent, EscapeConstants.XML_REPLACEMENTS, EscapeConstants.SOURCE_CHARS).trim()
|
||||
|
||||
return inputContent
|
||||
}
|
||||
+1
-1
@@ -27,7 +27,7 @@ public class ReplIdeDiagnosticMessageHolder : DiagnosticMessageHolder {
|
||||
private val diagnostics = arrayListOf<Pair<Diagnostic, String>>()
|
||||
|
||||
override fun report(diagnostic: Diagnostic, file: PsiFile, render: String) {
|
||||
diagnostics.add(diagnostic to render)
|
||||
diagnostics.add(Pair(diagnostic, render))
|
||||
}
|
||||
|
||||
override val renderedDiagnostics: String
|
||||
|
||||
@@ -21,7 +21,6 @@ import com.intellij.util.LineSeparator
|
||||
import java.io.PrintStream
|
||||
|
||||
public class ReplSystemOutWrapper(private val standardOut: PrintStream, private val ideMode: Boolean) : PrintStream(standardOut, true) {
|
||||
private val XML_PREFIX = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
private val END_LINE = LineSeparator.getSystemLineSeparator().separatorString
|
||||
|
||||
private enum class EscapeType {
|
||||
@@ -51,8 +50,8 @@ public class ReplSystemOutWrapper(private val standardOut: PrintStream, private
|
||||
}
|
||||
|
||||
private fun xmlEscape(s: String, escapeType: EscapeType): String {
|
||||
val singleLine = StringUtil.escapeLineBreak(s)
|
||||
return "$XML_PREFIX<output type=\"$escapeType\">${StringUtil.escapeXml(singleLine)}</output>"
|
||||
val singleLine = StringUtil.replace(s, EscapeConstants.SOURCE_CHARS, EscapeConstants.XML_REPLACEMENTS)
|
||||
return "${EscapeConstants.XML_PREAMBLE}<output type=\"$escapeType\">${StringUtil.escapeXml(singleLine)}</output>"
|
||||
}
|
||||
|
||||
fun printlnResult(x: Any?) = printlnWithEscaping(x.toString(), EscapeType.REPL_RESULT)
|
||||
|
||||
@@ -26,7 +26,6 @@ public class KotlinConsoleExecutor(
|
||||
private val runner: KotlinConsoleRunner,
|
||||
private val historyManager: KotlinConsoleHistoryManager
|
||||
) {
|
||||
private val XML_PREFIX = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
private val historyHighlighter = KotlinHistoryHighlighter(runner)
|
||||
|
||||
fun executeCommand() = WriteCommandAction.runWriteCommandAction(runner.project) {
|
||||
@@ -50,7 +49,12 @@ public class KotlinConsoleExecutor(
|
||||
val processInputOS = processHandler.processInput ?: return logError(javaClass, "<p>Broken process stream</p>")
|
||||
val charset = (processHandler as? BaseOSProcessHandler)?.charset ?: Charsets.UTF_8
|
||||
|
||||
val xmlRes = "$XML_PREFIX<input>${StringUtil.escapeXml(StringUtil.escapeLineBreak(command.trim()))}</input>"
|
||||
val xmlRes = "${ReplConstants.XML_PREAMBLE}" +
|
||||
"<input>" +
|
||||
"${StringUtil.escapeXml(
|
||||
StringUtil.replace(command.trim(), ReplConstants.SOURCE_CHARS, ReplConstants.XML_REPLACEMENTS)
|
||||
)}" +
|
||||
"</input>"
|
||||
val bytes = ("$xmlRes\n").toByteArray(charset)
|
||||
processInputOS.write(bytes)
|
||||
processInputOS.flush()
|
||||
|
||||
@@ -79,7 +79,7 @@ public class KotlinConsoleHistoryManager(private val ktConsole: KotlinConsoleRun
|
||||
|
||||
historyPos = Math.max(historyPos - 1, 0)
|
||||
WriteCommandAction.runWriteCommandAction(ktConsole.project) {
|
||||
document.setText(history[historyPos])
|
||||
document.setText(history[historyPos].trim())
|
||||
caret.moveToOffset(0)
|
||||
prevCaretOffset = 0
|
||||
}
|
||||
@@ -92,7 +92,7 @@ public class KotlinConsoleHistoryManager(private val ktConsole: KotlinConsoleRun
|
||||
|
||||
historyPos = Math.min(historyPos + 1, history.size())
|
||||
WriteCommandAction.runWriteCommandAction(ktConsole.project) {
|
||||
document.setText(if (historyPos == history.size()) unfinishedCommand else history[historyPos])
|
||||
document.setText(if (historyPos == history.size()) unfinishedCommand else history[historyPos].trim())
|
||||
caret.moveToOffset(document.textLength)
|
||||
prevCaretOffset = document.textLength
|
||||
}
|
||||
|
||||
@@ -35,17 +35,17 @@ public class KotlinReplOutputHandler(
|
||||
process: Process,
|
||||
commandLine: String
|
||||
) : OSProcessHandler(process, commandLine) {
|
||||
private val XML_START = "<?xml"
|
||||
|
||||
private val dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
|
||||
override fun notifyTextAvailable(text: String, key: Key<*>?) {
|
||||
// skip "/usr/lib/jvm/java-8-oracle/bin/java -cp ..." intro
|
||||
if (!text.startsWith(XML_START)) return super.notifyTextAvailable(text, key)
|
||||
if (!text.startsWith(ReplConstants.XML_PREFIX)) return super.notifyTextAvailable(text, key)
|
||||
|
||||
val output = dBuilder.parse(strToSource(text))
|
||||
val root = output.firstChild as Element
|
||||
val outputType = root.getAttribute("type")
|
||||
val content = StringUtil.unescapeStringCharacters(root.textContent).trim()
|
||||
val content = StringUtil.replace(root.textContent, ReplConstants.XML_REPLACEMENTS, ReplConstants.SOURCE_CHARS).trim()
|
||||
|
||||
when (outputType) {
|
||||
"INITIAL_PROMPT" -> super.notifyTextAvailable("$content\n", key)
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* 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.console
|
||||
|
||||
public object ReplConstants {
|
||||
public val XML_PREFIX: String = "<?xml"
|
||||
public val XML_PREAMBLE: String = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
|
||||
public val SOURCE_CHARS: Array<String> = arrayOf("\n", "#")
|
||||
public val XML_REPLACEMENTS: Array<String> = arrayOf("#n", "#diez")
|
||||
}
|
||||
+17
-6
@@ -17,6 +17,7 @@
|
||||
package org.jetbrains.kotlin.console.highlight
|
||||
|
||||
import com.intellij.execution.impl.ConsoleViewUtil
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||
@@ -27,13 +28,10 @@ import org.jetbrains.kotlin.console.gutter.ReplIcons
|
||||
public class KotlinHistoryHighlighter(private val runner: KotlinConsoleRunner ) {
|
||||
fun addAndHighlightNewCommand(command: String) {
|
||||
val historyEditor = runner.consoleView.historyViewer
|
||||
|
||||
addLineBreakIfNeeded(historyEditor)
|
||||
|
||||
val historyDocument = historyEditor.document
|
||||
|
||||
if (historyDocument.textLength == 0) { // this will work first time after 'Clear all' action
|
||||
historyDocument.setText("\n")
|
||||
runner.addGutterIndicator(historyEditor, ReplIcons.HISTORY_INDICATOR)
|
||||
}
|
||||
|
||||
val oldHistoryLength = historyDocument.textLength
|
||||
historyDocument.insertString(oldHistoryLength, command)
|
||||
EditorUtil.scrollToTheEnd(historyEditor)
|
||||
@@ -61,4 +59,17 @@ public class KotlinHistoryHighlighter(private val runner: KotlinConsoleRunner )
|
||||
lexer.advance()
|
||||
}
|
||||
}
|
||||
|
||||
private fun addLineBreakIfNeeded(historyEditor: EditorEx) {
|
||||
val historyDocument = historyEditor.document
|
||||
val historyText = historyDocument.text
|
||||
|
||||
if (!historyText.endsWith('\n')) {
|
||||
val textLength = historyText.length()
|
||||
historyDocument.insertString(textLength, "\n")
|
||||
|
||||
if (textLength == 0) // this will work first time after 'Clear all' action
|
||||
runner.addGutterIndicator(historyEditor, ReplIcons.HISTORY_INDICATOR)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user