Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions autoload/LanguageClient.vim
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,11 @@ function! s:MatchDelete(ids) abort
endfunction

function! s:ApplySemanticHighlights(bufnr, ns_id, clears, highlights) abort
" TODO: implement this for vim8
if !has('nvim')
return
endif

for clear in a:clears
call nvim_buf_clear_namespace(a:bufnr, a:ns_id, clear.line_start, clear.line_end)
endfor
Expand All @@ -300,10 +305,45 @@ function! s:ApplySemanticHighlights(bufnr, ns_id, clears, highlights) abort
endfunction

" Batch version of nvim_buf_add_highlight
function! s:AddHighlights(source, highlights) abort
function! s:AddHighlights(namespace_id, highlights) abort
if has('nvim')
for hl in a:highlights
call nvim_buf_add_highlight(0, a:namespace_id, hl.group, hl.line, hl.character_start, hl.character_end)
endfor
else
let match_ids = []
for hl in a:highlights
call nvim_buf_add_highlight(0, a:source, hl.group, hl.line, hl.character_start, hl.character_end)
let match_id = matchaddpos(hl.group, [[hl.line + 1, hl.character_start + 1, hl.character_end - hl.character_start]])
let match_ids = add(match_ids, match_id)
endfor

call setbufvar(bufname(), 'document_highlight_match_ids', match_ids)
endif
endfunction

function! s:SetHighlights(highlights) abort
call s:ClearHighlights()
if has('nvim')
let s:namespace_id = nvim_create_namespace('__LCN_DOCUMENT_HIGHLIGHTS__')
call s:AddHighlights(s:namespace_id, a:highlights)
else
call s:AddHighlights(0, a:highlights)
endif
endfunction

function! s:ClearHighlights() abort
if has('nvim')
let s:namespace_id = nvim_create_namespace('__LCN_DOCUMENT_HIGHLIGHTS__')
call nvim_buf_clear_namespace(0, s:namespace_id, 0, -1)
else
let match_ids = get(b:, 'document_highlight_match_ids', [])
for mid in match_ids
" call inside a try/catch to avoid error for manually cleared matches
try | call matchdelete(mid) | catch
endtry
endfor
call setbufvar(bufname(), 'document_highlight_match_ids', [])
endif
endfunction

" Get an variable value.
Expand Down
101 changes: 4 additions & 97 deletions src/language_server_protocol.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::language_client::LanguageClient;
use crate::sign::Sign;
use crate::vim::{try_get, Mode};
use crate::{extensions::java, viewport::Viewport};
use crate::{extensions::java, viewport::Viewport, vim::Highlight};
use crate::{
rpcclient::RpcClient,
types::*,
Expand Down Expand Up @@ -508,74 +508,15 @@ impl LanguageClient {
})
.collect::<Result<Vec<_>>>()?;

let buffer = self.vim()?.get_bufnr(&filename, params)?;

// The following code needs to be inside the critical section as a whole to update
// everything correctly and not leave hanging highlights.
self.update(|state| {
let source = if let Some(hs) = state.document_highlight_source {
if hs.buffer == buffer {
// If we want to highlight in the same buffer as last time, we can reuse
// the previous source.
Some(hs.source)
} else {
// Clear the highlight in the previous buffer.
state.vim.rpcclient.notify(
"nvim_buf_clear_highlight",
json!([hs.buffer, hs.source, 0, -1]),
)?;

None
}
} else {
None
};

let source = match source {
Some(source) => source,
None => {
// Create a new source.
let source = state.vim.rpcclient.call(
"nvim_buf_add_highlight",
json!([buffer, 0, "Error", 1, 1, 1]),
)?;
state.document_highlight_source = Some(HighlightSource { buffer, source });
source
}
};

state
.vim
.rpcclient
.notify("nvim_buf_clear_highlight", json!([buffer, source, 0, -1]))?;
state
.vim
.rpcclient
.notify("s:AddHighlights", json!([source, highlights]))?;

Ok(())
})?;
self.vim()?.set_highlights(&highlights)?;
}

Ok(result)
}

#[tracing::instrument(level = "info", skip(self))]
pub fn clear_document_highlight(&self, _params: &Value) -> Result<()> {
// The following code needs to be inside the critical section as a whole to update
// everything correctly and not leave hanging highlights.
self.update(|state| {
if let Some(HighlightSource { buffer, source }) = state.document_highlight_source.take()
{
state
.vim
.rpcclient
.notify("nvim_buf_clear_highlight", json!([buffer, source, 0, -1]))?;
}
Ok(())
})?;

Ok(())
self.vim()?.clear_highlights()
}

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

