neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

diagnostic_spec.lua (25249B)


      1 local t = require('test.testutil')
      2 local n = require('test.functional.testnvim')()
      3 
      4 local t_lsp = require('test.functional.plugin.lsp.testutil')
      5 
      6 local clear = n.clear
      7 local exec_lua = n.exec_lua
      8 local eq = t.eq
      9 local neq = t.neq
     10 
     11 local create_server_definition = t_lsp.create_server_definition
     12 
     13 describe('vim.lsp.diagnostic', function()
     14  local fake_uri --- @type string
     15  local client_id --- @type integer
     16  local diagnostic_bufnr --- @type integer
     17 
     18  before_each(function()
     19    clear { env = {
     20      NVIM_LUA_NOTRACK = '1',
     21      VIMRUNTIME = os.getenv 'VIMRUNTIME',
     22    } }
     23 
     24    exec_lua(function()
     25      require('vim.lsp')
     26 
     27      _G.make_range = function(x1, y1, x2, y2)
     28        return { start = { line = x1, character = y1 }, ['end'] = { line = x2, character = y2 } }
     29      end
     30 
     31      _G.make_error = function(msg, x1, y1, x2, y2)
     32        return {
     33          range = _G.make_range(x1, y1, x2, y2),
     34          message = msg,
     35          severity = 1,
     36        }
     37      end
     38 
     39      _G.make_warning = function(msg, x1, y1, x2, y2)
     40        return {
     41          range = _G.make_range(x1, y1, x2, y2),
     42          message = msg,
     43          severity = 2,
     44        }
     45      end
     46 
     47      _G.make_information = function(msg, x1, y1, x2, y2)
     48        return {
     49          range = _G.make_range(x1, y1, x2, y2),
     50          message = msg,
     51          severity = 3,
     52        }
     53      end
     54 
     55      function _G.get_extmarks(bufnr, client_id0)
     56        local namespace = vim.lsp.diagnostic.get_namespace(client_id0)
     57        local ns = vim.diagnostic.get_namespace(namespace)
     58        local extmarks = {}
     59        if ns.user_data.virt_text_ns then
     60          for _, e in
     61            pairs(
     62              vim.api.nvim_buf_get_extmarks(
     63                bufnr,
     64                ns.user_data.virt_text_ns,
     65                0,
     66                -1,
     67                { details = true }
     68              )
     69            )
     70          do
     71            table.insert(extmarks, e)
     72          end
     73        end
     74        if ns.user_data.underline_ns then
     75          for _, e in
     76            pairs(
     77              vim.api.nvim_buf_get_extmarks(
     78                bufnr,
     79                ns.user_data.underline_ns,
     80                0,
     81                -1,
     82                { details = true }
     83              )
     84            )
     85          do
     86            table.insert(extmarks, e)
     87          end
     88        end
     89        return extmarks
     90      end
     91    end)
     92 
     93    fake_uri = 'file:///fake/uri'
     94 
     95    exec_lua(function()
     96      diagnostic_bufnr = vim.uri_to_bufnr(fake_uri)
     97      local lines = { '1st line', '2nd line of text', 'wow', 'cool', 'more', 'lines' }
     98      vim.fn.bufload(diagnostic_bufnr)
     99      vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, 1, false, lines)
    100      vim.api.nvim_win_set_buf(0, diagnostic_bufnr)
    101    end)
    102  end)
    103 
    104  describe('vim.lsp.diagnostic.on_publish_diagnostics', function()
    105    before_each(function()
    106      exec_lua(function()
    107        client_id = assert(vim.lsp.start({
    108          cmd_env = {
    109            NVIM_LUA_NOTRACK = '1',
    110          },
    111          cmd = {
    112            vim.v.progpath,
    113            '-es',
    114            '-u',
    115            'NONE',
    116            '--headless',
    117          },
    118          offset_encoding = 'utf-16',
    119        }, { attach = false }))
    120      end)
    121    end)
    122 
    123    after_each(function()
    124      exec_lua(function()
    125        vim.lsp.get_client_by_id(client_id):stop()
    126        vim.api.nvim_exec_autocmds('VimLeavePre', { modeline = false })
    127      end)
    128    end)
    129 
    130    it('correctly handles UTF-16 offsets', function()
    131      local line = 'All 💼 and no 🎉 makes Jack a dull 👦'
    132      local result = exec_lua(function()
    133        vim.api.nvim_buf_set_lines(diagnostic_bufnr, 0, -1, false, { line })
    134 
    135        vim.lsp.diagnostic.on_publish_diagnostics(nil, {
    136          uri = fake_uri,
    137          diagnostics = {
    138            _G.make_error('UTF-16 Diagnostic', 0, 7, 0, 8),
    139          },
    140        }, { client_id = client_id })
    141 
    142        local diags = vim.diagnostic.get(diagnostic_bufnr)
    143        return diags
    144      end)
    145      eq(1, #result)
    146      eq(
    147        exec_lua(function()
    148          return vim.str_byteindex(line, 'utf-16', 7)
    149        end),
    150        result[1].col
    151      )
    152      eq(
    153        exec_lua(function()
    154          return vim.str_byteindex(line, 'utf-16', 8)
    155        end),
    156        result[1].end_col
    157      )
    158    end)
    159 
    160    it('does not create buffer on empty diagnostics', function()
    161      -- No buffer is created without diagnostics
    162      eq(
    163        -1,
    164        exec_lua(function()
    165          vim.lsp.diagnostic.on_publish_diagnostics(nil, {
    166            uri = 'file:///fake/uri2',
    167            diagnostics = {},
    168          }, { client_id = client_id })
    169          return vim.fn.bufnr(vim.uri_to_fname('file:///fake/uri2'))
    170        end)
    171      )
    172 
    173      -- Create buffer on diagnostics
    174      neq(
    175        -1,
    176        exec_lua(function()
    177          vim.lsp.diagnostic.on_publish_diagnostics(nil, {
    178            uri = 'file:///fake/uri2',
    179            diagnostics = {
    180              _G.make_error('Diagnostic', 0, 0, 0, 0),
    181            },
    182          }, { client_id = client_id })
    183          return vim.fn.bufnr(vim.uri_to_fname('file:///fake/uri2'))
    184        end)
    185      )
    186      eq(
    187        1,
    188        exec_lua(function()
    189          return #vim.diagnostic.get(_G.bufnr)
    190        end)
    191      )
    192 
    193      -- Clear diagnostics after buffer was created
    194      neq(
    195        -1,
    196        exec_lua(function()
    197          vim.lsp.diagnostic.on_publish_diagnostics(nil, {
    198            uri = 'file:///fake/uri2',
    199            diagnostics = {},
    200          }, { client_id = client_id })
    201          return vim.fn.bufnr(vim.uri_to_fname('file:///fake/uri2'))
    202        end)
    203      )
    204      eq(
    205        0,
    206        exec_lua(function()
    207          return #vim.diagnostic.get(_G.bufnr)
    208        end)
    209      )
    210    end)
    211 
    212    it('clears diagnostics for the client namespace on empty publish', function()
    213      local before_clear, after_clear = exec_lua(function()
    214        local ns = vim.lsp.diagnostic.get_namespace(client_id)
    215 
    216        -- Publish diagnostics
    217        vim.lsp.diagnostic.on_publish_diagnostics(nil, {
    218          uri = fake_uri,
    219          diagnostics = {
    220            _G.make_error('Diagnostic', 0, 0, 0, 0),
    221          },
    222        }, { client_id = client_id })
    223 
    224        local before_clear = vim.diagnostic.get(diagnostic_bufnr, { namespace = ns })
    225 
    226        -- Publish empty diagnostics
    227        vim.lsp.diagnostic.on_publish_diagnostics(nil, {
    228          uri = fake_uri,
    229          diagnostics = {},
    230        }, { client_id = client_id })
    231 
    232        local after_clear = vim.diagnostic.get(diagnostic_bufnr, { namespace = ns })
    233 
    234        return before_clear, after_clear
    235      end)
    236 
    237      eq(1, #before_clear)
    238      eq(0, #after_clear)
    239    end)
    240 
    241    it('clears diagnostics when buffer is deleted', function()
    242      local before_delete, after_delete = exec_lua(function()
    243        local ns = vim.lsp.diagnostic.get_namespace(client_id)
    244 
    245        -- Publish diagnostics
    246        vim.lsp.diagnostic.on_publish_diagnostics(nil, {
    247          uri = fake_uri,
    248          diagnostics = {
    249            _G.make_error('Diagnostic', 0, 0, 0, 0),
    250          },
    251        }, { client_id = client_id })
    252 
    253        local before_delete = vim.diagnostic.get(diagnostic_bufnr, { namespace = ns })
    254 
    255        -- Avoid deleting the currently displayed buffer (Windows teardown edge case)
    256        local scratch = vim.api.nvim_create_buf(false, true)
    257        vim.api.nvim_win_set_buf(0, scratch)
    258 
    259        -- Delete the buffer deterministically
    260        vim.api.nvim_buf_delete(diagnostic_bufnr, { force = true })
    261 
    262        -- Query remaining diagnostics via valid access path
    263        -- (deleted buffer cannot be queried directly)
    264        local after_delete = vim.diagnostic.get(nil, { namespace = ns })
    265 
    266        return before_delete, after_delete
    267      end)
    268 
    269      eq(1, #before_delete)
    270      eq(0, #after_delete)
    271    end)
    272  end)
    273 
    274  describe('vim.lsp.diagnostic.on_diagnostic', function()
    275    before_each(function()
    276      exec_lua(create_server_definition)
    277      exec_lua(function()
    278        _G.requests = 0
    279        _G.server = _G._create_server({
    280          capabilities = {
    281            diagnosticProvider = {},
    282          },
    283          handlers = {
    284            ['textDocument/diagnostic'] = function(_, params)
    285              _G.params = params
    286              _G.requests = _G.requests + 1
    287            end,
    288          },
    289        })
    290 
    291        function _G.get_extmarks(bufnr, client_id0)
    292          local namespace = vim.lsp.diagnostic.get_namespace(client_id0, true)
    293          local ns = vim.diagnostic.get_namespace(namespace)
    294          local extmarks = {}
    295          if ns.user_data.virt_text_ns then
    296            for _, e in
    297              pairs(
    298                vim.api.nvim_buf_get_extmarks(
    299                  bufnr,
    300                  ns.user_data.virt_text_ns,
    301                  0,
    302                  -1,
    303                  { details = true }
    304                )
    305              )
    306            do
    307              table.insert(extmarks, e)
    308            end
    309          end
    310          if ns.user_data.underline_ns then
    311            for _, e in
    312              pairs(
    313                vim.api.nvim_buf_get_extmarks(
    314                  bufnr,
    315                  ns.user_data.underline_ns,
    316                  0,
    317                  -1,
    318                  { details = true }
    319                )
    320              )
    321            do
    322              table.insert(extmarks, e)
    323            end
    324          end
    325          return extmarks
    326        end
    327 
    328        client_id = assert(vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }))
    329      end)
    330    end)
    331 
    332    it('adds diagnostics to vim.diagnostics', function()
    333      local diags = exec_lua(function()
    334        vim.lsp.diagnostic.on_diagnostic(nil, {
    335          kind = 'full',
    336          items = {
    337            _G.make_error('Pull Diagnostic', 4, 4, 4, 4),
    338          },
    339        }, {
    340          params = {
    341            textDocument = { uri = fake_uri },
    342          },
    343          uri = fake_uri,
    344          client_id = client_id,
    345          bufnr = diagnostic_bufnr,
    346        }, {})
    347 
    348        return vim.diagnostic.get(diagnostic_bufnr)
    349      end)
    350      eq(1, #diags)
    351      eq('Pull Diagnostic', diags[1].message)
    352    end)
    353 
    354    it('preserves push diagnostics when pull diagnostics are empty', function()
    355      local push_ns_count, pull_ns_count, all_diags_count, push_ns, pull_ns = exec_lua(function()
    356        vim.lsp.diagnostic.on_publish_diagnostics(nil, {
    357          uri = fake_uri,
    358          diagnostics = {
    359            _G.make_error('Push Diagnostic', 0, 0, 0, 0),
    360          },
    361        }, { client_id = client_id })
    362 
    363        vim.lsp.diagnostic.on_diagnostic(nil, {
    364          kind = 'full',
    365          items = {},
    366        }, {
    367          params = {
    368            textDocument = { uri = fake_uri },
    369          },
    370          uri = fake_uri,
    371          client_id = client_id,
    372          bufnr = diagnostic_bufnr,
    373        }, {})
    374 
    375        local push_ns = vim.lsp.diagnostic.get_namespace(client_id, false)
    376        local pull_ns = vim.lsp.diagnostic.get_namespace(client_id, true)
    377 
    378        return #vim.diagnostic.get(diagnostic_bufnr, { namespace = push_ns }),
    379          #vim.diagnostic.get(diagnostic_bufnr, { namespace = pull_ns }),
    380          #vim.diagnostic.get(diagnostic_bufnr),
    381          push_ns,
    382          pull_ns
    383      end)
    384 
    385      eq(1, push_ns_count)
    386      eq(0, pull_ns_count)
    387      eq(1, all_diags_count)
    388      neq(push_ns, pull_ns)
    389    end)
    390 
    391    it('uses pull_id to isolate pull diagnostic namespaces', function()
    392      local first_count, second_count, total_count, first_ns, second_ns = exec_lua(function()
    393        vim.lsp.diagnostic.on_diagnostic(nil, {
    394          kind = 'full',
    395          items = {
    396            _G.make_error('Pull Diagnostic A', 0, 0, 0, 0),
    397          },
    398        }, {
    399          params = {
    400            identifier = 'provider-a',
    401            textDocument = { uri = fake_uri },
    402          },
    403          uri = fake_uri,
    404          client_id = client_id,
    405          bufnr = diagnostic_bufnr,
    406        }, {})
    407 
    408        vim.lsp.diagnostic.on_diagnostic(nil, {
    409          kind = 'full',
    410          items = {},
    411        }, {
    412          params = {
    413            identifier = 'provider-b',
    414            textDocument = { uri = fake_uri },
    415          },
    416          uri = fake_uri,
    417          client_id = client_id,
    418          bufnr = diagnostic_bufnr,
    419        }, {})
    420 
    421        local first_ns = vim.lsp.diagnostic.get_namespace(client_id, 'provider-a')
    422        local second_ns = vim.lsp.diagnostic.get_namespace(client_id, 'provider-b')
    423 
    424        return #vim.diagnostic.get(diagnostic_bufnr, { namespace = first_ns }),
    425          #vim.diagnostic.get(diagnostic_bufnr, { namespace = second_ns }),
    426          #vim.diagnostic.get(diagnostic_bufnr),
    427          first_ns,
    428          second_ns
    429      end)
    430 
    431      eq(1, first_count)
    432      eq(0, second_count)
    433      eq(1, total_count)
    434      neq(first_ns, second_ns)
    435    end)
    436 
    437    it('handles multiline diagnostic ranges #33782', function()
    438      local diags = exec_lua(function()
    439        vim.lsp.diagnostic.on_diagnostic(nil, {
    440          kind = 'full',
    441          items = {
    442            _G.make_error('Pull Diagnostic', 0, 6, 1, 10),
    443          },
    444        }, {
    445          params = {
    446            textDocument = { uri = fake_uri },
    447          },
    448          uri = fake_uri,
    449          client_id = client_id,
    450          bufnr = diagnostic_bufnr,
    451        }, {})
    452 
    453        return vim.diagnostic.get(diagnostic_bufnr)
    454      end)
    455      local lines = exec_lua(function()
    456        return vim.api.nvim_buf_get_lines(diagnostic_bufnr, 0, -1, false)
    457      end)
    458      -- This test case must be run over a multiline diagnostic in which the start line is shorter
    459      -- than the end line, and the end_col exceeds the start line's length.
    460      eq(#lines[1], 8)
    461      eq(#lines[2], 16)
    462      eq(1, #diags)
    463      eq(6, diags[1].col)
    464      eq(10, diags[1].end_col)
    465    end)
    466 
    467    it('severity defaults to error if missing', function()
    468      ---@type vim.Diagnostic[]
    469      local diagnostics = exec_lua(function()
    470        vim.lsp.diagnostic.on_diagnostic(nil, {
    471          kind = 'full',
    472          items = {
    473            {
    474              range = _G.make_range(4, 4, 4, 4),
    475              message = 'bad!',
    476            },
    477          },
    478        }, {
    479          params = {
    480            textDocument = { uri = fake_uri },
    481          },
    482          uri = fake_uri,
    483          client_id = client_id,
    484          bufnr = diagnostic_bufnr,
    485        }, {})
    486        return vim.diagnostic.get(diagnostic_bufnr)
    487      end)
    488      eq(1, #diagnostics)
    489      eq(1, diagnostics[1].severity)
    490    end)
    491 
    492    it('clears diagnostics when client detaches', function()
    493      exec_lua(function()
    494        vim.lsp.diagnostic.on_diagnostic(nil, {
    495          kind = 'full',
    496          items = {
    497            _G.make_error('Pull Diagnostic', 4, 4, 4, 4),
    498          },
    499        }, {
    500          params = {
    501            textDocument = { uri = fake_uri },
    502          },
    503          uri = fake_uri,
    504          client_id = client_id,
    505          bufnr = diagnostic_bufnr,
    506        }, {})
    507      end)
    508 
    509      eq(
    510        1,
    511        exec_lua(function()
    512          return #vim.diagnostic.get(diagnostic_bufnr)
    513        end)
    514      )
    515 
    516      exec_lua(function()
    517        vim.lsp.get_client_by_id(client_id):stop()
    518      end)
    519 
    520      eq(
    521        0,
    522        exec_lua(function()
    523          return #vim.diagnostic.get(diagnostic_bufnr)
    524        end)
    525      )
    526    end)
    527 
    528    it('keeps diagnostics when one client detaches and others still are attached', function()
    529      exec_lua(function()
    530        _G.client_id2 = assert(vim.lsp.start({ name = 'dummy2', cmd = _G.server.cmd }))
    531 
    532        vim.lsp.diagnostic.on_diagnostic(nil, {
    533          kind = 'full',
    534          items = {
    535            _G.make_error('Pull Diagnostic', 4, 4, 4, 4),
    536          },
    537        }, {
    538          params = {
    539            textDocument = { uri = fake_uri },
    540          },
    541          uri = fake_uri,
    542          client_id = client_id,
    543          bufnr = diagnostic_bufnr,
    544        }, {})
    545      end)
    546 
    547      eq(
    548        1,
    549        exec_lua(function()
    550          return #vim.diagnostic.get(diagnostic_bufnr)
    551        end)
    552      )
    553 
    554      exec_lua(function()
    555        vim.lsp.get_client_by_id(_G.client_id2):stop()
    556      end)
    557 
    558      eq(
    559        1,
    560        exec_lua(function()
    561          return #vim.diagnostic.get(diagnostic_bufnr)
    562        end)
    563      )
    564    end)
    565 
    566    it('handles server cancellation', function()
    567      eq(
    568        1,
    569        exec_lua(function()
    570          vim.lsp.diagnostic.on_diagnostic({
    571            code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
    572            -- Empty data defaults to retriggering request
    573            data = {},
    574            message = '',
    575          }, {}, {
    576            method = 'textDocument/diagnostic',
    577            client_id = client_id,
    578            bufnr = diagnostic_bufnr,
    579          })
    580 
    581          return _G.requests
    582        end)
    583      )
    584 
    585      eq(
    586        2,
    587        exec_lua(function()
    588          vim.lsp.diagnostic.on_diagnostic({
    589            code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
    590            data = { retriggerRequest = true },
    591            message = '',
    592          }, {}, {
    593            method = 'textDocument/diagnostic',
    594            client_id = client_id,
    595            bufnr = diagnostic_bufnr,
    596          })
    597 
    598          return _G.requests
    599        end)
    600      )
    601 
    602      eq(
    603        2,
    604        exec_lua(function()
    605          vim.lsp.diagnostic.on_diagnostic({
    606            code = vim.lsp.protocol.ErrorCodes.ServerCancelled,
    607            data = { retriggerRequest = false },
    608            message = '',
    609          }, {}, {
    610            method = 'textDocument/diagnostic',
    611            client_id = client_id,
    612            bufnr = diagnostic_bufnr,
    613          })
    614 
    615          return _G.requests
    616        end)
    617      )
    618    end)
    619 
    620    it('supports dynamic registration', function()
    621      exec_lua(create_server_definition)
    622      exec_lua(function()
    623        _G.server2 = _G._create_server({
    624          diagnosticProvider = {
    625            documentSelector = vim.NIL,
    626          },
    627          handlers = {
    628            ['textDocument/diagnostic'] = function(_, _, callback)
    629              callback(nil, {
    630                kind = 'full',
    631                items = {
    632                  _G.make_error('Dynamic Diagnostic', 4, 4, 4, 4),
    633                },
    634              })
    635            end,
    636          },
    637        })
    638 
    639        local client_id2 = assert(vim.lsp.start({ name = 'dummy2', cmd = _G.server2.cmd }))
    640 
    641        vim.lsp.handlers['client/registerCapability'](nil, {
    642          registrations = {
    643            { id = 'diagnostic', method = 'textDocument/diagnostic' },
    644          },
    645        }, { client_id = client_id2, method = 'client/registerCapability' })
    646      end)
    647 
    648      eq(
    649        1,
    650        exec_lua(function()
    651          return #vim.diagnostic.get(diagnostic_bufnr)
    652        end)
    653      )
    654    end)
    655 
    656    it('requests with the `previousResultId`', function()
    657      -- Full reports
    658      eq(
    659        'dummy_server',
    660        exec_lua(function()
    661          vim.lsp.diagnostic.on_diagnostic(nil, {
    662            kind = 'full',
    663            resultId = 'dummy_server',
    664            items = {
    665              _G.make_error('Pull Diagnostic', 4, 4, 4, 4),
    666            },
    667          }, {
    668            method = 'textDocument/diagnostic',
    669            params = {
    670              textDocument = { uri = fake_uri },
    671            },
    672            client_id = client_id,
    673            bufnr = diagnostic_bufnr,
    674          })
    675          vim.api.nvim_exec_autocmds('LspNotify', {
    676            buffer = diagnostic_bufnr,
    677            data = {
    678              method = 'textDocument/didChange',
    679              client_id = client_id,
    680            },
    681          })
    682          return _G.params.previousResultId
    683        end)
    684      )
    685 
    686      -- Unchanged reports
    687      eq(
    688        'squidward',
    689        exec_lua(function()
    690          vim.lsp.diagnostic.on_diagnostic(nil, {
    691            kind = 'unchanged',
    692            resultId = 'squidward',
    693          }, {
    694            method = 'textDocument/diagnostic',
    695            params = {
    696              textDocument = { uri = fake_uri },
    697            },
    698            client_id = client_id,
    699            bufnr = diagnostic_bufnr,
    700          })
    701          vim.api.nvim_exec_autocmds('LspNotify', {
    702            buffer = diagnostic_bufnr,
    703            data = {
    704              method = 'textDocument/didChange',
    705              client_id = client_id,
    706            },
    707          })
    708          return _G.params.previousResultId
    709        end)
    710      )
    711    end)
    712 
    713    it('handles relatedDocuments diagnostics', function()
    714      local fake_uri_2 = 'file:///fake/uri2'
    715      ---@type vim.Diagnostic[], vim.Diagnostic[], string?
    716      local diagnostics, related_diagnostics, relatedPreviousResultId = exec_lua(function()
    717        local second_buf = vim.uri_to_bufnr(fake_uri_2)
    718        vim.fn.bufload(second_buf)
    719 
    720        -- Attach the client to both buffers.
    721        vim.api.nvim_win_set_buf(0, second_buf)
    722        vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd })
    723 
    724        vim.lsp.diagnostic.on_diagnostic(nil, {
    725          kind = 'full',
    726          relatedDocuments = {
    727            [fake_uri_2] = {
    728              kind = 'full',
    729              resultId = 'spongebob',
    730              items = {
    731                {
    732                  range = _G.make_range(4, 4, 4, 4),
    733                  message = 'related bad!',
    734                },
    735              },
    736            },
    737          },
    738          items = {},
    739        }, {
    740          params = {
    741            textDocument = { uri = fake_uri },
    742          },
    743          uri = fake_uri,
    744          client_id = client_id,
    745          bufnr = diagnostic_bufnr,
    746        }, {})
    747 
    748        vim.api.nvim_exec_autocmds('LspNotify', {
    749          buffer = second_buf,
    750          data = {
    751            method = 'textDocument/didChange',
    752            client_id = client_id,
    753          },
    754        })
    755 
    756        return vim.diagnostic.get(diagnostic_bufnr),
    757          vim.diagnostic.get(second_buf),
    758          _G.params.previousResultId
    759      end)
    760      eq(0, #diagnostics)
    761      eq(1, #related_diagnostics)
    762      eq('related bad!', related_diagnostics[1].message)
    763      eq('spongebob', relatedPreviousResultId)
    764    end)
    765  end)
    766 
    767  describe('vim.lsp.diagnostic.on_refresh', function()
    768    it('refreshes diagnostics on server-to-client request', function()
    769      exec_lua(create_server_definition)
    770      exec_lua(function()
    771        _G.requests = 0
    772        _G.server = _G._create_server({
    773          capabilities = {
    774            diagnosticProvider = {},
    775          },
    776          handlers = {
    777            ['textDocument/diagnostic'] = function(_, _, callback)
    778              _G.requests = _G.requests + 1
    779              callback(nil, {
    780                kind = 'full',
    781                items = {
    782                  _G.make_warning('Pull Diagnostic', 4, 4, 4, 4),
    783                },
    784              })
    785            end,
    786          },
    787        })
    788        client_id = assert(vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }))
    789      end)
    790 
    791      local diags = exec_lua(function()
    792        vim.lsp.diagnostic.on_diagnostic(nil, {
    793          kind = 'full',
    794          items = {
    795            _G.make_error('Pull Diagnostic', 4, 4, 4, 4),
    796          },
    797        }, {
    798          params = {
    799            textDocument = { uri = fake_uri },
    800          },
    801          uri = fake_uri,
    802          client_id = client_id,
    803          bufnr = diagnostic_bufnr,
    804        }, {})
    805 
    806        return vim.diagnostic.get(diagnostic_bufnr)
    807      end)
    808      eq(1, #diags)
    809      eq(1, diags[1].severity)
    810 
    811      local requests, refreshed_diags = exec_lua(function()
    812        vim.lsp.diagnostic.on_refresh(nil, nil, {
    813          method = 'workspace/diagnostic/refresh',
    814          client_id = client_id,
    815        })
    816 
    817        return _G.requests, vim.diagnostic.get(diagnostic_bufnr)
    818      end)
    819      eq(1, requests)
    820      eq(1, #refreshed_diags)
    821      eq(2, refreshed_diags[1].severity)
    822    end)
    823 
    824    it('refreshes workspace diagnostics', function()
    825      local fake_uri_3 = 'file:///fake/uri3'
    826      exec_lua(create_server_definition)
    827      exec_lua(function()
    828        _G.requests = 0
    829        _G.server = _G._create_server({
    830          capabilities = {
    831            diagnosticProvider = { workspaceDiagnostics = true },
    832          },
    833          handlers = {
    834            ['workspace/diagnostic'] = function(_, _, callback)
    835              _G.requests = _G.requests + 1
    836              callback(nil, {
    837                items = {
    838                  {
    839                    kind = 'full',
    840                    uri = fake_uri_3,
    841                    items = {
    842                      _G.make_error('Workspace Diagnostic', 4, 4, 4, 4),
    843                    },
    844                  },
    845                },
    846              })
    847            end,
    848          },
    849        })
    850        client_id = assert(vim.lsp.start({ name = 'dummy', cmd = _G.server.cmd }))
    851      end)
    852 
    853      eq(
    854        0,
    855        exec_lua(function()
    856          return #vim.diagnostic.get()
    857        end)
    858      )
    859 
    860      eq(
    861        { vim.NIL },
    862        exec_lua(function()
    863          local client = vim.lsp.get_client_by_id(client_id)
    864          assert(client)
    865          return client:_provider_value_get('workspace/diagnostic', 'identifier')
    866        end)
    867      )
    868 
    869      local requests, diags = exec_lua(function()
    870        vim.lsp.diagnostic.on_refresh(nil, nil, {
    871          method = 'workspace/diagnostic/refresh',
    872          client_id = client_id,
    873        })
    874 
    875        return _G.requests, vim.diagnostic.get()
    876      end)
    877      eq(1, requests)
    878      eq(1, #diags)
    879      eq(1, diags[1].severity)
    880    end)
    881  end)
    882 end)