feat: enable LSP semantic token highlighting by default
This commit is contained in:
+507
-3
@@ -11,7 +11,7 @@ use std::{
|
||||
use serde_json::{Value, json};
|
||||
use vamp::{
|
||||
editor::{Cursor, Input, Mode},
|
||||
plugin::DiagnosticSeverity,
|
||||
plugin::{DiagnosticSeverity, SemanticTokenKind, SemanticTokenModifier, TextPosition},
|
||||
session::EditorSession,
|
||||
};
|
||||
|
||||
@@ -67,6 +67,7 @@ end
|
||||
initialize_id = nil
|
||||
initialized_sent = false
|
||||
held = []
|
||||
semantic_held = []
|
||||
definition_count = 0
|
||||
source_uri = uri(source)
|
||||
documents = {}
|
||||
@@ -79,9 +80,16 @@ loop do
|
||||
when 'incremental' then {'openClose' => true, 'change' => 2, 'save' => {'includeText' => true}}
|
||||
else {'openClose' => true, 'change' => 1, 'save' => {'includeText' => true}}
|
||||
end
|
||||
result(initialize_id, {'capabilities' => {'positionEncoding' => 'utf-16',
|
||||
capabilities = {'positionEncoding' => 'utf-16',
|
||||
'textDocumentSync' => sync, 'definitionProvider' => ['unsupported', 'controlled-unsupported'].include?(mode) ? false : {'workDoneProgress' => false},
|
||||
'hoverProvider' => ['hover-unsupported', 'controlled-unsupported'].include?(mode) ? false : {'workDoneProgress' => false}}})
|
||||
'hoverProvider' => ['hover-unsupported', 'controlled-unsupported'].include?(mode) ? false : {'workDoneProgress' => false}}
|
||||
if mode.include?('semantic')
|
||||
capabilities['semanticTokensProvider'] = {
|
||||
'legend' => {'tokenTypes' => ['custom', 'variable', 'function', 'comment', 'type'],
|
||||
'tokenModifiers' => ['custom', 'readonly', 'declaration', 'async', 'defaultLibrary']},
|
||||
'range' => true, 'full' => mode.include?('range') ? false : {'delta' => true}}
|
||||
end
|
||||
result(initialize_id, {'capabilities' => capabilities})
|
||||
initialized_sent = true
|
||||
end
|
||||
if !held.empty? && File.exist?(release) && ['stale', 'hover-stale'].include?(mode)
|
||||
@@ -103,6 +111,12 @@ loop do
|
||||
'diagnostics' => command['action'] == 'clear' ? [] :
|
||||
[{'range' => range(0, 2, 4), 'severity' => 2, 'message' => command.fetch('message')}]}
|
||||
emit({'method' => 'textDocument/publishDiagnostics', 'params' => params})
|
||||
when 'semantic-result'
|
||||
request = semantic_held.delete_at(command.fetch('index', 0))
|
||||
raise 'no held semantic request' unless request
|
||||
emit({'id' => request.fetch('id')}.merge(command.fetch('response')))
|
||||
when 'refresh'
|
||||
emit({'id' => "refresh-#{control_id}", 'method' => 'workspace/semanticTokens/refresh'})
|
||||
when 'die'
|
||||
exit! 7
|
||||
when 'malformed'
|
||||
@@ -209,6 +223,19 @@ loop do
|
||||
hover = contents.nil? ? nil : {'contents' => contents, 'range' => range(mode == 'hover-invalid-range' ? 999 : 0, 2, 4)}
|
||||
result(frame['id'], hover)
|
||||
end
|
||||
when 'textDocument/semanticTokens/full', 'textDocument/semanticTokens/range'
|
||||
if mode.start_with?('controlled')
|
||||
semantic_held << frame
|
||||
else
|
||||
result(frame['id'], {'data' => [
|
||||
0, 0, 1, 4, 0,
|
||||
0, 1, 2, 1, 2,
|
||||
0, 2, 1, 2, 12,
|
||||
0, 1, 1, 3, 16,
|
||||
0, 1, 1, 0, 1,
|
||||
1, 0, 4, 4, 0
|
||||
]})
|
||||
end
|
||||
when 'shutdown'
|
||||
result(frame['id'], nil)
|
||||
when 'exit'
|
||||
@@ -1621,3 +1648,480 @@ fn lsp_python_stack_does_not_fall_back_to_ruff_when_ty_lacks_position_capabiliti
|
||||
assert_eq!(fixture.worker_count(worker, "textDocument/hover"), 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn semantic_reply(session: &mut EditorSession, fixture: &Fixture, id: u64, response: Value) {
|
||||
fixture.control(
|
||||
0,
|
||||
id,
|
||||
json!({"action": "semantic-result", "response": response}),
|
||||
);
|
||||
wait_protocol(fixture, || fixture.acknowledged(0, id));
|
||||
session.poll();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_semantics_are_automatic_full_or_whole_document_range_with_canonical_legends() {
|
||||
for mode in ["semantic-auto", "semantic-range"] {
|
||||
let fixture = Fixture::new();
|
||||
fs::write(&fixture.source, "A😀e\u{301}Z\nnext\n").unwrap();
|
||||
let mut session = fixture.open(mode);
|
||||
until(&mut session, &fixture, |session| {
|
||||
session.editor().semantic_tokens().len() == 5
|
||||
&& session.editor().diagnostics().count() == 2
|
||||
});
|
||||
let tokens = session.editor().semantic_tokens();
|
||||
assert_eq!(tokens[0].kind, SemanticTokenKind::Type);
|
||||
assert_eq!(tokens[1].kind, SemanticTokenKind::Variable);
|
||||
assert_eq!(tokens[1].start, TextPosition { row: 0, column: 1 });
|
||||
assert_eq!(tokens[1].end, TextPosition { row: 0, column: 2 });
|
||||
assert_eq!(tokens[1].modifiers, SemanticTokenModifier::Readonly as u16);
|
||||
assert_eq!(tokens[2].kind, SemanticTokenKind::Function);
|
||||
assert_eq!(
|
||||
tokens[2].modifiers,
|
||||
SemanticTokenModifier::Declaration as u16 | SemanticTokenModifier::Async as u16
|
||||
);
|
||||
assert_eq!(tokens[3].kind, SemanticTokenKind::Comment);
|
||||
assert_eq!(
|
||||
tokens[3].modifiers,
|
||||
SemanticTokenModifier::DefaultLibrary as u16
|
||||
);
|
||||
assert_eq!(tokens[2].start, tokens[3].start);
|
||||
assert_eq!(tokens[2].end, tokens[3].end);
|
||||
assert_eq!(tokens[4].start, TextPosition { row: 1, column: 0 });
|
||||
assert_eq!(tokens[4].end, TextPosition { row: 1, column: 4 });
|
||||
let frames = fixture.frames();
|
||||
let request = frames
|
||||
.iter()
|
||||
.find(|frame| {
|
||||
frame["method"]
|
||||
== if mode == "semantic-range" {
|
||||
"textDocument/semanticTokens/range"
|
||||
} else {
|
||||
"textDocument/semanticTokens/full"
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
if mode == "semantic-range" {
|
||||
assert_eq!(
|
||||
request["params"]["range"],
|
||||
json!({"start": {"line": 0, "character": 0},
|
||||
"end": {"line": 2, "character": 0}})
|
||||
);
|
||||
assert_eq!(fixture.method_count("textDocument/semanticTokens/full"), 0);
|
||||
} else {
|
||||
assert!(request["params"].get("range").is_none());
|
||||
assert_eq!(fixture.method_count("textDocument/semanticTokens/range"), 0);
|
||||
}
|
||||
assert_eq!(
|
||||
fixture.method_count("textDocument/semanticTokens/full/delta"),
|
||||
0
|
||||
);
|
||||
let initialize = frames
|
||||
.iter()
|
||||
.find(|frame| frame["method"] == "initialize")
|
||||
.unwrap();
|
||||
assert_eq!(
|
||||
initialize["params"]["capabilities"]["textDocument"]["semanticTokens"]["requests"]["full"],
|
||||
true
|
||||
);
|
||||
assert_eq!(
|
||||
initialize["params"]["capabilities"]["workspace"]["semanticTokens"]["refreshSupport"],
|
||||
true
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_semantics_never_request_unsupported_primary_or_supporting_sibling() {
|
||||
for modes in [
|
||||
["controlled", "controlled-semantic"],
|
||||
["controlled-semantic", "controlled-semantic"],
|
||||
] {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open_stack(modes, false);
|
||||
until(&mut session, &fixture, |_| {
|
||||
(0..2).all(|worker| fixture.worker_count(worker, "textDocument/didOpen") == 1)
|
||||
&& (modes[0] == "controlled"
|
||||
|| fixture.worker_count(0, "textDocument/semanticTokens/full") == 1)
|
||||
});
|
||||
for worker in 0..2 {
|
||||
fixture.control(worker, 1, json!({"action": "refresh"}));
|
||||
}
|
||||
until(&mut session, &fixture, |_| {
|
||||
(0..2).all(|worker| fixture.acknowledged(worker, 1))
|
||||
});
|
||||
assert_eq!(
|
||||
fixture.worker_count(1, "textDocument/semanticTokens/full"),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
fixture.worker_count(1, "textDocument/semanticTokens/range"),
|
||||
0
|
||||
);
|
||||
if modes[0] == "controlled" {
|
||||
assert_eq!(
|
||||
fixture.worker_count(0, "textDocument/semanticTokens/full"),
|
||||
0
|
||||
);
|
||||
assert_eq!(
|
||||
fixture.worker_count(0, "textDocument/semanticTokens/range"),
|
||||
0
|
||||
);
|
||||
} else {
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
2,
|
||||
json!({"result": {"data": [0, 0, 1, 1, 0]}}),
|
||||
);
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.worker_count(0, "textDocument/semanticTokens/full") == 2
|
||||
});
|
||||
assert!(session.editor().semantic_tokens().is_empty());
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
3,
|
||||
json!({"result": {"data": [0, 0, 1, 1, 0]}}),
|
||||
);
|
||||
assert_eq!(session.editor().semantic_tokens().len(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_semantics_coalesce_changed_text_and_refresh_without_decoding_stale_payloads() {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open("controlled-semantic");
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/semanticTokens/full") == 1
|
||||
});
|
||||
session.handle(Input::Char('A'));
|
||||
session.handle(Input::Char('!'));
|
||||
session.handle(Input::Char('?'));
|
||||
session.handle(Input::Escape);
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.frames().iter().any(|frame| {
|
||||
frame["method"] == "textDocument/didChange"
|
||||
&& frame["params"]["contentChanges"][0]["text"] == "A😀e\u{301}Z!?\n"
|
||||
})
|
||||
});
|
||||
assert_eq!(fixture.method_count("textDocument/semanticTokens/full"), 1);
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
1,
|
||||
json!({"result": {"data": ["stale malformed data"]}}),
|
||||
);
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/semanticTokens/full") == 2
|
||||
});
|
||||
assert!(
|
||||
!session
|
||||
.editor()
|
||||
.message()
|
||||
.contains("semantic tokens failed")
|
||||
);
|
||||
assert!(session.editor().semantic_tokens().is_empty());
|
||||
fixture.control(0, 2, json!({"action": "refresh"}));
|
||||
until(&mut session, &fixture, |_| fixture.acknowledged(0, 2));
|
||||
assert_eq!(fixture.method_count("textDocument/semanticTokens/full"), 2);
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
3,
|
||||
json!({"result": {"data": [0, 0, 1, 4, 0]}}),
|
||||
);
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/semanticTokens/full") == 3
|
||||
});
|
||||
assert!(session.editor().semantic_tokens().is_empty());
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
4,
|
||||
json!({"result": {"data": [0, 6, 2, 1, 0]}}),
|
||||
);
|
||||
let tokens = session.editor().semantic_tokens();
|
||||
assert_eq!(tokens.len(), 1);
|
||||
assert_eq!(tokens[0].start, TextPosition { row: 0, column: 4 });
|
||||
assert_eq!(tokens[0].end, TextPosition { row: 0, column: 6 });
|
||||
let frames = fixture.frames();
|
||||
let change = frames
|
||||
.iter()
|
||||
.rposition(|frame| frame["method"] == "textDocument/didChange")
|
||||
.unwrap();
|
||||
let request = frames
|
||||
.iter()
|
||||
.rposition(|frame| frame["method"] == "textDocument/semanticTokens/full")
|
||||
.unwrap();
|
||||
assert!(change < request);
|
||||
assert!(frames.iter().any(|frame| frame["id"] == "refresh-2"
|
||||
&& frame["result"].is_null()
|
||||
&& frame.get("result").is_some()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_semantics_discard_queued_same_revision_rename_and_close_outputs() {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open("controlled-semantic");
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/semanticTokens/full") == 1
|
||||
});
|
||||
fixture.control(
|
||||
0,
|
||||
1,
|
||||
json!({"action": "semantic-result",
|
||||
"response": {"result": {"data": [0, 0, 1, 1, 0]}}}),
|
||||
);
|
||||
wait_protocol(&fixture, || fixture.acknowledged(0, 1));
|
||||
let revision = session.editor().context().revision;
|
||||
let renamed = fixture.root.join("renamed.rs");
|
||||
session.execute(&format!("w {}", renamed.display()));
|
||||
assert_eq!(session.editor().context().revision, revision);
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/semanticTokens/full") == 2
|
||||
&& fixture.method_count("textDocument/didClose") == 1
|
||||
});
|
||||
assert!(session.editor().semantic_tokens().is_empty());
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
2,
|
||||
json!({"result": {"data": [0, 0, 1, 4, 0]}}),
|
||||
);
|
||||
assert_eq!(
|
||||
session.editor().semantic_tokens()[0].kind,
|
||||
SemanticTokenKind::Type
|
||||
);
|
||||
fixture.control(0, 3, json!({"action": "refresh"}));
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.acknowledged(0, 3) && fixture.method_count("textDocument/semanticTokens/full") == 3
|
||||
});
|
||||
let renamed_again = fixture.root.join("renamed-again.rs");
|
||||
session.execute(&format!("w {}", renamed_again.display()));
|
||||
assert_eq!(session.editor().context().revision, revision);
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/didClose") == 2
|
||||
});
|
||||
assert_eq!(fixture.method_count("textDocument/semanticTokens/full"), 3);
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
4,
|
||||
json!({"result": {"data": ["old path malformed"]}}),
|
||||
);
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/semanticTokens/full") == 4
|
||||
});
|
||||
assert!(session.editor().semantic_tokens().is_empty());
|
||||
assert!(
|
||||
!session
|
||||
.editor()
|
||||
.message()
|
||||
.contains("semantic tokens failed")
|
||||
);
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
5,
|
||||
json!({"result": {"data": [0, 0, 1, 4, 0]}}),
|
||||
);
|
||||
fixture.control(0, 6, json!({"action": "refresh"}));
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.acknowledged(0, 6) && fixture.method_count("textDocument/semanticTokens/full") == 5
|
||||
});
|
||||
fixture.control(
|
||||
0,
|
||||
7,
|
||||
json!({"action": "semantic-result",
|
||||
"response": {"result": {"data": [0, 0, 1, 1, 0]}}}),
|
||||
);
|
||||
wait_protocol(&fixture, || fixture.acknowledged(0, 7));
|
||||
session.execute("bdelete 1");
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/didClose") == 3
|
||||
});
|
||||
assert!(!session.editor().has_buffer(1));
|
||||
assert!(session.editor().semantic_tokens().is_empty());
|
||||
session.execute(&format!("edit {}", renamed_again.display()));
|
||||
let reopened = session.editor().buffer_id();
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/semanticTokens/full") == 6
|
||||
});
|
||||
session.execute(&format!("bdelete {reopened}"));
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/didClose") == 4
|
||||
});
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
8,
|
||||
json!({"result": {"data": ["closed malformed"]}}),
|
||||
);
|
||||
assert!(session.editor().semantic_tokens().is_empty());
|
||||
assert!(
|
||||
!session
|
||||
.editor()
|
||||
.message()
|
||||
.contains("semantic tokens failed")
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_semantic_empty_null_and_feature_errors_keep_diagnostics_hover_and_definition() {
|
||||
let fixture = Fixture::new();
|
||||
let mut session = fixture.open("controlled-semantic");
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.method_count("textDocument/semanticTokens/full") == 1
|
||||
});
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
1,
|
||||
json!({"result": {"data": [0, 0, 1, 1, 0]}}),
|
||||
);
|
||||
assert_eq!(session.editor().semantic_tokens().len(), 1);
|
||||
publish(
|
||||
&mut session,
|
||||
&fixture,
|
||||
0,
|
||||
2,
|
||||
json!({"action": "publish", "message": "lint intact"}),
|
||||
&["lint intact"],
|
||||
);
|
||||
for (index, response) in [
|
||||
json!({"result": null}),
|
||||
json!({"result": {"data": []}}),
|
||||
json!({"result": {"data": [0, 0, 1, 1]}}),
|
||||
json!({"result": {"data": [0, 2, 1, 0, 0]}}),
|
||||
json!({"result": {"data": [0, 0, 1, 1, 32]}}),
|
||||
json!({"error": {"code": -32603, "message": "feature error"}}),
|
||||
json!({"error": {"code": "bad", "message": "malformed error"}}),
|
||||
json!({"result": null, "error": {"code": -1, "message": "both"}}),
|
||||
json!({}),
|
||||
]
|
||||
.into_iter()
|
||||
.enumerate()
|
||||
{
|
||||
let id = 3 + index as u64 * 4;
|
||||
fixture.control(0, id, json!({"action": "refresh"}));
|
||||
until(&mut session, &fixture, |_| fixture.acknowledged(0, id));
|
||||
assert_eq!(
|
||||
session.editor().semantic_tokens().len(),
|
||||
1,
|
||||
"same-text refresh retains the existing overlay until its result"
|
||||
);
|
||||
semantic_reply(&mut session, &fixture, id + 1, response);
|
||||
assert!(session.editor().semantic_tokens().is_empty());
|
||||
assert_eq!(diagnostic_messages(&session), ["lint intact"]);
|
||||
if index >= 2 {
|
||||
assert!(
|
||||
session
|
||||
.editor()
|
||||
.message()
|
||||
.contains("semantic tokens failed")
|
||||
);
|
||||
}
|
||||
fixture.control(0, id + 2, json!({"action": "refresh"}));
|
||||
until(&mut session, &fixture, |_| fixture.acknowledged(0, id + 2));
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
id + 3,
|
||||
json!({"result": {"data": [0, 0, 1, 1, 0]}}),
|
||||
);
|
||||
assert_eq!(session.editor().semantic_tokens().len(), 1);
|
||||
}
|
||||
session.execute("lsp.hover");
|
||||
until(&mut session, &fixture, |session| {
|
||||
session.editor().hover().is_some()
|
||||
});
|
||||
assert_eq!(diagnostic_messages(&session), ["lint intact"]);
|
||||
session.execute("lsp.definition");
|
||||
until(&mut session, &fixture, |session| {
|
||||
session.editor().buffer_id() != 1
|
||||
});
|
||||
assert_eq!(session.editor().lines(), &["disk", ""]);
|
||||
session.execute("buffer 1");
|
||||
assert_eq!(diagnostic_messages(&session), ["lint intact"]);
|
||||
assert_eq!(session.editor().semantic_tokens().len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lsp_semantic_primary_retirement_clears_active_and_parked_without_sibling_takeover() {
|
||||
for failing in 0..2 {
|
||||
let fixture = Fixture::new();
|
||||
fs::write(&fixture.target, "A😀e\u{301}Z\n").unwrap();
|
||||
let mut session = fixture.open_stack(["controlled-semantic"; 2], false);
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.worker_count(0, "textDocument/semanticTokens/full") == 1
|
||||
&& fixture.worker_count(1, "textDocument/didOpen") == 1
|
||||
});
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
1,
|
||||
json!({"result": {"data": [0, 0, 1, 1, 0]}}),
|
||||
);
|
||||
for worker in 0..2 {
|
||||
fixture.control(
|
||||
worker,
|
||||
2,
|
||||
json!({"action": "publish",
|
||||
"message": if worker == failing { "retiring" } else { "healthy" }}),
|
||||
);
|
||||
}
|
||||
until(&mut session, &fixture, |_| {
|
||||
(0..2).all(|worker| fixture.acknowledged(worker, 2))
|
||||
});
|
||||
session.execute(&format!("edit {}", fixture.target.display()));
|
||||
until(&mut session, &fixture, |_| {
|
||||
fixture.worker_count(0, "textDocument/semanticTokens/full") == 2
|
||||
&& fixture.worker_count(1, "textDocument/didOpen") == 2
|
||||
});
|
||||
semantic_reply(
|
||||
&mut session,
|
||||
&fixture,
|
||||
3,
|
||||
json!({"result": {"data": [0, 0, 1, 1, 0]}}),
|
||||
);
|
||||
let target_uri = fixture
|
||||
.worker_frames(0)
|
||||
.into_iter()
|
||||
.filter(|frame| frame["method"] == "textDocument/didOpen")
|
||||
.nth(1)
|
||||
.unwrap()["params"]["textDocument"]["uri"]
|
||||
.clone();
|
||||
for worker in 0..2 {
|
||||
fixture.control(
|
||||
worker,
|
||||
4,
|
||||
json!({"action": "publish", "uri": target_uri,
|
||||
"message": if worker == failing { "retiring" } else { "healthy" }}),
|
||||
);
|
||||
}
|
||||
until(&mut session, &fixture, |_| {
|
||||
(0..2).all(|worker| fixture.acknowledged(worker, 4))
|
||||
});
|
||||
fixture.control(failing, 5, json!({"action": "die"}));
|
||||
until(&mut session, &fixture, |session| {
|
||||
session.editor().message().contains("server stopped")
|
||||
&& diagnostic_messages(session) == ["healthy"]
|
||||
});
|
||||
assert_eq!(
|
||||
session.editor().semantic_tokens().len(),
|
||||
usize::from(failing != 0)
|
||||
);
|
||||
session.execute("buffer 1");
|
||||
assert_eq!(diagnostic_messages(&session), ["healthy"]);
|
||||
assert_eq!(
|
||||
session.editor().semantic_tokens().len(),
|
||||
usize::from(failing != 0)
|
||||
);
|
||||
assert_eq!(
|
||||
fixture.worker_count(1, "textDocument/semanticTokens/full"),
|
||||
0
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user