Skip to content

Daemon plain file and plain text format support#76

Open
nikolayKeniston wants to merge 1 commit intoblock:mainfrom
nikolayKeniston:daemon-impr
Open

Daemon plain file and plain text format support#76
nikolayKeniston wants to merge 1 commit intoblock:mainfrom
nikolayKeniston:daemon-impr

Conversation

@nikolayKeniston
Copy link

@nikolayKeniston nikolayKeniston commented Mar 27, 2025

Added format command to daemon, which allows formatting either the specified files, or the specified code if the file list is "-" (similar to CLI), starting from a newline and ending with an ASCII EOT character (04); can be used as (echo "format -"; echo <CODE>; echo -ne '\04\n') | nc localhost <PORT>, or replace "echo <CODE>" with "cat" for standard input. Tests included.

@CLAassistant
Copy link

CLAassistant commented Mar 27, 2025

CLA assistant check
All committers have signed the CLA.

Copy link
Collaborator

@staktrace staktrace left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good. Have a question though

val inputLines = mutableListOf<String>()
while (true) {
val line = reader.readLine() ?: break
if (line.trim() == "\u0004") break
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this needed? Can we not just the end of stream as the break condition? I think it's possible for legitimate kotlin code files using a multiline string to have this character alone on a line, and then this would fail to format them correctly.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried this initially and I was able to achieve the expected result only by programmatically closing the output part of the stream. However, such simple tools as nc, as far as I understand, do not provide such an opportunity, and I wanted to keep the ability to use commands entirely from the terminal. Maybe there is a solution to this problem, but I did not find it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems to work for me. What I did:

  • comment out this one line (line 148, where you check for \u0004
  • build the shadowjar (../gradlew shadowJar)
  • start the daemon (java -jar build/libs/kotlin-format-1.0.4-SNAPSHOT-all.jar --daemon)
  • in a separate terminal, get the port number from cat ../.kotlinformatter/kf.lock
  • run (echo "format -"; echo "fun main() { println() }") | nc localhost 64957
  • as expected I get the output:
0
fun main() {
  println()
}

This matches my understanding that when the input pipe to nc is closed, nc will close the stream, and that will manifest as a reader.readLine() returning null in the daemon.

Do you not get the same behaviour?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also get the expected behavior if I cat a file into it:

$ (echo "format -"; cat foo.kt) | nc localhost 65065
0
fun main() {
  println()
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I changed the loop to:

while (true) {
  val line = reader.readLine()
  println(line)
  if (line == null) break;
  // if (line.trim() == "\u0004") break
  inputLines.add(line)
}

Then I do (echo "format -"; echo "fun main() { println() }") | nc localhost 45753. This results in the message fun main() { println() } in the console, and nothing happens until I close nc forcibly with ctrl+c. After closing, I see null (for line) and an exception:

Error handling client connection: Broken pipe
java.net.SocketException: Broken pipe
        at java.base/sun.nio.ch.SocketDispatcher.write0(Native Method)
        at java.base/sun.nio.ch.SocketDispatcher.write(SocketDispatcher.java:62)
        at java.base/sun.nio.ch.NioSocketImpl.tryWrite(NioSocketImpl.java:394)
        at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:410)
        at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:440)
        at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:819)
        at java.base/java.net.Socket$SocketOutputStream.implWrite(Socket.java:1231)
        at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1218)
        at java.base/java.io.OutputStream.write(OutputStream.java:124)
        at xyz.block.kotlinformatter.Daemon.write(Daemon.kt:275)
        at xyz.block.kotlinformatter.Daemon.start(Daemon.kt:162)
        at xyz.block.kotlinformatter.Cli.run(Cli.kt:42)
        at com.github.ajalt.clikt.core.CoreCliktCommandKt.parse(CoreCliktCommand.kt:107)
        at com.github.ajalt.clikt.core.CoreCliktCommandKt.main(CoreCliktCommand.kt:78)
        at com.github.ajalt.clikt.core.CoreCliktCommandKt.main(CoreCliktCommand.kt:90)
        at xyz.block.kotlinformatter.CliKt.main(Cli.kt:117)

It seems that nc closes both read and write streams when you press ctrl+c, and does nothing before this. I'm not sure why this behavior might be happening. Maybe there's some difference in the environments?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

weird. what shell/OS are you using? and what version of nc?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bash, arch linux, netcat (The GNU Netcat) 0.7.1

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to reproduce your issue using an arch linux docker image. I also reproduced it on an ubuntu docker image. Some searching online led me to https://stackoverflow.com/questions/53634911/why-does-sending-d-with-netcat-not-trigger-an-eof-when-reading-from-a-unix-sock

I was able to confirm that when using openbsd-netcat on arch linux or Ubuntu with the -N flag it works as intended. On ubuntu with netcat-traditional you have to use -q 1. On arch linux with gnu netcat I couldn't get it working since the -c option closes the connection right away instead of waiting for the response. I did however manage to get it working with socat instead of nc:

$ (echo "format -"; echo "fun main() { print() }") | socat STDIO TCP4:localhost:34169
0
fun main() {
  print()
}

Given the inconsistency of different netcat versions, perhaps you can get by with using socat instead?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants