Skip to content

Pass fname and cwd to parse function #246

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

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions