rktjmp / lush.nvim

Create Neovim themes with real-time feedback, export anywhere.
MIT License
1.45k stars 47 forks source link

conditional lighline loading #27

Closed ellisonleao closed 3 years ago

ellisonleao commented 3 years ago

hey guys, i see theres a lightline example here . Is there a way we can conditionally load this based if the user has the plugin or not? i am trying to support it in my gruvbox.nvim plugin, but the current code example only works if you have the plugin installed.

I have tried several approaches

if vim.g.lightline then
 -- call lightline fill and colorscheme
end

also:

vim.schedule_wrap(function()
  --- fill and colorscheme calls
end)

all of them are raising errors about lightline fill function not available yet.

rktjmp commented 3 years ago

Its a bit hard to tell without more code context but you won't be able to call other functions inside a lush spec because of how the meta programming works, if that's what you're trying to do.

If you're following the example (get parsed spec then use parsed spec to define lightline colours), then I can't see why if vim.g.lightline or however you can check for lightline wouldn't work.

At that point you're outside of lush and just working in Lua with a table (aka the parsed spec) that contains some colours. Maybe newer/older lightline versions don't include the fill helper now or something :shrug:

Per the comments, vim.schedule is just to handle lightlines stubborn-ness to apply realtime changes.

Just make sure you remember to return the lush spec at the end if you follow the example structure, though I would probably break the files up:

-- lush_theme/gruvbox.lua
return lush(... gruv spec)
-- lush_theme/gruvbox/plugins/lightline.lua
gruv = require 'lush_theme.gruvbox'

local lightline_theme = {
   normal = {
     left = {
       -- need to call hex for lightline helper to not fall over if I recall correctly
       {gruv.Normal.fg.hex, gruv.Normal.bg.hex},
   ...
}
-- do whatever to make lightline get colour
-- maybe in the colors/gruv.vim file?
if has_lightline then
  lua require('lush_theme.gruvbox.plugins.lightline')
fi -- or whatever viml looks like

Edit: Indeed, I made an example for this in lightline_two_file

rktjmp commented 3 years ago

Just to be explicit: Lush doesn't provide any supporting functions related to other plugins. The lightline#fill() function is part of lightline.

rktjmp commented 3 years ago

It may be related to load order (I load lightline, lush, lush theme then call colorscheme) or if you have the normal gruvbox and yours sharing the same "colors" name that might cause issues with light line maybe.

rktjmp commented 3 years ago

image

https://github.com/rktjmp/lush.nvim/blob/e4b4ce99253ee806fcf317fbd7e677207b0b253a/examples/lightline-one-file/lua/lush_theme/lightline_one_file.lua#L50

I don't really have time to go through the whole stream, but as per the example you have to call .hex on the colours (bg0.hex, assuming bg0 is a hsl() colour). Viml isn't smart enough to use the auto-stringify code when you cross the lua bridge. From what I remember, lightline is really specific about the format of the map you pass it too.

rktjmp commented 3 years ago

Nevermind I see you got there in the end.

I believe the errors you are getting at the end of the stream are actually from lightline's fill function.

Your maps dont have a second element, which is why you get the "no bg key" error. From what I remember it must have fg and bg in every entry.

image

I.e I think you need error = {{some_fg_value, some_bg_value}}

Pretty sure I made the exact same mistake when I first wrote my lightline stuff :1st_place_medal: (which was transplanted into the example so I am confident it works).

It's a bit confusing because they share the same key names, and you did have the error of accessing GruboxFg0.bg which fails for a similar but different error (the lush group never had a bg key defined).

Post your work on a branch and I can try and take a look at it.

rktjmp commented 3 years ago

TLDR

local has_lightline, _ = pcall(vim.fn["lightline#update"])
if has_lightline then
  local ll_filled = vim.fn['lightline#colorscheme#fill'](ll)
  vim.g['lightline#colorscheme#harbour#palette'] = ll_filled
  vim.schedule(function()
      -- need to call this, but could instruct user to call it after setting up
      -- their g:lightline {} settings or something (untested)
      vim.fn['lightline#disable']()
      vim.fn['lightline#enable']()
  end)
end

Why?!

There are a few moving parts here.

Likely, you have a package manager loading all your stuff, then you call colorscheme, them nvim enters a ready state.

why vim.g.loaded_lightline wont work

(PS: g:lightline is a user config variable afaik, not an indication of load state.)

You can inspect the output of :scriptnames to see how this all comes together, but for me it looks like:

  1: ~/.config/nvim/init.vim
  2: ~/.config/nvim/init.d/010-feature-detect.vim
  3: ~/.config/nvim/init.d/015-pre-plugs.vim
  4: ~/.config/nvim/init.d/020-plugs.vim
  5: ~/.local/share/nvim/site/autoload/plug.vim
  6: ~/.asdf/installs/neovim/nightly/share/nvim/runtime/filetype.vim
...
 21: ~/.config/nvim/init.d/100-autocomplete.vim
 22: ~/.config/nvim/init.d/100-colorscheme.vim <- file says what colorscheme to load
 23: ~/projects/lush-harbour/colors/harbour.vim <- vim finds color scheme and executes it
...
 26: ~/.config/nvim/init.d/100-lightline.vim <- sets my LL config (colorscheme name and custom info parts)
...
 30: ~/.config/nvim/init.d/900-wips.vim
 31: ~/.config/nvim/init.d/999-finally.vim <- leaving my vimrc
 32: ~/.local/share/nvim/site/plugged/nvim-treesitter/plugin/nvim-treesitter.vim
...
 64: ~/.local/share/nvim/site/plugged/lightline.vim/plugin/lightline.vim <- FINALLY lightline loads, sets g:loaded_lightline

As you can see, the scheme is loaded and run well before lightline bothers to show up, even though it's loaded first in the package manager. AFAIK, this is because the package manager only really manages vims runtime path, so it lets vim know what to load, but order and time are somewhat indeterminate.

Why banging lightline#enable() wont work

Somewhat infuriating, if we force calling lightline#enable() or similar our colorscheme file, we do force vim to autoload lightline, but not the main lightline plugin file, so g:loaded_lightline remains unset!

 22: ~/.config/nvim/init.d/100-colorscheme.vim
 23: ~/projects/lush-harbour/colors/harbour.vim
 24: ~/.local/share/nvim/site/plugged/lightline.vim/autoload/lightline.vim
 25: ~/.local/share/nvim/site/plugged/lightline.vim/autoload/lightline/tab.vim
 26: ~/.local/share/nvim/site/plugged/lightline.vim/autoload/lightline/colorscheme/default.vim
 27: ~/.local/share/nvim/site/plugged/lightline.vim/autoload/lightline/colorscheme/powerline.vim
 28: ~/.local/share/nvim/site/plugged/lightline.vim/autoload/lightline/colorscheme.vim
 29: ~/.config/nvim/init.d/100-dirvish.vim
 30: ~/.config/nvim/init.d/100-fvim.vim

But, it does prompt the autoloader to wake up

This may break via upstream...

We can abuse the autoloader to force lightline into memory, then check against that. lightline#update is the most common function called in lightline, and it seems safest as it has some checks around being inited or not.

https://github.com/itchyny/lightline.vim/blob/8e013f32f524157bf14ccaa87d97be3d3a7201e2/autoload/lightline.vim#L13

We can use the lua-vim bridge 1) force the autoload and 2) run a function.

