Skip to content

Pass fname and cwd to parse function #246

@mothzarella

Description

@mothzarella

Problem X you are solving:

Some linters output diagnostics for multiple files in a single run. For example, terraform validate always validates the entire terraform configuration and returns diagnostics for all .tf files. I need to filter diagnostics to show only those relevant to the current buffer.

Problems encountered under the status quo:

Right now, parse only gets (result, bufnr) as parameters. The issue is that parse runs inside async.run, which means it's in a fast event context where you can't call neovim api.

parse = function(result, bufnr)
  -- E5560: must not be called in a fast event context
  local fname = vim.api.nvim_buf_get_name(bufnr)
  
  -- This also doesn't work :(
  local fname = vim.fn.bufname(bufnr)
end

Suggested feature Y:

You're already capturing fname and cwd before the async context (line 82 in guard/lint.lua). Could you pass them to parse too?

-- Current (line ~107,122)
if #data > 0 then
  results = lint.parse(data, buf)
end

-- Proposed
if #data > 0 then
  results = lint.parse(data, buf, fname, cwd)
end

How to solve X now:

The only workaround I found is to capture the filename before vim.system and inject it through the linter output:

fn = function(acc)
  local co = assert(coroutine.running())
  
  vim.system({ 'terraform', 'validate', '-json' }, {...}, function(result)
    -- I also tried wrapping the code in a `vim.schedule`
    local rpath = vim.fn.expand('%:.')
    local decoded = vim.json.decode(result.stdout)
    decoded.current_file = rpath
    coroutine.resume(co, vim.json.encode(decoded))
  end)

  ...

end

I tried using vim.schedule in the callback to exit the fast context, but that breaks the coroutine flow and parse never gets called.

How can X be solved after feature Y is implmented:

parse = function(result, bufnr, fname, cwd)
  local current_file = fname:gsub('^%. /', '')
  local decoded = vim.json.decode(result)
  local diagnostics = {}
  
  for _, diag in ipairs(decoded.diagnostics or {}) do
    local diag_file = diag.range.filename:gsub('^%.  /', '')
    if diag_file == current_file then
      table.insert(diagnostics, diag)
    end
  end
  
  return diagnostics
end

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions