Skip to content

Commit

Permalink
Implement textDocument/documentHighlight (#561)
Browse files Browse the repository at this point in the history
* Implement textDocument/documentHighlight

* Replace index with .get()

* Add clearDocumentHighlight() notification

* Don't clear document highlight automatically

* Change the default documentHighlight texthls

* Document documentHighlight calls

* Add Document Highlight into the context menu
  • Loading branch information
YaLTeR authored and autozimu committed Aug 13, 2018
1 parent 6aa9952 commit 11b6d49
Show file tree
Hide file tree
Showing 6 changed files with 211 additions and 2 deletions.
18 changes: 18 additions & 0 deletions autoload/LanguageClient.vim
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,23 @@ function! LanguageClient#textDocument_didClose() abort
\ })
endfunction

function! LanguageClient#textDocument_documentHighlight(...) abort
let l:callback = get(a:000, 1, v:null)
let l:params = {
\ 'filename': LSP#filename(),
\ 'text': LSP#text(),
\ 'line': LSP#line(),
\ 'character': LSP#character(),
\ 'handle': s:IsFalse(l:callback),
\ }
call extend(l:params, get(a:000, 0, {}))
return LanguageClient#Call('textDocument/documentHighlight', l:params, l:callback)
endfunction

function! LanguageClient#clearDocumentHighlight() abort
return LanguageClient#Notify('languageClient/clearDocumentHighlight', {})
endfunction

function! LanguageClient#getState(callback) abort
return LanguageClient#Call('languageClient/getState', {}, a:callback)
endfunction
Expand Down Expand Up @@ -1017,6 +1034,7 @@ function! LanguageClient_contextMenuItems() abort
\ 'Rename': 'LanguageClient#textDocument_rename',
\ 'Signature Help': 'LanguageClient#textDocument_signatureHelp',
\ 'Type Definition': 'LanguageClient#textDocument_typeDefinition',
\ 'Document Highlight': 'LanguageClient#textDocument_documentHighlight',
\ 'Workspace Symbol': 'LanguageClient#workspace_symbol',
\ }
endfunction
Expand Down
32 changes: 32 additions & 0 deletions doc/LanguageClient.txt
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,26 @@ neovim >= 0.3.0.
Default: 0
Valid options: 1 | 0

2.22 g:LanguageClient_documentHighlightDisplay *g:LanguageClient_documentHighlightDisplay*

Control how document highlights are displayed.

Default: >
{
1: {
"name": "Text",
"texthl": "SpellCap",
},
2: {
"name": "Read",
"texthl": "SpellLocal",
},
3: {
"name": "Write",
"texthl": "SpellRare",
},
}
==============================================================================
3. Commands *LanguageClientCommands*

Expand Down Expand Up @@ -404,6 +424,18 @@ Signature: LanguageClient#textDocument_rangeFormatting(...)

Format selected lines.

*LanguageClient#textDocument_documentHighlight()*
*LanguageClient_textDocument_documentHighlight()*
Signature: LanguageClient#textDocument_documentHighlight(...)

Highlight usages of the symbol under the cursor.

*LanguageClient#clearDocumentHighlight()*
*LanguageClient_clearDocumentHighlight()*
Signature: LanguageClient#clearDocumentHighlight()

Clear the symbol usages highlighting.

*LanguageClient#workspace_symbol()*
*LanguageClient_workspace_symbol()*
Signature: LanguageClient#workspace_symbol([query: String], ...)
Expand Down
8 changes: 8 additions & 0 deletions plugin/LanguageClient.vim
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ function! LanguageClient_textDocument_rangeFormatting(...)
return call('LanguageClient#textDocument_rangeFormatting', a:000)
endfunction

function! LanguageClient_textDocument_documentHighlight(...)
return call('LanguageClient#textDocument_documentHighlight', a:000)
endfunction

function! LanguageClient_workspace_symbol(...)
return call('LanguageClient#workspace_symbol', a:000)
endfunction
Expand Down Expand Up @@ -86,6 +90,10 @@ function! LanguageClient_statusLine(...)
return call('LanguageClient#statusLine', a:000)
endfunction

function! LanguageClient_clearDocumentHighlight(...)
return call('LanguageClient#clearDocumentHighlight', a:000)
endfunction

