Skip to content

Commit a734a05

Browse files
authored
Lock the diagnostics highlight matching logic so that no race conditions are introduced (#1211)
1 parent e448f1f commit a734a05

File tree

1 file changed

+66
-65
lines changed

1 file changed

+66
-65
lines changed

src/language_client.rs

Lines changed: 66 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -527,75 +527,76 @@ impl LanguageClient {
527527
})?;
528528

529529
if !self.get_config(|c| c.is_nvim)? {
530-
// Clear old highlights.
531-
let ids = self.get_state(|state| state.highlight_match_ids.clone())?;
532-
self.vim()?
533-
.rpcclient
534-
.notify("s:MatchDelete", json!([ids]))?;
535-
536-
// Group diagnostics by severity so we can highlight them
537-
// in a single call.
538-
let mut match_groups: HashMap<_, Vec<_>> = HashMap::new();
539-
540-
for dn in diagnostics {
541-
let severity = dn.severity.unwrap_or(DiagnosticSeverity::Hint).to_int()?;
542-
match_groups
543-
.entry(severity)
544-
.or_insert_with(Vec::new)
545-
.push(dn);
546-
}
530+
// this needs to be in this locked block so that notifications that arrive too close to
531+
// each other do not have the chance to cause any race conditions.
532+
self.update_state(|state| {
533+
// Clear old highlights.
534+
let ids = state.highlight_match_ids.clone();
535+
state.vim.rpcclient.notify("s:MatchDelete", json!([ids]))?;
536+
537+
// Group diagnostics by severity so we can highlight them
538+
// in a single call.
539+
let mut match_groups: HashMap<_, Vec<_>> = HashMap::new();
540+
541+
for dn in diagnostics {
542+
let severity = dn.severity.unwrap_or(DiagnosticSeverity::Hint).to_int()?;
543+
match_groups
544+
.entry(severity)
545+
.or_insert_with(Vec::new)
546+
.push(dn);
547+
}
547548

548-
let mut new_match_ids = Vec::new();
549+
let mut new_match_ids = Vec::new();
549550

550-
for (severity, dns) in match_groups {
551-
let hl_group = diagnostics_display
552-
.get(&severity)
553-
.ok_or_else(|| anyhow!("Failed to get display"))?
554-
.texthl
555-
.clone();
556-
let ranges: Vec<Vec<_>> = dns
557-
.iter()
558-
.flat_map(|dn| {
559-
if dn.range.start.line == dn.range.end.line {
560-
let length = dn.range.end.character - dn.range.start.character;
561-
// Vim line numbers are 1 off
562-
// `matchaddpos` expects an array of [line, col, length]
563-
// for each match.
564-
vec![vec![
565-
dn.range.start.line + 1,
566-
dn.range.start.character + 1,
567-
length,
568-
]]
569-
} else {
570-
let mut middle_lines: Vec<_> = (dn.range.start.line + 1
571-
..dn.range.end.line)
572-
.map(|l| vec![l + 1])
573-
.collect();
574-
let start_line = vec![
575-
dn.range.start.line + 1,
576-
dn.range.start.character + 1,
577-
999_999, //Clear to the end of the line
578-
];
579-
let end_line =
580-
vec![dn.range.end.line + 1, 1, dn.range.end.character + 1];
581-
middle_lines.push(start_line);
582-
// For a multi-ringe range ending at the exact start of the last line,
583-
// don't highlight the first character of the last line.
584-
if dn.range.end.character > 0 {
585-
middle_lines.push(end_line);
551+
for (severity, dns) in match_groups {
552+
let hl_group = diagnostics_display
553+
.get(&severity)
554+
.ok_or_else(|| anyhow!("Failed to get display"))?
555+
.texthl
556+
.clone();
557+
let ranges: Vec<Vec<_>> = dns
558+
.iter()
559+
.flat_map(|dn| {
560+
if dn.range.start.line == dn.range.end.line {
561+
let length = dn.range.end.character - dn.range.start.character;
562+
// Vim line numbers are 1 off
563+
// `matchaddpos` expects an array of [line, col, length]
564+
// for each match.
565+
vec![vec![
566+
dn.range.start.line + 1,
567+
dn.range.start.character + 1,
568+
length,
569+
]]
570+
} else {
571+
let mut middle_lines: Vec<_> = (dn.range.start.line + 1
572+
..dn.range.end.line)
573+
.map(|l| vec![l + 1])
574+
.collect();
575+
let start_line = vec![
576+
dn.range.start.line + 1,
577+
dn.range.start.character + 1,
578+
999_999, //Clear to the end of the line
579+
];
580+
let end_line =
581+
vec![dn.range.end.line + 1, 1, dn.range.end.character + 1];
582+
middle_lines.push(start_line);
583+
// For a multi-ringe range ending at the exact start of the last line,
584+
// don't highlight the first character of the last line.
585+
if dn.range.end.character > 0 {
586+
middle_lines.push(end_line);
587+
}
588+
middle_lines
586589
}
587-
middle_lines
588-
}
589-
})
590-
.collect();
590+
})
591+
.collect();
592+
593+
let match_id = state
594+
.vim
595+
.rpcclient
596+
.call("matchaddpos", json!([hl_group, ranges]))?;
597+
new_match_ids.push(match_id);
598+
}
591599

592-
let match_id = self
593-
.vim()?
594-
.rpcclient
595-
.call("matchaddpos", json!([hl_group, ranges]))?;
596-
new_match_ids.push(match_id);
597-
}
598-
self.update_state(|state| {
599600
state.highlight_match_ids = new_match_ids;
600601
Ok(())
601602
})?;

0 commit comments

Comments
 (0)