Skip to content

Commit cfc5516

Browse files
authored
Add support for documentHighlight in vim8 (#1123)
1 parent 8b07645 commit cfc5516

File tree

4 files changed

+78
-125
lines changed

4 files changed

+78
-125
lines changed

autoload/LanguageClient.vim

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,11 @@ function! s:MatchDelete(ids) abort
290290
endfunction
291291

292292
function! s:ApplySemanticHighlights(bufnr, ns_id, clears, highlights) abort
293+
" TODO: implement this for vim8
294+
if !has('nvim')
295+
return
296+
endif
297+
293298
for clear in a:clears
294299
call nvim_buf_clear_namespace(a:bufnr, a:ns_id, clear.line_start, clear.line_end)
295300
endfor
@@ -300,10 +305,45 @@ function! s:ApplySemanticHighlights(bufnr, ns_id, clears, highlights) abort
300305
endfunction
301306

302307
" Batch version of nvim_buf_add_highlight
303-
function! s:AddHighlights(source, highlights) abort
308+
function! s:AddHighlights(namespace_id, highlights) abort
309+
if has('nvim')
310+
for hl in a:highlights
311+
call nvim_buf_add_highlight(0, a:namespace_id, hl.group, hl.line, hl.character_start, hl.character_end)
312+
endfor
313+
else
314+
let match_ids = []
304315
for hl in a:highlights
305-
call nvim_buf_add_highlight(0, a:source, hl.group, hl.line, hl.character_start, hl.character_end)
316+
let match_id = matchaddpos(hl.group, [[hl.line + 1, hl.character_start + 1, hl.character_end - hl.character_start]])
317+
let match_ids = add(match_ids, match_id)
318+
endfor
319+
320+
call setbufvar(bufname(), 'document_highlight_match_ids', match_ids)
321+
endif
322+
endfunction
323+
324+
function! s:SetHighlights(highlights) abort
325+
call s:ClearHighlights()
326+
if has('nvim')
327+
let s:namespace_id = nvim_create_namespace('__LCN_DOCUMENT_HIGHLIGHTS__')
328+
call s:AddHighlights(s:namespace_id, a:highlights)
329+
else
330+
call s:AddHighlights(0, a:highlights)
331+
endif
332+
endfunction
333+
334+
function! s:ClearHighlights() abort
335+
if has('nvim')
336+
let s:namespace_id = nvim_create_namespace('__LCN_DOCUMENT_HIGHLIGHTS__')
337+
call nvim_buf_clear_namespace(0, s:namespace_id, 0, -1)
338+
else
339+
let match_ids = get(b:, 'document_highlight_match_ids', [])
340+
for mid in match_ids
341+
" call inside a try/catch to avoid error for manually cleared matches
342+
try | call matchdelete(mid) | catch
343+
endtry
306344
endfor
345+
call setbufvar(bufname(), 'document_highlight_match_ids', [])
346+
endif
307347
endfunction
308348

309349
" Get an variable value.

src/language_server_protocol.rs

Lines changed: 4 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use crate::language_client::LanguageClient;
22
use crate::sign::Sign;
33
use crate::vim::{try_get, Mode};
4-
use crate::{extensions::java, viewport::Viewport};
4+
use crate::{extensions::java, viewport::Viewport, vim::Highlight};
55
use crate::{
66
rpcclient::RpcClient,
77
types::*,
@@ -508,74 +508,15 @@ impl LanguageClient {
508508
})
509509
.collect::<Result<Vec<_>>>()?;
510510

511-
let buffer = self.vim()?.get_bufnr(&filename, params)?;
512-
513-
// The following code needs to be inside the critical section as a whole to update
514-
// everything correctly and not leave hanging highlights.
515-
self.update(|state| {
516-
let source = if let Some(hs) = state.document_highlight_source {
517-
if hs.buffer == buffer {
518-
// If we want to highlight in the same buffer as last time, we can reuse
519-
// the previous source.
520-
Some(hs.source)
521-
} else {
522-
// Clear the highlight in the previous buffer.
523-
state.vim.rpcclient.notify(
524-
"nvim_buf_clear_highlight",
525-
json!([hs.buffer, hs.source, 0, -1]),
526-
)?;
527-
528-
None
529-
}
530-
} else {
531-
None
532-
};
533-
534-
let source = match source {
535-
Some(source) => source,
536-
None => {
537-
// Create a new source.
538-
let source = state.vim.rpcclient.call(
539-
"nvim_buf_add_highlight",
540-
json!([buffer, 0, "Error", 1, 1, 1]),
541-
)?;
542-
state.document_highlight_source = Some(HighlightSource { buffer, source });
543-
source
544-
}
545-
};
546-
547-
state
548-
.vim
549-
.rpcclient
550-
.notify("nvim_buf_clear_highlight", json!([buffer, source, 0, -1]))?;
551-
state
552-
.vim
553-
.rpcclient
554-
.notify("s:AddHighlights", json!([source, highlights]))?;
555-
556-
Ok(())
557-
})?;
511+
self.vim()?.set_highlights(&highlights)?;
558512
}
559513

