Skip to content
3 changes: 2 additions & 1 deletion lua/orgmode/capture/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,8 @@ function Capture:_refile_from_capture_buffer(opts)
local destination_headline = opts.destination_headline

if destination_headline then
target_line = destination_headline:get_range().end_line
-- target_line = destination_headline:get_range().end_line
target_line = destination_headline:get_range().start_line
end

if opts.template.datetree then
Expand Down
4 changes: 4 additions & 0 deletions lua/orgmode/config/init.lua
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ function Config:setup_ts_predicates()
local capture_id = pred[2]
local section_node = match[capture_id]
section_node = section_node and section_node[#section_node]

if not capture_id or not section_node or section_node:type() ~= 'section' then
return
end
Expand All @@ -386,6 +387,9 @@ function Config:setup_ts_predicates()
local empty_lines = 0
while end_row > start_row do
local line = vim.api.nvim_buf_get_lines(bufnr, end_row - 1, end_row, false)[1]
if not line then
return
end
if vim.trim(line) ~= '' then
break
end
Expand Down
157 changes: 146 additions & 11 deletions lua/orgmode/ui/virtual_indent.lua
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
local tree_utils = require('orgmode.utils.treesitter')
local utils = require('orgmode.utils')
---@class OrgVirtualIndent
---@field private _ns_id number extmarks namespace id
---@field private _bufnr integer Buffer VirtualIndent is attached to
Expand Down Expand Up @@ -85,10 +86,114 @@ function VirtualIndent:_get_indent_size(line, tree_has_errors)
return level + 1
end
end

return 0
end

---@param line_str string Lua-string representing the current line.
---@param indent number Current length of indentation.
---@param wrap_col number width of writable space in buffer, from utils.winwidth
function VirtualIndent:_set_wrappoints_of_luastring(line_str, indent, wrap_col, conf_wrapwidth)
local temp_wrap_arr = {}
temp_wrap_arr[1] = {
pos = 0,
spaces = indent,
}

local i = 2
local wrap_pos = 0
local last_break = 0
local break_before_word = 0
local idx = 1
local ext_pos = 0
local nr_spaces = indent
local cumsum_virt_cols = indent
local prefered_wrapwidth = conf_wrapwidth

if wrap_col < prefered_wrapwidth then
prefered_wrapwidth = wrap_col
end

local wrap_widthdiff = wrap_col - prefered_wrapwidth

while idx < #line_str do
local char_start = vim.str_utf_start(line_str, idx)
local char_end = vim.str_utf_end(line_str, idx)
local charclass = vim.fn.charclass(line_str:sub(idx + char_start, idx + char_end))
local char_is_tab = (line_str:byte(idx) == 9)

if vim.str_utf_end(line_str, idx) == 0 then
wrap_pos = wrap_pos + 1

if char_is_tab then
local shiftw = vim.fn.shiftwidth()
local tab_shift = (shiftw - (((idx + cumsum_virt_cols) - 1) % shiftw)) - 1

cumsum_virt_cols = cumsum_virt_cols + tab_shift

wrap_pos = wrap_pos + tab_shift
if wrap_pos > prefered_wrapwidth then
nr_spaces = tab_shift
end
end
end

if wrap_pos >= prefered_wrapwidth then
local cut_len = vim.api.nvim_strwidth(line_str:sub(break_before_word, idx))

if cut_len >= prefered_wrapwidth then
ext_pos = idx
nr_spaces = indent
-- Tabs are a big problem. This works as long as the tabs
-- are not on the breakpoint
elseif char_is_tab then
local overflow = wrap_pos - prefered_wrapwidth
ext_pos = break_before_word
nr_spaces = cut_len + nr_spaces + indent - overflow
cumsum_virt_cols = cumsum_virt_cols - nr_spaces
else
ext_pos = break_before_word
nr_spaces = indent + cut_len - 1
idx = break_before_word
end

temp_wrap_arr[i] = {
pos = ext_pos,
spaces = nr_spaces + wrap_widthdiff,
}
cumsum_virt_cols = cumsum_virt_cols + nr_spaces
i = i + 1
wrap_pos = 0
end

if charclass < 2 then
last_break = idx
end
if charclass > 1 then
break_before_word = last_break
end
idx = idx + 1
end
return temp_wrap_arr
end

function VirtualIndent:_indent_and_break_longlines(start_line, line, win_width, org_lines, indent, conf_wrapwidth)
local wrap_col = win_width - indent
local arr_index = (line - start_line) + 1

if org_lines[arr_index] then
local wrap_arr = self:_set_wrappoints_of_luastring(org_lines[arr_index], indent, wrap_col, conf_wrapwidth)

for _, wrap_point in ipairs(wrap_arr) do
pcall(vim.api.nvim_buf_set_extmark, self._bufnr, self._ns_id, line, wrap_point.pos, {
virt_text = { { string.rep(' ', wrap_point.spaces), 'OrgIndent' } },
virt_text_pos = 'inline',
right_gravity = false,
priority = 110,
})
end
end
end

---@param start_line number start line number to set the indentation, 0-based inclusive
---@param end_line number end line number to set the indentation, 0-based inclusive
---@param ignore_ts? boolean whether or not to skip the treesitter start & end lookup
Expand All @@ -113,17 +218,34 @@ function VirtualIndent:set_indent(start_line, end_line, ignore_ts)
end

self:_delete_old_extmarks(start_line, end_line)

-- Put this in as preparation for making it an config option.
-- wrapwidth adds virtual linebreaks to make paragraphs prettier.
local indent_longlines = true
local conf_wrapwidth = 70
local org_lines = {}
local win_width = 0
if indent_longlines then
org_lines = vim.api.nvim_buf_get_lines(0, start_line, end_line, false)
win_width = utils.winwidth(0)
end

for line = start_line, end_line do
local indent = self:_get_indent_size(line, tree_has_errors)

if indent > 0 then
-- NOTE: `ephemeral = true` is not implemented for `inline` virt_text_pos :(
pcall(vim.api.nvim_buf_set_extmark, self._bufnr, self._ns_id, line, 0, {
virt_text = { { string.rep(' ', indent), 'OrgIndent' } },
virt_text_pos = 'inline',
right_gravity = false,
priority = 110,
})
if indent_longlines then
self:_indent_and_break_longlines(start_line, line, win_width, org_lines, indent, conf_wrapwidth)
else
-- old behavior, no indent of longlines.
pcall(vim.api.nvim_buf_set_extmark, self._bufnr, self._ns_id, line, 0, {
virt_text = { { string.rep(' ', indent), 'OrgIndent' } },
virt_text_pos = 'inline',
right_gravity = false,
priority = 110,
})
end
end
end
end
Expand All @@ -133,20 +255,33 @@ function VirtualIndent:attach()
if self._attached then
return
end
self:set_indent(0, vim.api.nvim_buf_line_count(self._bufnr) - 1, true)
self:set_indent(0, vim.api.nvim_buf_line_count(self._bufnr))

-- vim.api.nvim_create_autocmd({ 'TextChangedI', 'TextChanged' }, {
-- buffer = self._bufnr,
-- callback = function()
-- self:set_indent(0, vim.api.nvim_buf_line_count(self._bufnr))
-- end,
-- })

vim.api.nvim_buf_attach(self._bufnr, false, {
on_lines = function(_, _, _, start_line, _, end_line)
if not self._attached then
return true
end

vim.schedule(function()
local indent_longlines = true
if indent_longlines then
indent_longlines = true
self:set_indent(start_line, end_line)
end)
else
vim.schedule(function()
self:set_indent(start_line, end_line)
end)
end
end,
on_reload = function()
self:set_indent(0, vim.api.nvim_buf_line_count(self._bufnr) - 1, true)
self:set_indent(0, vim.api.nvim_buf_line_count(self._bufnr))
end,
on_detach = function(_, bufnr)
self:detach()
Expand Down
12 changes: 12 additions & 0 deletions test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
a = 'aäi'

for i = 1, #a, 1 do
local char_start = vim.str_utf_start(a, i)
local char_end = vim.str_utf_end(a, i)
local charclass = vim.fn.charclass(a:sub(i + char_start, i + char_end))

print('----')
print(vim.fn.charclass(a:sub(i)))
print(charclass)
print('----')
end