Skip to content

Commit

Permalink
feat(vscode): make quick-lint a jerk πŸ–•
Browse files Browse the repository at this point in the history
Summary: It ticks me off 😠 how polite πŸ˜‡ quick-lint is when I code.  If I mess
up 😬, I want it to tell me.  Stop beating around the bush 🌳 and tell it to me
like it is. πŸ’―

Test plan: πŸ“
1. open TypeScript file with no errors in it βœ…
2. add an error ❌
3. note how quick-lint tells you what the problem is like a sissy 😭
4. enable settings > quick-lint-js > snarky 😈
5. note how quick-lint is straight up with you.  You suck. πŸ—‘οΈ

Closes #1188
  • Loading branch information
vegerot committed Jan 21, 2024
1 parent 55362b8 commit ac25774
Show file tree
Hide file tree
Showing 7 changed files with 201 additions and 2 deletions.
6 changes: 6 additions & 0 deletions plugin/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@
],
"default": "off",
"description": "Log document changes. Useful for quick-lint-js contributors."
},
"quick-lint-js.snarky": {
"scope": "window",
"type": "boolean",
"default": false,
"description": "Add spice to your failures."
}
}
},
Expand Down
12 changes: 12 additions & 0 deletions plugin/vscode/quick-lint-js/vscode/qljs-document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ void QLJS_Config_Document::on_config_file_changed(
diagnostic_collection);
}

void QLJS_Config_Document::on_translator_changed(
::Napi::Env env, QLJS_Workspace& workspace,
VSCode_Diagnostic_Collection diagnostic_collection) {
this->lint_config_and_publish_diagnostics(env, workspace,
diagnostic_collection);
}
void QLJS_Config_Document::lint_config_and_publish_diagnostics(
::Napi::Env env, QLJS_Workspace& workspace,
VSCode_Diagnostic_Collection diagnostic_collection) {
Expand Down Expand Up @@ -106,6 +112,12 @@ void QLJS_Lintable_Document::on_config_file_changed(
diagnostic_collection);
}

