bug: Elixir support #163

Closed
opened 2026-09-21 18:12:39 +00:00 by barrettruth · 8 comments
Owner

Original issue: barrettruth/diffs.nvim#370
Original author: srcrip
Original date: 2026-05-28T19:20:18Z

Prerequisites

Neovim version

nvim --version
NVIM v0.12.0-dev-934+ga9a4c271b1-Homebrew
Build type: Release
LuaJIT 2.1.1753364724
Run "nvim -V1 -v" for more info

Operating system

MacOS

Description

Hi there,

This is a reopening of: https://github.com/barrettruth/diffs.nvim/issues/221

I would love to reopen that issue but the permissions on this repo don't actually allow just anyone to re-open issues so if you want people to be able to I think you need to adjust the permissions...

I can confirm that, using the repro script to ensure a clean install (ie I am like 99.9% sure there is not another plugin interfering here), I do not get treesitter highlighting in fugitive with this plugin when I'm working on elixir repos. I get it in other langs, but not elixir.

Here is a screenshot of an elixir project with git changes open using the clean repro script:

Image

Let me know if there's any more info I can provide

Steps to reproduce

  1. Install this plugin and fugitive
  2. clone an elixir project, edit some random files (ideally elixir, heex, etc)
  3. open fugitive status buffer and see that the diff is identical to regular fugitive diff, its not using treesitter groups in any way

Health check

==============================================================================
diffs:                                                                      ✅

diffs.nvim ~
- ✅ OK Neovim 0.9.0+ detected
- ✅ OK vim-fugitive detected
- libvscode_diff not found (optional, using native vim.diff fallback)

Minimal reproduction

local host_data_site = vim.fn.stdpath('data') .. '/site'
local root = vim.fn.fnamemodify('.repro', ':p')
vim.fn.mkdir(root, 'p')
vim.env.XDG_CONFIG_HOME = root
vim.env.XDG_DATA_HOME = root
vim.env.XDG_STATE_HOME = root
vim.env.XDG_CACHE_HOME = root

local data_site = vim.fn.stdpath('data') .. '/site'
vim.fn.mkdir(data_site, 'p')
vim.opt.packpath:prepend(data_site)
if vim.uv.fs_stat(host_data_site) then
  vim.opt.runtimepath:append(host_data_site)
end

vim.o.background = 'dark'
vim.cmd.colorscheme('habamax')

vim.g.diffs = {
  debug = '/tmp/diffs.log',
  integrations = {
    fugitive = true,
    neogit = true,
    gitsigns = true,
    committia = true,
    telescope = true,
  },
}

local plugins = {
  { name = 'vim-fugitive', src = 'https://github.com/tpope/vim-fugitive' },
  { name = 'plenary.nvim', src = 'https://github.com/nvim-lua/plenary.nvim' },
  { name = 'neogit', src = 'https://github.com/NeogitOrg/neogit' },
  { name = 'gitsigns.nvim', src = 'https://github.com/lewis6991/gitsigns.nvim' },
  { name = 'committia.vim', src = 'https://github.com/rhysd/committia.vim' },
  { name = 'telescope.nvim', src = 'https://github.com/nvim-telescope/telescope.nvim' },
  { name = 'diffs.nvim', src = 'https://github.com/barrettruth/diffs.nvim' },
}

local uv = vim.uv or vim.loop

local function clone(src, path)
  if uv.fs_stat(path) then
    return
  end
  vim.fn.mkdir(vim.fn.fnamemodify(path, ':h'), 'p')
  local out = vim.fn.system({ 'git', 'clone', '--filter=blob:none', src, path })
  if vim.v.shell_error ~= 0 then
    error(out)
  end
end

if vim.pack then
  vim.pack.add(vim.tbl_map(function(plugin)
    return { src = plugin.src, name = plugin.name }
  end, plugins), { confirm = false })
else
  for _, plugin in ipairs(plugins) do
    local path = data_site .. '/pack/repro/start/' .. plugin.name
    clone(plugin.src, path)
    vim.opt.runtimepath:prepend(path)
  end
end

