Compare commits

...

10 commits

Author SHA1 Message Date
949da427bf
ci: format
Some checks failed
quality / changes (push) Has been cancelled
quality / Mapping Sync Check (push) Has been cancelled
quality / Lua Format Check (push) Has been cancelled
quality / Lua Lint Check (push) Has been cancelled
quality / Lua Type Check (push) Has been cancelled
quality / Markdown Format Check (push) Has been cancelled
2026-03-02 20:28:25 -05:00
e9a1612365
ci: systematically pull colors from nvim-web-devicons 2026-03-02 20:27:17 -05:00
ba45790910
feat: highlight 2026-03-02 20:22:07 -05:00
7a655c9919
feat(hl): highlight icons 2026-03-02 19:40:26 -05:00
4335fb8596
doc: note nvim-web-devicions as optional 2026-03-02 19:24:06 -05:00
91609349d2
ci: migrate to nix 2026-02-23 18:14:33 -05:00
92d00b66fc
build(flake): add lua-language-server to devShell
Problem: lua-language-server is not available in the dev shell, making
it impossible to run local diagnostics checks.

Solution: add pkgs.lua-language-server to the devShell packages.
2026-02-23 17:31:50 -05:00
7d35b23ba0
fix: resolve luacats return-type-mismatch and cast-local-type warnings
Problem: lua-language-server reports two diagnostics in override.lua:
char() returns string? but resolve() annotates its return as string,
and fallback_icon is typed as string but assigned a string? value.

Solution: add fallback values so both assignments satisfy the string
type — resolve() falls back to fallback_icon, and fallback_icon falls
back to an empty string.
2026-02-23 17:31:45 -05:00
6f445c30d6
fix: duplicate table index 2026-02-23 17:27:13 -05:00
b6d8733c3e
ci: format 2026-02-23 17:26:04 -05:00
17 changed files with 235 additions and 70 deletions

View file

