Skip to content

Commit 48c9fec

Browse files
committed
wasm: create workspace to hold all open documents (C++ half)
In the VS Code extension, in order to have a .js file lint using an open config file, we need to be able to correlate documents together. Do this by creating a workspace abstraction to hold all the documents in the C++ half. A future commit will expose workspaces to the JavaScript half. This commit should not change behavior.
1 parent a0d67b3 commit 48c9fec

File tree

5 files changed

+95
-13
lines changed

5 files changed

+95
-13
lines changed

src/c-api.cpp

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
// Copyright (C) 2020 Matthew "strager" Glazar
22
// See end of file for extended copyright information.
33

4+
#include <algorithm>
45
#include <cstddef>
6+
#include <memory>
7+
#include <quick-lint-js/assert.h>
58
#include <quick-lint-js/c-api-error-reporter.h>
69
#include <quick-lint-js/c-api.h>
710
#include <quick-lint-js/char8.h>
@@ -13,6 +16,7 @@
1316
#include <quick-lint-js/padded-string.h>
1417
#include <quick-lint-js/parse.h>
1518
#include <quick-lint-js/web-demo-location.h>
19+
#include <vector>
1620

1721
namespace quick_lint_js {
1822
namespace {
@@ -39,12 +43,34 @@ class qljs_document_base {
3943
}
4044
}
4145

46+
struct qljs_vscode_workspace {
47+
public:
48+
qljs_vscode_document* create_source_document();
49+
50+
void destroy_document(qljs_vscode_document*);
51+
52+
private:
53+
std::vector<std::unique_ptr<qljs_vscode_document>> documents_;
54+
};
55+
56+
qljs_vscode_workspace* qljs_vscode_create_workspace() {
57+
qljs_vscode_workspace* workspace = new qljs_vscode_workspace();
58+
return workspace;
59+
}
60+
61+
void qljs_vscode_destroy_workspace(qljs_vscode_workspace* workspace) {
62+
delete workspace;
63+
}
64+
4265
struct qljs_vscode_document final
4366
: public quick_lint_js::qljs_document_base<
4467
quick_lint_js::lsp_locator,
4568
quick_lint_js::c_api_error_reporter<qljs_vscode_diagnostic,
4669
quick_lint_js::lsp_locator>> {
4770
public:
71+
explicit qljs_vscode_document(qljs_vscode_workspace* workspace)
72+
: workspace(workspace) {}
73+
4874
void replace_text(int start_line, int start_character, int end_line,
4975
int end_character,
5076
quick_lint_js::string8_view replacement) {
@@ -57,14 +83,34 @@ struct qljs_vscode_document final
5783
},
5884
replacement);
5985
}
86+
87+
qljs_vscode_workspace* workspace;
6088
};
6189

