Git Product home page Git Product logo

lazyflex.nvim's Introduction

lazyflex.nvim

Update: This repository has been archived on 20240329

lazyflex.nvim is an add-on for lazy.nvim, a modern plugin manager for Neovim.

Its main objective is to make it easier to test and troubleshoot a Neovim configuration.

Demo

demo.mp4

The code used in the demo can be found here

Features

  • Easier troubleshooting/testing from one central location.
    • Enable/disable multiple plugins by keyword.
    • Define and use custom presets for your own configuration.
    • Has presets for each default plugin module in LazyVim.
    • Has options to skip loading the configuration modules (options, autocmds, keymaps) provided by LazyVim
  • Helps to verify the independence of the components in the configuration.
  • When creating an issue, facilitates writing a concise reproducible configuration.
    • Contains examples for minimal configurations using lazyflex.

Requirements

References:

Installation

The plugin must be the first item in the spec!

require("lazy").setup({
  spec = {
    {
      "abeldekat/lazyflex.nvim",
      version = "*",
      cond = true,
      import = "lazyflex.hook",
      opts = {},
    },
    -- your plugins:
    -- { "LazyVim/LazyVim", import = "lazyvim.plugins" },
    -- { import = "plugins" },
  },
})

Note: The cond property in the snippet above is practical for quickly toggling lazyflex on or off, whilst still keeping the plugin installed. Lazyflex is heavily optimized, and can also be kept enabled.

Note: It is not possible to configure multiple fragments of the plugin.

Note: See examples/lazyflex_spec.lua for a more complete lazyflex spec.

References:

Concepts

lazyflex:

  1. Returns immediately when there are no keywords or presets supplied to enable or disable
  2. Only operates on plugins that are not unconditionally disabled(plugin.enabled = false)

Important properties

  • filter_modules.kw: when enabled, only import a selection of the modules in use, thereby reducing the number of plugins to consider. See lazy.nvim's import
  • kw: a list of words matching names of plugins.
  • preset: a predefined list of words matching names of plugins
  • enable_match:
    • true(enable incrementally, default): enable all plugins that match keywords, disable the others.
    • false(disable incrementally): disable all plugins that match keywords, enable the others
  • override_kw: invert enable_match on a plugin when its name has a match in both this list of words and in kw including presets

When using presets: References to a non-existing preset will be ignored.

Colorscheme

Important: The name of the colorscheme must be in the keywords when enable_match = true

Approaches:

  1. Add the name to property kw in the opts: kw = { "toky" }
  2. Add the name to property kw_always_enable in the opts: kw_always_enable = { "toky" }
  3. When using LazyVim: Use the colorscheme preset.
  4. When using custom presets: Create a colorscheme preset.

Use cases

Using a personal configuration

The plugin can be used when the user's configuration is not build upon a community setup like LazyVim. It is possible to configure custom presets.

Using a community setup like LazyVim

Prerequisite: Add the LazyVim plugin

Reusing specs from a collection of plugins

LazyVim can be used without loading its options, autocommands or keymappings. The settings of the resulting configuration will default to stock Neovim.

Scenario's where this can be useful:

Prerequisite: Add the LazyVim plugin

Add to opts:

lazyvim = { settings = { enabled = false } }

Examples

  -- Enable harpoon, plenary and tokyonight. Disable all other plugins.
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = { kw = { "har", "plen", "tokyo" } },
  },

  -- Disable telescope and harpoon. Enable: all other plugins.
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = { enable_match = false, kw = { "tele", "har" } },
  },

  -- Only use lazy.nvim, LazyVim, and tokyonight, without LazyVim's settings.
  -- An alternative would be a lazy.nvim spec, addding tokyonight.
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      lazyvim = { settings = { enabled = false } },
      kw = { "tokyo" },
    },
  },

  -- All plugins except the ones defined in LazyVim's ui module
  -- Plugins: approximately 10 disabled
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      lazyvim = { presets = { "ui" } },
      enable_match = false,
    },
  },

  -- Enable plugins in LazyVim's editor module and plugins matching `cmp`,
  -- except nvim-spectre(editor), flash.nvim(editor) and cmp_luasnip
  -- Plugins: approximately 30 disabled
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      lazyvim = { presets = { "editor" } },
      kw = { "tokyo", "cmp" },
      override_kw = { "spectre", "fla", "luasn" },
    },
  },

  -- Enable all plugins, excluding plugins from other modules than
  -- either "lazyvim.plugins" or "plugins"
  -- To do this manually, one would need to comment out all "other" imports...
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      filter_modules = { enabled = true },
      enable_match = false,
    },
  },

  -- Enable the minimal amount of plugins needed for running neotest-python
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      filter_modules = { enabled = true, kw = { "py", "test" } },
      lazyvim = { presets = { "treesitter"} },
      kw = { "toky", "test", "plen" },
    },
  },

Configuration

lazyflex.nvim comes with the following defaults:

{
  -- when enabled: only import a selection of the modules in use
  filter_modules = {
    enabled = false,
    kw = {}, -- contains keywords for module names to import
    always_import = {}, -- always contains "lazyvim.plugins" and "plugins"
  },

  lazyvim = {
    presets = {}, -- example: { "coding" }: matches all plugins in the coding module

    settings = { -- load lazyvim's settings by default:
      enabled = true, -- quick switch. Disables the three options below:
      options = true, -- use config.options
      autocmds = true, -- use config.autocmds
      keymaps = true, -- use config.keymaps
    },
  },

  user = {
    -- optional: functions implementing presets and change_settings
    get_preset_keywords = nil,
    change_settings = nil,

    presets = {}, -- example, when implemented: { "editor" }

    settings = { -- passed into function change_settings:
      enabled = true, -- quick switch. Disables the three options below:
      options = true,
      autocmds = true,
      keymaps = true,
    },
  },

  -- either enable or disable matching plugins:
  enable_match = true,

  -- keywords matching plugins to always enable:
  kw_always_enable = {}, -- the "lazy" keyword is always included

  -- keywords matching plugins, as specified by the user:
  kw = {}, -- example: "line" matches lualine, bufferline and indent-blankline

  -- when the name of a plugin is matched and also has a match in override_kw:
  -- invert enable_match for that plugin
  override_kw = {},
}

Custom presets and settings

As an optional step, the user can add custom functions for handling presets and changing settings to the user section in opts. The following functions are used by lazyflex:

  • presets: get_preset_keywords(name, enable_match)
    • name: the name of the preset
    • enable_match: true when enabling, false otherwise
    • returns: a list with keywords or {}
  • settings: change_settings(settings)
    • settings: the settings provided in opts
    • returns: a spec(used by LazyVim) or {}

These functions can be implemented in a separate module in the user's configuration.

Suggestion: Copy the example module examples/lazyflex_collection.lua to the lua folder inside XDG_CONFIG_HOME. On linux, XDG_CONFIG_HOME defaults to ~/.config/nvim

Note: Do not use a folder lazy.nvim is configured to import from.

Example user opts:

user = {
  get_preset_keywords = require("lazyflex_collection").get_preset_keywords,
  change_settings = require("lazyflex_collection").change_settings,
  presets = {},
  settings = {},
},

Minimal reproducible configurations

There are two examples for writing reproducible configurations using lazyflex:

Lazyflex starter

When starting Neovim for the first time, lazyflex.nvim is not present yet. As a consequence, plugins will be cloned before lazyflex.nvim is activated.

It is possible to avoid cloning plugins that will be not be enabled by cloning lazyflex.nvim first.

See lazyflex starter, a modified LazyVim starter

About enabling and disabling

For each plugin managed by lazy.nvim that is not unconditionally disabled, lazyflex overrides its cond property.

The cond property needs to be set before lazy.nvim starts taking its value into consideration. Therefore, lazyflex operates in the "spec phase". As part of the "spec phase", lazy.nvim requires lazyflex.hook

See: :Lazy profile.

A similar approach can also be found in the following code:

References:

History

The idea grew over time:

Acknowledgements

  • lazy.nvim: The architecture, semantics and enhanced possibilities.
  • LazyVim: The concept of a plugin as a collection of other plugins.

lazyflex.nvim's People

Contributors

abeldekat avatar github-actions[bot] avatar dpetka2001 avatar

Stargazers

now-ing avatar Mike Funk avatar  avatar Igor Rončević avatar Shaun Clayton avatar Yusuf Aktepe avatar  avatar Valentin Degenne avatar Hsieh-Ting Lin (林協霆) avatar Mccranky83 avatar Vera Rei avatar  avatar Tyler Wardhaugh avatar Xie Zejian avatar  avatar antx avatar Hanchin Hsieh avatar  avatar Mars avatar Josh Thomas avatar Anno Knierim avatar Hans Bignon K. Tognon avatar Chris Thackrey avatar J.Hale avatar Wuelner Martínez avatar Yangyang Li avatar  avatar  avatar Samuel Otterman avatar  avatar Ivan Smirnov avatar Smith avatar  avatar vadim avatar Rishikesh Vaishnav avatar Jordan Mandel avatar Laurent Seigneurie avatar kohane27 avatar Chaz avatar Alan North avatar Naser Aleisa avatar Andy Shevchenko avatar hebe ⚸ dite avatar Kenneth House avatar tonmoymojumder avatar  avatar Bailey Bjornstad avatar Flo Wolfe avatar Yuta Katayama avatar

Watchers

 avatar  avatar

lazyflex.nvim's Issues

How do I add it to lazyvim?

Hi

The doc mentions to install with LazyVim but it does not tell where to add. Can you please clarify about where or which file to add this code ?

require("lazy").setup({
  spec = {
    {
      "abeldekat/lazyflex.nvim",
      version = "*",
      cond = true,
      import = "lazyflex.hook",
      opts = {},
    },
    -- your plugins:
    -- { "LazyVim/LazyVim", import = "lazyvim.plugins" },
    -- { import = "plugins" },
  },
})

refactor(core): breaking changes in v4

installation

Removed:

  • import = lazyflex.entry.lazyvim
  • import = lazyflex.entry.lazy

Use: import = lazyflex.hook

The plugin will detect LazyVim when the opts contain lazyvim = {}

opts

opts.filter_import

Has been renamed into opts.filter_modules

opts.user.mod

Has been removed.
Use functions to include your custom module:

user = {
  get_preset_keywords = require("flex_collection").get_preset_keywords,
  change_settings = require("flex_collection").change_settings,
  presets = { "editor" },
  settings = { enabled = false },
},

Reference: custom presets and settings

lazyflex release 2.0: announcing breaking changes

Renaming:

Import:

  1. import = "lazyflex.plugins.intercept" into import = "lazyflex.hook"

Options:

  1. enable_on_match into enable_match
  2. keywords_to_always_enable into kw_always_enable
  3. keywords into kw

Note: consider adding version = *

new installation instruction:

require("lazy").setup({
  spec = {
    {
      "abeldekat/lazyflex.nvim",
      version = "*",
      cond = true, -- enable/disable lazyflex.nvim
      import = "lazyflex.hook", --> CHANGED...
      -- opts = {},
    },
    -- your plugins:
    { "LazyVim/LazyVim", import = "lazyvim.plugins" },
    { import = "plugins" },
  },
})

Problem with lazyvim v10.0 and optional=true

in lazyvim.plugins.ui:

{
    "goolord/alpha-nvim",
    optional = true,
    enabled = function()
      require("lazyvim.util").warn({
        "`dashboard.nvim` is now the default LazyVim starter plugin.",
        "",
        "To keep using `alpha.nvim`, please enable the `lazyvim.plugins.extras.ui.alpha` extra.",
        "Or to hide this message, remove the alpha spec from your config.",
      })
      return false
    end,
},

lazyflex(v2.1.2) in attach, add: Tests the plugin for enabled = false, and the message is shown!

Also: fix enabled plugin (not enabled == false) having cond = false

feature: bail-out when user does not supply any options

Currently, without options, the plugin activates and enables only lazy.nvim, LazyVim and tokyonight.
This is not convenient.

Expected:

  { -- 1
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.plugins.intercept",
    opts = {}
  },

The plugin should return early, doing nothing. The same behavior is expected when the opts keyword is not specified.

feature: easier preset configuration for the user

The config when using personal presets:

  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.plugins.intercept",
    opts = {
      user = { mod = "config.lazyflex", presets = { "test" } },
    },
  },

The default config in the user section:

  -- user collection:
  user = {
    -- collection defined in a module in your config.
    -- The module must implement the following stub:
    mod = "lazyflex.collections.stub", -- for example: "config.lazyflex"
    presets = {}, -- example: {"test"}, where "test" provides keywords
  },

It would be easier when the user does not have to specify mod =, when that mod is implemented in the default location.

The default should be changed:
user.mod = "config.lazyflex"

New default user section:

  -- user collection:
  user = {
    -- lazyflex will first try to require the default "mod" property
    -- The module is -optional- in the user's configuration,
    -- and should implement "lazyflex.collections.stub"
    mod = "config.lazyflex",
    -- without user.mod, any user.presets specified will have no effect:
    fallback = "lazyflex.collections.stub", -- do not modify
    presets = {}, -- example when implemented: { "test" }
  },