@ -25,6 +25,7 @@ jobs:
- '*.lua' - '*.lua'
- '.luarc.json' - '.luarc.json'
- '*.toml' - '*.toml'
- 'vim.yaml'
markdown: markdown:
- '*.md' - '*.md'
@ -35,11 +36,8 @@ jobs:
if: ${{ needs.changes.outputs.lua == 'true' }} if: ${{ needs.changes.outputs.lua == 'true' }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- uses: JohnnyMorganz/stylua-action@v4 - uses: cachix/install-nix-action@v31
with: - run: nix develop --command stylua --check .
token: ${{ secrets.GITHUB_TOKEN }}
version: 2.1.0
args: --check .
lua-lint: lua-lint:
name: Lua Lint Check name: Lua Lint Check
@ -48,11 +46,8 @@ jobs:
if: ${{ needs.changes.outputs.lua == 'true' }} if: ${{ needs.changes.outputs.lua == 'true' }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Lint with Selene - uses: cachix/install-nix-action@v31
uses: NTBBloodbath/selene-action@v1.0.0 - run: nix develop --command selene --display-style quiet .
with:
token: ${{ secrets.GITHUB_TOKEN }}
args: --display-style quiet .
lua-typecheck: lua-typecheck:
name: Lua Type Check name: Lua Type Check
@ -75,18 +70,8 @@ jobs:
if: ${{ needs.changes.outputs.markdown == 'true' }} if: ${{ needs.changes.outputs.markdown == 'true' }}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Setup pnpm - uses: cachix/install-nix-action@v31
uses: pnpm/action-setup@v4 - run: nix develop --command prettier --check .
with:
version: 8
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install prettier
run: pnpm add -g prettier@3.1.0
- name: Check markdown formatting with prettier
run: prettier --check .
mapping-sync: mapping-sync:
name: Mapping Sync Check name: Mapping Sync Check

View file

@ -32,10 +32,21 @@ jobs:
echo 'return M' echo 'return M'
} > lua/nonicons/mapping.lua } > lua/nonicons/mapping.lua
- name: Generate colors from nvim-web-devicons
run: |
sudo apt-get install -y -qq lua5.4 > /dev/null
curl -sL https://raw.githubusercontent.com/nvim-tree/nvim-web-devicons/master/lua/nvim-web-devicons/default/icons_by_file_extension.lua \
-o /tmp/devicons_ext.lua
curl -sL https://raw.githubusercontent.com/nvim-tree/nvim-web-devicons/master/lua/nvim-web-devicons/default/icons_by_filename.lua \
-o /tmp/devicons_fname.lua
lua5.4 scripts/gen-colors.lua > lua/nonicons/colors.lua
- name: Check for changes - name: Check for changes
id: diff id: diff
run: | run: |
if git diff --quiet lua/nonicons/mapping.lua; then if git diff --quiet lua/nonicons/mapping.lua lua/nonicons/colors.lua; then
echo "changed=false" >> "$GITHUB_OUTPUT" echo "changed=false" >> "$GITHUB_OUTPUT"
else else
echo "changed=true" >> "$GITHUB_OUTPUT" echo "changed=true" >> "$GITHUB_OUTPUT"
@ -45,16 +56,17 @@ jobs:
if: steps.diff.outputs.changed == 'true' if: steps.diff.outputs.changed == 'true'
uses: peter-evans/create-pull-request@v7 uses: peter-evans/create-pull-request@v7
with: with:
branch: sync/upstream-mapping branch: sync/upstream
title: 'fix(mapping): sync with upstream nonicons font' title: 'fix: sync with upstream nonicons font and devicons colors'
body: | body: |
## Problem ## Problem
`mapping.lua` is out of sync with the [ya2s/nonicons](https://github.com/ya2s/nonicons) upstream font. `mapping.lua` or `colors.lua` is out of sync with upstream sources.
## Solution ## Solution
Auto-generated `mapping.lua` from upstream `nonicon.json`. Auto-generated from upstream [ya2s/nonicons](https://github.com/ya2s/nonicons) `nonicon.json`
commit-message: 'fix(mapping): sync with upstream nonicons font' and [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) color definitions.
commit-message: 'fix: sync with upstream nonicons font and devicons colors'
labels: upstream-sync labels: upstream-sync
delete-branch: true delete-branch: true

1
.gitignore vendored
View file

@ -10,3 +10,4 @@ node_modules/
result result
result-* result-*
.direnv/ .direnv/
.envrc

View file

@ -1,5 +1,5 @@
{ {
"runtime.version": "Lua 5.1", "runtime.version": "LuaJIT",
"runtime.path": ["lua/?.lua", "lua/?/init.lua"], "runtime.path": ["lua/?.lua", "lua/?/init.lua"],
"diagnostics.globals": ["vim", "jit"], "diagnostics.globals": ["vim", "jit"],
"workspace.library": ["$VIMRUNTIME/lua", "${3rd}/luv/library"], "workspace.library": ["$VIMRUNTIME/lua", "${3rd}/luv/library"],

View file

@ -12,8 +12,8 @@
## Requirements ## Requirements
- [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons)
- [nonicons font](https://github.com/ya2s/nonicons/releases) installed - [nonicons font](https://github.com/ya2s/nonicons/releases) installed
- (Optionally) [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons)
## Installation ## Installation

View file

@ -22,6 +22,7 @@
pkgs.prettier pkgs.prettier
pkgs.stylua pkgs.stylua
pkgs.selene pkgs.selene
pkgs.lua-language-server
]; ];
}; };
}); });

69
lua/nonicons/colors.lua Normal file
View file

