├── .gitignore ├── FUNDING.yml ├── LICENSE ├── README.md ├── doc └── linefly.txt ├── lua └── linefly │ ├── constants.lua │ ├── file.lua │ ├── git.lua │ ├── highlight.lua │ ├── init.lua │ ├── lsp.lua │ ├── options.lua │ ├── plugins.lua │ ├── utils.lua │ └── window.lua ├── nvim.toml ├── plugin └── linefly.lua ├── selene.toml └── stylua.toml /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | -------------------------------------------------------------------------------- /FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: bluz71 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023-present nvim-linefly authors 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | linefly 2 | ======= 3 | 4 | _linefly_ is a simple, fast and informative pure-Lua `statusline` for Neovim. 5 | 6 | _linefly_ provides a number of useful builtin components: 7 | 8 | - Git changes (via [Gitsigns](https://github.com/lewis6991/gitsigns.nvim) plugin if installed) 9 | - Diagnostic status 10 | - Attached LSP client namess 11 | - On-going LSP progress 12 | - Macro-recording status (useful when `set cmdheight=0`) 13 | - Current search count (useful when `set cmdheight=0`) 14 | - Spell status (useful when `set cmdheight=0`) 15 | - Indent status (tabs or spaces and their associated width) 16 | 17 | _linefly_ also provides optional `tabline` and `winbar` support when the 18 | appropriate settings are enabled; refer to 19 | [`tabline`](https://github.com/bluz71/nvim-linefly#tabline) 20 | and 21 | [`winbar`](https://github.com/bluz71/nvim-linefly#winbar). 22 | 23 | _linefly_ will adapt it's colors to the colorscheme currently in effect. Colors 24 | can also be 25 | [customized](https://github.com/bluz71/nvim-linefly#highlight-groups-and-colors) 26 | if desired. 27 | 28 | Lastly, _linefly_ is a lean `statusline` plugin clocking in at about 850 lines 29 | of Lua code. For comparison, the 30 | [lualine](https://github.com/nvim-lualine/lualine.nvim), 31 | [lightline](https://github.com/itchyny/lightline.vim) and 32 | [airline](https://github.com/vim-airline/vim-airline) plugins contain over 33 | 9,100, 3,700 and 7,400 lines of code respectively. In fairness, the latter 34 | plugins are more featureful, configurable and visually pleasing. 35 | 36 | :warning: _linefly_ has a predominantly fixed layout, this will **not** be an 37 | appropriate `statusline` plugin if layout flexibility is desired. 38 | 39 | Screenshots 40 | ----------- 41 | 42 | ![normal](https://raw.githubusercontent.com/bluz71/misc-binaries/master/statusline/statusline-normal.png) 43 | ![insert](https://raw.githubusercontent.com/bluz71/misc-binaries/master/statusline/statusline-insert.png) 44 | ![visual](https://raw.githubusercontent.com/bluz71/misc-binaries/master/statusline/statusline-visual.png) 45 | ![command](https://raw.githubusercontent.com/bluz71/misc-binaries/master/statusline/statusline-command.png) 46 | ![replace](https://raw.githubusercontent.com/bluz71/misc-binaries/master/statusline/statusline-replace.png) 47 | 48 | The above screenshots are using the 49 | [moonfly](https://github.com/bluz71/vim-moonfly-colors) colorscheme and the 50 | [Iosevka](https://github.com/be5invis/Iosevka) font with Git changes, 51 | Diagnostics and indent-status enabled. 52 | 53 | Statusline Startup Comparison 54 | ----------------------------- 55 | 56 | A startup comparison of _linefly_ against various popular `statusline` 57 | plugins, with their out-of-the-box defaults, on a clean and minimal Neovim setup 58 | with the [moonfly](https://github.com/bluz71/vim-moonfly-colors) colorscheme. 59 | The Neovim startup times in the following table are provived by the 60 | [dstein64/vim-startuptime](https://github.com/dstein64/vim-startuptime) plugin. 61 | 62 | Startup times are the average of five consecutive runs. Note, `stock` is run 63 | without any `statusline` plugin. 64 | 65 | | stock | linefly | lualine | lightline | airline 66 | |--------|---------|---------|-----------|-------- 67 | | 18.0ms | 18.9ms | 23.2ms | 22.0ms | 76.0ms 68 | 69 | Startup times as of March 2024 on my system; performance on other systems 70 | will vary. 71 | 72 | Modules And Plugins supported 73 | ----------------------------- 74 | 75 | - [Diagnostic](https://neovim.io/doc/user/diagnostic.html) 76 | 77 | - [nvim-dap-ui](https://github.com/rcarriga/nvim-dap-ui) 78 | 79 | - [nvim-lint](https://github.com/mfussenegger/nvim-lint) 80 | 81 | - [nvim-web-devicons](https://github.com/kyazdani42/nvim-web-devicons) 82 | 83 | - [Gitsigns](https://github.com/lewis6991/gitsigns.nvim) 84 | 85 | - [vim-obsession](https://github.com/tpope/vim-obsession) 86 | 87 | - [possession.nvim](https://github.com/jedrzejboczar/possession.nvim) 88 | 89 | - [nvim-possession](https://github.com/gennaro-tedesco/nvim-possession) 90 | 91 | :zap: Requirements 92 | ------------------ 93 | 94 | _linefly_ requires Neovim 0.10, or later. 95 | 96 | Lastly, please make sure that the `laststatus` option is set to either: `1`, `2` 97 | or `3`. 98 | 99 | Installation 100 | ------------ 101 | 102 | Install **bluz71/nvim-linefly** with your preferred plugin manager. 103 | 104 | [lazy.nvim](https://github.com/folke/lazy.nvim): 105 | 106 | ```lua 107 | { 'bluz71/nvim-linefly' }, 108 | ``` 109 | 110 | Please do **not** lazy-load _linefly_. 111 | 112 | Layout And Default Colors 113 | ------------------------- 114 | 115 | The *linefly* layout consists of three groupings: the left-side, middle and 116 | right-side as follows: 117 | 118 | ``` 119 | +-------------------------------------------------+ 120 | | A | B | C | D | E M U | V | W | X | Y | Z | 121 | +-------------------------------------------------+ 122 | ``` 123 | 124 | | Section | Purpose 125 | |---------|------------------ 126 | | A`*` | Mode status (normal, insert, visual, command and replace modes) 127 | | B | Filename (refer below for details) 128 | | C`*` | Git branch name (if applicable) 129 | | D`*` | Plugins notification (git, diagnostic and session status) 130 | | E | Active buffer-attached LSP client names 131 | | M | LSP progress status 132 | | U | `showcmd` content if `showcmdloc=statusline` 133 | | V`*` | Optional macro-recording status 134 | | W | Optional search count and spell status 135 | | X | Current position 136 | | Y`*` | Total lines and current location as percentage 137 | | Z | Optional indent status (spaces and tabs shift width) 138 | 139 | Sections marked with a `*` are linked to a highlight group and are colored, 140 | refer to the next section for details. 141 | 142 | Sections C, D, U, V & W will **not** be displayed when the `statusline` width is 143 | less than 80 columns. 144 | 145 | Section E, active buffer-attached LSP client names, will only be displayed when 146 | the `statusline` width is greater than or equal to 120 columns. 147 | 148 | Section M, LSP progress status, will only be displayed when a global 149 | `statusline` is in effect and the `statusline` width is greater than or equal to 150 | 120 columns. 151 | 152 | Note, filenames will be displayed as follows: 153 | 154 | - Pathless filenames for files in the current working directory 155 | 156 | - Relative paths in preference to absolute paths for files not in the current 157 | working directory 158 | 159 | - `~`-style home directory paths in preference to absolute paths 160 | 161 | - Possibly shortened, for example `foo/bar/bazz/hello.txt` will be displayed as 162 | `f/b/b/hello.txt` when `statusline` width is less than 120 columns. 163 | 164 | - Possibly trimmed. A maximum of four path components will be displayed for a 165 | filename; if a filename is more deeply nested then only the four most 166 | significant components, including the filename, will be displayed with an 167 | ellipsis prefix symbol used to indicate path trimming. 168 | 169 | Highlight Groups And Colors 170 | --------------------------- 171 | 172 | Sections marked with `*` in the previous section are linked to the following 173 | custom highlight groups with their associated fallbacks if the current 174 | colorscheme does not support _linefly_. 175 | 176 | | Segment | Custom Highlight Group | Synthesized Highlight Fallback 177 | |--------------------------|------------------------|------------------------------- 178 | | Normal Mode | `LineflyNormal` | `Directory` 179 | | Insert Mode | `LineflyInsert` | `String` 180 | | Visual Mode | `LineflyVisual` | `Statement` 181 | | Command Mode | `LineflyCommand` | `WarningMsg` 182 | | Replace Mode | `LineflyReplace` | `Error` 183 | 184 | Note, the following *dark* colorschemes support _linefly_, either within the 185 | colorscheme (moonfly & nightfly) or within this plugin (all others): 186 | 187 | - [moonfly](https://github.com/bluz71/vim-moonfly-colors) 188 | 189 | - [nightfly](https://github.com/bluz71/vim-nightfly-guicolors) 190 | 191 | - [bamboo](https://github.com/ribru17/bamboo.nvim) 192 | 193 | - [catppuccin](https://github.com/catppuccin/nvim) 194 | 195 | - [cyberdream](https://github.com/scottmckendry/cyberdream.nvim) 196 | 197 | - [default](https://github.com/neovim/neovim/issues/14790) 198 | 199 | - [dracula.nvim](https://github.com/Mofiqul/dracula.nvim) 200 | 201 | - [edge](https://github.com/sainnhe/edge) 202 | 203 | - [everforest](https://github.com/sainnhe/everforest) 204 | 205 | - [everforest.nvim](https://github.com/neanias/everforest-nvim) 206 | 207 | - [falcon](https://github.com/fenetikm/falcon) 208 | 209 | - [gruvbox.nvim](https://github.com/ellisonleao/gruvbox.nvim) 210 | 211 | - [gruvbox-material](https://github.com/sainnhe/gruvbox-material) 212 | 213 | - [kanagawa](https://github.com/rebelot/kanagawa.nvim) 214 | 215 | - [mini.base16](https://github.com/echasnovski/mini.base16) 216 | 217 | - [nightfox](https://github.com/EdenEast/nightfox.nvim) 218 | 219 | - [nord.nvim](https://github.com/shaunsingh/nord.nvim) 220 | 221 | - [Nordic](https://github.com/AlexvZyl/nordic.nvim) 222 | 223 | - [onedark.nvim](https://github.com/navarasu/onedark.nvim) 224 | 225 | - [oxocarbon.nvim](https://github.com/nyoom-engineering/oxocarbon.nvim) 226 | 227 | - [retrobox](https://github.com/vim/colorschemes) 228 | 229 | - [rose-pine](https://github.com/rose-pine/neovim) 230 | 231 | - [sonokai](https://github.com/sainnhe/sonokai) 232 | 233 | - [tokyodark](https://github.com/tiagovla/tokyodark.nvim) 234 | 235 | - [tokyonight](https://github.com/folke/tokyonight.nvim) 236 | 237 | - [vscode.nvim](https://github.com/Mofiqul/vscode.nvim) 238 | 239 | Lastly, if the fallback colors do not suit then it is very easy to override with 240 | your own highlights. 241 | 242 | :gift: Here is a simple example of customized _linefly_ colors. Save the 243 | following at the end of your initialization file after setting your 244 | `colorscheme`. 245 | 246 | ```lua 247 | local highlight = vim.api.nvim_set_hl 248 | 249 | highlight(0, "LineflyNormal", { link = "DiffChange" }) 250 | highlight(0, "LineflyInsert", { link = "WildMenu" }) 251 | highlight(0, "LineflyVisual", { link = "IncSearch" }) 252 | highlight(0, "LineflyCommand", { link = "WildMenu" }) 253 | highlight(0, "LineflyReplace", { link = "ErrorMsg" }) 254 | ``` 255 | 256 | :wrench: Options 257 | ---------------- 258 | 259 | Default option values: 260 | 261 | ```lua 262 | vim.g.linefly_options = { 263 | separator_symbol = "⎪", 264 | progress_symbol = "↓", 265 | active_tab_symbol = "▪", 266 | git_branch_symbol = "", 267 | error_symbol = "E", 268 | warning_symbol = "W", 269 | information_symbol = "I", 270 | ellipsis_symbol = "…", 271 | tabline = false, 272 | winbar = false, 273 | with_file_icon = true, 274 | with_git_branch = true, 275 | with_git_status = true, 276 | with_diagnostic_status = true, 277 | with_session_status = true, 278 | with_attached_clients = true, 279 | with_lsp_status = false, 280 | with_macro_status = false, 281 | with_search_count = false, 282 | with_spell_status = false, 283 | with_indent_status = false, 284 | } 285 | ``` 286 | 287 | | Option | Option | Option 288 | |--------|--------|------- 289 | | [separator_symbol](https://github.com/bluz71/nvim-linefly#separator_symbol) | [progress_symbol](https://github.com/bluz71/nvim-linefly#progress_symbol) | [active_tab_symbol](https://github.com/bluz71/nvim-linefly#active_tab_symbol) 290 | | [git_branch_symbol](https://github.com/bluz71/nvim-linefly#git_branch_symbol) 291 | | [error_symbol](https://github.com/bluz71/nvim-linefly#error_symbol) | [warning_symbol](https://github.com/bluz71/nvim-linefly#warning_symbol) | [information_symbol](https://github.com/bluz71/nvim-linefly#information_symbol) 292 | | [ellipsis_symbol](https://github.com/bluz71/nvim-linefly#ellipsis_symbol) 293 | | [tabline](https://github.com/bluz71/nvim-linefly#tabline) | [winbar](https://github.com/bluz71/nvim-linefly#winbar) 294 | | [with_file_icon](https://github.com/bluz71/nvim-linefly#with_file_icon) | [with_git_branch](https://github.com/bluz71/nvim-linefly#with_git_branch) | [with_git_status](https://github.com/bluz71/nvim-linefly#with_git_status) 295 | | [with_diagnostic_status](https://github.com/bluz71/nvim-linefly#with_diagnostic_status) | [with_session_status](https://github.com/bluz71/nvim-linefly#with_session_status) | [with_attached_clients](https://github.com/bluz71/nvim-linefly#with_attached_clients) 296 | | [with_lsp_status](https://github.com/bluz71/nvim-linefly#with_lsp_status) | [with_macro_status](https://github.com/bluz71/nvim-linefly#with_macro_status) | [with_search_count](https://github.com/bluz71/nvim-linefly#with_search_count) 297 | | [with_spell_status](https://github.com/bluz71/nvim-linefly#with_spell_status) | [with_indent_status](https://github.com/bluz71/nvim-linefly#with_indent_status) 298 | 299 | --- 300 | 301 | ### separator_symbol 302 | 303 | The `separator_symbol` option specifies which character symbol to use for 304 | segment separators in the `statusline`. 305 | 306 | By default, the `⎪` character (Unicode `U+23AA`) will be displayed. 307 | 308 | To specify your own separator symbol please add the following to your 309 | initialization file: 310 | 311 | ```lua 312 | vim.g.linefly_options = { 313 | separator_symbol = '<>' 314 | } 315 | ``` 316 | 317 | --- 318 | 319 | ### progress_symbol 320 | 321 | The `progress_symbol` option specifies which character symbol to use to 322 | indicate location-as-percentage in the `statusline`. 323 | 324 | By default, the `↓` character (Unicode `U+2193`) will be displayed. 325 | 326 | To specify your own progress symbol, or no symbol at all, please add the 327 | following to your initialization file: 328 | 329 | ```lua 330 | vim.g.linefly_options = { 331 | progress_symbol = '<>' 332 | } 333 | ``` 334 | 335 | --- 336 | 337 | ### active_tab_symbol 338 | 339 | The `active_tab_symbol` option specifies which character symbol to use to 340 | signify the active tab in the `tabline`. 341 | 342 | By default, the `▪` character (Unicode `U+25AA`) will be displayed. 343 | 344 | To specify your own active tab symbol please add the following to your 345 | initialization file: 346 | 347 | ```lua 348 | vim.g.linefly_options = { 349 | active_tab_symbol = '<>' 350 | } 351 | ``` 352 | 353 | --- 354 | 355 | ### git_branch_symbol 356 | 357 | The `git_branch_symbol` option specifies which character symbol to use when 358 | displaying Git branch details. 359 | 360 | By default, the `` character (Powerline `U+E0A0`) will be displayed. Many 361 | modern monospace fonts will contain that character. 362 | 363 | To specify your own Git branch symbol, or no symbol at all, please add the 364 | following to your initialization file: 365 | 366 | ```lua 367 | vim.g.linefly_options = { 368 | git_branch_symbol = '<>' 369 | } 370 | ``` 371 | 372 | --- 373 | 374 | ### error_symbol 375 | 376 | The `error_symbol` option specifies which character symbol to use when 377 | displaying [Diagnostic](https://neovim.io/doc/user/diagnostic.html) errors. 378 | 379 | By default, the `E` character will be displayed. 380 | 381 | To specify your own error symbol please add the following to your 382 | initialization file: 383 | 384 | ```lua 385 | vim.g.linefly_options = { 386 | error_symbol = '<>' 387 | } 388 | ``` 389 | 390 | --- 391 | 392 | ### warning_symbol 393 | 394 | The `warning_symbol` option specifies which character symbol to use when 395 | displaying [Diagnostic](https://neovim.io/doc/user/diagnostic.html) warnings. 396 | 397 | By default, the `W` character will be displayed. 398 | 399 | To specify your own warning symbol please add the following to your 400 | initialization file: 401 | 402 | ```lua 403 | vim.g.linefly_options = { 404 | warning_symbol = '<>' 405 | } 406 | ``` 407 | 408 | --- 409 | 410 | ### information_symbol 411 | 412 | The `information_symbol` option specifies which character symbol to use when 413 | displaying [Diagnostic](https://neovim.io/doc/user/diagnostic.html) 414 | information. 415 | 416 | By default, the `I` character will be displayed. 417 | 418 | To specify your own information symbol please add the following to your 419 | initialization file: 420 | 421 | ```lua 422 | vim.g.linefly_options = { 423 | information_symbol = '<>' 424 | } 425 | ``` 426 | 427 | --- 428 | 429 | ### ellipsis_symbol 430 | 431 | The `ellipsis_symbol` option specifies which character symbol to use when 432 | indicating truncation, for example, deeply nested path truncation. 433 | 434 | By default, the `…` character will be displayed. 435 | 436 | To specify your own ellipsis symbol please add the following to your 437 | initialization file: 438 | 439 | ```lua 440 | vim.g.linefly_options = { 441 | ellipsis_symbol = '<>' 442 | } 443 | ``` 444 | 445 | --- 446 | 447 | ### tabline 448 | 449 | The `tabline` option specifies whether to let this plugin manage the Neovim 450 | `tabline` in addition to the `statusline`. 451 | 452 | By default, Neovim `tabline` management will not be undertaken. 453 | 454 | If enabled, _linefly_ will render a simple numbered, and clickable, 455 | window-space layout in the `tabline`; note, no buffers will be displayed in 456 | the `tabline` since there are many plugins that already provide that 457 | capability. 458 | 459 | To enable `tabline` support please add the following to your initialization 460 | file: 461 | 462 | ```lua 463 | vim.g.linefly_options = { 464 | tabline = true, 465 | } 466 | ``` 467 | 468 | :bulb: Mappings, such as the following, may be useful to quickly switch between 469 | the numbered window-spaces: 470 | 471 | ```lua 472 | local map = vim.keymap.set 473 | 474 | map("n", "1", "1gt") 475 | map("n", "2", "2gt") 476 | map("n", "3", "3gt") 477 | map("n", "4", "4gt") 478 | map("n", "5", "5gt") 479 | map("n", "6", "6gt") 480 | map("n", "7", "7gt") 481 | map("n", "8", "8gt") 482 | map("n", "9", "9gt") 483 | ``` 484 | 485 | A screenshot of the `tabline`: 486 | 487 | ![tabline](https://raw.githubusercontent.com/bluz71/misc-binaries/master/statusline/tabline.png) 488 | 489 | --- 490 | 491 | ### winbar 492 | 493 | The `winbar` option specifies whether to display a window bar at the top of 494 | each window. 495 | 496 | By default, window bars will not be displayed. 497 | 498 | Displaying a window bar is recommended when the global statusline is enabled 499 | via `set laststatus=3`; the `winbar` will then display the file name at the 500 | top of each window to disambiguate splits. Also, if there is only one window 501 | in the current tab then a `winbar` will not be displayed (it won't be needed). 502 | 503 | To enable the `winbar` feature please add the following to your initialization 504 | file: 505 | 506 | ```lua 507 | vim.g.linefly_options = { 508 | winbar = true, 509 | } 510 | ``` 511 | 512 | --- 513 | 514 | ### with_file_icon 515 | 516 | The `with_file_icon` option specifies whether a filetype icon, from a Nerd 517 | Font, will be displayed prior to the filename in the `statusline` (and 518 | optional `winbar`). 519 | 520 | Note, a [Nerd Font](https://www.nerdfonts.com) must be active **and** the 521 | [nvim-web-devicons](https://github.com/kyazdani42/nvim-web-devicons) plugin 522 | must also be installed and active. 523 | 524 | By default, a filetype icon will be displayed if possible. 525 | 526 | To disable the display of a filetype icon please add the following to your 527 | initialization file: 528 | 529 | ```lua 530 | vim.g.linefly_options = { 531 | with_file_icon = false, 532 | } 533 | ``` 534 | 535 | --- 536 | 537 | ### with_git_branch 538 | 539 | The `with_git_branch` option specifies whether to display Git branch details 540 | in the `statusline`. 541 | 542 | By default, Git branches will be displayed in the `statusline`. 543 | 544 | To disable the display of Git branches in the `statusline` please add the 545 | following to your initialization file: 546 | 547 | ```lua 548 | vim.g.linefly_options = { 549 | with_git_branch = false, 550 | } 551 | ``` 552 | 553 | --- 554 | 555 | ### with_git_status 556 | 557 | The `with_git_status` option specifies whether to display 558 | [Gitsigns](https://github.com/lewis6991/gitsigns.nvim) of the current buffer 559 | in the `statusline`. 560 | 561 | By default, the Git status will be displayed if the plugin is loaded. 562 | 563 | To disable the display of Git status in the `statusline` please add the 564 | following to your initialization file: 565 | 566 | ```lua 567 | vim.g.linefly_options = { 568 | with_git_status = false, 569 | } 570 | ``` 571 | 572 | --- 573 | 574 | ### with_diagnostic_status 575 | 576 | _linefly_ supports [Diagnostics](https://neovim.io/doc/user/diagnostic.html). 577 | 578 | The `with_diagnostic_status` option specifies whether to indicate the presence 579 | of the diagnostics in the current buffer. 580 | 581 | By default, diagnostics will be displayed if the 582 | [nvim-lspconfig](https://github.com/neovim/nvim-lspconfig) plugin is loaded. 583 | 584 | If diagnostic display is not wanted then please add the following to your 585 | initialization file: 586 | 587 | ```lua 588 | vim.g.linefly_options = { 589 | with_diagnostic_status = false, 590 | } 591 | ``` 592 | 593 | --- 594 | 595 | ### with_session_status 596 | 597 | The `with_session_status` option specifies whether to display 598 | [vim-obsession](https://github.com/tpope/vim-obsession), 599 | [posession.nvim](https://github.com/jedrzejboczar/possession.nvim) or 600 | [nvim-possession](https://github.com/gennaro-tedesco/nvim-possession) session 601 | details in the `statusline`. 602 | 603 | By default, session details will be displayed if one of those plugins is 604 | loaded. 605 | 606 | To disable the display of session details in the `statusline` please add the 607 | following to your initialization file: 608 | 609 | ```lua 610 | vim.g.linefly_options = { 611 | with_session_status = false, 612 | } 613 | ``` 614 | 615 | --- 616 | 617 | ### with_attached_clients 618 | 619 | The `with_attached_clients` option specifies whether to display all active 620 | buffer-attached language servers and linters in the `statusline`. 621 | 622 | Note, linter names are derived from the 623 | [nvim-lint](https://github.com/mfussenegger/nvim-lint) plugin, if active. 624 | 625 | By default, attached clients will be displayed. 626 | 627 | To disable the display of attached clients in the `statusline` please add the 628 | following to your initialization file: 629 | 630 | ```lua 631 | vim.g.linefly_options = { 632 | with_attached_clients = false, 633 | } 634 | ``` 635 | 636 | --- 637 | 638 | ### with_lsp_status 639 | 640 | The `with_lsp_status` option specifies whether to display LSP progress status 641 | in the `statusline` if global `statusline` is in effect (`:set laststatus=3`). 642 | 643 | By default, LSP progress status will not be displayed. 644 | 645 | To enable the display of LSP progress status in the `statusline` please add the 646 | following to your initialization file: 647 | 648 | ```lua 649 | vim.g.linefly_options = { 650 | with_lsp_status = true, 651 | } 652 | ``` 653 | 654 | --- 655 | 656 | ### with_macro_status 657 | 658 | The `with_macro_status` option specifies whether to display macro-recording 659 | status in the `statusline`. This option is especially useful if `cmdheight` is 660 | set to `0`. 661 | 662 | By default, macro-recording status will not be displayed. 663 | 664 | To enable the display of macro-recording status in the `statusline` please add 665 | the following to your initialization file: 666 | 667 | ```lua 668 | vim.g.linefly_options = { 669 | with_macro_status = true, 670 | } 671 | ``` 672 | 673 | --- 674 | 675 | ### with_search_count 676 | 677 | The `with_search_count` option specifies whether to display the search count 678 | in the `statusline`. This option is especially useful if `cmdheight` is set to 679 | `0`. 680 | 681 | By default, search count will not be displayed. 682 | 683 | To enable the display of the search count in the `statusline` please add the 684 | following to your initialization file: 685 | 686 | ```lua 687 | vim.g.linefly_options = { 688 | with_search_count = true, 689 | } 690 | ``` 691 | 692 | Note, the search count is only displayed when the `hlsearch` option is set and 693 | the search count result is not zero. 694 | 695 | --- 696 | 697 | ### with_spell_status 698 | 699 | The `with_spell_status` option specifies whether to display the spell status 700 | in the `statusline`. This option is especially useful if `cmdheight` is set to 701 | `0`. 702 | 703 | By default, spell status will not be displayed. 704 | 705 | To enable spell status in the `statusline` please add the following to your 706 | initialization file: 707 | 708 | ```lua 709 | vim.g.linefly_options = { 710 | with_spell_status = true, 711 | } 712 | ``` 713 | 714 | --- 715 | 716 | ### with_indent_status 717 | 718 | The `with_indent_status` option specifies whether to display the indentation 719 | status as the last component in the `statusline`. 720 | 721 | By default, indentation status will not be displayed. 722 | 723 | Note, if the `expandtab` option is set, for the current buffer, then tab stop 724 | will be displayed, for example `Tab:4` (tab equals four spaces); if on the 725 | other hand `noexpandtab` option is set then shift width will be displayed 726 | instead, for example `Spc:2` ('spc' short for 'space'). 727 | 728 | To enable indentation status please add the following to your initialization 729 | file: 730 | 731 | ```lua 732 | vim.g.linefly_options = { 733 | with_indent_status = true, 734 | } 735 | ``` 736 | 737 | Sponsor 738 | ------- 739 | 740 | [![Ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/bluz71) 741 | 742 | License 743 | ------- 744 | 745 | [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT) 746 | -------------------------------------------------------------------------------- /doc/linefly.txt: -------------------------------------------------------------------------------- 1 | *linefly* A simple, fast and informative pure-Lua statusline for Neovim 2 | 3 | OPTIONS *linefly-options* 4 | 5 | Default option values: 6 | >lua 7 | vim.g.linefly_options = { 8 | separator_symbol = "⎪", 9 | progress_symbol = "↓", 10 | active_tab_symbol = "▪", 11 | git_branch_symbol = "", 12 | error_symbol = "E", 13 | warning_symbol = "W", 14 | information_symbol = "I", 15 | ellipsis_symbol = "…", 16 | tabline = false, 17 | winbar = false, 18 | with_file_icon = true, 19 | with_git_branch = true, 20 | with_git_status = true, 21 | with_diagnostic_status = true, 22 | with_session_status = true, 23 | with_attached_clients = true, 24 | with_lsp_status = false, 25 | with_macro_status = false, 26 | with_search_count = false, 27 | with_spell_status = false, 28 | with_indent_status = false, 29 | } 30 | < 31 | ------------------------------------------------------------------------------ 32 | separator_symbol~ *separator_symbol* 33 | 34 | The `separator_symbol` option specifies which character symbol to use for 35 | segment separators in the `statusline`. 36 | 37 | By default, the `⎪` character (Unicode `U+23AA`) will be displayed. 38 | 39 | To specify your own separator symbol please add the following to your 40 | initialization file: 41 | >lua 42 | vim.g.linefly_options = { 43 | separator_symbol = '<>' 44 | } 45 | < 46 | ------------------------------------------------------------------------------ 47 | progress_symbol~ *progress_symbol* 48 | 49 | The `progress_symbol` option specifies which character symbol to use to 50 | indicate location-as-percentage in the `statusline`. 51 | 52 | By default, the `↓` character (Unicode `U+2193`) will be displayed. 53 | 54 | To specify your own progress symbol, or no symbol at all, please add the 55 | following to your initialization file: 56 | >lua 57 | vim.g.linefly_options = { 58 | progress_symbol = '<>' 59 | } 60 | < 61 | ------------------------------------------------------------------------------ 62 | active_tab_symbol~ *active_tab_symbol* 63 | 64 | The `active_tab_symbol` option specifies which character symbol to use to 65 | signify the active tab in the `tabline`. 66 | 67 | By default, the `▪` character (Unicode `U+25AA`) will be displayed. 68 | 69 | To specify your own active tab symbol please add the following to your 70 | initialization file: 71 | >lua 72 | vim.g.linefly_options = { 73 | active_tab_symbol = '<>' 74 | } 75 | < 76 | ------------------------------------------------------------------------------ 77 | git_branch_symbol~ *git_branch_symbol* 78 | 79 | The `git_branch_symbol` option specifies which character symbol to use when 80 | displaying Git branch details. 81 | 82 | By default, the `` character (Powerline `U+E0A0`) will be displayed. Many 83 | modern monospace fonts will contain that character. 84 | 85 | To specify your own Git branch symbol, or no symbol at all, please add the 86 | following to your initialization file: 87 | >lua 88 | vim.g.linefly_options = { 89 | git_branch_symbol = '<>' 90 | } 91 | < 92 | ------------------------------------------------------------------------------ 93 | error_symbol~ *error_symbol* 94 | 95 | The `error_symbol` option specifies which character symbol to use when 96 | displaying Diagnostic (https://neovim.io/doc/user/diagnostic.html) errors. 97 | 98 | By default, the `E` character will be displayed. 99 | 100 | To specify your own error symbol please add the following to your 101 | initialization file: 102 | >lua 103 | vim.g.linefly_options = { 104 | error_symbol = '<>' 105 | } 106 | < 107 | ------------------------------------------------------------------------------ 108 | warning_symbol~ *warning_symbol* 109 | 110 | The `warning_symbol` option specifies which character symbol to use when 111 | displaying Diagnostic (https://neovim.io/doc/user/diagnostic.html) warnings. 112 | 113 | By default, the `W` character will be displayed. 114 | 115 | To specify your own warning symbol please add the following to your 116 | initialization file: 117 | >lua 118 | vim.g.linefly_options = { 119 | warning_symbol = '<>' 120 | } 121 | < 122 | ------------------------------------------------------------------------------ 123 | information_symbol~ *information_symbol* 124 | 125 | The `information_symbol` option specifies which character symbol to use when 126 | displaying Diagnostic (https://neovim.io/doc/user/diagnostic.html) 127 | information. 128 | 129 | By default, the `I` character will be displayed. 130 | 131 | To specify your own information symbol please add the following to your 132 | initialization file: 133 | >lua 134 | vim.g.linefly_options = { 135 | information_symbol = '<>' 136 | } 137 | < 138 | ------------------------------------------------------------------------------ 139 | ellipsis_symbol~ *ellipsis_symbol* 140 | 141 | 142 | The `ellipsis_symbol` option specifies which character symbol to use when 143 | indicating truncation, for example, deeply nested path truncation. 144 | 145 | By default, the `…` character will be displayed. 146 | 147 | To specify your own ellipsis symbol please add the following to your 148 | initialization file: 149 | >lua 150 | vim.g.linefly_options = { 151 | ellipsis_symbol = '<>' 152 | } 153 | < 154 | ------------------------------------------------------------------------------ 155 | tabline~ *tabline* 156 | 157 | The `tabline` option specifies whether to let this plugin manage the Neovim 158 | `tabline` in addition to the `statusline`. 159 | 160 | By default, Neovim `tabline` management will not be undertaken. 161 | 162 | If enabled, `linefly` will render a simple numbered, and clickable, 163 | window-space layout in the `tabline`; note, no buffers will be displayed in 164 | the `tabline` since there are many plugins that already provide that 165 | capability. 166 | 167 | To enable `tabline` support please add the following to your initialization 168 | file: 169 | >lua 170 | vim.g.linefly_options = { 171 | tabline = true, 172 | } 173 | < 174 | ------------------------------------------------------------------------------ 175 | winbar~ *winbar* 176 | 177 | The `winbar` option specifies whether to display a window bar at the top of 178 | each window. 179 | 180 | By default, window bars will not be displayed. 181 | 182 | Displaying a window bar is recommended when the global statusline is enabled 183 | via `set laststatus=3`; the `winbar` will then display the file name at the 184 | top of each window to disambiguate splits. Also, if there is only one window 185 | in the current tab then a `winbar` will not be displayed (it won't be needed). 186 | 187 | To enable the `winbar` feature please add the following to your initialization 188 | file: 189 | >lua 190 | vim.g.linefly_options = { 191 | winbar = true, 192 | } 193 | < 194 | ------------------------------------------------------------------------------ 195 | with_file_icon~ *with_file_icon* 196 | 197 | The `with_file_icon` option specifies whether a filetype icon, from a Nerd 198 | Font, will be displayed prior to the filename in the `statusline` (and 199 | optional `winbar`). 200 | 201 | Note, a Nerd Font (https://www.nerdfonts.com) must be active and the 202 | nvim-web-devicons (https://github.com/kyazdani42/nvim-web-devicons) plugin 203 | must also be installed and active. 204 | 205 | By default, a filetype icon will be displayed if possible. 206 | 207 | To disable the display of a filetype icon please add the following to your 208 | initialization file: 209 | >lua 210 | vim.g.linefly_options = { 211 | with_file_icon = false, 212 | } 213 | < 214 | ------------------------------------------------------------------------------ 215 | with_git_branch~ *with_git_branch* 216 | 217 | The `with_git_branch` option specifies whether to display Git branch details 218 | in the `statusline`. 219 | 220 | By default, Git branches will be displayed in the `statusline`. 221 | 222 | To disable the display of Git branches in the `statusline` please add the 223 | following to your initialization file: 224 | >lua 225 | vim.g.linefly_options = { 226 | with_git_branch = false, 227 | } 228 | < 229 | ------------------------------------------------------------------------------ 230 | with_git_status~ *with_git_status* 231 | 232 | The `with_git_status` option specifies whether to display Gitsigns 233 | (https://github.com/lewis6991/gitsigns.nvim) of the current buffer in the 234 | `statusline`. 235 | 236 | By default, the Git status will be displayed if the plugin is loaded. 237 | 238 | To disable the display of Git status in the `statusline` please add the 239 | following to your initialization file: 240 | >lua 241 | vim.g.linefly_options = { 242 | with_git_status = false, 243 | } 244 | < 245 | ------------------------------------------------------------------------------ 246 | with_diagnostic_status~ *with_diagnostic_status* 247 | 248 | The `with_diagnostic_status` option specifies whether to indicate the presence 249 | of the diagnostics in the current buffer. 250 | 251 | By default, diagnostics will be displayed if the nvim-lspconfig 252 | (https://github.com/neovim/nvim-lspconfig) plugin is loaded. 253 | 254 | If diagnostic display is not wanted then please add the following to your 255 | initialization file: 256 | >lua 257 | vim.g.linefly_options = { 258 | with_diagnostic_status = false, 259 | } 260 | < 261 | ------------------------------------------------------------------------------ 262 | with_session_status~ *with_session_status* 263 | 264 | The `with_session_status` option specifies whether to display vim-obsession 265 | (https://github.com/tpope/vim-obsession), possession.nvim 266 | (https://github.com/jedrzejboczar/possession.nvim) or nvim-possession 267 | (https://github.com/gennaro-tedesco/nvim-possession) session details in the 268 | `statusline`. 269 | 270 | By default, session details will be displayed if one of those plugins is 271 | loaded. 272 | 273 | To disable the display of session details in the `statusline` please add the 274 | following to your initialization file: 275 | >lua 276 | vim.g.linefly_options = { 277 | with_session_status = false, 278 | } 279 | < 280 | ------------------------------------------------------------------------------ 281 | with_attached_clients~ *with_attached_clients* 282 | 283 | The `with_attached_clients` option specifies whether to display all active 284 | buffer-attached language servers and linters in the `statusline`. 285 | 286 | Note, linter names are derived from the nvim-lint 287 | (https://github.com/mfussenegger/nvim-lint) plugin, if active. 288 | 289 | By default, attached clients will be displayed. 290 | 291 | To disable the display of attached clients in the `statusline` please add the 292 | following to your initialization file: 293 | >lua 294 | vim.g.linefly_options = { 295 | with_attached_clients = false, 296 | } 297 | < 298 | ------------------------------------------------------------------------------ 299 | with_lsp_status~ *with_lsp_status* 300 | 301 | The `with_lsp_status` option specifies whether to display LSP progress status 302 | in the `statusline` if global `statusline` is in effect (`:set laststatus=3`). 303 | 304 | By default, LSP progress status will not be displayed. 305 | 306 | To enable the display of LSP progress status in the `statusline` please add the 307 | following to your initialization file: 308 | >lua 309 | vim.g.linefly_options = { 310 | with_lsp_status = true, 311 | } 312 | < 313 | ------------------------------------------------------------------------------ 314 | with_macro_status~ *with_macro_status* 315 | 316 | The `with_macro_status` option specifies whether to display macro-recording 317 | status in the `statusline`. This option is especially useful if `cmdheight` is 318 | set to `0`. 319 | 320 | By default, macro-recording status will not be displayed. 321 | 322 | To enable the display of macro-recording status in the `statusline` please add 323 | the following to your initialization file: 324 | >lua 325 | vim.g.linefly_options = { 326 | with_macro_status = true, 327 | } 328 | < 329 | ------------------------------------------------------------------------------ 330 | with_search_count~ *with_search_count* 331 | 332 | The `with_search_count` option specifies whether to display the search count 333 | in the `statusline`. This option is especially useful if `cmdheight` is set to 334 | `0`. 335 | 336 | By default, search count will not be displayed. 337 | 338 | To enable the display of the search count in the `statusline` please add the 339 | following to your initialization file: 340 | >lua 341 | vim.g.linefly_options = { 342 | with_search_count = true, 343 | } 344 | < 345 | 346 | Note, the search count is only displayed when the `hlsearch` option is set and 347 | the search count result is not zero. 348 | ------------------------------------------------------------------------------ 349 | with_spell_status~ *with_spell_status* 350 | 351 | The `with_spell_status` option specifies whether to display the spell status 352 | in the `statusline`. This option is especially useful if `cmdheight` is set to 353 | `0`. 354 | 355 | By default, spell status will not be displayed. 356 | 357 | To enable spell status in the `statusline` please add the following to your 358 | initialization file: 359 | >lua 360 | vim.g.linefly_options = { 361 | with_spell_status = true, 362 | } 363 | < 364 | ------------------------------------------------------------------------------ 365 | with_indent_status~ *with_indent_status* 366 | 367 | The `with_indent_status` option specifies whether to display the indentation 368 | status as the last component in the `statusline`. 369 | 370 | By default, indentation status will not be displayed. 371 | 372 | Note, if the `expandtab` option is set, for the current buffer, then tab stop 373 | will be displayed, for example `Tab:4` (tab equals four spaces); if on the 374 | other hand `noexpandtab` option is set then shift width will be displayed 375 | instead, for example `Spc:2` ('spc' short for 'space'). 376 | 377 | To enable indentation status please add the following to your initialization 378 | file: 379 | >lua 380 | vim.g.linefly_options = { 381 | with_indent_status = true, 382 | } 383 | < 384 | 385 | vim:tw=78:ts=8:noet:ft=help:norl: 386 | -------------------------------------------------------------------------------- /lua/linefly/constants.lua: -------------------------------------------------------------------------------- 1 | local M = {} 2 | 3 | -- Context choices related to icon highlight generation. 4 | M.location = { 5 | ActiveStatusLine = 1, 6 | InactiveStatusLine = 2, 7 | WinBar = 3, 8 | } 9 | 10 | return M 11 | -------------------------------------------------------------------------------- /lua/linefly/file.lua: -------------------------------------------------------------------------------- 1 | local highlight = require("linefly.highlight") 2 | local is_empty = require("linefly.utils").is_empty 3 | local location = require("linefly.constants").location 4 | local options = require("linefly.options").list 5 | local buf_get_name = vim.api.nvim_buf_get_name 6 | local buf_get_option = vim.api.nvim_buf_get_option 7 | local expand = vim.fn.expand 8 | local fnamemodify = vim.fn.fnamemodify 9 | local pathshorten = vim.fn.pathshorten 10 | 11 | local file_icon = function(context) 12 | if not options().with_file_icon or is_empty(buf_get_name(0)) or not vim.g.nvim_web_devicons then 13 | return "" 14 | end 15 | 16 | local icon, icon_highlight = 17 | require("nvim-web-devicons").get_icon(expand("%"), nil, { default = true }) 18 | if icon_highlight ~= nil then 19 | -- Generate the custom icon highlight group name. 20 | local custom_icon_highlight = "Linefly" .. icon_highlight 21 | if context == location.WinBar then 22 | custom_icon_highlight = custom_icon_highlight .. "WinBar" 23 | elseif context == location.InactiveStatusLine then 24 | custom_icon_highlight = custom_icon_highlight .. "NC" 25 | end 26 | 27 | -- Generate a custom highlight if it does not exist. 28 | highlight.generate_icon_group(custom_icon_highlight, icon_highlight, context) 29 | 30 | return "%#" .. custom_icon_highlight .. "#" .. icon .. "%* " 31 | else 32 | return icon .. " " 33 | end 34 | end 35 | 36 | local file_path = function(short_path) 37 | if is_empty(expand("%:f")) then 38 | return "" 39 | end 40 | 41 | if buf_get_option(0, "buftype") == "terminal" then 42 | return expand("%:t") 43 | end 44 | 45 | local separator = "/" 46 | if vim.fn.has("win64") == 1 then 47 | separator = "\\" 48 | end 49 | 50 | local path 51 | if short_path then 52 | path = pathshorten(fnamemodify(expand("%:f"), ":~:.")) 53 | else 54 | path = fnamemodify(expand("%:f"), ":~:.") 55 | end 56 | 57 | local path_components = vim.split(path, separator) 58 | local num_path_components = #path_components 59 | if num_path_components > 4 then 60 | local ellipsis = options().ellipsis_symbol or "…" 61 | -- In future, if Neovim switches to Lua 5.2 or above, 'unpack' will need 62 | -- to change to 'table.unpack'. 63 | path = ellipsis .. separator 64 | path = path .. table.concat({ unpack(path_components, num_path_components - 3) }, separator) 65 | end 66 | 67 | return path 68 | end 69 | 70 | local M = {} 71 | 72 | M.name = function(short_path, context) 73 | return file_icon(context) .. file_path(short_path) 74 | end 75 | 76 | return M 77 | -------------------------------------------------------------------------------- /lua/linefly/git.lua: -------------------------------------------------------------------------------- 1 | local utils = require("linefly.utils") 2 | local options = require("linefly.options").list 3 | local b = vim.b 4 | local buf_get_name = vim.api.nvim_buf_get_name 5 | 6 | local M = {} 7 | 8 | M.current_branch_name = function() 9 | if not options().with_git_branch or utils.is_empty(buf_get_name(0)) then 10 | return "" 11 | end 12 | 13 | local git_branch_name 14 | 15 | if package.loaded.gitsigns ~= nil then 16 | -- Gitsigns is available, let's use it to access the branch name. 17 | git_branch_name = b.gitsigns_head 18 | else 19 | -- Else use fallback detection. 20 | git_branch_name = b.git_branch_name 21 | end 22 | 23 | if utils.is_empty(git_branch_name) then 24 | return "" 25 | elseif #git_branch_name > 30 then 26 | -- Truncate long branch names to a max of 30 characters. 27 | git_branch_name = utils.truncate(git_branch_name) 28 | end 29 | 30 | local git_branch_symbol = options().git_branch_symbol or "" 31 | if utils.is_empty(git_branch_symbol) then 32 | return " " .. git_branch_name 33 | else 34 | return " " .. git_branch_symbol .. " " .. git_branch_name 35 | end 36 | 37 | return git_branch_name 38 | end 39 | 40 | -- Detect the branch name. This function will only be called upon BufEnter, 41 | -- BufWrite and FocusGained events to avoid needlessly invoking a system call 42 | -- every time the statusline is redrawn. 43 | M.detect_branch_name = function() 44 | if not options().with_git_branch or utils.is_empty(buf_get_name(0)) then 45 | -- Don't calculate the branch name if it isn't wanted or the buffer is 46 | -- empty. 47 | return "" 48 | end 49 | 50 | -- Benchmark the 'git branch --show-current' system call if required. 51 | -- local start = vim.loop.hrtime() 52 | local file_process_handle = io.popen("git branch --show-current 2> /dev/null") 53 | local git_branch_name = file_process_handle:read("*a") 54 | file_process_handle:close() 55 | -- print("Time in ms: " .. (vim.loop.hrtime() - start) / 1000000) 56 | 57 | if utils.is_empty(git_branch_name) then 58 | return "" 59 | else 60 | return string.gsub(git_branch_name, "\n", "") 61 | end 62 | end 63 | 64 | return M 65 | -------------------------------------------------------------------------------- /lua/linefly/highlight.lua: -------------------------------------------------------------------------------- 1 | local is_empty = require("linefly.utils").is_empty 2 | local is_present = require("linefly.utils").is_present 3 | local location = require("linefly.constants").location 4 | local options = require("linefly.options").list 5 | local g = vim.g 6 | local get_highlight = vim.api.nvim_get_hl 7 | local highlight = vim.api.nvim_set_hl 8 | 9 | -- Cache current statusline backgrounds for performance reasons; that being to 10 | -- avoid needless highlight extraction and generation. 11 | local statusline_bg 12 | local statusline_nc_bg 13 | 14 | -- Use a cache to avoid needlessly regenerating a highlight group for the same 15 | -- DevIcon filetype. 16 | local file_icon_highlight_cache = {} 17 | 18 | local highlight_empty = function(group) 19 | return get_highlight(0, { name = group }) == vim.empty_dict() 20 | or is_empty(get_highlight(0, { name = group, link = false }).bg) 21 | end 22 | 23 | local highlight_present = function(group) 24 | return get_highlight(0, { name = group }) ~= vim.empty_dict() 25 | end 26 | 27 | local synthesize_highlight = function(target, source, reverse) 28 | local source_fg 29 | 30 | if reverse then 31 | source_fg = get_highlight(0, { name = source, link = false }).bg 32 | else 33 | source_fg = get_highlight(0, { name = source, link = false }).fg 34 | end 35 | 36 | if is_present(statusline_bg) and is_present(source_fg) then 37 | highlight(0, target, { bg = statusline_bg, fg = source_fg }) 38 | else 39 | -- Fallback to statusline highlighting. 40 | highlight(0, target, { link = "StatusLine" }) 41 | end 42 | end 43 | 44 | local synthesize_mode_highlight = function(target, background, foreground) 45 | local mode_bg = get_highlight(0, { name = background, link = false }).fg 46 | local mode_fg = get_highlight(0, { name = foreground, link = false }).fg 47 | 48 | if is_present(mode_bg) and is_present(mode_fg) then 49 | highlight(0, target, { bg = mode_bg, fg = mode_fg }) 50 | else 51 | -- Fallback to statusline highlighting. 52 | highlight(0, target, { link = "StatusLine" }) 53 | end 54 | end 55 | 56 | local colorscheme_diagnostic_highlights = function() 57 | if highlight_present("DiagnosticError") then 58 | synthesize_highlight("LineflyDiagnosticError", "DiagnosticError", false) 59 | else 60 | highlight(0, "LineflyDiagnosticError", { link = "StatusLine" }) 61 | end 62 | if highlight_present("DiagnosticWarn") then 63 | synthesize_highlight("LineflyDiagnosticWarning", "DiagnosticWarn", false) 64 | else 65 | highlight(0, "LineflyDiagnosticWarning", { link = "StatusLine" }) 66 | end 67 | if highlight_present("DiagnosticInfo") then 68 | synthesize_highlight("LineflyDiagnosticInformation", "DiagnosticInfo", false) 69 | else 70 | highlight(0, "LineflyDiagnosticInformation", { link = "StatusLine" }) 71 | end 72 | end 73 | 74 | local colorscheme_git_highlights = function() 75 | if g.colors_name == "default" and highlight_present("Added") then 76 | synthesize_highlight("LineflyGitAdd", "Added", false) 77 | synthesize_highlight("LineflyGitChange", "Changed", false) 78 | synthesize_highlight("LineflyGitDelete", "Removed", false) 79 | elseif highlight_present("GitSignsAdd") then 80 | synthesize_highlight("LineflyGitAdd", "GitSignsAdd", false) 81 | synthesize_highlight("LineflyGitChange", "GitSignsChange", false) 82 | synthesize_highlight("LineflyGitDelete", "GitSignsDelete", false) 83 | elseif highlight_present("diffAdded") then 84 | synthesize_highlight("LineflyGitAdd", "diffAdded", false) 85 | synthesize_highlight("LineflyGitChange", "diffChanged", false) 86 | synthesize_highlight("LineflyGitDelete", "diffRemoved", false) 87 | else 88 | highlight(0, "LineflyGitAdd", { link = "StatusLine" }) 89 | highlight(0, "LineflyGitChange", { link = "StatusLine" }) 90 | highlight(0, "LineflyGitDelete", { link = "StatusLine" }) 91 | end 92 | end 93 | 94 | local colorscheme_mode_highlights = function() 95 | if g.colors_name == "moonfly" or g.colors_name == "nightfly" then 96 | -- Do nothing since both colorschemes already set linefly mode colors. 97 | return 98 | elseif g.colors_name == "default" then 99 | synthesize_mode_highlight("LineflyNormal", "Directory", "VertSplit") 100 | synthesize_mode_highlight("LIneflyInsert", "String", "VertSplit") 101 | synthesize_mode_highlight("LineflyVisual", "Identifier", "VertSplit") 102 | synthesize_mode_highlight("LineflyCommand", "WarningMsg", "VertSplit") 103 | synthesize_mode_highlight("LineflyReplace", "Removed", "VertSplit") 104 | elseif 105 | g.colors_name == "bamboo" 106 | or g.colors_name == "catppuccin-frappe" 107 | or g.colors_name == "catppuccin-latte" 108 | or g.colors_name == "catppuccin-macchiato" 109 | or g.colors_name == "catppuccin-mocha" 110 | or g.colors_name == "cyberdream" 111 | or g.colors_name == "dracula" 112 | or g.colors_name == "dracula-soft" 113 | or g.colors_name == "edge" 114 | or g.colors_name == "everforest" 115 | or g.colors_name == "gruvbox-material" 116 | or g.colors_name == "kanagawa" 117 | or g.colors_name == "minicyan" 118 | or g.colors_name == "minischeme" 119 | or g.colors_name == "nord" 120 | or g.colors_name == "nordic" 121 | or g.colors_name == "onedark" 122 | or g.colors_name == "rose-pine" 123 | or g.colors_name == "sonokai" 124 | or g.colors_name == "tokyonight" 125 | or g.colors_name == "vscode" 126 | then 127 | highlight(0, "LineflyNormal", { link = "MiniStatuslineModeNormal" }) 128 | highlight(0, "LineflyInsert", { link = "MiniStatuslineModeInsert" }) 129 | highlight(0, "LineflyVisual", { link = "MiniStatuslineModeVisual" }) 130 | highlight(0, "LineflyCommand", { link = "MiniStatuslineModeCommand" }) 131 | highlight(0, "LineflyReplace", { link = "MiniStatuslineModeReplace" }) 132 | elseif g.colors_name == "gruvbox" then 133 | synthesize_mode_highlight("LineflyNormal", "GruvboxFg4", "GruvboxBg0") 134 | synthesize_mode_highlight("LineflyInsert", "GruvboxBlue", "GruvboxBg0") 135 | synthesize_mode_highlight("LineflyVisual", "GruvboxOrange", "GruvboxBg0") 136 | synthesize_mode_highlight("LineflyCommand", "GruvboxGreen", "GruvboxBg0") 137 | synthesize_mode_highlight("LineflyReplace", "GruvboxRed", "GruvboxBg0") 138 | elseif g.colors_name == "retrobox" then 139 | synthesize_mode_highlight("LineflyNormal", "Structure", "VertSplit") 140 | synthesize_mode_highlight("LineflyInsert", "Directory", "VertSplit") 141 | highlight(0, "LineflyVisual", { link = "Visual" }) 142 | synthesize_mode_highlight("LineflyCommand", "MoreMsg", "VertSplit") 143 | highlight(0, "LineflyReplace", { link = "ErrorMsg" }) 144 | elseif 145 | g.colors_name == "carbonfox" 146 | or g.colors_name == "dawnfox" 147 | or g.colors_name == "duskfox" 148 | or g.colors_name == "dayfox" 149 | or g.colors_name == "nightfox" 150 | or g.colors_name == "nordfox" 151 | or g.colors_name == "terafox" 152 | then 153 | highlight(0, "LineflyNormal", { link = "MiniStatuslineModeOther" }) 154 | highlight(0, "LineflyInsert", { link = "MiniStatuslineModeInsert" }) 155 | highlight(0, "LineflyVisual", { link = "MiniStatuslineModeVisual" }) 156 | highlight(0, "LineflyCommand", { link = "MiniStatuslineModeCommand" }) 157 | highlight(0, "LineflyReplace", { link = "MiniStatuslineModeReplace" }) 158 | elseif g.colors_name == "oxocarbon" then 159 | highlight(0, "LineflyNormal", { link = "StatusNormal" }) 160 | highlight(0, "LineflyInsert", { link = "StatusInsert" }) 161 | highlight(0, "LineflyVisual", { link = "StatusVisual" }) 162 | highlight(0, "LineflyCommand", { link = "StatusCommand" }) 163 | highlight(0, "LineflyReplace", { link = "StatusReplace" }) 164 | elseif g.colors_name == "tokyodark" then 165 | highlight(0, "LineflyNormal", { link = "WildMenu" }) 166 | highlight(0, "LineflyInsert", { link = "Search" }) 167 | synthesize_mode_highlight("LineflyVisual", "Number", "VertSplit") 168 | synthesize_mode_highlight("LineflyCommand", "WarningMsg", "VertSplit") 169 | synthesize_mode_highlight("LineflyReplace", "Error", "VertSplit") 170 | elseif g.colors_name == "falcon" then 171 | synthesize_mode_highlight("LineflyNormal", "Directory", "NonText") 172 | synthesize_mode_highlight("LineflyInsert", "Done", "NonText") 173 | synthesize_mode_highlight("LineflyVisual", "javaScriptGlobal", "NonText") 174 | synthesize_mode_highlight("LineflyCommand", "DiagnosticWarn", "NonText") 175 | synthesize_mode_highlight("LineflyReplace", "ErrorMsg", "NonText") 176 | else 177 | -- Fallback for all other colorschemes. 178 | if highlight_empty("LineflyNormal") then 179 | synthesize_mode_highlight("LineflyNormal", "Directory", "VertSplit") 180 | end 181 | if highlight_empty("LineflyInsert") then 182 | synthesize_mode_highlight("LIneflyInsert", "String", "VertSplit") 183 | end 184 | if highlight_empty("LineflyVisual") then 185 | synthesize_mode_highlight("LineflyVisual", "Statement", "VertSplit") 186 | end 187 | if highlight_empty("LineflyCommand") then 188 | synthesize_mode_highlight("LineflyCommand", "WarningMsg", "VertSplit") 189 | end 190 | if highlight_empty("LineflyReplace") then 191 | synthesize_mode_highlight("LineflyReplace", "Error", "VertSplit") 192 | end 193 | end 194 | end 195 | 196 | local M = {} 197 | 198 | M.generate_groups = function() 199 | if g.colors_name == nil then 200 | return 201 | end 202 | 203 | -- Tweak the default Neovim theme to work better with Linefly if it is in effect. 204 | if g.colors_name == "default" then 205 | highlight(0, "StatusLine", { link = "StatusLineNC" }) 206 | highlight(0, "WinSeparator", { link = "SignColumn" }) 207 | end 208 | 209 | -- Extract current StatusLine background color, we will likely need it. 210 | local statusline_reverse = get_highlight(0, { name = "StatusLine", link = false }).reverse 211 | if statusline_reverse and statusline_reverse == true then 212 | -- Need to handle reversed highlights, such as Gruvbox StatusLine. 213 | statusline_bg = get_highlight(0, { name = "StatusLine", link = false }).fg 214 | statusline_nc_bg = get_highlight(0, { name = "StatusLineNC", link = false }).fg 215 | else 216 | -- Most colorschemes fall through to here. 217 | statusline_bg = get_highlight(0, { name = "StatusLine", link = false }).bg 218 | statusline_nc_bg = get_highlight(0, { name = "StatusLineNC", link = false }).bg 219 | end 220 | 221 | -- Mode highlights. 222 | colorscheme_mode_highlights() 223 | 224 | -- Synthesize emphasis colors from the existing mode colors. 225 | synthesize_highlight("LineflyNormalEmphasis", "LineflyNormal", true) 226 | synthesize_highlight("LineflyInsertEmphasis", "LineflyInsert", true) 227 | synthesize_highlight("LineflyVisualEmphasis", "LineflyVisual", true) 228 | synthesize_highlight("LineflyCommandEmphasis", "LineflyCommand", true) 229 | synthesize_highlight("LineflyReplaceEmphasis", "LineflyReplace", true) 230 | 231 | -- Synthesize plugin colors from relevant existing highlight groups. 232 | colorscheme_git_highlights() 233 | colorscheme_diagnostic_highlights() 234 | highlight(0, "LineflySession", { link = "LineflyGitAdd" }) 235 | 236 | if options().tabline and highlight_empty("TablineSelSymbol") then 237 | highlight(0, "TablineSelSymbol", { link = "TablineSel" }) 238 | end 239 | 240 | -- Invalidate the file icon cache; likely because a colorscheme change has 241 | -- occurred. 242 | file_icon_highlight_cache = {} 243 | end 244 | 245 | M.generate_icon_group = function(custom_icon_highlight, icon_highlight, context) 246 | -- Check if the custom highlight group already exists in the cache. 247 | local cached_icon_highlight = file_icon_highlight_cache[custom_icon_highlight] 248 | if cached_icon_highlight then 249 | -- No need to do anything, custom highlight group already exists. 250 | return 251 | end 252 | 253 | -- Extract the foreground color of the file icon. 254 | local source_fg = get_highlight(0, { name = icon_highlight, link = false }).fg 255 | 256 | if context == location.WinBar then 257 | if highlight_empty("WinBar") then 258 | -- Some themes do not set the WinBar highlight group, so just link to the 259 | -- base DevIcon highlight group. 260 | highlight(0, custom_icon_highlight, { link = icon_highlight }) 261 | else 262 | -- Use the theme's WinBar background color. 263 | local winbar_bg = get_highlight(0, { name = "WinBar", link = false }).bg 264 | highlight(0, custom_icon_highlight, { bg = winbar_bg, fg = source_fg }) 265 | end 266 | elseif context == location.InactiveStatusLine then 267 | -- Custom icon highlight for the inactive StatusLineNC. 268 | highlight(0, custom_icon_highlight, { bg = statusline_nc_bg, fg = source_fg }) 269 | else 270 | -- Custom icon highlight must be for the active StatusLine. 271 | highlight(0, custom_icon_highlight, { bg = statusline_bg, fg = source_fg }) 272 | end 273 | 274 | -- And lastly add the new highlight to the cache. 275 | file_icon_highlight_cache[custom_icon_highlight] = icon_highlight 276 | end 277 | 278 | return M 279 | -------------------------------------------------------------------------------- /lua/linefly/init.lua: -------------------------------------------------------------------------------- 1 | local file = require("linefly.file") 2 | local location = require("linefly.constants").location 3 | local options = require("linefly.options").list 4 | local utils = require("linefly.utils") 5 | local window = require("linefly.window") 6 | local buf_get_option = vim.api.nvim_buf_get_option 7 | local fn = vim.fn 8 | local fnamemodify = fn.fnamemodify 9 | local mode = vim.api.nvim_get_mode 10 | local o = vim.o 11 | local pathshorten = fn.pathshorten 12 | local tabpagenr = fn.tabpagenr 13 | local win_get_height = vim.api.nvim_win_get_height 14 | local wo = vim.wo 15 | 16 | -- Refer to ':help mode()' for the full list of available modes. For now only handle the most common 17 | -- modes. 18 | local modes_map = setmetatable({ 19 | ["n"] = { "%#LineflyNormal#", " normal ", "%#LineflyNormalEmphasis#" }, -- Normal 20 | ["no"] = { "%#LineflyNormal#", " o-pend ", "%#LineflyNormalEmphasis#" }, -- Operator pending 21 | ["niI"] = { "%#LineflyNormal#", " i-pend ", "%#LineflyNormalEmphasis#" }, -- Insert mode pending (Ctrl-o) 22 | ["niR"] = { "%#LineflyNormal#", " r-pend ", "%#LineflyNormalEmphasis#" }, -- Replace mode pending (Ctrl-o) 23 | ["i"] = { "%#LineflyInsert#", " insert ", "%#LineflyInsertEmphasis#" }, -- Insert 24 | ["ic"] = { "%#LineflyInsert#", " i-comp ", "%#LineflyInsertEmphasis#" }, -- Insert completion (generic) 25 | ["ix"] = { "%#LineflyInsert#", " i-comp ", "%#LineflyInsertEmphasis#" }, -- Insert completion (Ctrl-x) 26 | ["v"] = { "%#LineflyVisual#", " visual ", "%#LineflyVisualEmphasis#" }, -- Visual (v) 27 | ["vs"] = { "%#LineflyVisual#", " visual ", "%#LineflyVisualEmphasis#" }, -- Visual (Ctrl-o select mode) 28 | ["V"] = { "%#LineflyVisual#", " v-line ", "%#LineflyVisualEmphasis#" }, -- Visual line (V) 29 | ["Vs"] = { "%#LineflyVisual#", " v-line ", "%#LineflyVisualEmphasis#" }, -- Visual line (Ctrl-o select mode) 30 | ["\022"] = { "%#LineflyVisual#", " v-bloc ", "%#LineflyVisualEmphasis#" }, -- Visual block (Ctrl-v) 31 | ["s"] = { "%#LineflyVisual#", " select ", "%#LineflyVisualEmphasis#" }, -- Select (gh) 32 | ["S"] = { "%#LineflyVisual#", " s-line ", "%#LineflyVisualEmphasis#" }, -- Select line (gH) 33 | ["\019"] = { "%#LineflyVisual#", " s-bloc ", "%#LineflyVisualEmphasis#" }, -- Select block (CTRL-S) 34 | ["c"] = { "%#LineflyCommand#", " c-mode ", "%#LineflyCommandEmphasis#" }, -- Command line 35 | ["r"] = { "%#LineflyCommand#", " prompt ", "%#LineflyReplaceEmphasis#" }, -- Prompt for 'enter' 36 | ["rm"] = { "%#LineflyCommand#", " prompt ", "%#LineflyReplaceEmphasis#" }, -- Prompt for 'more' 37 | ["r?"] = { "%#LineflyCommand#", " prompt ", "%#LineflyReplaceEmphasis#" }, -- Prompt for 'confirmation' 38 | ["!"] = { "%#LineflyCommand#", " !-mode ", "%#LineflyReplaceEmphasis#" }, -- Command execution 39 | ["R"] = { "%#LineflyReplace#", " r-mode ", "%#LineflyReplaceEmphasis#" }, -- Replace 40 | ["Rc"] = { "%#LineflyReplace#", " r-comp ", "%#LineflyReplaceEmphasis#" }, -- Replace completion (generic) 41 | ["Rx"] = { "%#LineflyReplace#", " r-comp ", "%#LineflyReplaceEmphasis#" }, -- Replace completion (Ctrl-x) 42 | ["t"] = { "%#LineflyInsert#", " t-mode ", "%#LineflyInsertEmphasis#" }, -- Terminal 43 | ["nt"] = { "%#LineflyNormal#", " normal ", "%#LineflyNormalEmphasis#" }, -- Terminal normal mode 44 | }, { 45 | __index = function() 46 | return { "%#LineflyNormal#", " normal ", "%#LineflyNormalEmphasis#" } -- Handle edge modes 47 | end, 48 | }) 49 | 50 | local M = {} 51 | 52 | _G.linefly = M 53 | 54 | ------------------------------------------------------------ 55 | -- Status line 56 | ------------------------------------------------------------ 57 | 58 | M.active_statusline = function(lsp_status) 59 | local current_mode = mode().mode 60 | local separator = options().separator_symbol or "⎪" 61 | local progress = options().progress_symbol or "↓" 62 | local branch_name = require("linefly.git").current_branch_name() 63 | local plugins_status = require("linefly.plugins").status() 64 | local mode_emphasis = modes_map[current_mode][3] 65 | local statusline_width = window.statusline_width() 66 | 67 | local statusline = modes_map[current_mode][1] 68 | if statusline_width < 80 then 69 | -- Use short mode-indicator if the statusline width is less than 80 columns. 70 | statusline = statusline .. string.sub(modes_map[current_mode][2], 1, 2) .. " " 71 | else 72 | statusline = statusline .. modes_map[current_mode][2] 73 | end 74 | statusline = statusline .. "%* %<" .. file.name(statusline_width < 120, location.ActiveStatusLine) 75 | statusline = statusline .. "%q%{exists('w:quickfix_title')? ' ' . w:quickfix_title : ''}" 76 | statusline = statusline .. "%{&modified ? '+ ' : ' '}" 77 | statusline = statusline .. "%{&readonly ? 'RO ' : ''}" 78 | if utils.is_present(branch_name) and statusline_width >= 80 then 79 | statusline = statusline .. "%*" .. separator .. mode_emphasis 80 | statusline = statusline .. branch_name .. "%* " 81 | end 82 | if utils.is_present(plugins_status) and statusline_width >= 80 then 83 | statusline = statusline .. plugins_status 84 | statusline = statusline .. "%*" 85 | end 86 | if options().with_attached_clients and statusline_width >= 120 then 87 | local attached_clients = require("linefly.lsp").attached_clients() 88 | if utils.is_present(attached_clients) then 89 | statusline = statusline .. separator .. " " .. attached_clients 90 | end 91 | end 92 | if 93 | options().with_lsp_status 94 | and utils.is_present(lsp_status) 95 | and o.laststatus == 3 96 | and statusline_width >= 120 97 | then 98 | -- Note, LSP progress status is propagated down from an LspProgress event. 99 | statusline = statusline .. "%=" .. lsp_status 100 | end 101 | -- Right-hand side section. 102 | statusline = statusline .. "%=" 103 | if o.showcmdloc == "statusline" and o.showcmd and statusline_width >= 80 then 104 | statusline = statusline .. "%S " .. separator .. " " 105 | end 106 | if options().with_macro_status and statusline_width >= 80 then 107 | local recording_register = fn.reg_recording() 108 | if utils.is_present(recording_register) then 109 | statusline = statusline .. "%#LineflySession#recording @" 110 | statusline = statusline .. recording_register .. "%* " .. separator .. " " 111 | end 112 | end 113 | if options().with_search_count and vim.v.hlsearch == 1 and statusline_width >= 80 then 114 | local search_count = utils.search_count() 115 | if utils.is_present(search_count) then 116 | statusline = statusline .. search_count .. " " .. separator .. " " 117 | end 118 | end 119 | if options().with_spell_status and o.spell and statusline_width >= 80 then 120 | statusline = statusline .. "Spell " .. separator .. " " 121 | end 122 | statusline = statusline .. "%l:%c " .. separator 123 | statusline = statusline .. " " .. mode_emphasis .. "%L%* " .. progress .. "%P " 124 | if options().with_indent_status then 125 | statusline = statusline .. separator .. " " .. utils.indent_status() 126 | end 127 | 128 | return statusline 129 | end 130 | 131 | M.inactive_statusline = function() 132 | local separator = options().separator_symbol or "⎪" 133 | local progress = options().progress_symbol or "↓" 134 | 135 | local statusline = " %<" 136 | statusline = statusline .. file.name(window.statusline_width() <= 120, location.InactiveStatusLine) 137 | statusline = statusline .. "%{&modified?'+ ':' '}" 138 | statusline = statusline .. "%{&readonly?'RO ':''}" 139 | statusline = statusline .. "%=%l:%c " .. separator .. " %L " .. progress .. "%P " 140 | if options().with_indent_status then 141 | statusline = statusline .. separator .. " " .. utils.indent_status() 142 | end 143 | 144 | return statusline 145 | end 146 | 147 | M.statusline = function(active, lsp_status) 148 | local bt = buf_get_option(0, "buftype") 149 | local ft = buf_get_option(0, "filetype") 150 | 151 | if bt == "nofile" or ft == "qf" or ft == "netrw" or ft == "oil" then 152 | if string.sub(ft, 1, 5) == "dapui" then 153 | -- This is a nvim-dap-ui window; hence, configure a simple statusline and winbar (if needed) 154 | -- for this type of debugger window. 155 | wo.statusline = " %f" 156 | if o.laststatus == 3 and options().winbar then 157 | wo.winbar = "%{%v:lua.linefly.inactive_winbar()%}" 158 | else 159 | wo.winbar = nil 160 | end 161 | return 162 | end 163 | -- Else, likely a file explorer or some other special type of buffer. Set a short path 164 | -- statusline for these types of buffers. 165 | wo.statusline = pathshorten(fnamemodify(vim.fn.getcwd(), ":~:.")) 166 | if options().winbar then 167 | wo.winbar = nil 168 | end 169 | elseif bt == "nowrite" then 170 | -- Do not set statusline and winbar for certain special windows. 171 | return 172 | elseif window.is_floating() then 173 | -- Do not set statusline and winbar for floating windows. 174 | return 175 | elseif active then 176 | if utils.is_present(lsp_status) then 177 | wo.statusline = "%{%v:lua.linefly.active_statusline('" .. lsp_status .. "')%}" 178 | else 179 | wo.statusline = "%{%v:lua.linefly.active_statusline()%}" 180 | end 181 | if options().winbar and window.count() > 1 then 182 | wo.winbar = "%{%v:lua.linefly.active_winbar()%}" 183 | else 184 | wo.winbar = nil 185 | end 186 | else 187 | wo.statusline = "%{%v:lua.linefly.inactive_statusline()%}" 188 | if options().winbar and window.count() > 1 and win_get_height(0) > 1 then 189 | wo.winbar = "%{%v:lua.linefly.inactive_winbar()%}" 190 | else 191 | wo.winbar = nil 192 | end 193 | end 194 | end 195 | 196 | ------------------------------------------------------------ 197 | -- Window bar 198 | ------------------------------------------------------------ 199 | 200 | M.active_winbar = function() 201 | local current_mode = mode().mode 202 | 203 | local winbar = modes_map[current_mode][1] 204 | winbar = winbar .. string.sub(modes_map[current_mode][2], 1, 2) 205 | winbar = winbar .. " %* %<" .. file.name(true, location.WinBar) 206 | winbar = winbar .. "%{&modified ? '+ ' : ' '}" 207 | winbar = winbar .. "%{&readonly ? 'RO ' : ''}" 208 | winbar = winbar .. "%#Normal#" 209 | 210 | return winbar 211 | end 212 | 213 | M.inactive_winbar = function() 214 | local winbar = " %<" .. file.name(true, location.WinBar) 215 | winbar = winbar .. "%{&modified?'+ ':' '}" 216 | winbar = winbar .. "%{&readonly?'RO ':''}" 217 | winbar = winbar .. "%#Normal#" 218 | 219 | return winbar 220 | end 221 | 222 | ------------------------------------------------------------ 223 | -- Tab line 224 | ------------------------------------------------------------ 225 | 226 | M.active_tabline = function() 227 | local symbol = options().active_tab_symbol or "▪" 228 | local tabline = "" 229 | local counter = 0 230 | 231 | for _ = 1, tabpagenr("$"), 1 do 232 | counter = counter + 1 233 | tabline = tabline .. "%" .. counter .. "T" 234 | if tabpagenr() == counter then 235 | tabline = tabline .. "%#TablineSelSymbol#" .. symbol .. "%#TablineSel# Tab:" 236 | else 237 | tabline = tabline .. "%#TabLine# Tab:" 238 | end 239 | tabline = tabline .. counter .. "%T" .. " %#TabLineFill#" 240 | end 241 | 242 | return tabline 243 | end 244 | 245 | M.tabline = function() 246 | if options().tabline then 247 | o.tabline = "%{%v:lua.linefly.active_tabline()%}" 248 | end 249 | end 250 | 251 | return M 252 | -------------------------------------------------------------------------------- /lua/linefly/lsp.lua: -------------------------------------------------------------------------------- 1 | local utils = require("linefly.utils") 2 | local options = require("linefly.options").list 3 | local get_current_buf = vim.api.nvim_get_current_buf 4 | local lsp = vim.lsp 5 | 6 | local M = {} 7 | 8 | M.attached_clients = function() 9 | local buf_attached_clients = {} 10 | local buf_lsp_clients 11 | buf_lsp_clients = lsp.get_clients({ bufnr = get_current_buf() }) 12 | 13 | if buf_lsp_clients and #buf_lsp_clients > 0 then 14 | for _, lsp_client in pairs(buf_lsp_clients) do 15 | table.insert(buf_attached_clients, lsp_client.name or "") 16 | end 17 | end 18 | 19 | -- Check if the nvim-lint plugin is loaded and whether any clients are attached. 20 | if package.loaded.lint ~= nil then 21 | local buf_lint_clients = require("lint").linters_by_ft[vim.bo.filetype] 22 | if buf_lint_clients and #buf_lint_clients > 0 then 23 | for _, lint_client in pairs(buf_lint_clients) do 24 | table.insert(buf_attached_clients, lint_client or "") 25 | end 26 | end 27 | end 28 | 29 | -- Exit early if there are no clients attached. 30 | if #buf_attached_clients == 0 then 31 | return 32 | end 33 | 34 | -- Comma-separate language server & linter names. 35 | local names = table.concat(buf_attached_clients, ", ") 36 | 37 | -- Truncate long language server & linter names if necessary. 38 | return utils.truncate(names) 39 | end 40 | 41 | M.status = function(data) 42 | if not options().with_lsp_status or vim.o.laststatus ~= 3 then 43 | -- Exit early if LSP status is not wanted or global statusline is not in effect. 44 | return 45 | end 46 | 47 | local lsp_status 48 | if data.data.params.value.kind == "end" then 49 | return "" -- This will clear out the last LSP status message in the statusline. 50 | else 51 | lsp_status = lsp.status() 52 | end 53 | 54 | if utils.is_present(lsp_status) then 55 | -- Truncate long LSP status messages if necessary. 56 | return utils.truncate(lsp_status) 57 | else 58 | return lsp_status 59 | end 60 | return "" 61 | end 62 | 63 | return M 64 | -------------------------------------------------------------------------------- /lua/linefly/options.lua: -------------------------------------------------------------------------------- 1 | local g = vim.g 2 | local options_initialized = false 3 | 4 | local M = {} 5 | 6 | M.list = function() 7 | if not options_initialized or not g.linefly_options then 8 | g.linefly_options = g.linefly_options or {} 9 | local default_options = { 10 | separator_symbol = "⎪", 11 | progress_symbol = "↓", 12 | active_tab_symbol = "▪", 13 | git_branch_symbol = "", 14 | error_symbol = "E", 15 | warning_symbol = "W", 16 | information_symbol = "I", 17 | ellipsis_symbol = "…", 18 | tabline = false, 19 | winbar = false, 20 | with_file_icon = true, 21 | with_git_branch = true, 22 | with_git_status = true, 23 | with_diagnostic_status = true, 24 | with_session_status = true, 25 | with_attached_clients = true, 26 | with_lsp_status = false, 27 | with_macro_status = false, 28 | with_search_count = false, 29 | with_spell_status = false, 30 | with_indent_status = false, 31 | } 32 | g.linefly_options = vim.tbl_extend("force", default_options, g.linefly_options) 33 | options_initialized = true 34 | end 35 | 36 | return g.linefly_options 37 | end 38 | 39 | return M 40 | -------------------------------------------------------------------------------- /lua/linefly/plugins.lua: -------------------------------------------------------------------------------- 1 | local utils = require("linefly.utils") 2 | local options = require("linefly.options").list 3 | local diagnostic = vim.diagnostic 4 | local eval = vim.api.nvim_eval 5 | local g = vim.g 6 | 7 | local M = {} 8 | 9 | M.status = function() 10 | local segments = "" 11 | 12 | -- Git status. 13 | if options().with_git_status and package.loaded.gitsigns then 14 | local signs = vim.b.gitsigns_status_dict 15 | if signs and signs.added and signs.added > 0 then 16 | segments = segments .. " %#LineflyGitAdd#+" .. signs.added .. "%*" 17 | end 18 | if signs and signs.changed and signs.changed > 0 then 19 | segments = segments .. " %#LineflyGitChange#~" .. signs.changed .. "%*" 20 | end 21 | if signs and signs.removed and signs.removed > 0 then 22 | segments = segments .. " %#LineflyGitDelete#-" .. signs.removed .. "%*" 23 | end 24 | if utils.is_present(segments) then 25 | segments = segments .. " " 26 | end 27 | end 28 | 29 | -- Diagnostic status. 30 | if options().with_diagnostic_status and g.lspconfig then 31 | local errors = #diagnostic.get(0, { severity = diagnostic.severity.ERROR }) 32 | local warnings = #diagnostic.get(0, { severity = diagnostic.severity.WARN }) 33 | local information = #diagnostic.get(0, { severity = diagnostic.severity.INFO }) 34 | local error_symbol = options().error_symbol or "E" 35 | local warning_symbol = options().warning_symbol or "W" 36 | local information_symbol = options().information_symbol or "I" 37 | 38 | if errors > 0 then 39 | segments = segments .. " %#LineflyDiagnosticError#" .. error_symbol 40 | segments = segments .. " " .. errors .. "%*" 41 | end 42 | if warnings > 0 then 43 | segments = segments .. " %#LineflyDiagnosticWarning#" .. warning_symbol 44 | segments = segments .. " " .. warnings .. "%*" 45 | end 46 | if information > 0 then 47 | segments = segments .. " %#LineflyDiagnosticInformation#" .. information_symbol 48 | segments = segments .. " " .. information .. "%*" 49 | end 50 | if errors > 0 or warnings > 0 or information > 0 then 51 | segments = segments .. " " 52 | end 53 | end 54 | 55 | -- Session status. 56 | local session_segment 57 | if options().with_session_status and g.loaded_obsession then 58 | session_segment = eval([[ObsessionStatus('obsession', '!obsession')]]) 59 | elseif options().with_session_status and package.loaded.possession then 60 | session_segment = require("possession.session").get_session_name() 61 | elseif options().with_session_status and package.loaded["nvim-possession"] then 62 | session_segment = require("nvim-possession").status() 63 | end 64 | if utils.is_present(session_segment) then 65 | segments = segments .. " %#LineflySession#" .. utils.truncate(session_segment) .. " %*" 66 | end 67 | 68 | return segments 69 | end 70 | 71 | return M 72 | -------------------------------------------------------------------------------- /lua/linefly/utils.lua: -------------------------------------------------------------------------------- 1 | local options = require("linefly.options").list 2 | local o = vim.o 3 | local format = string.format 4 | 5 | local M = {} 6 | 7 | M.is_empty = function(str) 8 | return str == nil or string.len(str) == 0 9 | end 10 | 11 | M.is_present = function(str) 12 | return str and string.len(str) > 0 13 | end 14 | 15 | M.truncate = function(str) 16 | if #str > 30 then 17 | return string.sub(str, 1, 29) .. options().ellipsis_symbol 18 | else 19 | return str 20 | end 21 | end 22 | 23 | M.indent_status = function() 24 | if not o.expandtab then 25 | return "Tab:" .. o.tabstop .. " " 26 | else 27 | local size = o.shiftwidth 28 | if size == 0 then 29 | size = o.tabstop 30 | end 31 | return "Spc:" .. size .. " " 32 | end 33 | end 34 | 35 | M.search_count = function() 36 | local ok, result = pcall(vim.fn.searchcount, { recompute = 1, maxcount = 0 }) 37 | 38 | if not ok or not result then -- failed search 39 | return "" 40 | elseif result.total == 0 then -- no matches 41 | return "" 42 | elseif result.incomplete == 1 then -- timed out 43 | return "[?/??]" 44 | elseif result.incomplete == 2 then -- max count exceeded 45 | if result.total > result.maxcount and result.current > result.maxcount then 46 | return format("[>%d/>%d]", result.current, result.total) 47 | elseif result.total > result.maxcount then 48 | return format("[%d/>%d]", result.current, result.total) 49 | end 50 | end 51 | 52 | return format("[%d/%d]", result.current, result.total) 53 | end 54 | 55 | return M 56 | -------------------------------------------------------------------------------- /lua/linefly/window.lua: -------------------------------------------------------------------------------- 1 | local options = require("linefly.options").list 2 | local utils = require("linefly.utils") 3 | local buf_get_option = vim.api.nvim_buf_get_option 4 | local get_option = vim.api.nvim_get_option 5 | local tabpage_list_wins = vim.api.nvim_tabpage_list_wins 6 | local win_get_buf = vim.api.nvim_win_get_buf 7 | local win_get_config = vim.api.nvim_win_get_config 8 | local win_get_height = vim.api.nvim_win_get_height 9 | local win_get_number = vim.api.nvim_win_get_number 10 | local win_get_width = vim.api.nvim_win_get_width 11 | local win_set_option = vim.api.nvim_win_set_option 12 | 13 | local M = {} 14 | 15 | -- Return the number of split windows exluding floating windows and other 16 | -- special windows. 17 | M.count = function() 18 | local count = 0 19 | local windows = tabpage_list_wins(0) 20 | 21 | for _, w in pairs(windows) do 22 | local cfg = win_get_config(w) 23 | local bt = buf_get_option(win_get_buf(w), "buftype") 24 | local ft = buf_get_option(win_get_buf(w), "filetype") 25 | 26 | if utils.is_empty(cfg.relative) and bt ~= "nofile" and ft ~= "netrw" and ft ~= "oil" then 27 | count = count + 1 28 | end 29 | end 30 | 31 | return count 32 | end 33 | 34 | M.is_floating = function() 35 | if utils.is_present(win_get_config(0).relative) then 36 | return true 37 | else 38 | return false 39 | end 40 | end 41 | 42 | -- Iterate though the windows and update the statusline and winbar for all 43 | -- inactive windows. 44 | -- 45 | -- This is needed when starting Neovim with multiple splits, for example 'nvim 46 | -- -O file1 file2', otherwise all statuslines/winbars will be rendered as if 47 | -- they are active. Inactive statuslines/winbar are usually rendered via the 48 | -- WinLeave and BufLeave events, but those events are not triggered when 49 | -- starting Vim. 50 | -- 51 | -- Note - https://jip.dev/posts/a-simpler-vim-statusline/#inactive-statuslines 52 | M.update_inactive = function() 53 | local windows = tabpage_list_wins(0) 54 | local current_window = win_get_number(0) 55 | 56 | for _, w in pairs(windows) do 57 | if win_get_number(w) ~= current_window then 58 | win_set_option(w, "statusline", "%{%v:lua.linefly.inactive_statusline()%}") 59 | if options().winbar and win_get_height(0) > 1 then 60 | win_set_option(w, "winbar", "%{%v:lua.linefly.inactive_winbar()%}") 61 | end 62 | end 63 | end 64 | end 65 | 66 | M.statusline_width = function() 67 | if vim.o.laststatus == 3 then 68 | return get_option("columns") 69 | else 70 | return win_get_width(0) 71 | end 72 | end 73 | 74 | return M 75 | -------------------------------------------------------------------------------- /nvim.toml: -------------------------------------------------------------------------------- 1 | [vim] 2 | any = true 3 | 4 | [use] 5 | any = true 6 | -------------------------------------------------------------------------------- /plugin/linefly.lua: -------------------------------------------------------------------------------- 1 | -- A simple Neovim statusline. 2 | -- 3 | -- URL: github.com/bluz71/nvim-linefly 4 | -- License: MIT (https://opensource.org/licenses/MIT) 5 | 6 | if vim.fn.has("nvim-0.10") ~= 1 then 7 | vim.api.nvim_echo({ { "nvim-linefly requires Neovim 0.10, or later", "WarningMsg" } }, false, {}) 8 | return 9 | end 10 | 11 | local g = vim.g 12 | if g.lineflyLoaded ~= nil then 13 | return 14 | end 15 | g.lineflyLoaded = true 16 | 17 | local linefly = require("linefly") 18 | local augroup = vim.api.nvim_create_augroup 19 | local autocmd = vim.api.nvim_create_autocmd 20 | 21 | local linefly_events = augroup("LineflyEvents", {}) 22 | 23 | autocmd({ "VimEnter", "ColorScheme" }, { 24 | callback = function() 25 | require("linefly.highlight").generate_groups() 26 | end, 27 | group = linefly_events, 28 | }) 29 | 30 | autocmd("VimEnter", { 31 | callback = function() 32 | require("linefly.window").update_inactive() 33 | end, 34 | group = linefly_events, 35 | }) 36 | 37 | autocmd("VimEnter", { 38 | callback = function() 39 | linefly.tabline() 40 | end, 41 | group = linefly_events, 42 | }) 43 | 44 | autocmd({ "WinEnter", "BufWinEnter" }, { 45 | callback = function() 46 | linefly.statusline(true) 47 | end, 48 | group = linefly_events, 49 | }) 50 | 51 | autocmd("WinLeave", { 52 | callback = function() 53 | linefly.statusline(false) 54 | end, 55 | group = linefly_events, 56 | }) 57 | 58 | autocmd({ "BufEnter", "BufWrite", "FocusGained" }, { 59 | callback = function() 60 | if package.loaded.gitsigns == nil then 61 | -- Gitsigns is not loaded, use fallback Git branch name detection. 62 | vim.b.git_branch_name = require("linefly.git").detect_branch_name() 63 | end 64 | end, 65 | group = linefly_events, 66 | }) 67 | 68 | autocmd({ "DiagnosticChanged", "LspAttach", "LspDetach" }, { 69 | callback = function() 70 | linefly.statusline(true) 71 | end, 72 | group = linefly_events, 73 | }) 74 | 75 | autocmd("User", { 76 | pattern = "GitSignsUpdate", 77 | callback = function() 78 | linefly.statusline(true) 79 | end, 80 | group = linefly_events, 81 | }) 82 | 83 | autocmd("LspProgress", { 84 | callback = function(data) 85 | local lsp_status = require("linefly.lsp").status(data) 86 | if lsp_status then 87 | linefly.statusline(true, lsp_status) 88 | end 89 | end, 90 | group = linefly_events, 91 | }) 92 | -------------------------------------------------------------------------------- /selene.toml: -------------------------------------------------------------------------------- 1 | std="lua51+nvim" 2 | 3 | [rules] 4 | global_usage = "allow" 5 | mixed_table = "allow" 6 | -------------------------------------------------------------------------------- /stylua.toml: -------------------------------------------------------------------------------- 1 | column_width = 100 2 | indent_type = "Spaces" 3 | indent_width = 2 4 | --------------------------------------------------------------------------------