Expected:

The user can specify:

opts = { user = { presets = { "test" } },

feat: Change the installation example. Set `cond` on lazyflex to keep the plugin around.

The problem:

The current installation example:

local use_flex = false -- true activates the plugin
local plugin_flex = not use_flex and {}
  or {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.plugins.intercept",
    opts = {
      -- 3 plugins by default: "LazyVim", "lazy.nvim", and "tokyonight"
    },
  }
require("lazy").setup({
  spec = {
    plugin_flex,
    { "LazyVim/LazyVim", import = "lazyvim.plugins" },
    { import = "plugins" },
  },
})

This is rather verbose. The objective is to only include the lazyflex plugin when use_flex is true.
Although perfectly fine in a minimal configuration, used to reproduce an issue, there should be a better solution for a personal configuration.

Not supported:

local use_flex = false
require("lazy").setup({
  spec = {
    {
      "abeldekat/lazyflex.nvim",
      cond = use_flex,
      import = "lazyflex.plugins.intercept",
      opts = {},
    },
    { "LazyVim/LazyVim", import = "lazyvim.plugins" },
    { import = "plugins" },
  },
})

The cond keyword allows a plugin to be kept installed and still be disabled.

Lazy.nvim does support the enabled keyword for this use case. When enabled=false, the plugin is not activated, and can be cleaned.
However, on cond=false, import lazyflex.plugins.intercept is still activated.

Workaround:

local use_flex = false
local import_flex = use_flex and "lazyflex.plugins.intercept" or "lazyflex.plugins.noop"
require("lazy").setup({
  spec = {
    {
      "abeldekat/lazyflex.nvim",
      cond = use_flex,
      import = import_flex,
      opts = {},
    },
    { "LazyVim/LazyVim", import = "lazyvim.plugins" },
    { import = "plugins" },
  },
})

When cond=false, lazy.nvim imports lazyflex.plugins.noop, returning immediately.

refactor(core): Split lazyflex.hook and remove the collection option

When only using lazy.nvim, the user has to additionally specify collections=false

A better approach: import lazyflex.entry.lazyvim or lazyflex.entry.lazy
The collection option will be removed from the options.

Other changes:

  • options:
    • For both LazyVim and user, the config table has been renamed into settings
  • the user preset module:
    • Function return_spec has been renamed into change_settings

Additions:

  • instead of providing a custom module, the user can now also add code for presets and changes into the opts directly. See "Custom presets and settings" in the readme.

Example lazyvim:

  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.entry.lazyvim", --> changed hook into entry.lazyvim!
    opts = {
      lazyvim = { settings = { enabled = false } }, --> changed config into settings
      kw = { "tokyo" },
    },
  },

Example lazy only:

  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.entry.lazy", --> changed hook into entry.lazy!
    -- collections = false,         --> changed, removed collections=false
    opts = { kw = { "har", "plen", "tokyo" } },
  },

feat(lazyvim): allow imports to be filtered dynamically

With lazyflex, from one central location, plugins can be enabled/disabled.
It would be nice to also have control over the imports(like lazyvim.plugins.extras).

Add to the config:

  -- when enabled: only import a selection of the modules in use
  filter_import = {
    enabled = false,
    kw = {}, -- contains keywords for module names to import
    always_import = {}, -- always contains "lazyvim.plugins" and "plugins"
  },

Add to the examples:

  -- Lazyvim: all plugins, without modules imported from "extras"
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.entry.lazyvim",
    opts = {
      kw_always_enable = { "tokyo" },
      filter_import = { enabled = true, kw = {} },
    },
  },

  -- Lazyvim: minimal set of plugins usable for neotest-python
  {
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.entry.lazyvim",
    opts = {
      filter_import = { enabled = true, kw = { "py", "test" } },
      lazyvim = { presets = { "treesitter"} }
      kw = { "toky", "test", "plen" },
    },
  },

feature: add override_kw

lazyflex would be more flexible when the enable_match setting can be inverted for selected keywords.

use case:

The user wishes to exclusively enable all plugins in the editor module from LazyVim. However, plugin nvim-spectre should be disabled.

Current approach

copy keywords from lazyflex.presets.lazyvim.editor and uncomment nvim-spectre

{
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      kw = {
        "neo-tree.nvim",
        -- "nvim-spectre",
        "telescope.nvim",
        "flash.nvim",
        "which-key.nvim",
        "gitsigns.nvim",
        "vim-illuminate",
        "mini.bufremove",
        "trouble.nvim",
        "todo-comments.nvim",
      },
    },
}  