require('gitsigns').setup()
require('neogit').setup({})
> Original issue: barrettruth/diffs.nvim#370 > Original author: `srcrip` > Original date: 2026-05-28T19:20:18Z ### Prerequisites - [x] I have searched [existing issues](https://github.com/barrettruth/diffs.nvim/issues) - [x] I have updated to the latest version ### Neovim version ```text nvim --version NVIM v0.12.0-dev-934+ga9a4c271b1-Homebrew Build type: Release LuaJIT 2.1.1753364724 Run "nvim -V1 -v" for more info ``` ### Operating system MacOS ### Description Hi there, This is a reopening of: https://github.com/barrettruth/diffs.nvim/issues/221 I would love to reopen that issue but the permissions on this repo don't actually allow just anyone to re-open issues so if you want people to be able to I think you need to adjust the permissions... I can confirm that, using the repro script to ensure a clean install (ie I am like 99.9% sure there is not another plugin interfering here), I do not get treesitter highlighting in fugitive with this plugin when I'm working on elixir repos. I get it in other langs, but not elixir. Here is a screenshot of an elixir project with git changes open using the clean repro script: <img width="1136" height="414" alt="Image" src="https://forge.barrettruth.com/attachments/54d50622-92b0-4b27-8a5f-d694a3db1ddc" /> Let me know if there's any more info I can provide ### Steps to reproduce 1. Install this plugin and fugitive 2. clone an elixir project, edit some random files (ideally elixir, heex, etc) 3. open fugitive status buffer and see that the diff is identical to regular fugitive diff, its not using treesitter groups in any way ### Health check ```text ============================================================================== diffs: ✅ diffs.nvim ~ - ✅ OK Neovim 0.9.0+ detected - ✅ OK vim-fugitive detected - libvscode_diff not found (optional, using native vim.diff fallback) ``` ### Minimal reproduction ```lua local host_data_site = vim.fn.stdpath('data') .. '/site' local root = vim.fn.fnamemodify('.repro', ':p') vim.fn.mkdir(root, 'p') vim.env.XDG_CONFIG_HOME = root vim.env.XDG_DATA_HOME = root vim.env.XDG_STATE_HOME = root vim.env.XDG_CACHE_HOME = root local data_site = vim.fn.stdpath('data') .. '/site' vim.fn.mkdir(data_site, 'p') vim.opt.packpath:prepend(data_site) if vim.uv.fs_stat(host_data_site) then vim.opt.runtimepath:append(host_data_site) end vim.o.background = 'dark' vim.cmd.colorscheme('habamax') vim.g.diffs = { debug = '/tmp/diffs.log', integrations = { fugitive = true, neogit = true, gitsigns = true, committia = true, telescope = true, }, } local plugins = { { name = 'vim-fugitive', src = 'https://github.com/tpope/vim-fugitive' }, { name = 'plenary.nvim', src = 'https://github.com/nvim-lua/plenary.nvim' }, { name = 'neogit', src = 'https://github.com/NeogitOrg/neogit' }, { name = 'gitsigns.nvim', src = 'https://github.com/lewis6991/gitsigns.nvim' }, { name = 'committia.vim', src = 'https://github.com/rhysd/committia.vim' }, { name = 'telescope.nvim', src = 'https://github.com/nvim-telescope/telescope.nvim' }, { name = 'diffs.nvim', src = 'https://github.com/barrettruth/diffs.nvim' }, } local uv = vim.uv or vim.loop local function clone(src, path) if uv.fs_stat(path) then return end vim.fn.mkdir(vim.fn.fnamemodify(path, ':h'), 'p') local out = vim.fn.system({ 'git', 'clone', '--filter=blob:none', src, path }) if vim.v.shell_error ~= 0 then error(out) end end if vim.pack then vim.pack.add(vim.tbl_map(function(plugin) return { src = plugin.src, name = plugin.name } end, plugins), { confirm = false }) else for _, plugin in ipairs(plugins) do local path = data_site .. '/pack/repro/start/' .. plugin.name clone(plugin.src, path) vim.opt.runtimepath:prepend(path) end end require('gitsigns').setup() require('neogit').setup({}) ```
barrettruth 2026-09-21 18:12:39 +00:00
  • closed this issue
  • added the
    bug
    label
