From cfb2e198f438ea5ddc45683f67fe50add5f9408d Mon Sep 17 00:00:00 2001 From: Satoru Kitaguchi Date: Wed, 27 Nov 2024 15:13:27 +0900 Subject: [PATCH] Add support for range diagnostics under cursor --- .../lsp/internal/diagnostics/under_cursor.vim | 17 +++- .../internal/diagnostics/under_cursor.vimspec | 88 +++++++++++++++++++ test/testproject-go/documentdiagnositcs.go | 11 +++ 3 files changed, 112 insertions(+), 4 deletions(-) create mode 100644 test/lsp/internal/diagnostics/under_cursor.vimspec create mode 100644 test/testproject-go/documentdiagnositcs.go diff --git a/autoload/lsp/internal/diagnostics/under_cursor.vim b/autoload/lsp/internal/diagnostics/under_cursor.vim index 48f8c47b1..8e139941e 100644 --- a/autoload/lsp/internal/diagnostics/under_cursor.vim +++ b/autoload/lsp/internal/diagnostics/under_cursor.vim @@ -29,20 +29,29 @@ function! lsp#internal#diagnostics#under_cursor#get_diagnostic(...) abort let l:line = line('.') let l:col = col('.') + return lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, l:line, l:col) +endfunction + +" Returns a diagnostic object, or empty dictionary if no diagnostics are +" available. +function! lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(diagnostics, line, col) abort let l:closest_diagnostic = {} let l:closest_distance = -1 + let l:closest_end_col = -1 - for l:diagnostic in l:diagnostics + for l:diagnostic in a:diagnostics let [l:start_line, l:start_col] = lsp#utils#position#lsp_to_vim('%', l:diagnostic['range']['start']) + let [l:end_line, l:end_col] = lsp#utils#position#lsp_to_vim('%', l:diagnostic['range']['end']) - if l:line == l:start_line - let l:distance = abs(l:start_col - l:col) + if (a:line > l:start_line || (a:line == l:start_line && a:col >= l:start_col)) && + \ (a:line < l:end_line || (a:line == l:end_line && a:col < l:end_col)) + let l:distance = abs(l:start_col - a:col) if l:closest_distance < 0 || l:distance < l:closest_distance + let l:closest_end_col = l:end_col let l:closest_diagnostic = l:diagnostic let l:closest_distance = l:distance endif endif endfor - return l:closest_diagnostic endfunction diff --git a/test/lsp/internal/diagnostics/under_cursor.vimspec b/test/lsp/internal/diagnostics/under_cursor.vimspec new file mode 100644 index 000000000..11a6fb85b --- /dev/null +++ b/test/lsp/internal/diagnostics/under_cursor.vimspec @@ -0,0 +1,88 @@ +Describe lsp#internal#diagnostics#under_cursor + " refer to lsp#test#projectdir('go') . '/documentdiagnostics.go' + + It should not trigger diagnostics when cursor is outside diagnostic range + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 1, 1), {}) + End + + It should trigger diagnostic when cursor is exactly at start position + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + let l:expected = { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 7, 13), l:expected) + End + + It should not trigger diagnostic when cursor is at line before start position + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 6, 13), {}) + End + + It should not trigger diagnostic when cursor is one character before start position + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 7, 12), {}) + End + + It should trigger diagnostic when cursor is at start column on an intermediate line within range + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + let l:expected = { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 8, 1), l:expected) + End + + It should trigger diagnostic when cursor is at end column on an intermediate line within range + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + let l:expected = { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 8, 15), l:expected) + End + + It should trigger diagnostic when cursor is exactly at end position + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + let l:expected = { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 9, 5), l:expected) + End + + It should not trigger diagnostic when cursor is at line after end position + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 10, 5), {}) + End + + It should not return diagnostic when cursor is one character after end position + let l:diagnostics = [ + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 9, 6), {}) + End + + It should return the closest diagnostic when multiple diagnostics exist across different ranges + let l:diagnostics = [ + \ { 'range': {'start': {'character': 10, 'line': 4}, 'end': {'character': 7, 'line': 10}} }, + \ { 'range': {'start': {'character': 12, 'line': 6}, 'end': {'character': 5, 'line': 8}} } + \ ] + let l:expected = { 'range': {'start': {'character': 10, 'line': 4}, 'end': {'character': 7, 'line': 10}} } + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 7, 3), l:expected) + End + + It should return the most specific diagnostic when multiple diagnostics overlap at cursor position + let l:diagnostics = [ + \ { 'range': {'start': {'character': 10, 'line': 4}, 'end': {'character': 15, 'line': 4}} }, + \ { 'range': {'start': {'character': 12, 'line': 4}, 'end': {'character': 14, 'line': 4}} } + \ ] + let l:expected = { 'range': {'start': {'character': 12, 'line': 4}, 'end': {'character': 14, 'line': 4}} } + Assert Equals(lsp#internal#diagnostics#under_cursor#_get_closest_diagnostic(l:diagnostics, 5, 13), l:expected) + End +End diff --git a/test/testproject-go/documentdiagnositcs.go b/test/testproject-go/documentdiagnositcs.go new file mode 100644 index 000000000..c3368cffd --- /dev/null +++ b/test/testproject-go/documentdiagnositcs.go @@ -0,0 +1,11 @@ +package main + +import "fmt" + +func documentdiagnostics() { + msg := "msg" + fmt.Printf(msg + + msg + + msg, + ) +}