if Some(highlights.clone())
!= self.get(|state| state.highlights_placed.get(&filename).cloned())?
&& self.get(|state| state.is_nvim)?
{
let source = if let Some(source) = self.get(|state| state.highlight_source)? {
source
} else {
let source = self
.vim()?
.rpcclient
.call("nvim_buf_add_highlight", json!([0, 0, "Error", 1, 1, 1]))?;
self.update(|state| {
state.highlight_source = Some(source);
Ok(())
})?;
source
};

self.update(|state| {
state
.highlights_placed
.insert(filename.clone(), highlights.clone());
Ok(())
})?;

self.vim()?.rpcclient.notify(
"nvim_buf_clear_highlight",
json!([0, source, current_viewport.start, current_viewport.end]),
)?;

self.vim()?
.rpcclient
.notify("s:AddHighlights", json!([source, highlights]))?;
}

self.vim()?.set_highlights(&highlights)?;
self.draw_virtual_texts(&params)?;

Ok(())
Expand Down
26 changes: 1 addition & 25 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
vim::Vim,
watcher::FSWatch,
};
use crate::{logger::Logger, viewport::Viewport};
use crate::{logger::Logger, viewport::Viewport, vim::Highlight};
use anyhow::{anyhow, Result};
use jsonrpc_core::Params;
use log::*;
Expand Down Expand Up @@ -119,12 +119,6 @@ pub enum Call {
Notification(LanguageId, jsonrpc_core::Notification),
}

#[derive(Clone, Copy, Serialize)]
pub struct HighlightSource {
pub buffer: Bufnr,
pub source: u64,
}

#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum UseVirtualText {
Diagnostics,
Expand Down Expand Up @@ -169,7 +163,6 @@ pub struct State {
pub highlights_placed: HashMap<String, Vec<Highlight>>,
// TODO: make file specific.
pub highlight_match_ids: Vec<u32>,
pub document_highlight_source: Option<HighlightSource>,
pub user_handlers: HashMap<String, String>,
#[serde(skip_serializing)]
pub watchers: HashMap<String, FSWatch>,
Expand Down Expand Up @@ -259,7 +252,6 @@ impl State {
highlights: HashMap::new(),
highlights_placed: HashMap::new(),
highlight_match_ids: Vec::new(),
document_highlight_source: None,
user_handlers: HashMap::new(),
watchers: HashMap::new(),
watcher_rxs: HashMap::new(),
Expand Down Expand Up @@ -506,22 +498,6 @@ pub struct TextDocumentSemanticHighlightState {
pub highlights: Option<Vec<Highlight>>,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Highlight {
pub line: u64,
pub character_start: u64,
pub character_end: u64,
pub group: String,
pub text: String,
}

impl PartialEq for Highlight {
fn eq(&self, other: &Self) -> bool {
// Quick check whether highlight should be updated.
self.text == other.text && self.group == other.group
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ClearNamespace {
pub line_start: u64,
Expand Down
32 changes: 31 additions & 1 deletion src/vim.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::rpcclient::RpcClient;
use crate::{
rpcclient::RpcClient,
sign::Sign,
types::{Bufnr, QuickfixEntry, VimExp, VirtualText},
utils::Canonicalize,
Expand All @@ -23,6 +23,21 @@ pub fn try_get<'a, R: Deserialize<'a>>(key: &str, params: &'a Value) -> Result<O
}
}

#[derive(Clone, Copy, Serialize)]
pub struct HighlightSource {
pub buffer: Bufnr,
pub source: u64,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Highlight {
pub line: u64,
pub character_start: u64,
pub character_end: u64,
pub group: String,
pub text: String,
}

#[derive(PartialEq)]
pub enum Mode {
Normal,
Expand Down Expand Up @@ -223,6 +238,21 @@ impl Vim {
Ok(())
}

/// clears all highlights in the current buffer.
pub fn clear_highlights(&self) -> Result<()> {
self.rpcclient.notify("s:ClearHighlights", json!([]))
}

/// replaces the highlights of the current document with the passed highlights.
pub fn set_highlights(&self, highlights: &[Highlight]) -> Result<()> {
if highlights.is_empty() {
return Ok(());
}

self.rpcclient
.notify("s:SetHighlights", json!([highlights]))
}

pub fn create_namespace(&self, name: &str) -> Result<i64> {
self.rpcclient.call("nvim_create_namespace", [name])
}
Expand Down