Skip to content

Commit 19cf27d

Browse files
authored
Support reading file from standard input
Fixes #263 Updates #275
1 parent 8c56d91 commit 19cf27d

File tree

9 files changed

+137
-5
lines changed

9 files changed

+137
-5
lines changed

docs/cli.adoc

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ See the "ERROR LISTS" section for a description of the format for _errors_.
5757
+
5858
Incompatible with *--lsp-server*.
5959

60+
*--stdin*::
61+
Read standard input as a JavaScript file.
62+
+
63+
Incompatible with *--lsp-server*.
64+
6065
*--lsp*::
6166
*--lsp-server*::
6267
Run *quick-lint-js* in LSP server mode.

docs/quick-lint-js.1

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
'\" t
99
.\" Title: quick-lint-js
1010
.\" Author: [see the "AUTHOR(S)" section]
11-
.\" Generator: Asciidoctor 2.0.10
11+
.\" Generator: Asciidoctor 2.0.12
1212
.\" Manual: \ \&
1313
.\" Source: quick-lint-js version 0.2.0
1414
.\" Language: English
@@ -88,6 +88,13 @@ See the "ERROR LISTS" section for a description of the format for \fIerrors\fP.
8888
Incompatible with \fB\-\-lsp\-server\fP.
8989
.RE
9090
.sp
91+
\fB\-\-stdin\fP
92+
.RS 4
93+
Read standard input as a JavaScript file.
94+
.sp
95+
Incompatible with \fB\-\-lsp\-server\fP.
96+
.RE
97+
.sp
9198
\fB\-\-lsp\fP, \fB\-\-lsp\-server\fP
9299
.RS 4
93100
Run \fBquick\-lint\-js\fP in LSP server mode.
@@ -199,12 +206,14 @@ To lint a file called \fIlib/index.js\fP, writing error messages to the terminal
199206
.sp
200207
.if n .RS 4
201208
.nf
209+
.fam C
202210
$ \fBquick\-lint\-js\fP lib/index.js
203211
lib/index.js:1:20: error: variable used before declaration: language [E058]
204212
lib/index.js:2:7: note: variable declared here [E058]
205213
lib/index.js:3:1: error: assignment to const variable [E003]
206214
lib/index.js:1:7: note: const variable declared here [E003]
207215
lib/index.js:5:25: warning: use of undeclared variable: ocupation [E057]
216+
.fam
208217
.fi
209218
.if n .RE
210219
.br
@@ -217,11 +226,13 @@ To lint three files, writing machine\-readable messages to \fI/tmp/vim\-qflist.j
217226
.sp
218227
.if n .RS 4
219228
.nf
229+
.fam C
220230
$ \fBquick\-lint\-js\fP \-\-output\-format=vim\-qflist\-json \(rs
221231
\-\-vim\-bufnr=3 lib/pizza\-dough.js \(rs
222232
\-\-vim\-bufnr=4 lib/pizza\-sauce.js \(rs
223233
\-\-vim\-bufnr=6 lib/pineapple.js \(rs
224234
>/tmp/vim\-qflist.json
235+
.fam
225236
.fi
226237
.if n .RE
227238
.br
@@ -236,10 +247,12 @@ To lint a file called \fIbad.js\fP, but don\(cqt fail on use\-of\-undeclared\-va
236247
.sp
237248
.if n .RS 4
238249
.nf
250+
.fam C
239251
$ \fBquick\-lint\-js\fP \-\-exit\-fail\-on=\-E057 bad.js
240252
bad.js:5:25: warning: use of undeclared variable: $ [E057]
241253
$ echo $?
242254
0
255+
.fam
243256
.fi
244257
.if n .RE
245258
.br

src/file.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,11 @@ read_file_result read_file(const char *path) {
211211
windows_handle_file file(handle);
212212
return read_file(path, file.ref());
213213
}
214+
215+
read_file_result read_stdin() {
216+
windows_handle_file_ref file(::GetStdHandle(STD_INPUT_HANDLE));
217+
return read_file("<stdin>", file);
218+
}
214219
#endif
215220

216221
#if defined(QLJS_FILE_POSIX)
@@ -224,6 +229,11 @@ read_file_result read_file(const char *path) {
224229
posix_fd_file file(fd);
225230
return read_file(path, file.ref());
226231
}
232+
233+
read_file_result read_stdin() {
234+
posix_fd_file_ref file(STDIN_FILENO);
235+
return read_file("<stdin>", file);
236+
}
227237
#endif
228238

229239
void write_file(const std::string &path, string8_view content) {

src/main.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,12 @@ void handle_options(quick_lint_js::options o) {
158158
quick_lint_js::any_error_reporter reporter =
159159
quick_lint_js::any_error_reporter::make(o.output_format, &o.exit_fail_on);
160160
for (const quick_lint_js::file_to_lint &file : o.files_to_lint) {
161-
quick_lint_js::read_file_result source =
162-
quick_lint_js::read_file(file.path);
161+
quick_lint_js::read_file_result source;
162+
if (file.is_stdin) {
163+
source = quick_lint_js::read_stdin();
164+
} else {
165+
source = quick_lint_js::read_file(file.path);
166+
}
163167
source.exit_if_not_ok();
164168
reporter.set_source(&source.content, file);
165169
quick_lint_js::process_file(&source.content, reporter.get(),
@@ -394,6 +398,7 @@ void print_help_message() {
394398
print_option("--exit-fail-on=[CODES]",
395399
"Fail with a non-zero exit code if any of these");
396400
print_option("", "errors are found (default: \"all\")");
401+
print_option("--stdin", "Read standard input as a JavaScript file");
397402
print_option("--lsp, --lsp-server",
398403
"Run in Language Server mode (for LSP-aware editors)");
399404
print_option("--output-format=[FORMAT]",

src/options.cpp

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ class arg_parser {
9898
}
9999
this->is_ignoring_options_ = true;
100100
this->option_ = std::nullopt;
101+
} else if (this->current_arg() == "-"sv) {
102+
this->option_ = std::nullopt;
101103
} else if (this->current_arg()[0] == '-') {
102104
const char* equal = std::strchr(this->current_arg(), '=');
103105
option o;
@@ -148,12 +150,28 @@ options parse_options(int argc, char** argv) {
148150
options o;
149151

150152
std::optional<int> next_vim_file_bufnr;
153+
bool has_stdin = false;
151154

152155
arg_parser parser(argc, argv);
153156
while (!parser.done()) {
154157
if (const char* argument = parser.match_argument()) {
155-
file_to_lint file{.path = argument, .vim_bufnr = next_vim_file_bufnr};
156-
o.files_to_lint.emplace_back(file);
158+
if (argument == "-"sv) {
159+
if (has_stdin) {
160+
o.has_multiple_stdin = true;
161+
continue;
162+
}
163+
file_to_lint file{.path = "<stdin>",
164+
.is_stdin = true,
165+
.vim_bufnr = next_vim_file_bufnr};
166+
has_stdin = true;
167+
o.files_to_lint.emplace_back(file);
168+
} else {
169+
file_to_lint file{.path = argument,
170+
.is_stdin = false,
171+
.vim_bufnr = next_vim_file_bufnr};
172+
o.files_to_lint.emplace_back(file);
173+
}
174+
157175
next_vim_file_bufnr = std::nullopt;
158176
} else if (parser.match_flag_option("--debug-parser-visits"sv,
159177
"--debug-p"sv)) {
@@ -188,6 +206,17 @@ options parse_options(int argc, char** argv) {
188206
o.version = true;
189207
} else if (parser.match_flag_option("--lsp-server"sv, "--lsp"sv)) {
190208
o.lsp_server = true;
209+
} else if (parser.match_flag_option("--stdin"sv, ""sv)) {
210+
if (has_stdin) {
211+
o.has_multiple_stdin = true;
212+
continue;
213+
}
214+
file_to_lint file{.path = "<stdin>",
215+
.is_stdin = true,
216+
.vim_bufnr = next_vim_file_bufnr};
217+
o.files_to_lint.emplace_back(file);
218+
has_stdin = true;
219+
next_vim_file_bufnr = std::nullopt;
191220
} else {
192221
const char* unrecognized = parser.match_anything();
193222
o.error_unrecognized_options.emplace_back(unrecognized);
@@ -213,6 +242,9 @@ bool options::dump_errors(std::ostream& out) const {
213242
"--lsp-server mode\n";
214243
}
215244
}
245+
if (this->has_multiple_stdin) {
246+
out << "warning: multiple standard input given on command line\n";
247+
}
216248
for (const auto& option : this->error_unrecognized_options) {
217249
out << "error: unrecognized option: " << option << '\n';
218250
have_errors = true;

src/quick-lint-js/file.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ read_file_result read_file(const char *path);
2424

2525
read_file_result read_file(const char *path, platform_file_ref);
2626

27+
read_file_result read_stdin(void);
28+
2729
void write_file(const std::string &path, string8_view content);
2830
void write_file(const char *path, string8_view content);
2931
}

src/quick-lint-js/options.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ enum class output_format {
1818

1919
struct file_to_lint {
2020
const char *path;
21+
bool is_stdin;
2122
std::optional<int> vim_bufnr;
2223
};
2324

@@ -32,6 +33,7 @@ struct options {
3233
compiled_error_list exit_fail_on;
3334

3435
std::vector<const char *> error_unrecognized_options;
36+
bool has_multiple_stdin = false;
3537

3638
bool dump_errors(std::ostream &) const;
3739
};

test/test-options.cpp

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,18 @@ TEST(test_options, vim_file_bufnr) {
156156
EXPECT_EQ(o.files_to_lint[1].vim_bufnr, 2);
157157
}
158158

159+
{
160+
options o = parse_options({"--vim-file-bufnr=42", "-"});
161+
ASSERT_EQ(o.files_to_lint.size(), 1);
162+
EXPECT_EQ(o.files_to_lint[0].vim_bufnr, 42);
163+
}
164+
165+
{
166+
options o = parse_options({"one.js", "--vim-file-bufnr=42", "--stdin"});
167+
ASSERT_EQ(o.files_to_lint.size(), 2);
168+
EXPECT_EQ(o.files_to_lint[1].vim_bufnr, 42);
169+
}
170+
159171
{
160172
options o = parse_options({"--vim-file-bufnr=1", "--", "one.js", "two.js"});
161173
ASSERT_EQ(o.files_to_lint.size(), 2);
@@ -176,6 +188,49 @@ TEST(test_options, lsp_server) {
176188
}
177189
}
178190

191+
TEST(test_options, dash_dash_stdin) {
192+
{
193+
options o = parse_options({"--stdin", "one.js"});
194+
ASSERT_EQ(o.files_to_lint.size(), 2);
195+
EXPECT_TRUE(o.files_to_lint[0].is_stdin);
196+
EXPECT_FALSE(o.has_multiple_stdin);
197+
}
198+
199+
{
200+
options o = parse_options({"one.js", "--stdin"});
201+
ASSERT_EQ(o.files_to_lint.size(), 2);
202+
EXPECT_TRUE(o.files_to_lint[1].is_stdin);
203+
EXPECT_FALSE(o.has_multiple_stdin);
204+
}
205+
206+
{
207+
options o = parse_options({"-"});
208+
ASSERT_EQ(o.files_to_lint.size(), 1);
209+
EXPECT_TRUE(o.files_to_lint[0].is_stdin);
210+
EXPECT_FALSE(o.has_multiple_stdin);
211+
}
212+
}
213+
214+
TEST(test_options, is_stdin_emplaced_only_once) {
215+
{
216+
options o = parse_options({"--stdin", "one.js", "-", "two.js"});
217+
ASSERT_EQ(o.files_to_lint.size(), 3);
218+
EXPECT_TRUE(o.has_multiple_stdin);
219+
}
220+
{
221+
options o = parse_options({"one.js", "-", "two.js", "-"});
222+
ASSERT_EQ(o.files_to_lint.size(), 3);
223+
EXPECT_TRUE(o.has_multiple_stdin);
224+
}
225+
}
226+
227+
TEST(test_options, single_hyphen_is_argument) {
228+
{
229+
options o = parse_options({"one.js", "-", "two.js"});
230+
ASSERT_EQ(o.files_to_lint.size(), 3);
231+
}
232+
}
233+
179234
TEST(test_options, print_help) {
180235
{
181236
options o = parse_options({"--help"});
@@ -353,6 +408,7 @@ TEST(test_options, dump_errors) {
353408
{
354409
const file_to_lint file = {
355410
.path = "file.js",
411+
.is_stdin = false,
356412
.vim_bufnr = std::optional<int>(),
357413
};
358414

website/public/cli/index.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,13 @@ <h2 id="_options">Options</h2>
168168
<p>Incompatible with <strong>--lsp-server</strong>.</p>
169169
</div>
170170
</dd>
171+
<dt class="hdlist1"><strong>--stdin</strong></dt>
172+
<dd>
173+
<p>Read standard input as a JavaScript file.</p>
174+
<div class="paragraph">
175+
<p>Incompatible with <strong>--lsp-server</strong>.</p>
176+
</div>
177+
</dd>
171178
<dt class="hdlist1"><strong>--lsp</strong></dt>
172179
<dt class="hdlist1"><strong>--lsp-server</strong></dt>
173180
<dd>

0 commit comments

Comments
 (0)