@ -0,0 +1,69 @@
---@type table<string, { [1]: string, [2]: integer }>
local M = {
['babel'] = { '#CBCB41', 185 },
['book'] = { '#3D6117', 22 },
['c'] = { '#A074C4', 140 },
['c-plusplus'] = { '#A074C4', 140 },
['c-sharp'] = { '#596706', 58 },
['code'] = { '#3882D2', 32 },
['css'] = { '#563D7C', 54 },
['dart'] = { '#03589C', 25 },
['database'] = { '#DAD8D8', 188 },
['diff'] = { '#41535B', 239 },
['docker'] = { '#458EE6', 68 },
['elixir'] = { '#A074C4', 140 },
['elm'] = { '#519ABA', 74 },
['eslint'] = { '#4B32C3', 56 },
['file'] = { '#89E051', 113 },
['file-binary'] = { '#4D2C0B', 52 },
['file-zip'] = { '#ECA517', 214 },
['gear'] = { '#6D8086', 66 },
['git-branch'] = { '#F14C28', 196 },
['git-commit'] = { '#F54D27', 196 },
['globe'] = { '#5D7096', 60 },
['go'] = { '#00ADD8', 38 },
['graphql'] = { '#E535AB', 199 },
['html'] = { '#E34C26', 196 },
['image'] = { '#A074C4', 140 },
['java'] = { '#ffaf67', 215 },
['javascript'] = { '#CBCB41', 185 },
['json'] = { '#CBCB41', 185 },
['key'] = { '#FAF743', 227 },
['kotlin'] = { '#7F52FF', 99 },
['law'] = { '#CBCB41', 185 },
['lock'] = { '#BBBBBB', 250 },
['log'] = { '#DDDDDD', 253 },
['logo-github'] = { '#E24329', 196 },
['lua'] = { '#00A2FF', 75 },
['markdown'] = { '#519ABA', 74 },
['next'] = { '#FFFFFF', 231 },
['npm'] = { '#E8274B', 197 },
['package'] = { '#EADCD1', 253 },
['perl'] = { '#519ABA', 74 },
['php'] = { '#F05340', 203 },
['play'] = { '#0075AA', 24 },
['prettier'] = { '#4285F4', 33 },
['python'] = { '#5AA7E4', 39 },
['r'] = { '#519ABA', 74 },
['react'] = { '#1354BF', 26 },
['rss'] = { '#FB9D3B', 215 },
['ruby'] = { '#701516', 52 },
['rust'] = { '#DEA584', 216 },
['scala'] = { '#CC3E44', 167 },
['server'] = { '#A074C4', 140 },
['shield'] = { '#BEC4C9', 251 },
['svelte'] = { '#FF3E00', 196 },
['swift'] = { '#E37933', 166 },
['terminal'] = { '#4273CA', 68 },
['terraform'] = { '#5F43E9', 93 },
['tmux'] = { '#14BA19', 34 },
['toml'] = { '#9C4221', 124 },
['typescript'] = { '#519ABA', 74 },
['typography'] = { '#ECECEC', 255 },
['vim'] = { '#019833', 28 },
['vue'] = { '#8DC149', 113 },
['yaml'] = { '#6D8086', 66 },
['yarn'] = { '#F9AD02', 214 },
}
return M

View file

@ -7,7 +7,7 @@ function M.check()
if ok and devicons then if ok and devicons then
vim.health.ok('nvim-web-devicons available') vim.health.ok('nvim-web-devicons available')
else else
vim.health.error('nvim-web-devicons not found') vim.health.info('nvim-web-devicons not found (using built-in highlights)')
end end
local result = vim.fn.system({ 'fc-list', ':family=nonicons' }) local result = vim.fn.system({ 'fc-list', ':family=nonicons' })

View file