function! LanguageClient_cquery_base(...)
return call('LanguageClient#cquery_base', a:000)
endfunction
Expand Down
104 changes: 102 additions & 2 deletions src/languageclient.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,12 @@ impl State {
].as_ref(),
)?;

let (diagnosticsSignsMax,): (Option<u64>,) =
self.eval(["get(g:, 'LanguageClient_diagnosticsSignsMax', v:null)"].as_ref())?;
let (diagnosticsSignsMax, documentHighlightDisplay): (Option<u64>, Value) = self.eval(
[
"get(g:, 'LanguageClient_diagnosticsSignsMax', v:null)",
"get(g:, 'LanguageClient_documentHighlightDisplay', {})",
].as_ref(),
)?;

// vimscript use 1 for true, 0 for false.
let autoStart = autoStart == 1;
Expand Down Expand Up @@ -190,6 +194,10 @@ impl State {
serde_json::to_value(&state.diagnosticsDisplay)?.combine(diagnosticsDisplay),
)?;
state.diagnosticsSignsMax = diagnosticsSignsMax;
state.documentHighlightDisplay = serde_json::from_value(
serde_json::to_value(&state.documentHighlightDisplay)?
.combine(documentHighlightDisplay),
)?;
state.windowLogMessageLevel = windowLogMessageLevel;
state.settingsPath = settingsPath;
state.loadSettings = loadSettings;
Expand Down Expand Up @@ -256,6 +264,98 @@ impl State {
Ok(())
}

pub fn textDocument_documentHighlight(&mut self, params: &Option<Params>) -> Result<Value> {
self.textDocument_didChange(params)?;
info!("Begin {}", lsp::request::DocumentHighlightRequest::METHOD);
let (languageId, filename, line, character, handle): (
String,
String,
u64,
u64,
bool,
) = self.gather_args(
&[
VimVar::LanguageId,
VimVar::Filename,
VimVar::Line,
VimVar::Character,
VimVar::Handle,
],
params,
)?;

let result = self.call(
Some(&languageId),
lsp::request::DocumentHighlightRequest::METHOD,
TextDocumentPositionParams {
text_document: TextDocumentIdentifier {
uri: filename.to_url()?,
},
position: Position { line, character },
},
)?;

if !handle {
return Ok(result);
}

let document_highlight: Option<Vec<DocumentHighlight>> =
serde_json::from_value(result.clone())?;
if let Some(document_highlight) = document_highlight {
let highlights = document_highlight
.into_iter()
.map(|DocumentHighlight { range, kind }| {
Ok(Highlight {
line: range.start.line,
character_start: range.start.character,
character_end: range.end.character,
group: self
.documentHighlightDisplay
.get(
&kind
.unwrap_or(DocumentHighlightKind::Text)
.to_int()
.unwrap(),
)
.ok_or_else(|| err_msg("Failed to get display"))?
.texthl
.clone(),
text: String::new(),
})
})
.collect::<Result<Vec<_>>>()?;

let source = if let Some(source) = self.document_highlight_source {
source
} else {
let source = self.call(
None,
"nvim_buf_add_highlight",
json!([0, 0, "Error", 1, 1, 1]),
)?;
self.document_highlight_source = Some(source);
source
};

self.notify(None, "nvim_buf_clear_highlight", json!([0, source, 0, -1]))?;
self.notify(None, "s:AddHighlights", json!([source, highlights]))?;
}

info!("End {}", lsp::request::DocumentHighlightRequest::METHOD);
Ok(result)
}

pub fn languageClient_clearDocumentHighlight(&mut self, _: &Option<Params>) -> Result<()> {
info!("Begin {}", NOTIFICATION__ClearDocumentHighlight);

if let Some(source) = self.document_highlight_source.take() {
self.notify(None, "nvim_buf_clear_highlight", json!([0, source, 0, -1]))?;
}

info!("End {}", NOTIFICATION__ClearDocumentHighlight);
Ok(())
}

fn apply_TextEdits<P: AsRef<Path>>(&mut self, path: P, edits: &[TextEdit]) -> Result<()> {
debug!("Begin apply TextEdits: {:?}", edits);
if edits.is_empty() {
Expand Down
6 changes: 6 additions & 0 deletions src/rpchandler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ impl State {
lsp::request::ApplyWorkspaceEdit::METHOD => {
self.workspace_applyEdit(&method_call.params)
}
lsp::request::DocumentHighlightRequest::METHOD => {
self.textDocument_documentHighlight(&method_call.params)
}
REQUEST__RustImplementations => self.rustDocument_implementations(&method_call.params),
// Extensions.
REQUEST__GetState => self.languageClient_getState(&method_call.params),
Expand Down Expand Up @@ -174,6 +177,9 @@ impl State {
NOTIFICATION__FZFSinkCommand => {
self.languageClient_FZFSinkCommand(&notification.params)?
}
NOTIFICATION__ClearDocumentHighlight => {
self.languageClient_clearDocumentHighlight(&notification.params)?
}
// Extensions by language servers.
NOTIFICATION__LanguageStatus => self.language_status(&notification.params)?,
NOTIFICATION__RustBeginBuild => self.rust_handleBeginBuild(&notification.params)?,
Expand Down
45 changes: 45 additions & 0 deletions src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub const NOTIFICATION__HandleCompleteDone: &str = "languageClient/handleComplet
pub const NOTIFICATION__FZFSinkLocation: &str = "LanguageClient_FZFSinkLocation";
pub const NOTIFICATION__FZFSinkCommand: &str = "LanguageClient_FZFSinkCommand";
pub const NOTIFICATION__ServerExited: &str = "$languageClient/serverExited";
pub const NOTIFICATION__ClearDocumentHighlight: &str = "languageClient/clearDocumentHighlight";

// Extensions by language servers.
pub const REQUEST__RustImplementations: &str = "rustDocument/implementations";
Expand Down Expand Up @@ -106,6 +107,7 @@ 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<u64>,
pub user_handlers: HashMap<String, String>,
#[serde(skip_serializing)]
pub watchers: HashMap<String, notify::RecommendedWatcher>,
Expand All @@ -126,6 +128,7 @@ pub struct State {
pub diagnosticsList: DiagnosticsList,
pub diagnosticsDisplay: HashMap<u64, DiagnosticsDisplay>,
pub diagnosticsSignsMax: Option<u64>,
pub documentHighlightDisplay: HashMap<u64, DocumentHighlightDisplay>,
pub windowLogMessageLevel: MessageType,
pub settingsPath: String,
pub loadSettings: bool,
Expand Down Expand Up @@ -170,6 +173,7 @@ 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 All @@ -187,6 +191,7 @@ impl State {
diagnosticsList: DiagnosticsList::Quickfix,
diagnosticsDisplay: DiagnosticsDisplay::default(),
diagnosticsSignsMax: None,
documentHighlightDisplay: DocumentHighlightDisplay::default(),
windowLogMessageLevel: MessageType::Warning,
settingsPath: format!(".vim{}settings.json", std::path::MAIN_SEPARATOR),
loadSettings: false,
Expand Down Expand Up @@ -361,6 +366,40 @@ impl Sign {
}
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DocumentHighlightDisplay {
pub name: String,
pub texthl: String,
}

impl DocumentHighlightDisplay {
pub fn default() -> HashMap<u64, DocumentHighlightDisplay> {
let mut map = HashMap::new();
map.insert(
1,
DocumentHighlightDisplay {
name: "Text".to_owned(),
texthl: "SpellCap".to_owned(),
},
);
map.insert(
2,
DocumentHighlightDisplay {
name: "Read".to_owned(),
texthl: "SpellLocal".to_owned(),
},
);
map.insert(
3,
DocumentHighlightDisplay {
name: "Write".to_owned(),
texthl: "SpellRare".to_owned(),
},
);
map
}
}

impl std::cmp::Ord for Sign {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.id.cmp(&other.id)
Expand Down Expand Up @@ -741,6 +780,12 @@ impl ToInt for MessageType {
}
}

impl ToInt for DocumentHighlightKind {
fn to_int(&self) -> Result<u64> {
Ok(*self as u64)
}
}

pub trait ToUsize {
fn to_usize(&self) -> Result<usize>;
}
Expand Down

0 comments on commit 11b6d49

Please sign in to comment.