commit 04791cd41a54ddfeb4bce80a65bcfe0790a3e757 Author: Barrett Ruth Date: Tue Feb 17 19:34:13 2026 -0500 feat: initial nonicons.nvim rewrite Problem: the original ya2s/nvim-nonicons fork diverged enough that maintaining it as a fork was pointless. The set_icon() approach broke with modern nvim-web-devicons, and the extensions directory coupled integrations that belong in user config. Solution: from-scratch rewrite with function wrapping + table mutation override engine, vim.g.nonicons config pattern, vendored mapping with integer codepoints, health check, vimdoc with recipes, and no built-in plugin integrations. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b9de190 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,9 @@ +root = true + +[*] +insert_final_newline = true +charset = utf-8 + +[*.lua] +indent_style = space +indent_size = 2 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..eabe60d --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +doc/tags +*.log + +.*cache* +CLAUDE.md +.claude/ + +node_modules/ + +result +result-* +.direnv/ diff --git a/.luarc.json b/.luarc.json new file mode 100644 index 0000000..b438cce --- /dev/null +++ b/.luarc.json @@ -0,0 +1,8 @@ +{ + "runtime.version": "Lua 5.1", + "runtime.path": ["lua/?.lua", "lua/?/init.lua"], + "diagnostics.globals": ["vim", "jit"], + "workspace.library": ["$VIMRUNTIME/lua", "${3rd}/luv/library"], + "workspace.checkThirdParty": false, + "completion.callSnippet": "Replace" +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..093db11 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Barrett Ruth + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2620c63 --- /dev/null +++ b/README.md @@ -0,0 +1,180 @@ +# nonicons.nvim + +**Nonicons for Neovim** + +Replace [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) +glyphs with icons from the [nonicons](https://github.com/yamatsum/nonicons) +font. Any plugin that uses nvim-web-devicons automatically displays nonicons +glyphs. + +## Requirements + +- [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) +- [nonicons font](https://github.com/yamatsum/nonicons/releases) installed in + your terminal + +## Installation + +Install the nonicons font and configure your terminal to use it as a fallback: + +**ghostty** + +``` +font-family = Nonicons +font-family = YourMainFont +``` + +**kitty** + +``` +symbol_map U+F101-U+F219 Nonicons +``` + +**wezterm** + +```lua +config.font = wezterm.font_with_fallback({ + 'YourMainFont', + 'Nonicons', +}) +``` + +Install with your package manager of choice or via +[luarocks](https://luarocks.org/modules/barrettruth/nonicons.nvim): + +``` +luarocks install nonicons.nvim +``` + +**lazy.nvim** + +```lua +{ + 'barrettruth/nonicons.nvim', + dependencies = { 'nvim-tree/nvim-web-devicons' }, +} +``` + +The plugin applies overrides automatically. No `setup()` call is needed. + +## Configuration + +Configure via `vim.g.nonicons` before the plugin loads: + +```lua +vim.g.nonicons = { + override = true, -- default; set false to disable devicons wrapping +} +``` + +## Usage + +```lua +local get = require('nonicons').get + +get('lua') -- returns the nonicons lua character +get('python') -- returns the nonicons python character +get('git-branch') -- returns the nonicons git-branch character +``` + +The raw mapping table is available for advanced use: + +```lua +local code = require('nonicons').mapping['lua'] +local icon = vim.fn.nr2char(code) +``` + +## Recipes + +**lualine** — mode icons: + +```lua +local get = require('nonicons').get + +require('lualine').setup({ + sections = { + lualine_a = { + { + 'mode', + fmt = function(mode) + local map = { + NORMAL = get('vim-normal-mode'), + INSERT = get('vim-insert-mode'), + VISUAL = get('vim-visual-mode'), + REPLACE = get('vim-replace-mode'), + COMMAND = get('vim-command-mode'), + } + return map[mode] or mode + end, + }, + }, + }, +}) +``` + +**nvim-notify**: + +```lua +require('notify').setup({ + icons = { + ERROR = require('nonicons').get('x-circle'), + WARN = require('nonicons').get('alert'), + INFO = require('nonicons').get('info'), + DEBUG = require('nonicons').get('bug'), + TRACE = require('nonicons').get('play'), + }, +}) +``` + +**mason.nvim**: + +```lua +require('mason').setup({ + ui = { + icons = { + package_installed = require('nonicons').get('check'), + package_pending = require('nonicons').get('sync'), + package_uninstalled = require('nonicons').get('x'), + }, + }, +}) +``` + +**oil.nvim**, **fzf-lua**, **telescope.nvim** — no configuration needed. These +plugins read from nvim-web-devicons which nonicons.nvim wraps automatically. + +**nvim-tree**: + +```lua +local get = require('nonicons').get + +require('nvim-tree').setup({ + renderer = { + icons = { + glyphs = { + default = get('file'), + symlink = get('file-symlink-file'), + folder = { + default = get('file-directory-fill'), + open = get('file-directory-open-fill'), + symlink = get('file-submodule'), + empty = get('file-directory-outline'), + arrow_open = get('chevron-down'), + arrow_closed = get('chevron-right'), + }, + }, + }, + }, +}) +``` + +## Documentation + +```vim +:help nonicons.nvim +``` + +## Acknowledgements + +- [yamatsum/nonicons](https://github.com/yamatsum/nonicons) — icon font +- [ya2s/nvim-nonicons](https://github.com/ya2s/nvim-nonicons) — original plugin diff --git a/doc/nonicons.nvim.txt b/doc/nonicons.nvim.txt new file mode 100644 index 0000000..3fc6b14 --- /dev/null +++ b/doc/nonicons.nvim.txt @@ -0,0 +1,207 @@ +*nonicons.nvim.txt* Nonicons for Neovim + +Author: Barrett Ruth +License: MIT + +============================================================================== +INTRODUCTION *nonicons.nvim* + +nonicons.nvim replaces nvim-web-devicons glyphs with icons from the nonicons +font (https://github.com/yamatsum/nonicons). It wraps devicons functions so +that any plugin using nvim-web-devicons automatically displays nonicons glyphs. + +============================================================================== +REQUIREMENTS *nonicons-requirements* + +- nvim-web-devicons (https://github.com/nvim-tree/nvim-web-devicons) +- nonicons font installed in your terminal + +============================================================================== +SETUP *nonicons-setup* + +Using lazy.nvim: >lua + { + 'barrettruth/nonicons.nvim', + dependencies = { 'nvim-tree/nvim-web-devicons' }, + } +< +The plugin applies overrides automatically via `plugin/nonicons.lua`. No +`setup()` call is needed. + +============================================================================== +CONFIGURATION *nonicons-config* + +Configure via `vim.g.nonicons` before the plugin loads: >lua + vim.g.nonicons = { + override = true, + } +< + *nonicons.config.override* +override ~ + `boolean` (default: `true`) + Whether to wrap nvim-web-devicons functions with nonicons glyphs. + +============================================================================== +API *nonicons-api* + + *nonicons.get()* +`require('nonicons').get(name)` + Returns the nonicons character for the given icon name, or `nil` if the + name is not in the mapping. + + Parameters: ~ + {name} `string` Icon name (e.g. `'lua'`, `'python'`, `'git-branch'`) + + Returns: ~ + `string?` The single-character nonicons glyph + + *nonicons.mapping* +`require('nonicons').mapping` + The raw `table` mapping icon names to Unicode codepoints. + Useful for advanced use cases where you need the codepoint directly: >lua + local code = require('nonicons').mapping['lua'] + local icon = vim.fn.nr2char(code) +< + +============================================================================== +RECIPES *nonicons-recipes* + +lualine ~ +>lua + local get = require('nonicons').get + + require('lualine').setup({ + sections = { + lualine_a = { + { + 'mode', + fmt = function(mode) + local map = { + NORMAL = get('vim-normal-mode'), + INSERT = get('vim-insert-mode'), + VISUAL = get('vim-visual-mode'), + ['V-LINE'] = get('vim-visual-mode'), + ['V-BLOCK'] = get('vim-visual-mode'), + REPLACE = get('vim-replace-mode'), + COMMAND = get('vim-command-mode'), + SELECT = get('vim-select-mode'), + TERMINAL = get('vim-terminal-mode'), + } + return map[mode] or mode + end, + }, + }, + }, + }) +< + +nvim-notify ~ +>lua + require('notify').setup({ + icons = { + ERROR = require('nonicons').get('x-circle'), + WARN = require('nonicons').get('alert'), + INFO = require('nonicons').get('info'), + DEBUG = require('nonicons').get('bug'), + TRACE = require('nonicons').get('play'), + }, + }) +< + +mason.nvim ~ +>lua + require('mason').setup({ + ui = { + icons = { + package_installed = require('nonicons').get('check'), + package_pending = require('nonicons').get('sync'), + package_uninstalled = require('nonicons').get('x'), + }, + }, + }) +< + +oil.nvim ~ + + No configuration needed. oil.nvim reads the devicons extension/filename + tables directly, which nonicons.nvim mutates on load. + +fzf-lua ~ + + No configuration needed. fzf-lua calls `get_icon()` from devicons, which + nonicons.nvim wraps automatically. + +telescope.nvim ~ + + No configuration needed. telescope.nvim calls `get_icon()` from devicons. + +nvim-tree ~ +>lua + local get = require('nonicons').get + + require('nvim-tree').setup({ + renderer = { + icons = { + glyphs = { + default = get('file'), + symlink = get('file-symlink-file'), + folder = { + default = get('file-directory-fill'), + open = get('file-directory-open-fill'), + symlink = get('file-submodule'), + empty = get('file-directory-outline'), + arrow_open = get('chevron-down'), + arrow_closed = get('chevron-right'), + }, + }, + }, + }, + }) +< + +============================================================================== +FONT SETUP *nonicons-font* + +The nonicons font must be installed and configured in your terminal emulator. + +Download the font from: https://github.com/yamatsum/nonicons/releases + +ghostty ~ +> + font-family = Nonicons + font-family = YourMainFont +< + +kitty ~ +> + symbol_map U+F101-U+F219 Nonicons +< + +wezterm ~ +>lua + config.font = wezterm.font_with_fallback({ + 'YourMainFont', + 'Nonicons', + }) +< + +iTerm2 ~ + + Preferences > Profiles > Text > Non-ASCII Font > select Nonicons + +============================================================================== +HEALTH CHECK *nonicons-health* + +Run `:checkhealth nonicons` to verify: +- nvim-web-devicons is available +- The nonicons font is installed (via `fc-list`) +- The mapping table loaded successfully + +============================================================================== +ACKNOWLEDGEMENTS *nonicons-acknowledgements* + +- yamatsum/nonicons (https://github.com/yamatsum/nonicons) — icon font +- ya2s/nvim-nonicons (https://github.com/ya2s/nvim-nonicons) — original plugin + +============================================================================== +vim:tw=78:ts=8:ft=help:norl: diff --git a/lua/nonicons/health.lua b/lua/nonicons/health.lua new file mode 100644 index 0000000..e78e2ae --- /dev/null +++ b/lua/nonicons/health.lua @@ -0,0 +1,29 @@ +local M = {} + +function M.check() + vim.health.start('nonicons.nvim') + + local ok, devicons = pcall(require, 'nvim-web-devicons') + if ok and devicons then + vim.health.ok('nvim-web-devicons available') + else + vim.health.error('nvim-web-devicons not found') + end + + local result = vim.fn.system({ 'fc-list', ':family=nonicons' }) + if result ~= '' then + vim.health.ok('nonicons font installed') + else + vim.health.warn('nonicons font not detected (fc-list :family=nonicons returned empty)') + end + + local mapping = require('nonicons.mapping') + local count = vim.tbl_count(mapping) + if count > 0 then + vim.health.ok('mapping loaded (' .. count .. ' icons)') + else + vim.health.error('mapping table is empty') + end +end + +return M diff --git a/lua/nonicons/init.lua b/lua/nonicons/init.lua new file mode 100644 index 0000000..dbdb186 --- /dev/null +++ b/lua/nonicons/init.lua @@ -0,0 +1,43 @@ +local M = {} + +---@class nonicons.Config +---@field override boolean + +---@type nonicons.Config +local default = { + override = true, +} + +local initialized = false + +---@type nonicons.Config +local config + +local function ensure_initialized() + if initialized then + return + end + local user = vim.g.nonicons or {} + vim.validate('nonicons', user, 'table') + vim.validate('nonicons.override', user.override, 'boolean', true) + config = vim.tbl_deep_extend('force', default, user) + initialized = true +end + +M.mapping = require('nonicons.mapping') + +function M.get(name) + local code = M.mapping[name] + if code then + return vim.fn.nr2char(code) + end +end + +function M.apply() + ensure_initialized() + if config.override then + require('nonicons.override').apply() + end +end + +return M diff --git a/lua/nonicons/mapping.lua b/lua/nonicons/mapping.lua new file mode 100644 index 0000000..6e74952 --- /dev/null +++ b/lua/nonicons/mapping.lua @@ -0,0 +1,333 @@ +---@type table +local M = { + ['alert'] = 61697, + ['angular'] = 61698, + ['archive'] = 61699, + ['arrow-both'] = 61700, + ['arrow-down'] = 61701, + ['arrow-left'] = 61702, + ['arrow-right'] = 61703, + ['arrow-switch'] = 61704, + ['arrow-up'] = 61705, + ['backbone'] = 61706, + ['beaker'] = 61707, + ['bell'] = 61708, + ['bell-slash'] = 61709, + ['bold'] = 61710, + ['book'] = 61711, + ['bookmark'] = 61712, + ['bookmark-slash'] = 61713, + ['briefcase'] = 61714, + ['broadcast'] = 61715, + ['browser'] = 61716, + ['bug'] = 61717, + ['c'] = 61718, + ['c-plusplus'] = 61719, + ['c-sharp'] = 61720, + ['calendar'] = 61721, + ['check'] = 61722, + ['check-circle'] = 61723, + ['check-circle-fill'] = 61724, + ['checklist'] = 61725, + ['chevron-down'] = 61726, + ['chevron-left'] = 61727, + ['chevron-right'] = 61728, + ['chevron-up'] = 61729, + ['circle'] = 61730, + ['circle-slash'] = 61731, + ['clippy'] = 61732, + ['clock'] = 61733, + ['code'] = 61734, + ['code-review'] = 61735, + ['code-square'] = 61736, + ['comment'] = 61737, + ['comment-discussion'] = 61738, + ['container'] = 61739, + ['cpu'] = 61740, + ['credit-card'] = 61741, + ['cross-reference'] = 61742, + ['css'] = 61743, + ['dart'] = 61744, + ['dash'] = 61745, + ['database'] = 61746, + ['desktop-download'] = 61747, + ['device-camera'] = 61748, + ['device-camera-video'] = 61749, + ['device-desktop'] = 61750, + ['device-mobile'] = 61751, + ['diff'] = 61752, + ['diff-added'] = 61753, + ['diff-ignored'] = 61754, + ['diff-modified'] = 61755, + ['diff-removed'] = 61756, + ['diff-renamed'] = 61757, + ['docker'] = 61758, + ['dot'] = 61759, + ['dot-fill'] = 61760, + ['download'] = 61761, + ['ellipsis'] = 61762, + ['elm'] = 61763, + ['eye'] = 61764, + ['eye-closed'] = 61765, + ['file'] = 61766, + ['file-badge'] = 61767, + ['file-binary'] = 61768, + ['file-code'] = 61769, + ['file-diff'] = 61770, + ['file-directory'] = 61771, + ['file-directory-outline'] = 61772, + ['file-submodule'] = 61773, + ['file-symlink-file'] = 61774, + ['file-zip'] = 61775, + ['filter'] = 61776, + ['flame'] = 61777, + ['fold'] = 61778, + ['fold-down'] = 61779, + ['fold-up'] = 61780, + ['gear'] = 61781, + ['gift'] = 61782, + ['git-branch'] = 61783, + ['git-commit'] = 61784, + ['git-compare'] = 61785, + ['git-merge'] = 61786, + ['git-pull-request'] = 61787, + ['globe'] = 61788, + ['go'] = 61789, + ['grabber'] = 61790, + ['graph'] = 61791, + ['heading'] = 61792, + ['heart'] = 61793, + ['heart-fill'] = 61794, + ['history'] = 61795, + ['home'] = 61796, + ['horizontal-rule'] = 61797, + ['hourglass'] = 61798, + ['html'] = 61799, + ['hubot'] = 61800, + ['image'] = 61801, + ['inbox'] = 61802, + ['infinity'] = 61803, + ['info'] = 61804, + ['issue-closed'] = 61805, + ['issue-opened'] = 61806, + ['issue-reopened'] = 61807, + ['italic'] = 61808, + ['java'] = 61809, + ['javascript'] = 61810, + ['json'] = 61811, + ['kebab-horizontal'] = 61812, + ['key'] = 61813, + ['kotlin'] = 61814, + ['kubernetes'] = 61815, + ['law'] = 61816, + ['light-bulb'] = 61817, + ['link'] = 61818, + ['link-external'] = 61819, + ['list-ordered'] = 61820, + ['list-unordered'] = 61821, + ['location'] = 61822, + ['lock'] = 61823, + ['logo-gist'] = 61824, + ['logo-github'] = 61825, + ['lua'] = 61826, + ['mail'] = 61827, + ['mark-github'] = 61828, + ['markdown'] = 61829, + ['megaphone'] = 61830, + ['mention'] = 61831, + ['meter'] = 61832, + ['milestone'] = 61833, + ['mirror'] = 61834, + ['moon'] = 61835, + ['mortar-board'] = 61836, + ['mute'] = 61837, + ['nginx'] = 61838, + ['no-entry'] = 61839, + ['node'] = 61840, + ['north-star'] = 61841, + ['note'] = 61842, + ['npm'] = 61843, + ['octoface'] = 61844, + ['organization'] = 61845, + ['package'] = 61846, + ['package-dependencies'] = 61847, + ['package-dependents'] = 61848, + ['paintbrush'] = 61849, + ['paper-airplane'] = 61850, + ['pencil'] = 61851, + ['people'] = 61852, + ['perl'] = 61853, + ['person'] = 61854, + ['php'] = 61855, + ['pin'] = 61856, + ['play'] = 61857, + ['plug'] = 61858, + ['plus'] = 61859, + ['plus-circle'] = 61860, + ['project'] = 61861, + ['pulse'] = 61862, + ['python'] = 61863, + ['question'] = 61864, + ['quote'] = 61865, + ['r'] = 61866, + ['react'] = 61867, + ['rectangle'] = 61868, + ['reply'] = 61869, + ['repo'] = 61870, + ['repo-clone'] = 61871, + ['repo-forked'] = 61872, + ['repo-pull'] = 61873, + ['repo-push'] = 61874, + ['repo-template'] = 61875, + ['report'] = 61876, + ['require'] = 61877, + ['rocket'] = 61878, + ['rss'] = 61879, + ['ruby'] = 61880, + ['rust'] = 61881, + ['scala'] = 61882, + ['screen-full'] = 61883, + ['screen-normal'] = 61884, + ['search'] = 61885, + ['server'] = 61886, + ['share'] = 61887, + ['share-android'] = 61888, + ['shield'] = 61889, + ['shield-check'] = 61890, + ['shield-lock'] = 61891, + ['shield-x'] = 61892, + ['sign-in'] = 61893, + ['sign-out'] = 61894, + ['skip'] = 61895, + ['smiley'] = 61896, + ['square'] = 61897, + ['square-fill'] = 61898, + ['squirrel'] = 61899, + ['star'] = 61900, + ['star-fill'] = 61901, + ['stop'] = 61902, + ['stopwatch'] = 61903, + ['strikethrough'] = 61904, + ['sun'] = 61905, + ['swift'] = 61906, + ['sync'] = 61907, + ['tag'] = 61908, + ['tasklist'] = 61909, + ['telescope'] = 61910, + ['terminal'] = 61911, + ['three-bars'] = 61912, + ['thumbsdown'] = 61913, + ['thumbsup'] = 61914, + ['tmux'] = 61915, + ['toml'] = 61916, + ['tools'] = 61917, + ['trashcan'] = 61918, + ['triangle-down'] = 61919, + ['triangle-left'] = 61920, + ['triangle-right'] = 61921, + ['triangle-up'] = 61922, + ['typescript'] = 61923, + ['typography'] = 61924, + ['unfold'] = 61925, + ['unlock'] = 61926, + ['unmute'] = 61927, + ['unverified'] = 61928, + ['upload'] = 61929, + ['verified'] = 61930, + ['versions'] = 61931, + ['vim'] = 61932, + ['vim-command-mode'] = 61933, + ['vim-insert-mode'] = 61934, + ['vim-normal-mode'] = 61935, + ['vim-replace-mode'] = 61936, + ['vim-select-mode'] = 61937, + ['vim-terminal-mode'] = 61938, + ['vim-visual-mode'] = 61939, + ['vue'] = 61940, + ['workflow'] = 61941, + ['x'] = 61942, + ['x-circle'] = 61943, + ['x-circle-fill'] = 61944, + ['yaml'] = 61945, + ['yarn'] = 61946, + ['zap'] = 61947, + ['multi-select'] = 61948, + ['number'] = 61949, + ['trash'] = 61950, + ['video'] = 61951, + ['class'] = 61952, + ['constant'] = 61953, + ['field'] = 61954, + ['interface'] = 61955, + ['keyword'] = 61956, + ['snippet'] = 61957, + ['struct'] = 61958, + ['type'] = 61959, + ['variable'] = 61960, + ['blocked'] = 61961, + ['codescan'] = 61962, + ['codescan-checkmark'] = 61963, + ['codespaces'] = 61964, + ['dependabot'] = 61965, + ['duplicate'] = 61966, + ['person-add'] = 61967, + ['sidebar-collapse'] = 61968, + ['sidebar-expand'] = 61969, + ['table'] = 61970, + ['elixir'] = 61971, + ['terraform'] = 61972, + ['columns'] = 61973, + ['diamond'] = 61974, + ['git-pull-request-closed'] = 61975, + ['git-pull-request-draft'] = 61976, + ['hash'] = 61977, + ['issue-draft'] = 61978, + ['rows'] = 61979, + ['select-single'] = 61980, + ['eslint'] = 61981, + ['prettier'] = 61982, + ['vscode'] = 61983, + ['copy'] = 61984, + ['key-asterisk'] = 61985, + ['paste'] = 61986, + ['sort-asc'] = 61987, + ['sort-desc'] = 61988, + ['babel'] = 61989, + ['ionic'] = 61990, + ['next'] = 61991, + ['svelte'] = 61992, + ['capacitor'] = 61993, + ['graphql'] = 61994, + ['accessibility'] = 61995, + ['apps'] = 61996, + ['bell-fill'] = 61997, + ['cloud'] = 61998, + ['cloud-offline'] = 61999, + ['code-of-conduct'] = 62000, + ['feed-discussion'] = 62001, + ['feed-forked'] = 62002, + ['feed-heart'] = 62003, + ['feed-merged'] = 62004, + ['feed-person'] = 62005, + ['feed-repo'] = 62006, + ['feed-rocket'] = 62007, + ['feed-star'] = 62008, + ['feed-tag'] = 62009, + ['feed-trophy'] = 62010, + ['file-directory-fill'] = 62011, + ['file-directory-open-fill'] = 62012, + ['id-badge'] = 62013, + ['iterations'] = 62014, + ['log'] = 62015, + ['person-fill'] = 62016, + ['repo-deleted'] = 62017, + ['repo-locked'] = 62018, + ['single-select'] = 62019, + ['sliders'] = 62020, + ['stack'] = 62021, + ['tab-external'] = 62022, + ['telescope-fill'] = 62023, + ['trophy'] = 62024, + ['webhook'] = 62025, +} +return M diff --git a/lua/nonicons/override.lua b/lua/nonicons/override.lua new file mode 100644 index 0000000..9e18a9b --- /dev/null +++ b/lua/nonicons/override.lua @@ -0,0 +1,580 @@ +local mapping = require('nonicons.mapping') + +local function char(name) + local code = mapping[name] + if code then + return vim.fn.nr2char(code) + end +end + +local fallback_icon + +local ext_map = { + lua = 'lua', + luac = 'lua', + luau = 'lua', + + js = 'javascript', + cjs = 'javascript', + mjs = 'javascript', + jsx = 'react', + tsx = 'react', + + ts = 'typescript', + cts = 'typescript', + mts = 'typescript', + ['d.ts'] = 'typescript', + + py = 'python', + pyc = 'python', + pyd = 'python', + pyi = 'python', + pyo = 'python', + pyw = 'python', + pyx = 'python', + + rb = 'ruby', + rake = 'ruby', + gemspec = 'ruby', + + rs = 'rust', + rlib = 'rust', + + go = 'go', + + c = 'c', + h = 'c', + + cpp = 'c-plusplus', + cc = 'c-plusplus', + cxx = 'c-plusplus', + ['c++'] = 'c-plusplus', + cp = 'c-plusplus', + cppm = 'c-plusplus', + cxxm = 'c-plusplus', + mpp = 'c-plusplus', + hh = 'c-plusplus', + hpp = 'c-plusplus', + hxx = 'c-plusplus', + ixx = 'c-plusplus', + mm = 'c-plusplus', + + cs = 'c-sharp', + cshtml = 'c-sharp', + csproj = 'c-sharp', + + java = 'java', + jar = 'java', + + kt = 'kotlin', + kts = 'kotlin', + + swift = 'swift', + + dart = 'dart', + + elm = 'elm', + + ex = 'elixir', + exs = 'elixir', + eex = 'elixir', + heex = 'elixir', + leex = 'elixir', + + vue = 'vue', + + svelte = 'svelte', + + html = 'html', + htm = 'html', + + css = 'css', + scss = 'css', + sass = 'css', + less = 'css', + styl = 'css', + + json = 'json', + json5 = 'json', + jsonc = 'json', + cson = 'json', + + yaml = 'yaml', + yml = 'yaml', + + toml = 'toml', + + md = 'markdown', + markdown = 'markdown', + mdx = 'markdown', + + php = 'php', + ['blade.php'] = 'php', + + pl = 'perl', + pm = 'perl', + + r = 'r', + R = 'r', + rmd = 'r', + + scala = 'scala', + sc = 'scala', + sbt = 'scala', + + vim = 'vim', + + graphql = 'graphql', + gql = 'graphql', + + tf = 'terraform', + tfvars = 'terraform', + + Dockerfile = 'docker', + dockerignore = 'docker', + + angular = 'angular', + + sh = 'terminal', + bash = 'terminal', + zsh = 'terminal', + fish = 'terminal', + ksh = 'terminal', + csh = 'terminal', + terminal = 'terminal', + ps1 = 'terminal', + + nix = 'code', + + sql = 'database', + sqlite = 'database', + sqlite3 = 'database', + db = 'database', + dump = 'database', + + rss = 'rss', + tmux = 'tmux', + nginx = 'nginx', + + diff = 'diff', + patch = 'diff', + + lock = 'lock', + lck = 'lock', + + conf = 'gear', + cfg = 'gear', + ini = 'gear', + env = 'key', + + git = 'git-branch', + + license = 'law', + + log = 'log', + + xml = 'code', + xslt = 'code', + + tex = 'book', + bib = 'book', + + png = 'image', + jpg = 'image', + jpeg = 'image', + gif = 'image', + bmp = 'image', + ico = 'image', + webp = 'image', + avif = 'image', + svg = 'image', + tiff = 'image', + jxl = 'image', + + zip = 'file-zip', + gz = 'file-zip', + tgz = 'file-zip', + ['7z'] = 'file-zip', + rar = 'file-zip', + bz = 'file-zip', + bz2 = 'file-zip', + bz3 = 'file-zip', + xz = 'file-zip', + zst = 'file-zip', + txz = 'file-zip', + tar = 'file-zip', + + bin = 'file-binary', + exe = 'file-binary', + dll = 'file-binary', + so = 'file-binary', + o = 'file-binary', + a = 'file-binary', + elf = 'file-binary', + ko = 'file-binary', + lib = 'file-binary', + out = 'file-binary', + + mp3 = 'play', + mp4 = 'play', + mkv = 'play', + mov = 'play', + avi = 'play', + flac = 'play', + ogg = 'play', + wav = 'play', + webm = 'play', + aac = 'play', + m4a = 'play', + m4v = 'play', + ogv = 'play', + wma = 'play', + wmv = 'play', + + ttf = 'typography', + otf = 'typography', + woff = 'typography', + woff2 = 'typography', + eot = 'typography', + + pdf = 'file', + doc = 'file', + docx = 'file', + ppt = 'file', + pptx = 'file', + xls = 'file', + xlsx = 'file', + csv = 'file', + txt = 'file', + + erl = 'code', + hrl = 'code', + hs = 'code', + lhs = 'code', + ml = 'code', + mli = 'code', + clj = 'code', + cljs = 'code', + cljc = 'code', + edn = 'code', + fnl = 'code', + el = 'code', + elc = 'code', + eln = 'code', + nim = 'code', + zig = 'code', + odin = 'code', + gleam = 'code', + cr = 'code', + jl = 'code', + nu = 'code', + pro = 'code', + scm = 'code', + rkt = 'code', + sol = 'code', + wasm = 'code', + ipynb = 'code', + gradle = 'code', + groovy = 'code', + ino = 'code', + prisma = 'code', + astro = 'code', + hx = 'code', + d = 'code', + ada = 'code', + adb = 'code', + ads = 'code', + f90 = 'code', + vala = 'code', + v = 'code', + vh = 'code', + vhd = 'code', + vhdl = 'code', + sv = 'code', + svh = 'code', + mo = 'code', + mojo = 'code', +} + +local filename_map = { + ['dockerfile'] = 'docker', + ['containerfile'] = 'docker', + ['docker-compose.yml'] = 'docker', + ['docker-compose.yaml'] = 'docker', + ['compose.yml'] = 'docker', + ['compose.yaml'] = 'docker', + ['.dockerignore'] = 'docker', + + ['.gitignore'] = 'git-branch', + ['.gitconfig'] = 'git-branch', + ['.gitattributes'] = 'git-branch', + ['.gitmodules'] = 'git-branch', + ['.git-blame-ignore-revs'] = 'git-branch', + ['.mailmap'] = 'git-branch', + ['commit_editmsg'] = 'git-commit', + + ['.bashrc'] = 'terminal', + ['.bash_profile'] = 'terminal', + ['.zshrc'] = 'terminal', + ['.zshenv'] = 'terminal', + ['.zprofile'] = 'terminal', + ['makefile'] = 'terminal', + ['gnumakefile'] = 'terminal', + ['.justfile'] = 'terminal', + ['justfile'] = 'terminal', + + ['.eslintrc'] = 'eslint', + ['.eslintignore'] = 'eslint', + ['eslint.config.js'] = 'eslint', + ['eslint.config.cjs'] = 'eslint', + ['eslint.config.mjs'] = 'eslint', + ['eslint.config.ts'] = 'eslint', + + ['.prettierrc'] = 'prettier', + ['.prettierignore'] = 'prettier', + ['.prettierrc.js'] = 'prettier', + ['.prettierrc.cjs'] = 'prettier', + ['.prettierrc.mjs'] = 'prettier', + ['.prettierrc.json'] = 'prettier', + ['.prettierrc.json5'] = 'prettier', + ['.prettierrc.toml'] = 'prettier', + ['.prettierrc.yaml'] = 'prettier', + ['.prettierrc.yml'] = 'prettier', + ['prettier.config.js'] = 'prettier', + ['prettier.config.cjs'] = 'prettier', + ['prettier.config.mjs'] = 'prettier', + ['prettier.config.ts'] = 'prettier', + + ['.babelrc'] = 'babel', + + ['package.json'] = 'npm', + ['package-lock.json'] = 'npm', + ['.npmrc'] = 'npm', + ['.npmignore'] = 'npm', + + ['pnpm-lock.yaml'] = 'yarn', + ['pnpm-workspace.yaml'] = 'package', + ['.pnpmfile.cjs'] = 'npm', + ['bun.lock'] = 'package', + ['bun.lockb'] = 'package', + + ['tsconfig.json'] = 'typescript', + + ['license'] = 'law', + ['license.md'] = 'law', + ['copying'] = 'law', + ['copying.lesser'] = 'law', + ['unlicense'] = 'law', + + ['tmux.conf'] = 'tmux', + ['tmux.conf.local'] = 'tmux', + + ['readme'] = 'book', + ['readme.md'] = 'book', + + ['go.mod'] = 'go', + ['go.sum'] = 'go', + ['go.work'] = 'go', + + ['.vimrc'] = 'vim', + ['.gvimrc'] = 'vim', + ['_vimrc'] = 'vim', + ['_gvimrc'] = 'vim', + + ['next.config.js'] = 'next', + ['next.config.cjs'] = 'next', + ['next.config.ts'] = 'next', + + ['svelte.config.js'] = 'svelte', + + ['mix.lock'] = 'elixir', + + ['.env'] = 'key', + + ['config'] = 'gear', + ['.editorconfig'] = 'gear', + + ['procfile'] = 'server', + + ['Gemfile'] = 'ruby', + ['rakefile'] = 'ruby', + + ['Jenkinsfile'] = 'gear', + + ['.gitlab-ci.yml'] = 'logo-github', + + ['security'] = 'shield', + ['security.md'] = 'shield', + + ['robots.txt'] = 'globe', + + ['vite.config.js'] = 'code', + ['vite.config.ts'] = 'code', + ['vite.config.cjs'] = 'code', + ['vite.config.cts'] = 'code', + ['vite.config.mjs'] = 'code', + ['vite.config.mts'] = 'code', + + ['build.gradle'] = 'code', + ['settings.gradle'] = 'code', + + ['pom.xml'] = 'code', + + ['hyprland.conf'] = 'gear', + ['hyprlock.conf'] = 'gear', + ['hypridle.conf'] = 'gear', + ['hyprpaper.conf'] = 'gear', + + ['cmakelists.txt'] = 'code', + ['code_of_conduct'] = 'book', + ['code_of_conduct.md'] = 'book', +} + +local function resolve(name, ext) + if ext and ext_map[ext] then + return char(ext_map[ext]) + end + if name then + local lower = name:lower() + if filename_map[lower] then + return char(filename_map[lower]) + end + local dot_ext = lower:match('%.(.+)$') + if dot_ext and ext_map[dot_ext] then + return char(ext_map[dot_ext]) + end + end + return fallback_icon +end + +local M = {} + +function M.apply() + local ok, devicons = pcall(require, 'nvim-web-devicons') + if not ok then + return + end + + fallback_icon = char('file') + + local orig_get_icon = devicons.get_icon + devicons.get_icon = function(name, ext, opts) + local icon, hl = orig_get_icon(name, ext, opts) + if icon then + icon = resolve(name, ext) + end + return icon, hl + end + + local orig_get_icon_by_filetype = devicons.get_icon_by_filetype + devicons.get_icon_by_filetype = function(ft, opts) + local icon, hl = orig_get_icon_by_filetype(ft, opts) + if icon then + local nonicons_name = ext_map[ft] + icon = nonicons_name and char(nonicons_name) or fallback_icon + end + return icon, hl + end + + local orig_get_icon_colors = devicons.get_icon_colors + devicons.get_icon_colors = function(name, ext, opts) + local icon, color, cterm_color = orig_get_icon_colors(name, ext, opts) + if icon then + icon = resolve(name, ext) + end + return icon, color, cterm_color + end + + local orig_get_icon_color = devicons.get_icon_color + devicons.get_icon_color = function(name, ext, opts) + local icon, color = orig_get_icon_color(name, ext, opts) + if icon then + icon = resolve(name, ext) + end + return icon, color + end + + local orig_get_icon_cterm_color = devicons.get_icon_cterm_color + devicons.get_icon_cterm_color = function(name, ext, opts) + local icon, cterm_color = orig_get_icon_cterm_color(name, ext, opts) + if icon then + icon = resolve(name, ext) + end + return icon, cterm_color + end + + local orig_get_icon_colors_by_filetype = devicons.get_icon_colors_by_filetype + devicons.get_icon_colors_by_filetype = function(ft, opts) + local icon, color, cterm_color = orig_get_icon_colors_by_filetype(ft, opts) + if icon then + local nonicons_name = ext_map[ft] + icon = nonicons_name and char(nonicons_name) or fallback_icon + end + return icon, color, cterm_color + end + + local orig_get_icon_color_by_filetype = devicons.get_icon_color_by_filetype + devicons.get_icon_color_by_filetype = function(ft, opts) + local icon, color = orig_get_icon_color_by_filetype(ft, opts) + if icon then + local nonicons_name = ext_map[ft] + icon = nonicons_name and char(nonicons_name) or fallback_icon + end + return icon, color + end + + local orig_get_icon_cterm_color_by_filetype = devicons.get_icon_cterm_color_by_filetype + devicons.get_icon_cterm_color_by_filetype = function(ft, opts) + local icon, cterm_color = orig_get_icon_cterm_color_by_filetype(ft, opts) + if icon then + local nonicons_name = ext_map[ft] + icon = nonicons_name and char(nonicons_name) or fallback_icon + end + return icon, cterm_color + end + + local function override_tables() + local by_ext = devicons.get_icons_by_extension() + for ext, data in pairs(by_ext) do + local name = ext_map[ext] + if name then + data.icon = char(name) or fallback_icon + else + data.icon = fallback_icon + end + end + + local by_filename = devicons.get_icons_by_filename() + for fname, data in pairs(by_filename) do + local name = filename_map[fname] + if name then + data.icon = char(name) or fallback_icon + else + data.icon = fallback_icon + end + end + + devicons.set_default_icon(fallback_icon) + end + + override_tables() + + local group = vim.api.nvim_create_augroup('Nonicons', { clear = true }) + + vim.api.nvim_create_autocmd('OptionSet', { + pattern = 'background', + group = group, + callback = function() + vim.schedule(override_tables) + end, + }) + + vim.api.nvim_create_autocmd('ColorScheme', { + group = group, + callback = function() + vim.schedule(override_tables) + end, + }) +end + +return M diff --git a/nonicons.nvim-scm-1.rockspec b/nonicons.nvim-scm-1.rockspec new file mode 100644 index 0000000..b5b50fb --- /dev/null +++ b/nonicons.nvim-scm-1.rockspec @@ -0,0 +1,21 @@ +rockspec_format = '3.0' +package = 'nonicons.nvim' +version = 'scm-1' + +source = { + url = 'git+https://github.com/barrettruth/nonicons.nvim.git', +} + +description = { + summary = 'Nonicons for Neovim', + homepage = 'https://github.com/barrettruth/nonicons.nvim', + license = 'MIT', +} + +dependencies = { + 'lua >= 5.1', +} + +build = { + type = 'builtin', +} diff --git a/plugin/nonicons.lua b/plugin/nonicons.lua new file mode 100644 index 0000000..7219658 --- /dev/null +++ b/plugin/nonicons.lua @@ -0,0 +1,6 @@ +if vim.g.loaded_nonicons then + return +end +vim.g.loaded_nonicons = 1 + +require('nonicons').apply() diff --git a/selene.toml b/selene.toml new file mode 100644 index 0000000..96cf5ab --- /dev/null +++ b/selene.toml @@ -0,0 +1 @@ +std = 'vim' diff --git a/stylua.toml b/stylua.toml new file mode 100644 index 0000000..01ded03 --- /dev/null +++ b/stylua.toml @@ -0,0 +1,8 @@ +column_width = 100 +line_endings = "Unix" +indent_type = "Spaces" +indent_width = 2 +quote_style = "AutoPreferSingle" +call_parentheses = "Always" +[sort_requires] +enabled = true diff --git a/vim.toml b/vim.toml new file mode 100644 index 0000000..3a84ac2 --- /dev/null +++ b/vim.toml @@ -0,0 +1,33 @@ +[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