Author
Owner

Original comment: barrettruth/diffs.nvim#370, comment 4630163814
Original author: barrettruth
Original date: 2026-06-05T09:41:25Z

The following reproduction is unable to create the problem. Run it from your shell. It clones the repo into /tmp and runs neovim with --clean -u NONE.

The likely issue is that your clean repro exposes parser .so files but not the nvim-treesitter runtime queries. Please check:

:lua for _, l in ipairs({ "elixir", "heex" }) do print(l, pcall(vim.treesitter.language.inspect, l), vim.treesitter.query.get(l, "highlights") ~= nil) end

which should be all true. Also, try with other non-bundled languages.

I am highly suspicious that you just have some parser-path-treesitter-loading-funkiness going on in your configuration.

Once again, I am going to close this. If the following repro works and, of course, you agree with it, do not reopen the issue - feel free to comment and I'll respond, but this is more of a setup-debugging thing than a problem with diffs.nvim. If you think I'm fundamentally mistaken on something here, feel free to say so and I'll reopen.

root=$(mktemp -d "${TMPDIR:-/tmp}/diffs-elixir.XXXXXX") && git clone --depth 1 https://github.com/barrettruth/diffs.nvim "$root/diffs.nvim" >/dev/null && cat > "$root/repro.diff" <<'DIFF'
diff --git a/lib/demo.ex b/lib/demo.ex
index 1111111..2222222 100644
--- a/lib/demo.ex
+++ b/lib/demo.ex
@@ -1,5 +1,7 @@
 defmodule Demo do
   def greeting(name) do
-    "hello #{name}"
+    String.upcase("hello #{name}")
   end
+
+  def farewell(name), do: "bye #{name}"
 end
diff --git a/lib/demo_web/live/page_live.html.heex b/lib/demo_web/live/page_live.html.heex
index 3333333..4444444 100644
--- a/lib/demo_web/live/page_live.html.heex
+++ b/lib/demo_web/live/page_live.html.heex
@@ -1,3 +1,4 @@
 <div id="page">
   <h1><%= @title %></h1>
+  <p :if={@notice}><%= @notice %></p>
 </div>
DIFF
cat > "$root/init.lua" <<EOF
local root = [[${root}]]
vim.g.diffs = { debug = root .. "/diffs.log" }
vim.opt.runtimepath:prepend(root .. "/diffs.nvim")

local data_site = vim.fn.stdpath("data") .. "/site"
vim.opt.runtimepath:append(data_site)
for _, pat in ipairs({ "*/start/nvim-treesitter", "*/opt/nvim-treesitter" }) do
  for _, p in ipairs(vim.fn.globpath(data_site .. "/pack", pat, true, true)) do
    vim.opt.runtimepath:append(p)
  end
end

