-
-
Save Darkyenus/693dc72993b5b8dbd80fe37194686291 to your computer and use it in GitHub Desktop.
OSProcessHandler that does not mangle results
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.darkyen.wemi.intellij.util | |
import com.intellij.execution.process.OSProcessHandler | |
import com.intellij.util.io.BaseDataReader | |
import com.intellij.util.io.BaseInputStreamReader | |
import com.intellij.util.io.BaseOutputReader | |
import java.io.IOException | |
import java.io.InputStream | |
import java.io.Reader | |
import java.nio.CharBuffer | |
class OSProcessHandlerWithCRLF(process: Process, commandLine: String) : OSProcessHandler(process, commandLine, Charsets.UTF_8) { | |
private object SaneOptions: BaseOutputReader.Options() { | |
override fun splitToLines(): Boolean = false | |
override fun sendIncompleteLines(): Boolean = true | |
override fun policy(): BaseDataReader.SleepingPolicy = BaseDataReader.SleepingPolicy.BLOCKING | |
override fun withSeparators(): Boolean = true | |
} | |
override fun readerOptions(): BaseOutputReader.Options = SaneOptions | |
// FOLLOWING CODE IS UNTESTED, BUT PROBABLY NOT NEEDED - TRY IT WITHOUT IT FIRST | |
override fun createProcessOutReader(): Reader { | |
return ReplacingReader(process.inputStream) | |
} | |
override fun createProcessErrReader(): Reader { | |
return ReplacingReader(process.errorStream) | |
} | |
// BaseInputStreamReader needed by IntelliJ | |
private class ReplacingReader(stream:InputStream) : BaseInputStreamReader(stream, Charsets.UTF_8) { | |
private val readBuffer = CharArray(1024) | |
private val buffer = CharArray(readBuffer.size * 2) | |
private var bufferPosition = 0 | |
private var bufferSize = 0 | |
private var justReadCR = false | |
/** @return true if EOF, false otherwise */ | |
private fun refillBufferIfNeeded(onlyIfReady:Boolean):Boolean { | |
if (bufferPosition < bufferSize || (onlyIfReady && !super.ready())) { | |
return false | |
} | |
bufferPosition = 0 | |
bufferSize = 0 | |
val readBuffer = readBuffer | |
val read = super.read(readBuffer, 0, readBuffer.size) // To not get delegated back to us | |
if (read < 0) { | |
return true // EOF | |
} | |
var justReadCR = justReadCR | |
val buffer = buffer | |
var outI = 0 | |
for (i in 0 until read) { | |
val c = readBuffer[i] | |
// If we encounter \n without preceding \r, add it. | |
if (c == '\n' && !justReadCR) { | |
buffer[outI++] = '\r' | |
} | |
buffer[outI++] = c | |
justReadCR = c == '\r' | |
} | |
bufferSize = outI | |
this.justReadCR = justReadCR | |
return false | |
} | |
override fun ready(): Boolean { | |
return bufferPosition < bufferSize || super.ready() | |
} | |
override fun skip(n: Long): Long { | |
if (refillBufferIfNeeded(false)) {// Blocking | |
return -1 // EOF | |
} | |
var skipped = 0L | |
while (bufferPosition < bufferSize && skipped < n) { | |
val available = (bufferSize - bufferPosition).toLong() | |
val required = n - skipped | |
val copied = minOf(available, required) | |
skipped += copied | |
bufferPosition += copied.toInt() | |
if (refillBufferIfNeeded(true)) { | |
break | |
} | |
} | |
return skipped | |
} | |
override fun read(target: CharBuffer): Int { | |
if (refillBufferIfNeeded(false)) {// Blocking | |
return -1 // EOF | |
} | |
var read = 0 | |
while (bufferPosition < bufferSize && target.remaining() > 0) { | |
val available = bufferSize - bufferPosition | |
val required = target.remaining() | |
val copied = minOf(available, required) | |
target.put(buffer, bufferPosition, copied) | |
read += copied | |
bufferPosition += copied | |
if (refillBufferIfNeeded(true)) { | |
break | |
} | |
} | |
return read | |
} | |
override fun read(): Int { | |
if (refillBufferIfNeeded(false)) { | |
return -1 // EOF | |
} | |
return buffer[bufferPosition++].toInt() | |
} | |
override fun read(cbuf: CharArray): Int { | |
return read(cbuf, 0, cbuf.size) | |
} | |
override fun read(cbuf: CharArray, off: Int, len: Int): Int { | |
if (refillBufferIfNeeded(false)) {// Blocking | |
return -1 // EOF | |
} | |
var read = 0 | |
while (bufferPosition < bufferSize && read < len) { | |
val available = bufferSize - bufferPosition | |
val required = len - read | |
val copied = minOf(available, required) | |
System.arraycopy(buffer, bufferPosition, cbuf, off + read, copied) | |
read += copied | |
bufferPosition += copied | |
if (refillBufferIfNeeded(true)) { | |
break | |
} | |
} | |
return read | |
} | |
override fun markSupported(): Boolean = false | |
override fun reset() { | |
throw IOException("reset() not supported") | |
} | |
override fun mark(readAheadLimit: Int) { | |
throw IOException("mark() not supported") | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment