Skip to content

Commit

Permalink
Support funcref in LanguageClient_diagnosticsList
Browse files Browse the repository at this point in the history
  • Loading branch information
martskins committed Oct 22, 2020
1 parent 2dd003d commit fe80766
Show file tree
Hide file tree
Showing 5 changed files with 182 additions and 92 deletions.
12 changes: 10 additions & 2 deletions autoload/LanguageClient.vim
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,14 @@ function! s:hasSnippetSupport() abort
return 0
endfunction

function! s:getStringOrFuncref(name, default) abort
if type(get(g:, a:name, a:default)) is s:TYPE.funcref
return string(get(g:, a:name, a:default))
else
return get(g:, a:name, a:default)
endif
endfunction

function! s:getSelectionUI() abort
if type(get(g:, 'LanguageClient_selectionUI', v:null)) is s:TYPE.funcref
return 'funcref'
Expand Down Expand Up @@ -1227,11 +1235,11 @@ function! LanguageClient#handleVimLeavePre() abort
endtry
endfunction

function! s:LanguageClient_FZFSinkLocation(line) abort
function! g:LanguageClient_FZFSinkLocation(line) abort
return LanguageClient#Notify('LanguageClient_FZFSinkLocation', [a:line])
endfunction

function! LanguageClient_FZFSinkCommand(selection) abort
function! g:LanguageClient_FZFSinkCommand(selection) abort
return LanguageClient#Notify('LanguageClient_FZFSinkCommand', {
\ 'selection': a:selection,
\ })
Expand Down
89 changes: 43 additions & 46 deletions doc/LanguageClient.txt
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,49 @@ Valid options: "off" | "messages" | "verbose"
List used to fill diagnostic messages.

Default: "Quickfix"
Valid options: "Quickfix" | "Location" | "Disabled"
Valid options: "Quickfix" | "Location" | "Disabled" | |Funcref|

If you use a |Funcref|, the referenced function should have two arguments
(filename, diagnostics). filename is the name of the file of which the
diagnostics correspond to, and diagnostics is the list of diagnostics for said
file. Those diagnostics are as specified in the LSP specification.

For example, if you wanted to use `dense-analysis/ale` to display diagnostics
instead of this plugin you could use something like this:

```
function! DisplayDiagnostics(filename, diagnostics) abort
let s:diagnostics = []

for d in a:diagnostics
let s:severity = 'I'
if d.severity == 1
let s:severity = 'E'
elseif d.severity == 2
let s:severity = 'W'
endif

call add(s:diagnostics, {
\ "filename": a:filename,
\ "text": d.message,
\ "lnum": d.range.start.line + 1,
\ "end_lnum": d.range.end.line + 1,
\ "col": d.range.end.character,
\ "end_col": d.range.end.character,
\ "type": s:severity,
\ })
endfor

call ale#other_source#ShowResults(bufnr('%'), 'LanguageClientNeovim', s:diagnostics)
endfunction

let g:LanguageClient_diagnosticsDisplayFuncref = function('DisplayDiagnostics')
```

Keep in mind that to complete the integration between `ale` and
`LanguageClient-neovim` you need to add `LanguageClientNeovim` (or the name of
the linter you used in the call to ShowResults) to the list of linters to be
used in `ale`.

2.10 g:LanguageClient_diagnosticsEnable *g:LanguageClient_diagnosticsEnable*

Expand Down Expand Up @@ -635,51 +677,6 @@ Highlight group to be used for code lens.

Default: 'Comment'

2.42 g:LanguageClient_diagnosticsDisplayFuncref *g:LanguageClient_diagnosticsDisplayFuncref*

If set, LanguageClient-neovim will call this function instead of setting the diagnostics signs. This
is useful to delegate the display of diagnostics to other engines. The function is called with two
arguments, the first one is the file name of which the diagnostics correspond to, and the seconds one
is the list of diagnostics for said file. Those diagnostics are as specified in the LSP specification.

For example, if you wanted to use `dense-analysis/ale` to display diagnostics instead of this plugin
you could use something like this:

```
function! g:DisplayDiagnostics(filename, diagnostics) abort
let s:diagnostics = []

for d in a:diagnostics
let s:severity = 'I'
if d.severity == 1
let s:severity = 'E'
elseif d.severity == 2
let s:severity = 'W'
endif

call add(s:diagnostics, {
\ "filename": a:filename,
\ "text": d.message,
\ "lnum": d.range.start.line + 1,
\ "end_lnum": d.range.end.line + 1,
\ "col": d.range.end.character,
\ "end_col": d.range.end.character,
\ "type": s:severity,
\ })
endfor

call ale#other_source#ShowResults(bufnr('%'), 'LanguageClientNeovim', s:diagnostics)
endfunction

let g:LanguageClient_diagnosticsDisplayFuncref = 'g:DisplayDiagnostics'
```

Keep in mind that to complete the integration between `ale` and `LanguageClient-neovim` you need to
add `LanguageClientNeovim` (or the name of the linter you used in the call to ShowResults) to the list
of linters to be used in `ale`.

Default: v:null

==============================================================================
3. Commands *LanguageClientCommands*

Expand Down
91 changes: 57 additions & 34 deletions src/language_server_protocol.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::extensions::java;
use crate::language_client::LanguageClient;
use crate::vim::{try_get, Mode};
use crate::{extensions::java, vim::Funcref};
use crate::{
rpcclient::RpcClient,
types::*,
Expand Down Expand Up @@ -127,15 +127,15 @@ impl LanguageClient {
): (
u64,
HashMap<String, Vec<String>>,
Option<String>,
Option<Either<Funcref, String>>,
Option<String>,
Vec<String>,
u64,
Option<RootMarkers>,
Option<f64>,
Option<f64>,
u64,
Option<String>,
Option<Either<Funcref, String>>,
Value,
String,
Option<String>,
Expand All @@ -145,15 +145,15 @@ impl LanguageClient {
[
"!!get(g:, 'LanguageClient_autoStart', 1)",
"s:GetVar('LanguageClient_serverCommands', {})",
"s:getSelectionUI()",
"s:getStringOrFuncref('LanguageClient_selectionUI', v:null)",
"get(g:, 'LanguageClient_trace', v:null)",
"map(s:ToList(get(g:, 'LanguageClient_settingsPath', '.vim/settings.json')), 'expand(v:val)')",
"!!get(g:, 'LanguageClient_loadSettings', 1)",
"get(g:, 'LanguageClient_rootMarkers', v:null)",
"get(g:, 'LanguageClient_changeThrottle', v:null)",
"get(g:, 'LanguageClient_waitOutputTimeout', v:null)",
"!!get(g:, 'LanguageClient_diagnosticsEnable', 1)",
"get(g:, 'LanguageClient_diagnosticsList', 'Quickfix')",
"s:getStringOrFuncref('LanguageClient_diagnosticsList', 'Quickfix')",
"get(g:, 'LanguageClient_diagnosticsDisplay', {})",
"get(g:, 'LanguageClient_windowLogMessageLevel', 'Warning')",
"get(g:, 'LanguageClient_hoverPreview', 'Auto')",
Expand All @@ -165,7 +165,6 @@ impl LanguageClient {

#[allow(clippy::type_complexity)]
let (
diagnostics_display_funcref,
diagnostics_signs_max,
diagnostics_max_severity,
diagnostics_ignore_sources,
Expand All @@ -181,7 +180,6 @@ impl LanguageClient {
enable_extensions,
code_lens_hl_group,
): (
Option<String>,
Option<usize>,
String,
Vec<String>,
Expand All @@ -198,7 +196,6 @@ impl LanguageClient {
String,
) = self.vim()?.eval(
[
"get(g:, 'LanguageClient_diagnosticsDisplayFuncref', v:null)",
"get(g:, 'LanguageClient_diagnosticsSignsMax', v:null)",
"get(g:, 'LanguageClient_diagnosticsMaxSeverity', 'Hint')",
"get(g:, 'LanguageClient_diagnosticsIgnoreSources', [])",
Expand Down Expand Up @@ -232,12 +229,12 @@ impl LanguageClient {
None => Some(TraceOption::default()),
};

let selection_ui = if let Some(s) = selection_ui {
SelectionUI::from_str(&s)?
} else if self.vim()?.eval::<_, i64>("get(g:, 'loaded_fzf')")? == 1 {
SelectionUI::Funcref
let selection_ui = if let Some(Either::Right(s)) = selection_ui {
Either::Right(SelectionUI::from_str(&s)?)
} else if let Some(Either::Left(s)) = selection_ui {
Either::Left(s)
} else {
SelectionUI::default()
Either::Right(SelectionUI::default())
};

let change_throttle = change_throttle.map(|t| Duration::from_millis((t * 1000.0) as u64));
Expand All @@ -246,10 +243,12 @@ impl LanguageClient {

let diagnostics_enable = diagnostics_enable == 1;

let diagnostics_list = if let Some(s) = diagnostics_list {
DiagnosticsList::from_str(&s)?
let diagnostics_list = if let Some(Either::Right(s)) = diagnostics_list {
Either::Right(DiagnosticsList::from_str(&s)?)
} else if let Some(Either::Left(s)) = diagnostics_list {
Either::Left(s)
} else {
DiagnosticsList::Disabled
Either::Right(DiagnosticsList::Disabled)
};

let window_log_level = match window_log_message_level.to_ascii_uppercase().as_str() {
Expand Down Expand Up @@ -331,7 +330,6 @@ impl LanguageClient {
state.preferred_markup_kind = preferred_markup_kind;
state.enable_extensions = enable_extensions;
state.code_lens_hl_group = code_lens_hl_group;
state.diagnostics_display_funcref = diagnostics_display_funcref;

Ok(())
})?;
Expand Down Expand Up @@ -686,15 +684,18 @@ impl LanguageClient {
.collect();

let title = "[LC]: diagnostics";
let diagnostics_list = self.get(|state| state.diagnostics_list)?;
let diagnostics_list = self.get(|state| state.diagnostics_list.clone())?;
match diagnostics_list {
DiagnosticsList::Quickfix => {
self.vim()?.setqflist(&qflist, "r", title)?;
}
DiagnosticsList::Location => {
self.vim()?.setloclist(&qflist, "r", title)?;
}
DiagnosticsList::Disabled => {}
Either::Left(_) => {}
Either::Right(dl) => match dl {
DiagnosticsList::Quickfix => {
self.vim()?.setqflist(&qflist, "r", title)?;
}
DiagnosticsList::Location => {
self.vim()?.setloclist(&qflist, "r", title)?;
}
DiagnosticsList::Disabled => {}
},
}

Ok(())
Expand Down Expand Up @@ -1890,14 +1891,21 @@ impl LanguageClient {
.map(|it| ListItem::string_item(it, self, &cwd))
.collect();

match self.get(|state| state.selection_ui)? {
SelectionUI::Funcref => {
match self.get(|state| state.selection_ui.clone())? {
Either::Left(f) => {
self.vim()?
.rpcclient
.notify(f, json!([actions?, NOTIFICATION_FZF_SINK_COMMAND]))?;
}
// this exists purely for compatibility purposes, we should consider dropping this at
// some point and letting the user set up the FZF integration via a funcref
Either::Right(SelectionUI::FZF) => {
self.vim()?.rpcclient.notify(
"s:selectionUI_funcref",
json!([actions?, NOTIFICATION_FZF_SINK_COMMAND]),
)?;
}
SelectionUI::Quickfix | SelectionUI::LocationList => {
Either::Right(SelectionUI::Quickfix) | Either::Right(SelectionUI::LocationList) => {
let mut actions: Vec<String> = actions?
.iter_mut()
.enumerate()
Expand All @@ -1922,11 +1930,25 @@ impl LanguageClient {
where
T: ListItem,
{
let selection_ui = self.get(|state| state.selection_ui)?;
let selection_ui = self.get(|state| state.selection_ui.clone())?;
let selection_ui_auto_open = self.get(|state| state.selection_ui_auto_open)?;

match selection_ui {
SelectionUI::Funcref => {
Either::Left(f) => {
let cwd: String = self.vim()?.eval("getcwd()")?;
let source: Result<Vec<_>> = items
.iter()
.map(|it| ListItem::string_item(it, self, &cwd))
.collect();
let source = source?;

self.vim()?
.rpcclient
.notify(f, json!([source, NOTIFICATION_FZF_SINK_LOCATION]))?;
}
// this exists purely for compatibility purposes, we should consider dropping this at
// some point and letting the user set up the FZF integration via a funcref
Either::Right(SelectionUI::FZF) => {
let cwd: String = self.vim()?.eval("getcwd()")?;
let source: Result<Vec<_>> = items
.iter()
Expand All @@ -1936,10 +1958,10 @@ impl LanguageClient {

self.vim()?.rpcclient.notify(
"s:selectionUI_funcref",
json!([source, format!("s:{}", NOTIFICATION_FZF_SINK_LOCATION)]),
json!([source, NOTIFICATION_FZF_SINK_LOCATION]),
)?;
}
SelectionUI::Quickfix => {
Either::Right(SelectionUI::Quickfix) => {
let list: Result<Vec<_>> = items
.iter()
.map(|it| ListItem::quickfix_item(it, self))
Expand All @@ -1951,7 +1973,7 @@ impl LanguageClient {
}
self.vim()?.echo("Populated quickfix list.")?;
}
SelectionUI::LocationList => {
Either::Right(SelectionUI::LocationList) => {
let list: Result<Vec<_>> = items
.iter()
.map(|it| ListItem::quickfix_item(it, self))
Expand Down Expand Up @@ -2476,10 +2498,11 @@ impl LanguageClient {
}

// if a diagnostics display funcref has been configured then call that function and return
if let Some(funcref) = self.get(|state| state.diagnostics_display_funcref.clone())? {
if let Either::Left(funcref) = self.get(|state| state.diagnostics_list.clone())? {
self.vim()?
.rpcclient
.notify(funcref, json!([filename, diagnostics]))?;
self.handle_cursor_moved(&Value::Null)?;
return Ok(());
}

Expand Down
Loading

0 comments on commit fe80766

Please sign in to comment.