@ -48,9 +48,14 @@ return function()
lines[#lines + 1] = 'nonicons.nvim — icon reference' lines[#lines + 1] = 'nonicons.nvim — icon reference'
lines[#lines + 1] = string.rep('=', 72) lines[#lines + 1] = string.rep('=', 72)
lines[#lines + 1] = '' lines[#lines + 1] = ''
lines[#lines + 1] = string.format(' %-4s %-28s %-7s %s', 'ICON', 'NAME', 'CODE', 'MAPPED FROM') lines[#lines + 1] =
string.format(' %-4s %-28s %-7s %s', 'ICON', 'NAME', 'CODE', 'MAPPED FROM')
lines[#lines + 1] = string.rep('-', 72) lines[#lines + 1] = string.rep('-', 72)
local highlights = require('nonicons.highlights')
local icon_line_start = #lines
local icon_glyphs = {} ---@type string[]
for _, name in ipairs(icon_names) do for _, name in ipairs(icon_names) do
local sources = {} ---@type string[] local sources = {} ---@type string[]
if ext_by_icon[name] then if ext_by_icon[name] then
@ -69,8 +74,11 @@ return function()
end end
end end
local glyph = char(name)
local src_str = #sources > 0 and table.concat(sources, ', ') or '' local src_str = #sources > 0 and table.concat(sources, ', ') or ''
lines[#lines + 1] = string.format(' %s %-28s U+%04X %s', char(name), name, mapping[name], src_str) lines[#lines + 1] =
string.format(' %s %-28s U+%04X %s', glyph, name, mapping[name], src_str)
icon_glyphs[#icon_glyphs + 1] = glyph
end end
lines[#lines + 1] = '' lines[#lines + 1] = ''
@ -78,6 +86,17 @@ return function()
local buf = vim.api.nvim_create_buf(false, true) local buf = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines) vim.api.nvim_buf_set_lines(buf, 0, -1, false, lines)
local ns = vim.api.nvim_create_namespace('nonicons_hitest')
for i, name in ipairs(icon_names) do
local col_start = 2
local col_end = col_start + #icon_glyphs[i]
vim.api.nvim_buf_set_extmark(buf, ns, icon_line_start + i - 1, col_start, {
end_col = col_end,
hl_group = highlights.get(name),
})
end
vim.bo[buf].modifiable = false vim.bo[buf].modifiable = false
vim.bo[buf].buftype = 'nofile' vim.bo[buf].buftype = 'nofile'
vim.bo[buf].bufhidden = 'wipe' vim.bo[buf].bufhidden = 'wipe'

View file

@ -0,0 +1,39 @@
local colors = require('nonicons.colors')
local M = {}
---@param icon_name string
---@return string
local function to_hl_group(icon_name)
local parts = {}
for part in icon_name:gmatch('[^-]+') do
parts[#parts + 1] = part:sub(1, 1):upper() .. part:sub(2)
end
return 'Nonicons' .. table.concat(parts)
end
local function apply()
for name, data in pairs(colors) do
vim.api.nvim_set_hl(0, to_hl_group(name), { fg = data[1], ctermfg = data[2] })
end
end
function M.setup()
apply()
local group = vim.api.nvim_create_augroup('NoniconsHighlights', { clear = true })
vim.api.nvim_create_autocmd('ColorScheme', {
group = group,
callback = apply,
})
end
---@param icon_name string
---@return string? hl_group
function M.get(icon_name)
if colors[icon_name] then
return to_hl_group(icon_name)
end
end
return M

View file

@ -29,15 +29,18 @@ M.mapping = require('nonicons.mapping')
---@param name string Icon name ---@param name string Icon name
---@return string? glyph ---@return string? glyph
---@return string? hl_group
function M.get(name) function M.get(name)
local code = M.mapping[name] local code = M.mapping[name]
if code then if code then
return vim.fn.nr2char(code) local hl = require('nonicons.highlights').get(name)
return vim.fn.nr2char(code), hl
end end
end end
function M.apply() function M.apply()
ensure_initialized() ensure_initialized()
require('nonicons.highlights').setup()
if config.override then if config.override then
require('nonicons.override').apply() require('nonicons.override').apply()
end end
@ -46,6 +49,7 @@ end
---@param name string? Filename (e.g. `'init.lua'`) ---@param name string? Filename (e.g. `'init.lua'`)
---@param ext string? File extension (e.g. `'lua'`) ---@param ext string? File extension (e.g. `'lua'`)
---@return string? glyph ---@return string? glyph
---@return string? hl_group
function M.get_icon(name, ext) function M.get_icon(name, ext)
local key = require('nonicons.resolve').resolve_name(name, ext) local key = require('nonicons.resolve').resolve_name(name, ext)
if key then if key then
@ -55,6 +59,7 @@ end
---@param ft string Vim filetype ---@param ft string Vim filetype
---@return string? glyph ---@return string? glyph
---@return string? hl_group
function M.get_icon_by_filetype(ft) function M.get_icon_by_filetype(ft)
local key = require('nonicons.resolve').resolve_filetype(ft) local key = require('nonicons.resolve').resolve_filetype(ft)
if key then if key then

View file

@ -19,7 +19,7 @@ local fallback_icon
local function resolve(name, ext) local function resolve(name, ext)
local key = resolve_mod.resolve_name(name, ext) local key = resolve_mod.resolve_name(name, ext)
if key then if key then
return char(key) return char(key) or fallback_icon
end end
return fallback_icon return fallback_icon
end end
@ -32,7 +32,7 @@ function M.apply()
return return
end end
fallback_icon = char('file') fallback_icon = char('file') or ''
local orig_get_icon = devicons.get_icon local orig_get_icon = devicons.get_icon
devicons.get_icon = function(name, ext, opts) devicons.get_icon = function(name, ext, opts)

View file

@ -106,7 +106,6 @@ M.ext_map = {
pl = 'perl', pl = 'perl',
pm = 'perl', pm = 'perl',
r = 'r',
r = 'r', r = 'r',
rmd = 'r', rmd = 'r',

39
scripts/gen-colors.lua Normal file
View file

@ -0,0 +1,39 @@
#!/usr/bin/env lua
local resolve = dofile('lua/nonicons/resolve.lua')
local ext_icons = dofile('/tmp/devicons_ext.lua')
local fname_icons = dofile('/tmp/devicons_fname.lua')
local colors = {}
for ext, icon_name in pairs(resolve.ext_map) do
if not colors[icon_name] and ext_icons[ext] then
colors[icon_name] = {
ext_icons[ext].color,
tonumber(ext_icons[ext].cterm_color),
}
end
end
for fname, icon_name in pairs(resolve.filename_map) do
if not colors[icon_name] and fname_icons[fname] then
colors[icon_name] = {
fname_icons[fname].color,
tonumber(fname_icons[fname].cterm_color),
}
end
end
local names = {}
for name in pairs(colors) do
names[#names + 1] = name
end
table.sort(names)
io.write('---@type table<string, { [1]: string, [2]: integer }>\n')
io.write('local M = {\n')
for _, name in ipairs(names) do
local c = colors[name]
io.write(string.format(" ['%s'] = { '%s', %d },\n", name, c[1], c[2]))
end
io.write('}\n\nreturn M\n')

View file

@ -1 +1,4 @@
std = 'vim' std = 'vim'
[lints]
bad_string_escape = 'allow'

View file

@ -1,33 +0,0 @@
[selene]
base = "lua51"
name = "vim"
[vim]
any = true
[jit]
any = true
[bit]
any = true
[assert]
any = true
[describe]
any = true
[it]
any = true
[before_each]
any = true
[after_each]
any = true
[spy]
any = true
[stub]
any = true

26
vim.yaml Normal file
View file

@ -0,0 +1,26 @@
---
base: lua51
name: vim
lua_versions:
- luajit
globals:
vim:
any: true
jit:
any: true
assert:
any: true
describe:
any: true
it:
any: true
before_each:
any: true
after_each:
any: true
spy:
any: true
stub:
any: true
bit:
any: true