Skip to content

Commit 85c5a99

Browse files
committed
Update signs only at visible lines.
1 parent d2bf188 commit 85c5a99

File tree

6 files changed

+112
-75
lines changed

6 files changed

+112
-75
lines changed

autoload/LSP.vim

+8
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,11 @@ function! LSP#range_end_line() abort
3030

3131
return getpos("'>")[1]
3232
endfunction
33+
34+
function! LSP#visible_line_start() abort
35+
return line('w0')
36+
endfunction
37+
38+
function! LSP#visible_line_end() abort
39+
return line('w$')
40+
endfunction

autoload/LanguageClient.vim

+2
Original file line numberDiff line numberDiff line change
@@ -742,6 +742,8 @@ function! LanguageClient#handleCursorMoved() abort
742742
\ 'buftype': &buftype,
743743
\ 'filename': LSP#filename(),
744744
\ 'line': l:cursor_line,
745+
\ 'LSP#visible_line_start()': LSP#visible_line_start(),
746+
\ 'LSP#visible_line_end()': LSP#visible_line_end(),
745747
\ })
746748
catch
747749
call s:Debug('LanguageClient caught exception: ' . string(v:exception))

src/languageclient.rs

+68-53
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ impl State {
330330
Ok(())
331331
}
332332

333-
fn display_diagnostics(&mut self, filename: &str, diagnostics: &[Diagnostic]) -> Result<()> {
333+
fn process_diagnostics(&mut self, filename: &str, diagnostics: &[Diagnostic]) -> Result<()> {
334334
// Line diagnostics.
335335
self.update(|state| {
336336
state
@@ -360,38 +360,36 @@ impl State {
360360
})?;
361361

362362
// Signs.
363-
if self.text_documents.contains_key(filename) {
364-
let text = self.text_documents
363+
{
364+
let lines: Option<Vec<_>> = self.text_documents
365365
.get(filename)
366-
.ok_or_else(|| format_err!("TextDocumentItem not found! filename: {}", filename))?
367-
.text
368-
.clone();
369-
let lines: Vec<&str> = text.lines().collect();
370-
let mut signs: Vec<_> = diagnostics
371-
.iter()
372-
.map(|dn| {
373-
let line = dn.range.start.line;
374-
let text = lines
375-
.get(line as usize)
376-
.map(|l| l.to_string())
377-
.unwrap_or_default();
378-
Some(Sign::new(line + 1, text, dn.severity))
379-
})
380-
.collect();
366+
.map(|d| d.text.lines().collect());
367+
let mut signs = if let Some(lines) = lines {
368+
diagnostics
369+
.iter()
370+
.map(|dn| {
371+
let line = dn.range.start.line;
372+
let text = lines
373+
.get(line as usize)
374+
.map(|l| l.to_string())
375+
.unwrap_or_default();
376+
377+
Sign::new(line + 1, text, dn.severity)
378+
})
379+
.collect()
380+
} else {
381+
vec![]
382+
};
383+
384+
// There might be multiple diagnostics for one line. Show only highest severity.
381385
signs.sort_unstable();
382-
signs.dedup_by_key(|s| s.line);
386+
signs.dedup();
387+
383388
if let Some(diagnosticSignsMax) = self.diagnosticsSignsMax {
384389
signs.truncate(diagnosticSignsMax as usize);
385390
}
386391

387-
let cmd = self.update(|state| {
388-
let signs_prev = state.signs.remove(filename).unwrap_or_default();
389-
let (signs_next, cmd) = get_command_update_signs(&signs_prev, &signs, filename);
390-
state.signs.insert(filename.to_string(), signs_next);
391-
Ok(cmd)
392-
})?;
393-
info!("Command to update signs: {}", cmd);
394-
self.command(&cmd)?;
392+
self.signs.insert(filename.to_owned(), signs);
395393
}
396394

397395
let diagnosticsDisplay = self.get(|state| Ok(state.diagnosticsDisplay.clone()))?;
@@ -708,8 +706,9 @@ impl State {
708706
}
709707
}
710708
for f in filenames {
711-
self.display_diagnostics(&f, &[])?;
709+
self.process_diagnostics(&f, &[])?;
712710
}
711+
self.languageClient_handleCursorMoved(&json!({}).to_params()?)?;
713712

714713
self.diagnostics.retain(|f, _| !f.starts_with(&root));
715714
self.update_quickfixlist()?;
@@ -1818,7 +1817,8 @@ impl State {
18181817
if filename != current_filename.canonicalize() {
18191818
return Ok(());
18201819
}
1821-
self.display_diagnostics(&current_filename, &diagnostics)?;
1820+
self.process_diagnostics(&current_filename, &diagnostics)?;
1821+
self.languageClient_handleCursorMoved(&json!({}).to_params()?)?;
18221822
self.call::<_, u8>(None, "s:ExecuteAutocmd", "LanguageClientDiagnosticsChanged")?;
18231823

18241824
info!("End {}", lsp::notification::PublishDiagnostics::METHOD);
@@ -2071,7 +2071,7 @@ impl State {
20712071
self.textDocument_didOpen(params)?;
20722072

20732073
if let Some(diagnostics) = self.diagnostics.get(&filename).cloned() {
2074-
self.display_diagnostics(&filename, &diagnostics)?;
2074+
self.process_diagnostics(&filename, &diagnostics)?;
20752075
self.languageClient_handleCursorMoved(params)?;
20762076
}
20772077
} else {
@@ -2145,36 +2145,51 @@ impl State {
21452145
info!("Begin {}", NOTIFICATION__HandleCursorMoved);
21462146
let (buftype, filename, line): (String, String, u64) =
21472147
self.gather_args(&[VimVar::Buftype, VimVar::Filename, VimVar::Line], params)?;
2148-
if !buftype.is_empty() || line == self.get(|state| Ok(state.last_cursor_line))? {
2148+
let (visible_line_start, visible_line_end): (u64, u64) = self.gather_args(
2149+
&["LSP#visible_line_start()", "LSP#visible_line_end()"],
2150+
params,
2151+
)?;
2152+
if !buftype.is_empty() && !self.diagnostics.contains_key(&filename) {
21492153
return Ok(());
21502154
}
21512155

2152-
self.update(|state| {
2153-
state.last_cursor_line = line;
2154-
Ok(())
2155-
})?;
2156-
let message = self.get(|state| {
2157-
state
2158-
.line_diagnostics
2156+
if line != self.last_cursor_line {
2157+
self.last_cursor_line = line;
2158+
2159+
let message = self.line_diagnostics
21592160
.get(&(filename.clone(), line))
21602161
.cloned()
2161-
.ok_or_else(|| {
2162-
format_err!(
2163-
"Line diagnostic message not found! filename: {}, line: {}",
2164-
filename,
2165-
line
2166-
)
2167-
})
2168-
}).unwrap_or_default();
2169-
if message == self.get(|state| Ok(state.last_line_diagnostic.clone()))? {
2170-
return Ok(());
2162+
.unwrap_or_default();
2163+
2164+
if message != self.last_line_diagnostic {
2165+
self.echo_ellipsis(&message)?;
2166+
self.last_line_diagnostic = message;
2167+
}
21712168
}
21722169

2173-
self.update(|state| {
2174-
state.last_line_diagnostic = message.clone();
2175-
Ok(())
2176-
})?;
2177-
self.echo_ellipsis(&message)?;
2170+
let signs_prev = self.signs_placed
2171+
.get(&filename)
2172+
.cloned()
2173+
.unwrap_or_default();
2174+
let signs: Vec<_> = self.signs
2175+
.entry(filename.clone())
2176+
.or_insert_with(|| vec![])
2177+
.iter()
2178+
.filter_map(|s| {
2179+
if s.line < visible_line_start || s.line > visible_line_end {
2180+
return None;
2181+
}
2182+
2183+
Some(s.clone())
2184+
})
2185+
.collect();
2186+
if signs != signs_prev {
2187+
let (signs, cmds) = get_command_update_signs(&signs_prev, &signs, &filename);
2188+
self.signs_placed.insert(filename.clone(), signs);
2189+
2190+
info!("Updating signs: {:?}", cmds);
2191+
self.command(&cmds)?;
2192+
}
21782193

21792194
info!("End {}", NOTIFICATION__HandleCursorMoved);
21802195
Ok(())

src/types.rs

+2
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ pub struct State {
101101
#[serde(skip_serializing)]
102102
pub line_diagnostics: HashMap<(String, u64), String>,
103103
pub signs: HashMap<String, Vec<Sign>>,
104+
pub signs_placed: HashMap<String, Vec<Sign>>,
104105
pub highlight_source: Option<u64>,
105106
// TODO: make file specific.
106107
pub highlight_match_ids: Vec<u32>,
@@ -163,6 +164,7 @@ impl State {
163164
diagnostics: HashMap::new(),
164165
line_diagnostics: HashMap::new(),
165166
signs: HashMap::new(),
167+
signs_placed: HashMap::new(),
166168
highlight_source: None,
167169
highlight_match_ids: Vec::new(),
168170
user_handlers: HashMap::new(),

src/utils.rs

+29-18
Original file line numberDiff line numberDiff line change
@@ -219,34 +219,37 @@ fn test_apply_TextEdit_overlong_end() {
219219

220220
fn get_command_add_sign(sign: &Sign, filename: &str) -> String {
221221
format!(
222-
" | execute 'sign place {} line={} name=LanguageClient{:?} file={}'",
223-
sign.id, sign.line, sign.severity, filename
222+
"sign place {} line={} name=LanguageClient{:?} file={}",
223+
sign.id,
224+
sign.line,
225+
sign.severity.unwrap_or(DiagnosticSeverity::Hint),
226+
filename
224227
)
225228
}
226229

227230
#[test]
228231
fn test_get_command_add_sign() {
229-
let sign = Sign::new(1, "".to_owned(), DiagnosticSeverity::Error);
232+
let sign = Sign::new(1, "".to_owned(), Some(DiagnosticSeverity::Error));
230233
assert_eq!(
231234
get_command_add_sign(&sign, ""),
232-
" | execute 'sign place 75000 line=1 name=LanguageClientError file='"
235+
"sign place 75000 line=1 name=LanguageClientError file="
233236
);
234237

235-
let sign = Sign::new(7, "".to_owned(), DiagnosticSeverity::Error);
238+
let sign = Sign::new(7, "".to_owned(), Some(DiagnosticSeverity::Error));
236239
assert_eq!(
237240
get_command_add_sign(&sign, ""),
238-
" | execute 'sign place 75024 line=7 name=LanguageClientError file='"
241+
"sign place 75024 line=7 name=LanguageClientError file="
239242
);
240243

241-
let sign = Sign::new(7, "".to_owned(), DiagnosticSeverity::Hint);
244+
let sign = Sign::new(7, "".to_owned(), Some(DiagnosticSeverity::Hint));
242245
assert_eq!(
243246
get_command_add_sign(&sign, ""),
244-
" | execute 'sign place 75027 line=7 name=LanguageClientHint file='"
247+
"sign place 75027 line=7 name=LanguageClientHint file="
245248
);
246249
}
247250

248251
fn get_command_delete_sign(sign: &Sign, filename: &str) -> String {
249-
format!(" | execute 'sign unplace {} file={}'", sign.id, filename)
252+
format!("sign unplace {} file={}", sign.id, filename)
250253
}
251254

252255
#[test]
@@ -258,19 +261,19 @@ pub fn get_command_update_signs(
258261
signs_prev: &[Sign],
259262
signs: &[Sign],
260263
filename: &str,
261-
) -> (Vec<Sign>, String) {
264+
) -> (Vec<Sign>, Vec<String>) {
262265
// Sign id might become different due to lines shifting. Use sign's existing sign id to
263266
// track same sign.
264267
let mut signs_next = vec![];
265268

266-
let mut cmd = "echo".to_owned();
269+
let mut cmds = vec![];
267270
for comp in diff::slice(signs_prev, signs) {
268271
match comp {
269272
diff::Result::Left(sign) => {
270-
cmd += &get_command_delete_sign(sign, filename);
273+
cmds.push(get_command_delete_sign(sign, filename));
271274
}
272275
diff::Result::Right(sign) => {
273-
cmd += &get_command_add_sign(sign, filename);
276+
cmds.push(get_command_add_sign(sign, filename));
274277
signs_next.push(sign.clone());
275278
}
276279
diff::Result::Both(sign, _) => {
@@ -279,19 +282,27 @@ pub fn get_command_update_signs(
279282
}
280283
}
281284

282-
(signs_next, cmd)
285+
(signs_next, cmds)
283286
}
284287

285288
#[test]
286289
fn test_get_command_update_signs() {
287-
let signs_prev = vec![Sign::new(1, "abcde".to_string(), DiagnosticSeverity::Error)];
288-
let signs = vec![Sign::new(3, "abcde".to_string(), DiagnosticSeverity::Error)];
289-
let (signs_next, cmd) = get_command_update_signs(&signs_prev, &signs, "f");
290+
let signs_prev = vec![Sign::new(
291+
1,
292+
"abcde".to_string(),
293+
Some(DiagnosticSeverity::Error),
294+
)];
295+
let signs = vec![Sign::new(
296+
3,
297+
"abcde".to_string(),
298+
Some(DiagnosticSeverity::Error),
299+
)];
300+
let (signs_next, cmds) = get_command_update_signs(&signs_prev, &signs, "f");
290301
assert_eq!(
291302
serde_json::to_string(&signs_next).unwrap(),
292303
"[{\"id\":75000,\"line\":1,\"text\":\"abcde\",\"severity\":1}]"
293304
);
294-
assert_eq!(cmd, "echo");
305+
assert!(cmds.is_empty());
295306
}
296307

297308
pub trait Combine {

src/vim.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -187,10 +187,9 @@ impl State {
187187
Ok(serde_json::from_value(result)?)
188188
}
189189

190-
pub fn command<S: AsRef<str>>(&mut self, cmd: S) -> Result<()> {
191-
let cmd = cmd.as_ref();
192-
if self.call::<_, u8>(None, "execute", cmd)? != 0 {
193-
bail!("Failed to execute command: {}", cmd);
190+
pub fn command<P: Serialize + Debug>(&mut self, cmds: P) -> Result<()> {
191+
if self.call::<_, u8>(None, "execute", &cmds)? != 0 {
192+
bail!("Failed to execute command: {:?}", cmds);
194193
}
195194
Ok(())
196195
}

0 commit comments

Comments
 (0)