vim.cmd("runtime plugin/diffs.lua")
vim.api.nvim_create_autocmd("VimEnter", {
  once = true,
  callback = function()
    vim.bo.filetype = "git"
    local runtime = require("diffs.runtime")
    runtime.attach(0)
    runtime._test.ensure_cache(0)

    local ns = vim.api.nvim_get_namespaces().diffs
    vim.api.nvim_buf_clear_namespace(0, ns, 0, -1)

    local hl = require("diffs.highlight")
    local opts = runtime.get_highlight_opts()
    local entry = runtime._test.hunk_cache[0]
    for _, h in ipairs(entry and entry.hunks or {}) do
      hl.highlight_hunk(0, ns, h, opts)
    end

    local function query(lang)
      local inspect_ok = pcall(vim.treesitter.language.inspect, lang)
      local query_ok, q = pcall(vim.treesitter.query.get, lang, "highlights")
      return string.format("%s inspect=%s query=%s", lang, tostring(inspect_ok), tostring(query_ok and q ~= nil))
    end

    local counts = { elixir = 0, heex = 0 }
    for _, m in ipairs(vim.api.nvim_buf_get_extmarks(0, ns, 0, -1, { details = true })) do
      local g = (m[4] or {}).hl_group
      if type(g) == "string" then
        if g:find(".elixir", 1, true) then counts.elixir = counts.elixir + 1 end
        if g:find(".heex", 1, true) then counts.heex = counts.heex + 1 end
      end
    end

    print(query("elixir"))
    print(query("heex"))
    print("elixir_extmarks=" .. counts.elixir .. " heex_extmarks=" .. counts.heex)
    print("log=" .. root .. "/diffs.log")
  end,
})
EOF
nvim --clean -u "$root/init.lua" "$root/repro.diff"
> Original comment: barrettruth/diffs.nvim#370, comment 4630163814 > Original author: `barrettruth` > Original date: 2026-06-05T09:41:25Z The following reproduction is unable to create the problem. Run it from your shell. It clones the repo into `/tmp` and runs neovim with `--clean -u NONE`. The likely issue is that your clean repro exposes parser .so files but not the nvim-treesitter runtime queries. Please check: `:lua for _, l in ipairs({ "elixir", "heex" }) do print(l, pcall(vim.treesitter.language.inspect, l), vim.treesitter.query.get(l, "highlights") ~= nil) end` which should be all true. Also, try with other non-bundled languages. I am _highly_ suspicious that you just have some parser-path-treesitter-loading-funkiness going on in your configuration. Once again, I am going to close this. ***If the following repro works and, of course, you agree with it, do not reopen the issue - feel free to comment and I'll respond, but this is more of a setup-debugging thing than a problem with diffs.nvim***. If you think I'm fundamentally mistaken on something here, feel free to say so and I'll reopen. ```shell root=$(mktemp -d "${TMPDIR:-/tmp}/diffs-elixir.XXXXXX") && git clone --depth 1 https://github.com/barrettruth/diffs.nvim "$root/diffs.nvim" >/dev/null && cat > "$root/repro.diff" <<'DIFF' diff --git a/lib/demo.ex b/lib/demo.ex index 1111111..2222222 100644 --- a/lib/demo.ex +++ b/lib/demo.ex @@ -1,5 +1,7 @@ defmodule Demo do def greeting(name) do - "hello #{name}" + String.upcase("hello #{name}") end + + def farewell(name), do: "bye #{name}" end diff --git a/lib/demo_web/live/page_live.html.heex b/lib/demo_web/live/page_live.html.heex index 3333333..4444444 100644 --- a/lib/demo_web/live/page_live.html.heex +++ b/lib/demo_web/live/page_live.html.heex @@ -1,3 +1,4 @@ <div id="page"> <h1><%= @title %></h1> + <p :if={@notice}><%= @notice %></p> </div> DIFF cat > "$root/init.lua" <<EOF local root = [[${root}]] vim.g.diffs = { debug = root .. "/diffs.log" } vim.opt.runtimepath:prepend(root .. "/diffs.nvim") local data_site = vim.fn.stdpath("data") .. "/site" vim.opt.runtimepath:append(data_site) for _, pat in ipairs({ "*/start/nvim-treesitter", "*/opt/nvim-treesitter" }) do for _, p in ipairs(vim.fn.globpath(data_site .. "/pack", pat, true, true)) do vim.opt.runtimepath:append(p) end end vim.cmd("runtime plugin/diffs.lua") vim.api.nvim_create_autocmd("VimEnter", { once = true, callback = function() vim.bo.filetype = "git" local runtime = require("diffs.runtime") runtime.attach(0) runtime._test.ensure_cache(0) local ns = vim.api.nvim_get_namespaces().diffs vim.api.nvim_buf_clear_namespace(0, ns, 0, -1) local hl = require("diffs.highlight") local opts = runtime.get_highlight_opts() local entry = runtime._test.hunk_cache[0] for _, h in ipairs(entry and entry.hunks or {}) do hl.highlight_hunk(0, ns, h, opts) end local function query(lang) local inspect_ok = pcall(vim.treesitter.language.inspect, lang) local query_ok, q = pcall(vim.treesitter.query.get, lang, "highlights") return string.format("%s inspect=%s query=%s", lang, tostring(inspect_ok), tostring(query_ok and q ~= nil)) end local counts = { elixir = 0, heex = 0 } for _, m in ipairs(vim.api.nvim_buf_get_extmarks(0, ns, 0, -1, { details = true })) do local g = (m[4] or {}).hl_group if type(g) == "string" then if g:find(".elixir", 1, true) then counts.elixir = counts.elixir + 1 end if g:find(".heex", 1, true) then counts.heex = counts.heex + 1 end end end print(query("elixir")) print(query("heex")) print("elixir_extmarks=" .. counts.elixir .. " heex_extmarks=" .. counts.heex) print("log=" .. root .. "/diffs.log") end, }) EOF nvim --clean -u "$root/init.lua" "$root/repro.diff" ```
Author
Owner

Original comment: barrettruth/diffs.nvim#370, comment 4695975290
Original author: srcrip
Original date: 2026-06-12T22:34:45Z

sure I will test it, sorry if it was annoying to re-open it, I guess I should have just responded in the previous issue but I was concerned it would be easy to miss

> Original comment: barrettruth/diffs.nvim#370, comment 4695975290 > Original author: `srcrip` > Original date: 2026-06-12T22:34:45Z sure I will test it, sorry if it was annoying to re-open it, I guess I should have just responded in the previous issue but I was concerned it would be easy to miss
Author
Owner

Original comment: barrettruth/diffs.nvim#370, comment 4696101785
Original author: barrettruth
Original date: 2026-06-12T22:50:16Z

all good

> Original comment: barrettruth/diffs.nvim#370, comment 4696101785 > Original author: `barrettruth` > Original date: 2026-06-12T22:50:16Z all good
Author
Owner

Original comment: barrettruth/diffs.nvim#370, comment 4721512805
Original author: barrettruth
Original date: 2026-06-16T17:32:48Z

Unless your dotfiles are out of date, I found the problem/

You do this:

vim.pack.add({ "https://github.com/nvim-treesitter/nvim-treesitter" })   -- no version, uses "main"
...
require("nvim-treesitter.configs").setup { ensure_installed = { ..."elixir","heex","eex"... }, auto_install = true, ... }

nvim-treesitter is deprecated mostly and now uses the main branch by default instead of master. The entire config setup via require('nvim-treesitter.configs') is completely deprecated. auto_install and ensure_installed do nothing and the parsers are most definitely not found on your system.

I'm going to close this finally. Given how many people use this plugin and that this has never cropped up for anyone else I'm fairly sure this is the problem.

> Original comment: barrettruth/diffs.nvim#370, comment 4721512805 > Original author: `barrettruth` > Original date: 2026-06-16T17:32:48Z Unless your dotfiles are out of date, I found the problem/ You do this: ```lua vim.pack.add({ "https://github.com/nvim-treesitter/nvim-treesitter" }) -- no version, uses "main" ... require("nvim-treesitter.configs").setup { ensure_installed = { ..."elixir","heex","eex"... }, auto_install = true, ... } ``` nvim-treesitter is deprecated mostly and now uses the main branch by default instead of master. The entire config setup via `require('nvim-treesitter.configs')` is completely deprecated. `auto_install` and `ensure_installed` do nothing and the parsers are most definitely not found on your system. I'm going to close this finally. Given how many people use this plugin and that this has never cropped up for anyone else I'm fairly sure this is the problem.
Author
Owner

Original comment: barrettruth/diffs.nvim#370, comment 4929167525
Original author: srcrip
Original date: 2026-07-09T20:18:45Z

Oh my god, wow thank you for going to the effort of going through my dotfiles! I am going to try that and let you know what I find out.

> Original comment: barrettruth/diffs.nvim#370, comment 4929167525 > Original author: `srcrip` > Original date: 2026-07-09T20:18:45Z Oh my god, wow thank you for going to the effort of going through my dotfiles! I am going to try that and let you know what I find out.
Author
Owner

Original comment: barrettruth/diffs.nvim#370, comment 4929799116
Original author: srcrip
Original date: 2026-07-09T21:40:09Z

ok, some good news some bad news. good is I figured out the actual problem going on here. I simply had the global vim var where you setup this plugin in the wrong place. I put it after the plugin initialized, so it was never reading it.

the weirder or bad news is I had to do some weird stuff with filetypes to get it working right. at first, I got exs highlighting fine, but not ex files.

I ended up having to add this:

-- Force ambiguous file extensions to correct filetypes                                                                                                                             
vim.filetype.add({                                                                                                                                                                  
  extension = {                                                                                                                                                                     
    ex = "elixir",                                                                                                                                                                  
    ts = "typescript",                                                                                                                                                              
  },                                                                                                                                                                                
})  

before they would highlight right in fugitive buffers, with the treesitter highlights from your plugin.

I asked claude why I would have highlighting when I actually view these files but not in fugitive, and it said this:

Image

I gotta say I'm pretty confused by this, I had no idea vim had some way to parse the filetype that didn't just involve the filename. does this ring any bells to you?

> Original comment: barrettruth/diffs.nvim#370, comment 4929799116 > Original author: `srcrip` > Original date: 2026-07-09T21:40:09Z ok, some good news some bad news. good is I figured out the *actual* problem going on here. I simply had the global vim var where you setup this plugin in the wrong place. I put it after the plugin initialized, so it was never reading it. the weirder or bad news is I had to do some weird stuff with filetypes to get it working right. at first, I got exs highlighting fine, but not ex files. I ended up having to add this: ``` -- Force ambiguous file extensions to correct filetypes vim.filetype.add({ extension = { ex = "elixir", ts = "typescript", }, }) ``` before they would highlight right in fugitive buffers, with the treesitter highlights from your plugin. I asked claude why I would have highlighting when I actually view these files but not in fugitive, and it said this: <img width="3034" height="1432" alt="Image" src="https://forge.barrettruth.com/attachments/d3aead99-5cd9-47b0-b7c2-721dab73b72a" /> I gotta say I'm pretty confused by this, I had no idea vim had some way to parse the filetype that didn't just involve the filename. does this ring any bells to you?
Author
Owner

Original comment: barrettruth/diffs.nvim#370, comment 4930579300
Original author: barrettruth
Original date: 2026-07-09T23:48:07Z

lol "ok do it" I feel that XD

yeah id just ask claude:

run nvim --clean -u NORC and see if it picks up on .ex files being elizir filetype.

if it doesn't recognize it then thats the problem - and your fix is right.

id just triple check this - im 99% sure that the extension is recognized by (neo)vim

> Original comment: barrettruth/diffs.nvim#370, comment 4930579300 > Original author: `barrettruth` > Original date: 2026-07-09T23:48:07Z lol "ok do it" I feel that XD yeah id just ask claude: run nvim --clean -u NORC and see if it picks up on .ex files being elizir filetype. if it doesn't recognize it then thats the problem - and your fix is right. id just triple check this - im 99% sure that the extension is recognized by (neo)vim
Author
Owner

Original comment: barrettruth/diffs.nvim#370, comment 4936877552
Original author: srcrip
Original date: 2026-07-10T15:28:00Z

yeah I mean things work ok as long as I specify those filetypes, but I've used vim a long time and I've never had to do that. I had syntax highlighting when I would open an ex file, but apparently when diffs.nvim was calling vim.filetype.match on the filename of a .ex file, it was returning nil.

as you can see here I guess claude is saying that nvim itself has some kind of fallback that can determine the right filetype based on the actual content, but just the filetype alone is not gauranteed to always return the right value for ambiguous extensions?

maybe the plugin could start passing some additional args to that match function to prevent that? Like could it pass part of the file buffer or something?

> Original comment: barrettruth/diffs.nvim#370, comment 4936877552 > Original author: `srcrip` > Original date: 2026-07-10T15:28:00Z yeah I mean things work ok as long as I specify those filetypes, but I've used vim a long time and I've never had to do that. I had syntax highlighting when I would open an `ex` file, but apparently when diffs.nvim was calling vim.filetype.match on the filename of a `.ex` file, it was returning nil. as you can see here I guess claude is saying that nvim itself has some kind of fallback that can determine the right filetype based on the actual content, but just the filetype alone is not gauranteed to always return the right value for ambiguous extensions? maybe the plugin could start passing some additional args to that `match` function to prevent that? Like could it pass part of the file buffer or something?
Sign in to join this conversation.
No milestone
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
barrettruth/diffs.nvim#163
No description provided.