void QLJS_Lintable_Document::on_translator_changed(
::Napi::Env env, QLJS_Workspace& workspace,
VSCode_Diagnostic_Collection diagnostic_collection) {
this->lint_javascript_and_publish_diagnostics(env, workspace,
diagnostic_collection);
}
void QLJS_Lintable_Document::lint_javascript_and_publish_diagnostics(
::Napi::Env env, QLJS_Workspace& workspace,
VSCode_Diagnostic_Collection diagnostic_collection) {
Expand Down
9 changes: 9 additions & 0 deletions plugin/vscode/quick-lint-js/vscode/qljs-document.h
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,10 @@ class QLJS_Document_Base {
VSCode_Diagnostic_Collection,
Loaded_Config_File* config_file) = 0;

virtual void on_translator_changed(
::Napi::Env env, QLJS_Workspace& workspace,
VSCode_Diagnostic_Collection diagnostic_collection) = 0;

protected:
::Napi::Value uri() { return this->vscode_document_.Value().uri(); }

Expand All @@ -118,6 +122,9 @@ class QLJS_Config_Document : public QLJS_Document_Base {
void on_config_file_changed(::Napi::Env, QLJS_Workspace&,
VSCode_Diagnostic_Collection,
Loaded_Config_File* config_file) override;
void on_translator_changed(
::Napi::Env env, QLJS_Workspace& workspace,
VSCode_Diagnostic_Collection diagnostic_collection) override;

private:
void lint_config_and_publish_diagnostics(::Napi::Env, QLJS_Workspace&,
Expand All @@ -142,6 +149,8 @@ class QLJS_Lintable_Document : public QLJS_Document_Base {
void on_config_file_changed(::Napi::Env, QLJS_Workspace&,
VSCode_Diagnostic_Collection,
Loaded_Config_File* config_file) override;
void on_translator_changed(::Napi::Env, QLJS_Workspace&,
VSCode_Diagnostic_Collection) override;

void lint_javascript_and_publish_diagnostics(::Napi::Env, QLJS_Workspace&,
VSCode_Diagnostic_Collection);
Expand Down
30 changes: 29 additions & 1 deletion plugin/vscode/quick-lint-js/vscode/qljs-workspace.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ class Extension_Configuration {
}
}

bool get_snarky(::Napi::Env env) {
::Napi::Value value = this->get(env, "snarky");
if (!value.IsBoolean()) {
return false;
}
return value.As<::Napi::Boolean>();
}

::Napi::Value get(::Napi::Env env, const char* section) {
return this->config_ref_.Get("get").As<::Napi::Function>().Call(
this->config_ref_.Value(), {::Napi::String::New(env, section)});
Expand Down Expand Up @@ -124,7 +132,8 @@ QLJS_Workspace::QLJS_Workspace(const Napi::CallbackInfo& info)
::Napi::Persistent(info[2].As<::Napi::Object>())),
ui_(this) {
QLJS_DEBUG_LOG("Workspace %p: created\n", this);
this->update_logging(info.Env());
configuration_changed(info);

this->fs_change_detection_thread_ =
Thread([this]() -> void { this->run_fs_change_detection_thread(); });
}
Expand Down Expand Up @@ -234,10 +243,29 @@ ::Napi::Value QLJS_Workspace::configuration_changed(
::Napi::Env env = info.Env();

this->update_logging(env);
this->on_translator_changed(env);

return env.Undefined();
}

void QLJS_Workspace::on_translator_changed(::Napi::Env env) {
Extension_Configuration config(env, this->vscode_);
bool is_snarky = config.get_snarky(env);

this->is_snarky_enabled_ = is_snarky;
if (is_snarky) {
this->translator_.use_messages_from_locale("en_US@snarky");
} else {
// TODO(#529): Use the locale from the VS Code configuration.
this->translator_.use_messages_from_source_code();
}
this->qljs_documents_.for_each(
[this, is_snarky, env](::Napi::Value value) -> void {
QLJS_Document_Base* doc = QLJS_Document_Base::unwrap(value);
doc->on_translator_changed(env, *this, this->diagnostic_collection());
});
}

::Napi::Value QLJS_Workspace::editor_visibility_changed(
const Napi::CallbackInfo& info) {
::Napi::Env env = info.Env();
Expand Down
7 changes: 7 additions & 0 deletions plugin/vscode/quick-lint-js/vscode/qljs-workspace.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@ class QLJS_Workspace : public ::Napi::ObjectWrap<QLJS_Workspace> {
// Disable logging if logging is enabled.
void disable_logging();

void on_translator_changed(::Napi::Env env);

~QLJS_Workspace();

::Napi::Value dispose(const ::Napi::CallbackInfo& info);
Expand Down Expand Up @@ -248,8 +250,13 @@ class QLJS_Workspace : public ::Napi::ObjectWrap<QLJS_Workspace> {
QLJS_Workspace* workspace_;
};

public:
bool is_snarky_enabled() const;

private:
bool is_snarky_enabled_ = false;
Translator translator_;

bool disposed_ = false;
VSCode_Tracer tracer_;
VSCode_Module vscode_;
Expand Down
138 changes: 137 additions & 1 deletion plugin/vscode/test/vscode-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -1095,6 +1095,142 @@ tests = {

// TODO(strager): Allow the user to delete the extenion, thereby deleting
// the output channel.

"snarky enabled at start": async ({ addCleanup }) => {
addCleanup(resetConfigurationAsync);

await vscode.workspace
.getConfiguration("quick-lint-js")
.update("snarky", true, vscode.ConfigurationTarget.Workspace);

let scratchDirectory = makeScratchDirectory({ addCleanup });
let helloFilePath = path.join(scratchDirectory, "hello.js");
fs.writeFileSync(helloFilePath, "let x = undeclaredVariable;\n");
let helloURI = vscode.Uri.file(helloFilePath);
let helloDocument = await vscode.workspace.openTextDocument(helloURI);
await loadExtensionAsync({ addCleanup });
let helloEditor = await vscode.window.showTextDocument(helloDocument);

await waitUntilAnyDiagnosticsAsync(helloURI);
let diags = normalizeDiagnostics(helloURI);
diags = diags.map(({ code, severity, message }) => ({
code,
severity,
message,
}));
assert.deepStrictEqual(diags, [
// use of undeclared variable 'undeclaredVariable'
{
code: {
target: "https://quick-lint-js.com/errors/E0057/",
value: "E0057",
},
severity: vscode.DiagnosticSeverity.Warning,
message: "did you fail spelling class?",
},
]);
},

"enable snarky": async ({ addCleanup }) => {
addCleanup(resetConfigurationAsync);
await loadExtensionAsync({ addCleanup });
let scratchDirectory = makeScratchDirectory({ addCleanup });
let helloFilePath = path.join(scratchDirectory, "hello.js");
fs.writeFileSync(helloFilePath, "let x = undeclaredVariable;\n");
let helloURI = vscode.Uri.file(helloFilePath);
let helloDocument = await vscode.workspace.openTextDocument(helloURI);
let helloEditor = await vscode.window.showTextDocument(helloDocument);

// 1. Make sure we're not snarky at the start
{
await waitUntilAnyDiagnosticsAsync(helloURI);
let diagMessages = normalizeDiagnostics(helloURI).map(
({ message }) => message
);
assert.deepStrictEqual(diagMessages, [
"use of undeclared variable: undeclaredVariable",
]);
}

// 2. Enable snarky
await vscode.workspace
.getConfiguration("quick-lint-js")
.update("snarky", true, vscode.ConfigurationTarget.Workspace);

// 3. Make sure we're snarky now
await pollAsync(() => {
let diagMessages = normalizeDiagnostics(helloURI).map(
({ message }) => message
);
let want = ["did you fail spelling class?"];
assert.deepStrictEqual(diagMessages, want);
});
},

// this starts off the exact same as snarky_enabled_at_start, so maybe these
// tests should be merged
"disable snarky": async ({ addCleanup }) => {
addCleanup(resetConfigurationAsync);

await vscode.workspace
.getConfiguration("quick-lint-js")
.update("snarky", true, vscode.ConfigurationTarget.Workspace);

let scratchDirectory = makeScratchDirectory({ addCleanup });
let helloFilePath = path.join(scratchDirectory, "hello.js");
fs.writeFileSync(helloFilePath, "let x = undeclaredVariable;\n");
let helloURI = vscode.Uri.file(helloFilePath);
let helloDocument = await vscode.workspace.openTextDocument(helloURI);
await loadExtensionAsync({ addCleanup });
let helloEditor = await vscode.window.showTextDocument(helloDocument);

// 1. Make sure we're snarky at the start
await waitUntilAnyDiagnosticsAsync(helloURI);
let diagMessages = normalizeDiagnostics(helloURI).map(
({ message }) => message
);
assert.deepStrictEqual(diagMessages, [
// use of undeclared variable 'undeclaredVariable'

"did you fail spelling class?",
]);

// 2. Disable snarky
await vscode.workspace
.getConfiguration("quick-lint-js")
.update("snarky", false, vscode.ConfigurationTarget.Workspace);

// 3. Make sure we're polite now
await pollAsync(() => {
let diags = normalizeDiagnostics(helloURI).map(({ message }) => message);
let got = diags;
let want = ["use of undeclared variable: undeclaredVariable"];
assert.deepStrictEqual(diags, want);
});
},

"snarky enabled at start config file": async ({ addCleanup }) => {
addCleanup(resetConfigurationAsync);
await vscode.workspace
.getConfiguration("quick-lint-js")
.update("snarky", true, vscode.ConfigurationTarget.Workspace);
let scratchDirectory = makeScratchDirectory({ addCleanup });
let configFilePath = path.join(scratchDirectory, "quick-lint-js.config");
fs.writeFileSync(configFilePath, "{");
let configURI = vscode.Uri.file(configFilePath);

await loadExtensionAsync({ addCleanup });
let configDocument = await vscode.workspace.openTextDocument(configURI);
let configEditor = await vscode.window.showTextDocument(configDocument);

await waitUntilAnyDiagnosticsAsync(configURI);

let configDiags = normalizeDiagnostics(configURI);
assert.deepStrictEqual(
configDiags.map(({ message }) => message),
["yeah, JSON sucks; try quick-lint-json"]
);
},
};

if (os.platform() === "linux") {
Expand Down Expand Up @@ -1535,7 +1671,7 @@ async function pollAsync(callback) {
}

async function resetConfigurationAsync() {
for (let setting of ["logging"]) {
for (let setting of ["logging", "snarky"]) {
await vscode.workspace
.getConfiguration("quick-lint-js")
.update(setting, undefined, vscode.ConfigurationTarget.Workspace);
Expand Down
1 change: 1 addition & 0 deletions src/quick-lint-js/i18n/translation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ Span<std::string_view> get_user_locale_preferences(

// TODO(strager): Determine the language using macOS' and Windows' native
// APIs. See GNU gettext's _nl_language_preferences_default.
// TODO (#529): Also use VSCode's "Display Language" setting

Vector<std::string_view> locales("locales", allocator);
locales.push_back(locale);
Expand Down

0 comments on commit ac25774

Please sign in to comment.