-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
How can I use the linter to read from stdin instead of the file? #327
Comments
No, unfortunately the linter does not support reading from stdin at the moment. Thanks. |
Hello @yoheimuta Thank you for the reply. The reason I'm asking is because I'm trying to improve UX in VS Code extension (https://github.com/plexsystems/vscode-protolint). At the moment the extension only shows up-to-date linting warnings when you open or save the protobuf document. If the document has some unsaved changes, obviously they are not yet in the file we pass to protolint. It doesn't seem to be a big deal for Unix/macOS, I guess even something like this may work: protolint lint /dev/stdin However, Windows doesn't seem to provide similar option: https://stackoverflow.com/questions/7395157/windows-equivalent-to-dev-stdin At the moment I don't see any cross-platform workaround. Even if I success to pass the "document" via Unix domain socket and Windows named pipe, it still needs to consider the platform when generating the socket/pipe name. And it still looks like an overengineering. Unlike that reading the document text from stdin seems to be easier to implement in the extension. Maybe you consider adding this feature to the linter. If you have any thoughts, let me know. |
@AlexCannonball Thank you for providing more details about your use case - it makes perfect sense now. To support this feature with minimal effort, I think protolint should create a temporary file in the func doLint before it starts processing:
# This is a rough sketch:
diff --git a/internal/cmd/cmd.go b/internal/cmd/cmd.go
index 6e172b2..5048ccc 100644
--- a/internal/cmd/cmd.go
+++ b/internal/cmd/cmd.go
@@ -3,6 +3,8 @@ package cmd
import (
"fmt"
"io"
+ "io/ioutil"
+ "os"
"strings"
"github.com/yoheimuta/protolint/internal/cmd/subcmds/lint"
@@ -73,9 +75,39 @@ func doSub(
func doLint(
args []string,
+ stdin io.Reader,
stdout io.Writer,
stderr io.Writer,
) osutil.ExitCode {
+ // Read from stdin
+ input, err := ioutil.ReadAll(stdin)
+ if err != nil {
+ _, _ = fmt.Fprintln(stderr, "Error reading from stdin:", err)
+ return osutil.ExitInternalFailure
+ }
+
+ // If there's input, create a temporary file and write the content to it
+ if len(input) > 0 {
+ tmpfile, err := ioutil.TempFile("", "protolint-stdin-*.proto")
+ if err != nil {
+ _, _ = fmt.Fprintln(stderr, "Error creating temporary file:", err)
+ return osutil.ExitInternalFailure
+ }
+ defer os.Remove(tmpfile.Name()) // Clean up
+
+ if _, err := tmpfile.Write(input); err != nil {
+ _, _ = fmt.Fprintln(stderr, "Error writing to temporary file:", err)
+ return osutil.ExitInternalFailure
+ }
+ if err := tmpfile.Close(); err != nil {
+ _, _ = fmt.Fprintln(stderr, "Error closing temporary file:", err)
+ return osutil.ExitInternalFailure
+ }
+
+ // Append temporary file path to args
+ args = append(args, tmpfile.Name())
+ }
+
if len(args) < 1 {
_, _ = fmt.Fprintln(stderr, "protolint lint requires at least one argument. See Usage.")
_, _ = fmt.Fprint(stderr, help) What do you think? Would this approach work for you? |
@yoheimuta thank you for sharing the sketch. I don't know the project and Golang well, so let me comment from
As I understand from the sketch, it creates a real file on the disk. My thoughts about disk files:
What do you think about these? Let me describe how I see an ideal approach (if we change
I was trying to find where loading from file to memory happens, and ended up in But I don't understand how many functions need to be added or changed for the "ideal" approach. Maybe just adding something like
|
@yoheimuta hello, a quick update on my research regarding passing the text via named pipes. It works on Windows like this: However, it works with problems:
|
Hello @yoheimuta As for linting from stdin, I think it's not necessary for VS Code extension, if the slow reading from the pipe is fixed. However, even if you decide to implement linting from stdin, the first read operation will empty stdin buffer, so the subsequent reading attempts may fail. |
Yes, 375f265 intentionally has changed to read the most recent content, even if the fixer modifies the file along the way. This decision was based on my assumption that local access to a small file is fast enough to justify the extra read.
While I understand your concern about potential performance issues with excessive disk access, I have not seen or heard of any problems with intellij-protolint, which creates a temporary file each time a source file is updated. Just did some experimenting on my MacBook Pro and wanted to share my results with you!
main.gopackage main
import (
"crypto/rand"
"flag"
"fmt"
"os"
"time"
)
func main() {
var numFiles int
var numChars int
flag.IntVar(&numFiles, "files", 1000, "Number of temporary files to create")
flag.IntVar(&numChars, "chars", 1000, "Number of random characters in each file")
flag.Parse()
start := time.Now()
for i := 0; i < numFiles; i++ {
content := generateRandomContent(numChars)
err := createTempFile(content)
if err != nil {
fmt.Printf("Error creating temp file: %v\n", err)
os.Exit(1)
}
}
elapsed := time.Since(start)
fmt.Printf("Time taken to create 1000 temp files with 10000 random characters each: %v\n", elapsed)
}
func generateRandomContent(length int) []byte {
content := make([]byte, length)
_, err := rand.Read(content)
if err != nil {
fmt.Printf("Error generating random content: %v\n", err)
os.Exit(1)
}
return content
}
func createTempFile(content []byte) error {
tempFile, err := os.CreateTemp("", "tempfile_")
if err != nil {
return err
}
defer tempFile.Close()
_, err = tempFile.Write(content)
if err != nil {
return err
}
return nil
}
Implementing this feature would require a lot of code to write and maintain. If your idea relies on this feature, I recommend trying the workaround I suggested first, rather than spending extra effort on implementing the ideal approach. This is especially true if your concern isn't certain |
Hello @yoheimuta and thank you for the reply.
As I understand, this estimation is related to the feature with reading from stdin + making no temporary disk files. This totally makes sense, for VS Code extension I see at least one workaround which doesn't need
Is adding a memory cache there also hard to maintain? By the memory cache I mean something like this:
If this change is acceptable, it removes extra reads from the target file URI. This hopefully fixes the slow reading the text via Nodejs IPC which I'm trying to use as a workaround for the VS Code Extension. |
@AlexCannonball Thank you for sharing your thought.
Unfortunately, I'm afraid that's the case. |
Hello,
I'm trying to run the linter and give it the text for linting via the shell stdin instead of passing the filename with the text.
Unfortunately I haven't find the way to do it on Windows. For example:
Does the linter support reading from stdin instead of the file mode? If yes, could you show me the proper command + args example?
Thank you.
The text was updated successfully, but these errors were encountered: