diff --git a/lua/orgmode/capture/init.lua b/lua/orgmode/capture/init.lua index 9f89c837d..9d111348d 100644 --- a/lua/orgmode/capture/init.lua +++ b/lua/orgmode/capture/init.lua @@ -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 diff --git a/lua/orgmode/config/init.lua b/lua/orgmode/config/init.lua index 81f117d9b..47244bd4d 100644 --- a/lua/orgmode/config/init.lua +++ b/lua/orgmode/config/init.lua @@ -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 @@ -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 diff --git a/lua/orgmode/ui/virtual_indent.lua b/lua/orgmode/ui/virtual_indent.lua index c1b29988e..a91012351 100644 --- a/lua/orgmode/ui/virtual_indent.lua +++ b/lua/orgmode/ui/virtual_indent.lua @@ -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 @@ -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 @@ -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 @@ -133,7 +255,14 @@ 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) @@ -141,12 +270,18 @@ function VirtualIndent:attach() 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() diff --git a/test.lua b/test.lua new file mode 100644 index 000000000..bc32dc0d2 --- /dev/null +++ b/test.lua @@ -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