Skip to content

Commit 0c5c1a6

Browse files
committed
refactor(lsp): use callbacks instead of string* for config items
We need to run code only if a config item changes. This isn't possible with the current interface of lsp_workspace_configuration, which only tells us the latest value of configs. Refactor lsp_workspace_configuration's interface to take callback functions instead output string pointers.
1 parent 89c0bd0 commit 0c5c1a6

File tree

4 files changed

+49
-27
lines changed

4 files changed

+49
-27
lines changed

src/lsp-server.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,9 @@ linting_lsp_server_handler::linting_lsp_server_handler(
111111
tracer_(tracer) {
112112
this->workspace_configuration_.add_item(
113113
u8"quick-lint-js.tracing-directory"sv,
114-
&this->server_config_.tracing_directory);
114+
[this](std::string_view new_value) {
115+
this->server_config_.tracing_directory = new_value;
116+
});
115117
}
116118

117119
void linting_lsp_server_handler::handle_request(

src/lsp-workspace-configuration.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
using namespace std::literals::string_view_literals;
1717

1818
namespace quick_lint_js {
19-
void lsp_workspace_configuration::add_item(string8_view name,
20-
std::string* out_value) {
19+
void lsp_workspace_configuration::add_item(
20+
string8_view name, std::function<void(std::string_view)>&& callback) {
2121
this->items_.push_back(item{
2222
.name = name,
23-
.value = out_value,
23+
.callback = std::move(callback),
2424
});
2525
}
2626

@@ -83,12 +83,12 @@ bool lsp_workspace_configuration::process_response(
8383
if (value.get(string_value) != ::simdjson::SUCCESS) {
8484
return false;
8585
}
86-
*spec_it->value = string_value;
86+
spec_it->callback(string_value);
8787
break;
8888
}
8989

9090
case ::simdjson::ondemand::json_type::null:
91-
spec_it->value->clear();
91+
spec_it->callback(std::string_view());
9292
break;
9393

9494
default:

src/quick-lint-js/lsp-workspace-configuration.h

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
// No LSP on the web.
99
#else
1010

11+
#include <functional>
1112
#include <quick-lint-js/char8.h>
1213
#include <quick-lint-js/lsp-endpoint.h>
1314
#include <simdjson.h>
@@ -23,11 +24,12 @@ class lsp_workspace_configuration {
2324
public:
2425
// Register a configuration setting.
2526
//
26-
// out_value is stored; *out_value will be modified by process_response.
27+
// callback is later called by process_response.
2728
//
2829
// name must be have global lifetime (e.g. be a compile-time string).
2930
// name must be a JSON-encoded string (without surrounding quotation marks).
30-
void add_item(string8_view name, std::string* out_value);
31+
void add_item(string8_view name,
32+
std::function<void(std::string_view)>&& callback);
3133

3234
// Create a workspace/configuration JSON-RPC request to send to the LSP
3335
// client.
@@ -40,7 +42,7 @@ class lsp_workspace_configuration {
4042
private:
4143
struct item {
4244
string8_view name;
43-
std::string* value;
45+
std::function<void(std::string_view)> callback;
4446
};
4547
std::vector<item> items_;
4648
};

test/test-lsp-workspace-configuration.cpp

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,10 @@ TEST(test_lsp_workspace_configuration, empty_config_request) {
5555
}
5656

5757
TEST(test_lsp_workspace_configuration, config_request_with_three_items) {
58-
std::string items[3];
5958
lsp_workspace_configuration config;
60-
config.add_item(u8"first"sv, &items[0]);
61-
config.add_item(u8"second"sv, &items[1]);
62-
config.add_item(u8"third"sv, &items[2]);
59+
config.add_item(u8"first"sv, [](std::string_view) {});
60+
config.add_item(u8"second"sv, [](std::string_view) {});
61+
config.add_item(u8"third"sv, [](std::string_view) {});
6362

6463
byte_buffer request_json;
6564
config.build_request(77, request_json);
@@ -85,9 +84,15 @@ TEST(test_lsp_workspace_configuration, empty_config_response) {
8584
TEST(test_lsp_workspace_configuration, config_response_with_strings) {
8685
std::string items[3];
8786
lsp_workspace_configuration config;
88-
config.add_item(u8"first"sv, &items[0]);
89-
config.add_item(u8"second"sv, &items[1]);
90-
config.add_item(u8"third"sv, &items[2]);
87+
config.add_item(u8"first"sv, [&items](std::string_view new_value) {
88+
items[0] = new_value;
89+
});
90+
config.add_item(u8"second"sv, [&items](std::string_view new_value) {
91+
items[1] = new_value;
92+
});
93+
config.add_item(u8"third"sv, [&items](std::string_view new_value) {
94+
items[2] = new_value;
95+
});
9196

9297
easy_simdjson_parser result(
9398
R"(["firstval", "secondval", "thirdval"])"_padded);
@@ -102,43 +107,56 @@ TEST(test_lsp_workspace_configuration, config_response_with_strings) {
102107

103108
TEST(test_lsp_workspace_configuration,
104109
empty_config_response_with_added_items_fails) {
105-
std::string myitem = "originalvalue";
106110
lsp_workspace_configuration config;
107-
config.add_item(u8"myitem"sv, &myitem);
111+
bool myitem_callback_called = false;
112+
config.add_item(u8"myitem"sv, [&myitem_callback_called](
113+
std::string_view new_value) {
114+
myitem_callback_called = true;
115+
ADD_FAILURE() << "myitem callback should not have been called; new_value="
116+
<< new_value;
117+
});
108118

109119
easy_simdjson_parser result("[]"_padded);
110120
ASSERT_EQ(result.error, ::simdjson::SUCCESS);
111121
bool ok = config.process_response(result.value);
112122
ASSERT_FALSE(ok);
113123

114-
EXPECT_EQ(myitem, "originalvalue") << "item should not change value";
124+
EXPECT_FALSE(myitem_callback_called);
115125
}
116126

117127
TEST(test_lsp_workspace_configuration,
118-
more_values_than_config_fails_but_sets_values_anyway) {
119-
std::string myitem;
128+
more_values_than_config_fails_but_calls_callback_anyway) {
120129
lsp_workspace_configuration config;
121-
config.add_item(u8"myitem"sv, &myitem);
130+
bool myitem_callback_called = false;
131+
config.add_item(u8"myitem"sv,
132+
[&myitem_callback_called](std::string_view new_value) {
133+
myitem_callback_called = true;
134+
EXPECT_EQ(new_value, "val");
135+
});
122136

123137
easy_simdjson_parser result(R"(["val", "otherval"])"_padded);
124138
ASSERT_EQ(result.error, ::simdjson::SUCCESS);
125139
bool ok = config.process_response(result.value);
126140
ASSERT_FALSE(ok);
127141

128-
EXPECT_EQ(myitem, "val");
142+
EXPECT_TRUE(myitem_callback_called);
129143
}
130144

131-
TEST(test_lsp_workspace_configuration, null_clears_string) {
132-
std::string myitem = "hello";
145+
TEST(test_lsp_workspace_configuration, null_is_coerced_to_empty_string) {
133146
lsp_workspace_configuration config;
134-
config.add_item(u8"myitem"sv, &myitem);
147+
bool myitem_callback_called = false;
148+
config.add_item(u8"myitem"sv,
149+
[&myitem_callback_called](std::string_view new_value) {
150+
myitem_callback_called = true;
151+
EXPECT_EQ(new_value, "");
152+
});
135153

136154
easy_simdjson_parser result(R"([null])"_padded);
137155
ASSERT_EQ(result.error, ::simdjson::SUCCESS);
138156
bool ok = config.process_response(result.value);
139157
EXPECT_TRUE(ok);
140158

141-
EXPECT_EQ(myitem, "");
159+
EXPECT_TRUE(myitem_callback_called);
142160
}
143161

144162
TEST(test_lsp_workspace_configuration, non_array_config_response_fails) {

0 commit comments

Comments
 (0)