Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Feature Request: Enable Option to Receive stdin and Output stdout #1153

Closed
rogerluan opened this issue May 19, 2021 · 9 comments
Closed

Feature Request: Enable Option to Receive stdin and Output stdout #1153

rogerluan opened this issue May 19, 2021 · 9 comments

Comments

@rogerluan
Copy link

Hey 👋

I've searched for this feature in existing issues and pull requests and couldn't find. I also read the documentation provided by ktlint --help.

Expected Behavior

  1. Why should this feature be added?

I use ktlint on Android projects, and use SwiftFormat on iOS projects. I've been using ktlint on my team's precommit git hook, where I created a custom script that basically "lints (and autoformat) git staged files". However, although we've been using this script for (probably) over 3 years now, it has some flaws. So I found out recently that SwiftFormat added a section that instructs users how to install a precommit hook pretty well: https://github.com/nicklockwood/SwiftFormat#git-pre-commit-hook

It uses https://github.com/hallettj/git-format-staged - which I found quite of an amazing strategy, take some time to read its README :). I believe it's the state of the art and most accurate way to perform such task of formatting staged files.

Current Behavior

Here comes the problem: ktlint doesn't work with git-format-staged out of the box because (AFAICT) there's no way to make it print the output to stdout, unlike SwiftFormat and other linters from other languages like ruby and javascript. In practical terms:

SwiftFormat stdin option prints result to stdout

$ echo "if true {\n\n\n\n}" | swiftformat stdin
Running SwiftFormat...
if true {}
Swiftformat completed successfully.

ktlint stdin option just analyzes the input stream and prints its analysis to stdout

$ echo "if (true) {\n\n\n}" | ktlint --stdin
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.jetbrains.kotlin.com.intellij.util.ReflectionUtil (file:/usr/local/Cellar/ktlint/0.41.0/libexec/ktlint) to method java.util.ResourceBundle.setParent(java.util.ResourceBundle)
WARNING: Please consider reporting this to the maintainers of org.jetbrains.kotlin.com.intellij.util.ReflectionUtil
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
<stdin>:2:1: Unexpected blank line(s) before "}"
<stdin>:3:1: Needless blank line(s)

Thus, this feature request consists of enabling us to request ktlint to print the output of a stdin, to stdout (same as -F or --format options, but printing to stdout instead of in-place at the original file).

Proposal

$ echo "if (true) {\n\n\n}" | ktlint --stdin --stdout
if (true) {}

Additional information

$ ktlint --version
0.41.0

Also, check out this excerpt taken from https://github.com/nicklockwood/SwiftFormat:

When using stdin, SwiftFormat does not have access to the file path of the input, so features that rely on the file location (such as inserting the creation date into header comments, or detecting .swiftformat configuration files in the file path) will not work. To solve this, you can provide the file path using the --stdinpath argument:

$ cat /path/to/file.swift | swiftformat stdin --stdinpath /path/to/file.swift

I don't know if this would make sense in ktlint as I don't know if there're rules that make use of the filepath (e.g. filepath linter, or detecting configuration files). May be something to keep in mind, though!


Thanks for reading! Looking forward to hearing your thoughts on this feature and its feasibility, as well as if there's any shortcut we can use to make this happen with zero code changes that I may not be aware of 🙏

@shashachu
Copy link
Contributor

@rogerluan so just to clarify - the request is to output the formatted file to stdout instead of modifying the file in-place with formatting?

@rogerluan
Copy link
Author

Yes @shashachu ! 😃

@mickael-menu
Copy link

I'm interested in this as well.

I maintain a project in both Swift and Kotlin. Thanks to git-format-staged, the Swift staged files are automagically formatted when committing them. With Kotlin, I need to manually call ktlint before each commit.

@paul-dingemans
Copy link
Collaborator

Same/similar idea is requested in #1123

@paul-dingemans
Copy link
Collaborator

This functionality seems to be available since version 0.2 of ktlint. After some experimenting, I found that command below does exactly what you want:

$ ktlint-0.46.1 --stdin -F 2> /dev/null
class Foo { 
fun foo() {}
}

results in output:

class Foo {
    fun foo() {}
}

Important notes:

  • without parameter -F ktlint does not format the code but only prints the violations.
  • parameter 2> /dev/null is needed to suppress the output of stderr (needed when using Java 11+)

@paul-dingemans paul-dingemans closed this as not planned Won't fix, can't repro, duplicate, stale Jul 6, 2022
@mickael-menu
Copy link

@paul-dingemans Thank you, it works perfectly. Here's my .git/hooks/pre-commit using git-format-staged, for reference:

#!/bin/sh
git-format-staged --formatter "ktlint --stdin -F {} 2> /dev/null" "*.kt"

@rogerluan
Copy link
Author

🤩 🤩 Amazing!! I can't wait to start using this! Thank you so much for showing this to us @paul-dingemans @michael-menu 🚀

I'll report back with my findings once I get to test this 🙌

@jackhypertrack
Copy link

@rogerluan did you have any success running this? On my side, running this command seems to have no effect.

@mickael-menu did you change anything else in your pre commit script?

Thank you for answers in advance.

@rogerluan
Copy link
Author

Just tried this out now. I've made bittersweet observations:

First, the precommit hook crashes (doesn't work and return an error) when the file has any import or package statements at the top. The errors:

format-staged-files: error: unable to read file content from object database: 1c5c5e90f41b688fc4f07f3870372559180a1253

When committing from Git Tower, and:

format-staged-files: error: formatter exited with non-zero status

When committing from Terminal.

This is probably something misconfigured, or something that could be skipped, so I continued...

Then I noticed that, for ktlint, it won't read only the modified lines. It reads and lints the entire file. So if I have a file with 100 lines, I add 23 lines at the end of it, all the 123 lines are going to be formatted by the linter. This is often not desired, and it's not the same behavior with other linters, even always using the same tool (https://github.com/hallettj/git-format-staged) 😬

Here's my pre-commit:

#!/bin/sh

format-staged-files --formatter "ktlint --stdin -F {} 2> /dev/null" "*.kt"

Here's the file I tested with: test.kt

import java.util.Locale

fun Int.doSomething(): Int {

    println("Hello, this is a test.")


    println("Hello, this is a test.")

    return 2

}


fun Int.doSomething2(): Int {



    println("Hello, this is a test.")

    if (true) {

        println("Hello, this is a test.")

    }

    println("Hello, this is a test.")

    return 2

}

I also tested with a real file from my project, and observed same results.

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

5 participants