Repository: nvim-lualine/lualine.nvim Branch: master Commit: 47f91c416dae Files: 138 Total size: 467.4 KB Directory structure: gitextract_a833klid/ ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── generated-files-bot.yml │ ├── stale.yml │ └── workflows/ │ ├── autogen.yml │ └── ci.yml ├── .gitignore ├── .luacheckrc ├── .luacov ├── .stylua.toml ├── CONTRIBUTING.md ├── LICENSE ├── Makefile ├── README.md ├── THEMES.md ├── doc/ │ └── lualine.txt ├── examples/ │ ├── bubbles.lua │ ├── cosmicink.lua │ ├── evil_lualine.lua │ └── slanted-gaps.lua ├── lua/ │ ├── lualine/ │ │ ├── component.lua │ │ ├── components/ │ │ │ ├── branch/ │ │ │ │ ├── git_branch.lua │ │ │ │ └── init.lua │ │ │ ├── buffers/ │ │ │ │ ├── buffer.lua │ │ │ │ └── init.lua │ │ │ ├── datetime.lua │ │ │ ├── diagnostics/ │ │ │ │ ├── config.lua │ │ │ │ ├── init.lua │ │ │ │ └── sources.lua │ │ │ ├── diff/ │ │ │ │ ├── git_diff.lua │ │ │ │ └── init.lua │ │ │ ├── encoding.lua │ │ │ ├── fileformat.lua │ │ │ ├── filename.lua │ │ │ ├── filesize.lua │ │ │ ├── filetype.lua │ │ │ ├── hostname.lua │ │ │ ├── location.lua │ │ │ ├── lsp_status.lua │ │ │ ├── mode.lua │ │ │ ├── progress.lua │ │ │ ├── searchcount.lua │ │ │ ├── selectioncount.lua │ │ │ ├── special/ │ │ │ │ ├── eval_func_component.lua │ │ │ │ ├── function_component.lua │ │ │ │ └── vim_var_component.lua │ │ │ ├── tabs/ │ │ │ │ ├── init.lua │ │ │ │ └── tab.lua │ │ │ └── windows/ │ │ │ ├── init.lua │ │ │ └── window.lua │ │ ├── config.lua │ │ ├── extensions/ │ │ │ ├── aerial.lua │ │ │ ├── assistant.lua │ │ │ ├── avante.lua │ │ │ ├── chadtree.lua │ │ │ ├── ctrlspace.lua │ │ │ ├── fern.lua │ │ │ ├── fugitive.lua │ │ │ ├── fzf.lua │ │ │ ├── lazy.lua │ │ │ ├── man.lua │ │ │ ├── mason.lua │ │ │ ├── mundo.lua │ │ │ ├── neo-tree.lua │ │ │ ├── nerdtree.lua │ │ │ ├── nvim-dap-ui.lua │ │ │ ├── nvim-tree.lua │ │ │ ├── oil.lua │ │ │ ├── overseer.lua │ │ │ ├── quickfix.lua │ │ │ ├── symbols-outline.lua │ │ │ ├── toggleterm.lua │ │ │ └── trouble.lua │ │ ├── highlight.lua │ │ ├── themes/ │ │ │ ├── 16color.lua │ │ │ ├── OceanicNext.lua │ │ │ ├── PaperColor.lua │ │ │ ├── Tomorrow.lua │ │ │ ├── auto.lua │ │ │ ├── ayu.lua │ │ │ ├── ayu_dark.lua │ │ │ ├── ayu_light.lua │ │ │ ├── ayu_mirage.lua │ │ │ ├── base16.lua │ │ │ ├── codedark.lua │ │ │ ├── dracula.lua │ │ │ ├── everforest.lua │ │ │ ├── gruvbox-material.lua │ │ │ ├── gruvbox.lua │ │ │ ├── gruvbox_dark.lua │ │ │ ├── gruvbox_light.lua │ │ │ ├── horizon.lua │ │ │ ├── iceberg.lua │ │ │ ├── iceberg_dark.lua │ │ │ ├── iceberg_light.lua │ │ │ ├── jellybeans.lua │ │ │ ├── material.lua │ │ │ ├── modus-vivendi.lua │ │ │ ├── molokai.lua │ │ │ ├── moonfly.lua │ │ │ ├── nightfly.lua │ │ │ ├── nord.lua │ │ │ ├── onedark.lua │ │ │ ├── onelight.lua │ │ │ ├── palenight.lua │ │ │ ├── papercolor_dark.lua │ │ │ ├── papercolor_light.lua │ │ │ ├── powerline.lua │ │ │ ├── powerline_dark.lua │ │ │ ├── pywal.lua │ │ │ ├── seoul256.lua │ │ │ ├── solarized.lua │ │ │ ├── solarized_dark.lua │ │ │ ├── solarized_light.lua │ │ │ ├── tomorrow_night.lua │ │ │ └── wombat.lua │ │ └── utils/ │ │ ├── class.lua │ │ ├── color_utils.lua │ │ ├── fn_store.lua │ │ ├── job.lua │ │ ├── loader.lua │ │ ├── mode.lua │ │ ├── notices.lua │ │ ├── nvim_opts.lua │ │ ├── section.lua │ │ └── utils.lua │ ├── lualine.lua │ └── lualine_require.lua ├── scripts/ │ ├── docgen.sh │ ├── nvim_isolated_conf.sh │ └── test_runner.sh └── tests/ ├── helpers.lua ├── minimal_init.lua ├── spec/ │ ├── component_spec.lua │ ├── config_spec.lua │ ├── lualine_spec.lua │ └── utils_spec.lua └── statusline.lua ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug Report about: Report a problem in lualine title: 'Bug: ' labels: bug --- ### Self Checks - [ ] I'm using the latest lualine. - [ ] I didn't find the issue in existing issues or PRs. ### How to reproduce the problem ### Expected behaviour ### Actual behaviour ### Minimal config to reproduce the issue ### Additional information ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature Request about: Request a feature for lualine title: "Feat: " labels: new feature --- ### Requested feature ### Motivation ================================================ FILE: .github/generated-files-bot.yml ================================================ generatedFiles: - path: "doc/lualine.txt" message: "`lualine.txt` is generated from README.md. Make changes there instead." ignoreAuthors: - 'github-actions[bot]' - 'shadmansaleh' ================================================ FILE: .github/stale.yml ================================================ # Number of days of inactivity before an issue becomes stale daysUntilStale: 60 # Number of days of inactivity before a stale issue is closed daysUntilClose: 7 # Issues with these labels will never be considered stale exemptLabels: - pinned - upstream - help wanted - wip - good first issue - discuss - pin # Label to use when marking an issue as stale staleLabel: stale # Comment to post when marking an issue as stale. Set to `false` to disable markComment: > This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions. # Comment to post when closing a stale issue. Set to `false` to disable closeComment: false ================================================ FILE: .github/workflows/autogen.yml ================================================ name: autogen on: push: branches: [master] paths: - "lua/**.lua" - "examples/**.lua" - "tests/**.lua" - ".stylua.toml" - "README.md" # Cancel any in-progress CI runs for a PR if it is updated concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true jobs: autogen: name: "(vimdoc|formating)" runs-on: ubuntu-22.04 timeout-minutes: 10 permissions: contents: write pull-requests: write steps: - uses: actions/checkout@v2 - name: Generate docs with panvimdoc uses: kdheepak/panvimdoc@v2.7.1 with: vimdoc: lualine description: fast and easy to configure statusline plugin for neovim - name: Apply stylua uses: JohnnyMorganz/stylua-action@1.0.0 with: token: ${{ secrets.GITHUB_TOKEN }} args: --config-path=.stylua.toml lua/ examples/ tests/ version: 0.14.1 - name: Push changes uses: stefanzweifel/git-auto-commit-action@v4 with: commit_user_name: github-actions[bot] commit_user_email: 41898282+github-actions[bot]@users.noreply.github.com commit_message: "chore: autogen (vimdocs+formating)" branch: ${{ github.head_ref }} file_pattern: lua/ examples/ tests/ doc/lualine.txt ================================================ FILE: .github/workflows/ci.yml ================================================ name: Tests on: push: branches: [master] pull_request: branches: [master] # Cancel any in-progress CI runs for a PR if it is updated concurrency: group: ${{ github.workflow }}-${{ github.event_name == 'pull_request' && github.head_ref || github.sha }} cancel-in-progress: true jobs: tests: name: ${{ matrix.os }} ${{ matrix.flavor }} strategy: fail-fast: false matrix: include: - flavor: nvim-0.7 runner: ubuntu-22.04 os: linux nvim_version: v0.7.0 - flavor: nvim-0.8 runner: ubuntu-22.04 os: linux nvim_version: v0.8.0 - flavor: nvim-0.9 runner: ubuntu-22.04 os: linux nvim_version: v0.9.0 - flavor: nvim-0.10 runner: ubuntu-22.04 os: linux nvim_version: v0.10.0 - flavor: nvim-0.11 runner: ubuntu-22.04 os: linux nvim_version: v0.11.0 - flavor: nvim-nightly runner: ubuntu-22.04 os: linux nvim_version: nightly runs-on: ${{ matrix.runner }} timeout-minutes: 10 steps: - uses: actions/checkout@v2 - name: Setup Test Environment run: | mkdir -p ./tmp_home/nvim/pack/vendor/start git clone --depth 1 https://github.com/nvim-lua/plenary.nvim ./tmp_home/nvim/pack/vendor/start/plenary.nvim git clone --depth 1 https://github.com/nvim-tree/nvim-web-devicons ./tmp_home/nvim/pack/vendor/start/nvim-web-devicons ln -s $(pwd) ./tmp_home/nvim/pack/vendor/start - name: Setup neovim ${{matrix.nvim_version}} uses: rhysd/action-setup-vim@v1 with: neovim: true version: ${{matrix.nvim_version}} - name: Run tests run: | make test lint: runs-on: ubuntu-22.04 timeout-minutes: 10 steps: - uses: actions/checkout@v2 - name: Setup linters run: | sudo apt-get update sudo apt-get install luarocks sudo luarocks install luacheck - name: Run luacheck run: | make lint ================================================ FILE: .gitignore ================================================ # Compiled Lua sources luac.out # luarocks build files *.src.rock *.zip *.tar.gz # Object files *.o *.os *.ko *.obj *.elf # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo *.def *.exp # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # ctags tags # helptags /doc/tags # panvimdoc /panvimdoc/ # luacov /luacov.report /luacov.report.index /luacov.stats # tests /tmp_home/ .luarc.json ================================================ FILE: .luacheckrc ================================================ globals = { "vim", "assert" } -- Don't report unused self arguments of methods. self = false -- Rerun tests only if their modification time changed. cache = true ignore = { "631", -- max_line_length "212/_.*", -- unused argument, for vars with "_" prefix } ================================================ FILE: .luacov ================================================ return { include = { "lua/lualine$", "lua/lualine_require$", "lua/lualine%/.+$", }, exclude = { "lua/lualine/themes%/.+$", "lua/lualine/extensions%/.+$", }, modules = { ["lualine"] = "lua/lualine.lua", ["lualine_require"] = "lua/lualine_require.lua", ["lualine.*"] = "lua/", }, statsfile = 'luacov.stats', reportfile = 'luacov.report', -- includeuntestedfiles = true, } -- vim:ft=lua ================================================ FILE: .stylua.toml ================================================ indent_type = "Spaces" indent_width = 2 quote_style = "AutoPreferSingle" call_parentheses = "NoSingleTable" collapse_simple_statement = "Never" ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing to lualine.nvim Thanks for considering to contribute. ### Getting started If you're not sure where to help? You can try these: - You can look at the currently open [issues](https://github.com/nvim-lualine/lualine.nvim/issues) to see if some bug needs fixing or for cool feature ideas.
You should also look at currently open PRs ([Pull requests](https://github.com/nvim-lualine/lualine.nvim/pulls)) to see if some abandoned PR interests you.
*We could really use some help with tests & docs they are currently lacking :)* - You can add an exciting new component, extension or theme. Note: Currently we aren't adding regular colorscheme based themes. We think they make more sense with colorschemes as they tend not to get updated once added here. But if you have some unique themes idea like [auto](https://github.com/nvim-lualine/lualine.nvim/blob/master/THEMES.md#auto) or [pywal](https://github.com/nvim-lualine/lualine.nvim/blob/master/THEMES.md#pywal) feel free to open an PR or issue. - Feel free to open issues or unfinished PRs for help. I'd actually recommend you to open an issue first for bigger PRs to discuss the feature with a maintainer beforehand. That way you can know if the feature is likely to be accepted or not before you get started. You'll get recommendation and help with implementation specially if you show willingness to implement it yourself. - Do add tests and docs for your changes. Good luck! ### Developer tools *Let's introduce you to the tools we use.* - Your PR needs to pass tests & linter. We lint our codebase with [luacheck](https://github.com/mpeterv/luacheck) and run tests with [plenary-test][plenary.nvim] these will be run on CI. If you want you can run tests & linter locally with `make test` & `make lint` respectively. Or `make check` to run both linter & tests. For running tests you'll have to make sure lualine.nvim, [plenary.nvim][plenary.nvim] and [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) are in same directory. - Lua codebase gets formatted with [stylua](https://github.com/JohnnyMorganz/StyLua) in CI. So you can ignore formatting. But if you want to submit formatted PR you can run formatter locally with `make format`. - VimDocs are auto generated with [panvimdoc](https://github.com/kdheepak/panvimdoc) from README.md. So don't make changes to doc/lualine.txt . Instead add your docs to README or Wiki. The docgen in ran by CI too. If you want to run it locally you can do so with `make docgen`. Note: you'll need to have [pandoc](https://github.com/jgm/pandoc) installed. - `make precommit_check` can come quite handy it'll run all the above mentioned tools - You can check our test coverage with `make testcov`. You'll need to have [luacov](https://github.com/keplerproject/luacov) & [luacov-console](https://github.com/spacewander/luacov-console) installed for that. If you want luacov's detailed report files, run the command with the `NOCLEAN` env set. For example `NOCLEAN=1 make testcov` [plenary.nvim]: https://github.com/nvim-lua/plenary.nvim ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2020 hoob3rt 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. ================================================ FILE: Makefile ================================================ .DEFAULT_GOAL = check lint: @luacheck lua/lualine/ @luacheck tests/ @luacheck examples/ format: @stylua --config-path=.stylua.toml lua/ examples/ tests/ test: @mkdir -p tmp_home @export XDG_DATA_HOME='./tmp_home' && \ export XDG_CONFIG_HOME='./tmp_home' && \ bash ./scripts/test_runner.sh @rm -rf tmp_home # Install luacov & luacov-console from luarocks testcov: @mkdir -p ./tmp_home/data/nvim @mkdir -p ./tmp_home/config/nvim @export XDG_DATA_HOME=$(realpath './tmp_home/data') && \ export XDG_CONFIG_HOME=$(realpath './tmp_home/config') && \ export TEST_COV=true && \ bash ./scripts/test_runner.sh @luacov-console lua/ @luacov-console -s ifeq ($(NOCLEAN), ) @rm luacov.* endif @rm -rf tmp_home docgen: @sh ./scripts/docgen.sh precommit_check: docgen format test lint check: lint test ================================================ FILE: README.md ================================================ # lualine.nvim ![code size](https://img.shields.io/github/languages/code-size/nvim-lualine/lualine.nvim?style=flat-square) ![license](https://img.shields.io/github/license/nvim-lualine/lualine.nvim?style=flat-square) A blazing fast and easy to configure Neovim statusline written in Lua. `lualine.nvim` requires Neovim >= 0.7. For previous versions of neovim please use compatability tags for example compat-nvim-0.5 ## Contributing Feel free to create an issue/PR if you want to see anything else implemented. If you have some question or need help with configuration, start a [discussion](https://github.com/nvim-lualine/lualine.nvim/discussions). Please read [CONTRIBUTING.md](./CONTRIBUTING.md) before opening a PR. You can also help with documentation in the [wiki](https://github.com/nvim-lualine/lualine.nvim/wiki). ## Screenshots Here is a preview of what lualine can look like.

Screenshots of all available themes are listed in [THEMES.md](./THEMES.md) For those who want to break the norms, you can create custom looks for lualine. **Example** : - [evil_lualine](examples/evil_lualine.lua) - [slanted-gaps](examples/slanted-gaps.lua) - [bubbles](examples/bubbles.lua) - [cosmicink](examples/cosmicink.lua) ## Performance compared to other plugins Unlike other statusline plugins, lualine loads only the components you specify, and nothing else. Startup time performance measured with an amazing plugin [dstein64/vim-startuptime](https://github.com/dstein64/vim-startuptime) Times are measured with a clean `init.vim` with only `vim-startuptime`, `vim-plug` and given statusline plugin installed. In control just `vim-startuptime` and`vim-plug` is installed. And measured time is complete startuptime of vim not time spent on specific plugin. These numbers are the average of 20 runs. | control | lualine | lightline | airline | | :-----: | :-----: | :-------: | :-----: | | 17.2 ms | 24.8 ms | 25.5 ms | 79.9 ms | Last Updated On: 18-04-2022 ## Installation ### [vim-plug](https://github.com/junegunn/vim-plug) ```vim Plug 'nvim-lualine/lualine.nvim' " If you want to have icons in your statusline choose one of these Plug 'nvim-tree/nvim-web-devicons' ``` ### [packer.nvim](https://github.com/wbthomason/packer.nvim) ```lua use { 'nvim-lualine/lualine.nvim', requires = { 'nvim-tree/nvim-web-devicons', opt = true } } ``` ### [lazy.nvim](https://github.com/folke/lazy.nvim) ```lua { 'nvim-lualine/lualine.nvim', dependencies = { 'nvim-tree/nvim-web-devicons' } } ``` You'll also need to have a patched font if you want icons. ## Usage and customization Lualine has sections as shown below. ```text +-------------------------------------------------+ | A | B | C X | Y | Z | +-------------------------------------------------+ ``` Each sections holds its components e.g. Vim's current mode. ### Configuring lualine in init.vim All the examples below are in lua. You can use the same examples in `.vim` files by wrapping them in lua heredoc like this: ```vim lua << END require('lualine').setup() END ``` For more information, check out `:help lua-heredoc`. #### Default configuration ```lua require('lualine').setup { options = { icons_enabled = true, theme = 'auto', component_separators = { left = '', right = ''}, section_separators = { left = '', right = ''}, disabled_filetypes = { statusline = {}, winbar = {}, }, ignore_focus = {}, always_divide_middle = true, always_show_tabline = true, globalstatus = false, refresh = { statusline = 1000, tabline = 1000, winbar = 1000, refresh_time = 16, -- ~60fps events = { 'WinEnter', 'BufEnter', 'BufWritePost', 'SessionLoadPost', 'FileChangedShellPost', 'VimResized', 'Filetype', 'CursorMoved', 'CursorMovedI', 'ModeChanged', }, } }, sections = { lualine_a = {'mode'}, lualine_b = {'branch', 'diff', 'diagnostics'}, lualine_c = {'filename'}, lualine_x = {'encoding', 'fileformat', 'filetype'}, lualine_y = {'progress'}, lualine_z = {'location'} }, inactive_sections = { lualine_a = {}, lualine_b = {}, lualine_c = {'filename'}, lualine_x = {'location'}, lualine_y = {}, lualine_z = {} }, tabline = {}, winbar = {}, inactive_winbar = {}, extensions = {} } ``` If you want to get your current lualine config, you can do so with: ```lua require('lualine').get_config() ``` --- ### Starting lualine ```lua require('lualine').setup() ``` --- ### Setting a theme ```lua options = { theme = 'gruvbox' } ``` All available themes are listed in [THEMES.md](./THEMES.md). Please create a PR if you managed to port a popular theme before us, [here is how to do it](./CONTRIBUTING.md). #### Customizing themes ```lua local custom_gruvbox = require'lualine.themes.gruvbox' -- Change the background of lualine_c section for normal mode custom_gruvbox.normal.c.bg = '#112233' require('lualine').setup { options = { theme = custom_gruvbox }, ... } ``` Theme structure is available [here](https://github.com/nvim-lualine/lualine.nvim/wiki/Writing-a-theme). --- ### Separators lualine defines two kinds of separators: - `section_separators` - separators between sections - `component_separators` - separators between the different components in sections **Note**: if viewing this README in a browser, chances are the characters below will not be visible. ```lua options = { section_separators = { left = '', right = '' }, component_separators = { left = '', right = '' } } ``` Here, left refers to the left-most sections (a, b, c), and right refers to the right-most sections (x, y, z). #### Disabling separators ```lua options = { section_separators = '', component_separators = '' } ``` --- ### Changing components in lualine sections ```lua sections = {lualine_a = {'mode'}} ``` #### Available components - `branch` (git branch) - `buffers` (shows currently available buffers) - `diagnostics` (diagnostics count from your preferred source) - `diff` (git diff status) - `encoding` (file encoding) - `fileformat` (file format) - `filename` - `filesize` - `filetype` - `hostname` - `location` (location in file in line:column format) - `mode` (vim mode) - `progress` (%progress in file) - `searchcount` (number of search matches when hlsearch is active) - `selectioncount` (number of selected characters or lines) - `tabs` (shows currently available tabs) - `windows` (shows currently available windows) - `lsp_status` (shows active LSPs in the current buffer and a progress spinner) #### Custom components ##### Lua functions as lualine component ```lua local function hello() return [[hello world]] end sections = { lualine_a = { hello } } ``` ##### Vim functions as lualine component ```lua sections = { lualine_a = {'FugitiveHead'} } ``` ##### Vim's statusline items as lualine component ```lua sections = { lualine_c = {'%=', '%t%m', '%3p'} } ``` ##### Vim variables as lualine component Variables from `g:`, `v:`, `t:`, `w:`, `b:`, `o:`, `to:`, `wo:`, `bo:` scopes can be used. See `:h lua-vim-variables` and `:h lua-vim-options` if you are not sure what to use. ```lua sections = { lualine_a = { 'g:coc_status', 'bo:filetype' } } ``` ##### Lua expressions as lualine component You can use any valid lua expression as a component including: - oneliners - global variables - require statements ```lua sections = { lualine_c = { "os.date('%a')", 'data', "require'lsp-status'.status()" } } ``` `data` is a global variable in this example. --- ### Component options Component options can change the way a component behave. There are two kinds of options: - global options affecting all components - local options affecting specific Global options can be used as local options (can be applied to specific components) but you cannot use local options as global. Global option used locally overwrites the global, for example: ```lua require('lualine').setup { options = { fmt = string.lower }, sections = { lualine_a = { { 'mode', fmt = function(str) return str:sub(1,1) end } }, lualine_b = {'branch'} } } ``` `mode` will be formatted with the passed function so only first char will be shown . On the other hand branch will be formatted with global formatter `string.lower` so it will be showed in lower case. #### Available options #### Global options These are `options` that are used in options table. They set behavior of lualine. Values set here are treated as default for other options that work in component level. For example even though `icons_enabled` is a general component option. You can set `icons_enabled` to `false` and icons will be disabled on all component. You can still overwrite defaults set in option table by specifying the option value in component. ```lua options = { theme = 'auto', -- lualine theme component_separators = { left = '', right = '' }, section_separators = { left = '', right = '' }, disabled_filetypes = { -- Filetypes to disable lualine for. statusline = {}, -- only ignores the ft for statusline. winbar = {}, -- only ignores the ft for winbar. }, ignore_focus = {}, -- If current filetype is in this list it'll -- always be drawn as inactive statusline -- and the last window will be drawn as active statusline. -- for example if you don't want statusline of -- your file tree / sidebar window to have active -- statusline you can add their filetypes here. -- -- Can also be set to a function that takes the -- currently focused window as its only argument -- and returns a boolean representing whether the -- window's statusline should be drawn as inactive. always_divide_middle = true, -- When set to true, left sections i.e. 'a','b' and 'c' -- can't take over the entire statusline even -- if neither of 'x', 'y' or 'z' are present. always_show_tabline = true -- When set to true, if you have configured lualine for displaying tabline -- then tabline will always show. If set to false, then tabline will be displayed -- only when there are more than 1 tab. (see :h showtabline) globalstatus = false, -- enable global statusline (have a single statusline -- at bottom of neovim instead of one for every window). -- This feature is only available in neovim 0.7 and higher. refresh = { -- sets how often lualine should refresh it's contents (in ms) statusline = 100, -- The refresh option sets minimum time that lualine tries tabline = 100, -- to maintain between refresh. It's not guarantied if situation winbar = 100 -- arises that lualine needs to refresh itself before this time -- it'll do it. refresh_time = 16, -- ~60fps the time after which refresh queue is processed. Mininum refreshtime for lualine events = { -- The auto command events at which lualine refreshes 'WinEnter', 'BufEnter', 'BufWritePost', 'SessionLoadPost', 'FileChangedShellPost', 'VimResized', 'Filetype', 'CursorMoved', 'CursorMovedI', 'ModeChanged', }, -- Also you can force lualine's refresh by calling refresh function -- like require('lualine').refresh() } } ``` #### General component options These are options that control behavior at component level and are available for all components. ```lua sections = { lualine_a = { { 'mode', icons_enabled = true, -- Enables the display of icons alongside the component. -- Defines the icon to be displayed in front of the component. -- Can be string|table -- As table it must contain the icon as first entry and can use -- color option to custom color the icon. Example: -- {'branch', icon = ''} / {'branch', icon = {'', color={fg='green'}}} -- icon position can also be set to the right side from table. Example: -- {'branch', icon = {'', align='right', color={fg='green'}}} icon = nil, separator = nil, -- Determines what separator to use for the component. -- Note: -- When a string is provided it's treated as component_separator. -- When a table is provided it's treated as section_separator. -- Passing an empty string disables the separator. -- -- These options can be used to set colored separators -- around a component. -- -- The options need to be set as such: -- separator = { left = '', right = ''} -- -- Where left will be placed on left side of component, -- and right will be placed on its right. -- cond = nil, -- Condition function, the component is loaded when the function returns `true`. draw_empty = false, -- Whether to draw component even if it's empty. -- Might be useful if you want just the separator. -- Defines a custom color for the component: -- -- 'highlight_group_name' | { fg = '#rrggbb'|cterm_value(0-255)|'color_name(red)', bg= '#rrggbb', gui='style' } | function -- Note: -- '|' is synonymous with 'or', meaning a different acceptable format for that placeholder. -- color function has to return one of other color types ('highlight_group_name' | { fg = '#rrggbb'|cterm_value(0-255)|'color_name(red)', bg= '#rrggbb', gui='style' }) -- color functions can be used to have different colors based on state as shown below. -- -- Examples: -- color = { fg = '#ffaa88', bg = 'grey', gui='italic,bold' }, -- color = { fg = 204 } -- When fg/bg are omitted, they default to the your theme's fg/bg. -- color = 'WarningMsg' -- Highlight groups can also be used. -- color = function(section) -- return { fg = vim.bo.modified and '#aa3355' or '#33aa88' } -- end, color = nil, -- The default is your theme's color for that section and mode. -- Specify what type a component is, if omitted, lualine will guess it for you. -- -- Available types are: -- [format: type_name(example)], mod(branch/filename), -- stl(%f/%m), var(g:coc_status/bo:modifiable), -- lua_expr(lua expressions), vim_fun(viml function name) -- -- Note: -- lua_expr is short for lua-expression and vim_fun is short for vim-function. type = nil, padding = 1, -- Adds padding to the left and right of components. -- Padding can be specified to left or right independently, e.g.: -- padding = { left = left_padding, right = right_padding } fmt = nil, -- Format function, formats the component's output. -- This function receives two arguments: -- - string that is going to be displayed and -- that can be changed, enhanced and etc. -- - context object with information you might -- need. E.g. tabnr if used with tabs. on_click = nil, -- takes a function that is called when component is clicked with mouse. -- the function receives several arguments -- - number of clicks in case of multiple clicks -- - mouse button used (l(left)/r(right)/m(middle)/...) -- - modifiers pressed (s(shift)/c(ctrl)/a(alt)/m(meta)...) } } } ``` #### Component specific options These are options that are available on specific components. For example, you have option on `diagnostics` component to specify what your diagnostic sources will be. #### buffers component options ```lua sections = { lualine_a = { { 'buffers', show_filename_only = true, -- Shows shortened relative path when set to false. hide_filename_extension = false, -- Hide filename extension when set to true. show_modified_status = true, -- Shows indicator when the buffer is modified. mode = 0, -- 0: Shows buffer name -- 1: Shows buffer index -- 2: Shows buffer name + buffer index -- 3: Shows buffer number -- 4: Shows buffer name + buffer number max_length = vim.o.columns * 2 / 3, -- Maximum width of buffers component, -- it can also be a function that returns -- the value of `max_length` dynamically. filetype_names = { TelescopePrompt = 'Telescope', dashboard = 'Dashboard', packer = 'Packer', fzf = 'FZF', alpha = 'Alpha' }, -- Shows specific buffer name for that filetype ( { `filetype` = `buffer_name`, ... } ) -- Automatically updates active buffer color to match color of other components (will be overidden if buffers_color is set) use_mode_colors = false, buffers_color = { -- Same values as the general color option can be used here. active = 'lualine_{section}_normal', -- Color for active buffer. inactive = 'lualine_{section}_inactive', -- Color for inactive buffer. }, symbols = { modified = ' ●', -- Text to show when the buffer is modified alternate_file = '#', -- Text to show to identify the alternate file directory = '', -- Text to show when the buffer is a directory }, } } } ``` #### datetime component options ```lua sections = { lualine_a = { { 'datetime', -- options: default, us, uk, iso, or your own format string ("%H:%M", etc..) style = 'default' } } } ``` #### diagnostics component options ```lua sections = { lualine_a = { { 'diagnostics', -- Table of diagnostic sources, available sources are: -- 'nvim_lsp', 'nvim_diagnostic', 'nvim_workspace_diagnostic', 'coc', 'ale', 'vim_lsp'. -- or a function that returns a table as such: -- { error=error_cnt, warn=warn_cnt, info=info_cnt, hint=hint_cnt } sources = { 'nvim_diagnostic', 'coc' }, -- Displays diagnostics for the defined severity types sections = { 'error', 'warn', 'info', 'hint' }, diagnostics_color = { -- Same values as the general color option can be used here. error = 'DiagnosticError', -- Changes diagnostics' error color. warn = 'DiagnosticWarn', -- Changes diagnostics' warn color. info = 'DiagnosticInfo', -- Changes diagnostics' info color. hint = 'DiagnosticHint', -- Changes diagnostics' hint color. }, symbols = {error = 'E', warn = 'W', info = 'I', hint = 'H'}, colored = true, -- Displays diagnostics status in color if set to true. update_in_insert = false, -- Update diagnostics in insert mode. always_visible = false, -- Show diagnostics even if there are none. } } } ``` #### diff component options ```lua sections = { lualine_a = { { 'diff', colored = true, -- Displays a colored diff status if set to true diff_color = { -- Same color values as the general color option can be used here. added = 'LuaLineDiffAdd', -- Changes the diff's added color modified = 'LuaLineDiffChange', -- Changes the diff's modified color removed = 'LuaLineDiffDelete', -- Changes the diff's removed color you }, symbols = {added = '+', modified = '~', removed = '-'}, -- Changes the symbols used by the diff. source = nil, -- A function that works as a data source for diff. -- It must return a table as such: -- { added = add_count, modified = modified_count, removed = removed_count } -- or nil on failure. count <= 0 won't be displayed. } } } ``` #### fileformat component options ```lua sections = { lualine_a = { { 'fileformat', symbols = { unix = '', -- e712 dos = '', -- e70f mac = '', -- e711 } } } } ``` #### filename component options ```lua sections = { lualine_a = { { 'filename', file_status = true, -- Displays file status (readonly status, modified status) newfile_status = false, -- Display new file status (new file means no write after created) path = 0, -- 0: Just the filename -- 1: Relative path -- 2: Absolute path -- 3: Absolute path, with tilde as the home directory -- 4: Filename and parent dir, with tilde as the home directory shorting_target = 40, -- Shortens path to leave 40 spaces in the window -- for other components. (terrible name, any suggestions?) -- It can also be a function that returns -- the value of `shorting_target` dynamically. symbols = { modified = '[+]', -- Text to show when the file is modified. readonly = '[-]', -- Text to show when the file is non-modifiable or readonly. unnamed = '[No Name]', -- Text to show for unnamed buffers. newfile = '[New]', -- Text to show for newly created file before first write } } } } ``` #### filetype component options ```lua sections = { lualine_a = { { 'filetype', colored = true, -- Displays filetype icon in color if set to true icon_only = false, -- Display only an icon for filetype icon = { align = 'right' }, -- Display filetype icon on the right hand side -- icon = {'X', align='right'} -- Icon string ^ in table is ignored in filetype component } } } ``` #### encoding component options ```lua sections = { lualine_a = { { 'encoding', -- Show '[BOM]' when the file has a byte-order mark show_bomb = false, } } } ``` #### searchcount component options ```lua sections = { lualine_a = { { 'searchcount', maxcount = 999, timeout = 500, } } } ``` #### tabs component options ```lua sections = { lualine_a = { { 'tabs', tab_max_length = 40, -- Maximum width of each tab. The content will be shorten dynamically (example: apple/orange -> a/orange) max_length = vim.o.columns / 3, -- Maximum width of tabs component. -- Note: -- It can also be a function that returns -- the value of `max_length` dynamically. mode = 0, -- 0: Shows tab_nr -- 1: Shows tab_name -- 2: Shows tab_nr + tab_name path = 0, -- 0: just shows the filename -- 1: shows the relative path and shorten $HOME to ~ -- 2: shows the full path -- 3: shows the full path and shorten $HOME to ~ -- Automatically updates active tab color to match color of other components (will be overidden if buffers_color is set) use_mode_colors = false, tabs_color = { -- Same values as the general color option can be used here. active = 'lualine_{section}_normal', -- Color for active tab. inactive = 'lualine_{section}_inactive', -- Color for inactive tab. }, show_modified_status = true, -- Shows a symbol next to the tab name if the file has been modified. symbols = { modified = '[+]', -- Text to show when the file is modified. }, fmt = function(name, context) -- Show + if buffer is modified in tab local buflist = vim.fn.tabpagebuflist(context.tabnr) local winnr = vim.fn.tabpagewinnr(context.tabnr) local bufnr = buflist[winnr] local mod = vim.fn.getbufvar(bufnr, '&mod') return name .. (mod == 1 and ' +' or '') end } } } ``` #### windows component options ```lua sections = { lualine_a = { { 'windows', show_filename_only = true, -- Shows shortened relative path when set to false. show_modified_status = true, -- Shows indicator when the window is modified. mode = 0, -- 0: Shows window name -- 1: Shows window index -- 2: Shows window name + window index max_length = vim.o.columns * 2 / 3, -- Maximum width of windows component, -- it can also be a function that returns -- the value of `max_length` dynamically. filetype_names = { TelescopePrompt = 'Telescope', dashboard = 'Dashboard', packer = 'Packer', fzf = 'FZF', alpha = 'Alpha' }, -- Shows specific window name for that filetype ( { `filetype` = `window_name`, ... } ) disabled_buftypes = { 'quickfix', 'prompt' }, -- Hide a window if its buffer's type is disabled -- Automatically updates active window color to match color of other components (will be overidden if buffers_color is set) use_mode_colors = false, windows_color = { -- Same values as the general color option can be used here. active = 'lualine_{section}_normal', -- Color for active window. inactive = 'lualine_{section}_inactive', -- Color for inactive window. }, } } } ``` #### lsp status component options ```lua sections = { lualine_a = { { 'lsp_status', icon = '', -- f013 symbols = { -- Standard unicode symbols to cycle through for LSP progress: spinner = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' }, -- Standard unicode symbol for when LSP is done: done = '✓', -- Delimiter inserted between LSP names: separator = ' ', }, -- List of LSP names to ignore (e.g., `null-ls`): ignore_lsp = {}, -- Display the LSP name show_name = true, } } } ``` --- ### Tabline You can use lualine to display components in tabline. The configuration for tabline sections is exactly the same as that of the statusline. ```lua tabline = { lualine_a = {}, lualine_b = {'branch'}, lualine_c = {'filename'}, lualine_x = {}, lualine_y = {}, lualine_z = {} } ``` This will show the branch and filename components on top of neovim inside tabline. lualine also provides 2 components, buffers and tabs, that you can use to get a more traditional tabline/bufferline. ```lua tabline = { lualine_a = {'buffers'}, lualine_b = {'branch'}, lualine_c = {'filename'}, lualine_x = {}, lualine_y = {}, lualine_z = {'tabs'} } ``` ### Winbar From neovim-0.8 you can customize your winbar with lualine. Winbar configuration is similar to statusline. ```lua winbar = { lualine_a = {}, lualine_b = {}, lualine_c = {'filename'}, lualine_x = {}, lualine_y = {}, lualine_z = {} } inactive_winbar = { lualine_a = {}, lualine_b = {}, lualine_c = {'filename'}, lualine_x = {}, lualine_y = {}, lualine_z = {} } ``` Just like statusline you can separately specify winbar for active and inactive windows. Any lualine component can be placed in winbar. All kinds of custom components supported in statusline are also supported for winbar too. In general You can treat winbar as another lualine statusline that just appears on top of windows instead of at bottom. #### Buffers Shows currently open buffers. Like bufferline . See [buffers options](#buffers-component-options) for all builtin behaviors of buffers component. You can use `:LualineBuffersJump` to jump to buffer based on index of buffer in buffers component. Jumping to non-existent buffer indices generates an error. To avoid these errors `LualineBuffersJump` provides `` support, meaning that you can call `:LualineBufferJump!` to ignore these errors. ```vim :LualineBuffersJump 2 " Jumps to 2nd buffer in buffers component. :LualineBuffersJump $ " Jumps to last buffer in buffers component. :LualineBuffersJump! 3 " Attempts to jump to 3rd buffer, if it exists. ``` #### Tabs Shows currently open tab. Like usual tabline. See [tabs options](#tabs-component-options) for all builtin behaviors of tabs component. You can also use `:LualineRenameTab` to set a name for a tabpage. For example: ```vim :LualineRenameTab Project_K ``` It's useful when you're using rendering mode 2/3 in tabs. To unname a tabpage run `:LualineRenameTab` without argument. #### Tabline as statusline You can also completely move your statusline to a tabline by configuring `lualine.tabline` and disabling `lualine.sections` and `lualine.inactive_sections`: ```lua tabline = { ...... }, sections = {}, inactive_sections = {}, ``` If you want a more sophisticated tabline you can use other tabline plugins with lualine too, for example: - [nvim-bufferline](https://github.com/akinsho/nvim-bufferline.lua) - [tabline.nvim](https://github.com/kdheepak/tabline.nvim) tabline.nvim even uses lualine's theme by default 🙌 You can find a bigger list [here](https://github.com/rockerBOO/awesome-neovim#tabline). --- ### Extensions lualine extensions change statusline appearance for a window/buffer with specified filetypes. By default no extensions are loaded to improve performance. You can load extensions with: ```lua extensions = {'quickfix'} ``` #### Available extensions - aerial - assistant - avante - chadtree - ctrlspace - fern - fugitive - fzf - lazy - man - mason - mundo - neo-tree - nerdtree - nvim-dap-ui - nvim-tree - oil - overseer - quickfix - symbols-outline - toggleterm - trouble #### Custom extensions You can define your own extensions. If you believe an extension may be useful to others, then please submit a PR. ```lua local my_extension = { sections = { lualine_a = {'mode'} }, filetypes = {'lua'} } require('lualine').setup { extensions = { my_extension } } ``` --- ### Refreshing lualine By default lualine refreshes itself based on timer and some events. You can set the interval of the timer with refresh option. However you can also force lualine to refresh at any time by calling `lualine.refresh` function. ```lua require('lualine').refresh({ scope = 'tabpage', -- scope of refresh all/tabpage/window place = { 'statusline', 'winbar', 'tabline' }, -- lualine segment ro refresh. }) ``` The arguments shown here are default values. So not passing any of them will be treated as if a default value was passed. So you can simply do ```lua require('lualine').refresh() ``` Also, note by default when you call refresh a refresh event is queued in lualine. It desn't refresh event immidiately. It'll refresh on next refresh check pass. By default this time is set to 16ms to match 60fps. This duration can be configured with `options.refresh.refresh_time` option. If you want to bypass the refresh queue and want lualine to process the refresh immmidiately call refresh with `force=true` parameter set like this. ```lua require('lualine').refresh({ force = true, -- do an immidiate refresh scope = 'tabpage', -- scope of refresh all/tabpage/window place = { 'statusline', 'winbar', 'tabline' }, -- lualine segment ro refresh. }) ``` Practically, speaking this is almost never needed. Also you should avoid calling `lualine.refresh` with `force` inside components. Since components are evaluated during refresh, calling refresh while refreshing can have undesirable effects. ### Disabling lualine You can disable lualine for specific filetypes: ```lua options = { disabled_filetypes = {'lua'} } ``` You can also disable lualine completely. Note that you need to call this after the setup ```lua require('lualine').hide({ place = {'statusline', 'tabline', 'winbar'}, -- The segment this change applies to. unhide = false, -- whether to re-enable lualine again/ }) ``` The arguments show for hide above are default values. Which means even if the hide function is called without arguments it'll work as if these were passed. So in short to disable lualine completely you can do ```lua require('lualine').hide() ``` To enable it again you can do ```lua require('lualine').hide({unhide=true}) ``` ### Contributors Thanks to these wonderful people, we enjoy this awesome plugin. ### Wiki Check out the [wiki](https://github.com/nvim-lualine/lualine.nvim/wiki) for more info. You can find some useful [configuration snippets](https://github.com/nvim-lualine/lualine.nvim/wiki/Component-snippets) here. You can also share your awesome snippets with others. If you want to extend lualine with plugins or want to know which ones already do, [wiki/plugins](https://github.com/nvim-lualine/lualine.nvim/wiki/Plugins) is for you. ================================================ FILE: THEMES.md ================================================ # Available themes All available themes are only best effort ports by myself/ other users. If you find a theme to be weird/ wrong please open an issue/ pr. ### auto auto is a special theme. It will automatically load theme for your colorscheme. If there's no theme available for your colorscheme then it'll try it's best to generate one. ### 16color

### ayu_dark

### ayu_light

### ayu_mirage

### ayu It's a combination of ayu_light, ayu_dark & ayu_mirage. If `g:ayucolor` exists, it loads one of these based on your `g:ayucolor` option. Otherwise, it will load ayu_light when background=light and ayu_dark when background=dark But if `g:ayuprefermirage` exists, it will load ayu_mirage instead when `background=dark`. ### base16 This theme will automatically use colors defined by your colorscheme using [tinted-theming/tinted-vim](https://github.com/tinted-theming/tinted-vim) or [RRethy/nvim-base16](https://github.com/RRethy/nvim-base16)] plugin. The following example is using the `tomorrow-night` colorscheme:

### codedark

### dracula

### everforest

### gruvbox_dark

### gruvbox_light

### gruvbox It's a combination of gruvbox_light and gruvbox_dark. It loads either of them based you your `background` option. ### gruvbox-material

### horizon

### iceberg_dark

### iceberg_light

### iceberg It's a combination of iceberg_light and iceberg_dark. It loads either of them based you your `background` option. ### jellybeans

### material

### modus-vivendi

### molokai

### moonfly

### nightfly

### nord

### OceanicNext

### onedark

### onelight

### palenight

### papercolor_dark

### papercolor_light

### PaperColor It's a combination of papercolor_light and papercolor_dark. It loads either of them based you your `background` option. ### powerline

### powerline_dark

### pywal pywal is another special theme. It will load the colors from your current [pywal](https://github.com/dylanaraps/pywal) cache, specifically `~/.cache/wal/colors.sh` and generate a theme. #### `wal --theme ashes` #### `wal --theme -l github` #### `wal --theme vscode` #### `wal --theme zenburn` ### seoul256

### solarized_dark

### solarized_light

### Tomorrow

### tomorrow_night

### wombat

================================================ FILE: doc/lualine.txt ================================================ *lualine.txt* fast and easy to configure statusline plugin for neovim ============================================================================== Table of Contents *lualine-table-of-contents* 1. lualine.nvim |lualine-lualine.nvim| - Contributing |lualine-contributing| - Performance compared to other plugins|lualine-performance-compared-to-other-plugins| - Installation |lualine-installation| - Usage and customization |lualine-usage-and-customization| ============================================================================== 1. lualine.nvim *lualine-lualine.nvim* A blazing fast and easy to configure Neovim statusline written in Lua. `lualine.nvim` requires Neovim >= 0.7. For previous versions of neovim please use compatability tags for example compat-nvim-0.5 CONTRIBUTING *lualine-contributing* Feel free to create an issue/PR if you want to see anything else implemented. If you have some question or need help with configuration, start a discussion . Please read CONTRIBUTING.md <./CONTRIBUTING.md> before opening a PR. You can also help with documentation in the wiki . PERFORMANCE COMPARED TO OTHER PLUGINS*lualine-performance-compared-to-other-plugins* Unlike other statusline plugins, lualine loads only the components you specify, and nothing else. Startup time performance measured with an amazing plugin dstein64/vim-startuptime Times are measured with a clean `init.vim` with only `vim-startuptime`, `vim-plug` and given statusline plugin installed. In control just `vim-startuptime` and`vim-plug` is installed. And measured time is complete startuptime of vim not time spent on specific plugin. These numbers are the average of 20 runs. │control│lualine│lightline│airline│ │17.2 ms│24.8 ms│ 25.5 ms │79.9 ms│ Last Updated On: 18-04-2022 INSTALLATION *lualine-installation* VIM-PLUG ~ > Plug 'nvim-lualine/lualine.nvim' " If you want to have icons in your statusline choose one of these Plug 'nvim-tree/nvim-web-devicons' < PACKER.NVIM ~ > use { 'nvim-lualine/lualine.nvim', requires = { 'nvim-tree/nvim-web-devicons', opt = true } } < LAZY.NVIM ~ > { 'nvim-lualine/lualine.nvim', dependencies = { 'nvim-tree/nvim-web-devicons' } } < You’ll also need to have a patched font if you want icons. USAGE AND CUSTOMIZATION *lualine-usage-and-customization* Lualine has sections as shown below. > +-------------------------------------------------+ | A | B | C X | Y | Z | +-------------------------------------------------+ < Each sections holds its components e.g. Vim’s current mode. CONFIGURING LUALINE IN INIT.VIM ~ All the examples below are in lua. You can use the same examples in `.vim` files by wrapping them in lua heredoc like this: > lua << END require('lualine').setup() END < For more information, check out `:help lua-heredoc`. *lualine-Default-configuration* > require('lualine').setup { options = { icons_enabled = true, theme = 'auto', component_separators = { left = '', right = ''}, section_separators = { left = '', right = ''}, disabled_filetypes = { statusline = {}, winbar = {}, }, ignore_focus = {}, always_divide_middle = true, always_show_tabline = true, globalstatus = false, refresh = { statusline = 1000, tabline = 1000, winbar = 1000, refresh_time = 16, -- ~60fps events = { 'WinEnter', 'BufEnter', 'BufWritePost', 'SessionLoadPost', 'FileChangedShellPost', 'VimResized', 'Filetype', 'CursorMoved', 'CursorMovedI', 'ModeChanged', }, } }, sections = { lualine_a = {'mode'}, lualine_b = {'branch', 'diff', 'diagnostics'}, lualine_c = {'filename'}, lualine_x = {'encoding', 'fileformat', 'filetype'}, lualine_y = {'progress'}, lualine_z = {'location'} }, inactive_sections = { lualine_a = {}, lualine_b = {}, lualine_c = {'filename'}, lualine_x = {'location'}, lualine_y = {}, lualine_z = {} }, tabline = {}, winbar = {}, inactive_winbar = {}, extensions = {} } < Default configuration If you want to get your current lualine config, you can do so with: > require('lualine').get_config() < ------------------------------------------------------------------------------ STARTING LUALINE ~ > require('lualine').setup() < ------------------------------------------------------------------------------ SETTING A THEME ~ > options = { theme = 'gruvbox' } < All available themes are listed in THEMES.md <./THEMES.md>. Please create a PR if you managed to port a popular theme before us, here is how to do it <./CONTRIBUTING.md>. *lualine-Customizing-themes* > local custom_gruvbox = require'lualine.themes.gruvbox' -- Change the background of lualine_c section for normal mode custom_gruvbox.normal.c.bg = '#112233' require('lualine').setup { options = { theme = custom_gruvbox }, ... } < Customizing themes Theme structure is available here . ------------------------------------------------------------------------------ SEPARATORS ~ lualine defines two kinds of separators: - `section_separators` - separators between sections - `component_separators` - separators between the different components in sections **Note**: if viewing this README in a browser, chances are the characters below will not be visible. > options = { section_separators = { left = '', right = '' }, component_separators = { left = '', right = '' } } < Here, left refers to the left-most sections (a, b, c), and right refers to the right-most sections (x, y, z). *lualine-Disabling-separators* > options = { section_separators = '', component_separators = '' } < ------------------------------------------------------------------------------ CHANGING COMPONENTS IN LUALINE SECTIONS ~ > sections = {lualine_a = {'mode'}} < *lualine-Available-components* - `branch` (git branch) - `buffers` (shows currently available buffers) - `diagnostics` (diagnostics count from your preferred source) - `diff` (git diff status) - `encoding` (file encoding) - `fileformat` (file format) - `filename` - `filesize` - `filetype` - `hostname` - `location` (location in file in line:column format) - `mode` (vim mode) - `progress` (%progress in file) - `searchcount` (number of search matches when hlsearch is active) - `selectioncount` (number of selected characters or lines) - `tabs` (shows currently available tabs) - `windows` (shows currently available windows) - `lsp_status` (shows active LSPs in the current buffer and a progress spinner) *lualine-Custom-components* LUA FUNCTIONS AS LUALINE COMPONENT > local function hello() return [[hello world]] end sections = { lualine_a = { hello } } < VIM FUNCTIONS AS LUALINE COMPONENT > sections = { lualine_a = {'FugitiveHead'} } < VIM’S STATUSLINE ITEMS AS LUALINE COMPONENT > sections = { lualine_c = {'%=', '%t%m', '%3p'} } < VIM VARIABLES AS LUALINE COMPONENT Variables from `g:`, `v:`, `t:`, `w:`, `b:`, `o:`, `to:`, `wo:`, `bo:` scopes can be used. See `:h lua-vim-variables` and `:h lua-vim-options` if you are not sure what to use. > sections = { lualine_a = { 'g:coc_status', 'bo:filetype' } } < LUA EXPRESSIONS AS LUALINE COMPONENT You can use any valid lua expression as a component including: - oneliners - global variables - require statements > sections = { lualine_c = { "os.date('%a')", 'data', "require'lsp-status'.status()" } } < `data` is a global variable in this example. ------------------------------------------------------------------------------ COMPONENT OPTIONS ~ Component options can change the way a component behave. There are two kinds of options: - global options affecting all components - local options affecting specific Global options can be used as local options (can be applied to specific components) but you cannot use local options as global. Global option used locally overwrites the global, for example: > require('lualine').setup { options = { fmt = string.lower }, sections = { lualine_a = { { 'mode', fmt = function(str) return str:sub(1,1) end } }, lualine_b = {'branch'} } } < `mode` will be formatted with the passed function so only first char will be shown. On the other hand branch will be formatted with global formatter `string.lower` so it will be showed in lower case. *lualine-Available-options* *lualine-Global-options* Global options These are `options` that are used in options table. They set behavior of lualine. Values set here are treated as default for other options that work in component level. For example even though `icons_enabled` is a general component option. You can set `icons_enabled` to `false` and icons will be disabled on all component. You can still overwrite defaults set in option table by specifying the option value in component. > options = { theme = 'auto', -- lualine theme component_separators = { left = '', right = '' }, section_separators = { left = '', right = '' }, disabled_filetypes = { -- Filetypes to disable lualine for. statusline = {}, -- only ignores the ft for statusline. winbar = {}, -- only ignores the ft for winbar. }, ignore_focus = {}, -- If current filetype is in this list it'll -- always be drawn as inactive statusline -- and the last window will be drawn as active statusline. -- for example if you don't want statusline of -- your file tree / sidebar window to have active -- statusline you can add their filetypes here. -- -- Can also be set to a function that takes the -- currently focused window as its only argument -- and returns a boolean representing whether the -- window's statusline should be drawn as inactive. always_divide_middle = true, -- When set to true, left sections i.e. 'a','b' and 'c' -- can't take over the entire statusline even -- if neither of 'x', 'y' or 'z' are present. always_show_tabline = true -- When set to true, if you have configured lualine for displaying tabline -- then tabline will always show. If set to false, then tabline will be displayed -- only when there are more than 1 tab. (see :h showtabline) globalstatus = false, -- enable global statusline (have a single statusline -- at bottom of neovim instead of one for every window). -- This feature is only available in neovim 0.7 and higher. refresh = { -- sets how often lualine should refresh it's contents (in ms) statusline = 100, -- The refresh option sets minimum time that lualine tries tabline = 100, -- to maintain between refresh. It's not guarantied if situation winbar = 100 -- arises that lualine needs to refresh itself before this time -- it'll do it. refresh_time = 16, -- ~60fps the time after which refresh queue is processed. Mininum refreshtime for lualine events = { -- The auto command events at which lualine refreshes 'WinEnter', 'BufEnter', 'BufWritePost', 'SessionLoadPost', 'FileChangedShellPost', 'VimResized', 'Filetype', 'CursorMoved', 'CursorMovedI', 'ModeChanged', }, -- Also you can force lualine's refresh by calling refresh function -- like require('lualine').refresh() } } < *lualine-General-component-options* General component options These are options that control behavior at component level and are available for all components. > sections = { lualine_a = { { 'mode', icons_enabled = true, -- Enables the display of icons alongside the component. -- Defines the icon to be displayed in front of the component. -- Can be string|table -- As table it must contain the icon as first entry and can use -- color option to custom color the icon. Example: -- {'branch', icon = ''} / {'branch', icon = {'', color={fg='green'}}} -- icon position can also be set to the right side from table. Example: -- {'branch', icon = {'', align='right', color={fg='green'}}} icon = nil, separator = nil, -- Determines what separator to use for the component. -- Note: -- When a string is provided it's treated as component_separator. -- When a table is provided it's treated as section_separator. -- Passing an empty string disables the separator. -- -- These options can be used to set colored separators -- around a component. -- -- The options need to be set as such: -- separator = { left = '', right = ''} -- -- Where left will be placed on left side of component, -- and right will be placed on its right. -- cond = nil, -- Condition function, the component is loaded when the function returns `true`. draw_empty = false, -- Whether to draw component even if it's empty. -- Might be useful if you want just the separator. -- Defines a custom color for the component: -- -- 'highlight_group_name' | { fg = '#rrggbb'|cterm_value(0-255)|'color_name(red)', bg= '#rrggbb', gui='style' } | function -- Note: -- '|' is synonymous with 'or', meaning a different acceptable format for that placeholder. -- color function has to return one of other color types ('highlight_group_name' | { fg = '#rrggbb'|cterm_value(0-255)|'color_name(red)', bg= '#rrggbb', gui='style' }) -- color functions can be used to have different colors based on state as shown below. -- -- Examples: -- color = { fg = '#ffaa88', bg = 'grey', gui='italic,bold' }, -- color = { fg = 204 } -- When fg/bg are omitted, they default to the your theme's fg/bg. -- color = 'WarningMsg' -- Highlight groups can also be used. -- color = function(section) -- return { fg = vim.bo.modified and '#aa3355' or '#33aa88' } -- end, color = nil, -- The default is your theme's color for that section and mode. -- Specify what type a component is, if omitted, lualine will guess it for you. -- -- Available types are: -- [format: type_name(example)], mod(branch/filename), -- stl(%f/%m), var(g:coc_status/bo:modifiable), -- lua_expr(lua expressions), vim_fun(viml function name) -- -- Note: -- lua_expr is short for lua-expression and vim_fun is short for vim-function. type = nil, padding = 1, -- Adds padding to the left and right of components. -- Padding can be specified to left or right independently, e.g.: -- padding = { left = left_padding, right = right_padding } fmt = nil, -- Format function, formats the component's output. -- This function receives two arguments: -- - string that is going to be displayed and -- that can be changed, enhanced and etc. -- - context object with information you might -- need. E.g. tabnr if used with tabs. on_click = nil, -- takes a function that is called when component is clicked with mouse. -- the function receives several arguments -- - number of clicks in case of multiple clicks -- - mouse button used (l(left)/r(right)/m(middle)/...) -- - modifiers pressed (s(shift)/c(ctrl)/a(alt)/m(meta)...) } } } < *lualine-Component-specific-options* Component specific options These are options that are available on specific components. For example, you have option on `diagnostics` component to specify what your diagnostic sources will be. *lualine-buffers-component-options* > sections = { lualine_a = { { 'buffers', show_filename_only = true, -- Shows shortened relative path when set to false. hide_filename_extension = false, -- Hide filename extension when set to true. show_modified_status = true, -- Shows indicator when the buffer is modified. mode = 0, -- 0: Shows buffer name -- 1: Shows buffer index -- 2: Shows buffer name + buffer index -- 3: Shows buffer number -- 4: Shows buffer name + buffer number max_length = vim.o.columns * 2 / 3, -- Maximum width of buffers component, -- it can also be a function that returns -- the value of `max_length` dynamically. filetype_names = { TelescopePrompt = 'Telescope', dashboard = 'Dashboard', packer = 'Packer', fzf = 'FZF', alpha = 'Alpha' }, -- Shows specific buffer name for that filetype ( { `filetype` = `buffer_name`, ... } ) -- Automatically updates active buffer color to match color of other components (will be overidden if buffers_color is set) use_mode_colors = false, buffers_color = { -- Same values as the general color option can be used here. active = 'lualine_{section}_normal', -- Color for active buffer. inactive = 'lualine_{section}_inactive', -- Color for inactive buffer. }, symbols = { modified = ' ●', -- Text to show when the buffer is modified alternate_file = '#', -- Text to show to identify the alternate file directory = '', -- Text to show when the buffer is a directory }, } } } < *lualine-datetime-component-options* > sections = { lualine_a = { { 'datetime', -- options: default, us, uk, iso, or your own format string ("%H:%M", etc..) style = 'default' } } } < *lualine-diagnostics-component-options* > sections = { lualine_a = { { 'diagnostics', -- Table of diagnostic sources, available sources are: -- 'nvim_lsp', 'nvim_diagnostic', 'nvim_workspace_diagnostic', 'coc', 'ale', 'vim_lsp'. -- or a function that returns a table as such: -- { error=error_cnt, warn=warn_cnt, info=info_cnt, hint=hint_cnt } sources = { 'nvim_diagnostic', 'coc' }, -- Displays diagnostics for the defined severity types sections = { 'error', 'warn', 'info', 'hint' }, diagnostics_color = { -- Same values as the general color option can be used here. error = 'DiagnosticError', -- Changes diagnostics' error color. warn = 'DiagnosticWarn', -- Changes diagnostics' warn color. info = 'DiagnosticInfo', -- Changes diagnostics' info color. hint = 'DiagnosticHint', -- Changes diagnostics' hint color. }, symbols = {error = 'E', warn = 'W', info = 'I', hint = 'H'}, colored = true, -- Displays diagnostics status in color if set to true. update_in_insert = false, -- Update diagnostics in insert mode. always_visible = false, -- Show diagnostics even if there are none. } } } < *lualine-diff-component-options* > sections = { lualine_a = { { 'diff', colored = true, -- Displays a colored diff status if set to true diff_color = { -- Same color values as the general color option can be used here. added = 'LuaLineDiffAdd', -- Changes the diff's added color modified = 'LuaLineDiffChange', -- Changes the diff's modified color removed = 'LuaLineDiffDelete', -- Changes the diff's removed color you }, symbols = {added = '+', modified = '~', removed = '-'}, -- Changes the symbols used by the diff. source = nil, -- A function that works as a data source for diff. -- It must return a table as such: -- { added = add_count, modified = modified_count, removed = removed_count } -- or nil on failure. count <= 0 won't be displayed. } } } < *lualine-fileformat-component-options* > sections = { lualine_a = { { 'fileformat', symbols = { unix = '', -- e712 dos = '', -- e70f mac = '', -- e711 } } } } < *lualine-filename-component-options* > sections = { lualine_a = { { 'filename', file_status = true, -- Displays file status (readonly status, modified status) newfile_status = false, -- Display new file status (new file means no write after created) path = 0, -- 0: Just the filename -- 1: Relative path -- 2: Absolute path -- 3: Absolute path, with tilde as the home directory -- 4: Filename and parent dir, with tilde as the home directory shorting_target = 40, -- Shortens path to leave 40 spaces in the window -- for other components. (terrible name, any suggestions?) -- It can also be a function that returns -- the value of `shorting_target` dynamically. symbols = { modified = '[+]', -- Text to show when the file is modified. readonly = '[-]', -- Text to show when the file is non-modifiable or readonly. unnamed = '[No Name]', -- Text to show for unnamed buffers. newfile = '[New]', -- Text to show for newly created file before first write } } } } < *lualine-filetype-component-options* > sections = { lualine_a = { { 'filetype', colored = true, -- Displays filetype icon in color if set to true icon_only = false, -- Display only an icon for filetype icon = { align = 'right' }, -- Display filetype icon on the right hand side -- icon = {'X', align='right'} -- Icon string ^ in table is ignored in filetype component } } } < *lualine-encoding-component-options* > sections = { lualine_a = { { 'encoding', -- Show '[BOM]' when the file has a byte-order mark show_bomb = false, } } } < *lualine-searchcount-component-options* > sections = { lualine_a = { { 'searchcount', maxcount = 999, timeout = 500, } } } < *lualine-tabs-component-options* > sections = { lualine_a = { { 'tabs', tab_max_length = 40, -- Maximum width of each tab. The content will be shorten dynamically (example: apple/orange -> a/orange) max_length = vim.o.columns / 3, -- Maximum width of tabs component. -- Note: -- It can also be a function that returns -- the value of `max_length` dynamically. mode = 0, -- 0: Shows tab_nr -- 1: Shows tab_name -- 2: Shows tab_nr + tab_name path = 0, -- 0: just shows the filename -- 1: shows the relative path and shorten $HOME to ~ -- 2: shows the full path -- 3: shows the full path and shorten $HOME to ~ -- Automatically updates active tab color to match color of other components (will be overidden if buffers_color is set) use_mode_colors = false, tabs_color = { -- Same values as the general color option can be used here. active = 'lualine_{section}_normal', -- Color for active tab. inactive = 'lualine_{section}_inactive', -- Color for inactive tab. }, show_modified_status = true, -- Shows a symbol next to the tab name if the file has been modified. symbols = { modified = '[+]', -- Text to show when the file is modified. }, fmt = function(name, context) -- Show + if buffer is modified in tab local buflist = vim.fn.tabpagebuflist(context.tabnr) local winnr = vim.fn.tabpagewinnr(context.tabnr) local bufnr = buflist[winnr] local mod = vim.fn.getbufvar(bufnr, '&mod') return name .. (mod == 1 and ' +' or '') end } } } < *lualine-windows-component-options* > sections = { lualine_a = { { 'windows', show_filename_only = true, -- Shows shortened relative path when set to false. show_modified_status = true, -- Shows indicator when the window is modified. mode = 0, -- 0: Shows window name -- 1: Shows window index -- 2: Shows window name + window index max_length = vim.o.columns * 2 / 3, -- Maximum width of windows component, -- it can also be a function that returns -- the value of `max_length` dynamically. filetype_names = { TelescopePrompt = 'Telescope', dashboard = 'Dashboard', packer = 'Packer', fzf = 'FZF', alpha = 'Alpha' }, -- Shows specific window name for that filetype ( { `filetype` = `window_name`, ... } ) disabled_buftypes = { 'quickfix', 'prompt' }, -- Hide a window if its buffer's type is disabled -- Automatically updates active window color to match color of other components (will be overidden if buffers_color is set) use_mode_colors = false, windows_color = { -- Same values as the general color option can be used here. active = 'lualine_{section}_normal', -- Color for active window. inactive = 'lualine_{section}_inactive', -- Color for inactive window. }, } } } < *lualine-lsp-status-component-options* > sections = { lualine_a = { { 'lsp_status', icon = '', -- f013 symbols = { -- Standard unicode symbols to cycle through for LSP progress: spinner = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' }, -- Standard unicode symbol for when LSP is done: done = '✓', -- Delimiter inserted between LSP names: separator = ' ', }, -- List of LSP names to ignore (e.g., `null-ls`): ignore_lsp = {}, -- Display the LSP name show_name = true, } } } < ------------------------------------------------------------------------------ TABLINE ~ You can use lualine to display components in tabline. The configuration for tabline sections is exactly the same as that of the statusline. > tabline = { lualine_a = {}, lualine_b = {'branch'}, lualine_c = {'filename'}, lualine_x = {}, lualine_y = {}, lualine_z = {} } < This will show the branch and filename components on top of neovim inside tabline. lualine also provides 2 components, buffers and tabs, that you can use to get a more traditional tabline/bufferline. > tabline = { lualine_a = {'buffers'}, lualine_b = {'branch'}, lualine_c = {'filename'}, lualine_x = {}, lualine_y = {}, lualine_z = {'tabs'} } < WINBAR ~ From neovim-0.8 you can customize your winbar with lualine. Winbar configuration is similar to statusline. > winbar = { lualine_a = {}, lualine_b = {}, lualine_c = {'filename'}, lualine_x = {}, lualine_y = {}, lualine_z = {} } inactive_winbar = { lualine_a = {}, lualine_b = {}, lualine_c = {'filename'}, lualine_x = {}, lualine_y = {}, lualine_z = {} } < Just like statusline you can separately specify winbar for active and inactive windows. Any lualine component can be placed in winbar. All kinds of custom components supported in statusline are also supported for winbar too. In general You can treat winbar as another lualine statusline that just appears on top of windows instead of at bottom. *lualine-Buffers* Buffers Shows currently open buffers. Like bufferline. See |lualine-buffers-options| for all builtin behaviors of buffers component. You can use `:LualineBuffersJump` to jump to buffer based on index of buffer in buffers component. Jumping to non-existent buffer indices generates an error. To avoid these errors `LualineBuffersJump` provides `` support, meaning that you can call `:LualineBufferJump!` to ignore these errors. > :LualineBuffersJump 2 " Jumps to 2nd buffer in buffers component. :LualineBuffersJump $ " Jumps to last buffer in buffers component. :LualineBuffersJump! 3 " Attempts to jump to 3rd buffer, if it exists. < *lualine-Tabs* Tabs Shows currently open tab. Like usual tabline. See |lualine-tabs-options| for all builtin behaviors of tabs component. You can also use `:LualineRenameTab` to set a name for a tabpage. For example: > :LualineRenameTab Project_K < It’s useful when you’re using rendering mode 2/3 in tabs. To unname a tabpage run `:LualineRenameTab` without argument. *lualine-Tabline-as-statusline* Tabline as statusline You can also completely move your statusline to a tabline by configuring `lualine.tabline` and disabling `lualine.sections` and `lualine.inactive_sections`: > tabline = { ...... }, sections = {}, inactive_sections = {}, < If you want a more sophisticated tabline you can use other tabline plugins with lualine too, for example: - nvim-bufferline - tabline.nvim tabline.nvim even uses lualine’s theme by default 🙌 You can find a bigger list here . ------------------------------------------------------------------------------ EXTENSIONS ~ lualine extensions change statusline appearance for a window/buffer with specified filetypes. By default no extensions are loaded to improve performance. You can load extensions with: > extensions = {'quickfix'} < *lualine-Available-extensions* - aerial - assistant - avante - chadtree - ctrlspace - fern - fugitive - fzf - lazy - man - mason - mundo - neo-tree - nerdtree - nvim-dap-ui - nvim-tree - oil - overseer - quickfix - symbols-outline - toggleterm - trouble *lualine-Custom-extensions* Custom extensions You can define your own extensions. If you believe an extension may be useful to others, then please submit a PR. > local my_extension = { sections = { lualine_a = {'mode'} }, filetypes = {'lua'} } require('lualine').setup { extensions = { my_extension } } < ------------------------------------------------------------------------------ REFRESHING LUALINE ~ By default lualine refreshes itself based on timer and some events. You can set the interval of the timer with refresh option. However you can also force lualine to refresh at any time by calling `lualine.refresh` function. > require('lualine').refresh({ scope = 'tabpage', -- scope of refresh all/tabpage/window place = { 'statusline', 'winbar', 'tabline' }, -- lualine segment ro refresh. }) < The arguments shown here are default values. So not passing any of them will be treated as if a default value was passed. So you can simply do > require('lualine').refresh() < Also, note by default when you call refresh a refresh event is queued in lualine. It desn’t refresh event immidiately. It’ll refresh on next refresh check pass. By default this time is set to 16ms to match 60fps. This duration can be configured with `options.refresh.refresh_time` option. If you want to bypass the refresh queue and want lualine to process the refresh immmidiately call refresh with `force=true` parameter set like this. > require('lualine').refresh({ force = true, -- do an immidiate refresh scope = 'tabpage', -- scope of refresh all/tabpage/window place = { 'statusline', 'winbar', 'tabline' }, -- lualine segment ro refresh. }) < Practically, speaking this is almost never needed. Also you should avoid calling `lualine.refresh` with `force` inside components. Since components are evaluated during refresh, calling refresh while refreshing can have undesirable effects. DISABLING LUALINE ~ You can disable lualine for specific filetypes: > options = { disabled_filetypes = {'lua'} } < You can also disable lualine completely. Note that you need to call this after the setup > require('lualine').hide({ place = {'statusline', 'tabline', 'winbar'}, -- The segment this change applies to. unhide = false, -- whether to re-enable lualine again/ }) < The arguments show for hide above are default values. Which means even if the hide function is called without arguments it’ll work as if these were passed. So in short to disable lualine completely you can do > require('lualine').hide() < To enable it again you can do > require('lualine').hide({unhide=true}) < WIKI ~ Check out the wiki for more info. You can find some useful configuration snippets here. You can also share your awesome snippets with others. If you want to extend lualine with plugins or want to know which ones already do, wiki/plugins is for you. Generated by panvimdoc vim:tw=78:ts=8:noet:ft=help:norl: ================================================ FILE: examples/bubbles.lua ================================================ -- Bubbles config for lualine -- Author: lokesh-krishna -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { blue = '#80a0ff', cyan = '#79dac8', black = '#080808', white = '#c6c6c6', red = '#ff5189', violet = '#d183e8', grey = '#303030', } local bubbles_theme = { normal = { a = { fg = colors.black, bg = colors.violet }, b = { fg = colors.white, bg = colors.grey }, c = { fg = colors.white }, }, insert = { a = { fg = colors.black, bg = colors.blue } }, visual = { a = { fg = colors.black, bg = colors.cyan } }, replace = { a = { fg = colors.black, bg = colors.red } }, inactive = { a = { fg = colors.white, bg = colors.black }, b = { fg = colors.white, bg = colors.black }, c = { fg = colors.white }, }, } require('lualine').setup { options = { theme = bubbles_theme, component_separators = '', section_separators = { left = '', right = '' }, }, sections = { lualine_a = { { 'mode', separator = { left = '' }, right_padding = 2 } }, lualine_b = { 'filename', 'branch' }, lualine_c = { '%=', --[[ add your center components here in place of this comment ]] }, lualine_x = {}, lualine_y = { 'filetype', 'progress' }, lualine_z = { { 'location', separator = { right = '' }, left_padding = 2 }, }, }, inactive_sections = { lualine_a = { 'filename' }, lualine_b = {}, lualine_c = {}, lualine_x = {}, lualine_y = {}, lualine_z = { 'location' }, }, tabline = {}, extensions = {}, } ================================================ FILE: examples/cosmicink.lua ================================================ -- CosmicInk config for lualine -- Author: Yeeloman -- MIT license, see LICENSE for more details. -- Main configuration for setting up lualine.nvim statusline plugin -- Default Theme Colors: Define a set of base colors for your theme local colors = { BG = '#16181b', -- Dark background FG = '#c5c4c4', -- Light foreground for contrast YELLOW = '#e8b75f', -- Vibrant yellow CYAN = '#00bcd4', -- Soft cyan DARKBLUE = '#2b3e50', -- Deep blue GREEN = '#00e676', -- Bright green ORANGE = '#ff7733', -- Warm orange VIOLET = '#7a3ba8', -- Strong violet MAGENTA = '#d360aa', -- Deep magenta BLUE = '#4f9cff', -- Light-medium blue RED = '#ff3344', -- Strong red } -- Function to get the color associated with the current mode in Vim local function get_mode_color() -- Define a table mapping modes to their associated colors local mode_color = { n = colors.DARKBLUE, i = colors.VIOLET, v = colors.RED, [''] = colors.BLUE, V = colors.RED, c = colors.MAGENTA, no = colors.RED, s = colors.ORANGE, S = colors.ORANGE, [''] = colors.ORANGE, ic = colors.YELLOW, R = colors.ORANGE, Rv = colors.ORANGE, cv = colors.RED, ce = colors.RED, r = colors.CYAN, rm = colors.CYAN, ['r?'] = colors.CYAN, ['!'] = colors.RED, t = colors.RED, } -- Return the opposite color, or fallback to foreground color return mode_color[vim.fn.mode()] end -- Function to get the opposite color of a given mode color local function get_opposite_color(mode_color) -- Define a table mapping colors to their opposite color local opposite_colors = { [colors.RED] = colors.CYAN, [colors.BLUE] = colors.ORANGE, [colors.GREEN] = colors.MAGENTA, [colors.MAGENTA] = colors.DARKBLUE, [colors.ORANGE] = colors.BLUE, [colors.CYAN] = colors.YELLOW, [colors.VIOLET] = colors.GREEN, [colors.YELLOW] = colors.RED, [colors.DARKBLUE] = colors.VIOLET, } -- Return the opposite color, or fallback to foreground color return opposite_colors[mode_color] or colors.FG end -- Function to get an animated color (randomly chosen from available colors) local function get_animated_color(mode_color) -- Define a list of all available colors local all_colors = { colors.RED, colors.BLUE, colors.GREEN, colors.MAGENTA, colors.ORANGE, colors.CYAN, colors.VIOLET, colors.YELLOW, colors.DARKBLUE, } -- Create a list of possible opposite colors (excluding the current mode color) local possible_opposites = {} for _, color in ipairs(all_colors) do if color ~= mode_color then table.insert(possible_opposites, color) end end -- Randomly select an opposite color if #possible_opposites > 0 then local random_index = math.random(1, #possible_opposites) return possible_opposites[random_index] else return colors.FG -- Default to foreground color if no opposite found end end -- Function to interpolate between two colors for a smooth transition local function interpolate_color(color1, color2, step) -- Blend two colors based on the given step factor (0.0 -> color1, 1.0 -> color2) local blend = function(c1, c2, stp) return math.floor(c1 + (c2 - c1) * stp) end -- Extract the RGB values of both colors (in hex) local r1, g1, b1 = tonumber(color1:sub(2, 3), 16), tonumber(color1:sub(4, 5), 16), tonumber(color1:sub(6, 7), 16) local r2, g2, b2 = tonumber(color2:sub(2, 3), 16), tonumber(color2:sub(4, 5), 16), tonumber(color2:sub(6, 7), 16) -- Calculate the new RGB values for the blended color local r = blend(r1, r2, step) local g = blend(g1, g2, step) local b = blend(b1, b2, step) -- Return the blended color in hex format return string.format('#%02X%02X%02X', r, g, b) end -- Function to get a middle color by interpolating between mode color and its opposite local function get_middle_color(color_step) -- Set default value for color_step if not provided color_step = color_step or 0.5 -- If color_step is nil, default to 0.5 local color1 = get_mode_color() -- Get the current mode color local color2 = get_opposite_color(color1) -- Get the opposite color -- Return an interpolated color between the two (based on the color_step value) return interpolate_color(color1, color2, color_step) end -- Condition: Check if the buffer is not empty -- This checks whether the current file's name is non-empty. -- If the file is open (i.e., has a name), it returns true, meaning the buffer is not empty. -- local function buffer_not_empty() -- return vim.fn.empty(vim.fn.expand('%:t')) ~= 1 -- 'expand('%:t')' gets the file name -- end -- Condition: Hide in width (only show the statusline when the window width is greater than 80) -- This ensures that the statusline will only appear if the current window width exceeds 80 characters. local function hide_in_width() return vim.fn.winwidth(0) > 80 -- 'winwidth(0)' returns the current window width end -- Condition: Check if the current workspace is inside a Git repository -- This function checks if the current file is inside a Git repository by looking for a `.git` directory -- in the current file's path. Returns true if the file is in a Git workspace. -- local function check_git_workspace() -- local filepath = vim.fn.expand('%:p:h') -- Get the current file's directory -- local gitdir = vim.fn.finddir('.git', filepath .. ';') -- Search for a `.git` directory in the file path -- return gitdir and #gitdir > 0 and #gitdir < #filepath -- Returns true if a `.git` directory is found -- end -- -- Set random seed based on current time for randomness math.randomseed(os.time()) -- Icon sets for random selection local icon_sets = { stars = { '★', '☆', '✧', '✦', '✶', '✷', '✸', '✹' }, -- Set of star-like icons runes = { '✠', '⛧', '𖤐', 'ᛟ', 'ᚨ', 'ᚱ', 'ᚷ', 'ᚠ', 'ᛉ', 'ᛊ', 'ᛏ', '☠', '☾', '♰', '✟', '☽', '⚚', '🜏', }, -- Set of rune-like symbols hearts = { '❤', '♥', '♡', '❦', '❧' }, -- Set of heart-shaped icons waves = { '≈', '∿', '≋', '≀', '⌀', '≣', '⌇' }, -- Set of wave-like symbols crosses = { '☨', '✟', '♰', '♱', '⛨', '' }, -- Set of cross-like symbols } -- Function to select a random icon from a given set local function get_random_icon(icons) return icons[math.random(#icons)] -- Returns a random icon from the set end -- Function to shuffle the elements in a table local function shuffle_table(tbl) local n = #tbl while n > 1 do local k = math.random(n) tbl[n], tbl[k] = tbl[k], tbl[n] -- Swap elements n = n - 1 -- Decrease the size of the unsorted portion end end -- Create a list of all icon sets to allow for random selection from any set local icon_sets_list = {} for _, icons in pairs(icon_sets) do table.insert(icon_sets_list, icons) -- Add each icon set to the list end shuffle_table(icon_sets_list) -- Shuffle the icon sets list -- Function to reverse the order of elements in a table local function reverse_table(tbl) local reversed = {} for i = #tbl, 1, -1 do table.insert(reversed, tbl[i]) -- Insert elements in reverse order end return reversed end -- Create a reversed list of icon sets local reversed_icon_sets = reverse_table(icon_sets_list) -- Function to create a separator component based on side (left/right) and optional mode color local function create_separator(side, use_mode_color) return { function() return side == 'left' and '' or '' -- Choose separator symbol based on side end, color = function() -- Set color based on mode or opposite color local color = use_mode_color and get_mode_color() or get_opposite_color(get_mode_color()) return { fg = color, } end, padding = { left = 0, }, } end -- Function to create a mode-based component (e.g., statusline) -- with optional content, icon, and colors local function create_mode_based_component(content, icon, color_fg, color_bg) return { content, icon = icon, color = function() local mode_color = get_mode_color() local opposite_color = get_opposite_color(mode_color) return { fg = color_fg or colors.FG, bg = color_bg or opposite_color, gui = 'bold', } end, } end -- -- Function to get the current mode indicator as a single character local function mode() -- Map of modes to their respective shorthand indicators local mode_map = { n = 'N', -- Normal mode i = 'I', -- Insert mode v = 'V', -- Visual mode [''] = 'V', -- Visual block mode V = 'V', -- Visual line mode c = 'C', -- Command-line mode no = 'N', -- NInsert mode s = 'S', -- Select mode S = 'S', -- Select line mode ic = 'I', -- Insert mode (completion) R = 'R', -- Replace mode Rv = 'R', -- Virtual Replace mode cv = 'C', -- Command-line mode ce = 'C', -- Ex mode r = 'R', -- Prompt mode rm = 'M', -- More mode ['r?'] = '?', -- Confirm mode ['!'] = '!', -- Shell mode t = 'T', -- Terminal mode } -- Return the mode shorthand or [UNKNOWN] if no match return mode_map[vim.fn.mode()] or '[UNKNOWN]' end -- Config local config = { options = { component_separators = '', section_separators = '', theme = { normal = { c = { fg = colors.FG, bg = colors.BG, }, }, inactive = { c = { fg = colors.FG, bg = colors.BG, }, }, -- Simplified inactive theme }, disabled_filetypes = { 'neo-tree', 'undotree', 'sagaoutline', 'diff', }, }, sections = { lualine_a = {}, lualine_b = {}, lualine_c = {}, lualine_x = {}, lualine_y = {}, lualine_z = {}, }, inactive_sections = { lualine_a = {}, lualine_b = {}, lualine_c = { { 'location', color = function() return { fg = colors.FG, gui = 'bold', } end, }, }, lualine_x = { { 'filename', color = function() return { fg = colors.FG, gui = 'bold,italic', } end, }, }, lualine_y = {}, lualine_z = {}, }, } -- Helper functions local function ins_left(component) table.insert(config.sections.lualine_c, component) end local function ins_right(component) table.insert(config.sections.lualine_x, component) end -- LEFT ins_left { mode, color = function() local mode_color = get_mode_color() return { fg = colors.BG, bg = mode_color, gui = 'bold', } end, padding = { left = 1, right = 1 }, } ins_left(create_separator('left', true)) ins_left { function() return vim.fn.fnamemodify(vim.fn.getcwd(), ':t') end, icon = ' ', color = function() local virtual_env = vim.env.VIRTUAL_ENV if virtual_env then return { fg = get_mode_color(), gui = 'bold,strikethrough', } else return { fg = get_mode_color(), gui = 'bold', } end end, } ins_left(create_separator('right')) ins_left(create_mode_based_component('filename', nil, colors.BG)) ins_left(create_separator('left')) ins_left { function() return '' end, color = function() return { fg = get_middle_color(), } end, cond = hide_in_width, } ins_left { function() local git_status = vim.b.gitsigns_status_dict if git_status then return string.format('+%d ~%d -%d', git_status.added or 0, git_status.changed or 0, git_status.removed or 0) end return '' end, -- icon = '󰊢 ', color = { fg = colors.YELLOW, gui = 'bold', }, cond = hide_in_width, } for _, icons in pairs(icon_sets_list) do ins_left { function() return get_random_icon(icons) end, color = function() return { fg = get_animated_color(), } end, cond = hide_in_width, } end ins_left { 'searchcount', color = { fg = colors.GREEN, gui = 'bold', }, } -- RIGHT ins_right { function() local reg = vim.fn.reg_recording() return reg ~= '' and '[' .. reg .. ']' or '' end, color = { fg = '#ff3344', gui = 'bold', }, cond = function() return vim.fn.reg_recording() ~= '' end, } ins_right { 'selectioncount', color = { fg = colors.GREEN, gui = 'bold', }, } for _, icons in ipairs(reversed_icon_sets) do ins_right { function() return get_random_icon(icons) end, color = function() return { fg = get_animated_color(), } end, cond = hide_in_width, } end ins_right { function() local msg = 'No Active Lsp' local buf_ft = vim.api.nvim_buf_get_option(0, 'filetype') local clients = vim.lsp.get_clients() if next(clients) == nil then return msg end local lsp_short_names = { pyright = 'py', tsserver = 'ts', rust_analyzer = 'rs', lua_ls = 'lua', clangd = 'c++', bashls = 'sh', jsonls = 'json', html = 'html', cssls = 'css', tailwindcss = 'tw', dockerls = 'docker', sqlls = 'sql', yamlls = 'yml', } for _, client in ipairs(clients) do local filetypes = client.config.filetypes if filetypes and vim.fn.index(filetypes, buf_ft) ~= -1 then return lsp_short_names[client.name] or client.name:sub(1, 2) end end return msg end, icon = ' ', color = { fg = colors.YELLOW, gui = 'bold', }, } ins_right { function() return '' end, color = function() return { fg = get_middle_color() } end, cond = hide_in_width, } ins_right(create_separator('right')) ins_right(create_mode_based_component('location', nil, colors.BG)) ins_right(create_separator('left')) ins_right { 'branch', icon = ' ', --[[ Truncates and formats Git branch names for display in lualine: First segment: Uppercase, truncated to 1 character. Middle segments: Lowercase, truncated to 1 character. Last segment: Unchanged. Separator: › between truncated segments and the last segment. Example Input/Output: Branch Name Output backend/setup/tailwind Bs›tailwind feature/add-ui Fa›add-ui main main ]] fmt = function(branch) if branch == '' or branch == nil then return 'No Repo' end -- Function to truncate a segment to a specified length local function truncate_segment(segment, max_length) if #segment > max_length then return segment:sub(1, max_length) end return segment end -- Split the branch name by '/' local segments = {} for segment in branch:gmatch('[^/]+') do table.insert(segments, segment) end -- Truncate all segments except the last one for i = 1, #segments - 1 do segments[i] = truncate_segment(segments[i], 1) -- Truncate to 1 character end -- If there's only one segment (no '/'), return it as-is if #segments == 1 then return segments[1] end -- Capitalize the first segment and lowercase the rest (except the last one) segments[1] = segments[1]:upper() -- First segment uppercase for i = 2, #segments - 1 do segments[i] = segments[i]:lower() -- Other segments lowercase end -- Combine the first segments with no separator and add '›' before the last segment local truncated_branch = table.concat(segments, '', 1, #segments - 1) .. '›' .. segments[#segments] -- Ensure the final result doesn't exceed a maximum length local max_total_length = 15 if #truncated_branch > max_total_length then truncated_branch = truncated_branch:sub(1, max_total_length) .. '…' end return truncated_branch end, color = function() local mode_color = get_mode_color() return { fg = mode_color, gui = 'bold', } end, } ins_right(create_separator('right')) ins_right(create_mode_based_component('progress', nil, colors.BG)) require('lualine').setup(config) ================================================ FILE: examples/evil_lualine.lua ================================================ -- Eviline config for lualine -- Author: shadmansaleh -- Credit: glepnir local lualine = require('lualine') -- Color table for highlights -- stylua: ignore local colors = { bg = '#202328', fg = '#bbc2cf', yellow = '#ECBE7B', cyan = '#008080', darkblue = '#081633', green = '#98be65', orange = '#FF8800', violet = '#a9a1e1', magenta = '#c678dd', blue = '#51afef', red = '#ec5f67', } local conditions = { buffer_not_empty = function() return vim.fn.empty(vim.fn.expand('%:t')) ~= 1 end, hide_in_width = function() return vim.fn.winwidth(0) > 80 end, check_git_workspace = function() local filepath = vim.fn.expand('%:p:h') local gitdir = vim.fn.finddir('.git', filepath .. ';') return gitdir and #gitdir > 0 and #gitdir < #filepath end, } -- Config local config = { options = { -- Disable sections and component separators component_separators = '', section_separators = '', theme = { -- We are going to use lualine_c an lualine_x as left and -- right section. Both are highlighted by c theme . So we -- are just setting default looks o statusline normal = { c = { fg = colors.fg, bg = colors.bg } }, inactive = { c = { fg = colors.fg, bg = colors.bg } }, }, }, sections = { -- these are to remove the defaults lualine_a = {}, lualine_b = {}, lualine_y = {}, lualine_z = {}, -- These will be filled later lualine_c = {}, lualine_x = {}, }, inactive_sections = { -- these are to remove the defaults lualine_a = {}, lualine_b = {}, lualine_y = {}, lualine_z = {}, lualine_c = {}, lualine_x = {}, }, } -- Inserts a component in lualine_c at left section local function ins_left(component) table.insert(config.sections.lualine_c, component) end -- Inserts a component in lualine_x at right section local function ins_right(component) table.insert(config.sections.lualine_x, component) end ins_left { function() return '▊' end, color = { fg = colors.blue }, -- Sets highlighting of component padding = { left = 0, right = 1 }, -- We don't need space before this } ins_left { -- mode component function() return '' end, color = function() -- auto change color according to neovims mode local mode_color = { n = colors.red, i = colors.green, v = colors.blue, [''] = colors.blue, V = colors.blue, c = colors.magenta, no = colors.red, s = colors.orange, S = colors.orange, [''] = colors.orange, ic = colors.yellow, R = colors.violet, Rv = colors.violet, cv = colors.red, ce = colors.red, r = colors.cyan, rm = colors.cyan, ['r?'] = colors.cyan, ['!'] = colors.red, t = colors.red, } return { fg = mode_color[vim.fn.mode()] } end, padding = { right = 1 }, } ins_left { -- filesize component 'filesize', cond = conditions.buffer_not_empty, } ins_left { 'filename', cond = conditions.buffer_not_empty, color = { fg = colors.magenta, gui = 'bold' }, } ins_left { 'location' } ins_left { 'progress', color = { fg = colors.fg, gui = 'bold' } } ins_left { 'diagnostics', sources = { 'nvim_diagnostic' }, symbols = { error = ' ', warn = ' ', info = ' ' }, diagnostics_color = { error = { fg = colors.red }, warn = { fg = colors.yellow }, info = { fg = colors.cyan }, }, } -- Insert mid section. You can make any number of sections in neovim :) -- for lualine it's any number greater then 2 ins_left { function() return '%=' end, } ins_left { -- Lsp server name . function() local msg = 'No Active Lsp' local buf_ft = vim.api.nvim_get_option_value('filetype', { buf = 0 }) local clients = vim.lsp.get_clients() if next(clients) == nil then return msg end for _, client in ipairs(clients) do local filetypes = client.config.filetypes if filetypes and vim.fn.index(filetypes, buf_ft) ~= -1 then return client.name end end return msg end, icon = ' LSP:', color = { fg = '#ffffff', gui = 'bold' }, } -- Add components to right sections ins_right { 'o:encoding', -- option component same as &encoding in viml fmt = string.upper, -- I'm not sure why it's upper case either ;) cond = conditions.hide_in_width, color = { fg = colors.green, gui = 'bold' }, } ins_right { 'fileformat', fmt = string.upper, icons_enabled = false, -- I think icons are cool but Eviline doesn't have them. sigh color = { fg = colors.green, gui = 'bold' }, } ins_right { 'branch', icon = '', color = { fg = colors.violet, gui = 'bold' }, } ins_right { 'diff', -- Is it me or the symbol for modified us really weird symbols = { added = ' ', modified = '󰝤 ', removed = ' ' }, diff_color = { added = { fg = colors.green }, modified = { fg = colors.orange }, removed = { fg = colors.red }, }, cond = conditions.hide_in_width, } ins_right { function() return '▊' end, color = { fg = colors.blue }, padding = { left = 1 }, } -- Now don't forget to initialize lualine lualine.setup(config) ================================================ FILE: examples/slanted-gaps.lua ================================================ local colors = { red = '#ca1243', grey = '#a0a1a7', black = '#383a42', white = '#f3f3f3', light_green = '#83a598', orange = '#fe8019', green = '#8ec07c', } local theme = { normal = { a = { fg = colors.white, bg = colors.black }, b = { fg = colors.white, bg = colors.grey }, c = { fg = colors.black, bg = colors.white }, z = { fg = colors.white, bg = colors.black }, }, insert = { a = { fg = colors.black, bg = colors.light_green } }, visual = { a = { fg = colors.black, bg = colors.orange } }, replace = { a = { fg = colors.black, bg = colors.green } }, } local empty = require('lualine.component'):extend() function empty:draw(default_highlight) self.status = '' self.applied_separator = '' self:apply_highlights(default_highlight) self:apply_section_separators() return self.status end -- Put proper separators and gaps between components in sections local function process_sections(sections) for name, section in pairs(sections) do local left = name:sub(9, 10) < 'x' for pos = 1, name ~= 'lualine_z' and #section or #section - 1 do table.insert(section, pos * 2, { empty, color = { fg = colors.white, bg = colors.white } }) end for id, comp in ipairs(section) do if type(comp) ~= 'table' then comp = { comp } section[id] = comp end comp.separator = left and { right = '' } or { left = '' } end end return sections end local function search_result() if vim.v.hlsearch == 0 then return '' end local last_search = vim.fn.getreg('/') if not last_search or last_search == '' then return '' end local searchcount = vim.fn.searchcount { maxcount = 9999 } return last_search .. '(' .. searchcount.current .. '/' .. searchcount.total .. ')' end local function modified() if vim.bo.modified then return '+' elseif vim.bo.modifiable == false or vim.bo.readonly == true then return '-' end return '' end require('lualine').setup { options = { theme = theme, component_separators = '', section_separators = { left = '', right = '' }, }, sections = process_sections { lualine_a = { 'mode' }, lualine_b = { 'branch', 'diff', { 'diagnostics', source = { 'nvim' }, sections = { 'error' }, diagnostics_color = { error = { bg = colors.red, fg = colors.white } }, }, { 'diagnostics', source = { 'nvim' }, sections = { 'warn' }, diagnostics_color = { warn = { bg = colors.orange, fg = colors.white } }, }, { 'filename', file_status = false, path = 1 }, { modified, color = { bg = colors.red } }, { '%w', cond = function() return vim.wo.previewwindow end, }, { '%r', cond = function() return vim.bo.readonly end, }, { '%q', cond = function() return vim.bo.buftype == 'quickfix' end, }, }, lualine_c = {}, lualine_x = {}, lualine_y = { search_result, 'filetype' }, lualine_z = { '%l:%c', '%p%%/%L' }, }, inactive_sections = { lualine_c = { '%f %y %m' }, lualine_x = {}, }, } ================================================ FILE: lua/lualine/component.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local lualine_require = require('lualine_require') local require = lualine_require.require local M = require('lualine.utils.class'):extend() local modules = lualine_require.lazy_require { highlight = 'lualine.highlight', utils_notices = 'lualine.utils.notices', fn_store = 'lualine.utils.fn_store', } -- Used to provide a unique id for each component local component_no = 1 function M._reset_components() component_no = 1 end -- variable to store component output for manipulation M.status = '' function M:__tostring() local str = 'Component: ' .. self.options.component_name if self.debug then str = str .. '\n---------------------\n' .. vim.inspect(self) end return str end M.__is_lualine_component = true ---initialize new component ---@param options table options for component function M:init(options) self.options = options or {} component_no = component_no + 1 if not self.options.component_name then self.options.component_name = tostring(component_no) end self.component_no = component_no self:set_separator() self:create_option_highlights() self:set_on_click() end ---sets the default separator for component based on whether the component ---is in left sections or right sections when separator option is omitted. function M:set_separator() if self.options.separator == nil then if self.options.component_separators then if self.options.self.section < 'x' then self.options.separator = self.options.component_separators.left else self.options.separator = self.options.component_separators.right end end end end ---creates hl group from color option function M:create_option_highlights() -- set custom highlights if self.options.color then self.options.color_highlight = self:create_hl(self.options.color) end -- setup icon highlight if type(self.options.icon) == 'table' and self.options.icon.color then self.options.icon_color_highlight = self:create_hl(self.options.icon.color) end end ---Setup on click function so they can be added during drawing. function M:set_on_click() if self.options.on_click ~= nil then if vim.fn.has('nvim-0.8') == 0 then modules.utils_notices.add_notice( '### Options.on_click\nSorry `on_click` can only be used in neovim 0.8 or higher.\n' ) self.options.on_click = nil return end self.on_click_id = modules.fn_store.register_fn(self.component_no, self.options.on_click) end end ---adds spaces to left and right of a component function M:apply_padding() local padding = self.options.padding local l_padding, r_padding if padding == nil then padding = 1 end if type(padding) == 'number' then l_padding, r_padding = padding, padding elseif type(padding) == 'table' then l_padding, r_padding = padding.left, padding.right end if l_padding then if self.status:find('%%#.*#') == 1 then -- When component has changed the highlight at beginning -- we will add the padding after the highlight local pre_highlight = vim.fn.matchlist(self.status, [[\(%#.\{-\}#\)]])[2] self.status = pre_highlight .. string.rep(' ', l_padding) .. self.status:sub(#pre_highlight + 1, #self.status) else self.status = string.rep(' ', l_padding) .. self.status end end if r_padding then if self.status:reverse():find('%*%%.*#.*#%%') == 1 then -- When component has changed the highlight at the end -- we will add the padding before the highlight terminates self.status = self.status:sub(1, -3) .. string.rep(' ', r_padding) .. self.status:sub(-2, -1) else self.status = self.status .. string.rep(' ', r_padding) end end end ---applies custom highlights for component function M:apply_highlights(default_highlight) if self.options.color_highlight then local hl_fmt hl_fmt, M.color_fn_cache = self:format_hl(self.options.color_highlight) self.status = hl_fmt .. self.status end if type(self.options.separator) ~= 'table' and self.status:find('%%#') then -- Apply default highlight only when we aren't applying trans sep and -- the component has changed it's hl. Since we won't be applying -- regular sep in those cases so ending with default hl isn't necessary self.status = self.status .. default_highlight -- Also put it in applied sep so when sep get striped so does the hl self.applied_separator = default_highlight end -- Prepend default hl when the component doesn't start with hl otherwise -- color in previous component can cause side effect if not self.status:find('^%%#') then self.status = default_highlight .. self.status end end ---apply icon to component (appends/prepends component with icon) function M:apply_icon() local icon = self.options.icon if self.options.icons_enabled and icon then if type(icon) == 'table' then icon = icon[1] end if self.options.icon_color_highlight and type(self.options.icon) == 'table' and self.options.icon.align == 'right' then self.status = table.concat { self.status, ' ', self:format_hl(self.options.icon_color_highlight), icon, self:get_default_hl(), } elseif self.options.icon_color_highlight then self.status = table.concat { self:format_hl(self.options.icon_color_highlight), icon, self:get_default_hl(), ' ', self.status, } elseif type(self.options.icon) == 'table' and self.options.icon.align == 'right' then self.status = table.concat({ self.status, icon }, ' ') else self.status = table.concat({ icon, self.status }, ' ') end end end ---apply separator at end of component only when ---custom highlights haven't affected background function M:apply_separator() local separator = self.options.separator if type(separator) == 'table' then if self.options.separator[2] == '' then if self.options.self.section < 'x' then separator = self.options.component_separators.left else separator = self.options.component_separators.right end else return end end if separator and #separator > 0 then self.status = self.status .. separator self.applied_separator = self.applied_separator .. separator end end ---apply transitional separator for the component function M:apply_section_separators() if type(self.options.separator) ~= 'table' then return end if self.options.separator.left ~= nil and self.options.separator.left ~= '' then self.status = string.format('%%z{%s}%s', self.options.separator.left, self.status) self.strip_previous_separator = true end if self.options.separator.right ~= nil and self.options.separator.right ~= '' then self.status = string.format('%s%%Z{%s}', self.status, self.options.separator.right) end end ---Add on click function description to already drawn item function M:apply_on_click() if self.on_click_id then self.status = self:format_fn(self.on_click_id, self.status) end end ---remove separator from tail of this component. ---called by lualine.utils.sections.draw_section to manage unnecessary separators function M:strip_separator() if not self.applied_separator then self.applied_separator = '' end self.status = self.status:sub(1, (#self.status - #self.applied_separator)) self.applied_separator = nil return self.status end function M:get_default_hl() if self.options.color_highlight then return self:format_hl(self.options.color_highlight) elseif self.default_hl then return self.default_hl else return modules.highlight.format_highlight(self.options.self.section) end end ---create a lualine highlight for color ---@param color table|string|function defined color for hl ---@param hint string|nil hint for hl name ---@return table an identifier to later retrieve the hl for application function M:create_hl(color, hint) hint = hint and self.options.component_name .. '_' .. hint or self.options.component_name return modules.highlight.create_component_highlight_group(color, hint, self.options, false) end ---Get stl formatted hl group for hl_token ---@param hl_token table identifier received from create_hl or create_component_highlight_group ---@return string stl formatted hl group for hl_token function M:format_hl(hl_token) return modules.highlight.component_format_highlight(hl_token) end ---Wrap str with click format for function of id ---@param id number ---@param str string ---@return string function M:format_fn(id, str) return string.format("%%%d@v:lua.require'lualine.utils.fn_store'.call_fn@%s%%T", id, str) end -- luacheck: push no unused args ---actual function that updates a component. Must be overwritten with component functionality function M:update_status(is_focused) end -- luacheck: pop ---driver code of the class ---@param default_highlight string default hl group of section where component resides ---@param is_focused boolean|number whether drawing for active or inactive statusline. ---@return string stl formatted rendering string for component function M:draw(default_highlight, is_focused) self.status = '' self.applied_separator = '' if self.options.cond ~= nil and self.options.cond() ~= true then return self.status end self.default_hl = default_highlight local status = self:update_status(is_focused) if self.options.fmt then status = self.options.fmt(status or '', self) end if type(status) == 'string' and (#status > 0 or self.options.draw_empty) then self.status = status if #status > 0 then self:apply_icon() self:apply_padding() end self:apply_on_click() self:apply_highlights(default_highlight) self:apply_section_separators() self:apply_separator() end return self.status end return M ================================================ FILE: lua/lualine/components/branch/git_branch.lua ================================================ local M = {} local require = require('lualine_require').require local utils = require('lualine.utils.utils') -- vars local current_git_branch = '' local current_git_dir = '' local branch_cache = {} -- stores last known branch for a buffer local active_bufnr = '0' -- os specific path separator local sep = package.config:sub(1, 1) -- event watcher to watch head file -- Use file watch for non Windows and poll for Windows. -- Windows doesn't like file watch for some reason. local file_changed = sep ~= '\\' and vim.loop.new_fs_event() or vim.loop.new_fs_poll() local git_dir_cache = {} -- Stores git paths that we already know of ---sets git_branch variable to branch name or commit hash if not on branch ---@param head_file string full path of .git/HEAD file local function get_git_head(head_file) local f_head = io.open(head_file) if f_head then local HEAD = f_head:read() f_head:close() local branch = HEAD:match('ref: refs/heads/(.+)$') if branch then current_git_branch = branch else current_git_branch = HEAD:sub(1, 6) end end return nil end ---updates the current value of git_branch and sets up file watch on HEAD file local function update_branch() active_bufnr = tostring(vim.api.nvim_get_current_buf()) file_changed:stop() local git_dir = current_git_dir if git_dir and #git_dir > 0 then local head_file = git_dir .. sep .. 'HEAD' get_git_head(head_file) file_changed:start( head_file, sep ~= '\\' and {} or 1000, vim.schedule_wrap(function() -- reset file-watch update_branch() end) ) else -- set to '' when git dir was not found current_git_branch = '' end branch_cache[vim.api.nvim_get_current_buf()] = current_git_branch end ---updates the current value of current_git_branch and sets up file watch on HEAD file if value changed local function update_current_git_dir(git_dir) if current_git_dir ~= git_dir then current_git_dir = git_dir update_branch() end end ---returns full path to git directory for dir_path or current directory ---@param dir_path string|nil ---@return string|nil function M.find_git_dir(dir_path) local git_dir = vim.env.GIT_DIR if git_dir then update_current_git_dir(git_dir) return git_dir end -- get file dir so we can search from that dir local file_dir = dir_path or vim.fn.expand('%:p:h') if package.loaded.oil then local oil = require('oil') local ok, dir = pcall(oil.get_current_dir) if ok and dir and dir ~= '' then file_dir = vim.fn.fnamemodify(dir, ':p:h') end end -- extract correct file dir from terminals if file_dir and file_dir:match('term://.*') then file_dir = vim.fn.expand(file_dir:gsub('term://(.+)//.+', '%1')) end local root_dir = file_dir -- Search upward for .git file or folder while root_dir do if git_dir_cache[root_dir] then git_dir = git_dir_cache[root_dir] break end local git_path = root_dir .. sep .. '.git' local git_file_stat = vim.loop.fs_stat(git_path) if git_file_stat then if git_file_stat.type == 'directory' then git_dir = git_path elseif git_file_stat.type == 'file' then -- separate git-dir or submodule is used local file = io.open(git_path) if file then git_dir = file:read() git_dir = git_dir and git_dir:match('gitdir: (.+)$') file:close() end -- submodule / relative file path if git_dir and git_dir:sub(1, 1) ~= sep and not git_dir:match('^%a:.*$') then git_dir = git_path:match('(.*).git') .. git_dir end end if git_dir then local head_file_stat = vim.loop.fs_stat(git_dir .. sep .. 'HEAD') if head_file_stat and head_file_stat.type == 'file' then break else git_dir = nil end end end root_dir = root_dir:match('(.*)' .. sep .. '.-') end git_dir_cache[file_dir] = git_dir if dir_path == nil then update_current_git_dir(git_dir) end return git_dir end ---initializes git_branch module function M.init() -- run watch head on load so branch is present when component is loaded M.find_git_dir() -- update branch state of BufEnter as different Buffer may be on different repos utils.define_autocmd('BufEnter', "lua require'lualine.components.branch.git_branch'.find_git_dir()") end function M.get_branch(bufnr) if vim.g.actual_curbuf ~= nil and active_bufnr ~= vim.g.actual_curbuf then -- Workaround for https://github.com/nvim-lualine/lualine.nvim/issues/286 -- See upstream issue https://github.com/neovim/neovim/issues/15300 -- Diff is out of sync re sync it. M.find_git_dir() end if bufnr then return branch_cache[bufnr] or '' else branch_cache[vim.api.nvim_get_current_buf()] = current_git_branch end return current_git_branch end return M ================================================ FILE: lua/lualine/components/branch/init.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local M = require('lualine.component'):extend() local modules = require('lualine_require').lazy_require { git_branch = 'lualine.components.branch.git_branch', highlight = 'lualine.highlight', utils = 'lualine.utils.utils', } -- Initializer M.init = function(self, options) M.super.init(self, options) if not self.options.icon then self.options.icon = '' -- e0a0 end modules.git_branch.init() end M.update_status = function(_, is_focused) local buf = (not is_focused and vim.api.nvim_get_current_buf()) local branch = modules.git_branch.get_branch(buf) return modules.utils.stl_escape(branch) end return M ================================================ FILE: lua/lualine/components/buffers/buffer.lua ================================================ local Buffer = require('lualine.utils.class'):extend() local modules = require('lualine_require').lazy_require { highlight = 'lualine.highlight', utils = 'lualine.utils.utils', } ---initialize a new buffer from opts ---@param opts table function Buffer:init(opts) assert(opts.bufnr, 'Cannot create Buffer without bufnr') self.bufnr = opts.bufnr self.buf_index = opts.buf_index self.options = opts.options self.highlights = opts.highlights self:get_props() end function Buffer:is_current() return vim.api.nvim_get_current_buf() == self.bufnr end function Buffer:is_alternate() return vim.fn.bufnr('#') == self.bufnr and not self:is_current() end ---setup icons, modified status for buffer function Buffer:get_props() self.file = modules.utils.stl_escape(vim.api.nvim_buf_get_name(self.bufnr)) self.buftype = vim.api.nvim_buf_get_option(self.bufnr, 'buftype') self.filetype = vim.api.nvim_buf_get_option(self.bufnr, 'filetype') local modified = self.options.show_modified_status and vim.api.nvim_buf_get_option(self.bufnr, 'modified') self.modified_icon = modified and self.options.symbols.modified or '' self.alternate_file_icon = self:is_alternate() and self.options.symbols.alternate_file or '' self.icon = '' if self.options.icons_enabled then local dev local status, _ = pcall(require, 'nvim-web-devicons') if not status then dev, _ = '', '' elseif self.filetype == 'TelescopePrompt' then dev, _ = require('nvim-web-devicons').get_icon('telescope') elseif self.filetype == 'fugitive' then dev, _ = require('nvim-web-devicons').get_icon('git') elseif self.filetype == 'vimwiki' then dev, _ = require('nvim-web-devicons').get_icon('markdown') elseif self.buftype == 'terminal' then dev, _ = require('nvim-web-devicons').get_icon('zsh') elseif vim.fn.isdirectory(self.file) == 1 then dev, _ = self.options.symbols.directory, nil else dev, _ = require('nvim-web-devicons').get_icon(self.file, vim.fn.expand('#' .. self.bufnr .. ':e')) end if dev then self.icon = dev .. ' ' end end end ---returns line configured for handling mouse click ---@param name string ---@return string function Buffer:configure_mouse_click(name) return string.format('%%%s@LualineSwitchBuffer@%s%%T', self.bufnr, name) end ---returns rendered buffer ---@return string function Buffer:render() local name = self:name() if self.options.fmt then name = self.options.fmt(name or '', self) end if self.ellipse then -- show ellipsis name = '...' else name = self:apply_mode(name) end name = Buffer.apply_padding(name, self.options.padding) self.len = vim.fn.strchars(name) -- setup for mouse clicks local line = self:configure_mouse_click(name) -- apply highlight line = modules.highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) .. line -- apply separators if self.options.self.section < 'x' and not self.first then local sep_before = self:separator_before() line = sep_before .. line self.len = self.len + vim.fn.strchars(sep_before) elseif self.options.self.section >= 'x' and not self.last then local sep_after = self:separator_after() line = line .. sep_after self.len = self.len + vim.fn.strchars(sep_after) end return line end ---apply separator before current buffer ---@return string function Buffer:separator_before() if self.current or self.aftercurrent then return '%Z{' .. self.options.section_separators.left .. '}' else return self.options.component_separators.left end end ---apply separator after current buffer ---@return string function Buffer:separator_after() if self.current or self.beforecurrent then return '%z{' .. self.options.section_separators.right .. '}' else return self.options.component_separators.right end end ---returns name of current buffer after filtering special buffers ---@return string function Buffer:name() if self.options.filetype_names[self.filetype] then return self.options.filetype_names[self.filetype] elseif self.buftype == 'help' then return 'help:' .. vim.fn.fnamemodify(self.file, ':t:r') elseif self.buftype == 'terminal' then local match = string.match(vim.split(self.file, ' ')[1], 'term:.*:(%a+)') return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t') elseif self.buftype == 'quickfix' then local is_loclist = 0 ~= vim.fn.getloclist(0, { filewinid = 1 }).filewinid return is_loclist and 'Location list' or 'Quickfix List' elseif vim.fn.isdirectory(self.file) == 1 then return vim.fn.fnamemodify(self.file, ':p:.') elseif self.file == '' then return '[No Name]' end local name if self.options.show_filename_only then name = vim.fn.fnamemodify(self.file, ':t') else name = vim.fn.pathshorten(vim.fn.fnamemodify(self.file, ':p:.')) end if self.options.hide_filename_extension then name = vim.fn.fnamemodify(name, ':r') end return name end ---adds spaces to left and right function Buffer.apply_padding(str, padding) local l_padding, r_padding = 1, 1 if type(padding) == 'number' then l_padding, r_padding = padding, padding elseif type(padding) == 'table' then l_padding, r_padding = padding.left or 0, padding.right or 0 end return string.rep(' ', l_padding) .. str .. string.rep(' ', r_padding) end function Buffer:apply_mode(name) if self.options.mode == 0 then return string.format('%s%s%s%s', self.alternate_file_icon, self.icon, name, self.modified_icon) end if self.options.mode == 1 then return string.format('%s%s %s%s', self.alternate_file_icon, self.buf_index or '', self.icon, self.modified_icon) end if self.options.mode == 2 then return string.format( '%s%s %s%s%s', self.alternate_file_icon, self.buf_index or '', self.icon, name, self.modified_icon ) end if self.options.mode == 3 then return string.format('%s%s %s%s', self.alternate_file_icon, self.bufnr or '', self.icon, self.modified_icon) end -- if self.options.mode == 4 then return string.format('%s%s %s%s%s', self.alternate_file_icon, self.bufnr or '', self.icon, name, self.modified_icon) end return Buffer ================================================ FILE: lua/lualine/components/buffers/init.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local require = require('lualine_require').require local Buffer = require('lualine.components.buffers.buffer') local M = require('lualine.component'):extend() local highlight = require('lualine.highlight') local default_options = { show_filename_only = true, hide_filename_extension = false, show_modified_status = true, mode = 0, max_length = 0, filetype_names = { TelescopePrompt = 'Telescope', dashboard = 'Dashboard', packer = 'Packer', fzf = 'FZF', alpha = 'Alpha', }, use_mode_colors = false, buffers_color = { active = nil, inactive = nil, }, symbols = { modified = ' ●', alternate_file = '#', directory = '', }, } -- This function is duplicated in tabs ---returns the proper hl for buffer in section. Used for setting default highlights ---@param section string name of section buffers component is in ---@param is_active boolean ---@return string hl name local function get_hl(section, is_active) local suffix = is_active and highlight.get_mode_suffix() or '_inactive' local section_redirects = { lualine_x = 'lualine_c', lualine_y = 'lualine_b', lualine_z = 'lualine_a', } if section_redirects[section] then section = highlight.highlight_exists(section .. suffix) and section or section_redirects[section] end return section .. suffix end function M:init(options) M.super.init(self, options) -- if use_mode_colors is set, use a function so that the colors update local default_active = options.use_mode_colors and function() return get_hl('lualine_' .. options.self.section, true) end or get_hl('lualine_' .. options.self.section, true) default_options.buffers_color = { active = default_active, inactive = get_hl('lualine_' .. options.self.section, false), } self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) if self.options.component_name == 'buffers' then self.highlights = { active = self:create_hl(self.options.buffers_color.active, 'active'), inactive = self:create_hl(self.options.buffers_color.inactive, 'inactive'), } end end function M:new_buffer(bufnr, buf_index) bufnr = bufnr or vim.api.nvim_get_current_buf() buf_index = buf_index or '' return Buffer:new { bufnr = bufnr, buf_index = buf_index, options = self.options, highlights = self.highlights, } end function M:buffers() local buffers = {} M.bufpos2nr = {} for b = 1, vim.fn.bufnr('$') do if vim.fn.buflisted(b) ~= 0 and vim.api.nvim_buf_get_option(b, 'buftype') ~= 'quickfix' then buffers[#buffers + 1] = self:new_buffer(b, #buffers + 1) M.bufpos2nr[#buffers] = b end end return buffers end function M:update_status() local data = {} local buffers = self:buffers() local current = -2 -- mark the first, last, current, before current, after current buffers -- for rendering if buffers[1] then buffers[1].first = true end if buffers[#buffers] then buffers[#buffers].last = true end for i, buffer in ipairs(buffers) do if buffer:is_current() then buffer.current = true current = i end end if buffers[current - 1] then buffers[current - 1].beforecurrent = true end if buffers[current + 1] then buffers[current + 1].aftercurrent = true end local max_length = self.options.max_length if type(max_length) == 'function' then max_length = max_length(self) end if max_length == 0 then max_length = math.floor(2 * vim.o.columns / 3) end local total_length for i, buffer in pairs(buffers) do if buffer.current then current = i end end -- start drawing from current buffer and draw left and right of it until -- all buffers are drawn or max_length has been reached. if current == -2 then local b = self:new_buffer() b.current = true if self.options.self.section < 'x' then b.last = true if #buffers > 0 then buffers[#buffers].last = nil end buffers[#buffers + 1] = b current = #buffers else b.first = true if #buffers > 0 then buffers[1].first = nil end table.insert(buffers, 1, b) current = 1 end end local current_buffer = buffers[current] data[#data + 1] = current_buffer:render() total_length = current_buffer.len local i = 0 local before, after while true do i = i + 1 before = buffers[current - i] after = buffers[current + i] local rendered_before, rendered_after if before == nil and after == nil then break end -- draw left most undrawn buffer if fits in max_length if before then rendered_before = before:render() total_length = total_length + before.len if total_length > max_length then break end table.insert(data, 1, rendered_before) end -- draw right most undrawn buffer if fits in max_length if after then rendered_after = after:render() total_length = total_length + after.len if total_length > max_length then break end data[#data + 1] = rendered_after end end -- draw ellipsis (...) on relevant sides if all buffers don't fit in max_length if total_length > max_length then if before ~= nil then before.ellipse = true before.first = true table.insert(data, 1, before:render()) end if after ~= nil then after.ellipse = true after.last = true data[#data + 1] = after:render() end end return table.concat(data) end function M:draw() self.status = '' self.applied_separator = '' if self.options.cond ~= nil and self.options.cond() ~= true then return self.status end local status = self:update_status() if type(status) == 'string' and #status > 0 then self.status = status self:apply_section_separators() self:apply_separator() end return self.status end function M.buffer_jump(buf_pos, bang) if buf_pos == '$' then buf_pos = #M.bufpos2nr else buf_pos = tonumber(buf_pos) end if buf_pos < 1 or buf_pos > #M.bufpos2nr then if bang ~= '!' then error('Error: Unable to jump buffer position out of range') else return end end vim.api.nvim_set_current_buf(M.bufpos2nr[buf_pos]) end vim.cmd([[ function! LualineSwitchBuffer(bufnr, mouseclicks, mousebutton, modifiers) execute ":buffer " . a:bufnr endfunction command! -nargs=1 -bang LualineBuffersJump call v:lua.require'lualine.components.buffers'.buffer_jump(, "") ]]) return M ================================================ FILE: lua/lualine/components/datetime.lua ================================================ -- Copyright (c) 2023 Willothy -- MIT license, see LICENSE for more details. local lualine_require = require('lualine_require') local M = lualine_require.require('lualine.component'):extend() local default_options = { -- default, us, uk, iso, or format (ex. "%d/%m/%Y ...") style = 'default', } function M:init(options) M.super.init(self, options) self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) end function M:update_status() local fmt = self.options.style if self.options.style == 'default' then fmt = '%A, %B %d | %H:%M' elseif self.options.style == 'us' then fmt = '%m/%d/%Y' elseif self.options.style == 'uk' then fmt = '%d/%m/%Y' elseif self.options.style == 'iso' then fmt = '%Y-%m-%d' end return os.date(fmt) end return M ================================================ FILE: lua/lualine/components/diagnostics/config.lua ================================================ local require = require('lualine_require').require local utils = require('lualine.utils.utils') local M = {} -- default symbols for diagnostics component M.symbols = { icons = { error = '󰅚 ', -- x000f015a warn = '󰀪 ', -- x000f002a info = '󰋽 ', -- x000f02fd hint = '󰌶 ', -- x000f0336 }, no_icons = { error = 'E:', warn = 'W:', info = 'I:', hint = 'H:' }, } -- default options for diagnostics component M.options = { colored = true, update_in_insert = false, always_visible = false, sources = { 'nvim_diagnostic', 'coc' }, sections = { 'error', 'warn', 'info', 'hint' }, } function M.apply_default_colors(opts) local default_diagnostics_color = { error = { fg = utils.extract_color_from_hllist( { 'fg', 'sp' }, { 'DiagnosticError', 'LspDiagnosticsDefaultError', 'DiffDelete' }, '#e32636' ), }, warn = { fg = utils.extract_color_from_hllist( { 'fg', 'sp' }, { 'DiagnosticWarn', 'LspDiagnosticsDefaultWarning', 'DiffText' }, '#ffa500' ), }, info = { fg = utils.extract_color_from_hllist( { 'fg', 'sp' }, { 'DiagnosticInfo', 'LspDiagnosticsDefaultInformation', 'Normal' }, '#ffffff' ), }, hint = { fg = utils.extract_color_from_hllist( { 'fg', 'sp' }, { 'DiagnosticHint', 'LspDiagnosticsDefaultHint', 'DiffChange' }, '#273faf' ), }, } opts.diagnostics_color = vim.tbl_deep_extend('keep', opts.diagnostics_color or {}, default_diagnostics_color) end return M ================================================ FILE: lua/lualine/components/diagnostics/init.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local lualine_require = require('lualine_require') local modules = lualine_require.lazy_require { default_config = 'lualine.components.diagnostics.config', sources = 'lualine.components.diagnostics.sources', highlight = 'lualine.highlight', utils = 'lualine.utils.utils', utils_notices = 'lualine.utils.notices', } local M = lualine_require.require('lualine.component'):extend() M.diagnostics_sources = modules.sources.sources M.get_diagnostics = modules.sources.get_diagnostics -- Initializer function M:init(options) -- Run super() M.super.init(self, options) -- Apply default options modules.default_config.apply_default_colors(self.options) self.options = vim.tbl_deep_extend('keep', self.options or {}, modules.default_config.options) -- Apply default symbols self.symbols = vim.tbl_extend( 'keep', self.options.symbols or {}, self.options.icons_enabled ~= false and modules.default_config.symbols.icons or modules.default_config.symbols.no_icons ) -- Initialize highlight groups if self.options.colored then self.highlight_groups = { error = self:create_hl(self.options.diagnostics_color.error, 'error'), warn = self:create_hl(self.options.diagnostics_color.warn, 'warn'), info = self:create_hl(self.options.diagnostics_color.info, 'info'), hint = self:create_hl(self.options.diagnostics_color.hint, 'hint'), } end -- Initialize variable to store last update so we can use it in insert -- mode for no update_in_insert self.last_diagnostics_count = {} -- Error out no source if #self.options.sources < 1 then modules.utils_notices.add_notice( '### diagnostics.sources\n\nno sources for diagnostics configured.\nPlease specify which diagnostics source you want lualine to use with `sources` option.\n' ) end end function M:update_status() local bufnr = vim.api.nvim_get_current_buf() local diagnostics_count local result = {} if self.options.update_in_insert or vim.api.nvim_get_mode().mode:sub(1, 1) ~= 'i' then local error_count, warning_count, info_count, hint_count = 0, 0, 0, 0 local diagnostic_data = modules.sources.get_diagnostics(self.options.sources) -- sum all the counts for _, data in pairs(diagnostic_data) do error_count = error_count + data.error warning_count = warning_count + data.warn info_count = info_count + data.info hint_count = hint_count + data.hint end diagnostics_count = { error = error_count, warn = warning_count, info = info_count, hint = hint_count, } -- Save count for insert mode self.last_diagnostics_count[bufnr] = diagnostics_count else -- Use cached count in insert mode with update_in_insert disabled diagnostics_count = self.last_diagnostics_count[bufnr] or { error = 0, warn = 0, info = 0, hint = 0 } end local always_visible = false if type(self.options.always_visible) == 'boolean' then always_visible = self.options.always_visible elseif type(self.options.always_visible) == 'function' then always_visible = self.options.always_visible() end -- format the counts with symbols and highlights if self.options.colored then local colors, bgs = {}, {} for name, hl in pairs(self.highlight_groups) do colors[name] = self:format_hl(hl) bgs[name] = modules.utils.extract_highlight_colors(colors[name]:match('%%#(.-)#'), 'bg') end local previous_section, padding for _, section in ipairs(self.options.sections) do if diagnostics_count[section] ~= nil and (always_visible or diagnostics_count[section] > 0) then padding = previous_section and (bgs[previous_section] ~= bgs[section]) and ' ' or '' previous_section = section table.insert(result, colors[section] .. padding .. self.symbols[section] .. diagnostics_count[section]) end end else for _, section in ipairs(self.options.sections) do if diagnostics_count[section] ~= nil and (always_visible or diagnostics_count[section] > 0) then table.insert(result, self.symbols[section] .. diagnostics_count[section]) end end end return table.concat(result, ' ') end return M ================================================ FILE: lua/lualine/components/diagnostics/sources.lua ================================================ local M = {} ---functions that how how to retrieve diagnostics from specific source. ---returns error_count:number, warning_count:number, --- info_count:number, hint_count:number M.sources = { nvim_lsp = function() local error_count, warning_count, info_count, hint_count local diagnostics = vim.diagnostic.get(0) local count = { 0, 0, 0, 0 } for _, diagnostic in ipairs(diagnostics) do if vim.startswith(vim.diagnostic.get_namespace(diagnostic.namespace).name, 'vim.lsp') then count[diagnostic.severity] = count[diagnostic.severity] + 1 end end error_count = count[vim.diagnostic.severity.ERROR] warning_count = count[vim.diagnostic.severity.WARN] info_count = count[vim.diagnostic.severity.INFO] hint_count = count[vim.diagnostic.severity.HINT] return error_count, warning_count, info_count, hint_count end, nvim_workspace_diagnostic = function() local diag_severity = vim.diagnostic.severity local function workspace_diag(severity) local count = vim.diagnostic.get(nil, { severity = severity }) return vim.tbl_count(count) end return workspace_diag(diag_severity.ERROR), workspace_diag(diag_severity.WARN), workspace_diag(diag_severity.INFO), workspace_diag(diag_severity.HINT) end, nvim_diagnostic = function() local count if vim.diagnostic.count ~= nil then -- neovim >= 0.10.0 count = vim.diagnostic.count(0) return count[vim.diagnostic.severity.ERROR] or 0, count[vim.diagnostic.severity.WARN] or 0, count[vim.diagnostic.severity.INFO] or 0, count[vim.diagnostic.severity.HINT] or 0 end -- fallback local diagnostics = vim.diagnostic.get(0) count = { 0, 0, 0, 0 } for _, diagnostic in ipairs(diagnostics) do count[diagnostic.severity] = count[diagnostic.severity] + 1 end return count[vim.diagnostic.severity.ERROR], count[vim.diagnostic.severity.WARN], count[vim.diagnostic.severity.INFO], count[vim.diagnostic.severity.HINT] end, coc = function() local data = vim.b.coc_diagnostic_info if data then return data.error, data.warning, data.information, data.hint else return 0, 0, 0, 0 end end, ale = function() local ok, data = pcall(vim.fn['ale#statusline#Count'], vim.api.nvim_get_current_buf()) if ok then return data.error + data.style_error, data.warning + data.style_warning, data.info, 0 else return 0, 0, 0, 0 end end, vim_lsp = function() local ok, data = pcall(vim.fn['lsp#get_buffer_diagnostics_counts']) if ok then return data.error, data.warning, data.information, data.hint else return 0, 0, 0, 0 end end, } ---returns list of diagnostics count from all sources ---@param sources table list of sources ---@return table {{error_count, warning_count, info_count, hint_count}} M.get_diagnostics = function(sources) local result = {} for index, source in ipairs(sources) do if type(source) == 'string' then local error_count, warning_count, info_count, hint_count = M.sources[source]() result[index] = { error = error_count, warn = warning_count, info = info_count, hint = hint_count, } elseif type(source) == 'function' then local source_result = source() source_result = type(source_result) == 'table' and source_result or {} result[index] = { error = source_result.error or 0, warn = source_result.warn or 0, info = source_result.info or 0, hint = source_result.hint or 0, } end end return result end return M ================================================ FILE: lua/lualine/components/diff/git_diff.lua ================================================ local lualine_require = require('lualine_require') local modules = lualine_require.lazy_require { utils = 'lualine.utils.utils', Job = 'lualine.utils.job', } local M = {} -- Vars -- variable to store git diff stats local git_diff = nil -- accumulates output from diff process local diff_output_cache = {} -- variable to store git_diff job local diff_job = nil local active_bufnr = '0' local diff_cache = {} -- Stores last known value of diff of a buffer ---initialize the module ---param opts table function M.init(opts) if type(opts.source) == 'function' then M.src = opts.source else modules.utils.define_autocmd('BufEnter', "lua require'lualine.components.diff.git_diff'.update_diff_args()") modules.utils.define_autocmd('BufWritePost', "lua require'lualine.components.diff.git_diff'.update_git_diff()") M.update_diff_args() end end ---Api to get git sign count ---scheme : ---{ --- added = added_count, --- modified = modified_count, --- removed = removed_count, ---} ---error_code = { added = -1, modified = -1, removed = -1 } ---@param bufnr number|nil function M.get_sign_count(bufnr) if bufnr then return diff_cache[bufnr] end if M.src then git_diff = M.src() diff_cache[vim.api.nvim_get_current_buf()] = git_diff elseif vim.g.actual_curbuf ~= nil and active_bufnr ~= vim.g.actual_curbuf then -- Workaround for https://github.com/nvim-lualine/lualine.nvim/issues/286 -- See upstream issue https://github.com/neovim/neovim/issues/15300 -- Diff is out of sync re sync it. M.update_diff_args() end return git_diff end ---process diff data and update git_diff{ added, removed, modified } ---@param data string output on stdout od git diff job local function process_diff(data) -- Adapted from https://github.com/wbthomason/nvim-vcs.lua local added, removed, modified = 0, 0, 0 for _, line in ipairs(data) do if string.find(line, [[^@@ ]]) then local tokens = vim.fn.matchlist(line, [[^@@ -\v(\d+),?(\d*) \+(\d+),?(\d*)]]) local line_stats = { mod_count = tokens[3] == nil and 0 or tokens[3] == '' and 1 or tonumber(tokens[3]), new_count = tokens[5] == nil and 0 or tokens[5] == '' and 1 or tonumber(tokens[5]), } if line_stats.mod_count == 0 and line_stats.new_count > 0 then added = added + line_stats.new_count elseif line_stats.mod_count > 0 and line_stats.new_count == 0 then removed = removed + line_stats.mod_count else local min = math.min(line_stats.mod_count, line_stats.new_count) modified = modified + min added = added + line_stats.new_count - min removed = removed + line_stats.mod_count - min end end end git_diff = { added = added, modified = modified, removed = removed } end ---updates the job args function M.update_diff_args() -- Don't show git diff when current buffer doesn't have a filename active_bufnr = tostring(vim.api.nvim_get_current_buf()) if #vim.fn.expand('%') == 0 then M.diff_args = nil git_diff = nil return end M.diff_args = { cmd = { 'git', '-C', vim.fn.expand('%:h'), '--no-pager', 'diff', '--no-color', '--no-ext-diff', '-U0', '--', vim.fn.expand('%:t'), }, on_stdout = function(_, data) if next(data) then diff_output_cache = vim.list_extend(diff_output_cache, data) end end, on_stderr = function(_, data) data = table.concat(data, '\n') if #data > 0 then git_diff = nil diff_output_cache = {} end end, on_exit = function() if #diff_output_cache > 0 then process_diff(diff_output_cache) else git_diff = { added = 0, modified = 0, removed = 0 } end diff_cache[vim.api.nvim_get_current_buf()] = git_diff end, } M.update_git_diff() end ---update git_diff variable function M.update_git_diff() if M.diff_args then diff_output_cache = {} if diff_job then diff_job:stop() end diff_job = modules.Job(M.diff_args) if diff_job then diff_job:start() end end end return M ================================================ FILE: lua/lualine/components/diff/init.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local lualine_require = require('lualine_require') local modules = lualine_require.lazy_require { git_diff = 'lualine.components.diff.git_diff', utils = 'lualine.utils.utils', utils_notices = 'lualine.utils.notices', highlight = 'lualine.highlight', } local M = lualine_require.require('lualine.component'):extend() local default_options = { colored = true, symbols = { added = '+', modified = '~', removed = '-' }, } local function apply_default_colors(opts) local default_diff_color = { added = { fg = modules.utils.extract_color_from_hllist( 'fg', { 'LuaLineDiffAdd', 'GitSignsAdd', 'GitGutterAdd', 'DiffAdded', 'DiffAdd' }, '#90ee90' ), }, modified = { fg = modules.utils.extract_color_from_hllist( 'fg', { 'LuaLineDiffChange', 'GitSignsChange', 'GitGutterChange', 'DiffChanged', 'DiffChange' }, '#f0e130' ), }, removed = { fg = modules.utils.extract_color_from_hllist( 'fg', { 'LuaLineDiffDelete', 'GitSignsDelete', 'GitGutterDelete', 'DiffRemoved', 'DiffDelete' }, '#ff0038' ), }, } opts.diff_color = vim.tbl_deep_extend('keep', opts.diff_color or {}, default_diff_color) end -- Initializer function M:init(options) M.super.init(self, options) apply_default_colors(self.options) self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) -- create highlights and save highlight_name in highlights table if self.options.colored then self.highlights = { added = self:create_hl(self.options.diff_color.added, 'added'), modified = self:create_hl(self.options.diff_color.modified, 'modified'), removed = self:create_hl(self.options.diff_color.removed, 'removed'), } end modules.git_diff.init(self.options) end -- Function that runs every time statusline is updated function M:update_status(is_focused) local git_diff = modules.git_diff.get_sign_count((not is_focused and vim.api.nvim_get_current_buf())) if git_diff == nil then return '' end local colors = {} if self.options.colored then -- load the highlights and store them in colors table for name, highlight_name in pairs(self.highlights) do colors[name] = self:format_hl(highlight_name) end end local result = {} -- loop though data and load available sections in result table for _, name in ipairs { 'added', 'modified', 'removed' } do if git_diff[name] and git_diff[name] > 0 then if self.options.colored then table.insert(result, colors[name] .. self.options.symbols[name] .. git_diff[name]) else table.insert(result, self.options.symbols[name] .. git_diff[name]) end end end if #result > 0 then return table.concat(result, ' ') else return '' end end return M ================================================ FILE: lua/lualine/components/encoding.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local lualine_require = require('lualine_require') local M = lualine_require.require('lualine.component'):extend() local default_options = { -- Show '[BOM]' when the file has a byte-order mark show_bomb = false, } function M:init(options) M.super.init(self, options) self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) end function M:update_status() local show_bomb = self.options.show_bomb local result = vim.opt.fileencoding:get() if not show_bomb then return result end if vim.opt.bomb:get() then result = result .. ' [BOM]' end return result end return M ================================================ FILE: lua/lualine/components/fileformat.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local M = require('lualine.component'):extend() -- stylua: ignore local symbols = { unix = '', -- e712 dos = '', -- e70f mac = '', -- e711 } -- Initializer function M:init(options) -- Run super() M.super.init(self, options) -- Apply default symbols self.symbols = vim.tbl_extend('keep', self.options.symbols or {}, symbols) end -- Function that runs every time statusline is updated function M:update_status() local format = vim.bo.fileformat if self.options.icons_enabled then return self.symbols[format] or format else return format end end return M ================================================ FILE: lua/lualine/components/filename.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local M = require('lualine.component'):extend() local modules = require('lualine_require').lazy_require { utils = 'lualine.utils.utils', } local default_options = { symbols = { modified = '[+]', readonly = '[-]', unnamed = '[No Name]', newfile = '[New]', }, file_status = true, newfile_status = false, path = 0, shorting_target = 40, } local function is_new_file() local filename = vim.fn.expand('%') return filename ~= '' and filename:match('^%a+://') == nil and vim.bo.buftype == '' and vim.fn.filereadable(filename) == 0 end ---shortens path by turning apple/orange -> a/orange ---@param path string ---@param sep string path separator ---@param max_len integer maximum length of the full filename string ---@return string local function shorten_path(path, sep, max_len) local len = #path if len <= max_len then return path end local segments = vim.split(path, sep) for idx = 1, #segments - 1 do if len <= max_len then break end local segment = segments[idx] local shortened = segment:sub(1, vim.startswith(segment, '.') and 2 or 1) segments[idx] = shortened len = len - (#segment - #shortened) end return table.concat(segments, sep) end local function filename_and_parent(path, sep) local segments = vim.split(path, sep) if #segments == 0 then return path elseif #segments == 1 then return segments[#segments] else return table.concat({ segments[#segments - 1], segments[#segments] }, sep) end end M.init = function(self, options) M.super.init(self, options) self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) end M.update_status = function(self) local path_separator = package.config:sub(1, 1) local data if self.options.path == 1 then -- relative path data = vim.fn.expand('%:~:.') elseif self.options.path == 2 then -- absolute path data = vim.fn.expand('%:p') elseif self.options.path == 3 then -- absolute path, with tilde data = vim.fn.expand('%:p:~') elseif self.options.path == 4 then -- filename and immediate parent data = filename_and_parent(vim.fn.expand('%:p:~'), path_separator) else -- just filename data = vim.fn.expand('%:t') end if data == '' then data = self.options.symbols.unnamed end local shorting_target = self.options.shorting_target if type(shorting_target) == 'function' then shorting_target = shorting_target() end if shorting_target ~= 0 then local windwidth = self.options.globalstatus and vim.go.columns or vim.fn.winwidth(0) local estimated_space_available = windwidth - shorting_target data = shorten_path(data, path_separator, estimated_space_available) end data = modules.utils.stl_escape(data) local symbols = {} if self.options.file_status then if vim.bo.modified then table.insert(symbols, self.options.symbols.modified) end if vim.bo.modifiable == false or vim.bo.readonly == true then table.insert(symbols, self.options.symbols.readonly) end end if self.options.newfile_status and is_new_file() then table.insert(symbols, self.options.symbols.newfile) end return data .. (#symbols > 0 and ' ' .. table.concat(symbols, '') or '') end return M ================================================ FILE: lua/lualine/components/filesize.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local function filesize() local file = vim.fn.expand('%:p') if file == nil or #file == 0 then return '' end local size = vim.fn.getfsize(file) if size <= 0 then return '' end local suffixes = { 'b', 'k', 'm', 'g' } local i = 1 while size > 1024 and i < #suffixes do size = size / 1024 i = i + 1 end local format = i == 1 and '%d%s' or '%.1f%s' return string.format(format, size, suffixes[i]) end return filesize ================================================ FILE: lua/lualine/components/filetype.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local lualine_require = require('lualine_require') local modules = lualine_require.lazy_require { highlight = 'lualine.highlight', utils = 'lualine.utils.utils', } local M = lualine_require.require('lualine.component'):extend() local default_options = { colored = true, icon_only = false, } function M:init(options) M.super.init(self, options) self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) self.icon_hl_cache = {} end function M.update_status() local ft = vim.bo.filetype or '' return modules.utils.stl_escape(ft) end function M:apply_icon() if not self.options.icons_enabled then return end local icon, icon_highlight_group local ok, devicons = pcall(require, 'nvim-web-devicons') if ok then icon, icon_highlight_group = devicons.get_icon(vim.fn.expand('%:t')) if icon == nil then icon, icon_highlight_group = devicons.get_icon_by_filetype(vim.bo.filetype) end if icon == nil and icon_highlight_group == nil then icon = '' icon_highlight_group = 'DevIconDefault' end if icon then icon = icon .. ' ' end if self.options.colored then local highlight_color = modules.utils.extract_highlight_colors(icon_highlight_group, 'fg') if highlight_color then local default_highlight = self:get_default_hl() local icon_highlight = self.icon_hl_cache[highlight_color] if not icon_highlight or not modules.highlight.highlight_exists(icon_highlight.name .. '_normal') then icon_highlight = self:create_hl({ fg = highlight_color }, icon_highlight_group) self.icon_hl_cache[highlight_color] = icon_highlight end icon = self:format_hl(icon_highlight) .. icon .. default_highlight end end else ok = vim.fn.exists('*WebDevIconsGetFileTypeSymbol') if ok ~= 0 then icon = vim.fn.WebDevIconsGetFileTypeSymbol() if icon then icon = icon .. ' ' end end end if not icon then return end if self.options.icon_only then self.status = icon elseif type(self.options.icon) == 'table' and self.options.icon.align == 'right' then self.status = self.status .. ' ' .. icon else self.status = icon .. self.status end end return M ================================================ FILE: lua/lualine/components/hostname.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local modules = require('lualine_require').lazy_require { utils = 'lualine.utils.utils', } local function hostname() return modules.utils.stl_escape(vim.loop.os_gethostname()) end return hostname ================================================ FILE: lua/lualine/components/location.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local function location() local line = vim.fn.line('.') local col = vim.fn.charcol('.') return string.format('%3d:%-2d', line, col) end return location ================================================ FILE: lua/lualine/components/lsp_status.lua ================================================ local lualine_require = require('lualine_require') local M = lualine_require.require('lualine.component'):extend() local default_options = { icon = '', -- f013 symbols = { -- Use standard unicode characters for the spinner and done symbols: spinner = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' }, done = '✓', separator = ' ', }, -- List of LSP names to ignore (e.g., `null-ls`): ignore_lsp = {}, show_name = true, } function M:init(options) -- Run `super()`. M.super.init(self, options) -- Apply default options. self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) -- Apply symbols. self.symbols = self.options.symbols or {} ---The difference between the `begin` and `end` progress events for each LSP. --- ---@type table self.lsp_work_by_client_id = {} -- Listen to progress updates only if `nvim` supports the `LspProgress` event. pcall(vim.api.nvim_create_autocmd, 'LspProgress', { desc = 'Update the Lualine LSP status component with progress', group = vim.api.nvim_create_augroup('lualine_lsp_progress', {}), ---@param event {data: {client_id: integer, params: lsp.ProgressParams}} callback = function(event) local kind = event.data.params.value.kind local client_id = event.data.client_id local work = self.lsp_work_by_client_id[client_id] or 0 local work_change = kind == 'begin' and 1 or (kind == 'end' and -1 or 0) self.lsp_work_by_client_id[client_id] = math.max(work + work_change, 0) -- Refresh Lualine to update the LSP status symbol if it changed. if (work == 0 and work_change > 0) or (work == 1 and work_change < 0) then require('lualine').refresh() end end, }) end function M:update_status() local result = {} local processed = {} -- Backwards-compatible function to get the active LSP clients. ---@diagnostic disable-next-line: deprecated local get_lsp_clients = vim.lsp.get_clients or vim.lsp.get_active_clients local clients = get_lsp_clients { bufnr = vim.api.nvim_get_current_buf() } -- Backwards-compatible function to get the current time in nanoseconds. local hrtime = (vim.uv or vim.loop).hrtime -- Advance the spinner every 80ms only once, not for each client (otherwise the spinners will skip steps). -- NOTE: the spinner symbols table is 1-indexed. local spinner_symbol = self.symbols.spinner[math.floor(hrtime() / (1e6 * 80)) % #self.symbols.spinner + 1] for _, client in ipairs(clients) do local status local work = self.lsp_work_by_client_id[client.id] if work ~= nil and work > 0 then status = spinner_symbol elseif work ~= nil and work == 0 then status = self.symbols.done end -- Backwards-compatible function to check if a list contains a value. local list_contains = vim.list_contains or vim.tbl_contains -- Append the status to the LSP only if it supports progress reporting and is not ignored. if not processed[client.name] and not list_contains(self.options.ignore_lsp, client.name) then local status_display = ((status and status ~= '') and (' ' .. status) or '') if self.options.show_name then table.insert(result, client.name .. status_display) else table.insert(result, status_display) end processed[client.name] = true end end return table.concat(result, self.symbols.separator) end return M ================================================ FILE: lua/lualine/components/mode.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local get_mode = require('lualine.utils.mode').get_mode return get_mode ================================================ FILE: lua/lualine/components/progress.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local function progress() local cur = vim.fn.line('.') local total = vim.fn.line('$') if cur == 1 then return 'Top' elseif cur == total then return 'Bot' else return string.format('%2d%%%%', math.floor(cur / total * 100)) end end return progress ================================================ FILE: lua/lualine/components/searchcount.lua ================================================ local M = require('lualine.component'):extend() local default_options = { maxcount = 999, timeout = 500, } -- Initializer function M:init(options) -- Run super() M.super.init(self, options) -- Apply default options self.options = vim.tbl_extend('keep', self.options or {}, default_options) end -- Function that runs every time statusline is updated function M:update_status() if vim.v.hlsearch == 0 then return '' end local ok, result = pcall(vim.fn.searchcount, { maxcount = self.options.maxcount, timeout = self.options.timeout }) if not ok or next(result) == nil then return '' end local denominator = math.min(result.total, result.maxcount) return string.format('[%d/%d]', result.current, denominator) end return M ================================================ FILE: lua/lualine/components/selectioncount.lua ================================================ local function selectioncount() local mode = vim.fn.mode(true) local line_start, col_start = vim.fn.line('v'), vim.fn.col('v') local line_end, col_end = vim.fn.line('.'), vim.fn.col('.') if mode:match('') then return string.format('%dx%d', math.abs(line_start - line_end) + 1, math.abs(col_start - col_end) + 1) elseif mode:match('V') or line_start ~= line_end then return math.abs(line_start - line_end) + 1 elseif mode:match('v') then return math.abs(col_start - col_end) + 1 else return '' end end return selectioncount ================================================ FILE: lua/lualine/components/special/eval_func_component.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local M = require('lualine.component'):extend() function M:update_status() local component = self.options[1] local ok, status if self.options.type == nil then ok, status = pcall(M.lua_eval, component) if not ok then status = M.vim_function(component) end else if self.options.type == 'lua_expr' then ok, status = pcall(M.lua_eval, component) if not ok then status = nil end elseif self.options.type == 'vim_fun' then status = M.vim_function(component) end end return status end ---evaluate the lua code and return it's result as string ---@param code string ---@return string function M.lua_eval(code) local result = loadstring('return ' .. code)() assert(result, 'String expected got nil') return tostring(result) end ---call vim function (name) and return it's result as string ---@param name string ---@return string function M.vim_function(name) -- vim function component local ok, return_val = pcall(vim.api.nvim_call_function, name, {}) if not ok then return '' end -- function call failed ok, return_val = pcall(tostring, return_val) return ok and return_val or '' end return M ================================================ FILE: lua/lualine/components/special/function_component.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local M = require('lualine.component'):extend() M.update_status = function(self, is_focused) -- 1st element in options table is the function provided by config local ok, retval ok, retval = pcall(self.options[1], self, is_focused) if not ok then return '' end if type(retval) ~= 'string' then ok, retval = pcall(tostring, retval) if not ok then return '' end end return retval end return M ================================================ FILE: lua/lualine/components/special/vim_var_component.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local require = require('lualine_require').require local M = require('lualine.component'):extend() local utils = require('lualine.utils.utils') function M:update_status() local component = self.options[1] -- vim variable component -- accepts g:, v:, t:, w:, b:, o, go:, vo:, to:, wo:, bo: -- filters g portion from g:var local scope = component:match('[gvtwb]?o?') -- filters var portion from g:var local var_name = component:sub(#scope + 2, #component) -- Displays nothing when variable aren't present if not (scope and var_name) then return '' end -- Support accessing keys within dictionary -- https://github.com/nvim-lualine/lualine.nvim/issues/25#issuecomment-907374548 local name_chunks = vim.split(var_name, '%.') local return_val = vim[scope][name_chunks[1]] for i = 2, #name_chunks do if return_val == nil then break end return_val = return_val[name_chunks[i]] end if return_val == nil then return '' end local ok ok, return_val = pcall(tostring, return_val) return ok and utils.stl_escape(return_val) or '' end return M ================================================ FILE: lua/lualine/components/tabs/init.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local require = require('lualine_require').require local Tab = require('lualine.components.tabs.tab') local M = require('lualine.component'):extend() local highlight = require('lualine.highlight') local default_options = { max_length = 0, tab_max_length = 40, mode = 0, use_mode_colors = false, path = 0, tabs_color = { active = nil, inactive = nil, }, show_modified_status = true, symbols = { modified = '[+]', }, } -- This function is duplicated in buffers ---returns the proper hl for tab in section. Used for setting default highlights ---@param section string name of section tabs component is in ---@param is_active boolean ---@return string hl name local function get_hl(section, is_active) local suffix = is_active and highlight.get_mode_suffix() or '_inactive' local section_redirects = { lualine_x = 'lualine_c', lualine_y = 'lualine_b', lualine_z = 'lualine_a', } if section_redirects[section] then section = highlight.highlight_exists(section .. suffix) and section or section_redirects[section] end return section .. suffix end function M:init(options) M.super.init(self, options) -- if use_mode_colors is set, use a function so that the colors update local default_active = options.use_mode_colors and function() return get_hl('lualine_' .. options.self.section, true) end or get_hl('lualine_' .. options.self.section, true) default_options.tabs_color = { active = default_active, inactive = get_hl('lualine_' .. options.self.section, false), } self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) -- stylua: ignore self.highlights = { active = self:create_hl(self.options.tabs_color.active, 'active'), inactive = self:create_hl(self.options.tabs_color.inactive, 'inactive'), } end function M:update_status() local data = {} local tabs = {} for nr, id in ipairs(vim.api.nvim_list_tabpages()) do tabs[#tabs + 1] = Tab { tabId = id, tabnr = nr, options = self.options, highlights = self.highlights } end -- mark the first, last, current, before current, after current tabpages -- for rendering local current = vim.fn.tabpagenr() tabs[1].first = true tabs[#tabs].last = true if tabs[current] then tabs[current].current = true end if tabs[current - 1] then tabs[current - 1].beforecurrent = true end if tabs[current + 1] then tabs[current + 1].aftercurrent = true end local max_length = self.options.max_length if type(max_length) == 'function' then max_length = max_length(self) end if max_length == 0 then max_length = math.floor(vim.o.columns / 3) end local total_length for i, tab in pairs(tabs) do if tab.current then current = i end end local current_tab = tabs[current] -- start drawing from current tab and draw left and right of it until -- all tabpages are drawn or max_length has been reached. if current_tab == nil then -- maybe redundant code local t = Tab { tabId = vim.api.nvim_get_current_tabpage(), tabnr = vim.fn.tabpagenr(), options = self.options, highlights = self.highlights, } t.current = true t.last = true data[#data + 1] = t:render() else data[#data + 1] = current_tab:render() total_length = current_tab.len local i = 0 local before, after while true do i = i + 1 before = tabs[current - i] after = tabs[current + i] local rendered_before, rendered_after if before == nil and after == nil then break end -- draw left most undrawn tab if fits in max_length if before then rendered_before = before:render() total_length = total_length + before.len if total_length > max_length then break end table.insert(data, 1, rendered_before) end -- draw right most undrawn tab if fits in max_length if after then rendered_after = after:render() total_length = total_length + after.len if total_length > max_length then break end data[#data + 1] = rendered_after end end -- draw ellipsis (...) on relevant sides if all tabs don't fit in max_length if total_length > max_length then if before ~= nil then before.ellipse = true before.first = true table.insert(data, 1, before:render()) end if after ~= nil then after.ellipse = true after.last = true data[#data + 1] = after:render() end end end return table.concat(data) end function M:draw() self.status = '' self.applied_separator = '' if self.options.cond ~= nil and self.options.cond() ~= true then return self.status end local status = self:update_status() if type(status) == 'string' and #status > 0 then self.status = status self:apply_section_separators() self:apply_separator() end return self.status end vim.cmd([[ function! LualineSwitchTab(tabnr, mouseclicks, mousebutton, modifiers) execute a:tabnr . "tabnext" endfunction function! LualineRenameTab(...) if a:0 == 1 let t:tabname = a:1 else unlet t:tabname end redrawtabline endfunction command! -nargs=? LualineRenameTab call LualineRenameTab("") ]]) return M ================================================ FILE: lua/lualine/components/tabs/tab.lua ================================================ local Tab = require('lualine.utils.class'):extend() local modules = require('lualine_require').lazy_require { highlight = 'lualine.highlight', utils = 'lualine.utils.utils', } ---initialize a new tab from opts ---@param opts table function Tab:init(opts) assert(opts.tabnr, 'Cannot create Tab without tabnr') self.tabnr = opts.tabnr self.tabId = opts.tabId self.options = opts.options self.highlights = opts.highlights self.modified_icon = '' self:get_props() end function Tab:get_props() local buflist = vim.fn.tabpagebuflist(self.tabnr) local winnr = vim.fn.tabpagewinnr(self.tabnr) local bufnr = buflist[winnr] self.file = modules.utils.stl_escape(vim.api.nvim_buf_get_name(bufnr)) self.filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype') self.buftype = vim.api.nvim_buf_get_option(bufnr, 'buftype') if self.options.show_modified_status then for _, b in ipairs(buflist) do if vim.api.nvim_buf_get_option(b, 'modified') then self.modified_icon = self.options.symbols.modified or '' break end end end end ---returns name for tab. Tabs name is the name of buffer in last active window --- of the tab. ---@return string function Tab:label() local ok, custom_tabname = pcall(vim.api.nvim_tabpage_get_var, self.tabId, 'tabname') if not ok then custom_tabname = nil end if custom_tabname and custom_tabname ~= '' then return modules.utils.stl_escape(custom_tabname) end if self.filetype == 'fugitive' then return 'fugitive: ' .. vim.fn.fnamemodify(self.file, ':h:h:t') elseif self.buftype == 'help' then return 'help:' .. vim.fn.fnamemodify(self.file, ':t:r') elseif self.buftype == 'terminal' then local match = string.match(vim.split(self.file, ' ')[1], 'term:.*:(%a+)') return match ~= nil and match or vim.fn.fnamemodify(vim.env.SHELL, ':t') elseif self.file == '' then return '[No Name]' end if self.options.path == 1 then return vim.fn.fnamemodify(self.file, ':~:.') elseif self.options.path == 2 then return vim.fn.fnamemodify(self.file, ':p') elseif self.options.path == 3 then return vim.fn.fnamemodify(self.file, ':p:~') else return vim.fn.fnamemodify(self.file, ':t') end end ---shortens path by turning apple/orange -> a/orange ---@param path string ---@param sep string path separator ---@param max_len integer maximum length of the full filename string ---@return string local function shorten_path(path, sep, max_len) local len = #path if len <= max_len then return path end local segments = vim.split(path, sep) for idx = 1, #segments - 1 do if len <= max_len then break end local segment = segments[idx] local shortened = segment:sub(1, vim.startswith(segment, '.') and 2 or 1) segments[idx] = shortened len = len - (#segment - #shortened) end return table.concat(segments, sep) end ---returns rendered tab ---@return string function Tab:render() local name = self:label() if self.options.tab_max_length ~= 0 then local path_separator = package.config:sub(1, 1) name = shorten_path(name, path_separator, self.options.tab_max_length) end if self.options.fmt then name = self.options.fmt(name or '', self) end if self.ellipse then -- show ellipsis name = '...' else -- different formats for different modes if self.options.mode == 0 then name = tostring(self.tabnr) if self.modified_icon ~= '' then name = string.format('%s%s', name, self.modified_icon) end elseif self.options.mode == 1 then if self.modified_icon ~= '' then name = string.format('%s %s', self.modified_icon, name) end else name = string.format('%s%s %s', tostring(self.tabnr), self.modified_icon, name) end end name = Tab.apply_padding(name, self.options.padding) self.len = vim.fn.strchars(name) -- setup for mouse clicks local line = string.format('%%%s@LualineSwitchTab@%s%%T', self.tabnr, name) -- apply highlight line = modules.highlight.component_format_highlight(self.highlights[(self.current and 'active' or 'inactive')]) .. line -- apply separators if self.options.self.section < 'x' and not self.first then local sep_before = self:separator_before() line = sep_before .. line self.len = self.len + vim.fn.strchars(sep_before) elseif self.options.self.section >= 'x' and not self.last then local sep_after = self:separator_after() line = line .. sep_after self.len = self.len + vim.fn.strchars(sep_after) end return line end ---apply separator before current tab ---@return string function Tab:separator_before() if self.current or self.aftercurrent then return '%Z{' .. self.options.section_separators.left .. '}' else return self.options.component_separators.left end end ---apply separator after current tab ---@return string function Tab:separator_after() if self.current or self.beforecurrent then return '%z{' .. self.options.section_separators.right .. '}' else return self.options.component_separators.right end end ---adds spaces to left and right function Tab.apply_padding(str, padding) local l_padding, r_padding = 1, 1 if type(padding) == 'number' then l_padding, r_padding = padding, padding elseif type(padding) == 'table' then l_padding, r_padding = padding.left or 0, padding.right or 0 end return string.rep(' ', l_padding) .. str .. string.rep(' ', r_padding) end return Tab ================================================ FILE: lua/lualine/components/windows/init.lua ================================================ local Window = require('lualine.components.windows.window') local M = require('lualine.components.buffers'):extend() local default_options = { disabled_filetypes = {}, disabled_buftypes = { 'quickfix', 'prompt' }, } function M:init(options) options.buffers_color = nil -- buffers_color isn't windows option. M.super.init(self, options) self.options = vim.tbl_deep_extend('keep', self.options or {}, default_options) self.options.windows_color = vim.tbl_deep_extend('keep', self.options.windows_color or {}, self.options.buffers_color) self.options.buffers_color = nil -- this is the default value of colors generated by parent buffers component. self.highlights = { active = self:create_hl(self.options.windows_color.active, 'active'), inactive = self:create_hl(self.options.windows_color.inactive, 'inactive'), } end function M:new_buffer(winnr) winnr = winnr or vim.api.nvim_get_current_win() return Window:new { winnr = winnr, options = self.options, highlights = self.highlights, } end --- Override to only return buffers shown in the windows of the current tab function M:buffers() local tabnr = vim.api.nvim_get_current_tabpage() local buffers = {} for _, winnr in ipairs(vim.api.nvim_tabpage_list_wins(tabnr)) do if not self:should_hide(winnr) then buffers[#buffers + 1] = self:new_buffer(winnr) end end return buffers end function M:should_hide(winnr) local bufnr = vim.api.nvim_win_get_buf(winnr) local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype') local buftype = vim.api.nvim_buf_get_option(bufnr, 'buftype') local is_filetype_disabled = vim.tbl_contains(self.options.disabled_filetypes, filetype) local is_buftype_disabled = vim.tbl_contains(self.options.disabled_buftypes, buftype) local is_floating = '' ~= vim.api.nvim_win_get_config(winnr).relative return is_floating or is_buftype_disabled or is_filetype_disabled end vim.cmd([[ function! LualineSwitchWindow(win_number, mouseclicks, mousebutton, modifiers) execute a:win_number . 'wincmd w' endfunction ]]) return M ================================================ FILE: lua/lualine/components/windows/window.lua ================================================ local Window = require('lualine.components.buffers.buffer'):extend() ---initialize a new buffer from opts ---@param opts table function Window:init(opts) assert(opts.winnr, 'Cannot create Window without winnr') opts.bufnr = vim.api.nvim_win_get_buf(opts.winnr) Window.super.init(self, opts) self.winnr = opts.winnr self.win_number = vim.api.nvim_win_get_number(self.winnr) end function Window:is_current() return vim.api.nvim_get_current_win() == self.winnr end function Window:apply_mode(name) if self.options.mode == 0 then return string.format('%s%s%s', self.icon, name, self.modified_icon) end if self.options.mode == 1 then return string.format('%s %s%s', self.win_number, self.icon, self.modified_icon) end return string.format('%s %s%s%s', self.win_number, self.icon, name, self.modified_icon) end function Window:configure_mouse_click(name) return string.format('%%%s@LualineSwitchWindow@%s%%T', self.win_number, name) end return Window ================================================ FILE: lua/lualine/config.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local require = require('lualine_require').require local utils = require('lualine.utils.utils') local modules = require('lualine_require').lazy_require { utils_notices = 'lualine.utils.notices', } local config = { options = { icons_enabled = true, theme = 'auto', component_separators = { left = '', right = '' }, section_separators = { left = '', right = '' }, disabled_filetypes = { statusline = {}, winbar = {}, }, ignore_focus = {}, always_divide_middle = true, always_show_tabline = true, globalstatus = vim.go.laststatus == 3, refresh = { statusline = 1000, tabline = 1000, winbar = 1000, refresh_time = 16, -- ~60fps events = { 'WinEnter', 'BufEnter', 'BufWritePost', 'SessionLoadPost', 'FileChangedShellPost', 'VimResized', 'Filetype', 'CursorMoved', 'CursorMovedI', 'ModeChanged', }, }, }, sections = { lualine_a = { 'mode' }, lualine_b = { 'branch', 'diff', 'diagnostics' }, lualine_c = { 'filename' }, lualine_x = { 'encoding', 'fileformat', 'filetype' }, lualine_y = { 'progress' }, lualine_z = { 'location' }, }, inactive_sections = { lualine_a = {}, lualine_b = {}, lualine_c = { 'filename' }, lualine_x = { 'location' }, lualine_y = {}, lualine_z = {}, }, tabline = {}, winbar = {}, inactive_winbar = {}, extensions = {}, } --- Ensure sure user config doesn't disable lualine refresh completely ---@param refresh_options table | nil ---@return table | nil local function fix_refresh_timer(refresh_options) if refresh_options == nil then return end if refresh_options.refresh_time <= 0 then refresh_options.refresh_time = 16 end return refresh_options end --- change separator format 'x' to {left='x', right='x'} ---@param separators string|table ---@return table local function fix_separators(separators) if separators ~= nil then if type(separators) == 'string' then return { left = separators, right = separators } end end return separators end ---copy raw disabled_filetypes to inner statusline & winbar tables. ---@param disabled_filetypes table ---@return table local function fix_disabled_filetypes(disabled_filetypes) if disabled_filetypes == nil then return end if disabled_filetypes.statusline == nil then disabled_filetypes.statusline = {} end if disabled_filetypes.winbar == nil then disabled_filetypes.winbar = {} end for k, disabled_ft in ipairs(disabled_filetypes) do table.insert(disabled_filetypes.statusline, disabled_ft) table.insert(disabled_filetypes.winbar, disabled_ft) disabled_filetypes[k] = nil end return disabled_filetypes end ---extends config based on config_table ---@param config_table table ---@return table copy of config local function apply_configuration(config_table) if not config_table then return utils.deepcopy(config) end local function parse_sections(section_group_name) if config_table[section_group_name] == nil then return end if not next(config_table[section_group_name]) then config[section_group_name] = {} return end for section_name, section in pairs(config_table[section_group_name]) do if section_name == 'refresh' then config[section_group_name][section_name] = vim.tbl_deep_extend('force', config[section_group_name][section_name], utils.deepcopy(section)) else config[section_group_name][section_name] = utils.deepcopy(section) end end end if vim.fn.has('nvim-0.8') == 0 and (next(config_table.winbar or {}) or next(config_table.inactive_winbar or {})) then modules.utils_notices.add_notice('### winbar\nSorry `winbar can only be used in neovim 0.8 or higher.\n') config_table.winbar = {} config_table.inactive_winbar = {} end parse_sections('options') parse_sections('sections') parse_sections('inactive_sections') parse_sections('tabline') parse_sections('winbar') parse_sections('inactive_winbar') if config_table.extensions then config.extensions = utils.deepcopy(config_table.extensions) end config.options.refresh = fix_refresh_timer(config.options.refresh) config.options.section_separators = fix_separators(config.options.section_separators) config.options.component_separators = fix_separators(config.options.component_separators) config.options.disabled_filetypes = fix_disabled_filetypes(config.options.disabled_filetypes) return utils.deepcopy(config) end --- returns current active config ---@return table a copy of config local function get_current_config() return utils.deepcopy(config) end return { get_config = get_current_config, apply_configuration = apply_configuration, } ================================================ FILE: lua/lualine/extensions/aerial.lua ================================================ -- MIT license, see LICENSE for more details. -- Extension for aerial.nvim local M = {} M.sections = { lualine_a = { 'filetype' } } M.filetypes = { 'aerial' } return M ================================================ FILE: lua/lualine/extensions/assistant.lua ================================================ -- lualine extension for assistant.nvim local ok, assistant = pcall(require, 'assistant') if not ok then return nil end local M = {} M.sections = { lualine_a = { function() return 'Assistant' end, }, lualine_b = { function() return vim.bo.filetype:match('%w+.(%w+)') end, }, lualine_c = { function() return assistant.status()[vim.bo.filetype:match('%w+.(%w+)')] or '' end, }, } M.filetypes = { 'assistant-panel', 'assistant-previewer', 'assistant-picker', 'assistant-dialog', 'assistant-patcher', } return M ================================================ FILE: lua/lualine/extensions/avante.lua ================================================ -- MIT license, see LICENSE for more details. -- Extension for avante.nvim local M = {} local function ft_info() local ft = vim.opt_local.filetype:get() if ft == 'Avante' then return 'Output' elseif ft == 'AvanteInput' then return require('lualine.utils.mode').get_mode() elseif ft == 'AvanteSelectedCode' then local max_shown = vim.api.nvim_win_get_height(0) local avante_ok, avante_config = pcall(require, 'avante.config') if avante_ok and avante_config.windows.sidebar_header.enabled then max_shown = max_shown - 1 end local num_lines = vim.api.nvim_buf_line_count(0) return string.format('Code Fragment: %s%d lines', max_shown < num_lines and (max_shown .. '/') or '', num_lines) elseif ft == 'AvanteSelectedFiles' then return 'Total Files: ' .. vim.api.nvim_buf_line_count(0) elseif ft == 'AvanteTodos' then local todos = vim.api.nvim_buf_get_lines(0, 0, -1, false) local completed = vim.iter(todos):fold(0, function(counter, todo) if todo:sub(1, 3) == '[x]' then counter = counter + 1 end return counter end) return string.format('Todos: %d/%d', completed, #todos) end end M.sections = { lualine_a = { ft_info } } M.filetypes = { 'Avante', 'AvanteInput', 'AvanteSelectedCode', 'AvanteSelectedFiles', 'AvanteTodos' } return M ================================================ FILE: lua/lualine/extensions/chadtree.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local nerdtree = require('lualine.extensions.nerdtree') local M = {} M.sections = vim.deepcopy(nerdtree.sections) M.filetypes = { 'CHADTree' } return M ================================================ FILE: lua/lualine/extensions/ctrlspace.lua ================================================ local M = {} M.sections = { lualine_a = { function() return vim.fn['ctrlspace#context#Configuration']().Symbols.CS end, }, lualine_b = { 'ctrlspace#api#StatuslineModeSegment' }, lualine_y = { 'ctrlspace#api#StatuslineTabSegment' }, lualine_z = { function() return 'CtrlSpace' end, }, } M.filetypes = { 'ctrlspace' } return M ================================================ FILE: lua/lualine/extensions/fern.lua ================================================ -- MIT license, see LICENSE for more details. -- Extension for fern file explorer. local nerdtree = require('lualine.extensions.nerdtree') local M = {} M.sections = vim.deepcopy(nerdtree.sections) M.filetypes = { 'fern' } return M ================================================ FILE: lua/lualine/extensions/fugitive.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local M = {} local function fugitive_branch() local icon = '' -- e0a0 return icon .. ' ' .. vim.fn.FugitiveHead() end M.sections = { lualine_a = { fugitive_branch }, lualine_z = { 'location' }, } M.filetypes = { 'fugitive' } return M ================================================ FILE: lua/lualine/extensions/fzf.lua ================================================ --[[ lualine extension for fzf filetypes: works with both https://github.com/junegunn/fzf.vim and https://github.com/ibhagwan/fzf-lua -- fzf-lua must be set-up in split mode ]] local function has_fzf() return pcall(require, 'fzf-lua') end local function fzf_picker() if not has_fzf() then return '' end local info_string = vim.inspect(require('fzf-lua').get_info()['fnc']) return info_string:gsub('"', '') end local function fzf_element() if not has_fzf() then return '' end local fzf = require('fzf-lua') local selected = fzf.get_info().selected return fzf.path.entry_to_file(selected).path end local function fzf_statusline() return 'FZF' end local M = {} M.sections = { lualine_a = { fzf_statusline }, lualine_y = { fzf_element }, lualine_z = { fzf_picker }, } M.filetypes = { 'fzf' } return M ================================================ FILE: lua/lualine/extensions/lazy.lua ================================================ -- lualine extension for lazy.nvim local ok, lazy = pcall(require, 'lazy') if not ok then return '' end local M = {} M.sections = { lualine_a = { function() return 'lazy 💤' end, }, lualine_b = { function() return 'loaded: ' .. lazy.stats().loaded .. '/' .. lazy.stats().count end, }, lualine_c = { { require('lazy.status').updates, cond = require('lazy.status').has_updates, }, }, } M.filetypes = { 'lazy' } return M ================================================ FILE: lua/lualine/extensions/man.lua ================================================ local M = {} M.sections = { lualine_a = { function() return 'MAN' end, }, lualine_b = { { 'filename', file_status = false } }, lualine_y = { 'progress' }, lualine_z = { 'location' }, } M.filetypes = { 'man' } return M ================================================ FILE: lua/lualine/extensions/mason.lua ================================================ -- lualine extension for mason.nvim local mason_registry local function maybe_set_registry() local ok, registry = pcall(require, 'mason-registry') if ok then mason_registry = registry end end local M = {} M.sections = { lualine_a = { function() return 'Mason' end, }, lualine_b = { function() maybe_set_registry() return 'Installed: ' .. #mason_registry.get_installed_packages() .. '/' .. #mason_registry.get_all_package_specs() end, }, } M.filetypes = { 'mason' } return M ================================================ FILE: lua/lualine/extensions/mundo.lua ================================================ local M = {} M.sections = { lualine_a = { function() local ft = vim.opt_local.filetype:get() return (ft == 'Mundo') and 'Change tree' or (ft == 'MundoDiff') and 'Change diff' end, }, lualine_y = { 'progress' }, lualine_z = { 'location' }, } M.filetypes = { 'Mundo', 'MundoDiff', } return M ================================================ FILE: lua/lualine/extensions/neo-tree.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local nerdtree = require('lualine.extensions.nerdtree') local M = {} M.sections = vim.deepcopy(nerdtree.sections) M.filetypes = { 'neo-tree' } return M ================================================ FILE: lua/lualine/extensions/nerdtree.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local function get_short_cwd() return vim.fn.fnamemodify(vim.fn.getcwd(), ':~') end local M = {} M.sections = { lualine_a = { get_short_cwd }, } M.filetypes = { 'nerdtree' } return M ================================================ FILE: lua/lualine/extensions/nvim-dap-ui.lua ================================================ -- MIT license, see LICENSE for more details. -- Extension for nvim-dap-ui local M = {} M.sections = { lualine_a = { { 'filename', file_status = false } }, } M.filetypes = { 'dap-repl', 'dapui_console', 'dapui_watches', 'dapui_stacks', 'dapui_breakpoints', 'dapui_scopes', } return M ================================================ FILE: lua/lualine/extensions/nvim-tree.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local nerdtree = require('lualine.extensions.nerdtree') local M = {} M.sections = vim.deepcopy(nerdtree.sections) M.filetypes = { 'NvimTree' } return M ================================================ FILE: lua/lualine/extensions/oil.lua ================================================ -- Extension for oil.nvim local M = {} M.sections = { lualine_a = { function() local ok, oil = pcall(require, 'oil') if ok then return vim.fn.fnamemodify(oil.get_current_dir(), ':~') else return '' end end, }, } M.filetypes = { 'oil' } return M ================================================ FILE: lua/lualine/extensions/overseer.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local M = {} M.sections = { lualine_a = { function() return 'OverseerList' end, }, } M.filetypes = { 'OverseerList' } return M ================================================ FILE: lua/lualine/extensions/quickfix.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -- local function is_loclist() return vim.fn.getloclist(0, { filewinid = 1 }).filewinid ~= 0 end local function label() return is_loclist() and 'Location List' or 'Quickfix List' end local function title() if is_loclist() then return vim.fn.getloclist(0, { title = 0 }).title end return vim.fn.getqflist({ title = 0 }).title end local M = {} function M.init() -- Make sure ft wf doesn't create a custom statusline vim.g.qf_disable_statusline = true end M.sections = { lualine_a = { label }, lualine_b = { title }, lualine_z = { 'location' }, } M.filetypes = { 'qf' } return M ================================================ FILE: lua/lualine/extensions/symbols-outline.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local M = {} M.sections = { lualine_a = { 'filetype' } } M.filetypes = { 'Outline' } return M ================================================ FILE: lua/lualine/extensions/toggleterm.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local function toggleterm_statusline() return 'ToggleTerm #' .. vim.b.toggle_number end local M = {} M.sections = { lualine_a = { toggleterm_statusline }, } M.filetypes = { 'toggleterm' } return M ================================================ FILE: lua/lualine/extensions/trouble.lua ================================================ local M = {} ---Format mode, eg: lsp_document_symbols -> Lsp Document Symbols ---@param mode string ---@return string local function _format_mode(mode) local words = vim.split(mode, '[%W]') for i, word in ipairs(words) do words[i] = word:sub(1, 1):upper() .. word:sub(2) end return table.concat(words, ' ') end local function get_trouble_mode() local opts = require('trouble.config').options if opts ~= nil and opts.mode ~= nil then return _format_mode(opts.mode) end local win = vim.api.nvim_get_current_win() if vim.w[win] ~= nil then local trouble = vim.w[win].trouble if trouble ~= nil and trouble.mode ~= nil then return _format_mode(trouble.mode) end end return '' end M.sections = { lualine_a = { get_trouble_mode, }, } M.filetypes = { 'trouble', 'Trouble' } return M ================================================ FILE: lua/lualine/highlight.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local M = {} local lualine_require = require('lualine_require') local require = lualine_require.require local modules = lualine_require.lazy_require { utils = 'lualine.utils.utils', color_utils = 'lualine.utils.color_utils', } local section_highlight_map = { x = 'c', y = 'b', z = 'a' } local active_theme = nil local theme_hls = {} local create_cterm_colors = false -- table to store the highlight names created by lualine local loaded_highlights = {} -- table to map mode to highlight suffixes local mode_to_highlight = { ['VISUAL'] = '_visual', ['V-BLOCK'] = '_visual', ['V-LINE'] = '_visual', ['SELECT'] = '_visual', ['S-LINE'] = '_visual', ['S-BLOCK'] = '_visual', ['REPLACE'] = '_replace', ['V-REPLACE'] = '_replace', ['INSERT'] = '_insert', ['COMMAND'] = '_command', ['EX'] = '_command', ['MORE'] = '_command', ['CONFIRM'] = '_command', ['TERMINAL'] = '_terminal', } --- Get highlight suffix for current mode, or inactive if not focused ---@return string mode_suffix function M.get_mode_suffix() local mode = require('lualine.utils.mode').get_mode() return mode_to_highlight[mode] or '_normal' end --- determine if an highlight exist and isn't cleared ---@param highlight_name string ---@return boolean whether hl_group was defined with highlight_name function M.highlight_exists(highlight_name) return loaded_highlights[highlight_name] or false end --- Creates lualine owned Normal mirror. Used for transparent background local function create_transparent_hlgroup() local base_color = modules.utils.extract_highlight_colors('Normal') if base_color.reverse then base_color.fg, base_color.bg = base_color.bg, base_color.fg end M.highlight('lualine_transparent', base_color.fg, base_color.bg, nil, nil) end --- clears loaded_highlights table and highlights local function clear_highlights() for highlight_name, _ in pairs(loaded_highlights) do vim.cmd('highlight clear ' .. highlight_name) end loaded_highlights = {} create_transparent_hlgroup() end ---converts cterm, color_name type colors to #rrggbb format ---@param color string|number ---@return string local function sanitize_color(color) if color == nil or color == '' or (type(color) == 'string' and color:lower() == 'none') then return 'None' end if type(color) == 'string' then if color:sub(1, 1) == '#' then return color end -- RGB value return modules.color_utils.color_name2rgb(color) elseif type(color) == 'number' then if color > 255 then error("What's this it can't be higher then 255 and you've given " .. color) end return modules.color_utils.cterm2rgb(color) end end ---converts color_name type colors to cterm format and let cterm color pass through ---@param color string|number ---@return string local function sanitize_color_for_cterm(color) if type(color) == 'number' then if color > 255 then error("What's this it can't be higher then 255 and you've given " .. color) end return color end return modules.color_utils.rgb2cterm(sanitize_color(color)) end function M.get_lualine_hl(name) local hl = loaded_highlights[name] if hl and not hl.empty then if hl.link then return modules.utils.extract_highlight_colors(hl.link) end local hl_def = { fg = hl.fg ~= 'None' and vim.deepcopy(hl.fg) or nil, bg = hl.bg ~= 'None' and vim.deepcopy(hl.bg) or nil, sp = hl.sp ~= 'None' and vim.deepcopy(hl.sp) or nil, } if hl.gui then for _, flag in ipairs(vim.split(hl.gui, ',')) do if flag ~= 'None' then hl_def[flag] = true end end end return hl_def end end --- Define a hl_group ---@param name string ---@param foreground string|number: color ---@param background string|number: color ---@param gui table cterm/gui options like bold/italic etc. ---@param link string hl_group name to link new hl to function M.highlight(name, foreground, background, gui, link) local command = { 'highlight!' } if link and #link > 0 then if loaded_highlights[name] and loaded_highlights[name].link == link then return end vim.list_extend(command, { 'link', name, link }) else local foreground_rgb = sanitize_color(foreground) local background_rgb = sanitize_color(background) gui = gui or '' if string.find(gui, 'nocombine') == nil then gui = gui ~= '' and gui .. ',nocombine' or 'nocombine' end if loaded_highlights[name] and loaded_highlights[name].fg == foreground_rgb and loaded_highlights[name].bg == background_rgb and loaded_highlights[name].gui == gui then return -- color is already defined why are we doing this anyway ? end table.insert(command, name) table.insert(command, 'guifg=' .. foreground_rgb) table.insert(command, 'guibg=' .. background_rgb) table.insert(command, 'gui=' .. gui) if create_cterm_colors then -- Not setting color from xxxground_rgb to let possible user 256 number through table.insert(command, 'ctermfg=' .. sanitize_color_for_cterm(foreground)) table.insert(command, 'ctermbg=' .. sanitize_color_for_cterm(background)) table.insert(command, 'cterm=' .. gui) end end vim.cmd(table.concat(command, ' ')) -- update attached hl groups local old_hl_def = loaded_highlights[name] if old_hl_def and next(old_hl_def.attached) then -- Update attached hl groups as they announced to depend on hl_group 'name' -- 'hl' being in 'name's attached table means 'hl' -- depends of 'name'. -- 'hl' key in attached table will contain a table that -- defines the relation between 'hl' & 'name'. -- name.attached.hl = { bg = 'fg' } means -- hl's fg is same as 'names' bg . So 'hl's fg should -- be updated when ever 'name' changes it's 'bg' local bg_changed = old_hl_def.bg ~= background local fg_changed = old_hl_def.bg ~= foreground local gui_changed = old_hl_def.gui ~= gui for attach_name, attach_desc in pairs(old_hl_def.attached) do if bg_changed and attach_desc.bg and loaded_highlights[attach_name] then M.highlight( attach_name, attach_desc.bg == 'fg' and background or loaded_highlights[attach_name].fg, attach_desc.bg == 'bg' and background or loaded_highlights[attach_name].bg, loaded_highlights[attach_name].gui, loaded_highlights[attach_name].link ) end if fg_changed and attach_desc.fg and loaded_highlights[attach_name] then M.highlight( attach_name, attach_desc.fg == 'fg' and foreground or loaded_highlights[attach_name].fg, attach_desc.fg == 'bg' and foreground or loaded_highlights[attach_name].bg, loaded_highlights[attach_name].gui, loaded_highlights[attach_name].link ) end if gui_changed and attach_desc.gui and loaded_highlights[attach_name] then M.highlight( attach_name, loaded_highlights[attach_name].fg, loaded_highlights[attach_name].bg, gui, loaded_highlights[attach_name].link ) end end end -- store current hl state loaded_highlights[name] = { fg = foreground, bg = background, gui = gui, link = link, attached = old_hl_def and old_hl_def.attached or {}, } end ---Attach a hl to another, so the attached auto updates on change to hl that it's attached too. ---@param provider string the hl receiver is getting attached to ---@param receiver string the hl that will be auto updated upon change to provider ---@param provider_el_type string (fg/bg) what element receiver relates to of provider ---@param receiver_el_type string (fg/bg) what element provider relates to of receiver local function attach_hl(provider, receiver, provider_el_type, receiver_el_type) if loaded_highlights[provider] == nil then loaded_highlights[provider] = { empty = true, attached = {} } end loaded_highlights[provider].attached[receiver] = { [provider_el_type] = receiver_el_type } end ---define hl_groups for a theme ---@param theme table function M.create_highlight_groups(theme) clear_highlights() active_theme = theme theme_hls = {} local psudo_options = { self = { section = 'a' } } create_cterm_colors = not vim.go.termguicolors for mode, sections in pairs(theme) do theme_hls[mode] = {} for section, color in pairs(sections) do local hl_tag = mode psudo_options.self.section = section theme_hls[mode][section] = M.create_component_highlight_group(color, hl_tag, psudo_options, true) end end end ---@description: adds '_mode' at end of highlight_group ---@param highlight_group string name of highlight group ---@return string highlight group name with mode local function append_mode(highlight_group, is_focused) if is_focused == nil then is_focused = modules.utils.is_focused() end if is_focused == false then return highlight_group .. '_inactive' end return highlight_group .. M.get_mode_suffix() end -- Helper function for create component highlight ---Handles fall back of colors when creating highlight group ---@param hl_name string name of highlight that we are setting default values for ---@param mode string mode which default component color should be given. ---@param section string the lualine section component is in. ---@param color table color passed for creating component highlight ---@param options table Options table of component this is first fall back local function get_default_component_color(hl_name, mode, section, color, options) local default_theme_color if active_theme[mode] and active_theme[mode][section] then default_theme_color = active_theme[mode][section] elseif section >= 'c' and active_theme[mode] and active_theme[mode][section_highlight_map[section]] then default_theme_color = active_theme[mode][section_highlight_map[section]] elseif section >= 'c' and active_theme.normal[section_highlight_map[section]] then default_theme_color = active_theme.normal[section_highlight_map[section]] else default_theme_color = active_theme.normal[section] end local ret = { fg = color.fg, bg = color.bg, gui = color.gui } if ret.fg and ret.bg then return ret end local function apply_default(def_color, def_name) if type(def_color) == 'function' and loaded_highlights[def_name] and not loaded_highlights[def_name].empty then if loaded_highlights[def_name].link then def_color = loaded_highlights[def_name].link else def_color = loaded_highlights[def_name] end end if type(def_color) == 'function' then def_color = def_color { section = section } end if type(def_color) == 'string' then def_color = modules.utils.extract_highlight_colors(def_color) end if type(def_color) == 'table' then if not ret.fg then ret.fg = def_color.fg attach_hl(def_name, hl_name, 'fg', 'fg') end if not ret.bg then ret.bg = def_color.bg attach_hl(def_name, hl_name, 'bg', 'bg') end end end if options.color and options.color_highlight and options.color_highlight.name and options.color_highlight.name .. '_' .. mode ~= hl_name then apply_default(options.color, options.color_highlight.name .. '_' .. mode) end if not ret.fg or not ret.bg then apply_default(default_theme_color, string.format('lualine_%s_%s', section, mode)) end ret.fg = sanitize_color(ret.fg) ret.bg = sanitize_color(ret.bg) return ret end ---Create highlight group with fg bg and gui from theme ---@param color table has to be { fg = "#rrggbb", bg="#rrggbb" gui = "effect" } --- all the color elements are optional if fg or bg is not given options --- must be provided So fg and bg can default the themes colors ---@param highlight_tag string is unique tag for highlight group ---returns the name of highlight group ---@param options table is parameter of component.init() function ---@return table that can be used by component_format_highlight --- to retrieve highlight group function M.create_component_highlight_group(color, highlight_tag, options, apply_no_default) local section = options.self.section local tag_id = 1 local highlight_tag_counted = highlight_tag while M.highlight_exists(table.concat({ 'lualine', section, highlight_tag_counted }, '_')) or (section and M.highlight_exists(table.concat({ 'lualine', section, highlight_tag_counted, 'normal' }, '_'))) do highlight_tag_counted = highlight_tag .. '_' .. tostring(tag_id) tag_id = tag_id + 1 end highlight_tag = highlight_tag_counted if type(color) == 'string' then local highlight_group_name = table.concat({ 'lualine', section, highlight_tag }, '_') M.highlight(highlight_group_name, nil, nil, nil, color) -- link to group return { name = highlight_group_name, fn = nil, no_mode = true, link = true, section = section, options = options, no_default = apply_no_default, } end if type(color) ~= 'function' and (apply_no_default or (color.bg and color.fg)) then -- When bg and fg are both present we don't need to set highlights for -- each mode as they will surely look the same. So we can work without options local highlight_group_name = table.concat({ 'lualine', section, highlight_tag }, '_') M.highlight(highlight_group_name, color.fg, color.bg, color.gui, nil) return { name = highlight_group_name, fn = nil, no_mode = true, section = section, options = options, no_default = apply_no_default, } end local modes = { 'normal', 'insert', 'visual', 'replace', 'command', 'terminal', 'inactive', } for _, mode in ipairs(modes) do local hl_name = table.concat({ 'lualine', section, highlight_tag, mode }, '_') local cl = color if type(color) == 'function' then cl = color { section = section } or {} end if type(cl) == 'string' then cl = { link = cl } else cl = get_default_component_color(hl_name, mode, section, cl, options) end M.highlight(hl_name, cl.fg, cl.bg, cl.gui, cl.link) end return { name = table.concat({ 'lualine', section, highlight_tag }, '_'), fn = type(color) == 'function' and color, no_mode = false, link = false, section = section, options = options, no_default = apply_no_default, } end ---@description: retrieve highlight_groups for components ---@param highlight table return value of create_component_highlight_group --- return value of create_component_highlight_group is to be passed in --- this parameter to receive highlight that was created ---@return string formatted highlight group name function M.component_format_highlight(highlight, is_focused) if not highlight.fn then local highlight_group = highlight.name if highlight.no_mode then return '%#' .. highlight_group .. '#' end highlight_group = append_mode(highlight_group, is_focused) return '%#' .. highlight_group .. '#' else local color = highlight.fn { section = highlight.section } or {} local hl_name = highlight.name if type(color) == 'string' then M.highlight(hl_name .. M.get_mode_suffix(), nil, nil, nil, color) return '%#' .. hl_name .. M.get_mode_suffix() .. '#' elseif type(color) == 'table' then if not highlight.no_default and not (color.fg and color.bg) then hl_name = append_mode(highlight.name, is_focused) color = get_default_component_color(hl_name, M.get_mode_suffix():sub(2), highlight.section, color, highlight.options) end M.highlight(hl_name, color.fg, color.bg, color.gui, color.link) return '%#' .. hl_name .. '#', color end end end ---@description: retrieve highlight_groups for section ---@param section string highlight group name without mode --- return value of create_component_highlight_group is to be passed in --- this parameter to receive highlight that was created ---@param is_focused boolean ---@return string formatted highlight group name function M.format_highlight(section, is_focused) local mode = append_mode('', is_focused):sub(2) local ret = '' if theme_hls[mode] and theme_hls[mode][section] then ret = M.component_format_highlight(theme_hls[mode][section], is_focused) elseif theme_hls[mode] and section > 'c' and theme_hls[mode][section_highlight_map[section]] then ret = M.component_format_highlight(theme_hls[mode][section_highlight_map[section]], is_focused) elseif theme_hls['normal'] and theme_hls['normal'][section] then ret = M.component_format_highlight(theme_hls['normal'][section], is_focused) elseif theme_hls['normal'] and section > 'c' and theme_hls['normal'][section_highlight_map[section]] then ret = M.component_format_highlight(theme_hls['normal'][section_highlight_map[section]], is_focused) end return ret end ---@description : Provides transitional highlights for section separators. ---@param left_hl string this highlights bg is used for fg of transitional hl ---@param right_hl string this highlights bg is used for bg of transitional hl --- '▶️' and '◀️' ' needs reverse colors so the caller should swap left and right ---@return string formatted highlight group name function M.get_transitional_highlights(left_hl, right_hl) -- When both left and right highlights are same or one is absent -- nothing to transition to. if left_hl == nil or right_hl == nil or left_hl == right_hl then return nil end -- construct the name of highlight group local highlight_name = table.concat({ 'lualine_transitional', left_hl, 'to', right_hl }, '_') if not M.highlight_exists(highlight_name) then -- Create the highlight_group if needed -- Get colors from highlights local fg = modules.utils.extract_highlight_colors(left_hl, 'bg') local bg = modules.utils.extract_highlight_colors(right_hl, 'bg') if not fg and not bg then return nil -- Color retrieval failed end if bg == fg then return nil -- Separator won't be visible anyway end M.highlight(highlight_name, fg, bg, nil, nil) attach_hl(left_hl, highlight_name, 'bg', 'fg') attach_hl(right_hl, highlight_name, 'bg', 'bg') end return '%#' .. highlight_name .. '#' end function M.get_stl_default_hl(focused) if focused == 3 then return 'TabLineFill' elseif not focused then return 'StatusLineNC' else return 'StatusLine' end end return M ================================================ FILE: lua/lualine/themes/16color.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit itchyny, jackno (lightline) -- stylua: ignore local colors = { black = '#000000', maroon = '#800000', green = '#008000', olive = '#808000', navy = '#000080', purple = '#800080', teal = '#008080', silver = '#c0c0c0', gray = '#808080', red = '#ff0000', lime = '#00ff00', yellow = '#ffff00', blue = '#0000ff', fuchsia = '#ff00ff', aqua = '#00ffff', white = '#ffffff', } return { normal = { a = { fg = colors.white, bg = colors.blue, gui = 'bold' }, b = { fg = colors.white, bg = colors.gray }, c = { fg = colors.silver, bg = colors.black }, }, insert = { a = { fg = colors.white, bg = colors.green, gui = 'bold' } }, visual = { a = { fg = colors.white, bg = colors.purple, gui = 'bold' } }, replace = { a = { fg = colors.white, bg = colors.red, gui = 'bold' } }, inactive = { a = { fg = colors.silver, bg = colors.gray, gui = 'bold' }, b = { fg = colors.gray, bg = colors.black }, c = { fg = colors.silver, bg = colors.black }, }, } ================================================ FILE: lua/lualine/themes/OceanicNext.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color0 = '#ffffff', color1 = '#99c794', color2 = '#65737e', color3 = '#343d46', color4 = '#6699cc', color5 = '#d8dee9', color6 = '#f99157', color7 = '#ec5f67', } return { insert = { a = { fg = colors.color0, bg = colors.color1, gui = 'bold' }, b = { fg = colors.color0, bg = colors.color2 }, c = { fg = colors.color0, bg = colors.color3 }, }, normal = { a = { fg = colors.color0, bg = colors.color4, gui = 'bold' }, b = { fg = colors.color0, bg = colors.color2 }, c = { fg = colors.color0, bg = colors.color3 }, }, inactive = { a = { fg = colors.color5, bg = colors.color2, gui = 'bold' }, b = { fg = colors.color5, bg = colors.color3 }, c = { fg = colors.color2, bg = colors.color3 }, }, visual = { a = { fg = colors.color0, bg = colors.color6, gui = 'bold' }, b = { fg = colors.color0, bg = colors.color2 }, c = { fg = colors.color0, bg = colors.color3 }, }, replace = { a = { fg = colors.color0, bg = colors.color7, gui = 'bold' }, b = { fg = colors.color0, bg = colors.color2 }, c = { fg = colors.color0, bg = colors.color3 }, }, } ================================================ FILE: lua/lualine/themes/PaperColor.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: itchyny(lightline) local background = vim.opt.background:get() return require('lualine.themes.papercolor_' .. background) ================================================ FILE: lua/lualine/themes/Tomorrow.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color14 = '#718c00', color0 = '#666666', color1 = '#c8c8c8', color2 = '#808080', color3 = '#fafafa', color4 = '#4271ae', color5 = '#4d4d4c', color6 = '#b4b4b4', color7 = '#555555', color8 = '#8959a8', color11 = '#f5871f', } return { inactive = { a = { fg = colors.color0, bg = colors.color1, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, c = { fg = colors.color0, bg = colors.color1 }, }, normal = { a = { fg = colors.color1, bg = colors.color4, gui = 'bold' }, b = { fg = colors.color5, bg = colors.color6 }, c = { fg = colors.color7, bg = colors.color1 }, }, visual = { a = { fg = colors.color1, bg = colors.color8, gui = 'bold' }, b = { fg = colors.color5, bg = colors.color6 }, }, replace = { a = { fg = colors.color1, bg = colors.color11, gui = 'bold' }, b = { fg = colors.color5, bg = colors.color6 }, }, insert = { a = { fg = colors.color1, bg = colors.color14, gui = 'bold' }, b = { fg = colors.color5, bg = colors.color6 }, }, } ================================================ FILE: lua/lualine/themes/auto.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local utils = require('lualine.utils.utils') local loader = require('lualine.utils.loader') local color_name = vim.g.colors_name if color_name then -- All base16 colorschemes share the same theme if 'base16' == color_name:sub(1, 6) then color_name = 'base16' end -- Check if there's a theme for current colorscheme -- If there is load that instead of generating a new one local ok, theme = pcall(loader.load_theme, color_name) if ok and theme then return theme end end --------------- -- Constants -- --------------- -- fg and bg must have this much contrast range 0 < contrast_threshold < 0.5 local contrast_threshold = 0.3 -- how much brightness is changed in percentage for light and dark themes local brightness_modifier_parameter = 10 -- Turns #rrggbb -> { red, green, blue } local function rgb_str2num(rgb_color_str) if rgb_color_str:find('#') == 1 then rgb_color_str = rgb_color_str:sub(2, #rgb_color_str) end local red = tonumber(rgb_color_str:sub(1, 2), 16) local green = tonumber(rgb_color_str:sub(3, 4), 16) local blue = tonumber(rgb_color_str:sub(5, 6), 16) return { red = red, green = green, blue = blue } end -- Turns { red, green, blue } -> #rrggbb local function rgb_num2str(rgb_color_num) local rgb_color_str = string.format('#%02x%02x%02x', rgb_color_num.red, rgb_color_num.green, rgb_color_num.blue) return rgb_color_str end -- Returns brightness level of color in range 0 to 1 -- arbitrary value it's basically an weighted average local function get_color_brightness(rgb_color) local color = rgb_str2num(rgb_color) local brightness = (color.red * 2 + color.green * 3 + color.blue) / 6 return brightness / 256 end -- returns average of colors in range 0 to 1 -- used to determine contrast level local function get_color_avg(rgb_color) local color = rgb_str2num(rgb_color) return (color.red + color.green + color.blue) / 3 / 256 end -- Clamps the val between left and right local function clamp(val, left, right) if val > right then return right end if val < left then return left end return val end -- Changes brightness of rgb_color by percentage local function brightness_modifier(rgb_color, percentage) local color = rgb_str2num(rgb_color) color.red = clamp(color.red + (color.red * percentage / 100), 0, 255) color.green = clamp(color.green + (color.green * percentage / 100), 0, 255) color.blue = clamp(color.blue + (color.blue * percentage / 100), 0, 255) return rgb_num2str(color) end -- Changes contrast of rgb_color by amount local function contrast_modifier(rgb_color, amount) local color = rgb_str2num(rgb_color) color.red = clamp(color.red + amount, 0, 255) color.green = clamp(color.green + amount, 0, 255) color.blue = clamp(color.blue + amount, 0, 255) return rgb_num2str(color) end -- Changes brightness of foreground color to achieve contrast -- without changing the color local function apply_contrast(highlight) local highlight_bg_avg = get_color_avg(highlight.bg) local contrast_threshold_config = clamp(contrast_threshold, 0, 0.5) local contrast_change_step = 5 if highlight_bg_avg > 0.5 then contrast_change_step = -contrast_change_step end -- Don't waste too much time here max 25 iteration should be more than enough local iteration_count = 1 while math.abs(get_color_avg(highlight.fg) - highlight_bg_avg) < contrast_threshold_config and iteration_count < 25 do highlight.fg = contrast_modifier(highlight.fg, contrast_change_step) iteration_count = iteration_count + 1 end end -- Get the colors to create theme -- stylua: ignore local colors = { normal = utils.extract_color_from_hllist('bg', { 'PmenuSel', 'PmenuThumb', 'TabLineSel' }, '#000000'), insert = utils.extract_color_from_hllist('fg', { 'String', 'MoreMsg' }, '#000000'), replace = utils.extract_color_from_hllist('fg', { 'Number', 'Type' }, '#000000'), visual = utils.extract_color_from_hllist('fg', { 'Special', 'Boolean', 'Constant' }, '#000000'), command = utils.extract_color_from_hllist('fg', { 'Identifier' }, '#000000'), back1 = utils.extract_color_from_hllist('bg', { 'Normal', 'StatusLineNC' }, '#000000'), fore = utils.extract_color_from_hllist('fg', { 'Normal', 'StatusLine' }, '#000000'), back2 = utils.extract_color_from_hllist('bg', { 'StatusLine' }, '#000000'), } -- Change brightness of colors -- Darken if light theme (or) Lighten if dark theme local normal_color = utils.extract_highlight_colors('Normal', 'bg') if normal_color ~= nil then if get_color_brightness(normal_color) > 0.5 then brightness_modifier_parameter = -brightness_modifier_parameter end for name, color in pairs(colors) do colors[name] = brightness_modifier(color, brightness_modifier_parameter) end end -- Basic theme definition local M = { normal = { a = { bg = colors.normal, fg = colors.back1, gui = 'bold' }, b = { bg = colors.back1, fg = colors.normal }, c = { bg = colors.back2, fg = colors.fore }, }, insert = { a = { bg = colors.insert, fg = colors.back1, gui = 'bold' }, b = { bg = colors.back1, fg = colors.insert }, c = { bg = colors.back2, fg = colors.fore }, }, replace = { a = { bg = colors.replace, fg = colors.back1, gui = 'bold' }, b = { bg = colors.back1, fg = colors.replace }, c = { bg = colors.back2, fg = colors.fore }, }, visual = { a = { bg = colors.visual, fg = colors.back1, gui = 'bold' }, b = { bg = colors.back1, fg = colors.visual }, c = { bg = colors.back2, fg = colors.fore }, }, command = { a = { bg = colors.command, fg = colors.back1, gui = 'bold' }, b = { bg = colors.back1, fg = colors.command }, c = { bg = colors.back2, fg = colors.fore }, }, } M.terminal = M.command M.inactive = M.normal -- Apply proper contrast so text is readable for _, section in pairs(M) do for _, highlight in pairs(section) do apply_contrast(highlight) end end return M ================================================ FILE: lua/lualine/themes/ayu.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: itchyny(lightline) -- License: MIT License local background = vim.opt.background:get() local style = vim.g.ayucolor or ((background == 'dark') and vim.g.ayuprefermirage and 'mirage' or background) return require('lualine.themes.ayu_' .. style) ================================================ FILE: lua/lualine/themes/ayu_dark.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color2 = '#0f1419', color3 = '#ffee99', color4 = '#e6e1cf', color5 = '#14191f', color13 = '#b8cc52', color10 = '#36a3d9', color8 = '#f07178', color9 = '#3e4b59', } return { visual = { a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, replace = { a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, inactive = { c = { fg = colors.color4, bg = colors.color2 }, a = { fg = colors.color4, bg = colors.color5, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, normal = { c = { fg = colors.color9, bg = colors.color2 }, a = { fg = colors.color2, bg = colors.color10, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, insert = { a = { fg = colors.color2, bg = colors.color13, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, } ================================================ FILE: lua/lualine/themes/ayu_light.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color2 = '#f3f3f3', color3 = '#A37ACC', color4 = '#5C6773', color5 = '#d3d3d3', color13 = '#86B300', color10 = '#59c2ff', color8 = '#f07178', color9 = '#828C99', } return { visual = { a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, replace = { a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, inactive = { c = { fg = colors.color4, bg = colors.color2 }, a = { fg = colors.color4, bg = colors.color5, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, normal = { c = { fg = colors.color9, bg = colors.color2 }, a = { fg = colors.color2, bg = colors.color10, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, insert = { a = { fg = colors.color2, bg = colors.color13, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, } ================================================ FILE: lua/lualine/themes/ayu_mirage.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color2 = '#242b38', color3 = '#d4bfff', color4 = '#d9d7ce', color5 = '#272d38', color13 = '#bbe67e', color10 = '#59c2ff', color8 = '#f07178', color9 = '#607080', } return { visual = { a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, replace = { a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, inactive = { c = { fg = colors.color4, bg = colors.color2 }, a = { fg = colors.color4, bg = colors.color5, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, normal = { c = { fg = colors.color9, bg = colors.color2 }, a = { fg = colors.color2, bg = colors.color10, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, insert = { a = { fg = colors.color2, bg = colors.color13, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, } ================================================ FILE: lua/lualine/themes/base16.lua ================================================ local modules = require('lualine_require').lazy_require { notices = 'lualine.utils.notices' } local function add_notice(notice) modules.notices.add_notice('theme(base16): ' .. notice) end local function setup(colors) local theme = { normal = { a = { fg = colors.bg, bg = colors.normal }, b = { fg = colors.light_fg, bg = colors.alt_bg }, c = { fg = colors.fg, bg = colors.bg }, }, replace = { a = { fg = colors.bg, bg = colors.replace }, b = { fg = colors.light_fg, bg = colors.alt_bg }, }, insert = { a = { fg = colors.bg, bg = colors.insert }, b = { fg = colors.light_fg, bg = colors.alt_bg }, }, visual = { a = { fg = colors.bg, bg = colors.visual }, b = { fg = colors.light_fg, bg = colors.alt_bg }, }, inactive = { a = { fg = colors.dark_fg, bg = colors.bg }, b = { fg = colors.dark_fg, bg = colors.bg }, c = { fg = colors.dark_fg, bg = colors.bg }, }, } theme.command = theme.normal theme.terminal = theme.insert return theme end local function setup_default() return setup { bg = '#282a2e', alt_bg = '#373b41', dark_fg = '#969896', fg = '#b4b7b4', light_fg = '#c5c8c6', normal = '#81a2be', insert = '#b5bd68', visual = '#b294bb', replace = '#de935f', } end local function setup_base16_nvim() -- Continue to load nvim-base16 local loaded, base16 = pcall(require, 'base16-colorscheme') if not loaded then add_notice( 'nvim-base16 is not currently present in your runtimepath, make sure it is properly installed,' .. ' fallback to default colors.' ) return nil end if not base16.colors and not vim.env.BASE16_THEME then add_notice( 'nvim-base16 is not loaded yet, you should update your configuration to load it before lualine' .. ' so that the colors from your colorscheme can be used, fallback to "tomorrow-night" theme.' ) elseif not base16.colors and not base16.colorschemes[vim.env.BASE16_THEME] then add_notice( 'The colorscheme "%s" defined by the environment variable "BASE16_THEME" is not handled by' .. ' nvim-base16, fallback to "tomorrow-night" theme.' ) end local colors = base16.colors or base16.colorschemes[vim.env.BASE16_THEME or 'tomorrow-night'] return setup { bg = colors.base01, alt_bg = colors.base02, dark_fg = colors.base03, fg = colors.base04, light_fg = colors.base05, normal = colors.base0D, insert = colors.base0B, visual = colors.base0E, replace = colors.base09, } end local function setup_base16_vim() -- Check if tinted-theming/base16-vim is already loaded if vim.g.base16_gui00 and vim.g.base16_gui0F then return setup { bg = vim.g.base16_gui01, alt_bg = vim.g.base16_gui02, dark_fg = vim.g.base16_gui03, fg = vim.g.base16_gui04, light_fg = vim.g.base16_gui05, normal = vim.g.base16_gui0D, insert = vim.g.base16_gui0B, visual = vim.g.base16_gui0E, replace = vim.g.base16_gui09, } end -- base16-vim has been renamed to tinted-vim along with colors -- context: https://github.com/nvim-lualine/lualine.nvim/pull/1352 if vim.g.tinted_gui00 and vim.g.tinted_gui0F then return setup { bg = vim.g.tinted_gui01, alt_bg = vim.g.tinted_gui02, dark_fg = vim.g.tinted_gui03, fg = vim.g.tinted_gui04, light_fg = vim.g.tinted_gui05, normal = vim.g.tinted_gui0D, insert = vim.g.tinted_gui0B, visual = vim.g.tinted_gui0E, replace = vim.g.tinted_gui09, } end return nil end return setup_base16_vim() or setup_base16_nvim() or setup_default() ================================================ FILE: lua/lualine/themes/codedark.lua ================================================ -- Copyright (c) 2020-2021 Shatur95 -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { gray = '#3C3C3C', lightred = '#D16969', blue = '#569CD6', pink = '#C586C0', black = '#262626', white = '#D4D4D4', green = '#608B4E', } return { normal = { b = { fg = colors.green, bg = colors.black }, a = { fg = colors.black, bg = colors.green, gui = 'bold' }, c = { fg = colors.white, bg = colors.black }, }, visual = { b = { fg = colors.pink, bg = colors.black }, a = { fg = colors.black, bg = colors.pink, gui = 'bold' }, }, inactive = { b = { fg = colors.black, bg = colors.blue }, a = { fg = colors.white, bg = colors.gray, gui = 'bold' }, }, replace = { b = { fg = colors.lightred, bg = colors.black }, a = { fg = colors.black, bg = colors.lightred, gui = 'bold' }, c = { fg = colors.white, bg = colors.black }, }, insert = { b = { fg = colors.blue, bg = colors.black }, a = { fg = colors.black, bg = colors.blue, gui = 'bold' }, c = { fg = colors.white, bg = colors.black }, }, } ================================================ FILE: lua/lualine/themes/dracula.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit itchyny, jackno (lightline) -- stylua: ignore local colors = { gray = '#44475a', lightgray = '#5f6a8e', orange = '#ffb86c', purple = '#bd93f9', red = '#ff5555', yellow = '#f1fa8c', green = '#50fa7b', white = '#f8f8f2', black = '#282a36', } return { normal = { a = { bg = colors.purple, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.gray, fg = colors.white }, }, insert = { a = { bg = colors.green, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.gray, fg = colors.white }, }, visual = { a = { bg = colors.yellow, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.gray, fg = colors.white }, }, replace = { a = { bg = colors.red, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.gray, fg = colors.white }, }, command = { a = { bg = colors.orange, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.gray, fg = colors.white }, }, inactive = { a = { bg = colors.gray, fg = colors.white, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.gray, fg = colors.white }, }, } ================================================ FILE: lua/lualine/themes/everforest.lua ================================================ -- Copyright (c) 2020-2021 gnuyent -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { bg0 = '#323d43', bg1 = '#3c474d', bg3 = '#505a60', fg = '#d8caac', aqua = '#87c095', green = '#a7c080', orange = '#e39b7b', purple = '#d39bb6', red = '#e68183', grey1 = '#868d80', } return { normal = { a = { bg = colors.green, fg = colors.bg0, gui = 'bold' }, b = { bg = colors.bg3, fg = colors.fg }, c = { bg = colors.bg1, fg = colors.fg }, }, insert = { a = { bg = colors.fg, fg = colors.bg0, gui = 'bold' }, b = { bg = colors.bg3, fg = colors.fg }, c = { bg = colors.bg1, fg = colors.fg }, }, visual = { a = { bg = colors.red, fg = colors.bg0, gui = 'bold' }, b = { bg = colors.bg3, fg = colors.fg }, c = { bg = colors.bg1, fg = colors.fg }, }, replace = { a = { bg = colors.orange, fg = colors.bg0, gui = 'bold' }, b = { bg = colors.bg3, fg = colors.fg }, c = { bg = colors.bg1, fg = colors.fg }, }, command = { a = { bg = colors.aqua, fg = colors.bg0, gui = 'bold' }, b = { bg = colors.bg3, fg = colors.fg }, c = { bg = colors.bg1, fg = colors.fg }, }, terminal = { a = { bg = colors.purple, fg = colors.bg0, gui = 'bold' }, b = { bg = colors.bg3, fg = colors.fg }, c = { bg = colors.bg1, fg = colors.fg }, }, inactive = { a = { bg = colors.bg1, fg = colors.grey1, gui = 'bold' }, b = { bg = colors.bg1, fg = colors.grey1 }, c = { bg = colors.bg1, fg = colors.grey1 }, }, } ================================================ FILE: lua/lualine/themes/gruvbox-material.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { fg1 = '#282828', color2 = '#504945', fg2 = '#ddc7a1', color3 = '#32302f', color4 = '#a89984', color5 = '#7daea3', color6 = '#a9b665', color7 = '#d8a657', color8 = '#d3869b', color9 = '#ea6962', } return { normal = { a = { fg = colors.fg1, bg = colors.color4, gui = 'bold' }, b = { fg = colors.fg2, bg = colors.color2 }, c = { fg = colors.fg2, bg = colors.color3 }, }, command = { a = { fg = colors.fg1, bg = colors.color5, gui = 'bold' } }, inactive = { a = { fg = colors.fg2, bg = colors.color2 } }, insert = { a = { fg = colors.fg1, bg = colors.color6, gui = 'bold' } }, replace = { a = { fg = colors.fg1, bg = colors.color7, gui = 'bold' } }, terminal = { a = { fg = colors.fg1, bg = colors.color8, gui = 'bold' } }, visual = { a = { fg = colors.fg1, bg = colors.color9, gui = 'bold' } }, } ================================================ FILE: lua/lualine/themes/gruvbox.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: itchyny(lightline) -- License: MIT License local background = vim.opt.background:get() return require('lualine.themes.gruvbox_' .. background) ================================================ FILE: lua/lualine/themes/gruvbox_dark.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { black = '#282828', white = '#ebdbb2', red = '#fb4934', green = '#b8bb26', blue = '#83a598', yellow = '#fe8019', gray = '#a89984', darkgray = '#3c3836', lightgray = '#504945', inactivegray = '#7c6f64', } return { normal = { a = { bg = colors.gray, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.darkgray, fg = colors.gray }, }, insert = { a = { bg = colors.blue, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.lightgray, fg = colors.white }, }, visual = { a = { bg = colors.yellow, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.inactivegray, fg = colors.black }, }, replace = { a = { bg = colors.red, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.black, fg = colors.white }, }, command = { a = { bg = colors.green, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.inactivegray, fg = colors.black }, }, inactive = { a = { bg = colors.darkgray, fg = colors.gray, gui = 'bold' }, b = { bg = colors.darkgray, fg = colors.gray }, c = { bg = colors.darkgray, fg = colors.gray }, }, } ================================================ FILE: lua/lualine/themes/gruvbox_light.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { black = '#3c3836', white = '#f9f5d7', orange = '#af3a03', green = '#427b58', blue = '#076678', gray = '#d5c4a1', darkgray = '#7c6f64', lightgray = '#ebdbb2', inactivegray = '#a89984' } return { normal = { a = { bg = colors.darkgray, fg = colors.white, gui = 'bold' }, b = { bg = colors.gray, fg = colors.darkgray }, c = { bg = colors.lightgray, fg = colors.darkgray }, }, insert = { a = { bg = colors.blue, fg = colors.white, gui = 'bold' }, b = { bg = colors.gray, fg = colors.darkgray }, c = { bg = colors.gray, fg = colors.black }, }, visual = { a = { bg = colors.orange, fg = colors.white, gui = 'bold' }, b = { bg = colors.gray, fg = colors.darkgray }, c = { bg = colors.darkgray, fg = colors.white }, }, replace = { a = { bg = colors.green, fg = colors.white, gui = 'bold' }, b = { bg = colors.gray, fg = colors.darkgray }, c = { bg = colors.gray, fg = colors.black }, }, command = { a = { bg = colors.darkgray, fg = colors.white, gui = 'bold' }, b = { bg = colors.gray, fg = colors.darkgray }, c = { bg = colors.lightgray, fg = colors.darkgray }, }, inactive = { a = { bg = colors.lightgray, fg = colors.inactivegray }, b = { bg = colors.lightgray, fg = colors.inactivegray }, c = { bg = colors.lightgray, fg = colors.inactivegray }, }, } ================================================ FILE: lua/lualine/themes/horizon.lua ================================================ -- Copyright (c) 2021 Jnhtr -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { black = '#1c1e26', white = '#6C6F93', red = '#F43E5C', green = '#09F7A0', blue = '#25B2BC', yellow = '#F09383', gray = '#E95678', darkgray = '#1A1C23', lightgray = '#2E303E', inactivegray = '#1C1E26', } return { normal = { a = { bg = colors.gray, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.darkgray, fg = colors.white }, }, insert = { a = { bg = colors.blue, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.darkgray, fg = colors.white }, }, visual = { a = { bg = colors.yellow, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.darkgray, fg = colors.white }, }, replace = { a = { bg = colors.red, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.darkgray, fg = colors.white }, }, command = { a = { bg = colors.green, fg = colors.black, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.white }, c = { bg = colors.darkgray, fg = colors.white }, }, inactive = { a = { bg = colors.inactivegray, fg = colors.lightgray, gui = 'bold' }, b = { bg = colors.inactivegray, fg = colors.lightgray }, c = { bg = colors.inactivegray, fg = colors.lightgray }, }, } ================================================ FILE: lua/lualine/themes/iceberg.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: itchyny(lightline) -- License: MIT License local background = vim.opt.background:get() return require('lualine.themes.iceberg_' .. background) ================================================ FILE: lua/lualine/themes/iceberg_dark.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color2 = '#161821', color3 = '#b4be82', color4 = '#c6c8d1', color5 = '#2e313f', color8 = '#e2a478', color9 = '#3e445e', color10 = '#0f1117', color11 = '#17171b', color12 = '#818596', color15 = '#84a0c6', } return { visual = { a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, replace = { a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, inactive = { a = { fg = colors.color9, bg = colors.color10, gui = 'bold' }, b = { fg = colors.color9, bg = colors.color10 }, c = { fg = colors.color9, bg = colors.color10 }, }, normal = { a = { fg = colors.color11, bg = colors.color12, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, c = { fg = colors.color4, bg = colors.color10 }, }, insert = { a = { fg = colors.color2, bg = colors.color15, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, } ================================================ FILE: lua/lualine/themes/iceberg_light.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color5 = '#668e3d', color8 = '#757ca3', color9 = '#8b98b6', color10 = '#cad0de', color11 = '#2d539e', color0 = '#e8e9ec', color1 = '#9fa6c0', color2 = '#c57339', } return { replace = { b = { fg = colors.color0, bg = colors.color1 }, a = { fg = colors.color0, bg = colors.color2, gui = 'bold' }, }, visual = { b = { fg = colors.color0, bg = colors.color1 }, a = { fg = colors.color0, bg = colors.color5, gui = 'bold' }, }, normal = { b = { fg = colors.color0, bg = colors.color1 }, a = { fg = colors.color0, bg = colors.color8, gui = 'bold' }, c = { fg = colors.color9, bg = colors.color10 }, }, inactive = { b = { fg = colors.color9, bg = colors.color10 }, a = { fg = colors.color9, bg = colors.color10, gui = 'bold' }, c = { fg = colors.color9, bg = colors.color10 }, }, insert = { b = { fg = colors.color0, bg = colors.color1 }, a = { fg = colors.color0, bg = colors.color11, gui = 'bold' }, }, } ================================================ FILE: lua/lualine/themes/jellybeans.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color2 = '#30302c', color3 = '#f0a0c0', color4 = '#e8e8d3', color5 = '#4e4e43', color8 = '#cf6a4c', color9 = '#666656', color10 = '#808070', color11 = '#8197bf', color14 = '#99ad6a', } return { visual = { a = { fg = colors.color2, bg = colors.color3, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, replace = { a = { fg = colors.color2, bg = colors.color8, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, inactive = { c = { fg = colors.color9, bg = colors.color2 }, a = { fg = colors.color10, bg = colors.color2, gui = 'bold' }, b = { fg = colors.color9, bg = colors.color2 }, }, normal = { c = { fg = colors.color10, bg = colors.color2 }, a = { fg = colors.color2, bg = colors.color11, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, insert = { a = { fg = colors.color2, bg = colors.color14, gui = 'bold' }, b = { fg = colors.color4, bg = colors.color5 }, }, } ================================================ FILE: lua/lualine/themes/material.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: Lokesh Krishna(lightline) -- stylua: ignore local colors = { fg = '#eeffff', bg = '#263238', blue = '#82aaff', green = '#c3e88d', purple = '#c792ea', red1 = '#f07178', red2 = '#ff5370', yellow = '#ffcb6b', gray1 = '#314549', gray2 = '#2E3C43', gray3 = '#515559', } return { normal = { a = { fg = colors.bg, bg = colors.blue, gui = 'bold' }, b = { fg = colors.fg, bg = colors.gray3 }, c = { fg = colors.fg, bg = colors.gray2 }, }, insert = { a = { fg = colors.bg, bg = colors.green, gui = 'bold' }, b = { fg = colors.fg, bg = colors.gray3 }, }, visual = { a = { fg = colors.bg, bg = colors.purple, gui = 'bold' }, b = { fg = colors.fg, bg = colors.gray3 }, }, replace = { a = { fg = colors.bg, bg = colors.red1, gui = 'bold' }, b = { fg = colors.fg, bg = colors.gray3 }, }, inactive = { a = { fg = colors.fg, bg = colors.bg, gui = 'bold' }, b = { fg = colors.fg, bg = colors.bg }, c = { fg = colors.fg, bg = colors.gray2 }, }, } ================================================ FILE: lua/lualine/themes/modus-vivendi.lua ================================================ -- Copyright (c) 2020-2021 ronniedroid -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { black = '#000000', white = '#eeeeee', red = '#ffa0a0', green = '#88cf88', blue = '#92baff', magenta = '#feacd0', cyan = '#a0bfdf', gray = '#2f2f2f', darkgray = '#202020', lightgray = '#434343' } return { normal = { a = { bg = colors.blue, fg = colors.lightgray, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.blue }, c = { bg = colors.gray, fg = colors.white }, }, insert = { a = { bg = colors.cyan, fg = colors.lightgray, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.cyan }, c = { bg = colors.gray, fg = colors.white }, }, visual = { a = { bg = colors.magenta, fg = colors.lightgray, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.magenta }, c = { bg = colors.gray, fg = colors.white }, }, replace = { a = { bg = colors.red, fg = colors.lightgray, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.red }, c = { bg = colors.gray, fg = colors.white }, }, command = { a = { bg = colors.green, fg = colors.lightgray, gui = 'bold' }, b = { bg = colors.lightgray, fg = colors.green }, c = { bg = colors.gray, fg = colors.white }, }, inactive = { a = { bg = colors.darkgray, fg = colors.lightgray, gui = 'bold' }, b = { bg = colors.darkgray, fg = colors.lightgray }, c = { bg = colors.darkgray, fg = colors.lightgray }, }, } ================================================ FILE: lua/lualine/themes/molokai.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: challsted(lightline) -- stylua: ignore local colors = { black = '#232526', gray = '#808080', white = '#f8f8f2', cyan = '#66d9ef', green = '#a6e22e', orange = '#ef5939', pink = '#f92672', red = '#ff0000', yellow = '#e6db74', } return { normal = { a = { fg = colors.black, bg = colors.cyan, gui = 'bold' }, b = { fg = colors.black, bg = colors.pink }, c = { fg = colors.orange, bg = colors.black }, }, insert = { a = { fg = colors.black, bg = colors.green, gui = 'bold' } }, visual = { a = { fg = colors.black, bg = colors.yellow, gui = 'bold' } }, replace = { a = { fg = colors.black, bg = colors.red, gui = 'bold' } }, inactive = { a = { fg = colors.pink, bg = colors.black, gui = 'bold' }, b = { fg = colors.white, bg = colors.pink }, c = { fg = colors.gray, bg = colors.black }, }, } ================================================ FILE: lua/lualine/themes/moonfly.lua ================================================ -- moonfly color scheme for lualine -- -- URL: github.com/bluz71/vim-moonfly-colors -- License: MIT (https://opensource.org/licenses/MIT) -- stylua: ignore local colors = { color3 = '#303030', color6 = '#9e9e9e', color7 = '#80a0ff', color8 = '#ae81ff', color0 = '#1c1c1c', color1 = '#ff5189', color2 = '#c6c6c6', } return { replace = { a = { fg = colors.color0, bg = colors.color1, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, }, inactive = { a = { fg = colors.color6, bg = colors.color3, gui = 'bold' }, b = { fg = colors.color6, bg = colors.color3 }, c = { fg = colors.color6, bg = colors.color3 }, }, normal = { a = { fg = colors.color0, bg = colors.color7, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, c = { fg = colors.color2, bg = colors.color3 }, }, visual = { a = { fg = colors.color0, bg = colors.color8, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, }, insert = { a = { fg = colors.color0, bg = colors.color2, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, }, } ================================================ FILE: lua/lualine/themes/nightfly.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { color3 = '#2c3043', color6 = '#a1aab8', color7 = '#82aaff', color8 = '#ae81ff', color0 = '#092236', color1 = '#ff5874', color2 = '#c3ccdc', } return { replace = { a = { fg = colors.color0, bg = colors.color1, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, }, inactive = { a = { fg = colors.color6, bg = colors.color3, gui = 'bold' }, b = { fg = colors.color6, bg = colors.color3 }, c = { fg = colors.color6, bg = colors.color3 }, }, normal = { a = { fg = colors.color0, bg = colors.color7, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, c = { fg = colors.color2, bg = colors.color3 }, }, visual = { a = { fg = colors.color0, bg = colors.color8, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, }, insert = { a = { fg = colors.color0, bg = colors.color2, gui = 'bold' }, b = { fg = colors.color2, bg = colors.color3 }, }, } ================================================ FILE: lua/lualine/themes/nord.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { nord1 = '#3B4252', nord3 = '#4C566A', nord5 = '#E5E9F0', nord6 = '#ECEFF4', nord7 = '#8FBCBB', nord8 = '#88C0D0', nord13 = '#EBCB8B', } return { normal = { a = { fg = colors.nord1, bg = colors.nord8, gui = 'bold' }, b = { fg = colors.nord5, bg = colors.nord1 }, c = { fg = colors.nord5, bg = colors.nord3 }, }, insert = { a = { fg = colors.nord1, bg = colors.nord6, gui = 'bold' } }, visual = { a = { fg = colors.nord1, bg = colors.nord7, gui = 'bold' } }, replace = { a = { fg = colors.nord1, bg = colors.nord13, gui = 'bold' } }, inactive = { a = { fg = colors.nord1, bg = colors.nord8, gui = 'bold' }, b = { fg = colors.nord5, bg = colors.nord1 }, c = { fg = colors.nord5, bg = colors.nord1 }, }, } ================================================ FILE: lua/lualine/themes/onedark.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: Zoltan Dalmadi(lightline) -- stylua: ignore local colors = { blue = '#61afef', green = '#98c379', purple = '#c678dd', cyan = '#56b6c2', red1 = '#e06c75', red2 = '#be5046', yellow = '#e5c07b', fg = '#abb2bf', bg = '#282c34', gray1 = '#828997', gray2 = '#2c323c', gray3 = '#3e4452', } return { normal = { a = { fg = colors.bg, bg = colors.green, gui = 'bold' }, b = { fg = colors.fg, bg = colors.gray3 }, c = { fg = colors.fg, bg = colors.gray2 }, }, command = { a = { fg = colors.bg, bg = colors.yellow, gui = 'bold' } }, insert = { a = { fg = colors.bg, bg = colors.blue, gui = 'bold' } }, visual = { a = { fg = colors.bg, bg = colors.purple, gui = 'bold' } }, terminal = { a = { fg = colors.bg, bg = colors.cyan, gui = 'bold' } }, replace = { a = { fg = colors.bg, bg = colors.red1, gui = 'bold' } }, inactive = { a = { fg = colors.gray1, bg = colors.bg, gui = 'bold' }, b = { fg = colors.gray1, bg = colors.bg }, c = { fg = colors.gray1, bg = colors.gray2 }, }, } ================================================ FILE: lua/lualine/themes/onelight.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: Zoltan Dalmadi(lightline) -- stylua: ignore local colors = { blue = '#4078f2', green = '#50a14f', purple = '#a626a4', red1 = '#e45649', red2 = '#ca1243', yellow = '#c18401', fg = '#494b53', bg = '#fafafa', gray1 = '#696c77', gray2 = '#f0f0f0', gray3 = '#d0d0d0', } return { normal = { a = { fg = colors.bg, bg = colors.green, gui = 'bold' }, b = { fg = colors.fg, bg = colors.gray3 }, c = { fg = colors.fg, bg = colors.gray2 }, }, command = { a = { fg = colors.bg, bg = colors.yellow, gui = 'bold' } }, insert = { a = { fg = colors.bg, bg = colors.blue, gui = 'bold' } }, visual = { a = { fg = colors.bg, bg = colors.purple, gui = 'bold' } }, replace = { a = { fg = colors.bg, bg = colors.red1, gui = 'bold' } }, inactive = { a = { fg = colors.bg, bg = colors.gray3, gui = 'bold' }, b = { fg = colors.bg, bg = colors.gray3 }, c = { fg = colors.gray3, bg = colors.gray2 }, }, } ================================================ FILE: lua/lualine/themes/palenight.lua ================================================ -- Copyright (c) 2020-2021 IGI-111 -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { vertsplit = '#181A1F', special_grey = '#3B4048', menu_grey = '#3E4452', cursor_grey = '#2C323C', gutter_fg_grey = '#4B5263', blue = '#82b1ff', dark_red = '#BE5046', white = '#bfc7d5', green = '#C3E88D', purple = '#c792ea', yellow = '#ffcb6b', light_red = '#ff869a', red = '#ff5370', dark_yellow = '#F78C6C', cyan = '#89DDFF', comment_grey = '#697098', black = '#292D3E', } return { normal = { a = { fg = colors.black, bg = colors.purple, gui = 'bold' }, b = { fg = colors.purple, bg = colors.menu_grey }, c = { fg = colors.comment_grey, bg = colors.black }, }, insert = { a = { fg = colors.black, bg = colors.blue, gui = 'bold' }, b = { fg = colors.blue, bg = colors.menu_grey }, }, visual = { a = { fg = colors.black, bg = colors.cyan, gui = 'bold' }, b = { fg = colors.cyan, bg = colors.menu_grey }, }, replace = { a = { fg = colors.black, bg = colors.green, gui = 'bold' }, b = { fg = colors.green, bg = colors.menu_grey }, }, inactive = { a = { fg = colors.black, bg = colors.menu_grey, gui = 'bold' }, b = { fg = colors.black, bg = colors.menu_grey }, c = { fg = colors.black, bg = colors.menu_grey }, }, } ================================================ FILE: lua/lualine/themes/papercolor_dark.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: TKNGUE(lightline) -- stylua: ignore local colors = { red = '#df0000', green = '#008700', blue = '#00afaf', pink = '#afdf00', olive = '#dfaf5f', navy = '#df875f', orange = '#d75f00', purple = '#8959a8', aqua = '#3e999f', foreground = '#d0d0d0', background = '#444444', window = '#efefef', status = '#c6c6c6', error = '#5f0000', statusline_active_fg = '#1c1c1c', statusline_active_bg = '#5f8787', statusline_inactive_fg = '#c6c6c6', statusline_inactive_bg = '#444444', visual_fg = '#000000', visual_bg = '#8787af', } return { normal = { a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, b = { fg = colors.statusline_active_fg, bg = colors.status }, c = { fg = colors.statusline_active_fg, bg = colors.statusline_active_bg }, }, insert = { a = { fg = colors.background, bg = colors.blue, gui = 'bold' } }, visual = { a = { fg = colors.visual_fg, bg = colors.visual_bg, gui = 'bold' } }, replace = { a = { fg = colors.background, bg = colors.pink, gui = 'bold' } }, inactive = { a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, b = { fg = colors.foreground, bg = colors.background }, c = { fg = colors.foreground, bg = colors.background }, }, } ================================================ FILE: lua/lualine/themes/papercolor_light.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: TKNGUE(lightline) -- stylua: ignore local colors = { red = '#df0000', green = '#008700', blue = '#4271ae', pink = '#d7005f', olive = '#718c00', navy = '#005f87', orange = '#d75f00', purple = '#8959a8', aqua = '#3e999f', foreground = '#4d4d4c', background = '#F5F5F5', window = '#efefef', status = '#3e999f', error = '#ffafdf', statusline_active_fg = '#efefef', statusline_active_bg = '#005f87', statusline_inactive_fg = '#4d4d4c', statusline_inactive_bg = '#dadada', } return { normal = { a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, b = { fg = colors.statusline_active_fg, bg = colors.status }, c = { fg = colors.statusline_active_fg, bg = colors.statusline_active_bg }, }, insert = { a = { fg = colors.blue, bg = colors.background, gui = 'bold' } }, visual = { a = { fg = colors.background, bg = colors.orange, gui = 'bold' } }, replace = { a = { fg = colors.background, bg = colors.pink, gui = 'bold' } }, inactive = { a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, b = { fg = colors.foreground, bg = colors.background }, c = { fg = colors.foreground, bg = colors.background }, }, } ================================================ FILE: lua/lualine/themes/powerline.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- stylua: ignore local Colors = { white = '#ffffff', darkestgreen = '#005f00', brightgreen = '#afdf00', darkestcyan = '#005f5f', mediumcyan = '#87dfff', darkestblue = '#005f87', darkred = '#870000', brightred = '#df0000', brightorange = '#ff8700', gray1 = '#262626', gray2 = '#303030', gray4 = '#585858', gray5 = '#606060', gray7 = '#9e9e9e', gray10 = '#f0f0f0', } local M = { normal = { a = { fg = Colors.darkestgreen, bg = Colors.brightgreen, gui = 'bold' }, b = { fg = Colors.gray10, bg = Colors.gray5 }, c = { fg = Colors.gray7, bg = Colors.gray2 }, }, insert = { a = { fg = Colors.darkestcyan, bg = Colors.white, gui = 'bold' }, b = { fg = Colors.darkestcyan, bg = Colors.mediumcyan }, c = { fg = Colors.mediumcyan, bg = Colors.darkestblue }, }, visual = { a = { fg = Colors.darkred, bg = Colors.brightorange, gui = 'bold' } }, replace = { a = { fg = Colors.white, bg = Colors.brightred, gui = 'bold' } }, inactive = { a = { fg = Colors.gray1, bg = Colors.gray5, gui = 'bold' }, b = { fg = Colors.gray1, bg = Colors.gray5 }, c = { bg = Colors.gray1, fg = Colors.gray5 }, }, } M.terminal = M.insert return M ================================================ FILE: lua/lualine/themes/powerline_dark.lua ================================================ -- Copyright (c) 2021 Ashish Panigrahi -- MIT license, see LICENSE for more details. -- stylua: ignore local colors = { black = '#202020', neon = '#DFFF00', white = '#FFFFFF', green = '#00D700', purple = '#5F005F', blue = '#00DFFF', darkblue = '#00005F', navyblue = '#000080', brightgreen = '#9CFFD3', gray = '#444444', darkgray = '#3c3836', lightgray = '#504945', inactivegray = '#7c6f64', orange = '#FFAF00', red = '#5F0000', brightorange = '#C08A20', brightred = '#AF0000', cyan = '#00DFFF', } local M = { normal = { a = { bg = colors.neon, fg = colors.black, gui = 'bold' }, b = { bg = colors.gray, fg = colors.white }, c = { bg = colors.black, fg = colors.brightgreen }, }, insert = { a = { bg = colors.blue, fg = colors.darkblue, gui = 'bold' }, b = { bg = colors.navyblue, fg = colors.white }, c = { bg = colors.purple, fg = colors.white }, }, visual = { a = { bg = colors.orange, fg = colors.black, gui = 'bold' }, b = { bg = colors.darkgray, fg = colors.white }, c = { bg = colors.red, fg = colors.white }, }, replace = { a = { bg = colors.brightred, fg = colors.white, gui = 'bold' }, b = { bg = colors.cyan, fg = colors.darkblue }, c = { bg = colors.navyblue, fg = colors.white }, }, command = { a = { bg = colors.green, fg = colors.black, gui = 'bold' }, b = { bg = colors.darkgray, fg = colors.white }, c = { bg = colors.black, fg = colors.brightgreen }, }, inactive = { a = { bg = colors.darkgray, fg = colors.gray, gui = 'bold' }, b = { bg = colors.darkgray, fg = colors.gray }, c = { bg = colors.darkgray, fg = colors.gray }, }, } M.terminal = M.insert return M ================================================ FILE: lua/lualine/themes/pywal.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local lualine_require = require('lualine_require') local modules = lualine_require.lazy_require { utils_notices = 'lualine.utils.notices', } local sep = package.config:sub(1, 1) local wal_colors_path = table.concat({ os.getenv('HOME'), '.cache', 'wal', 'colors.sh' }, sep) local wal_colors_file = io.open(wal_colors_path, 'r') if wal_colors_file == nil then modules.utils_notices.add_notice('lualine.nvim: ' .. wal_colors_path .. ' not found') error('') end local ok, wal_colors_text = pcall(wal_colors_file.read, wal_colors_file, '*a') wal_colors_file:close() if not ok then modules.utils_notices.add_notice('lualine.nvim: ' .. wal_colors_path .. ' could not be read: ' .. wal_colors_text) error('') end local colors = {} for line in vim.gsplit(wal_colors_text, '\n') do if line:match("^[a-z0-9]+='#[a-fA-F0-9]+'$") ~= nil then local i = line:find('=') local key = line:sub(0, i - 1) local value = line:sub(i + 2, #line - 1) colors[key] = value end end return { normal = { a = { fg = colors.background, bg = colors.color4, gui = 'bold' }, b = { fg = colors.foreground, bg = colors.color8 }, c = { fg = colors.foreground, bg = colors.background }, }, insert = { a = { fg = colors.background, bg = colors.color2, gui = 'bold' } }, visual = { a = { fg = colors.background, bg = colors.color3, gui = 'bold' } }, replace = { a = { fg = colors.background, bg = colors.color1, gui = 'bold' } }, inactive = { a = { fg = colors.foreground, bg = colors.background, gui = 'bold' }, b = { fg = colors.foreground, bg = colors.background }, c = { fg = colors.foreground, bg = colors.background }, }, } ================================================ FILE: lua/lualine/themes/seoul256.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Generated by lightline to lualine theme converter -- https://gist.github.com/shadmansaleh/000871c9a608a012721c6acc6d7a19b9 -- stylua: ignore local colors = { color5 = '#d7afaf', color6 = '#666656', color7 = '#808070', color10 = '#87af87', color13 = '#df5f87', color14 = '#87afaf', color0 = '#e8e8d3', color1 = '#4e4e43', color4 = '#30302c', } return { visual = { b = { fg = colors.color0, bg = colors.color1 }, a = { fg = colors.color4, bg = colors.color5, gui = 'bold' }, }, inactive = { b = { fg = colors.color6, bg = colors.color4 }, c = { fg = colors.color6, bg = colors.color4 }, a = { fg = colors.color7, bg = colors.color4, gui = 'bold' }, }, insert = { b = { fg = colors.color0, bg = colors.color1 }, a = { fg = colors.color4, bg = colors.color10, gui = 'bold' }, }, replace = { b = { fg = colors.color0, bg = colors.color1 }, a = { fg = colors.color4, bg = colors.color13, gui = 'bold' }, }, normal = { b = { fg = colors.color0, bg = colors.color1 }, c = { fg = colors.color7, bg = colors.color4 }, a = { fg = colors.color4, bg = colors.color14, gui = 'bold' }, }, } ================================================ FILE: lua/lualine/themes/solarized.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: itchyny(lightline) -- License: MIT License local background = vim.opt.background:get() return require('lualine.themes.solarized_' .. background) ================================================ FILE: lua/lualine/themes/solarized_dark.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: itchyny(lightline) -- stylua: ignore local colors = { base03 = '#002b36', base02 = '#073642', base01 = '#586e75', base00 = '#657b83', base0 = '#839496', base1 = '#93a1a1', base2 = '#eee8d5', base3 = '#fdf6e3', yellow = '#b58900', orange = '#cb4b16', red = '#dc322f', magenta = '#d33682', violet = '#6c71c4', blue = '#268bd2', cyan = '#2aa198', green = '#859900', } return { normal = { a = { fg = colors.base03, bg = colors.blue, gui = 'bold' }, b = { fg = colors.base03, bg = colors.base1 }, c = { fg = colors.base1, bg = colors.base02 }, }, insert = { a = { fg = colors.base03, bg = colors.green, gui = 'bold' } }, visual = { a = { fg = colors.base03, bg = colors.magenta, gui = 'bold' } }, replace = { a = { fg = colors.base03, bg = colors.red, gui = 'bold' } }, inactive = { a = { fg = colors.base0, bg = colors.base02, gui = 'bold' }, b = { fg = colors.base03, bg = colors.base00 }, c = { fg = colors.base01, bg = colors.base02 }, }, } ================================================ FILE: lua/lualine/themes/solarized_light.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: itchyny(lightline) -- stylua: ignore local colors = { base3 = '#002b36', base2 = '#073642', base1 = '#586e75', base0 = '#657b83', base00 = '#839496', base01 = '#93a1a1', base02 = '#eee8d5', base03 = '#fdf6e3', yellow = '#b58900', orange = '#cb4b16', red = '#dc322f', magenta = '#d33682', violet = '#6c71c4', blue = '#268bd2', cyan = '#2aa198', green = '#859900', } return { normal = { a = { fg = colors.base03, bg = colors.blue, gui = 'bold' }, b = { fg = colors.base03, bg = colors.base1 }, c = { fg = colors.base1, bg = colors.base02 }, }, insert = { a = { fg = colors.base03, bg = colors.green, gui = 'bold' } }, visual = { a = { fg = colors.base03, bg = colors.magenta, gui = 'bold' } }, replace = { a = { fg = colors.base03, bg = colors.red, gui = 'bold' } }, inactive = { a = { fg = colors.base0, bg = colors.base02, gui = 'bold' }, b = { fg = colors.base03, bg = colors.base00 }, c = { fg = colors.base01, bg = colors.base02 }, }, } ================================================ FILE: lua/lualine/themes/tomorrow_night.lua ================================================ local colors = { bg = '#282a2e', alt_bg = '#373b41', dark_fg = '#969896', fg = '#b4b7b4', light_fg = '#c5c8c6', normal = '#81a2be', insert = '#b5bd68', visual = '#b294bb', replace = '#de935f', } local theme = { normal = { a = { fg = colors.bg, bg = colors.normal }, b = { fg = colors.light_fg, bg = colors.alt_bg }, c = { fg = colors.fg, bg = colors.bg }, }, replace = { a = { fg = colors.bg, bg = colors.replace }, b = { fg = colors.light_fg, bg = colors.alt_bg }, }, insert = { a = { fg = colors.bg, bg = colors.insert }, b = { fg = colors.light_fg, bg = colors.alt_bg }, }, visual = { a = { fg = colors.bg, bg = colors.visual }, b = { fg = colors.light_fg, bg = colors.alt_bg }, }, inactive = { a = { fg = colors.dark_fg, bg = colors.bg }, b = { fg = colors.dark_fg, bg = colors.bg }, c = { fg = colors.dark_fg, bg = colors.bg }, }, } theme.command = theme.normal theme.terminal = theme.insert return theme ================================================ FILE: lua/lualine/themes/wombat.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- Credit: itchyny(lightline) -- stylua: ignore local colors = { base03 = '#242424', base023 = '#353535', base02 = '#444444', base01 = '#585858', base00 = '#666666', base0 = '#808080', base1 = '#969696', base2 = '#a8a8a8', base3 = '#d0d0d0', yellow = '#cae682', orange = '#e5786d', red = '#e5786d', magenta = '#f2c68a', blue = '#8ac6f2', cyan = '#8ac6f2', green = '#95e454', } return { normal = { a = { fg = colors.base02, bg = colors.blue, gui = 'bold' }, b = { fg = colors.base02, bg = colors.base0 }, c = { fg = colors.base2, bg = colors.base02 }, }, insert = { a = { fg = colors.base02, bg = colors.green, gui = 'bold' } }, visual = { a = { fg = colors.base02, bg = colors.magenta, gui = 'bold' } }, replace = { a = { fg = colors.base023, bg = colors.red, gui = 'bold' } }, inactive = { a = { fg = colors.base1, bg = colors.base02, gui = 'bold' }, b = { fg = colors.base023, bg = colors.base01 }, c = { fg = colors.base1, bg = colors.base023 }, }, } ================================================ FILE: lua/lualine/utils/class.lua ================================================ -- Adapted from https://github.com/rxi/classic/blob/master/classic.lua local Object = {} Object.__index = Object -- luacheck: push no unused args ---Initializer function Object:init(...) end -- luacheck: pop ---Extend base class to create a child class function Object:extend() local cls = {} for k, v in pairs(self) do if k:find('__') == 1 then cls[k] = v end end cls.__index = cls cls.super = self setmetatable(cls, self) return cls end -- luacheck: push no unused args function Object:__tostring() return 'Object' end -- luacheck: pop ---Creates a new object function Object:new(...) local obj = setmetatable({}, self) obj:init(...) return obj end ---Creates a new object function Object:__call(...) return self:new(...) end return Object ================================================ FILE: lua/lualine/utils/color_utils.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local M = {} -- stylua: ignore start -- color conversion local color_table = { -- lookup table for cterm colors -- format {'color_code', {r,g,b}} -- Primary 3-bit (8 colors). Unique representation! {'00', { 0, 0, 0 }}, {'01', { 128, 0, 0 }}, {'02', { 0, 128, 0 }}, {'03', { 128, 128, 0 }}, {'04', { 0, 0, 128 }}, {'05', { 128, 0, 128 }}, {'06', { 0, 128, 128 }}, {'07', { 192, 192, 192 }}, -- equivalent "bright" versions of original 8 colors. {'08', { 128, 128, 128 }}, {'09', { 255, 0, 0 }}, {'10', { 0, 255, 0 }}, {'11', { 255, 255, 0 }}, {'12', { 0, 0, 255 }}, {'13', { 255, 0, 255 }}, {'14', { 0, 255, 255 }}, {'15', { 255, 255, 255 }}, -- Strictly ascending. {'16', { 0, 0, 0 }}, {'17', { 0, 0, 95 }}, {'18', { 0, 0, 135 }}, {'19', { 0, 0, 175 }}, {'20', { 0, 0, 215 }}, {'21', { 0, 0, 255 }}, {'22', { 0, 95, 0 }}, {'23', { 0, 95, 95 }}, {'24', { 0, 95, 135 }}, {'25', { 0, 95, 175 }}, {'26', { 0, 95, 215 }}, {'27', { 0, 95, 255 }}, {'28', { 0, 135, 0 }}, {'29', { 0, 135, 95 }}, {'30', { 0, 135, 135 }}, {'31', { 0, 135, 175 }}, {'32', { 0, 135, 215 }}, {'33', { 0, 135, 255 }}, {'34', { 0, 175, 0 }}, {'35', { 0, 175, 95 }}, {'36', { 0, 175, 135 }}, {'37', { 0, 175, 175 }}, {'38', { 0, 175, 215 }}, {'39', { 0, 175, 255 }}, {'40', { 0, 215, 0 }}, {'41', { 0, 215, 95 }}, {'42', { 0, 215, 135 }}, {'43', { 0, 215, 175 }}, {'44', { 0, 215, 215 }}, {'45', { 0, 215, 255 }}, {'46', { 0, 255, 0 }}, {'47', { 0, 255, 95 }}, {'48', { 0, 255, 135 }}, {'49', { 0, 255, 175 }}, {'50', { 0, 255, 215 }}, {'51', { 0, 255, 255 }}, {'52', { 95, 0, 0 }}, {'53', { 95, 0, 95 }}, {'54', { 95, 0, 135 }}, {'55', { 95, 0, 175 }}, {'56', { 95, 0, 215 }}, {'57', { 95, 0, 255 }}, {'58', { 95, 95, 0 }}, {'59', { 95, 95, 95 }}, {'60', { 95, 95, 135 }}, {'61', { 95, 95, 175 }}, {'62', { 95, 95, 215 }}, {'63', { 95, 95, 255 }}, {'64', { 95, 135, 0 }}, {'65', { 95, 135, 95 }}, {'66', { 95, 135, 135 }}, {'67', { 95, 135, 175 }}, {'68', { 95, 135, 215 }}, {'69', { 95, 135, 255 }}, {'70', { 95, 175, 0 }}, {'71', { 95, 175, 95 }}, {'72', { 95, 175, 135 }}, {'73', { 95, 175, 175 }}, {'74', { 95, 175, 215 }}, {'75', { 95, 175, 255 }}, {'76', { 95, 215, 0 }}, {'77', { 95, 215, 95 }}, {'78', { 95, 215, 135 }}, {'79', { 95, 215, 175 }}, {'80', { 95, 215, 215 }}, {'81', { 95, 215, 255 }}, {'82', { 95, 255, 0 }}, {'83', { 95, 255, 95 }}, {'84', { 95, 255, 135 }}, {'85', { 95, 255, 175 }}, {'86', { 95, 255, 215 }}, {'87', { 95, 255, 255 }}, {'88', { 135, 0, 0 }}, {'89', { 135, 0, 95 }}, {'90', { 135, 0, 135 }}, {'91', { 135, 0, 175 }}, {'92', { 135, 0, 215 }}, {'93', { 135, 0, 255 }}, {'94', { 135, 95, 0 }}, {'95', { 135, 95, 95 }}, {'96', { 135, 95, 135 }}, {'97', { 135, 95, 175 }}, {'98', { 135, 95, 215 }}, {'99', { 135, 95, 255 }}, {'100', { 135, 135, 0 }}, {'101', { 135, 135, 95 }}, {'102', { 135, 135, 135 }}, {'103', { 135, 135, 175 }}, {'104', { 135, 135, 215 }}, {'105', { 135, 135, 255 }}, {'106', { 135, 175, 0 }}, {'107', { 135, 175, 95 }}, {'108', { 135, 175, 135 }}, {'109', { 135, 175, 175 }}, {'110', { 135, 175, 215 }}, {'111', { 135, 175, 255 }}, {'112', { 135, 215, 0 }}, {'113', { 135, 215, 95 }}, {'114', { 135, 215, 135 }}, {'115', { 135, 215, 175 }}, {'116', { 135, 215, 215 }}, {'117', { 135, 215, 255 }}, {'118', { 135, 255, 0 }}, {'119', { 135, 255, 95 }}, {'120', { 135, 255, 135 }}, {'121', { 135, 255, 175 }}, {'122', { 135, 255, 215 }}, {'123', { 135, 255, 255 }}, {'124', { 175, 0, 0 }}, {'125', { 175, 0, 95 }}, {'126', { 175, 0, 135 }}, {'127', { 175, 0, 175 }}, {'128', { 175, 0, 215 }}, {'129', { 175, 0, 255 }}, {'130', { 175, 95, 0 }}, {'131', { 175, 95, 95 }}, {'132', { 175, 95, 135 }}, {'133', { 175, 95, 175 }}, {'134', { 175, 95, 215 }}, {'135', { 175, 95, 255 }}, {'136', { 175, 135, 0 }}, {'137', { 175, 135, 95 }}, {'138', { 175, 135, 135 }}, {'139', { 175, 135, 175 }}, {'140', { 175, 135, 215 }}, {'141', { 175, 135, 255 }}, {'142', { 175, 175, 0 }}, {'143', { 175, 175, 95 }}, {'144', { 175, 175, 135 }}, {'145', { 175, 175, 175 }}, {'146', { 175, 175, 215 }}, {'147', { 175, 175, 255 }}, {'148', { 175, 215, 0 }}, {'149', { 175, 215, 95 }}, {'150', { 175, 215, 135 }}, {'151', { 175, 215, 175 }}, {'152', { 175, 215, 215 }}, {'153', { 175, 215, 255 }}, {'154', { 175, 255, 0 }}, {'155', { 175, 255, 95 }}, {'156', { 175, 255, 135 }}, {'157', { 175, 255, 175 }}, {'158', { 175, 255, 215 }}, {'159', { 175, 255, 255 }}, {'160', { 215, 0, 0 }}, {'161', { 215, 0, 95 }}, {'162', { 215, 0, 135 }}, {'163', { 215, 0, 175 }}, {'164', { 215, 0, 215 }}, {'165', { 215, 0, 255 }}, {'166', { 215, 95, 0 }}, {'167', { 215, 95, 95 }}, {'168', { 215, 95, 135 }}, {'169', { 215, 95, 175 }}, {'170', { 215, 95, 215 }}, {'171', { 215, 95, 255 }}, {'172', { 215, 135, 0 }}, {'173', { 215, 135, 95 }}, {'174', { 215, 135, 135 }}, {'175', { 215, 135, 175 }}, {'176', { 215, 135, 215 }}, {'177', { 215, 135, 255 }}, {'178', { 215, 175, 0 }}, {'179', { 215, 175, 95 }}, {'180', { 215, 175, 135 }}, {'181', { 215, 175, 175 }}, {'182', { 215, 175, 215 }}, {'183', { 215, 175, 255 }}, {'184', { 215, 215, 0 }}, {'185', { 215, 215, 95 }}, {'186', { 215, 215, 135 }}, {'187', { 215, 215, 175 }}, {'188', { 215, 215, 215 }}, {'189', { 215, 215, 255 }}, {'190', { 215, 255, 0 }}, {'191', { 215, 255, 95 }}, {'192', { 215, 255, 135 }}, {'193', { 215, 255, 175 }}, {'194', { 215, 255, 215 }}, {'195', { 215, 255, 255 }}, {'196', { 255, 0, 0 }}, {'197', { 255, 0, 95 }}, {'198', { 255, 0, 135 }}, {'199', { 255, 0, 175 }}, {'200', { 255, 0, 215 }}, {'201', { 255, 0, 255 }}, {'202', { 255, 95, 0 }}, {'203', { 255, 95, 95 }}, {'204', { 255, 95, 135 }}, {'205', { 255, 95, 175 }}, {'206', { 255, 95, 215 }}, {'207', { 255, 95, 255 }}, {'208', { 255, 135, 0 }}, {'209', { 255, 135, 95 }}, {'210', { 255, 135, 135 }}, {'211', { 255, 135, 175 }}, {'212', { 255, 135, 215 }}, {'213', { 255, 135, 255 }}, {'214', { 255, 175, 0 }}, {'215', { 255, 175, 95 }}, {'216', { 255, 175, 135 }}, {'217', { 255, 175, 175 }}, {'218', { 255, 175, 215 }}, {'219', { 255, 175, 255 }}, {'220', { 255, 215, 0 }}, {'221', { 255, 215, 95 }}, {'222', { 255, 215, 135 }}, {'223', { 255, 215, 175 }}, {'224', { 255, 215, 215 }}, {'225', { 255, 215, 255 }}, {'226', { 255, 255, 0 }}, {'227', { 255, 255, 95 }}, {'228', { 255, 255, 135 }}, {'229', { 255, 255, 175 }}, {'230', { 255, 255, 215 }}, {'231', { 255, 255, 255 }}, -- Gray-scale range. {'232', { 8, 8, 8 }}, {'233', { 18, 18, 18 }}, {'234', { 28, 28, 28 }}, {'235', { 38, 38, 38 }}, {'236', { 48, 48, 48 }}, {'237', { 58, 58, 58 }}, {'238', { 68, 68, 68 }}, {'239', { 78, 78, 78 }}, {'240', { 88, 88, 88 }}, {'241', { 98, 98, 98 }}, {'242', { 108, 108, 108 }}, {'243', { 118, 118, 118 }}, {'244', { 128, 128, 128 }}, {'245', { 138, 138, 138 }}, {'246', { 148, 148, 148 }}, {'247', { 158, 158, 158 }}, {'248', { 168, 168, 168 }}, {'249', { 178, 178, 178 }}, {'250', { 188, 188, 188 }}, {'251', { 198, 198, 198 }}, {'252', { 208, 208, 208 }}, {'253', { 218, 218, 218 }}, {'254', { 228, 228, 228 }}, {'255', { 238, 238, 238 }}, } -- stylua: ignore end ---converts #rrggbb formatted color to cterm ('0'-'255') color ---@param hex_color string ---@return string function M.rgb2cterm(hex_color) if hex_color == 'None' then return 'None' end local function get_color_distance(color1, color2) -- returns how much color2 deviates from color1 local dr = math.abs(color1[1] - color2[1]) / (color1[1] + 1) * 100 local dg = math.abs(color1[2] - color2[2]) / (color1[2] + 1) * 100 local db = math.abs(color1[3] - color2[3]) / (color1[3] + 1) * 100 return (dr + dg + db) end local r = tonumber(hex_color:sub(2, 3), 16) local g = tonumber(hex_color:sub(4, 5), 16) local b = tonumber(hex_color:sub(6, 7), 16) -- check which cterm color is closest to hex colors in terms of rgb values local closest_cterm_color = 0 local min_distance = 10000 for _, color in ipairs(color_table) do local current_distance = get_color_distance(color[2], { r, g, b }) if current_distance < min_distance then min_distance = current_distance closest_cterm_color = color[1] end end return closest_cterm_color end ---converts color name (only ones supported by neovim) formatted colors to #rrggbb ---@param name string like red,green,grey ---@return string function M.color_name2rgb(name) local color_val = vim.api.nvim_get_color_by_name(name) if color_val == -1 then return '#' .. name -- Assuming it's 'rrggbb' without # not rad instead of red end return string.format('#%06x', color_val) end ---converts cterm(0-255) to #rrggbb ---@param color number ---@return string function M.cterm2rgb(color) local color_data = color_table[color + 1] if color_data ~= nil then color_data = color_data[2] return string.format('#%02x%02x%02x', color_data[1], color_data[2], color_data[3]) end end return M ================================================ FILE: lua/lualine/utils/fn_store.lua ================================================ local M = {} local fns = {} ---Store fn with id in fns so it can be later called ---@param id integer component id (for now we are only doing one fn per component) ---@param fn function the actual function to store. ---@return number same id that was provided. function M.register_fn(id, fn) vim.validate { id = { id, 'n' }, fn = { fn, 'f' }, } fns[id] = fn return id end ---Get the function with id ---@param id number id of the fn to retrieve ---@return function function M.get_fn(id) vim.validate { id = { id, 'n' } } return fns[id] or function() end end ---Call the function of id with args ---@param id number ---@param ... any ---@return any function M.call_fn(id, ...) return M.get_fn(id)(...) end ---Clear the fns table function M.clear_fns() fns = {} end return M ================================================ FILE: lua/lualine/utils/job.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. --- wrapper around job api --- creates a job handler when called local Job = setmetatable({ --- start the job start = function(self) self.job_id = vim.fn.jobstart(self.args.cmd, self.args) return self.job_id > 0 end, --- stop the job. Also immediately disables io from the job. stop = function(self) if self.killed then return end if self.job_id and self.job_id > 0 then vim.fn.jobstop(self.job_id) end self.job_id = 0 self.killed = true end, -- Wraps callbacks so they are only called when job is alive -- This avoids race conditions wrap_cb_alive = function(self, name) local original_cb = self.args[name] if original_cb then self.args[name] = function(...) if not self.killed then return original_cb(...) end end end end, }, { ---create new job handler ---@param self table base job table ---@param args table same args as jobstart except cmd is also passed in part of it ---@return table new job handler __call = function(self, args) args = vim.deepcopy(args or {}) if type(args.cmd) == 'string' then args.cmd = vim.split(args.cmd, ' ') end self.__index = self local job = setmetatable({ args = args }, self) job:wrap_cb_alive('on_stdout') job:wrap_cb_alive('on_stderr') job:wrap_cb_alive('on_stdin') return job end, }) return Job ================================================ FILE: lua/lualine/utils/loader.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local lualine_require = require('lualine_require') local require = lualine_require.require local modules = lualine_require.lazy_require { utils = 'lualine.utils.utils', notice = 'lualine.utils.notices', fn_store = 'lualine.utils.fn_store', } local is_valid_filename = lualine_require.is_valid_filename local sep = lualine_require.sep --- function that loads specific type of component local component_types = { -- loads custom component custom = function(component) return component[1](component) end, --- loads lua functions as component lua_fun = function(component) return require('lualine.components.special.function_component')(component) end, --- loads lua modules as components (ones in /lua/lualine/components/) mod = function(component) local ok, loaded_component = pcall(require, 'lualine.components.' .. component[1]) if ok then component.component_name = component[1] if type(loaded_component) == 'table' then loaded_component = loaded_component(component) elseif type(loaded_component) == 'function' then component[1] = loaded_component loaded_component = require('lualine.components.special.function_component')(component) end return loaded_component end end, --- loads builtin statusline patterns as component stl = function(component) local stl_expr = component[1] -- Vim's %p %l statusline elements component[1] = function() return stl_expr end return require('lualine.components.special.function_component')(component) end, --- loads variables & options (g:,go:,b:,bo:...) as components var = function(component) return require('lualine.components.special.vim_var_component')(component) end, --- loads vim functions and lua expressions as components ['_'] = function(component) return require('lualine.components.special.eval_func_component')(component) end, } ---load a component from component config ---@param component table component + component options ---@return table the loaded & initialized component local function component_loader(component) if type(component[1]) == 'function' then return component_types.lua_fun(component) elseif type(component[1]) == 'string' then -- load the component if component.type ~= nil then if component_types[component.type] and component.type ~= 'lua_fun' then return component_types[component.type](component) elseif component.type == 'vim_fun' or component.type == 'lua_expr' then return component_types['_'](component) else modules.notice.add_notice(string.format( [[ ### component.type component type '%s' isn't recognised. Check if spelling is correct.]], component.type )) end end local loaded_component = component_types.mod(component) if loaded_component then return loaded_component elseif string.char(component[1]:byte(1)) == '%' then return component_types.stl(component) elseif component[1]:find('[gvtwb]?o?:') == 1 then return component_types.var(component) else return component_types['_'](component) end elseif type(component[1]) == 'table' then return component_types.custom(component) end end --- Shows notice about invalid types passed as component --- @param index number the index of component in section table --- @param component table containing component elements --- return bool whether check passed or not local function is_valid_component_type(index, component) if type(component_types) == 'table' and type(index) == 'number' then return true end modules.notice.add_notice(string.format( [[ ### Unrecognized component Only functions, strings and tables can be used as component. You seem to have a `%s` as component indexed as `%s`. Something like: ```lua %s = %s, ``` This commonly occurs when you forget to pass table with option for component. When a component has option that component needs to be a table which holds the component as first element and the options as key value pairs. For example: ```lua lualine_c = { {'diagnostics', sources = {'nvim'}, } } ``` Notice the inner extra {} surrounding the component and it's options. Make sure your config follows this. ]], type(component), index, index, vim.inspect(component) )) return false end ---loads all the section from a config ---@param sections table list of sections ---@param options table global options table local function load_sections(sections, options) for section_name, section in pairs(sections) do for index, component in pairs(section) do if (type(component) == 'string' or type(component) == 'function') or modules.utils.is_component(component) then component = { component } end if is_valid_component_type(index, component) then component.self = {} component.self.section = section_name:match('lualine_(.*)') -- apply default args component = vim.tbl_extend('keep', component, options) section[index] = component_loader(component) end end end end ---loads all the configs (active, inactive, tabline) ---@param config table user config local function load_components(config) local sec_names = { 'sections', 'inactive_sections', 'tabline', 'winbar', 'inactive_winbar' } for _, section in ipairs(sec_names) do load_sections(config[section], config.options) end end ---loads all the extensions ---@param config table user config local function load_extensions(config) local loaded_extensions = {} local sec_names = { 'sections', 'inactive_sections', 'winbar', 'inactive_winbar' } for _, extension in pairs(config.extensions) do if type(extension) == 'string' then local ok ok, extension = pcall(require, 'lualine.extensions.' .. extension) if not ok then modules.notice.add_notice(string.format( [[ ### Extensions Extension named `%s` was not found . Check if spelling is correct. ]], extension )) end end if type(extension) == 'table' then local local_extension = modules.utils.deepcopy(extension) for _, section in ipairs(sec_names) do if local_extension[section] then load_sections(local_extension[section], config.options) end end if type(local_extension.init) == 'function' then local_extension.init() end table.insert(loaded_extensions, local_extension) end end config.extensions = loaded_extensions end ---loads sections and extensions or entire user config ---@param config table user config local function load_all(config) require('lualine.component')._reset_components() modules.fn_store.clear_fns() require('lualine.utils.nvim_opts').reset_cache() load_components(config) load_extensions(config) end ---loads a theme from lua module ---prioritizes external themes (from user config or other plugins) over the bundled ones ---@param theme_name string ---@return table theme definition from module local function load_theme(theme_name) assert(is_valid_filename(theme_name), 'Invalid filename') local retval local path = table.concat { 'lua/lualine/themes/', theme_name, '.lua' } local files = vim.api.nvim_get_runtime_file(path, true) if #files <= 0 then path = table.concat { 'lua/lualine/themes/', theme_name, '/init.lua' } files = vim.api.nvim_get_runtime_file(path, true) end local n_files = #files if n_files == 0 then -- No match found on runtimepath. Fall back to package.path local file = assert(package.searchpath('lualine.themes.' .. theme_name, package.path), 'Theme ' .. theme_name .. ' not found') retval = dofile(file) elseif n_files == 1 then -- when only one is found run that and return it's return value retval = dofile(files[1]) else -- put entries from user config path in front local user_config_path = vim.fn.stdpath('config') table.sort(files, function(a, b) local pattern = table.concat { user_config_path, sep } return string.match(a, pattern) or not string.match(b, pattern) end) -- More then 1 found . Use the first one that isn't in lualines repo local lualine_repo_pattern = table.concat({ 'lualine.nvim', 'lua', 'lualine' }, sep) local file_found = false for _, file in ipairs(files) do if not file:find(lualine_repo_pattern) then retval = dofile(file) file_found = true break end end if not file_found then -- This shouldn't happen but somehow we have multiple files but they -- appear to be in lualines repo . Just run the first one retval = dofile(files[1]) end end return retval end return { load_all = load_all, load_theme = load_theme, } ================================================ FILE: lua/lualine/utils/mode.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local Mode = {} -- stylua: ignore Mode.map = { ['n'] = 'NORMAL', ['no'] = 'O-PENDING', ['nov'] = 'O-PENDING', ['noV'] = 'O-PENDING', ['no\22'] = 'O-PENDING', ['niI'] = 'NORMAL', ['niR'] = 'NORMAL', ['niV'] = 'NORMAL', ['nt'] = 'NORMAL', ['ntT'] = 'NORMAL', ['v'] = 'VISUAL', ['vs'] = 'VISUAL', ['V'] = 'V-LINE', ['Vs'] = 'V-LINE', ['\22'] = 'V-BLOCK', ['\22s'] = 'V-BLOCK', ['s'] = 'SELECT', ['S'] = 'S-LINE', ['\19'] = 'S-BLOCK', ['i'] = 'INSERT', ['ic'] = 'INSERT', ['ix'] = 'INSERT', ['R'] = 'REPLACE', ['Rc'] = 'REPLACE', ['Rx'] = 'REPLACE', ['Rv'] = 'V-REPLACE', ['Rvc'] = 'V-REPLACE', ['Rvx'] = 'V-REPLACE', ['c'] = 'COMMAND', ['cv'] = 'EX', ['ce'] = 'EX', ['r'] = 'REPLACE', ['rm'] = 'MORE', ['r?'] = 'CONFIRM', ['!'] = 'SHELL', ['t'] = 'TERMINAL', } ---@return string current mode name function Mode.get_mode() local mode_code = vim.api.nvim_get_mode().mode if Mode.map[mode_code] == nil then return mode_code end return Mode.map[mode_code] end return Mode ================================================ FILE: lua/lualine/utils/notices.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. -- To provide notices for user local M = {} local notices = {} local persistent_notices = {} ---append new notice ---@param notice string|table table is a list of strings function M.add_notice(notice) if type(notice) == 'string' then notice = vim.split(notice, '\n') end if notice[#notice] ~= '' then notice[#notice + 1] = '' end table.insert(notices, notice) end ---appends persistent notice. These don't get cleared on setup ---@param notice string|table table is a list of strings function M.add_persistent_notice(notice) if type(notice) == 'string' then notice = vim.split(notice, '\n') end if not vim.tbl_contains(persistent_notices, notice) then table.insert(persistent_notices, notice) end end ---show setup :LuaLineNotices and show notification about error when there ---are notices available local notify_done = false function M.notice_message_startup() notify_done = false vim.defer_fn(function() if notify_done then return end if #notices > 0 or #persistent_notices > 0 then vim.cmd('command! -nargs=0 LualineNotices lua require"lualine.utils.notices".show_notices()') vim.notify( 'lualine: There are some issues with your config. Run :LualineNotices for details', vim.log.levels.WARN, {} ) end notify_done = true end, 2000) end ---create notice view function M.show_notices() vim.cmd('silent! keepalt split') local winid = vim.api.nvim_get_current_win() local bufnr = vim.api.nvim_create_buf(false, true) vim.api.nvim_win_set_buf(winid, bufnr) vim.wo[winid].winfixheight = true vim.wo[winid].winfixwidth = true vim.wo[winid].number = false vim.wo[winid].foldcolumn = '0' vim.wo[winid].relativenumber = false vim.wo[winid].signcolumn = 'no' vim.bo[bufnr].filetype = 'markdown' vim.api.nvim_buf_set_keymap(bufnr, 'n', 'q', 'bd', { noremap = true, silent = true }) local ok, _ = pcall(vim.api.nvim_buf_set_name, 0, 'Lualine Notices') if not ok then vim.notify('Lualine Notices is already open in another buffer', vim.log.levels.ERROR, {}) vim.cmd('normal q') return end local notice = vim.tbl_flatten(persistent_notices) notice = vim.list_extend(notice, vim.tbl_flatten(notices)) vim.fn.appendbufline(bufnr, 0, notice) vim.fn.deletebufline(bufnr, #notice, vim.fn.line('$')) vim.api.nvim_win_set_cursor(winid, { 1, 0 }) vim.bo[bufnr].modified = false vim.bo[bufnr].modifiable = false end function M.clear_notices() notices = {} end return M ================================================ FILE: lua/lualine/utils/nvim_opts.lua ================================================ local M = {} -- keeps backup of options that we cahge so we can restore it. -- format: -- options { -- global = <1> { -- name = {prev, set} -- }, -- buffer = { -- buf1 = <1>, -- buf2 = <1> -- }, -- window = { -- win1 = <1>, -- win2 = <1> -- } -- } ---@class LualineNvimOptCacheOptStore ---@field prev any ---@field set any ---@alias LualineNvimOptCacheOpt table ---@class LualineNvimOptCache ---@field global LualineNvimOptCacheOptStore[] ---@field buffer table ---@field window table ---@type LualineNvimOptCache local options = { global = {}, buffer = {}, window = {} } -- helper function for M.set local function set_opt(name, val, getter_fn, setter_fn, cache_tbl) -- before nvim 0.7 nvim_win_get_option... didn't return default value when -- the option wasn't set instead threw error. -- So we need pcall (probably just for test) local ok, cur = pcall(getter_fn, name) if not ok then cur = nil end if cur == val then return end if cache_tbl[name] == nil then cache_tbl[name] = {} end if cache_tbl[name].set ~= cur then if type(cur) ~= 'string' or not cur:find('lualine') then cache_tbl[name].prev = cur end end cache_tbl[name].set = val setter_fn(name, val) -- Mitigation for https://github.com/nvim-lualine/lualine.nvim/issues/1323 -- when redrawstatus is ran and winbar usues some eval syntax %{} for some -- odd reason nvim renders the winbar in statusline first then actual statusline -- replaces it. Also it doesn't happen on top window. Might be some bug in -- neovims rendering. Also it doesn't seem to happend with laststatus=2 or -- global status turned off in lualine. lualines rendering might be at fault -- too. but we should never be evaluating an externally set winbar so that -- doen't make much sense either. -- Tested: nvim v0.9.5 -- TODO: Needs further investigation. if vim.api.nvim_get_mode().mode == 'c' then if name == 'statusline' or name == 'winbar' then vim.cmd('redrawstatus') end if name == 'tabline' then vim.cmd('redrawtabline') end end end -- set a option value ---@param name string ---@param val any ---@param opts table|nil when table can be {global=true | buffer = bufnr | window = winnr} --- when nil it's treated as {global = true} function M.set(name, val, opts) if opts == nil or opts.global then set_opt(name, val, vim.api.nvim_get_option, vim.api.nvim_set_option, options.global) elseif opts.buffer then if options.buffer[opts.buffer] == nil then options.buffer[opts.buffer] = {} end set_opt(name, val, function(nm) if not vim.tbl_contains(vim.api.nvim_list_bufs(), opts.buffer) then return nil end return vim.api.nvim_buf_get_option(opts.buffer, nm) end, function(nm, vl) if not vim.tbl_contains(vim.api.nvim_list_bufs(), opts.buffer) then return nil end vim.api.nvim_buf_set_option(opts.buffer, nm, vl) end, options.buffer[opts.buffer]) elseif opts.window then if options.window[opts.window] == nil then options.window[opts.window] = {} end set_opt(name, val, function(nm) if not vim.tbl_contains(vim.api.nvim_list_wins(), opts.window) then return nil end return vim.api.nvim_win_get_option(opts.window, nm) end, function(nm, vl) if not vim.tbl_contains(vim.api.nvim_list_wins(), opts.window) then return nil end vim.api.nvim_win_set_option(opts.window, nm, vl) end, options.window[opts.window]) end end -- resoters old value of option name ---@param name string ---@param opts table|nil same as M.set function M.restore(name, opts) if opts == nil or opts.global then if options.global[name] ~= nil and options.global[name].prev ~= nil then local restore_to = options.global[name].prev if type(restore_to) == 'string' and restore_to:find('lualine') then restore_to = '' end vim.api.nvim_set_option(name, restore_to) end elseif opts.buffer then if options.buffer[opts.buffer] ~= nil and options.buffer[opts.buffer][name] ~= nil and options.buffer[opts.buffer][name].prev ~= nil then local restore_to = options.buffer[opts.buffer][name].prev if type(restore_to) == 'string' and restore_to:find('lualine') then restore_to = '' end vim.api.nvim_buf_set_option(opts.buffer, name, restore_to) end elseif opts.window then if options.window[opts.window] ~= nil and options.window[opts.window][name] ~= nil and options.window[opts.window][name].prev ~= nil then local restore_to = options.window[opts.window][name].prev if type(restore_to) == 'string' and restore_to:find('lualine') then restore_to = '' end vim.api.nvim_win_set_option(opts.window, name, restore_to) end end end -- returns cache for the option name ---@param name string ---@param opts table|nil same as M.set function M.get_cache(name, opts) if opts == nil or opts.global then if options.global[name] ~= nil and options.global[name].prev ~= nil then return options.global[name].prev end elseif opts.buffer then if options.buffer[opts.buffer] ~= nil and options.buffer[opts.buffer][name] ~= nil and options.buffer[opts.buffer][name].prev ~= nil then return options.buffer[opts.buffer][name].prev end elseif opts.window then if options.window[opts.window] ~= nil and options.window[opts.window][name] ~= nil and options.window[opts.window][name].prev ~= nil then return options.window[opts.window][name].prev end end end -- resets cache for options function M.reset_cache() options = { global = {}, buffer = {}, window = {} } end return M ================================================ FILE: lua/lualine/utils/section.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local M = {} local require = require('lualine_require').require local utils = require('lualine.utils.utils') local highlight = require('lualine.highlight') ---runs draw function on components in section ---handles separator edge cases :/ ---also handles default transitional separators at section boundaries ---(why? I don't know) ---@param section table list of components ---@param section_name string used for getting proper hl ---@param is_focused boolean ---@return string formatted string for a section --TODO Clean this up this does lots of messy stuff. function M.draw_section(section, section_name, is_focused) local highlight_name = highlight.format_highlight(section_name, is_focused) local status = {} for _, component in pairs(section) do -- load components into status table if type(component) ~= 'table' or (type(component) == 'table' and not component.component_no) then return '' -- unknown element in section. section possibly not yet loaded end table.insert(status, component:draw(highlight_name, is_focused)) end local section_color = utils.extract_highlight_colors(string.match(highlight_name, '%%#(.*)#')) -- Flags required for knowing when to remove component separator local strip_next_component = false local last_component_found = false local first_component_no = #section -- Check through components to see when component separator need to be removed for component_no = #section, 1, -1 do if #status[component_no] > 0 then first_component_no = component_no end -- Remove component separator with highlight for last component if not last_component_found and #status[component_no] > 0 then last_component_found = true status[component_no] = section[component_no]:strip_separator() if section_name < 'c' then if type(section[first_component_no].options.separator) ~= 'table' and (section[1].options.section_separators.left ~= nil and section[1].options.section_separators.left ~= '') then status[component_no] = string.format('%s%%Z{%s}', status[component_no], section[1].options.section_separators.left) end end end -- Remove component separator when color option is used in next component if strip_next_component then strip_next_component = false status[component_no] = section[component_no]:strip_separator() end -- Remove component separator when color option is used to color background if ( type(section[component_no].options.color) == 'table' and section[component_no].options.color.bg and section[component_no].options.color.bg ~= section_color.bg ) or type(section[component_no].options.color) == 'string' or ( type(section[component_no].options.color) == 'function' and section[component_no].color_fn_cache and section[component_no].color_fn_cache.bg and section[component_no].color_fn_cache.bg ~= section_color.bg ) then strip_next_component = true status[component_no] = section[component_no]:strip_separator() end if section[component_no].strip_previous_separator == true then strip_next_component = true end end local left_separator_string = '' if section_name > 'x' and section[first_component_no] and type(section[first_component_no].options.separator) ~= 'table' and (section[1].options.section_separators.right ~= nil and section[1].options.section_separators.right ~= '') then left_separator_string = string.format( '%%z{%s}', section[first_component_no].options.ls_separator or section[1].options.section_separators.right ) end -- Remove empty strings from status status = utils.list_shrink(status) local status_str = table.concat(status) if #status_str == 0 then return '' end local needs_hl local find_start_trans_sep_start, find_start_trans_sep_end = status_str:find('^%%z{.-}') if find_start_trans_sep_start then -- the section doesn't need to be prepended with default hl when sections -- first component has transitional sep needs_hl = status_str:find('^%%#', find_start_trans_sep_end + 1) else needs_hl = status_str:find('^%%#') end if needs_hl then -- Don't prepend with old highlight when the component changes it immediately return left_separator_string .. status_str else return left_separator_string .. highlight_name .. status_str end end return M ================================================ FILE: lua/lualine/utils/utils.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local M = {} -- Note for now only works for termguicolors scope can be bg or fg or any other -- attr parameter like bold/italic/reverse ---@param color_group string hl_group name ---@param scope string bg | fg | sp ---@return table|string returns #rrggbb formatted color when scope is specified ---- or complete color table when scope isn't specified function M.extract_highlight_colors(color_group, scope) local color = require('lualine.highlight').get_lualine_hl(color_group) if not color then if vim.fn.hlexists(color_group) == 0 then return nil end color = vim.api.nvim_get_hl_by_name(color_group, true) if color.background ~= nil then color.bg = string.format('#%06x', color.background) color.background = nil end if color.foreground ~= nil then color.fg = string.format('#%06x', color.foreground) color.foreground = nil end if color.special ~= nil then color.sp = string.format('#%06x', color.special) color.special = nil end end if scope then return color[scope] end return color end --- retrieves color value from highlight group name in syntax_list --- first present highlight is returned ---@param scope string|table ---@param syntaxlist table ---@param default string ---@return string|nil function M.extract_color_from_hllist(scope, syntaxlist, default) scope = type(scope) == 'string' and { scope } or scope for _, highlight_name in ipairs(syntaxlist) do if vim.fn.hlexists(highlight_name) ~= 0 then local color = M.extract_highlight_colors(highlight_name) for _, sc in ipairs(scope) do if color.reverse then if sc == 'bg' then sc = 'fg' else sc = 'bg' end end if color[sc] then return color[sc] end end end end return default end ---remove empty strings from list ---@param list table ---@return table function M.list_shrink(list) local new_list = {} for i = 1, #list do if list[i] and #list[i] > 0 then table.insert(new_list, list[i]) end end return new_list end --- Check if a auto command is already defined ---@param event string ---@param pattern string ---@param command_str string ---@return boolean whether autocmd is already defined local function autocmd_is_defined(event, pattern, command_str) return vim.api.nvim_exec(string.format('au lualine %s %s', event, pattern), true):find(command_str) ~= nil end --- Define a auto command if it's not already defined ---@param event string event name ---@param pattern string event pattern ---@param cmd string command to run on event ---@param group string group name defaults to lualine function M.define_autocmd(event, pattern, cmd, group) if not cmd then cmd = pattern pattern = '*' end if not autocmd_is_defined(event, pattern, cmd) then vim.cmd(string.format('autocmd %s %s %s %s', group or 'lualine', event, pattern, cmd)) end end -- Check if statusline is on focused window or not function M.is_focused() return tonumber(vim.g.actual_curwin) == vim.api.nvim_get_current_win() end --- Check what's the character at pos ---@param str string ---@param pos number ---@return string character at position pos in string str function M.charAt(str, pos) return string.char(str:byte(pos)) end -- deepcopy adapted from penlight -- https://github.com/lunarmodules/Penlight/blob/0653cdb05591454a9804a7fee8c873b8f06b0b8f/lua/pl/tablex.lua#L98-L122 local function cycle_aware_copy(t, cache) if type(t) ~= 'table' then return t end if cache[t] then return cache[t] end local res = {} cache[t] = res local mt = getmetatable(t) for k, v in pairs(t) do k = cycle_aware_copy(k, cache) v = cycle_aware_copy(v, cache) res[k] = v end setmetatable(res, mt) return res end --- make a deep copy of a table, recursively copying all the keys and fields. -- This supports cycles in tables; cycles will be reproduced in the copy. -- This will also set the copied table's metatable to that of the original. -- @within Copying -- @tab t A table -- @return new table function M.deepcopy(t) return cycle_aware_copy(t, {}) end --- Check if comp is a lualine component --- @param comp any --- @return boolean function M.is_component(comp) if type(comp) ~= 'table' then return false end local mt = getmetatable(comp) return mt and mt.__is_lualine_component == true end --- Call function with args and return it's result. --- If error occurs during fn retry times times. ---@param fn function Function to call. ---@param args table List of arguments used for calling function. ---@param times number Number of times to retry on error. ---@return any Result of fn. function M.retry_call(fn, args, times) times = times or 3 for _ = 0, times - 1 do local result = { pcall(fn, unpack(args)) } if result[1] == true then return unpack(result, 2) end end return fn(unpack(args)) end --- Wrap a function in retry_call ---@param fn function Function to call. ---@param times number Number of times to retry on error. ---@return function retry call wrapped function function M.retry_call_wrap(fn, times) return function(...) return M.retry_call(fn, { ... }, times) end end ---Escape % in str so it doesn't get picked as stl item. ---@param str string ---@return string function M.stl_escape(str) if type(str) ~= 'string' then return str end return str:gsub('%%', '%%%%') end ---A safe call inside a timer ---@param timer userdata ---@param augroup string|nil autocmd group to reset too on error. ---@param fn function ---@param max_err integer ---@param err_msg string ---@return function a wrapped fn that can be called inside a timer and that ---stops the timer after max_err errors in calling fn function M.timer_call(timer, augroup, fn, max_err, err_msg) local err_cnt, ret = 0, nil max_err = max_err or 3 return vim.schedule_wrap(function(...) if err_cnt > max_err then vim.loop.timer_stop(timer) if augroup then vim.cmd(string.format([[augroup %s | exe "autocmd!" | augroup END]], augroup)) end error(err_msg .. ':\n' .. tostring(ret)) end local ok ok, ret = pcall(fn, ...) if ok then err_cnt = 0 else err_cnt = err_cnt + 1 end return ret end) end return M ================================================ FILE: lua/lualine.lua ================================================ -- Copyright (c) 2020-2021 hoob3rt -- MIT license, see LICENSE for more details. local M = {} local lualine_require = require('lualine_require') local modules = lualine_require.lazy_require { highlight = 'lualine.highlight', loader = 'lualine.utils.loader', utils_section = 'lualine.utils.section', utils = 'lualine.utils.utils', utils_notices = 'lualine.utils.notices', config_module = 'lualine.config', nvim_opts = 'lualine.utils.nvim_opts', } local config -- Stores currently applied config local timers = { stl_timer = vim.loop.new_timer(), tal_timer = vim.loop.new_timer(), wb_timer = vim.loop.new_timer(), refresh_check_timer = vim.loop.new_timer(), halt_stl_refresh = false, -- mutex ? halt_tal_refresh = false, halt_wb_refresh = false, } local last_focus = {} local refresh_real_curwin -- Helper for apply_transitional_separators() --- finds first applied highlight group after str_checked in status ---@param status string : unprocessed statusline string ---@param str_checked number : position of how far status has been checked ---@return string|nil the hl group name or nil local function find_next_hl(status, str_checked) -- Gets the next valid hl group from str_checked local hl_pos_start, hl_pos_end = status:find('%%#.-#', str_checked) while true do if not hl_pos_start then return nil end -- When there are more that one hl group next to one another like -- %#HL1#%#HL2#%#HL3# we need to return HL3. This makes that happen. local next_start, next_end = status:find('^%%#.-#', hl_pos_end + 1) if next_start == nil then break end hl_pos_start, hl_pos_end = next_start, next_end end return status:sub(hl_pos_start + 2, hl_pos_end - 1) end -- Helper for apply_transitional_separators() --- applies transitional separator highlight + transitional separator ---@param status string : unprocessed statusline string ---@param str_checked number : position of how far status has been checked ---@param last_hl string : last applied hl group name before str_checked ---@param reverse boolean : reverse the hl group ( true for right separators ) ---@return string|nil concatenate separator highlight and transitional separator local function fill_section_separator(status, is_focused, str_checked, last_hl, sep, reverse) -- Inserts transitional separator along with transitional highlight local next_hl = find_next_hl(status, str_checked) if last_hl == nil then last_hl = modules.highlight.get_stl_default_hl(is_focused) end if next_hl == nil then next_hl = modules.highlight.get_stl_default_hl(is_focused) end if #next_hl == 0 or #last_hl == 0 then return end local transitional_highlight = reverse -- lua ternary assignment x ? y : z and modules.highlight.get_transitional_highlights(last_hl, next_hl) or modules.highlight.get_transitional_highlights(next_hl, last_hl) if transitional_highlight then return transitional_highlight .. sep end end --- processes statusline string --- replaces %z/Z{sep} with proper left/right separator highlight + sep ---@param status string : unprocessed statusline string ---@return string : processed statusline string local function apply_transitional_separators(status, is_focused) local status_applied = {} -- Collects all the pieces for concatenation local last_hl -- Stores last highlight group that we found local last_hl_reseted = false -- Whether last_hl is nil after reset -- it after %= local copied_pos = 1 -- Tracks how much we've copied over to status_applied local str_checked = 1 -- Tracks where the searcher head is at -- Process entire status replace the %z{sep} & %Z{sep} placeholders -- with proper transitional separator. while str_checked ~= nil do str_checked = status:find('%%', str_checked) if str_checked == nil then break end table.insert(status_applied, status:sub(copied_pos, str_checked - 1)) -- -1 so we don't copy '%' copied_pos = str_checked local next_char = modules.utils.charAt(status, str_checked + 1) if next_char == '#' then -- %#hl_name# highlights last_hl = status:match('^%%#(.-)#', str_checked) str_checked = str_checked + #last_hl + 3 elseif next_char == 'z' then -- %z{sep} is marker for left separator and local sep = status:match('^%%z{(.-)}', str_checked) str_checked = str_checked + #sep + 4 -- 4 = len(%{}) if not (last_hl == nil and last_hl_reseted) then local trans_sep = fill_section_separator(status, is_focused, str_checked, last_hl, sep, false) if trans_sep then table.insert(status_applied, trans_sep) end end if last_hl_reseted then last_hl_reseted = false end copied_pos = str_checked elseif next_char == 'Z' then -- %Z{sep} is marker for right separator and local sep = status:match('^%%Z{(.-)}', str_checked) str_checked = str_checked + #sep + 4 -- 4 = len(%{}) if status:find('^%%z', str_checked) or status:find('^%%<%%Z', str_checked) then -- When transitional right_sep and left_sep are right next to each other -- and in this exact order skip the left sep as we can't draw both. str_checked = status:find('}', str_checked) + 1 end local trans_sep = fill_section_separator(status, is_focused, str_checked, last_hl, sep, true) if trans_sep then table.insert(status_applied, trans_sep) end copied_pos = str_checked elseif next_char == '%' then str_checked = str_checked + 2 -- Skip the following % too elseif next_char == '=' and last_hl and (last_hl:find('^lualine_a') or last_hl:find('^lualine_b')) then -- TODO: Fix this properly -- This check for lualine_a and lualine_b is dumb. It doesn't guarantee -- c or x section isn't present. Worst case scenario after this patch -- we have another visual bug that occurs less frequently. -- Annoying Edge Cases last_hl = nil last_hl_reseted = true str_checked = str_checked + 1 -- Skip the following % too else str_checked = str_checked + 1 -- Push it forward to avoid inf loop end end table.insert(status_applied, status:sub(copied_pos)) -- Final chunk return table.concat(status_applied) end --- creates the statusline string ---@param sections table : section config where components are replaced with --- component objects ---@param is_focused boolean : whether being evaluated for focused window or not ---@return string statusline string local statusline = modules.utils.retry_call_wrap(function(sections, is_focused, is_winbar) -- The sequence sections should maintain [SECTION_SEQUENCE] local section_sequence = { 'a', 'b', 'c', 'x', 'y', 'z' } local status = {} local applied_midsection_divider = false local applied_trunc = false for _, section_name in ipairs(section_sequence) do if sections['lualine_' .. section_name] then -- insert highlight+components of this section to status_builder local section_data = modules.utils_section.draw_section(sections['lualine_' .. section_name], section_name, is_focused) if #section_data > 0 then if not applied_midsection_divider and section_name > 'c' then applied_midsection_divider = true section_data = modules.highlight.format_highlight('c', is_focused) .. '%=' .. section_data end if not applied_trunc and section_name > 'b' then applied_trunc = true section_data = '%<' .. section_data end table.insert(status, section_data) end end end if applied_midsection_divider == false and config.options.always_divide_middle ~= false and not is_winbar then -- When non of section x,y,z is present table.insert(status, modules.highlight.format_highlight('c', is_focused) .. '%=') end return apply_transitional_separators(table.concat(status), is_focused) end) --- check if any extension matches the filetype and return proper sections ---@param current_ft_list string[] : filetype name of current file ---@param is_focused boolean : whether being evaluated for focused window or not ---@return table|nil : (section_table) section config where components are replaced with --- component objects -- TODO: change this so it uses a hash table instead of iteration over list -- to improve redraws. Add buftype / bufname for extensions -- or some kind of cond ? local function get_extension_sections(current_ft_list, is_focused, sec_name) for _, ft in ipairs(current_ft_list) do for _, extension in ipairs(config.extensions) do if vim.tbl_contains(extension.filetypes, ft) then if is_focused then return extension[sec_name] else return extension['inactive_' .. sec_name] or extension[sec_name] end end end end return nil end ---@return string statusline string for tabline local function tabline() return statusline(config.tabline, 3) end local function notify_theme_error(theme_name) local message_template = theme_name ~= 'auto' and [[ ### options.theme Theme `%s` not found, falling back to `auto`. Check if spelling is right. ]] or [[ ### options.theme Theme `%s` failed, falling back to `gruvbox`. This shouldn't happen. Please report the issue at https://github.com/nvim-lualine/lualine.nvim/issues . Also provide what colorscheme you're using. ]] modules.utils_notices.add_notice(string.format(message_template, theme_name)) end --- Sets up theme by defining hl groups and setting theme cache in 'highlight.lua'. --- Uses 'options.theme' variable to apply the theme: --- - If the value is a string, it'll load a theme of that name. --- - If it's a table, it's directly used as the theme. --- If loading the theme fails, this falls back to 'auto' theme. --- If the 'auto' theme also fails, this falls back to 'gruvbox' theme. --- Also sets up auto command to reload lualine on ColorScheme or background changes. local function setup_theme() local function get_theme_from_config() local theme_name = config.options.theme if type(theme_name) == 'string' then local ok, theme = pcall(modules.loader.load_theme, theme_name) if ok and theme then return theme end elseif type(theme_name) == 'table' then -- use the provided theme as-is return config.options.theme elseif type(theme_name) == 'function' then -- call function and use returned (dyanmic) theme, either as-is or as a string local ok, dynamic_theme = pcall(theme_name) if ok and (type(dynamic_theme) == 'string') then local ok_string, theme = pcall(modules.loader.load_theme, dynamic_theme) if ok_string and theme then return theme end elseif ok and (type(dynamic_theme) == 'table') then return dynamic_theme else local error_message = 'Invalid theme type returned from function: ' .. type(dynamic_theme) notify_theme_error(error_message) return dynamic_theme end end if theme_name ~= 'auto' then notify_theme_error(theme_name) local ok, theme = pcall(modules.loader.load_theme, 'auto') if ok and theme then return theme end end notify_theme_error('auto') return modules.loader.load_theme('gruvbox') end local theme = get_theme_from_config() modules.highlight.create_highlight_groups(theme) vim.cmd([[autocmd lualine ColorScheme * lua require'lualine'.setup() autocmd lualine OptionSet background lua require'lualine'.setup()]]) end ---@alias StatusDispatchSecs ---| 'sections' ---| 'winbar' --- generates lualine.statusline & lualine.winbar function --- creates a closer that can draw sections of sec_name. ---@param sec_name StatusDispatchSecs ---@return function(focused:bool):string local function status_dispatch(sec_name) return function(focused) local retval local current_ft = refresh_real_curwin and vim.api.nvim_buf_get_option(vim.api.nvim_win_get_buf(refresh_real_curwin), 'filetype') or vim.bo.filetype local current_ft_list = vim.split(current_ft, '%.') -- handle compound filetypes c.doxygen local is_focused = focused ~= nil and focused or modules.utils.is_focused() for _, ft in ipairs(current_ft_list) do if vim.tbl_contains(config.options.disabled_filetypes[(sec_name == 'sections' and 'statusline' or sec_name)], ft) then -- disable on specific filetypes return nil end end local extension_sections = get_extension_sections(current_ft_list, is_focused, sec_name) if extension_sections ~= nil then retval = statusline(extension_sections, is_focused, sec_name == 'winbar') else retval = statusline(config[(is_focused and '' or 'inactive_') .. sec_name], is_focused, sec_name == 'winbar') end return retval end end ---Determines if a focus event for this window should be ignored. --- ---@param user_config table The user's config ---@param win number The Neovim window handle to check ---@return boolean Whether focus events for this window should be ignored local function should_ignore_focus(user_config, win) local ignore_focus = user_config.options.ignore_focus if type(ignore_focus) == 'table' then -- ignore focus on filetypes listed in options.ignore_focus local buf = vim.api.nvim_win_get_buf(win) local filetype = vim.api.nvim_buf_get_option(buf, 'filetype') return vim.tbl_contains(ignore_focus, filetype) elseif type(ignore_focus) == 'function' then return vim.api.nvim_win_call(win, function() return ignore_focus(win) end) else return false end end local refresh_event_queue = { has_events = false, statusline = {}, tabline = {}, winbar = {}, } ---@alias LualineRefreshOptsKind ---| 'all' ---| 'tabpage' ---| 'window' ---@alias LualineRefreshOptsPlace ---| 'statusline' ---| 'tabline' ---| 'winbar' ---@class LualineRefreshOpts ---@field scope LualineRefreshOptsKind ---@field place LualineRefreshOptsPlace[] ---@field trigger 'timer' | 'init' | 'autocmd' |'unknown' ---@field queued boolean the refresh even was queue and queue is now being processed ---@field force boolean force refresh now instead of queuing --- Refresh contents of lualine ---@param opts LualineRefreshOpts local function refresh(opts) if opts == nil then opts = {} end opts = vim.tbl_extend('keep', opts, { scope = 'tabpage', place = { 'statusline', 'winbar', 'tabline' }, trigger = 'unknown', }) if not opts.queued and not opts.force then for _, place in ipairs(opts.place) do refresh_event_queue['has_events'] = true refresh_event_queue[place] = vim.tbl_extend('force', opts, { place = { place }, queued = true }) end return end local wins = {} local old_actual_curwin = vim.g.actual_curwin local curwin = vim.api.nvim_get_current_win() local curtab = vim.api.nvim_get_current_tabpage() if last_focus[curtab] == nil or not vim.api.nvim_win_is_valid(last_focus[curtab]) then if not should_ignore_focus(config, curwin) then last_focus[curtab] = curwin else local tab_wins = vim.api.nvim_tabpage_list_wins(curtab) if #tab_wins == 1 then last_focus[curtab] = curwin else local focusable_win = curwin for _, win in ipairs(tab_wins) do if not should_ignore_focus(config, win) then focusable_win = win break end end last_focus[curtab] = focusable_win end end else if not should_ignore_focus(config, curwin) then last_focus[curtab] = curwin end end vim.g.actual_curwin = last_focus[curtab] -- gather which windows needs update if opts.scope == 'all' then if vim.tbl_contains(opts.place, 'statusline') or vim.tbl_contains(opts.place, 'winbar') then wins = vim.tbl_filter(function(win) return vim.fn.win_gettype(win) ~= 'popup' end, vim.api.nvim_list_wins()) end elseif opts.scope == 'tabpage' then if vim.tbl_contains(opts.place, 'statusline') or vim.tbl_contains(opts.place, 'winbar') then wins = vim.tbl_filter(function(win) return vim.fn.win_gettype(win) ~= 'popup' end, vim.api.nvim_tabpage_list_wins(0)) end elseif opts.scope == 'window' then wins = { curwin } end -- update them if not timers.halt_stl_refresh and vim.tbl_contains(opts.place, 'statusline') then for _, win in ipairs(wins) do refresh_real_curwin = config.options.globalstatus and last_focus[curtab] or win local set_win = config.options.globalstatus and vim.fn.win_gettype(refresh_real_curwin) == 'popup' and refresh_real_curwin or win local stl_cur = vim.api.nvim_win_call(refresh_real_curwin, M.statusline) local stl_last = modules.nvim_opts.get_cache('statusline', { window = set_win }) if stl_cur or stl_last then modules.nvim_opts.set('statusline', stl_cur, { window = set_win }) end end end if not timers.halt_wb_refresh and vim.tbl_contains(opts.place, 'winbar') then for _, win in ipairs(wins) do refresh_real_curwin = win if vim.api.nvim_win_get_height(win) > 1 then local wbr_cur = vim.api.nvim_win_call(refresh_real_curwin, M.winbar) local wbr_last = modules.nvim_opts.get_cache('winbar', { window = win }) if wbr_cur or wbr_last then modules.nvim_opts.set('winbar', wbr_cur, { window = win }) end end end end if not timers.halt_tal_refresh and vim.tbl_contains(opts.place, 'tabline') then refresh_real_curwin = curwin local tbl_cur = vim.api.nvim_win_call(curwin, tabline) local tbl_last = modules.nvim_opts.get_cache('tabline', { global = true }) if tbl_cur or tbl_last then modules.nvim_opts.set('tabline', tbl_cur, { global = true }) end end vim.g.actual_curwin = old_actual_curwin refresh_real_curwin = nil end --- Sets &tabline option to lualine ---@param hide boolean|nil if should hide tabline local function set_tabline(hide) vim.loop.timer_stop(timers.tal_timer) timers.halt_tal_refresh = true vim.cmd([[augroup lualine_tal_refresh | exe "autocmd!" | augroup END]]) if not hide and next(config.tabline) ~= nil then vim.loop.timer_start( timers.tal_timer, 0, config.options.refresh.tabline, modules.utils.timer_call(timers.tal_timer, 'lualine_tal_refresh', function() refresh { scope = 'tabpage', place = { 'tabline' }, trigger = 'timer' } end, 3, 'lualine: Failed to refresh tabline') ) modules.utils.define_autocmd( table.concat(config.options.refresh.events, ','), '*', "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['tabline'], 'trigger': 'autocmd'})", 'lualine_tal_refresh' ) modules.nvim_opts.set('showtabline', config.options.always_show_tabline and 2 or 1, { global = true }) timers.halt_tal_refresh = false vim.schedule(function() -- imediately refresh upon load -- schedule needed so stuff like filetype detect can run first refresh { scope = 'tabpage', place = { 'tabline' }, trigger = 'init' } end) else modules.nvim_opts.restore('tabline', { global = true }) modules.nvim_opts.restore('showtabline', { global = true }) end end local function check_refresh() if not refresh_event_queue.has_events then return end refresh_event_queue.has_events = nil for place, refresh_cmd in pairs(refresh_event_queue) do if type(refresh_cmd) == 'table' and refresh_cmd.queued == true then refresh(refresh_cmd) refresh_event_queue[place] = {} end end end local function set_refresh_checker() vim.loop.timer_stop(timers.refresh_check_timer) vim.loop.timer_start( timers.refresh_check_timer, 0, config.options.refresh.refresh_time, modules.utils.timer_call( timers.refresh_check_timer, 'lualine_refresh_check', check_refresh, 3, 'lualine: Failed to refresh statusline' ) ) end --- Sets &statusline option to lualine --- adds auto command to redraw lualine on VimResized event ---@param hide boolean|nil if should hide statusline local function set_statusline(hide) vim.loop.timer_stop(timers.stl_timer) timers.halt_stl_refresh = true vim.cmd([[augroup lualine_stl_refresh | exe "autocmd!" | augroup END]]) if not hide and (next(config.sections) ~= nil or next(config.inactive_sections) ~= nil) then modules.nvim_opts.set('statusline', '%#lualine_transparent#', { global = true }) if config.options.globalstatus then modules.nvim_opts.set('laststatus', 3, { global = true }) vim.loop.timer_start( timers.stl_timer, 0, config.options.refresh.statusline, modules.utils.timer_call(timers.stl_timer, 'lualine_stl_refresh', function() refresh { scope = 'window', place = { 'statusline' }, trigger = 'timer' } end, 3, 'lualine: Failed to refresh statusline') ) modules.utils.define_autocmd( table.concat(config.options.refresh.events, ','), '*', "call v:lua.require'lualine'.refresh({'kind': 'window', 'place': ['statusline'], 'trigger': 'autocmd'})", 'lualine_stl_refresh' ) else modules.nvim_opts.set('laststatus', 2, { global = true }) vim.loop.timer_start( timers.stl_timer, 0, config.options.refresh.statusline, modules.utils.timer_call(timers.stl_timer, 'lualine_stl_refresh', function() refresh { scope = 'tabpage', place = { 'statusline' }, trigger = 'timer' } end, 3, 'lualine: Failed to refresh statusline') ) modules.utils.define_autocmd( table.concat(config.options.refresh.events, ','), '*', "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['statusline'], 'trigger': 'autocmd'})", 'lualine_stl_refresh' ) end timers.halt_stl_refresh = false vim.schedule(function() -- imediately refresh upon load -- schedule needed so stuff like filetype detect can run first if config.options.globalstatus then refresh { scope = 'window', place = { 'statusline' }, trigger = 'init' } else refresh { scope = 'tabpage', place = { 'statusline' }, trigger = 'init' } end end) else modules.nvim_opts.restore('statusline', { global = true }) for _, win in ipairs(vim.api.nvim_list_wins()) do modules.nvim_opts.restore('statusline', { window = win }) end modules.nvim_opts.restore('laststatus', { global = true }) end end --- Sets &winbar option to lualine ---@param hide boolean|nil if should unset winbar local function set_winbar(hide) vim.loop.timer_stop(timers.wb_timer) timers.halt_wb_refresh = true vim.cmd([[augroup lualine_wb_refresh | exe "autocmd!" | augroup END]]) if not hide and (next(config.winbar) ~= nil or next(config.inactive_winbar) ~= nil) then vim.loop.timer_start( timers.wb_timer, 0, config.options.refresh.winbar, modules.utils.timer_call(timers.wb_timer, 'lualine_wb_refresh', function() refresh { scope = 'tabpage', place = { 'winbar' }, trigger = 'timer' } end, 3, 'lualine: Failed to refresh winbar') ) modules.utils.define_autocmd( table.concat(config.options.refresh.events, ','), '*', "call v:lua.require'lualine'.refresh({'kind': 'tabpage', 'place': ['winbar'], 'trigger': 'autocmd'})", 'lualine_wb_refresh' ) timers.halt_wb_refresh = false vim.schedule(function() -- imediately refresh upon load. -- schedule needed so stuff like filetype detect can run first refresh { scope = 'tabpage', place = { 'winbar' }, trigger = 'init' } end) elseif vim.fn.has('nvim-0.8') == 1 then modules.nvim_opts.restore('winbar', { global = true }) for _, win in ipairs(vim.api.nvim_list_wins()) do modules.nvim_opts.restore('winbar', { window = win }) end end end ---@alias LualineHideOptsPlace ---| 'statusline' ---| 'tabline' ---| 'winbar' ---@class LualineHideOpts ---@field place LualineHideOptsPlace[] ---@field unhide boolean ---@param opts LualineHideOpts local function hide(opts) if opts == nil then opts = {} end opts = vim.tbl_extend('keep', opts, { place = { 'statusline', 'tabline', 'winbar' }, unhide = false, }) local hide_fn = { statusline = set_statusline, tabline = set_tabline, winbar = set_winbar, } for _, place in ipairs(opts.place) do if hide_fn[place] then hide_fn[place](not opts.unhide) end end end --- Check neovim compatibilitu local function verify_nvim_version() if vim.fn.has('nvim-0.7') == 1 then return true end modules.utils_notices.add_notice([[ ### Incompatible Neovim version Lualine supports neovim 0.7 and up. It seems you're using a older version. Please update to newer version. Or if you have atleast neovim 0.5 you can use older compatible versions of lualine using compat tags like `compat-nvim-0.5`, `compat-nvim-0.6`. ]]) return false end -- lualine.setup function --- sets new user config --- This function doesn't load components/theme etc... They are done before --- first statusline redraw and after new config. This is more efficient when --- lualine config is done in several setup calls as chunks. This way --- we don't initialize components just to throw them away. Instead they are --- initialized when we know we will use them. --- sets &last_status to 2 ---@param user_config table table local function setup(user_config) if package.loaded['lualine.utils.notices'] then -- When notices module is not loaded there are no notices to clear. modules.utils_notices.clear_notices() end if verify_nvim_version() then config = modules.config_module.apply_configuration(user_config) vim.cmd([[augroup lualine | exe "autocmd!" | augroup END]]) setup_theme() -- load components & extensions modules.loader.load_all(config) set_statusline() set_tabline() set_winbar() set_refresh_checker() end if package.loaded['lualine.utils.notices'] then modules.utils_notices.notice_message_startup() end end M = { setup = setup, statusline = status_dispatch('sections'), tabline = tabline, get_config = modules.config_module.get_config, refresh = refresh, winbar = status_dispatch('winbar'), hide = hide, } return M ================================================ FILE: lua/lualine_require.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local M = {} M.sep = package.config:sub(1, 1) local luv = vim.uv or vim.loop -- Figures out full path of lualine installation local source = debug.getinfo(1, 'S').source if source:sub(1, 1) == '@' then local base_start = source:find(table.concat({ 'lualine.nvim', 'lua', 'lualine_require.lua' }, M.sep)) if base_start then source = source:sub(2, base_start + #'lualine.nvim/lua') if source then M.plugin_dir = source end end end --- checks if name is valid ---@param name string ---@return boolean function M.is_valid_filename(name) local invalid_chars = '[^a-zA-Z0-9_. -]' return name:find(invalid_chars) == nil end ---require module module ---@param module string module_name ---@return any the required module function M.require(module) if not package or not package.loaded then -- Something wrong with execution environment. Maybe plugin manager cleared it. -- Just let regular require handle. We'll use the cache next time. return require(module) end if package.loaded[module] then return package.loaded[module] end local pattern_dir = module:gsub('%.', M.sep) local pattern_path = pattern_dir .. '.lua' if M.plugin_dir then local path = M.plugin_dir .. pattern_path assert(M.is_valid_filename(module), 'Invalid filename') local file_stat, dir_stat file_stat = luv.fs_stat(path) if not file_stat then path = M.plugin_dir .. pattern_dir dir_stat = luv.fs_stat(path) if dir_stat and dir_stat.type == 'directory' then path = path .. M.sep .. 'init.lua' file_stat = luv.fs_stat(path) end end if file_stat and file_stat.type == 'file' then local mod_result = dofile(path) package.loaded[module] = mod_result return mod_result end end pattern_path = table.concat { 'lua/', module:gsub('%.', '/'), '.lua' } local paths = vim.api.nvim_get_runtime_file(pattern_path, true) if #paths <= 0 then pattern_path = table.concat { 'lua/', module:gsub('%.', '/'), '/init.lua' } paths = vim.api.nvim_get_runtime_file(pattern_path, true) end if #paths > 0 then -- put entries from user config path in front local user_config_path = vim.fn.stdpath('config') table.sort(paths, function(a, b) local pattern = table.concat { user_config_path, M.sep } return string.match(a, pattern) or not string.match(b, pattern) end) local mod_result = dofile(paths[1]) package.loaded[module] = mod_result return mod_result end return require(module) end ---requires modules when they are used ---@param modules table k-v table where v is module path and k is name that will --- be indexed ---@return table metatable where when a key is indexed it gets required and cached function M.lazy_require(modules) return setmetatable({}, { __index = function(self, key) local loaded = rawget(self, key) if loaded ~= nil then return loaded end local module_location = modules[key] if module_location == nil then return nil end local module = M.require(module_location) rawset(self, key, module) return module end, }) end return M ================================================ FILE: scripts/docgen.sh ================================================ # Copyright (c) 2020-2021 shadmansaleh # MIT license, see LICENSE for more details. PANVIMDOC_TAG_VERSION="v2.7.1" # panvimdoc version # panvimdocs metadata PANVIMDOC_VIMDOC="lualine" PANVIMDOC_DESCRIPTION="fast and easy to configure statusline plugin for neovim" PANVIMDOC_PANDOC="README.md" PANVIMDOC_VERSION="NVIM v0.5.0" PANVIMDOC_TOC=true PANDOC_OUTPUT="doc/lualine.txt" PANVIMDOC_INSTALLED=false # Whether panvimdoc was installed by this script if [ ! -d "panvimdoc/" ];then # Grab panvimdoc if not present PANVIMDOC_INSTALLED=true echo "Installing panvimdoc" git clone --depth 1\ --branch "${PANVIMDOC_TAG_VERSION}"\ "https://github.com/kdheepak/panvimdoc" "panvimdoc" fi echo "Generating docs" pandoc --metadata=project:"${PANVIMDOC_VIMDOC}"\ --metadata=toc:${PANVIMDOC_TOC}\ --metadata=vimversion:"${PANVIMDOC_VERSION}"\ --metadata=description:"${PANVIMDOC_DESCRIPTION}"\ --lua-filter ./panvimdoc/scripts/skip-blocks.lua\ --lua-filter ./panvimdoc/scripts/include-files.lua\ -t ./panvimdoc/scripts/panvimdoc.lua\ -o "${PANDOC_OUTPUT}"\ "${PANVIMDOC_PANDOC}" if $PANVIMDOC_INSTALLED ;then # Remove panvimdoc if it was installed by this script echo "Removing panvimdoc" rm -rf panvimdoc fi ================================================ FILE: scripts/nvim_isolated_conf.sh ================================================ #!/bin/sh # Copyright (c) 2020-2021 shadmansaleh # MIT license, see LICENSE for more details. USAGE="Usage nvim_isolated_conf.sh [OPTIONS] Directory A tool to easily test isolated neovim config Options: -c Create a minimal config tree at Directory -e Edit init.vim of config in Directory -h Show this message -l Load neovim with config from Directory " INIT_TEMPLATE="call plug#begin(\"%s/.local/share/nvim/plugged\") \" Your plugins go here like Plug 'nvim-lualine/lualine.nvim' call plug#end() \" Your Viml part of config goes here \" colorscheme onedark lua << END -- Your lua part of config goes here require'lualine'.setup { } END \" Instructions: \" ------------------------------------------------------------- \" Load this config with nvim_isolated_conf.sh -l %s \" Remember to run :PlugInstall after changing plugin section \" Also delete the comments before putting this file on issue \" That will reduce noise \" You can delete %s once you're done" while getopts "c:e:hl:" arg; do case $arg in h) Help=true;; c) CreateDirInput=$OPTARG;; l) LoadDirInput=$OPTARG;; e) EditDirInput=$OPTARG;; esac done shift $((OPTIND -1)) if ! [ -z $LoadDirInput ];then LoadDir=$(realpath $LoadDirInput) if [ -d $LoadDir ];then export NVIM_CONFIG_HOME=$LoadDir export XDG_CONFIG_HOME=$NVIM_CONFIG_HOME/.config export XDG_DATA_HOME=$NVIM_CONFIG_HOME/.local/share export XDG_CACHE_HOME=$NVIM_CONFIG_HOME/.cache export XDG_STATE_HOME=$NVIM_CONFIG_HOME/.local/state nvim $@ else echo "Sorry can't load neovim config. ${LoadDir} doesn't exist" fi elif ! [ -z $CreateDirInput ];then CreateDir=$(realpath $CreateDirInput) echo "Creating directories" mkdir -p ${CreateDir}/.local/share/nvim/site/autoload mkdir -p ${CreateDir}/.config/nvim echo "Installing VimPlug" wget -q "https://raw.githubusercontent.com/junegunn/vim-plug/master/plug.vim" -O ${CreateDir}/.local/share/nvim/site/autoload/plug.vim echo "Writing minimal init" printf "${INIT_TEMPLATE}" ${CreateDir} ${CreateDir} ${CreateDir} > ${CreateDir}/.config/nvim/init.vim echo "" echo "You can edit the ${CreateDirInput}/.config/nvim/init.vim to put your config" echo "You can load this config with nvim_isolated_conf.sh -l ${CreateDirInput}" echo "You can open config (init.vim) to edit with nvim_isolated_conf.sh -e ${CreateDirInput}" elif ! [ -z $EditDirInput ];then if [ -d $EditDirInput ];then if ! [ -z $EDITOR ];then $EDITOR $EditDirInput/.config/nvim/init.vim else nvim $EditDirInput/.config/nvim/init.vim fi else echo "Sorry can't load neovim config. ${LoadDir} doesn't exist" fi else printf "$USAGE" fi ================================================ FILE: scripts/test_runner.sh ================================================ #!/usr/bin/env bash set -e REPO_DIR=$(git rev-parse --show-toplevel) nvim_t() { nvim -u "$REPO_DIR/tests/minimal_init.lua" -c "set runtimepath+=$REPO_DIR" "$@" } if [ -n "$1" ]; then nvim_t --headless -c "lua require('plenary.busted').run('$1')" else nvim_t --headless -c "lua require'plenary.test_harness'.test_directory( 'tests/', { minimal_init = './tests/minimal_init.lua' })" -c "qa!" fi ================================================ FILE: tests/helpers.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. assert = require('luassert') local eq = assert.are.same local M = {} M.meths = setmetatable({}, { __index = function(_, key) return vim.api['nvim_' .. key] end, }) function M.init_component(component, opts) if component == nil then component = 'special.function_component' else opts.component_name = component end local comp = require('lualine.components.' .. component) if type(comp) == 'table' then comp = comp(opts) elseif type(comp) == 'function' then opts[1] = comp comp = require('lualine.components.special.function_component')(opts) end return comp end -- Checks output of a component M.assert_component = function(component, opts, result, is_active) local comp = M.init_component(component, opts) -- for testing global options eq(result, comp:draw(opts.hl, is_active or true)) end function M.assert_component_instance(comp, result) eq(result, comp:draw(comp.options.hl)) end -- sets defaults for component options M.build_component_opts = function(opts) if not opts then opts = {} end if opts[1] == nil then opts[1] = function() return 'test' end end if not opts.self then opts.self = { section = 'c' } end if not opts.theme then opts.theme = 'gruvbox' end if not opts.hl then opts.hl = '' end if opts.icons_enabled == nil then opts.icons_enabled = true end if not opts.component_separators then opts.component_separators = { left = '', right = '' } end if not opts.section_separators then opts.section_separators = { left = '', right = '' } end return opts end M.P = function(t) print(vim.inspect(t)) end function M.dedent(str, leave_indent) if str == nil then return nil end -- find minimum common indent across lines local indent = nil for line in str:gmatch('[^\n]+') do local line_indent = line:match('^%s+') or '' if indent == nil or #line_indent < #indent then indent = line_indent end end if indent == nil or #indent == 0 then -- no minimum common indent return str end local left_indent = (' '):rep(leave_indent or 0) -- create a pattern for the indent indent = indent:gsub('%s', '[ \t]') -- strip it from the first line str = str:gsub('^' .. indent, left_indent) -- strip it from the remaining lines str = str:gsub('[\n]' .. indent, '\n' .. left_indent) return str end return M ================================================ FILE: tests/minimal_init.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. if os.getenv('TEST_COV') then require('luacov') end -- load lualine and plenary vim.cmd([[ set noswapfile set rtp+=. set rtp+=../plenary.nvim set rtp+=../nvim-web-devicons/ runtime plugin/plenary.vim ]]) ================================================ FILE: tests/spec/component_spec.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local helpers = require('tests.helpers') local eq = assert.are.same local neq = assert.are_not.same local assert_component = helpers.assert_component local build_component_opts = helpers.build_component_opts local stub = require('luassert.stub') describe('Component:', function() it('can select separators', function() local opts = build_component_opts() local comp = require('lualine.components.special.function_component')(opts) -- correct for lualine_c eq('', comp.options.separator) local opts2 = build_component_opts { self = { section = 'y' } } local comp2 = require('lualine.components.special.function_component')(opts2) -- correct for lualine_u eq('', comp2.options.separator) end) it('can provide unique identifier', function() local opts1 = build_component_opts() local comp1 = require('lualine.components.special.function_component')(opts1) local opts2 = build_component_opts() local comp2 = require('lualine.components.special.function_component')(opts2) neq(comp1.component_no, comp2.component_no) end) it('create option highlights', function() local color = { fg = '#224532', bg = '#892345' } local opts1 = build_component_opts { color = color } local hl = require('lualine.highlight') stub(hl, 'create_component_highlight_group') hl.create_component_highlight_group.returns('MyCompHl') local comp1 = require('lualine.components.special.function_component')(opts1) eq('MyCompHl', comp1.options.color_highlight) -- color highlight wan't in options when create_comp_hl was -- called so remove it before assert comp1.options.color_highlight = nil assert .stub(hl.create_component_highlight_group) .was_called_with(color, comp1.options.component_name, comp1.options, false) hl.create_component_highlight_group:revert() color = 'MyHl' local opts2 = build_component_opts { color = color } stub(hl, 'create_component_highlight_group') hl.create_component_highlight_group.returns('MyCompLinkedHl') local comp2 = require('lualine.components.special.function_component')(opts2) eq('MyCompLinkedHl', comp2.options.color_highlight) -- color highlight wan't in options when create_comp_hl was -- called so remove it before assert comp2.options.color_highlight = nil assert .stub(hl.create_component_highlight_group) .was_called_with(color, comp2.options.component_name, comp2.options, false) hl.create_component_highlight_group:revert() end) it('can draw', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } assert_component(nil, opts, 'test') end) it('can apply separators', function() local opts = build_component_opts { padding = 0 } assert_component(nil, opts, 'test') end) it('can apply default highlight', function() local opts = build_component_opts { padding = 0, hl = '%#My_highlight#' } assert_component(nil, opts, '%#My_highlight#test') opts = build_component_opts { function() return '%#Custom_hl#test' end, padding = 0, hl = '%#My_highlight#', } assert_component(nil, opts, '%#Custom_hl#test%#My_highlight#') opts = build_component_opts { function() return 'in middle%#Custom_hl#test' end, padding = 0, hl = '%#My_highlight#', } assert_component(nil, opts, '%#My_highlight#in middle%#Custom_hl#test%#My_highlight#') end) describe('Global options:', function() it('left_padding', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = { left = 5 }, } assert_component(nil, opts, ' test') end) it('right_padding', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = { right = 5 }, } assert_component(nil, opts, 'test ') end) it('padding', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 5, } assert_component(nil, opts, ' test ') end) it('icon', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, icon = '0', } assert_component(nil, opts, '0 test') end) it('icons_enabled', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, icons_enabled = true, icon = '0', } assert_component(nil, opts, '0 test') local opts2 = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, icons_enabled = false, icon = '0', } assert_component(nil, opts2, 'test') end) it('separator', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, separator = '|', } assert_component(nil, opts, 'test|') end) it('fmt', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, fmt = function(data) return data:sub(1, 1):upper() .. data:sub(2, #data) end, } assert_component(nil, opts, 'Test') end) it('cond', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, cond = function() return true end, } assert_component(nil, opts, 'test') local opts2 = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, cond = function() return false end, } assert_component(nil, opts2, '') end) it('color', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, color = 'MyHl', } local comp = require('lualine.components.special.function_component')(opts) local custom_link_hl_name = 'lualine_c_' .. comp.options.component_name eq('%#' .. custom_link_hl_name .. '#test', comp:draw(opts.hl)) local opts2 = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, color = { bg = '#230055', fg = '#223344' }, } local hl = require('lualine.highlight') stub(hl, 'component_format_highlight') hl.component_format_highlight.returns('%#MyCompHl#') local comp2 = require('lualine.components.special.function_component')(opts2) assert_component(nil, opts2, '%#MyCompHl#test') assert.stub(hl.component_format_highlight).was_called_with(comp2.options.color_highlight) hl.component_format_highlight:revert() end) it('draw_empty', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, separator = '>', fmt = function() return '' end, draw_empty = true, } assert_component(nil, opts, '>') end) end) end) describe('Encoding component', function() it('works', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } local tmp_path = 'tmp.txt' local tmp_fp = io.open(tmp_path, 'w') tmp_fp:write('test file') tmp_fp:close() vim.cmd('e ' .. tmp_path) assert_component('encoding', opts, 'utf-8') vim.cmd('bd!') os.remove(tmp_path) end) it('works with BOM and default config', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } local tmp_path = 'tmp.txt' local tmp_fp = io.open(tmp_path, 'w') tmp_fp:write('test file') tmp_fp:close() vim.cmd('e ' .. tmp_path) vim.cmd('set bomb') assert_component('encoding', opts, 'utf-8') vim.cmd('bd!') os.remove(tmp_path) end) it('works with BOM and option enabled', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, show_bomb = true, } local tmp_path = 'tmp.txt' local tmp_fp = io.open(tmp_path, 'w') tmp_fp:write('test file') tmp_fp:close() vim.cmd('e ' .. tmp_path) vim.cmd('set bomb') assert_component('encoding', opts, 'utf-8 [BOM]') vim.cmd('bd!') os.remove(tmp_path) end) end) describe('Fileformat component', function() it('works with icons', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } local fmt = vim.bo.fileformat vim.bo.fileformat = 'unix' assert_component('fileformat', opts, '') vim.bo.fileformat = 'dos' assert_component('fileformat', opts, '') vim.bo.fileformat = 'mac' assert_component('fileformat', opts, '') vim.bo.fileformat = fmt end) it('works without icons', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, icons_enabled = false, } assert_component('fileformat', opts, vim.bo.fileformat) end) end) describe('Filetype component', function() local filetype before_each(function() filetype = vim.bo.filetype vim.bo.filetype = 'lua' end) after_each(function() vim.bo.filetype = filetype end) it('does not add icon when library unavailable', function() local old_require = _G.require function _G.require(...) if select(1, ...) == 'nvim-web-devicons' then error('Test case not suppose to have web-dev-icon 👀') end return old_require(...) end local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } assert_component('filetype', opts, 'lua') _G.require = old_require end) it('colors nvim-web-devicons icons', function() vim.g.actual_curwin = tostring(vim.api.nvim_get_current_win()) stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:t').returns('test.lua') local hl = require('lualine.highlight') stub(hl, 'create_component_highlight_group') hl.create_component_highlight_group.returns { name = 'MyCompHl', no_mode = false, section = 'a' } stub(hl, 'format_highlight') hl.format_highlight.returns('%#lualine_c_normal#') local utils = require('lualine.utils.utils') stub(utils, 'extract_highlight_colors') utils.extract_highlight_colors.returns('#000') local devicons = require('nvim-web-devicons') stub(devicons, 'get_icon') devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group') local opts = build_component_opts { component_separators = { left = '', right = '' }, hl = '%#lualine_c_normal#', padding = 0, colored = true, icon_only = false, } assert_component('filetype', opts, '%#MyCompHl_normal#* %#lualine_c_normal#lua%#lualine_c_normal#') assert.stub(devicons.get_icon).was_called_with('test.lua') assert.stub(utils.extract_highlight_colors).was_called_with('test_highlight_group', 'fg') assert .stub(hl.create_component_highlight_group) .was_called_with({ fg = '#000' }, 'filetype_test_highlight_group', opts, false) assert.stub(vim.fn.expand).was_called_with('%:t') devicons.get_icon:revert() utils.extract_highlight_colors:revert() hl.create_component_highlight_group:revert() hl.format_highlight:revert() vim.fn.expand:revert() vim.g.actual_curwin = nil end) it("doesn't color when colored is false", function() stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:t').returns('test.lua') local hl = require('lualine.highlight') stub(hl, 'create_component_highlight_group') local utils = require('lualine.utils.utils') stub(utils, 'extract_highlight_colors') local devicons = require('nvim-web-devicons') stub(devicons, 'get_icon') devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group') local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, colored = false, } assert_component('filetype', opts, '* lua') assert.stub(devicons.get_icon).was_called_with('test.lua') assert.stub(utils.extract_highlight_colors).was_not_called() assert.stub(hl.create_component_highlight_group).was_not_called() assert.stub(vim.fn.expand).was_called_with('%:t') devicons.get_icon:revert() utils.extract_highlight_colors:revert() hl.create_component_highlight_group:revert() vim.fn.expand:revert() end) it('displays only icon when icon_only is true', function() stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:t').returns('test.lua') local hl = require('lualine.highlight') stub(hl, 'create_component_highlight_group') local utils = require('lualine.utils.utils') stub(utils, 'extract_highlight_colors') local devicons = require('nvim-web-devicons') stub(devicons, 'get_icon') devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group') local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, colored = false, icon_only = true, } assert_component('filetype', opts, '* ') assert.stub(devicons.get_icon).was_called_with('test.lua') assert.stub(utils.extract_highlight_colors).was_not_called() assert.stub(hl.create_component_highlight_group).was_not_called() assert.stub(vim.fn.expand).was_called_with('%:t') devicons.get_icon:revert() utils.extract_highlight_colors:revert() hl.create_component_highlight_group:revert() vim.fn.expand:revert() end) it('displays right aligned icon when icon.align is "right"', function() stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:t').returns('test.lua') local hl = require('lualine.highlight') stub(hl, 'create_component_highlight_group') local utils = require('lualine.utils.utils') stub(utils, 'extract_highlight_colors') local devicons = require('nvim-web-devicons') stub(devicons, 'get_icon') devicons.get_icon.on_call_with('test.lua').returns('*', 'test_highlight_group') local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, colored = false, icon_only = false, icon = { align = 'right' }, } assert_component('filetype', opts, 'lua * ') assert.stub(devicons.get_icon).was_called_with('test.lua') assert.stub(utils.extract_highlight_colors).was_not_called() assert.stub(hl.create_component_highlight_group).was_not_called() assert.stub(vim.fn.expand).was_called_with('%:t') devicons.get_icon:revert() utils.extract_highlight_colors:revert() hl.create_component_highlight_group:revert() vim.fn.expand:revert() end) it('uses filetype lookup when file has no extension', function() stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:t').returns('test') local hl = require('lualine.highlight') stub(hl, 'create_component_highlight_group') local utils = require('lualine.utils.utils') stub(utils, 'extract_highlight_colors') local devicons = require('nvim-web-devicons') stub(devicons, 'get_icon') devicons.get_icon.on_call_with('test').returns(nil) stub(devicons, 'get_icon_by_filetype') devicons.get_icon_by_filetype.on_call_with('lua').returns('*', 'test_highlight_group') local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, colored = false, icon_only = false, } assert_component('filetype', opts, '* lua') assert.stub(devicons.get_icon).was_called_with('test') assert.stub(devicons.get_icon_by_filetype).was_called_with('lua') assert.stub(utils.extract_highlight_colors).was_not_called() assert.stub(hl.create_component_highlight_group).was_not_called() assert.stub(vim.fn.expand).was_called_with('%:t') devicons.get_icon_by_filetype:revert() devicons.get_icon:revert() utils.extract_highlight_colors:revert() hl.create_component_highlight_group:revert() vim.fn.expand:revert() end) end) describe('Hostname component', function() it('works', function() stub(vim.loop, 'os_gethostname') vim.loop.os_gethostname.returns('localhost') local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } assert_component('hostname', opts, 'localhost') vim.loop.os_gethostname:revert() end) end) describe('Location component', function() it('works', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } assert_component('location', opts, ' 1:1 ') vim.cmd('normal! 9o') assert_component('location', opts, ' 10:1 ') vim.api.nvim_win_set_cursor(0, { 5, 0 }) assert_component('location', opts, ' 5:1 ') -- test column number vim.cmd('normal! oTest') assert_component('location', opts, ' 6:4 ') -- test column number in line containing cyrillic symbols vim.cmd('normal! oТест') assert_component('location', opts, ' 7:4 ') vim.cmd('bdelete!') end) end) describe('Progress component', function() it('works', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } assert_component('progress', opts, 'Top') vim.cmd('normal! 9o') assert_component('progress', opts, 'Bot') vim.api.nvim_win_set_cursor(0, { 5, 0 }) assert_component('progress', opts, '50%%') vim.cmd('bdelete!') end) end) describe('Mode component', function() it('works', function() stub(vim.api, 'nvim_get_mode') vim.api.nvim_get_mode.returns { mode = 'n', blocking = false } local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } assert_component('mode', opts, 'NORMAL') vim.api.nvim_get_mode:revert() end) end) describe('FileSize component', function() it('works', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } local fname = 'test-file.txt' local f = io.open(fname, 'w') f:write(string.rep('........................................\n', 200)) f:close() vim.cmd(':edit ' .. fname) assert_component('filesize', opts, '8.0k') vim.cmd(':bdelete!') os.remove(fname) end) end) describe('Filename component', function() it('works', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, file_status = false, path = 0, } vim.cmd(':e test-file.txt') assert_component('filename', opts, 'test-file.txt') vim.cmd(':bdelete!') end) it('can show file_status', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, file_status = true, path = 0, } vim.cmd(':e test-file.txt') vim.bo.modified = false assert_component('filename', opts, 'test-file.txt') vim.bo.modified = true assert_component('filename', opts, 'test-file.txt [+]') vim.bo.ro = true assert_component('filename', opts, 'test-file.txt [+][-]') vim.bo.modified = false assert_component('filename', opts, 'test-file.txt [-]') vim.cmd(':bdelete!') end) it('can show new_file_status', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, newfile_status = true, path = 0, } vim.cmd(':e new-file.txt') assert_component('filename', opts, 'new-file.txt [New]') vim.bo.modified = true assert_component('filename', opts, 'new-file.txt [+][New]') vim.cmd(':bdelete!') end) it('can show relative path', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, file_status = false, path = 1, } vim.cmd(':e test-file.txt') assert_component('filename', opts, vim.fn.expand('%:~:.')) vim.cmd(':bdelete!') end) it('can show full path', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, file_status = false, path = 2, shorting_target = 0, } vim.cmd(':e test-file.txt') assert_component('filename', opts, vim.fn.expand('%:p')) vim.cmd(':bdelete!') end) it('shortens path', function() stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:p').returns('/home/foobar/test/test.lua') stub(vim.fn, 'winwidth') vim.fn.winwidth.on_call_with(0).returns(100) local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, file_status = false, path = 2, shorting_target = 90, } vim.cmd(':e test-file.txt') assert_component('filename', opts, '/h/f/t/test.lua') vim.cmd(':bdelete!') vim.fn.winwidth:revert() vim.fn.expand:revert() end) it('shortens path with tilde', function() stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:p:~').returns('~/test/test.lua') stub(vim.fn, 'winwidth') vim.fn.winwidth.on_call_with(0).returns(100) local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, file_status = false, path = 3, shorting_target = 90, } vim.cmd(':e test-file.txt') assert_component('filename', opts, '~/t/test.lua') vim.cmd(':bdelete!') vim.fn.winwidth:revert() vim.fn.expand:revert() end) it('shortens path with hidden directory', function() stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:p').returns('/home/foobar/.test/test.lua') stub(vim.fn, 'winwidth') vim.fn.winwidth.on_call_with(0).returns(100) local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, file_status = false, path = 2, shorting_target = 90, } vim.cmd(':e test-file.txt') assert_component('filename', opts, '/h/f/.t/test.lua') vim.cmd(':bdelete!') vim.fn.winwidth:revert() vim.fn.expand:revert() end) it('shortens path with %', function() stub(vim.fn, 'expand') vim.fn.expand.on_call_with('%:p').returns('%dir1/%dir2/%actual_%file') stub(vim.fn, 'winwidth') vim.fn.winwidth.on_call_with(0).returns(100) local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, file_status = false, path = 2, shorting_target = 90, } vim.cmd(':e test-file.txt') assert_component('filename', opts, '%%/%%/%%actual_%%file') vim.cmd(':bdelete!') vim.fn.winwidth:revert() vim.fn.expand:revert() end) end) describe('vim option & variable component', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } local function assert_vim_var_component(name, options, result) options[1] = name assert_component('special.vim_var_component', options, result) opts[1] = nil end it('works with variable', function() assert_vim_var_component('g:gvar', opts, '') vim.g.gvar = 'var1' assert_vim_var_component('g:gvar', opts, 'var1') vim.g.gvar = 'var2' assert_vim_var_component('g:gvar', opts, 'var2') vim.b.gvar = 'bvar1' assert_vim_var_component('b:gvar', opts, 'bvar1') vim.w.gvar = 'wvar1' assert_vim_var_component('w:gvar', opts, 'wvar1') end) it('can index dictionaries', function() vim.g.gvar = { a = { b = 'var-value' } } assert_vim_var_component('g:gvar.a.b', opts, 'var-value') end) it('works with options', function() local old_number = vim.wo.number vim.wo.number = false assert_vim_var_component('wo:number', opts, 'false') vim.wo.number = old_number local old_tw = vim.go.tw vim.go.tw = 80 assert_vim_var_component('go:tw', opts, '80') vim.go.tw = old_tw end) end) describe('Vim option & variable component', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } local function assert_vim_var_component(name, options, result) options[1] = name assert_component('special.eval_func_component', options, result) opts[1] = nil end it('works with vim function', function() vim.cmd([[ func! TestFunction() abort return "TestVimFunction" endf ]]) assert_vim_var_component('TestFunction', opts, 'TestVimFunction') vim.cmd('delfunction TestFunction') end) it('works with lua expression', function() _G.TestFunction = function() return 'TestLuaFunction' end assert_vim_var_component('TestFunction()', opts, 'TestLuaFunction') _G.TestFunction = nil end) end) describe('Branch component', function() -- these tests are broken in wsl will look at them later if vim.fn.has('wsl') == 1 then return end local tmpdir local file local git = function(...) return vim.fn.system( "git -c user.name='asdf' -c user.email='asdf@jlk.org' -C " .. tmpdir .. ' ' .. string.format(...) ) end local assert_comp_ins = helpers.assert_component_instance before_each(function() tmpdir = os.tmpname() os.remove(tmpdir) file = tmpdir .. '/test.txt' vim.fn.mkdir(tmpdir, 'p') git('init -b test_branch') vim.cmd([[aug lualine au! aug END ]]) end) after_each(function() os.remove(tmpdir) end) it('works with regular branches', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, } local branch_comp = helpers.init_component('branch', opts) vim.cmd('e ' .. file) assert_comp_ins(branch_comp, ' test_branch') git('checkout -b test_branch2') vim.cmd('e k') vim.cmd('bd') vim.cmd('e ' .. file) opts.icons_enabled = false assert_comp_ins(branch_comp, 'test_branch2') end) it('works in detached head mode', function() local opts = build_component_opts { component_separators = { left = '', right = '' }, icons_enabled = false, padding = 0, } git('checkout -b test_branch2') git('commit --allow-empty -m "test commit1"') git('commit --allow-empty -m "test commit2"') git('commit --allow-empty -m "test commit3"') git('checkout HEAD~1') vim.cmd('e ' .. file) local rev = git('rev-parse --short=6 HEAD'):sub(1, 6) assert_component('branch', opts, rev) end) end) describe('lsp_status component', function() local assert_comp_ins = helpers.assert_component_instance local tmpdir local file local opts = build_component_opts { component_separators = { left = '', right = '' }, padding = 0, ignore_lsp = { 'null-ls' }, } local lsp_status_comp before_each(function() require('lualine').setup {} tmpdir = os.tmpname() file = os.tmpname() .. '/test.lua' vim.cmd([[ augroup lualine autocmd! augroup END ]]) lsp_status_comp = helpers.init_component('lsp_status', opts) end) after_each(function() os.remove(tmpdir) end) it('is empty when no LSP', function() vim.cmd('edit ' .. file) stub(vim.lsp, 'get_clients') vim.lsp.get_clients.on_call_with({ bufnr = vim.api.nvim_get_current_buf() }).returns {} assert_comp_ins(lsp_status_comp, '') end) it('is empty when LSP is ignored', function() vim.cmd('edit ' .. file) stub(vim.lsp, 'get_clients') vim.lsp.get_clients.on_call_with({ bufnr = vim.api.nvim_get_current_buf() }).returns { { id = 2, name = 'null-ls' }, } assert_comp_ins(lsp_status_comp, '') end) it('shows LSP name and icon when attached', function() vim.cmd('edit ' .. file) stub(vim.lsp, 'get_clients') vim.lsp.get_clients.on_call_with({ bufnr = vim.api.nvim_get_current_buf() }).returns { { id = 2, name = 'lua_ls' } } vim.cmd('edit ' .. file) assert_comp_ins(lsp_status_comp, ' lua_ls') end) it('shows LSP name and icon in older nvim version', function() vim.cmd('edit ' .. file) vim.lsp.get_clients = nil stub(vim.lsp, 'get_active_clients') ---@diagnostic disable-next-line: deprecated vim.lsp.get_active_clients .on_call_with({ bufnr = vim.api.nvim_get_current_buf() }) .returns { { id = 2, name = 'lua_ls' } } assert_comp_ins(lsp_status_comp, ' lua_ls') end) it('shows LSP progress when supported', function() vim.cmd('edit ' .. file) stub(vim.lsp, 'get_clients') vim.lsp.get_clients.on_call_with({ bufnr = vim.api.nvim_get_current_buf() }).returns { { id = 2, name = 'lua_ls' } } if vim.uv and vim.uv.hrtime then stub(vim.uv, 'hrtime') vim.uv.hrtime.on_call_with().returns(12 * 1e6 * 80) elseif vim.loop and vim.loop.hrtime then stub(vim.loop, 'hrtime') vim.loop.hrtime.on_call_with().returns(12 * 1e6 * 80) end local ok = pcall(vim.api.nvim_exec_autocmds, 'LspProgress', { data = { client_id = 2, params = { value = { kind = 'begin' } } }, }) -- Skip assertion if LSP progress updates are not supported by the current `nvim` version. if ok then -- Use spinner symbol 12 (time) % 10 (symbol table size) = 2. assert_comp_ins(lsp_status_comp, ' lua_ls ⠹') end end) it('shows LSP done when supported', function() vim.cmd('edit ' .. file) stub(vim.lsp, 'get_clients') vim.lsp.get_clients.on_call_with({ bufnr = vim.api.nvim_get_current_buf() }).returns { { id = 2, name = 'lua_ls' } } local ok = pcall(vim.api.nvim_exec_autocmds, 'LspProgress', { data = { client_id = 2, params = { value = { kind = 'begin' } } }, }) and pcall(vim.api.nvim_exec_autocmds, 'LspProgress', { data = { client_id = 2, params = { value = { kind = 'end' } } }, }) -- Skip assertion if LSP progress updates are not supported by the current `nvim` version. if ok then assert_comp_ins(lsp_status_comp, ' lua_ls ✓') end end) it('shows LSP done when supported and no begin', function() vim.cmd('edit ' .. file) stub(vim.lsp, 'get_clients') vim.lsp.get_clients.on_call_with({ bufnr = vim.api.nvim_get_current_buf() }).returns { { id = 2, name = 'lua_ls' } } local ok = pcall(vim.api.nvim_exec_autocmds, 'LspProgress', { data = { client_id = 2, params = { value = { kind = 'end' } } }, }) -- Skip assertion if LSP progress updates are not supported by the current `nvim` version. if ok then assert_comp_ins(lsp_status_comp, ' lua_ls ✓') end end) it('does not add unnecessary space separator when LSP progress symbol is empty', function() local opts_no_done = opts opts_no_done.symbols = { done = '' } local lsp_status_comp_no_done = helpers.init_component('lsp_status', opts) vim.cmd('edit ' .. file) stub(vim.lsp, 'get_clients') vim.lsp.get_clients.on_call_with({ bufnr = vim.api.nvim_get_current_buf() }).returns { { id = 2, name = 'lua_ls' } } local ok = pcall(vim.api.nvim_exec_autocmds, 'LspProgress', { data = { client_id = 2, params = { value = { kind = 'begin' } } }, }) and pcall(vim.api.nvim_exec_autocmds, 'LspProgress', { data = { client_id = 2, params = { value = { kind = 'end' } } }, }) -- Skip assertion if LSP progress updates are not supported by the current `nvim` version. if ok then assert_comp_ins(lsp_status_comp_no_done, ' lua_ls') end end) end) ================================================ FILE: tests/spec/config_spec.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local eq = assert.are.same describe('config parsing', function() local config_module = require('lualine.config') describe('options', function() describe('icons_enabled', function() it('default', function() local config = config_module.apply_configuration {} eq(config.options.icons_enabled, true) end) it('custom', function() local config = { options = { icons_enabled = false } } config = config_module.apply_configuration(config) eq(config.options.icons_enabled, false) end) end) describe('theme', function() it('default', function() local config = config_module.apply_configuration {} eq(config.options.theme, 'auto') end) it('custom', function() local config = { options = { theme = 'nord' } } config = config_module.apply_configuration(config) eq(config.options.theme, 'nord') config = { options = { theme = {} } } config = config_module.apply_configuration(config) eq(config.options.theme, {}) end) end) describe('separators', function() it('default', function() local config = config_module.apply_configuration {} eq(config.options.component_separators, { left = '', right = '' }) eq(config.options.section_separators, { left = '', right = '' }) end) it('double separators', function() local config = { options = { component_separators = { left = 'a', right = 'b' }, section_separators = { left = 'c', right = 'd' }, }, } config = config_module.apply_configuration(config) eq(config.options.component_separators, { left = 'a', right = 'b' }) eq(config.options.section_separators, { left = 'c', right = 'd' }) end) describe('single separator', function() it('string', function() local config = { options = { component_separators = 'a', section_separators = 'b' }, } config = config_module.apply_configuration(config) eq(config.options.component_separators, { left = 'a', right = 'a' }) eq(config.options.section_separators, { left = 'b', right = 'b' }) end) it('table', function() local config = { options = { component_separators = { left = 'a', right = 'b' }, section_separators = { left = 'b', right = 'a' }, }, } config = config_module.apply_configuration(config) eq(config.options.component_separators, { left = 'a', right = 'b' }) eq(config.options.section_separators, { left = 'b', right = 'a' }) end) end) it('no separators', function() local config = { options = { component_separators = {}, section_separators = {} }, } config = config_module.apply_configuration(config) eq(config.options.component_separators, {}) eq(config.options.section_separators, {}) end) end) describe('disabled filetypes', function() it('default', function() local config = config_module.apply_configuration {} eq(config.options.disabled_filetypes, { statusline = {}, winbar = {} }) end) it('custom', function() local config = { options = { disabled_filetypes = { 'lua' } } } config = config_module.apply_configuration(config) eq(config.options.disabled_filetypes, { statusline = { 'lua' }, winbar = { 'lua' } }) end) end) describe('non default global option', function() it('default', function() local config = { options = {} } config = config_module.apply_configuration(config) eq(config.options.non_default_global_option, nil) end) it('custom', function() local config = { options = { non_default_global_option = 1 } } config = config_module.apply_configuration(config) eq(config.options.non_default_global_option, 1) end) end) end) describe('sections', function() it('default', function() local config = {} config = config_module.apply_configuration(config) local lualine_default_sections = { lualine_a = { 'mode' }, lualine_b = { 'branch', 'diff', 'diagnostics' }, lualine_c = { 'filename' }, lualine_x = { 'encoding', 'fileformat', 'filetype' }, lualine_y = { 'progress' }, lualine_z = { 'location' }, } eq(config.sections, lualine_default_sections) end) it('custom', function() local custom_sections = { lualine_a = { { 'mode', lower = true } }, lualine_b = { 'branch', { 'branch', lower = true } }, lualine_c = nil, lualine_x = {}, } local expected_sections = { lualine_a = { { 'mode', lower = true } }, lualine_b = { 'branch', { 'branch', lower = true } }, lualine_c = { 'filename' }, lualine_x = {}, lualine_y = { 'progress' }, lualine_z = { 'location' }, } local config = { sections = custom_sections } config = config_module.apply_configuration(config) eq(config.sections, expected_sections) end) end) describe('inactive_sections', function() end) describe('tabline', function() it('default', function() local config = {} config = config_module.apply_configuration(config) eq(config.tabline, {}) end) it('custom', function() local custom_sections = { lualine_a = { { 'mode', lower = true } }, lualine_b = { 'branch', { 'branch', lower = true } }, lualine_c = nil, lualine_x = {}, } local expected_sections = { lualine_a = { { 'mode', lower = true } }, lualine_b = { 'branch', { 'branch', lower = true } }, lualine_x = {}, } local config = { tabline = custom_sections } config = config_module.apply_configuration(config) eq(config.tabline, expected_sections) end) end) describe('extensions', function() it('default', function() local config = { options = {} } config = config_module.apply_configuration(config) eq(config.extensions, {}) end) it('custom', function() local config = { extensions = { 'fugitive' } } config = config_module.apply_configuration(config) eq(config.extensions, { 'fugitive' }) end) end) end) ================================================ FILE: tests/spec/lualine_spec.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local eq = assert.are.same local statusline = require('tests.statusline').new(120, 'active') local inactive_statusline = statusline.new(120, 'inactive') local tabline = statusline.new(120, 'tabline') describe('Lualine', function() local config before_each(function() config = { options = { icons_enabled = true, theme = 'gruvbox', component_separators = { left = '', right = '' }, section_separators = { left = '', right = '' }, disabled_filetypes = { statusline = {}, winbar = {}, }, ignore_focus = {}, always_divide_middle = true, always_show_tabline = true, globalstatus = false, refresh = { statusline = 1000, tabline = 1000, winbar = 1000, refresh_time = 16, -- ~60fps events = { 'WinEnter', 'BufEnter', 'BufWritePost', 'SessionLoadPost', 'FileChangedShellPost', 'VimResized', 'Filetype', 'CursorMoved', 'CursorMovedI', 'ModeChanged', }, }, }, sections = { lualine_a = { 'mode' }, -- We can't test branch component inside lualines repo. -- As branch name will differ in pr/CI. We could setup a dummy repo -- but plenary doesn't yet support setup() & teardown() so replacing -- branch with a dummy component. lualine_b = { { function() return 'master' end, icon = '', }, 'diagnostics', }, lualine_c = { 'filename' }, lualine_x = { 'encoding', 'fileformat', 'filetype' }, lualine_y = { 'progress' }, lualine_z = { 'location' }, }, inactive_sections = { lualine_a = {}, lualine_b = {}, lualine_c = { 'filename' }, lualine_x = { 'location' }, lualine_y = {}, lualine_z = {}, }, tabline = {}, winbar = {}, inactive_winbar = {}, extensions = {}, } vim.opt.swapfile = false vim.cmd('bufdo bdelete') pcall(vim.cmd, 'tabdo tabclose') require('lualine').setup(config) end) it('shows active statusline', function() statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984", nocombine = true } 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: NORMAL } {2:} {3:  master } {4:} {5: [No Name] } {5: } {5:  } {4:} {3: Top } {2:} {1: 1:1 }| ]===]) end) it('shows inactive statusline', function() inactive_statusline:expect([===[ highlights = { 1: lualine_c_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: [No Name] } {1: } {1: 1:1 }| ]===]) end) it('get_config can retrieve config', function() eq(config, require('lualine').get_config()) end) it('can live update config', function() local conf = require('lualine').get_config() conf.sections.lualine_a = {} require('lualine').setup(conf) statusline:expect([===[ highlights = { 1: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 2: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 4: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984", nocombine = true } 5: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } } |{1:  master } {2:} {3: [No Name] } {3: } {3:  } {2:} {1: Top } {4:} {5: 1:1 }| ]===]) end) it('Can work without section separators', function() config.options.section_separators = '' require('lualine').setup(config) statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: NORMAL } {2:  master } {3: [No Name] } {3: } {3:  } {2: Top } {1: 1:1 }| ]===]) end) it('Can work without component_separators', function() table.insert(config.sections.lualine_a, function() return 'test_comp1' end) table.insert(config.sections.lualine_z, function() return 'test_comp2' end) require('lualine').setup(config) statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984", nocombine = true } 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: NORMAL } {1: test_comp1 } {2:} {3:  master } {4:} {5: [No Name] } {5: } {5:  } {4:} {3: Top } {2:} {1: 1:1 } {1: test_comp2 }| ]===]) config.options.component_separators = '' require('lualine').setup(config) statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984", nocombine = true } 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: NORMAL } {1: test_comp1 } {2:} {3:  master } {4:} {5: [No Name] } {5: } {5:  } {4:} {3: Top } {2:} {1: 1:1 } {1: test_comp2 }| ]===]) end) it('mid divider can be disabled on special case', function() config.options.always_divide_middle = false config.sections.lualine_x = {} config.sections.lualine_y = {} config.sections.lualine_z = {} require('lualine').setup(config) statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984", nocombine = true } 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: NORMAL } {2:} {3:  master } {4:} {5: [No Name] }| ]===]) end) it('works with icons disabled', function() config.options.icons_enabled = false config.options.section_separators = '' require('lualine').setup(config) statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: NORMAL } {2: master } {3: [No Name] } {3: } {3: unix } {2: Top } {1: 1:1 }| ]===]) end) it('can be desabled for specific filetypes', function() config.options.disabled_filetypes = { 'test_ft' } require('lualine').setup(config) local old_ft = vim.bo.ft vim.bo.ft = 'test_ft' statusline:expect(nil) vim.bo.ft = old_ft end) it('can apply custom extensions', function() table.insert(config.extensions, { filetypes = { 'test_ft' }, sections = { lualine_a = { function() return 'custom_extension_component' end, }, }, }) local old_ft = vim.bo.ft vim.bo.ft = 'test_ft' require('lualine').setup(config) statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: custom_extension_component } {2:} {3: }| ]===]) vim.bo.ft = old_ft end) it('same extension can be applied to multiple filetypes', function() table.insert(config.extensions, { filetypes = { 'test_ft1', 'test_ft2' }, sections = { lualine_a = { function() return 'custom_extension_component' end, }, }, }) local old_ft = vim.bo.ft vim.bo.ft = 'test_ft1' require('lualine').setup(config) statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: custom_extension_component } {2:} {3: }| ]===]) vim.bo.ft = old_ft statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984", nocombine = true } 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: NORMAL } {2:} {3:  master } {4:} {5: [No Name] } {5: } {5:  } {4:} {3: Top } {2:} {1: 1:1 }| ]===]) vim.bo.ft = 'test_ft2' statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: custom_extension_component } {2:} {3: }| ]===]) vim.bo.ft = old_ft end) -- TODO: figure put why some of the tablines tests fail in CI describe('tabline', function() local tab_conf = vim.deepcopy(config) tab_conf.tabline = { lualine_a = { function() return 'tabline_component' end, }, lualine_b = {}, lualine_c = {}, lualine_x = {}, lualine_y = {}, lualine_z = {}, } it('can use tabline', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { function() return 'tabline_component' end, } require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: tabline_component } {2:} {3: }| ]===]) end) it('can use tabline as statusline', function() local conf = vim.deepcopy(config) conf.tabline = conf.sections conf.sections = {} conf.inactive_sections = {} require('lualine').setup(conf) require('lualine').statusline() -- TODO: check why this test fails because of debounce -- eq('%#Normal#', vim.go.statusline) tabline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_b_normal = { bg = "#504945", fg = "#a89984", nocombine = true } 3: lualine_b_normal = { bg = "#504945", fg = "#ebdbb2", nocombine = true } 4: lualine_transitional_lualine_b_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#504945", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: NORMAL } {2:} {3:  master } {4:} {5: [No Name] } {5: } {5:  } {4:} {3: Top } {2:} {1: 1:1 }| ]===]) end) describe('tabs component', function() it('works', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'tabs', max_length = 1e3 } } vim.cmd('tabnew') vim.cmd('tabnew') require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 } {1: 2 } {2:} {3: 3 } {4:} {5: }| ]===]) vim.cmd('tabprev') tabline:expect([===[ highlights = { 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_tabs_active_to_lualine_a_tabs_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 } {2:} {3: 2 } {4:} {1: 3 } {5: }| ]===]) vim.cmd('tabprev') tabline:expect([===[ highlights = { 1: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_tabs_active_to_lualine_a_tabs_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 } {2:} {3: 2 } {3: 3 } {4: }| ]===]) end) it('mode option can change layout', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'tabs', max_length = 1e3, mode = 0 } } vim.cmd('tabe ' .. 'a.txt') vim.cmd('tabe ' .. 'b.txt') require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 } {1: 2 } {2:} {3: 3 } {4:} {5: }| ]===]) tabline:expect([===[ highlights = { 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 } {1: 2 } {2:} {3: 3 } {4:} {5: }| ]===]) conf.tabline.lualine_a = { { 'tabs', max_length = 1e3, mode = 1 } } require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: [No Name] } {1: a.txt } {2:} {3: b.txt } {4:} {5: }| ]===]) conf.tabline.lualine_a = { { 'tabs', max_length = 1e3, mode = 2 } } require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 [No Name] } {1: 2 a.txt } {2:} {3: 3 b.txt } {4:} {5: }| ]===]) end) end) describe('buffers component', function() it('works', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, icons_enabled = false } } vim.cmd('tabe ' .. 'a.txt') vim.cmd('tabe ' .. 'b.txt') require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: #a.txt } {2:} {3: b.txt } {4:} {1: [No Name] } {MATCH:{5:%s+}|} ]===]) vim.cmd('tabprev') tabline:expect([===[ highlights = { 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: a.txt } {2:} {3: b.txt } {3: #[No Name] } {MATCH:{4:%s+}|} ]===]) vim.cmd('tabprev') tabline:expect([===[ highlights = { 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: a.txt } {1: #b.txt } {2:} {3: [No Name] } {4:} {MATCH:{5:%s+}|} ]===]) end) it('mode option can change layout', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'tabs', max_length = 1e3, mode = 0, icons_enabled = false } } vim.cmd('tabe ' .. 'a.txt') vim.cmd('tabe ' .. 'b.txt') require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } {MATCH:|{1: %d+ }} {MATCH:{1: %d+ }} {2:} {MATCH:{3: %d+ }} {4:} {MATCH:{5:%s+}|} ]===]) conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, mode = 1, icons_enabled = false } } require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } {MATCH:|{1: #%d+ }} {2:} {MATCH:{3: %d+ }} {4:} {MATCH:{1: %d+ }} {MATCH:{5:%s+}|} ]===]) conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, mode = 2, icons_enabled = false } } require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } {MATCH:|{1: #%d+ a.txt }} {2:} {MATCH:{3: %d+ b.txt }} {4:} {MATCH:{1: %d+ %[No Name%] }} {MATCH:{5:%s+}|} ]===]) end) it('can show modified status', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, show_modified_status = true, icons_enabled = false } } require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: [No Name] } {2:} {3: }| ]===]) vim.bo.modified = true tabline:expect([===[ highlights = { 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: [No Name] ● } {2:} {3: }| ]===]) vim.bo.modified = false end) it('can show relative path', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, show_filename_only = false, icons_enabled = false } } require('lualine').setup(conf) require('lualine').statusline() local path = 'aaaaaa/bbbbb/cccccc/ddddd/eeeee/ffff/gggg' vim.fn.mkdir(path, 'p') vim.cmd('e ' .. path .. '/asdf.txt') tabline:expect([===[ highlights = { 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: a/b/c/d/e/f/g/asdf.txt } {2:} {3: }| ]===]) vim.fn.delete(path:match('(%w+)/.*'), 'rf') end) it('can show ellipsis when max_width is crossed', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'buffers', max_length = 1 } } vim.cmd('tabe a.txt') vim.cmd('tabe b.txt') vim.cmd('tabprev') require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 󰈙 a.txt } {2:} {3: ... } {4: }| ]===]) end) it('can show filetype icons', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'buffers', max_length = 1e3, show_filename_only = false } } require('lualine').setup(conf) require('lualine').statusline() vim.cmd('e t.lua') tabline:expect([===[ highlights = { 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1:  t.lua } {2:} {3: }| ]===]) end) it('can show buffer numbers instead of indices (without file names)', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'buffers', mode = 3, max_length = 1e3, icons_enabled = false } } require('lualine').setup(conf) require('lualine').statusline() vim.cmd('e a.txt') vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions. vim.cmd('e b.txt') local bufnr_a = vim.fn.bufnr('a.txt') local bufnr_b = vim.fn.bufnr('b.txt') tabline:expect([===[ highlights = { 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: #]===] .. bufnr_a .. [===[ } {2:} {3: ]===] .. bufnr_b .. [===[ } {4:} {MATCH:{5:%s+}|} ]===]) end) it('can show buffer numbers instead of indices (with file names)', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'buffers', mode = 4, max_length = 1e3, icons_enabled = false } } vim.cmd('e a.txt') vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions. vim.cmd('e b.txt') local bufnr_a = vim.fn.bufnr('a.txt') local bufnr_b = vim.fn.bufnr('b.txt') require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: #]===] .. bufnr_a .. [===[ a.txt } {2:} {3: ]===] .. bufnr_b .. [===[ b.txt } {4:} {MATCH:{5:%s+}|} ]===]) end) it('displays alternate buffer correctly when switching buffers', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'buffers', mode = 3, max_length = 1e3, icons_enabled = false } } require('lualine').setup(conf) require('lualine').statusline() vim.cmd('e a.txt') vim.cmd('silent! bd #') -- NeoVim 0.5 does not create an unnamed buffer. This ensures consistent results between NeoVim versions. vim.cmd('e b.txt') local bufnr_a = vim.fn.bufnr('a.txt') local bufnr_b = vim.fn.bufnr('b.txt') tabline:expect([===[ highlights = { 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: #]===] .. bufnr_a .. [===[ } {2:} {3: ]===] .. bufnr_b .. [===[ } {4:} {MATCH:{5:%s+}|} ]===]) vim.cmd('e a.txt') tabline:expect([===[ highlights = { 1: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_buffers_active_to_lualine_a_buffers_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: ]===] .. bufnr_a .. [===[ } {2:} {3: #]===] .. bufnr_b .. [===[ } {MATCH:{4:%s+}|} ]===]) vim.cmd('bprev') tabline:expect([===[ highlights = { 1: lualine_a_buffers_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 2: lualine_transitional_lualine_a_buffers_inactive_to_lualine_a_buffers_active = { bg = "#a89984", fg = "#3c3836", nocombine = true } 3: lualine_a_buffers_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 4: lualine_transitional_lualine_a_buffers_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: #]===] .. bufnr_a .. [===[ } {2:} {3: ]===] .. bufnr_b .. [===[ } {4:} {MATCH:{5:%s+}|} ]===]) end) end) describe('windows component', function() it('works', function() local conf = vim.deepcopy(tab_conf) conf.tabline.lualine_a = { { 'windows', max_length = 1e3, mode = 2, icons_enabled = false } } vim.cmd('e ' .. 'a.txt') vim.cmd('tabe ' .. 'b.txt') vim.cmd('vsplit ' .. 'c.txt') vim.cmd('tabe ' .. 'd.txt') require('lualine').setup(conf) require('lualine').statusline() tabline:expect([===[ highlights = { 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_windows_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 d.txt } {2:} {3: }| ]===]) vim.cmd('tabprev') tabline:expect([===[ highlights = { 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_windows_active_to_lualine_a_windows_inactive = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_a_windows_inactive = { bg = "#3c3836", bold = true, fg = "#a89984", nocombine = true } 4: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 c.txt } {2:} {3: 2 b.txt } {4: }| ]===]) vim.cmd('tabprev') tabline:expect([===[ highlights = { 1: lualine_a_windows_active = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_windows_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: 1 a.txt } {2:} {3: }| ]===]) end) end) end) describe('diagnostics', function() local diagnostics_conf = vim.deepcopy(config) diagnostics_conf.sections = { lualine_a = { { 'diagnostics', symbols = { error = 'E:', warn = 'W:', info = 'I:', hint = 'H:' }, diagnostics_color = { error = { bg = '#a89984', fg = '#ff0000' }, warn = { bg = '#a89984', fg = '#ffa500' }, info = { bg = '#a89984', fg = '#add8e6' }, hint = { bg = '#a89984', fg = '#d3d3d3' }, }, sources = { function() return {} end, }, }, }, lualine_b = {}, lualine_c = {}, lualine_x = {}, lualine_y = {}, lualine_z = {}, } it('does not show without diagnostics', function() local conf = vim.deepcopy(diagnostics_conf) require('lualine').setup(conf) statusline:expect([===[ highlights = { 1: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: }| ]===]) end) it('shows only positive diagnostics', function() local conf = vim.deepcopy(diagnostics_conf) conf.sections.lualine_a[1].sources[1] = function() return { error = 0, warn = 0, info = 1, hint = 0 } end require('lualine').setup(conf) statusline:expect([===[ highlights = { 1: lualine_a_diagnostics_info = { bg = "#a89984", fg = "#add8e6", nocombine = true } 2: lualine_transitional_lualine_a_diagnostics_info_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: I:1 } {2:} {3: }| ]===]) end) it('shows all diagnostics with same background', function() local conf = vim.deepcopy(diagnostics_conf) conf.sections.lualine_a[1].sources[1] = function() return { error = 1, warn = 2, info = 3, hint = 4 } end require('lualine').setup(conf) statusline:expect([===[ highlights = { 1: lualine_a_diagnostics_error = { bg = "#a89984", fg = "#ff0000", nocombine = true } 2: lualine_a_diagnostics_warn = { bg = "#a89984", fg = "#ffa500", nocombine = true } 3: lualine_a_diagnostics_info = { bg = "#a89984", fg = "#add8e6", nocombine = true } 4: lualine_a_diagnostics_hint = { bg = "#a89984", fg = "#d3d3d3", nocombine = true } 5: lualine_transitional_lualine_a_diagnostics_hint_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 6: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: E:1 } {2:W:2 } {3:I:3 } {4:H:4 } {5:} {6: }| ]===]) end) it('shows all diagnostics with padding when background changes', function() local conf = vim.deepcopy(diagnostics_conf) conf.sections.lualine_a[1].sources[1] = function() return { error = 1, warn = 2, info = 3, hint = 4 } end conf.sections.lualine_a[1].diagnostics_color = { error = { bg = '#ff0000', fg = '#a89984' }, warn = { bg = '#ffa500', fg = '#a89984' }, info = { bg = '#add8e6', fg = '#a89984' }, hint = { bg = '#add8e6', fg = '#a89984' }, } require('lualine').setup(conf) statusline:expect([===[ highlights = { 1: lualine_a_diagnostics_error = { bg = "#ff0000", fg = "#a89984", nocombine = true } 2: lualine_a_diagnostics_warn = { bg = "#ffa500", fg = "#a89984", nocombine = true } 3: lualine_a_diagnostics_info = { bg = "#add8e6", fg = "#a89984", nocombine = true } 4: lualine_a_diagnostics_hint = { bg = "#add8e6", fg = "#a89984", nocombine = true } 5: lualine_transitional_lualine_a_diagnostics_hint_to_lualine_c_normal = { bg = "#3c3836", fg = "#add8e6", nocombine = true } 6: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: E:1 } {2: W:2 } {3: I:3 } {4:H:4 } {5:} {6: }| ]===]) end) end) describe('supports compound filetypes', function() it('disabled filetypes', function() local conf = require('lualine').get_config() conf.options.disabled_filetypes = { 'java' } require('lualine').setup(conf) local old_ft = vim.bo.ft vim.bo.ft = 'lua.java' vim.opt.filetype = 'lua.java' statusline:expect(nil) vim.bo.ft = old_ft end) it('extensions for work on compound filetypes', function() local conf = require('lualine').get_config() table.insert(conf.extensions, { filetypes = { 'test_ft' }, sections = { lualine_a = { function() return 'custom_extension_component' end, }, }, }) local old_ft = vim.bo.ft vim.bo.ft = 'lua.test_ft' require('lualine').setup(conf) statusline:expect([===[ highlights = { 1: lualine_a_normal = { bg = "#a89984", bold = true, fg = "#282828", nocombine = true } 2: lualine_transitional_lualine_a_normal_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } 3: lualine_c_normal = { bg = "#3c3836", fg = "#a89984", nocombine = true } } |{1: custom_extension_component } {2:} {3: }| ]===]) vim.bo.ft = old_ft end) end) end) ================================================ FILE: tests/spec/utils_spec.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. local helpers = require('tests.helpers') local eq = assert.are.same local build_component_opts = helpers.build_component_opts local stub = require('luassert.stub') describe('Utils', function() local utils = require('lualine.utils.utils') it('can retrieve highlight groups', function() local hl2 = { fg = '#aabbcc', bg = '#889977', sp = '#997788', reverse = true, undercurl = true } -- handles non existing hl groups eq(utils.extract_highlight_colors('hl2'), nil) -- create highlight vim.cmd(string.format('hi hl2 guifg=%s guibg=%s guisp=%s gui=reverse,undercurl', hl2.fg, hl2.bg, hl2.sp)) -- Can retrieve entire highlight table eq(utils.extract_highlight_colors('hl2'), hl2) -- Can retrieve specific parts of highlight eq(utils.extract_highlight_colors('hl2', 'fg'), hl2.fg) -- clear hl2 vim.cmd('hi clear hl2') end) it('can extract individual highlight color', function() local fg_clr = '#aabbcc' local bg_clr = '#889977' local sp_clr = '#997788' local def_clr = '#ff0000' local hl_std = { fg = fg_clr, bg = bg_clr } local hl_rvs = { fg = fg_clr, bg = bg_clr, reverse = true } local hl_ul = { sp = sp_clr, undercurl = true } local hl_ul_rvs = { fg = fg_clr, bg = bg_clr, sp = sp_clr, reverse = true, undercurl = true } -- create highlights vim.cmd(string.format('hi hl_std guifg=%s guibg=%s', hl_std.fg, hl_std.bg)) vim.cmd(string.format('hi hl_rvs guifg=%s guibg=%s gui=reverse', hl_rvs.fg, hl_rvs.bg)) vim.cmd(string.format('hi hl_ul guisp=%s gui=undercurl', hl_ul.sp)) vim.cmd( string.format( 'hi hl_ul_rvs guifg=%s guibg=%s guisp=%s gui=reverse,undercurl', hl_ul_rvs.fg, hl_ul_rvs.bg, hl_ul_rvs.sp ) ) -- Can extract color from primary highlight group eq(utils.extract_color_from_hllist('fg', { 'hl_std', 'hl_ul' }, def_clr), fg_clr) -- Can extract color from fallback highlight group eq(utils.extract_color_from_hllist('fg', { 'hl_noexist', 'hl_std' }, def_clr), fg_clr) -- Can fall back to default color on nonexistent color eq(utils.extract_color_from_hllist('fg', { 'hl_ul' }, def_clr), def_clr) -- Can fall back to default color on nonexistent highlight group eq(utils.extract_color_from_hllist('fg', { 'hl_noexist' }, def_clr), def_clr) -- Can extract fallback color eq(utils.extract_color_from_hllist({ 'fg', 'sp' }, { 'hl_ul' }, def_clr), sp_clr) -- Can extract reverse color eq(utils.extract_color_from_hllist('fg', { 'hl_rvs' }, def_clr), bg_clr) -- Can extract fallback reverse color eq(utils.extract_color_from_hllist({ 'sp', 'fg' }, { 'hl_rvs' }, def_clr), bg_clr) -- clear highlights vim.cmd('hi clear hl_std') vim.cmd('hi clear hl_rvs') vim.cmd('hi clear hl_ul') vim.cmd('hi clear hl_ul_rvs') end) it('can shrink list with holes', function() local list_with_holes = { '2', '4', '6', nil, '43', nil, '2', '', 'a', '', 'b', ' ', } local list_without_holes = { '2', '4', '6', '43', '2', 'a', 'b', ' ' } eq(utils.list_shrink(list_with_holes), list_without_holes) end) end) describe('Cterm generator', function() local cterm = require('lualine.utils.color_utils') it('can convert rgb to cterm', function() local colors = { ['#112233'] = 235, ['#7928ae'] = 97, ['#017bdc'] = 68 } for rgb, ct in pairs(colors) do eq(cterm.rgb2cterm(rgb), tostring(ct)) end end) end) describe('Section generator', function() local hl = require('lualine.highlight') stub(hl, 'format_highlight') hl.format_highlight.returns('%#lualine_c_normal#') local sec = require('lualine.utils.section') it('can draw', function() local opts = build_component_opts { section_separators = { left = '', right = '' } } local section = { require('lualine.components.special.function_component')(opts), require('lualine.components.special.function_component')(opts), } eq('%#lualine_c_normal# test %#lualine_c_normal# test ', sec.draw_section(section, 'c', true)) hl.format_highlight:revert() end) it('can remove separators from component with custom colors', function() stub(hl, 'format_highlight') stub(hl, 'get_lualine_hl') hl.format_highlight.returns('%#lualine_MySection_normal#') hl.get_lualine_hl.returns { fg = '#000000', bg = '#ffffff' } vim.g.actual_curwin = tostring(vim.api.nvim_get_current_win()) local opts = build_component_opts { section_separators = { left = '', right = '' } } local opts_colored = build_component_opts { color = 'MyColor' } local opts_colored2 = build_component_opts { color = { bg = '#223344' }, section_separators = { left = '', right = '' }, } local opts_colored3 = build_component_opts { color = { fg = '#223344' }, section_separators = { left = '', right = '' }, } require('lualine.highlight').create_highlight_groups(require('lualine.themes.gruvbox')) local section = { require('lualine.components.special.function_component')(opts), require('lualine.components.special.function_component')(opts_colored), require('lualine.components.special.function_component')(opts), } local highlight_name2 = 'lualine_c_' .. section[2].options.component_name -- Removes separator on string color eq( '%#lualine_MySection_normal# test %#' .. highlight_name2 .. '#' .. ' test %#lualine_MySection_normal# test ', sec.draw_section(section, 'MySection') ) section[2] = require('lualine.components.special.function_component')(opts_colored2) local highlight_name = '%#lualine_c_' .. section[2].options.component_name .. '_normal#' -- Removes separator on color with bg eq( '%#lualine_MySection_normal# test ' .. highlight_name .. ' test %#lualine_MySection_normal# test ', sec.draw_section(section, 'MySection') ) section[2] = require('lualine.components.special.function_component')(opts_colored3) highlight_name2 = '%#lualine_c_' .. section[2].options.component_name .. '_normal#' -- Doesn't remove separator on color without bg eq( '%#lualine_MySection_normal# test ' .. highlight_name2 .. ' test %#lualine_MySection_normal#%#lualine_MySection_normal# test ', sec.draw_section(section, 'MySection') ) vim.g.actual_curwin = nil hl.format_highlight:revert() hl.get_lualine_hl:revert() end) end) ================================================ FILE: tests/statusline.lua ================================================ -- Copyright (c) 2020-2021 shadmansaleh -- MIT license, see LICENSE for more details. --- ## Testing module for lualines statusline --- --- ###Uses: --- --- Create a new instance with status width 120 & for active statusline --- like following. --- --- ``lua --- local statusline = require('tests.statusline').new(120, 'active') --- ``` --- --- To create a new instance with status width 80 & for inactive statusline use following. --- --- ``lua --- local statusline = require('tests.statusline').new(120, 'inactive') --- ``` --- --- Now setup the state you want to test. --- To test you'll call `expect` method on statusline for example. --- --- To create a new instance with status width 80 & tabline --- --- ``lua --- local statusline = require('tests.statusline').new(120, 'tabline') --- ``` --- --- Now setup the state you want to test. --- To test you'll call `expect` method on statusline for example. --- --- ``lua --- statusline:expect([===[ --- highlights = { --- 1: lualine_c_inactive = { bg = "#3c3836", fg = "#a89984" } --- } --- |{1: [No Name] } --- {1: } --- {1: 0:1 }| --- ---]===]) --- ``` --- --- For more flexibility you can match a pattern in expect block. --- ``lua --- statusline:expect([===[ --- highlights = { --- 1: lualine_a_tabs_inactive = { bg = "#3c3836", bold = true, fg = "#a89984" } --- 2: lualine_transitional_lualine_a_tabs_inactive_to_lualine_a_tabs_active = { bg = "#a89984", fg = "#3c3836" } --- 3: lualine_a_tabs_active = { bg = "#a89984", bold = true, fg = "#282828" } --- 4: lualine_transitional_lualine_a_tabs_active_to_lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } --- 5: lualine_c_normal = { bg = "#3c3836", fg = "#a89984" } --- } --- {MATCH:|{1: %d+ }} --- {MATCH:{1: %d+ }} --- {2:} --- {MATCH:{3: %d+ }} --- {4:} --- {MATCH:{5:%s+}|} --- ---]===]) --- ``` --- --- An easy way to create an expect block is to call `snapshot` method --- on statusline where you'll call expect and run the test. It will print --- an expect block based on the state of statusline. You can copy it and --- replace the snapshot call with the expect call. --- --- ``lua --- statusline:snapshot() --- ``` local helpers = require('tests.helpers') local stub = require('luassert.stub') local M = {} local function eval_stl(stl_expr, width, eval_type) local stl_buf, hl_list, stl_eval_res if stl_expr == nil then return nil end stl_eval_res = vim.api.nvim_eval_statusline( stl_expr, { maxwidth = width, highlights = true, fillchar = ' ', use_tabline = (eval_type == 'tabline') } ) stl_buf, hl_list = stl_eval_res.str, stl_eval_res.highlights local hl_map = {} local buf = { 'highlights = {' } local hl_id = 1 for _, hl in ipairs(hl_list) do local hl_name = hl.group if not hl_map[hl_name] then hl_map[hl_name] = require('lualine.utils.utils').extract_highlight_colors(hl_name) or {} table.insert( buf, string.format(' %4d: %s = %s', hl_id, hl_name, vim.inspect(hl_map[hl_name], { newline = ' ', indent = '' })) ) hl_map[hl_name].id = hl_id hl_id = hl_id + 1 end end table.insert(buf, '}') local stl = {} for i = 1, #hl_list do local start, finish = hl_list[i].start, hl_list[i + 1] and hl_list[i + 1].start or #stl_buf if start ~= finish then table.insert( stl, string.format('{%d:%s}', hl_map[hl_list[i].group].id, vim.fn.strpart(stl_buf, start, finish - start)) ) end end table.insert(buf, '|' .. table.concat(stl, '\n') .. '|') table.insert(buf, '') return table.concat(buf, '\n') end function M:expect_expr(expect, expr) if expr == nil then -- test if both are nil when running expect against nil assert.are.same(expect, nil) return end local actual = eval_stl(expr, self.width, self.type) if expect == nil then assert.are.same(expect, actual) return end expect = helpers.dedent(expect) local matched = true local errmsg = {} if expect ~= actual then expect = expect ~= nil and vim.split(expect, '\n') actual = vim.split(actual, '\n') if expect[#expect] == '' then expect[#expect] = nil end if actual[#actual] == '' then actual[#actual] = nil end for i = 1, math.max(#expect, #actual) do if expect[i] and actual[i] then local match_pat = expect[i]:match('{MATCH:(.*)}') if expect[i] == actual[i] or (match_pat and actual[i]:match(match_pat)) then expect[i] = string.rep(' ', 2) .. expect[i] actual[i] = string.rep(' ', 2) .. actual[i] goto loop_end end end matched = false if expect[i] then expect[i] = '*' .. string.rep(' ', 1) .. expect[i] end if actual[i] then actual[i] = '*' .. string.rep(' ', 1) .. actual[i] end ::loop_end:: end end if not matched then table.insert(errmsg, 'Unexpected statusline') table.insert(errmsg, 'Expected:') table.insert(errmsg, table.concat(expect, '\n') .. '\n') table.insert(errmsg, 'Actual:') table.insert(errmsg, table.concat(actual, '\n')) end assert(matched, table.concat(errmsg, '\n')) end function M:snapshot_expr(expr) local type_map = { active = 'statusline', inactive = 'inactive_statusline', tabline = 'tabline', } if expr == nil then print((type_map[self.type] or 'statusline') .. ':expect(nil)') return end print((type_map[self.type] or 'statusline') .. ':expect([===[') print(eval_stl(expr, self.width, self.type) .. ']===])') end function M:snapshot() local utils = require('lualine.utils.utils') stub(utils, 'is_focused') utils.is_focused.returns(self.type ~= 'inactive') local expr if self.type == 'inactive' then expr = require('lualine').statusline(false) elseif self.type == 'tabline' then expr = require('lualine').tabline() else expr = require('lualine').statusline(true) end self:snapshot_expr(expr) utils.is_focused:revert() end function M:expect(result) local utils = require('lualine.utils.utils') stub(utils, 'is_focused') utils.is_focused.returns(self.type ~= 'inactive') local expr if self.type == 'inactive' then expr = require('lualine').statusline(false) elseif self.type == 'tabline' then expr = require('lualine').tabline() else expr = require('lualine').statusline(true) end self:expect_expr(result, expr) utils.is_focused:revert() end function M.new(_, width, eval_type) if type(_) ~= 'table' then eval_type = width width = _ end local self = {} self.width = width or 120 self.type = eval_type if self.type == nil then self.type = 'active' end return setmetatable(self, { __index = M, __call = function(_, ...) M.new(...) end, }) end return M.new()