nvim-orgmode / orgmode

Orgmode clone written in Lua for Neovim 0.9+.

Home Page:https://nvim-orgmode.github.io/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Set Default Fold Level

orhnk opened this issue · comments

Describe the bug

Initial setup folds all headers in an org file on startup.

You can disable this by:

vim.cmd [[setlocal nofoldenable]] -- No folds initially

With this in you config the headers don't fold anymore but folding works incorrect!

tmp-2023-09-24_08.42.30.mp4

As you can see:

  • The file has no folds initially
  • When I try to fold a header whole file collapses
  • So I refold each header.

Steps to reproduce

  1. define setlocal nofoldenable in your config
  2. open a org file
  3. try to fold any header

Expected behavior

Wanted to see the merge-lang header folded only. (in the video)

Emacs functionality

Emacs works as it should

Minimal init.lua

vim.cmd [[setlocal nofoldenable]] -- No folds initially

Screenshots and recordings

No response

OS / Distro

archlinux

Neovim version/commit

NVIM v0.10.0-dev-bb38c06 Build type: RelWithDebInfo LuaJIT 2.1.1693350652

Additional context

No response

@UTFeight does your folds work correctly when you do not do setlocal nofoldenable?

Here is how they look without setlocal nofoldenable:

tmp-2023-09-24_11.44.51.mp4

They do work how they should be now.

But there is no option to set as fold_depth which I want to see like the following:

Note
This is set foldlevel=99
And works exactly how I want it to work.
But... (look at the next video)

tmp-2023-09-24_11.47.12.mp4

Note
Using set foldlevel=99 loaded after the config,
seems to not work?

tmp-2023-09-24_11.51.15.mp4

for some nice blue switch ASMR, you can unmute videos :D

I have found a solution for this!

adding options after lazy loading the plugin didn't work well so this was happening.