We can use pcall to trap any error (i.e. if lightline doesn't exist, calling the function will fail). pcall returns {true, function_return_value} or {false, error}.

Note: vim.fn.___ are metaprogrammed, you can't just check if vim.fn["lightline#enabled"] then exists, because any key will return a function, you have to call it to actually resolve.

So:

-- attempt to call lightline#update, will return:

-- {true, some_value} if the function exists and the call was successful, or

-- {false, error} if it doesn't exist and the call failed

-- more technically, false if the function spawns a lua error,
-- which can't happen here, but may if you apply this check style
-- to other (lua) plugins and they fail internally.

local has_lightline, _ = pcall(vim.fn["lightline#update"])
if has_lightline then
   -- do stuff
end

This is pretty ugly IMO, and is liable to break if neovim or lightline change something (probably unlikely) but I am not sure there is a better solution to plugin discovery, just because of how vim loading works.

Maybe there is some method to not set the colorscheme until after absolutely everything else is loaded but that might be even uglier.

You could also just elect to have a user config setting and check that, which is probably much more reliable in the long run (?), if a little worse for UX.

Pretty frustrating to track down.

You might want to try and get some function added to Lightline, "has_lightline()", that can be called for no side effects. But I imagine you would just get hit with a "we don't support NVim and what is a Lush".

Also

his may cause you trouble, ensure you are configuring LL when you're testing, the lua code provided only adds the lush theme into LL colorscheme choices, it doesn't set the colorscheme.

See here: https://github.com/rktjmp/lush.nvim/blob/8f03467d6dbb6c24cfa5ebe538ec873f9d4235bb/examples/lightline-one-file/colors/lightline_one_file.vim#L1

rktjmp commented 3 years ago

Note you need the schedule call too to convince LL to pay attention. Maybe you don't need this if you ... i dunno, refresh light line somewhere else. I think because it all goes in vim dictionaries and LL caches them or something, I don't quite care enough to dive into it but there probably is a performance impact, but I doubt it's a very big one.

if has_lightline then
  local ll_filled = vim.fn['lightline#colorscheme#fill'](ll)
  vim.g['lightline#colorscheme#harbour#palette'] = ll_filled
  vim.schedule(function()
      vim.fn['lightline#disable']()
      vim.fn['lightline#enable']()
  end)
end
ellisonleao commented 3 years ago

yeah, ive also tried the pcall approach but i am still getting a could not load theme X, despite of seeing the actual hightlights in the statusline.

Screenshot from 2021-03-17 11-51-44

I guess i will just stick with that approach for now. Huge thanks for the help

rktjmp commented 3 years ago

I don't get the error when I clone down gruvbox.

I set my colorscheme to gruvbox then configure lightline to gruvbox, then it all just works.