560514
Ok(result)
561515
}
562516

563517
#[tracing::instrument(level = "info", skip(self))]
564518
pub fn clear_document_highlight(&self, _params: &Value) -> Result<()> {
565-
// The following code needs to be inside the critical section as a whole to update
566-
// everything correctly and not leave hanging highlights.
567-
self.update(|state| {
568-
if let Some(HighlightSource { buffer, source }) = state.document_highlight_source.take()
569-
{
570-
state
571-
.vim
572-
.rpcclient
573-
.notify("nvim_buf_clear_highlight", json!([buffer, source, 0, -1]))?;
574-
}
575-
Ok(())
576-
})?;
577-
578-
Ok(())
519+
self.vim()?.clear_highlights()
579520
}
580521

581522
#[tracing::instrument(level = "info", skip(self))]
@@ -3139,41 +3080,7 @@ impl LanguageClient {
31393080
.collect())
31403081
})?;
31413082

3142-
if Some(highlights.clone())
3143-
!= self.get(|state| state.highlights_placed.get(&filename).cloned())?
3144-
&& self.get(|state| state.is_nvim)?
3145-
{
3146-
let source = if let Some(source) = self.get(|state| state.highlight_source)? {
3147-
source
3148-
} else {
3149-
let source = self
3150-
.vim()?
3151-
.rpcclient
3152-
.call("nvim_buf_add_highlight", json!([0, 0, "Error", 1, 1, 1]))?;
3153-
self.update(|state| {
3154-
state.highlight_source = Some(source);
3155-
Ok(())
3156-
})?;
3157-
source
3158-
};
3159-
3160-
self.update(|state| {
3161-
state
3162-
.highlights_placed
3163-
.insert(filename.clone(), highlights.clone());
3164-
Ok(())
3165-
})?;
3166-
3167-
self.vim()?.rpcclient.notify(
3168-
"nvim_buf_clear_highlight",
3169-
json!([0, source, current_viewport.start, current_viewport.end]),
3170-
)?;
3171-
3172-
self.vim()?
3173-
.rpcclient
3174-
.notify("s:AddHighlights", json!([source, highlights]))?;
3175-
}
3176-
3083+
self.vim()?.set_highlights(&highlights)?;
31773084
self.draw_virtual_texts(&params)?;
31783085

31793086
Ok(())

src/types.rs

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use crate::{
55
vim::Vim,
66
watcher::FSWatch,
77
};
8-
use crate::{logger::Logger, viewport::Viewport};
8+
use crate::{logger::Logger, viewport::Viewport, vim::Highlight};
99
use anyhow::{anyhow, Result};
1010
use jsonrpc_core::Params;
1111
use log::*;
@@ -119,12 +119,6 @@ pub enum Call {
119119
Notification(LanguageId, jsonrpc_core::Notification),
120120
}
121121

