nvim-orgmode / nvim-orgmode/orgmode

Capture failure due to newline parsing in files with CRLF line endings

Open Beginner friendly
#1,174 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Lua
Stars
3.9k
Forks
190
Avg merge
2d 13h
Merged PRs (30d)
7

Description

Describe the bug

nvim-orgmode capture finalization for custom datetree target fails with Index out of bounds when destination .org file has CRLF line endings and has not yet been opened in the current Neovim session. This can be considered a Windows issue, but is more specifically a consequence of file formats that use CRLF line endings.

Here is the traceback:

E5108: Lua: ...l/lazyvim-data/lazy/orgmode/lua/orgmode/capture/init.lua:369: Index out of bounds
stack traceback:
[C]: in function 'nvim_buf_get_lines'
...l/lazyvim-data/lazy/orgmode/lua/orgmode/capture/init.lua:369: in function 'is_line_empty'
...l/lazyvim-data/lazy/orgmode/lua/orgmode/capture/init.lua:374: in function '_get_destination_range_without_empty_lines'
...l/lazyvim-data/lazy/orgmode/lua/orgmode/capture/init.lua:202: in function 'action'
...cal/lazyvim-data/lazy/orgmode/lua/orgmode/files/file.lua:152: in function 'update'
...cal/lazyvim-data/lazy/orgmode/lua/orgmode/files/file.lua:162: in function 'update_sync'
...l/lazyvim-data/lazy/orgmode/lua/orgmode/capture/init.lua:194: in function '_refile_from_capture_buffer'
...l/lazyvim-data/lazy/orgmode/lua/orgmode/capture/init.lua:130: in function <...l/lazyvim-data/lazy/orgmode/lua/orgmode/capture/init.lua:123>

I also suspect the same same bug may affect agenda navigation. I have found that selecting an item from the agenda sometimes jumps to the bottom of the target buffer instead of the correct headline, suggesting that the target_line may be greater than the total number of lines in the file.

checkhealth

Orgmode ~

  • ✅ OK Treesitter grammar installed (version 2.0.4)
  • ✅ OK Setup called
  • ✅ OK org_agenda_files configured
  • ✅ OK org_default_notes_file configured
  • ⚠️ WARNING shellslash is not set. This might cause issues with file paths in links. Set vim.opt.shellslash = true in your configuration.
Steps to reproduce

I reproduced this with a minimal config using only lazy.nvim + nvim-orgmode. Within the minimal config I have defined the following custom template

Steps:

  1. Start Neovim with the minimal repro config.
  2. Trigger capture. Org capture
  3. Press m to select the custom-defined capture template
  4. Finalize with C-c C-c.
Expected behavior

The capture should be inserted into the correct datetree location in the path meetings_file

Emacs functionality

No response

Minimal init.lua

local tmp_dir = vim.env.TMPDIR or vim.env.TMP or vim.env.TEMP or "/tmp"
local nvim_root = tmp_dir .. "/nvim_orgmode"
local lazy_root = nvim_root .. "/lazy"
local lazypath = lazy_root .. "/lazy.nvim"

for _, name in ipairs({ "config", "data", "state", "cache" }) do
	vim.env[("XDG_%s_HOME"):format(name:upper())] = nvim_root .. "/" .. name
end

-- Install lazy.nvim if not already installed
if not vim.uv.fs_stat(lazypath) then
	vim.fn.system({
		"git",
		"clone",
		"--filter=blob:none",
		"https://github.com/folke/lazy.nvim.git",
		"--branch=stable", -- latest stable release
		lazypath,
	})
end
vim.opt.rtp:prepend(lazypath)

local repro_dir = vim.fn.stdpath("state") .. "/org_capture_repro"
local meetings_file = repro_dir .. "/meetings.org"
vim.fn.mkdir(repro_dir, "p")

require("lazy").setup({
	{
		"nvim-orgmode/orgmode",
		event = "VeryLazy",
		ft = { "org" },
		config = function()
			require("orgmode").setup({
				org_agenda_files = { repro_dir .. "/*.org" },
				org_default_notes_file = meetings_file,
				org_capture_templates = {
					m = {
						description = "Work Meeting Note",
						template = "*** %? Meeting at %<%H:%M>\t:WORK:MEETING:\nCreated %U",
						target = meetings_file,
						datetree = {
							tree_type = "custom",
							tree = {
								{
									format = "%Y-%m",
									pattern = "^(%d%d%d%d)%-(%d%d)$",
									order = { 1 },
								},
								{
									format = "%Y-%m-%d %A",
									pattern = "^(%d%d%d%d)%-(%d%d)%-(%d%d) (Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)$",
									order = { 1, 2 },
								},
							},
						},
					},
				},
			})
		end,
	},
}, {
	root = lazy_root,
	lockfile = nvim_root .. "/lazy.json",
	install = {
		missing = false,
	},
})

require("lazy").sync({
	wait = true,
	show = false,
})


Screenshots and recordings

No response

nvim-orgmode version

f32a06d

OS / Distro

Windows 11

Neovim version/commit

v0.12.4

Additional context

The problem appears to be coming from

destination_headline, target_line = Datetree:new({ files = self.files }):create(opts.template)

I traced it back to the OrgFile:load functions and finally to the root cause: utils.readfile found in utils/init.lua (link below). The issue seems to be the consequence of how this functions splits the file data into lines:

https://github.com/nvim-orgmode/orgmode/blob/f32a06d4b5399dad8bf0478c772bf6c513f02dcf/lua/orgmode/utils/init.lua#L34

The pattern in the sep argument means that \r and \n get split separately instead of together, so CRLF (\r\n) gets treated as two separators instead of one line ending. This appears to double line counts in unopened CRLF files (e.g. fileformat=dos) and then propagates to headling locations/ranges and ultimately to the target line for the capture.

A safer approach that solves this problem could be to be normalize line endings first, and then split on plain \n, for example:

local normalized = data:gsub('\r\n', '\n'):gsub('\r', '\n')
local lines = vim.split(normalized, '\n', { plain = true })
Potential workaround for other users experiencing this issue

Convert all org files to unix file format.

For example in neovim, open up an org file. Then run the command :set ff=unix. Finally save the file.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in lua/orgmode/utils/init.lua around the readfile implementation linked in the issue, then trace how OrgFile:load uses its returned lines. Reproduce with a CRLF-formatted .org file and the provided capture template. Done means unopened CRLF files produce correct line counts and capture inserts at the expected datetree location without an index error.

Written by the indexing model from the issue text.

Assessment

Tech stack
lua
Domain
tooling
Issue type
Bug
Difficulty
2/5
Estimated time
1-3 hours
Activity status
Quiet
Clarity
Clearly specified
Newbie friendliness
72/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.