Suggested approach

{
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      lazyvim = { presets = { "editor" } },
      override_kw = { "nvim-spectre" },
    },
 }

Consider using custom `user.config`

I'm not sure if the current implementation supports it or not, but it would be worth considering that a user with a custom configuration not based on any Neovim distro, might have a different file structure, such as:

├── lua
│   ├── autocommands.lua
│   ├── keybindings.lua
│   ├── plugins
│   │   ├── animate.lua
│   │   ├── completion.lua
│   │   └── editor.lua
│   └── settings.lua

It would be useful, for any user with a custom configuration close to the file architecture as stated above, to be able to customize his user.config with the filenames provided by his own choosing. Not sure if this needs special implementation or if we could provide just an extra example in the Adding custom presets to show how such a thing can be achieved with return_spec method.

feature: Simplify installation instructions

Depends on PR:
lazy.nvim: feat: on conditional disable, do not import additional plugin modules

See also:
feat: Change the installation example. Set cond on lazyflex to keep the plugin around

Old

local cond_flex = true -- enable lazyflex.nvim
local import_flex = cond_flex and "lazyflex.plugins.intercept" or "lazyflex.plugins.noop"
require("lazy").setup({
  spec = {
    {
      "abeldekat/lazyflex.nvim",
      cond = cond_flex,
      import = import_flex,
      -- opts = {},
    },
    { "LazyVim/LazyVim", import = "lazyvim.plugins" },
    { import = "plugins" },
  },
})

New

require("lazy").setup({
  spec = {
    {
      "abeldekat/lazyflex.nvim",
      cond = true, -- enable lazyflex.nvim
      import = "lazyflex.plugins.intercept",
      -- opts = {},
    },
    { "LazyVim/LazyVim", import = "lazyvim.plugins" },
    { import = "plugins" },
  },
})

feature: remove the target_property feature

Remove the target_property from the options. Always use cond.

First problem

{target_property = "enabled", enable_match = false, kw = {"noice"}}
Result:
The plugins the user has marked with enabled = false are also enabled!

{target_property = "enabled", enable_match = true, lazyvim = { presets = {"ui"} } }
Result:
The "ui" plugins the user has marked with enabled = false are also enabled!

Second problem:

From the readme:

Note: It is also possible to attach to the enabled property instead, allowing plugins to be cleaned. (See target_property)

This is not necessary. Remove the directory containing the plugins. With lazyflex enabled, restart nvim. Only the plugins matching the keywords are installed.

Concluding

Implementing the property correctly is too complex and offers no benefits.

fix(core): error in lazyflex when opts is a function

    lazyflex = {
      "abeldekat/lazyflex.nvim",
      cond = true,
      import = "lazyflex.hook",
      opts = function()
        local enable_match = false
        local kw = {}

        if not enable_match then
          table.insert(kw, require("misc.colorscheme").color)
        end
        return {
          enable_match = enable_match,
          lazyvim = { presets = { "coding" } },
          user = { presets = { "coding" } },
          kw = kw,
        }
      end,
    },

fix: lazyflex should skip plugins having enabled = false

The intention of the plugin should be to only operate on active plugins in the configuration. Plugins the user disabled should be skipped.

Example

Prerequisite:

A config using LazyVim, where mini.comment is disabled and replaced with another plugin.
On startup, without lazyflex, mini.comment is shown in the UI, in section disabled. The plugin can be cleaned

Enable

{
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      lazyvim = { presets = { "coding" } },
      kw = { "toky" },
    },
}

{ "LazyVim/LazyVim", import = "lazyvim.plugins" },
{ "echasnovski/mini.comment", enabled = false }

Result, correct: mini.comment is still shown in section disabled and can be cleaned

Lazyflex attached cond=true to mini.comment. When cond = true, the enabled property determines if the plugin is enabled or disabled.

Disable

{
    "abeldekat/lazyflex.nvim",
    import = "lazyflex.hook",
    opts = {
      enable_match = false,
      lazyvim = { presets = { "coding" } },
    },
}

{ "LazyVim/LazyVim", import = "lazyvim.plugins" },
{ "echasnovski/mini.comment", enabled = false }

Result, wrong: mini.comment is shown in section disabled as conditionally disabled and can not be cleaned

This happens because lazyflex attaches cond=false to all plugins that match, including mini.comment. The plugin is moved to the internal list of disabled plugins, and the enabled property is not consulted anymore.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.