62-
qljs_vscode_document* qljs_vscode_create_document(void) {
63-
qljs_vscode_document* p = new qljs_vscode_document();
64-
return p;
90+
qljs_vscode_document* qljs_vscode_workspace::create_source_document() {
91+
std::unique_ptr<qljs_vscode_document>& doc = this->documents_.emplace_back(
92+
std::make_unique<qljs_vscode_document>(this));
93+
return doc.get();
94+
}
95+
96+
void qljs_vscode_workspace::destroy_document(qljs_vscode_document* doc) {
97+
auto doc_it = std::find_if(
98+
this->documents_.begin(), this->documents_.end(),
99+
[&](const std::unique_ptr<qljs_vscode_document>& existing_doc) {
100+
return existing_doc.get() == doc;
101+
});
102+
QLJS_ASSERT(doc_it != this->documents_.end());
103+
this->documents_.erase(doc_it);
104+
}
105+
106+
qljs_vscode_document* qljs_vscode_create_source_document(
107+
qljs_vscode_workspace* workspace) {
108+
return workspace->create_source_document();
65109
}
66110

67-
void qljs_vscode_destroy_document(qljs_vscode_document* p) { delete p; }
111+
void qljs_vscode_destroy_document(qljs_vscode_document* p) {
112+
p->workspace->destroy_document(p);
113+
}
68114

69115
void qljs_vscode_replace_text(qljs_vscode_document* p, int start_line,
70116
int start_character, int end_line,

src/quick-lint-js/c-api.h

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ typedef enum qljs_severity {
1515
qljs_severity_warning = 2,
1616
} qljs_severity;
1717

18+
typedef struct qljs_vscode_workspace qljs_vscode_workspace;
1819
typedef struct qljs_vscode_document qljs_vscode_document;
20+
qljs_vscode_workspace* qljs_vscode_create_workspace();
21+
void qljs_vscode_destroy_workspace(qljs_vscode_workspace*);
22+
1923
struct qljs_vscode_diagnostic {
2024
const char* message;
2125
const char* code;
@@ -25,7 +29,8 @@ struct qljs_vscode_diagnostic {
2529
int end_line;
2630
int end_character;
2731
};
28-
qljs_vscode_document* qljs_vscode_create_document(void);
32+
qljs_vscode_document* qljs_vscode_create_source_document(
33+
qljs_vscode_workspace*);
2934
void qljs_vscode_destroy_document(qljs_vscode_document*);
3035
void qljs_vscode_replace_text(qljs_vscode_document*, int start_line,
3136
int start_character, int end_line,

test/test-c-api.cpp

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,12 @@
88
namespace quick_lint_js {
99
namespace {
1010
TEST(test_c_api_vscode, empty_document_has_no_diagnostics) {
11-
qljs_vscode_document* p = qljs_vscode_create_document();
11+
qljs_vscode_workspace* workspace = qljs_vscode_create_workspace();
12+
qljs_vscode_document* p = qljs_vscode_create_source_document(workspace);
1213
const qljs_vscode_diagnostic* diagnostics = qljs_vscode_lint(p);
1314
EXPECT_EQ(diagnostics[0].message, nullptr);
1415
qljs_vscode_destroy_document(p);
16+
qljs_vscode_destroy_workspace(workspace);
1517
}
1618

1719
TEST(test_c_api_web_demo, empty_document_has_no_diagnostics) {
@@ -22,7 +24,8 @@ TEST(test_c_api_web_demo, empty_document_has_no_diagnostics) {
2224
}
2325

2426
TEST(test_c_api_vscode, lint_error_after_text_insertion) {
25-
qljs_vscode_document* p = qljs_vscode_create_document();
27+
qljs_vscode_workspace* workspace = qljs_vscode_create_workspace();
28+
qljs_vscode_document* p = qljs_vscode_create_source_document(workspace);
2629

2730
const char8* document_text = u8"let x;let x;";
2831
qljs_vscode_replace_text(p, /*start_line=*/0, /*start_character=*/0,
@@ -41,6 +44,7 @@ TEST(test_c_api_vscode, lint_error_after_text_insertion) {
4144
EXPECT_EQ(diagnostics[0].end_character, strlen(u8"let x;let x"));
4245

4346
qljs_vscode_destroy_document(p);
47+
qljs_vscode_destroy_workspace(workspace);
4448
}
4549

4650
TEST(test_c_api_web_demo, lint_error_after_text_insertion) {
@@ -62,7 +66,8 @@ TEST(test_c_api_web_demo, lint_error_after_text_insertion) {
6266
}
6367

6468
TEST(test_c_api_vscode, lint_new_error_after_second_text_insertion) {
65-
qljs_vscode_document* p = qljs_vscode_create_document();
69+
qljs_vscode_workspace* workspace = qljs_vscode_create_workspace();
70+
qljs_vscode_document* p = qljs_vscode_create_source_document(workspace);
6671

6772
const char8* document_text = u8"let x;";
6873
qljs_vscode_replace_text(p, /*start_line=*/0, /*start_character=*/0,
@@ -88,6 +93,7 @@ TEST(test_c_api_vscode, lint_new_error_after_second_text_insertion) {
8893
EXPECT_EQ(diagnostics[0].end_character, strlen(u8"let x;let x"));
8994

9095
qljs_vscode_destroy_document(p);
96+
qljs_vscode_destroy_workspace(workspace);
9197
}
9298

9399
TEST(test_c_api_web_demo, lint_new_error_after_second_text_insertion) {
@@ -114,7 +120,8 @@ TEST(test_c_api_web_demo, lint_new_error_after_second_text_insertion) {
114120
}
115121

116122
TEST(test_c_api_vscode, diagnostic_severity) {
117-
qljs_vscode_document* p = qljs_vscode_create_document();
123+
qljs_vscode_workspace* workspace = qljs_vscode_create_workspace();
124+
qljs_vscode_document* p = qljs_vscode_create_source_document(workspace);
118125

119126
const char8* document_text = u8"let x;let x;\nundeclaredVariable;";
120127
qljs_vscode_replace_text(p, /*start_line=*/0, /*start_character=*/0,
@@ -132,6 +139,19 @@ TEST(test_c_api_vscode, diagnostic_severity) {
132139
EXPECT_EQ(diagnostics[1].severity, qljs_severity_warning);
133140

134141
qljs_vscode_destroy_document(p);
142+
qljs_vscode_destroy_workspace(workspace);
143+
}
144+
145+
TEST(test_c_api_vscode,
146+
destroying_workspace_makes_destroying_documents_unnecessary) {
147+
qljs_vscode_workspace* workspace = qljs_vscode_create_workspace();
148+
[[maybe_unused]] qljs_vscode_document* doc1 =
149+
qljs_vscode_create_source_document(workspace);
150+
[[maybe_unused]] qljs_vscode_document* doc2 =
151+
qljs_vscode_create_source_document(workspace);
152+
qljs_vscode_destroy_workspace(workspace);
153+
// Leak checkers such as AddressSantizers would report a leak and fail the
154+
// test.
135155
}
136156
}
137157
}

wasm/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if (EMSCRIPTEN)
1212
quick-lint-js-vscode
1313
PRIVATE
1414
quick-lint-js-lib
15-
"-sEXPORTED_FUNCTIONS=[\"_qljs_vscode_create_document\",\"_qljs_vscode_destroy_document\",\"_qljs_vscode_replace_text\",\"_qljs_vscode_lint\",\"_qljs_web_demo_create_document\",\"_qljs_web_demo_destroy_document\",\"_qljs_web_demo_set_text\",\"_qljs_web_demo_lint\",\"_malloc\",\"_free\"]"
15+
"-sEXPORTED_FUNCTIONS=[\"_qljs_vscode_create_workspace\",\"_qljs_vscode_destroy_workspace\",\"_qljs_vscode_create_source_document\",\"_qljs_vscode_destroy_document\",\"_qljs_vscode_replace_text\",\"_qljs_vscode_lint\",\"_qljs_web_demo_create_document\",\"_qljs_web_demo_destroy_document\",\"_qljs_web_demo_set_text\",\"_qljs_web_demo_lint\",\"_malloc\",\"_free\"]"
1616
-sASSERTIONS=0
1717
-sSTANDALONE_WASM=1
1818
--no-entry

wasm/quick-lint-js.js

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -498,8 +498,12 @@ class Process {
498498

499499
this._malloc = wrap("malloc");
500500
this._free = wrap("free");
501-
this._vscodeCreateDocument = wrap("qljs_vscode_create_document");
501+
this._vscodeCreateSourceDocument = wrap(
502+
"qljs_vscode_create_source_document"
503+
);
504+
this._vscodeCreateWorkspace = wrap("qljs_vscode_create_workspace");
502505
this._vscodeDestroyDocument = wrap("qljs_vscode_destroy_document");
506+
this._vscodeDestroyWorkspace = wrap("qljs_vscode_destroy_workspace");
503507
this._vscodeLint = wrap("qljs_vscode_lint");
504508
this._vscodeReplaceText = wrap("qljs_vscode_replace_text");
505509
this._webDemoCreateDocument = wrap("qljs_web_demo_create_document");
@@ -526,8 +530,10 @@ class Process {
526530
// Make future calls crash and also reduce memory usage.
527531
this._malloc = tainted;
528532
this._free = tainted;
529-
this._vscodeCreateDocument = tainted;
533+
this._vscodeCreateSourceDocument = tainted;
534+
this._vscodeCreateWorkspace = tainted;
530535
this._vscodeDestroyDocument = tainted;
536+
this._vscodeDestroyWorkspace = tainted;
531537
this._vscodeLint = tainted;
532538
this._vscodeReplaceText = tainted;
533539
this._webDemoCreateDocument = tainted;
@@ -552,7 +558,10 @@ class Process {
552558
class DocumentForVSCode {
553559
constructor(process) {
554560
this._process = process;
555-
this._wasmDoc = this._process._vscodeCreateDocument();
561+
this._wasmWorkspace = this._process._vscodeCreateWorkspace();
562+
this._wasmDoc = this._process._vscodeCreateSourceDocument(
563+
this._wasmWorkspace
564+
);
556565
}
557566

558567
replaceText(range, replacementText) {
@@ -637,6 +646,8 @@ class DocumentForVSCode {
637646
dispose() {
638647
this._process._vscodeDestroyDocument(this._wasmDoc);
639648
this._wasmDoc = null;
649+
this._process._vscodeDestroyWorkspace(this._wasmWorkspace);
650+
this._wasmWorkspace = null;
640651
}
641652

642653
get process() {

0 commit comments

Comments
 (0)