122-
#[derive(Clone, Copy, Serialize)]
123-
pub struct HighlightSource {
124-
pub buffer: Bufnr,
125-
pub source: u64,
126-
}
127-
128122
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
129123
pub enum UseVirtualText {
130124
Diagnostics,
@@ -169,7 +163,6 @@ pub struct State {
169163
pub highlights_placed: HashMap<String, Vec<Highlight>>,
170164
// TODO: make file specific.
171165
pub highlight_match_ids: Vec<u32>,
172-
pub document_highlight_source: Option<HighlightSource>,
173166
pub user_handlers: HashMap<String, String>,
174167
#[serde(skip_serializing)]
175168
pub watchers: HashMap<String, FSWatch>,
@@ -259,7 +252,6 @@ impl State {
259252
highlights: HashMap::new(),
260253
highlights_placed: HashMap::new(),
261254
highlight_match_ids: Vec::new(),
262-
document_highlight_source: None,
263255
user_handlers: HashMap::new(),
264256
watchers: HashMap::new(),
265257
watcher_rxs: HashMap::new(),
@@ -506,22 +498,6 @@ pub struct TextDocumentSemanticHighlightState {
506498
pub highlights: Option<Vec<Highlight>>,
507499
}
508500

509-
#[derive(Debug, Clone, Serialize, Deserialize)]
510-
pub struct Highlight {
511-
pub line: u64,
512-
pub character_start: u64,
513-
pub character_end: u64,
514-
pub group: String,
515-
pub text: String,
516-
}
517-
518-
impl PartialEq for Highlight {
519-
fn eq(&self, other: &Self) -> bool {
520-
// Quick check whether highlight should be updated.
521-
self.text == other.text && self.group == other.group
522-
}
523-
}
524-
525501
#[derive(Debug, Clone, Serialize, Deserialize)]
526502
pub struct ClearNamespace {
527503
pub line_start: u64,

src/vim.rs

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
use crate::rpcclient::RpcClient;
21
use crate::{
2+
rpcclient::RpcClient,
33
sign::Sign,
44
types::{Bufnr, QuickfixEntry, VimExp, VirtualText},
55
utils::Canonicalize,
@@ -23,6 +23,21 @@ pub fn try_get<'a, R: Deserialize<'a>>(key: &str, params: &'a Value) -> Result<O
2323
}
2424
}
2525

26+
#[derive(Clone, Copy, Serialize)]
27+
pub struct HighlightSource {
28+
pub buffer: Bufnr,
29+
pub source: u64,
30+
}
31+
32+
#[derive(Debug, Clone, Serialize, Deserialize)]
33+
pub struct Highlight {
34+
pub line: u64,
35+
pub character_start: u64,
36+
pub character_end: u64,
37+
pub group: String,
38+
pub text: String,
39+
}
40+
2641
#[derive(PartialEq)]
2742
pub enum Mode {
2843
Normal,
@@ -223,6 +238,21 @@ impl Vim {
223238
Ok(())
224239
}
225240

241+
/// clears all highlights in the current buffer.
242+
pub fn clear_highlights(&self) -> Result<()> {
243+
self.rpcclient.notify("s:ClearHighlights", json!([]))
244+
}
245+
246+
/// replaces the highlights of the current document with the passed highlights.
247+
pub fn set_highlights(&self, highlights: &[Highlight]) -> Result<()> {
248+
if highlights.is_empty() {
249+
return Ok(());
250+
}
251+
252+
self.rpcclient
253+
.notify("s:SetHighlights", json!([highlights]))
254+
}
255+
226256
pub fn create_namespace(&self, name: &str) -> Result<i64> {
227257
self.rpcclient.call("nvim_create_namespace", [name])
228258
}

0 commit comments

Comments
 (0)