Solution

  • After orgmode is (lazy) loaded (for lazy.nvim It's in config fn):

    -- vim.cmd [[setlocal foldlevel=99]] -- WARNING: DIDN'T WORK!!!
    vim.cmd [[setlocal nofoldenable]] -- No folds initially
    -- ABOVE SNIPPET IS RAN RIGHT AFTER THE PLUGIN IS LOADED

Note
Using foldlevel=99 in lazy's config fn didn't work.
I wonder why?

  • init.lua
    vim.cmd [[autocmd FileType org setlocal foldlevel=99]]

Autocmd Version:

-- (Put that in init.lua if you don't have an option like mine)
vim.cmd [[autocmd FileType org setlocal nofoldenable]] -- No folds initially
vim.cmd [[autocmd FileType org setlocal foldlevel=99]]

Without UFO I can reproduce this issue, I have attached a minimal init.

Here's a minimal init that reproduces the folding behavior
local M = {}

---@class MinPlugin A plugin to download and register on the package path

---@alias PluginName string The plugin name, will be used as part of the git clone destination
---@alias PluginUrl string The git url at which a plugin is located, can be a path. See https://git-scm.com/book/en/v2/Git-on-the-Server-The-Protocols for details
---@alias MinPlugins table<PluginName, PluginUrl>

local base_root_path = vim.fn.fnamemodify(debug.getinfo(1, "S").source:sub(2), ":p:h") .. "/.min"
---Gets the root directory of the minimal init and if path is specified appends the given path to the root allowing for
---subdirectories within the current cwd
---@param path string? The additional path to append to the root, not required
---@return string root The root path suffixed with the path provided or an empty suffix if none was given
function M.root(path)
  return base_root_path .. "/" .. (path or "")
end

---Downloads a plugin from a given url and overwrites the 'packpath' to the new plugin package path
---@param plugin_name PluginName
---@param plugin_url PluginUrl
function M.load_plugin(plugin_name, plugin_url)
  local package_root = M.root("plugins/")
  local install_destination = package_root .. plugin_name
  vim.opt.runtimepath:append(install_destination)

  if not vim.loop.fs_stat(package_root) then
    vim.fn.mkdir(package_root, "p")
  end

  -- If the plugin install path already exists, we don't need to clone it again.
  if not vim.loop.fs_stat(install_destination) then
    print(string.format("> Downloading plugin '%s' to '%s'", plugin_name, install_destination))
    vim.fn.system({
      "git",
      "clone",
      "--depth=1",
      plugin_url,
      install_destination,
    })
    if vim.v.shell_error > 0 then
      error(string.format("> Failed to clone plugin: '%s' in '%s'!", plugin_name, install_destination),
        vim.log.levels.ERROR)
    end
  end
end

---@class MinSetupConfig
---@field root? string The base path to use for all data storage, if nil the root will be set to this module's directory
---@field plugins? MinPlugins The plugins to install and register on the runtimepath

---Do the initial setup. Downloads plugins, ensures the minimal init does not pollute the filesystem by keeping
---everything self contained to the CWD of the minimal init file. Run prior to running tests, reproducing issues, etc.
---READ: It is on the user to setup the plugins, this only registers them to the runtime path, it does NOT set them up!
---@param config? MinSetupConfig
function M.setup(config)
  vim.opt.packpath = {}                      -- Empty the package path so we use only the plugins specified
  vim.opt.runtimepath:append(M.root(".min")) -- Ensure the runtime detects the root min dir

  if config ~= nil then
    if config.root ~= nil then
      base_root_path = config.root
    end

    -- Install required plugins
    if config.plugins ~= nil then
      for plugin_name, plugin_url in pairs(config.plugins) do
        M.load_plugin(plugin_name, plugin_url)
      end
    end
  end

  vim.env.XDG_CONFIG_HOME = M.root("xdg/config")
  vim.env.XDG_DATA_HOME = M.root("xdg/data")
  vim.env.XDG_STATE_HOME = M.root("xdg/state")
  vim.env.XDG_CACHE_HOME = M.root("xdg/cache")

  -- NOTE: Cleanup the xdg cache on exit so new runs of the minimal init doesn't share any previous state, e.g. shada
  vim.api.nvim_create_autocmd("VimLeave", {
    callback = function()
      vim.fn.system({
        "rm",
        "-r",
        "-f",
        M.root("xdg")
      })
    end
  })
end

-- NOTE: If you have additional plugins you need to install to reproduce your issue, include them in the plugins
-- table within the setup call below.
M.setup({
  plugins = {
    treesitter = "https://github.com/nvim-treesitter/nvim-treesitter",
    orgmode = "https://github.com/nvim-orgmode/orgmode",
  }
})
-- WARN: Do all plugin setup, test runs, reproductions, etc. AFTER calling setup with a list of plugins!
-- Basically, do all that stuff AFTER this line.
-- Load treesitter grammar for org
require("orgmode").setup_ts_grammar()

-- Setup treesitter
vim.opt.foldexpr = "nvim_treesitter#foldexpr()"
require("nvim-treesitter.configs").setup({
  highlight = {
    enable = true,
    additional_vim_regex_highlighting = { "org" },
  },
  ensure_installed = { "org" },
  sync_install = true
})

-- Setup orgmode
vim.opt.foldlevel = 99
require("orgmode").setup({})
vim.cmd.edit("tester.org")
vim.api.nvim_buf_set_lines(0, 0, -1, false, {
  "* Heading uno",
  "",
  "** Inner Heading One",
  "",
  "** Inner Heading Two",
  "",
  "* Another toplevel heading",
  "",
})
vim.cmd.write()
-- Have to make treesitter happy so we get correct folds. So we have to write after writing the content, TS wasn't
-- ready.
local timer = vim.loop.new_timer()
timer:start(100, 0, vim.schedule_wrap(function()
  vim.cmd.edit()
  print("Notice that some of the content is already folded when opened... this shouldn't be the case.")
  timer:close()
end))

Just copy and paste that big 'ol beast and run it with nvim -nu MINIMAL_INIT_FILE_NAME.lua.
It'll show the issue.

Orgmode is overriding the foldlevel to 0 and doesn't respect the foldlevel a user may have set. It does that
here.

Ideally Orgmode wouldn't mess with the foldlevel as it is 0 by default and if a user changed that it really should
respect the custom foldlevel.

I think I have fixed the issues with UFO as well by commenting out the fillchars line as well in the ftplugin file,
link to that.

When both the foldlevel and fillchars lines are removed from the ftplugin/org.vim file, UFO and my foldlevel
settings work as expected.

@kristijanhusak Is there a particular reason Orgmode ignores user configuration of fillchars & foldlevel? If so, I'll
see if I can find an alternative work around.

Orgmode is overriding the foldlevel to 0 and doesn't respect the foldlevel a user may have set. It does that
here.

@treatybreaker That looks like a PR!

@treatybreaker I understand why foldlevel might be an issue, but I'm not sure why would fillchars cause an issue?
We could introduce org-startup-folded as described here https://orgmode.org/manual/Initial-visibility.html and have it's value as overview by default. This value would set foldlevel to 0 as it does now. This is just to keep everything backward compatible. Valid options would be overview (foldlevel=0), content (foldlevel=1), showeverything (foldlevel=99) and nil (no foldlevel set). Then we should read this configuration and act accordingly.

@kristijanhusak

So the fillchars may be more relevant to #599, as the fold fillchars interfere with nvim-ufo's fold chars.

In terms of foldlevel, would you accept a PR with that variable org-startup-folded? I'd be more than happy to send it
your way.

Arguably though we don't even need options for that as Neovim has buffer options which a user could set themselves or just the filetype plugin in their after directory to set the foldlevel. It's basically built-in. The best way to handle this may just be to just not set the foldlevel at all and mention it in the docs.

In terms of foldlevel, would you accept a PR with that variable org-startup-folded? I'd be more than happy to send it
your way.

Yes, please send it over. I'd like to streamline it through configuration.