diff --git a/lua/word/config.lua b/lua/word/config.lua index c49931a..4a2805c 100644 --- a/lua/word/config.lua +++ b/lua/word/config.lua @@ -12,7 +12,7 @@ local f = vim.fn --- @field markdown boolean --- @class (exact) word.configuration.user ---- @field hook? fun(manual: boolean, arguments?: string) A user-defined function that is invoked whenever word starts up. May be used to e.g. set custom keybindings. +--- @field hook? fun(manual: boolean, args?: string) A user-defined function that is invoked whenever word starts up. May be used to e.g. set custom keybindings. --- @field lazy? boolean Whether to defer loading the word base until after the user has entered a `.word` file. --- @field logger? word.log.configuration A configuration table for the logger. diff --git a/lua/word/mod/base/init.lua-E b/lua/word/mod/base/init.lua-E deleted file mode 100644 index aba6308..0000000 --- a/lua/word/mod/base/init.lua-E +++ /dev/null @@ -1,42 +0,0 @@ -local mod = require("word.mod") - -return mod.create_meta( - "treesitter", - -- "encrypt", - -- "agenda", - -- "job", - -- "tag", - "base", - "note", - -- "maps", - "cmd", - "vault", - -- "treesitter", - "store", - "ui", - -- "code", - -- "export", - -- "icon", - -- "preview", - -- "pick", - -- "icon", - -- "lsp", - -- 'completion', - -- 'data', - -- "resources", - -- "metadata", - -- "capture", - -- "template", - -- "track", - -- "media", - -- "snippets", - -- "time", - -- "run", - -- "sync", - -- "search", - -- "publish", - -- "link" - "todo", - "ui", - "ui.calendar" -) diff --git a/lua/word/mod/base/init.lua-E-E b/lua/word/mod/base/init.lua-E-E deleted file mode 100644 index 08c1f2a..0000000 --- a/lua/word/mod/base/init.lua-E-E +++ /dev/null @@ -1,42 +0,0 @@ -local mod = require("word.mod") - -return mod.create_meta( - "treesitter", - -- "encrypt", - -- "agenda", - -- "job", - -- "tag", - "base", - "note", - -- "maps", - "cmd", - "vault", - -- "treesitter", - "store", - "ui", - -- "code", - -- "export", - -- "icon", - -- "preview", - -- "pick", - -- "icon", - -- "lsp", - -- 'completion', - -- 'data', - -- "resources", - -- "metadata", - -- "capture", - -- "template", - -- "track", - -- "media", - -- "snippets", - -- "time", - -- "run", - -- "sync", - -- "search", - -- "publish", - -- "link" - "todo", - "ui", - "calendar" -) diff --git a/lua/word/mod/calendar/init.lua-E b/lua/word/mod/calendar/init.lua-E deleted file mode 100644 index 8d0d1fb..0000000 --- a/lua/word/mod/calendar/init.lua-E +++ /dev/null @@ -1,196 +0,0 @@ ---[[ - file: Calendar - title: Frictionless Dates - description: The calendar init provides a range of functionality for different date-related tasks. - summary: Opens an interactive calendar for date-related tasks. ---- -The calendar is most often invoked with the intent of selecting a date, but may -also be launched in standalone mode, select date range mode and others. - -To view keys and help, press `?` in the calendar view. ---]] - -local word = require("word") -local mod = word.mod - -local init = mod.create("calendar") - -init.setup = function() - return { - requires = { - "ui", - "calendar.views.monthly", - }, - } -end - -init.private = { - - modes = {}, - views = {}, - - get_mode = function(name, callback) - if init.private.modes[name] ~= nil then - local cur_mode = init.private.modes[name](callback) - cur_mode.name = name - return cur_mode - end - - print("Error: mode not set or not available") - end, - - get_view = function(name) - if init.private.views[name] ~= nil then - return init.private.views[name] - end - - print("Error: view not set or not available") - end, - - extract_ui_info = function(buffer, window) - local width = vim.api.nvim_win_get_width(window) - local height = vim.api.nvim_win_get_height(window) - - local half_width = math.floor(width / 2) - local half_height = math.floor(height / 2) - - return { - window = window, - buffer = buffer, - width = width, - height = height, - half_width = half_width, - half_height = half_height, - } - end, - - open_window = function(options) - local MIN_HEIGHT = 14 - - local buffer, window = init.required["ui"].create_split( - "calendar-" .. tostring(os.clock()):gsub("%.", "-"), - {}, - options.height or MIN_HEIGHT + (options.padding or 0) - ) - - vim.api.nvim_create_autocmd({ "WinClosed", "BufDelete" }, { - buffer = buffer, - - callback = function() - pcall(vim.api.nvim_win_close, window, true) - pcall(vim.api.nvim_buf_delete, buffer, { force = true }) - end, - }) - - return buffer, window - end, -} - ----@class base.calendar -init.public = { - add_mode = function(name, factory) - init.private.modes[name] = factory - end, - - add_view = function(name, details) - init.private.views[name] = details - end, - - create_calendar = function(buffer, window, options) - local callback_and_close = function(result) - if options.callback ~= nil then - options.callback(result) - end - - pcall(vim.api.nvim_win_close, window, true) - pcall(vim.api.nvim_buf_delete, buffer, { force = true }) - end - - local mode = init.private.get_mode(options.mode, callback_and_close) - if mode == nil then - return - end - - local ui_info = init.private.extract_ui_info(buffer, window) - - local view = init.private.get_view(options.view or "monthly") - - view.setup(ui_info, mode, options.date or os.date("*t"), options) - end, - - open = function(options) - local buffer, window = init.private.open_window(options) - - options.mode = "standalone" - - return init.public.create_calendar(buffer, window, options) - end, - - select_date = function(options) - local buffer, window = init.private.open_window(options) - - options.mode = "select_date" - - return init.public.create_calendar(buffer, window, options) - end, - - select_date_range = function(options) - local buffer, window = init.private.open_window(options) - - options.mode = "select_range" - - return init.public.create_calendar(buffer, window, options) - end, -} - -init.load = function() - -- Add base calendar modes - init.public.add_mode("standalone", function(_) - return {} - end) - - init.public.add_mode("select_date", function(callback) - return { - on_select = function(_, date) - if callback then - callback(date) - end - return false - end, - } - end) - - init.public.add_mode("select_range", function(callback) - return { - range_start = nil, - range_end = nil, - - on_select = function(self, date) - if not self.range_start then - self.range_start = date - return true - else - if os.time(date) <= os.time(self.range_start) then - print("Error: you should choose a date that is after the starting day.") - return false - end - - self.range_end = date - callback({ self.range_start, self.range_end }) - return false - end - end, - - get_day_highlight = function(self, date, base_highlight) - if self.range_start ~= nil then - if os.time(date) < os.time(self.range_start) then - return "@comment" - end - end - return base_highlight - end, - } - end) -end - -return init diff --git a/lua/word/mod/calendar/views/monthly/init.lua-E b/lua/word/mod/calendar/views/monthly/init.lua-E deleted file mode 100644 index 04c4210..0000000 --- a/lua/word/mod/calendar/views/monthly/init.lua-E +++ /dev/null @@ -1,1162 +0,0 @@ -local word = require("word") -local lib, log, mod, utils = word.lib, word.log, word.mod, word.utils - -local init = mod.create("calendar.views.monthly") - -local function reformat_time(date) - return os.date("*t", os.time(date)) -end - -init.setup = function() - return { - requires = { - "ui.calendar", - "time", - }, - } -end - -init.private = { - namespaces = { - logical = vim.api.nvim_create_namespace("word/calendar/logical"), - decorational = vim.api.nvim_create_namespace("word/calendar/decorational"), - }, - - set_extmark = function(ui_info, namespace, row, col, length, virt_text, alignment, extra) - if alignment then - local text_length = 0 - - for _, tuple in ipairs(virt_text) do - text_length = text_length + tuple[1]:len() - end - - if alignment == "center" then - col = col + (ui_info.half_width - math.floor(text_length / 2)) - elseif alignment == "right" then - col = col + (ui_info.width - text_length) - end - end - - local base_extra = { - virt_text = virt_text, - virt_text_pos = "overlay", - } - - if length then - base_extra.end_col = col + length - end - - return vim.api.nvim_buf_set_extmark( - ui_info.buffer, - namespace, - row, - col, - vim.tbl_deep_extend("force", base_extra, extra or {}) - ) - end, - - set_decorational_extmark = function(ui_info, row, col, length, virt_text, alignment, extra) - return init.private.set_extmark( - ui_info, - init.private.namespaces.decorational, - row, - col, - length, - virt_text, - alignment, - extra - ) - end, - - set_logical_extmark = function(ui_info, row, col, virt_text, alignment, extra) - return init.private.set_extmark( - ui_info, - init.private.namespaces.logical, - row, - col, - nil, - virt_text, - alignment, - extra - ) - end, - - new_view_instance = function() - return { - current_mode = {}, - - extmarks = { - decorational = { - calendar_text = nil, - help_and_custom_input = nil, - current_view = nil, - month_headings = {}, - weekday_displays = {}, - }, - logical = { - year = nil, - months = { - -- [3] = { [31] = } - }, - }, - }, - - -- TODO: implemant distance like in render_weekday_banner - render_month_banner = function(self, ui_info, date, weekday_banner_extmark_id) - local month_name = os.date( - "%B", - os.time({ - year = date.year, - month = date.month, - day = date.day, - }) - ) - ---@cast month_name string - local month_length = vim.api.nvim_strwidth(month_name) - - local weekday_banner_id = vim.api.nvim_buf_get_extmark_by_id( - ui_info.buffer, - init.private.namespaces.decorational, - weekday_banner_extmark_id, - { - details = true, - } - ) - - self.extmarks.decorational.month_headings[weekday_banner_extmark_id] = init.private - .set_decorational_extmark( - ui_info, - 4, - weekday_banner_id[2] - + math.ceil((weekday_banner_id[3].end_col - weekday_banner_id[2]) / 2) - - math.floor(month_length / 2), - month_length, - { { month_name, "@text.underline" } }, - nil, - { - id = self.extmarks.decorational.month_headings[weekday_banner_extmark_id], - } - ) - end, - - render_weekday_banner = function(self, ui_info, offset, distance) - offset = offset or 0 - distance = distance or 4 - - -- Render the days of the week - -- To effectively do this, we grab all the weekdays from a constant time. - -- This makes the weekdays retrieved locale dependent (which is what we want). - local weekdays = {} - local weekdays_string_length = 0 - for i = 1, 7 do - local weekday = os.date("%a", os.time({ year = 2000, month = 5, day = i })) - ---@cast weekday string - local truncated = utils.truncate_by_cell(weekday, 2) - local truncated_length = vim.api.nvim_strwidth(truncated) - weekdays[#weekdays + 1] = { truncated, "@text.title" } - weekdays[#weekdays + 1] = { (" "):rep(4 - truncated_length) } - weekdays_string_length = truncated_length -- remember last day's length - end - weekdays[#weekdays] = nil -- delete last padding - weekdays_string_length = weekdays_string_length + 4 * 6 - - -- This serves as the index of this week banner extmark inside the extmark table - local absolute_offset = offset + (offset < 0 and (-offset * 100) or 0) - - local extmark_position = 0 - - -- Calculate offset position only for the previous and following months - if offset ~= 0 then - extmark_position = (weekdays_string_length * math.abs(offset)) + (distance * math.abs(offset)) - end - - -- For previous months, revert the offset - if offset < 0 then - extmark_position = -extmark_position - end - - local weekday_banner_id = init.private.set_decorational_extmark( - ui_info, - 6, - extmark_position, - weekdays_string_length, - weekdays, - "center", - { - id = self.extmarks.decorational.weekday_displays[absolute_offset], - } - ) - - self.extmarks.decorational.weekday_displays[absolute_offset] = weekday_banner_id - - return weekday_banner_id - end, - - render_month = function(self, ui_info, target_date, weekday_banner_extmark_id) - --> Month rendering routine - -- We render the first month at the very center of the screen. Each - -- month takes up a static amount of characters. - - -- Render the top text of the month (June, August etc.) - -- Render the numbers for weekdays - local days_of_month = { - -- [day of month] = , - } - - local current_date = os.date("*t") - - local month, year = target_date.month, target_date.year - - local days_in_current_month = init.private.get_month_length(month, year) - - for i = 1, days_in_current_month do - days_of_month[i] = tonumber(os.date( - "%u", - os.time({ - year = year, - month = month, - day = i, - }) - )) - end - - local beginning_of_weekday_extmark = vim.api.nvim_buf_get_extmark_by_id( - ui_info.buffer, - init.private.namespaces.decorational, - weekday_banner_extmark_id, - {} - ) - - local render_column = days_of_month[1] - 1 - local render_row = 1 - - self.extmarks.logical.months[month] = self.extmarks.logical.months[month] or {} - - for day_of_month, day_of_week in ipairs(days_of_month) do - local is_current_day = current_date.year == year - and current_date.month == month - and day_of_month == current_date.day - - local start_row = beginning_of_weekday_extmark[1] + render_row - local start_col = beginning_of_weekday_extmark[2] + (4 * render_column) - - if is_current_day then - -- TODO: Make this configurable. The user might want the cursor to start - -- on a specific date in a specific month. - -- Just look up the extmark and place the cursor there. - vim.api.nvim_win_set_cursor(ui_info.window, { start_row + 1, start_col }) - end - - local day_highlight = is_current_day and "@text.todo" or nil - - if self.current_mode.get_day_highlight then - day_highlight = self.current_mode:get_day_highlight({ - year = year, - month = month, - day = day_of_month, - }, day_highlight) - end - - self.extmarks.logical.months[month][day_of_month] = vim.api.nvim_buf_set_extmark( - ui_info.buffer, - init.private.namespaces.logical, - start_row, - start_col, - { - virt_text = { - { - (day_of_month < 10 and "0" or "") .. tostring(day_of_month), - day_highlight, - }, - }, - virt_text_pos = "overlay", - id = self.extmarks.logical.months[month][day_of_month], - } - ) - - if day_of_week == 7 then - render_column = 0 - render_row = render_row + 1 - else - render_column = render_column + 1 - end - end - end, - - render_month_array = function(self, ui_info, date, options) - -- Render the first weekday banner in the middle - local weekday_banner = self:render_weekday_banner(ui_info, 0, options.distance) - self:render_month_banner(ui_info, date, weekday_banner) - self:render_month(ui_info, date, weekday_banner) - - local months_to_render = init.private.rendered_months_in_width(ui_info.width, options.distance) - months_to_render = math.floor(months_to_render / 2) - - for i = 1, months_to_render do - weekday_banner = self:render_weekday_banner(ui_info, i, options.distance) - - local positive_target_date = reformat_time({ - year = date.year, - month = date.month + i, - day = 1, - }) - - self:render_month_banner(ui_info, positive_target_date, weekday_banner) - self:render_month(ui_info, positive_target_date, weekday_banner) - - weekday_banner = self:render_weekday_banner(ui_info, i * -1, options.distance) - - local negative_target_date = reformat_time({ - year = date.year, - month = date.month - i, - day = 1, - }) - - self:render_month_banner(ui_info, negative_target_date, weekday_banner) - self:render_month(ui_info, negative_target_date, weekday_banner) - end - end, - - render_year_tag = function(self, ui_info, year) - -- Display the current year (i.e. `< 2022 >`) - local extra = nil - - if self.extmarks.logical.year ~= nil then - extra = { - id = self.extmarks.logical.year, - } - end - - local extmark = init.private.set_logical_extmark( - ui_info, - 2, - 0, - { { "< ", "Whitespace" }, { tostring(year), "@number" }, { " >", "Whitespace" } }, - "center", - extra - ) - - if self.extmarks.logical.year == nil then - self.extmarks.logical.year = extmark - end - end, - - render_decorative_text = function(self, ui_info, view) - --> Decorational section - -- CALENDAR text: - self.extmarks.decorational = vim.tbl_deep_extend("force", self.extmarks.decorational, { - calendar_text = init.private.set_decorational_extmark(ui_info, 0, 0, 0, { - { "ui.calendar", "@text.strong" }, - }, "center"), - - -- Help text at the bottom left of the screen - help_and_custom_input = init.private.set_decorational_extmark( - ui_info, - ui_info.height - 1, - 0, - 0, - { - { "?", "@character" }, - { " - " }, - { "help", "@text.strong" }, - { " " }, - { "i", "@character" }, - { " - " }, - { "custom input", "@text.strong" }, - } - ), - - -- The current view (bottom right of the screen) - current_view = init.private.set_decorational_extmark( - ui_info, - ui_info.height - 1, - 0, - 0, - { { "[", "Whitespace" }, { view, "@label" }, { "]", "Whitespace" } }, - "right" - ), - }) - end, - - select_current_day = function(self, ui_info, date) - local extmark_id = self.extmarks.logical.months[date.month][date.day] - - local position = vim.api.nvim_buf_get_extmark_by_id( - ui_info.buffer, - init.private.namespaces.logical, - extmark_id, - {} - ) - - vim.api.nvim_win_set_cursor(ui_info.window, { position[1] + 1, position[2] }) - end, - - render_view = function(self, ui_info, date, previous_date, options) - local is_first_render = (previous_date == nil) - - if is_first_render then - vim.api.nvim_buf_clear_namespace(ui_info.buffer, init.private.namespaces.decorational, 0, -1) - vim.api.nvim_buf_clear_namespace(ui_info.buffer, init.private.namespaces.logical, 0, -1) - - vim.api.nvim_buf_set_option(ui_info.buffer, "modifiable", true) - - init.private.fill_buffer(ui_info) - self:render_decorative_text(ui_info, init.public.view_name:upper()) - self:render_year_tag(ui_info, date.year) - self:render_month_array(ui_info, date, options) - self:select_current_day(ui_info, date) - - vim.api.nvim_buf_set_option(ui_info.buffer, "modifiable", false) - vim.api.nvim_set_option_value("winfixbuf", true, { win = ui_info.window }) - - return - end - - local year_changed = (date.year ~= previous_date.year) - local month_changed = (date.month ~= previous_date.month) - local day_changed = (date.day ~= previous_date.day) - - if year_changed then - self:render_year_tag(ui_info, date.year) - end - - if year_changed or month_changed then - self:render_month_array(ui_info, date, options) - self:clear_extmarks(ui_info, date, options) - end - - if year_changed or month_changed or day_changed then - self:select_current_day(ui_info, date) - end - end, - - clear_extmarks = function(self, ui_info, current_date, options) - local cur_month = current_date.month - - local rendered_months_offset = - math.floor(init.private.rendered_months_in_width(ui_info.width, options.distance) / 2) - - -- Mimics ternary operator to be concise - local month_min = cur_month - rendered_months_offset - month_min = month_min <= 0 and (12 + month_min) or month_min - - local month_max = cur_month + rendered_months_offset - month_max = month_max > 12 and (month_max - 12) or month_max - - local clear_extmarks_for_month = function(month) - for _, extmark_id in ipairs(self.extmarks.logical.months[month]) do - vim.api.nvim_buf_del_extmark(ui_info.buffer, init.private.namespaces.logical, extmark_id) - end - - self.extmarks.logical.months[month] = nil - end - - for month, _ in pairs(self.extmarks.logical.months) do - -- Check if the month is outside the current view range - -- considering the month wrapping after 12 - if month_min < month_max then - if month_min > month or month > month_max then - clear_extmarks_for_month(month) - end - elseif month_min > month_max then - if month_max < month and month < month_min then - clear_extmarks_for_month(month) - end - elseif month_min == month_max then - if month ~= cur_month then - clear_extmarks_for_month(month) - end - end - end - end, - } - end, - - fill_buffer = function(ui_info) - -- There are many steps to render a calendar. - -- The first step is to fill the entire buffer with spaces. This lets - -- us place extmarks at any position in the document. Won't be used for - -- the meaty stuff, but will come in handy for rendering decorational - -- elements. - local fill = {} - local filler = string.rep(" ", ui_info.width) - - for i = 1, ui_info.height do - fill[i] = filler - end - - vim.api.nvim_buf_set_lines(ui_info.buffer, 0, -1, true, fill) - end, - - --- get the number of days in the month, months are wrapped (ie, month 13 <==> month 1) - get_month_length = function(month, year) - return ({ - 31, - (init.private.is_leap_year(year)) and 29 or 28, - 31, - 30, - 31, - 30, - 31, - 31, - 30, - 31, - 30, - 31, - })[lib.number_wrap(month, 1, 12)] - end, - - is_leap_year = function(year) - if year % 4 ~= 0 then - return false - end - - -- Years disible by 100 are leap years only if also divisible by 400 - if year % 100 == 0 and year % 400 ~= 0 then - return false - end - - return true - end, - - rendered_months_in_width = function(width, distance) - local rendered_month_width = 26 - local months = math.floor(width / (rendered_month_width + distance)) - - -- Do not show more than one year - if months > 12 then - months = 12 - end - - if months % 2 == 0 then - return months - 1 - end - return months - end, - - display_help = function(lines) - local width, height = 44, 32 - local buffer = vim.api.nvim_create_buf(false, true) - local window = vim.api.nvim_open_win(buffer, true, { - style = "minimal", - border = "rounded", - title = " Calendar ", - title_pos = "center", - row = (vim.o.lines / 2) - height / 2, - col = (vim.o.columns / 2) - width / 2, - width = width, - height = height, - relative = "editor", - noautocmd = true, - }) - vim.api.nvim_set_option_value("winfixbuf", true, { win = window }) - - local function quit() - vim.api.nvim_win_close(window, true) - pcall(vim.api.nvim_buf_delete, buffer, { force = true }) - end - - vim.keymap.set("n", "q", quit, { buffer = buffer }) - - vim.api.nvim_create_autocmd({ "BufLeave", "WinLeave" }, { - buffer = buffer, - callback = quit, - }) - - local namespace = vim.api.nvim_create_namespace("word/calendar-help") - vim.api.nvim_buf_set_option(buffer, "modifiable", false) - - vim.api.nvim_buf_set_extmark(buffer, namespace, 0, 0, { - virt_lines = lines, - }) - end, -} - ----@class base.calendar.views.monthly -init.public = { - - view_name = "monthly", - - setup = function(ui_info, mode, date, options) - options.distance = options.distance or 4 - - local view = init.private.new_view_instance() - - view.current_mode = mode - - view:render_view(ui_info, date, nil, options) - - do - vim.keymap.set("n", "q", function() - vim.api.nvim_buf_delete(ui_info.buffer, { force = true }) - end, { buffer = ui_info.buffer }) - - -- TODO: Make cursor wrapping behaviour configurable - vim.keymap.set("n", "l", function() - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = date.day + 1 * vim.v.count1, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "h", function() - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = date.day - 1 * vim.v.count1, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "j", function() - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = date.day + 7 * vim.v.count1, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "k", function() - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = date.day - 7 * vim.v.count1, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "", function() - local should_redraw = false - - if view.current_mode.on_select ~= nil then - should_redraw = view.current_mode:on_select(date) - end - - if should_redraw then - view:render_view(ui_info, date, nil, options) - end - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "L", function() - local new_date = reformat_time({ - year = date.year, - month = date.month + vim.v.count1, - day = date.day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "H", function() - local new_date = reformat_time({ - year = date.year, - month = date.month - vim.v.count1, - day = date.day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "m", function() - local new_date = reformat_time({ - year = date.year, - month = date.month + vim.v.count1, - day = 1, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "M", function() - if date.day > 1 then - date.month = date.month + 1 - end - local new_date = reformat_time({ - year = date.year, - month = date.month - vim.v.count1, - day = 1, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "y", function() - local new_date = reformat_time({ - year = date.year + vim.v.count1, - month = date.month, - day = date.day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "Y", function() - local new_date = reformat_time({ - year = date.year - vim.v.count1, - month = date.month, - day = date.day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "$", function() - local new_day = date.day - (lib.number_wrap(date.wday - 1, 1, 7) - 1) + 6 - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = new_day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - local start_of_week = function() - local new_day = date.day - (lib.number_wrap(date.wday - 1, 1, 7) - 1) - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = new_day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end - - vim.keymap.set("n", "0", start_of_week, { buffer = ui_info.buffer }) - vim.keymap.set("n", "_", start_of_week, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "t", function() - local new_date = os.date("*t") - - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "e", function() - local end_of_current_month = init.private.get_month_length(date.month, date.year) - if end_of_current_month > date.day then - date.month = date.month - 1 - end - local new_date = reformat_time({ - year = date.year, - month = date.month + vim.v.count1, - day = init.private.get_month_length(date.month + vim.v.count1, date.year), - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "E", function() - local new_date = reformat_time({ - year = date.year, - month = date.month - vim.v.count1, - day = init.private.get_month_length(date.month - vim.v.count1, date.year), - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "w", function() - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = date.day + 7 * vim.v.count1, - }) - new_date.day = new_date.day - (lib.number_wrap(new_date.wday - 1, 1, 7) - 1) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "W", function() - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = date.day - 7 * vim.v.count1, - }) - new_date.day = new_date.day - (lib.number_wrap(new_date.wday - 1, 1, 7) - 1) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - local months = {} - for i = 1, 12 do - table.insert( - months, - (os.date("%B", os.time({ year = 2000, month = i, day = 1 })) --[[@as string]]):lower() - ) - end - - -- store the last `;` repeatable search - local last_semi_jump = nil - -- flag to set when we're using `;` so it doesn't cycle - local skip_next = false - - vim.keymap.set("n", ";", function() - if last_semi_jump then - vim.api.nvim_feedkeys(last_semi_jump, "m", false) - end - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", ",", function() - if last_semi_jump then - local action = string.sub(last_semi_jump, 1, 1) - local subject = string.sub(last_semi_jump, 2) - local new_keys - if string.upper(action) == action then - new_keys = action:lower() .. subject - else - new_keys = action:upper() .. subject - end - vim.api.nvim_feedkeys(new_keys, "m", false) - - skip_next = true - end - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "f", function() - local char = vim.fn.getcharstr() - - for i = date.month + 1, date.month + 12 do - local m = lib.number_wrap(i, 1, 12) - if months[m]:match("^" .. char) then - if not skip_next then - last_semi_jump = "f" .. char - else - skip_next = false - end - - local new_date = reformat_time({ - year = date.year, - month = m, - day = date.day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - break - end - end - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "F", function() - local char = vim.fn.getcharstr() - - for i = date.month + 11, date.month, -1 do - local m = lib.number_wrap(i, 1, 12) - if months[m]:match("^" .. char) then - if not skip_next then - last_semi_jump = "F" .. char - else - skip_next = false - end - local new_date = reformat_time({ - year = date.year, - month = m, - day = date.day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - break - end - end - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "g", function() - local day = math.min(vim.v.count1, init.private.get_month_length(date.month, date.year)) - - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = day, - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer, nowait = true }) - - vim.keymap.set("n", "G", function() - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = init.private.get_month_length(date.month, date.year), - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer }) - - vim.keymap.set("n", "d", function() - local n = vim.v.count1 - local weekday = math.min(n, 7) - local new_date = reformat_time({ - year = date.year, - month = date.month, - day = date.day + (weekday - lib.number_wrap(date.wday - 1, 1, 7)), - }) - view:render_view(ui_info, new_date, date, options) - date = new_date - end, { buffer = ui_info.buffer, nowait = true }) - - vim.keymap.set( - "n", - "?", - lib.wrap(init.private.display_help, { - { - { "q", "@namespace" }, - { " - " }, - { "close this window", "@text.strong" }, - }, - {}, - { - { "", "@namespace" }, - { " - " }, - { "select date", "@text.strong" }, - }, - {}, - { - { "--- Basic Movement ---", "@text.title" }, - }, - {}, - { - { "l/h", "@namespace" }, - { " - " }, - { "next/previous day", "@text.strong" }, - }, - { - { "j/k", "@namespace" }, - { " - " }, - { "next/previous week", "@text.strong" }, - }, - { - { "w/W", "@namespace" }, - { " - " }, - { "start of next/this or previous week", "@text.strong" }, - }, - { - { "t", "@namespace" }, - { " - " }, - { "today", "@text.strong" }, - }, - { - { "d", "@namespace" }, - { "n" }, - { " - " }, - { "weekday ", "@text.strong" }, - { "n" }, - { " (1 = monday)", "@text.strong" }, - }, - {}, - { - { "--- Moving Between Months ---", "@text.title" }, - }, - {}, - { - { "L/H", "@namespace" }, - { " - " }, - { "next/previous month (same day)", "@text.strong" }, - }, - { - { "m/M", "@namespace" }, - { " - " }, - { "1st of next/this or previous month", "@text.strong" }, - }, - { - { "f", "@namespace" }, - { "x" }, - { "/F", "@namespace" }, - { "x" }, - { " - " }, - { "next/previous month starting with ", "@text.strong" }, - { "x" }, - }, - {}, - { - { "--- Moving Between Years ---", "@text.title" }, - }, - {}, - { - { "y/Y", "@namespace" }, - { " - " }, - { "next/previous year (same day)", "@text.strong" }, - }, - { - { "gy", "@namespace" }, - { " - " }, - { "start of the current year", "@text.strong" }, - }, - { - { "c/C", "@namespace" }, - { " - " }, - { "next/this or previous century", "@text.strong" }, - }, - { - { "g/G", "@namespace" }, - { " - " }, - { "start/end of month", "@text.strong" }, - }, - { - { " " }, - { "g takes you to day of the month", "@text.strong" }, - }, - {}, - { - { "--- Additional Info ---", "@text.title" }, - }, - {}, - { - { "All movements accept counts" }, - }, - { - { "f/F and g/G work with `;` and `,`" }, - }, - }), - { buffer = ui_info.buffer } - ) - - vim.keymap.set("n", "i", function() - local buffer = vim.api.nvim_create_buf(false, true) - vim.api.nvim_open_win(buffer, true, { - style = "minimal", - border = "single", - title = "Date (`?` for help)", - row = vim.api.nvim_win_get_height(0), - col = 0, - width = vim.o.columns, - height = 1, - relative = "win", - win = vim.api.nvim_get_current_win(), - noautocmd = true, - }) - - vim.cmd.startinsert() - - local function quit() - vim.cmd.stopinsert() - vim.api.nvim_buf_delete(buffer, { force = true }) - end - - vim.keymap.set("n", "", quit, { buffer = buffer }) - vim.keymap.set("i", "", quit, { buffer = buffer }) - vim.keymap.set( - "n", - "?", - lib.wrap(init.private.display_help, { - { - { "q", "@namespace" }, - { " - " }, - { "close this window", "@text.strong" }, - }, - { - { "", "@namespace" }, - { " - " }, - { "confirm date", "@text.strong" }, - }, - {}, - { - { "--- Quitting ---", "@text.title" }, - }, - {}, - { - { " (insert mode)", "@namespace" }, - { " - " }, - { "quit", "@text.strong" }, - }, - { - { "", "@namespace" }, - { " - " }, - { "quit", "@text.strong" }, - }, - {}, - { - { "--- Date Syntax ---", "@text.title" }, - }, - {}, - { - { "Order " }, - { "does not matter", "@text.strong" }, - { " with dates." }, - }, - {}, - { - { "Some things depend on locale." }, - }, - {}, - { - { "Months and weekdays may be written" }, - }, - { - { "with a shorthand." }, - }, - {}, - { - { "Years must contain 4 digits at" }, - }, - { - { "all times. Prefix with zeroes" }, - }, - { - { "where necessary." }, - }, - {}, - { - { "Hour syntax: `00:00.00` (hour, min, sec)" }, - }, - {}, - { - { "--- Examples ---", "@text.title" }, - }, - {}, - { - { "Tuesday May 5th 2023 19:00.23", "@word.markup.verbatim" }, - }, - { - { "10 Feb CEST 0600", "@word.markup.verbatim" }, - { " (", "@comment" }, - { "0600", "@text.emphasis" }, - { " is the year)", "@comment" }, - }, - { - { "9:00.4 2nd March Wed", "@word.markup.verbatim" }, - }, - }), - { buffer = buffer } - ) - - vim.keymap.set({ "n", "i" }, "", function() - local line = vim.api.nvim_buf_get_lines(buffer, 0, -1, true)[1] - - local parsed_date = init.required["time"].parse_date(line) - - if type(parsed_date) == "string" then - log.error("[ERROR]:", parsed_date) - return - end - - quit() - - local lua_date = init.required["time"].to_lua_date(parsed_date) - - local should_redraw = false - - if view.current_mode.on_select ~= nil then - should_redraw = view.current_mode:on_select(lua_date) - end - - if should_redraw then - view:render_view(ui_info, lua_date, nil, options) - end - end, { buffer = buffer }) - end, { buffer = ui_info.buffer }) - end - end, -} - -init.load = function() - init.required["calendar"].add_view(init.public.view_name, init.public) -end - -return init diff --git a/lua/word/mod/cmd/commands/rename/init.lua-E b/lua/word/mod/cmd/commands/rename/init.lua-E deleted file mode 100644 index 4f58cce..0000000 --- a/lua/word/mod/cmd/commands/rename/init.lua-E +++ /dev/null @@ -1,57 +0,0 @@ ---[[ - file: cmd-rename - title: Provides the `:word rename` Command - summary: rename to last location before entering word. - internal: true - --- -When executed (`:word rename`), all currently open `.word` files are deleted from -the buffer list, and the current vault is set to "base". ---]] -local word = require("word") -local mod = word.mod - -local init = mod.create("cmd.commands.rename") - -init.setup = function() - return { success = true, requires = { "cmd" } } -end - -init.public = { - word_commands = { - rename = { - args = 0, - name = "rename", - }, - }, -} - -init.on_event = function(event) - if event.type == "cmd.events.rename" then - -- Get all the buffers - local buffers = vim.api.nvim_list_bufs() - - local to_delete = {} - for buffer in vim.iter(buffers):rev() do - if vim.fn.buflisted(buffer) == 1 then - -- If the listed buffer we're working with has a .word extension then remove it (not forcibly) - if not vim.endswith(vim.api.nvim_buf_get_name(buffer), ".md") then - vim.api.nvim_win_set_buf(0, buffer) - break - else - table.insert(to_delete, buffer) - end - end - end - - for _, buffer in ipairs(to_delete) do - vim.api.nvim_buf_delete(buffer, {}) - end - end -end - -init.events.subscribed = { - cmd = { - rename = true, - }, -} -return init diff --git a/lua/word/mod/cmd/commands/return/init.lua-E b/lua/word/mod/cmd/commands/return/init.lua-E deleted file mode 100644 index 1a36320..0000000 --- a/lua/word/mod/cmd/commands/return/init.lua-E +++ /dev/null @@ -1,59 +0,0 @@ ---[[ - file: cmd-return - title: Provides the `:word return` Command - summary: Return to last location before entering word. - internal: true - --- -When executed (`:word return`), all currently open `.word` files are deleted from -the buffer list, and the current vault is set to "base". ---]] - -local word = require("word") -local mod = word.mod - -local init = mod.create("cmd.commands.return") - -init.setup = function() - return { success = true, requires = { "cmd" } } -end - -init.public = { - word_commands = { - ["return"] = { - args = 0, - name = "return", - }, - }, -} - -init.on_event = function(event) - if event.type == "cmd.events.return" then - -- Get all the buffers - local buffers = vim.api.nvim_list_bufs() - - local to_delete = {} - for buffer in vim.iter(buffers):rev() do - if vim.fn.buflisted(buffer) == 1 then - -- If the listed buffer we're working with has a .word extension then remove it (not forcibly) - if not vim.endswith(vim.api.nvim_buf_get_name(buffer), ".md") then - vim.api.nvim_win_set_buf(0, buffer) - break - else - table.insert(to_delete, buffer) - end - end - end - - for _, buffer in ipairs(to_delete) do - vim.api.nvim_buf_delete(buffer, {}) - end - end -end - -init.events.subscribed = { - cmd = { - ["return"] = true, - }, -} - -return init diff --git a/lua/word/mod/cmd/init.lua-E b/lua/word/mod/cmd/init.lua-E deleted file mode 100644 index efc5bbf..0000000 --- a/lua/word/mod/cmd/init.lua-E +++ /dev/null @@ -1,509 +0,0 @@ ---[[ - file: cmd-init - title: Does the Heavy Lifting for the `:word` Command - summary: This init deals with handling everything related to the `:word` command. - internal: true - --- -This internal init handles everything there is for the `:word` command to function. - -Different mod can define their own commands, completions and conditions on when they'd -like these commands to be avaiable. - -For a full example on how to create your own command, it is recommended to read the -`base.cmd`'s `mod.lua` file. At the beginning of the file is an examples table -which walks you through the necessary steps. ---]] - -local word = require("word") -local log, mod, util = word.log, word.mod, word.utils - -local M = mod.create("cmd") - - -M.private = { - - --- Handles the calling of the appropriate function based on the command the user entered - command_callback = function(data) - local args = data.fargs - - local current_buf = vim.api.nvim_get_current_buf() - local is_word = vim.bo[current_buf].filetype == "markdown" - - local function check_condition(condition) - if condition == nil then - return true - end - - if condition == "markdown" and not is_word then - return false - end - - if type(condition) == "function" then - return condition(current_buf, is_word) - end - - return condition - end - - local ref = { - subcommands = M.public.commands, - } - local argument_index = 0 - - for i, cmd in ipairs(args) do - if not ref.subcommands or vim.tbl_isempty(ref.subcommands) then - break - end - - ref = ref.subcommands[cmd] - - if not ref then - log.error( - ("Error when executing `:word %s` - such a command does not exist!"):format( - table.concat(vim.list_slice(args, 1, i), " ") - ) - ) - return - elseif not check_condition(ref.condition) then - log.error( - ("Error when executing `:word %s` - the command is currently disabled. Some commands will only become available under certain conditions, e.g. being within a `.word` file!") - :format( - table.concat(vim.list_slice(args, 1, i), " ") - ) - ) - return - end - - argument_index = i - end - - local argument_count = (#args - argument_index) - - if ref.args then - ref.min_args = ref.args - ref.max_args = ref.args - elseif ref.min_args and not ref.max_args then - ref.max_args = math.huge - else - ref.min_args = ref.min_args or 0 - ref.max_args = ref.max_args or 0 - end - - if #args == 0 or argument_count < ref.min_args then - local completions = M.private.generate_completions(_, table.concat({ "word ", data.args, " " })) - M.private.select_next_cmd_arg(data.args, completions) - return - elseif argument_count > ref.max_args then - log.error( - ("Error when executing `:word %s` - too many arguments supplied! The command expects %s argument%s.") - :format( - data.args, - ref.max_args == 0 and "no" or ref.max_args, - ref.max_args == 1 and "" or "s" - ) - ) - return - end - - if not ref.name then - log.error( - ("Error when executing `:word %s` - the ending command didn't have a `name` variable associated with it! This is an implementation error on the developer's side, so file a report to the author of the mod.") - :format( - data.args - ) - ) - return - end - - if not M.events.defined[ref.name] then - M.events.defined[ref.name] = mod.define_event(M, ref.name) - end - - mod.broadcast_event( - assert( - mod.create_event( - M, - table.concat({ "cmd.events.", ref.name }), - vim.list_slice(args, argument_index + 1) - ) - ) - ) - end, - - --- This function returns all available commands to be used for the :word command - ---@param _ nil #Placeholder variable - ---@param command string #Supplied by nvim itself; the full typed out command - generate_completions = function(_, command) - local current_buf = vim.api.nvim_get_current_buf() - local is_word = vim.api.nvim_buf_get_option(current_buf, "filetype") == "markdown" - - local function check_condition(condition) - if condition == nil then - return true - end - - if condition == "word" and not is_word then - return false - end - - if type(condition) == "function" then - return condition(current_buf, is_word) - end - - return condition - end - - command = command:gsub("^%s*", "") - - local splitcmd = vim.list_slice( - vim.split(command, " ", { - plain = true, - trimempty = true, - }), - 2 - ) - - local ref = { - subcommands = M.public.commands, - } - local last_valid_ref = ref - local last_completion_level = 0 - - for _, cmd in ipairs(splitcmd) do - if not ref or not check_condition(ref.condition) then - break - end - - ref = ref.subcommands or {} - ref = ref[cmd] - - if ref then - last_valid_ref = ref - last_completion_level = last_completion_level + 1 - end - end - - if not last_valid_ref.subcommands and last_valid_ref.complete then - if type(last_valid_ref.complete) == "function" then - last_valid_ref.complete = last_valid_ref.complete(current_buf, is_word) - end - - if vim.endswith(command, " ") then - local completions = last_valid_ref.complete[#splitcmd - last_completion_level + 1] or {} - - if type(completions) == "function" then - completions = completions(current_buf, is_word) or {} - end - - return completions - else - local completions = last_valid_ref.complete[#splitcmd - last_completion_level] or {} - - if type(completions) == "function" then - completions = completions(current_buf, is_word) or {} - end - - return vim.tbl_filter(function(key) - return key:find(splitcmd[#splitcmd]) - end, completions) - end - end - - -- TODO: Fix `:word m ` giving invalid completions - local keys = ref and vim.tbl_keys(ref.subcommands or {}) - or ( - vim.tbl_filter(function(key) - return key:find(splitcmd[#splitcmd]) - end, vim.tbl_keys(last_valid_ref.subcommands or {})) - ) - table.sort(keys) - - do - local subcommands = (ref and ref.subcommands or last_valid_ref.subcommands) or {} - - return vim.tbl_filter(function(key) - return check_condition(subcommands[key].condition) - end, keys) - end - end, - - --- Queries the user to select next argument - ---@param qargs table #A string of arguments previously supplied to the word command - ---@param choices table #all possible choices for the next argument - select_next_cmd_arg = function(qargs, choices) - local current = table.concat({ "Word ", qargs }) - - local query - - if vim.tbl_isempty(choices) then - query = function(...) - vim.ui.input(...) - end - else - query = function(...) - vim.ui.select(choices, ...) - end - end - - query({ - prompt = current, - }, function(choice) - if choice ~= nil then - vim.cmd(string.format("%s %s", current, choice)) - end - end) - end, -} -M.public = { - - -- The table containing all the functions. This can get a tad complex so I recommend you read the wiki entry - commands = { - mod = { - subcommands = { - new = { - args = 1, - name = "mod.new", - }, - load = { - args = 1, - name = "mod.load", - }, - - list = { - args = 0, - name = "mod.list", - }, - }, - }, - }, - - --- Recursively merges the contents of the init's config.public.funtions table with base.cmd's mod.config.public.commands table. - ---@param mod_name string #An absolute path to a loaded init with a mod.config.public.commands table following a valid structure - add_commands = function(mod_name) - local mod_config = mod.get_mod(mod_name) - - if not mod_config or not mod_config.commands then - return - end - - M.public.commands = - vim.tbl_extend("force", M.public.commands, mod_config.commands) - end, - - --- Recursively merges the provided table with the mod.config.public.commands table. - ---@param functions table #A table that follows the mod.config.public.commands structure - add_cmd_tbl = function(functions) - M.public.commands = vim.tbl_extend("force", M.public.commands, functions) - end, - - --- Takes a relative path (e.g "list.mod") and loads it from the commands/ directory - ---@param name string #The relative path of the init we want to load - add_commands_from_file = function(name) - -- Attempt to require the file - local err, ret = pcall(require, "word.mod.cmd.commands." .. name .. "init") - - -- If we've failed bail out - if not err then - log.warn( - "Could not load command", - name, - "for init base.cmd - the corresponding mod.lua file does not exist." - ) - return - end - - -- Load the init from table - mod.load_mod_table(ret) - end, - - --- Rereads data from all mod and rebuild the list of available autocompletiinitinitons and commands - sync = function() - -- Loop through every loaded init and set up all their commands - for _, lm in pairs(mod.loaded_mod) do - if lm.public.commands then - M.public.add_cmd_tbl(lm.public.word_commands) - end - end - end, - - --- Defines a custom completion function to use for `base.cmd`. - ---@param callback function The same function format as you would receive by being called by `:command -completion=customlist,v:lua.callback word`. - set_completion_callback = function(callback) - M.private.generate_completions = callback - end, -} -M.load = function() - -- Define the :word command with autocompletion taking any number of arguments (-nargs=*) - -- If the user passes no arguments or too few, we'll query them for the remainder using select_next_cmd_arg. - vim.api.nvim_create_user_command("Word", M.private.command_callback, { - desc = "The word command", - force = true, - -- bang = true, - nargs = "*", - complete = M.private.generate_completions, - }) - - -- Loop through all the command mod we want to load and load them - for _, command in ipairs(M.config.public.load) do - -- If one of the command mod is "base" then load all the base mod - if command == "base" then - for _, base_command in ipairs(M.config.public.base) do - M.public.add_commands_from_file(base_command) - end - end - end -end - -M.config.public = { - -- A list of cmd mod to load automatically. - -- This feature will soon be deprecated, so it is not recommended to touch it. - load = { - "base", - }, - - -- A list of base commands to load. - -- - -- This feature will soon be deprecated, so it is not recommended to touch it. - base = { - "return", - "rename", - }, -} ----@class base.cmd - - -M.word_post_load = M.public.sync - -M.on_event = function(event) - if event.type == "cmd.events.mod.load" then - local ok = pcall(mod.load_mod, event.content[1]) - - if not ok then - vim.notify(string.format("init `%s` does not exist!", event.content[1]), vim.log.levels.ERROR, {}) - end - end - - if event.type == "cmd.events.mod.list" then - local Popup = require("nui.popup") - - local mod_list_popup = Popup({ - position = "50%", - size = { width = "50%", height = "80%" }, - enter = true, - buf_options = { - filetype = "markdown", - modifiable = true, - readonly = false, - }, - win_options = { - conceallevel = 3, - concealcursor = "nvi", - }, - }) - - mod_list_popup:on("VimResized", function() - mod_list_popup:update_layout() - end) - - local function close() - mod_list_popup:unmount() - end - - mod_list_popup:map("n", "", close, {}) - mod_list_popup:map("n", "q", close, {}) - - local lines = {} - - for name, _ in pairs(word.mod.loaded_mod) do - table.insert(lines, "- `" .. name .. "`") - end - - vim.api.nvim_buf_set_lines(mod_list_popup.bufnr, 0, -1, true, lines) - - vim.bo[mod_list_popup.bufnr].modifiable = false - - mod_list_popup:mount() - end -end - -M.events.subscribed = { - cmd = { - -- ["mod.new"] = true, - ["mod.load"] = true, - ["mod.list"] = true, - }, -} - -M.examples = { - ["Adding a word command"] = function() - -- In your mod.setup(), make sure to require base.cmd (requires = { "cmd" }) - -- Afterwards in a function of your choice that gets called *after* base.cmd gets intialized e.g. load(): - - M.load = function() - M.required["cmd"].add_cmd_tbl({ - -- The name of our command - my_command = { - min_args = 1, -- Tells cmd that we want at least one argument for this command - max_args = 1, -- Tells cmd we want no more than one argument - args = 1, -- Setting this variable instead would be the equivalent of min_args = 1 and max_args = 1 - -- This command is only avaiable within `.word` files. - -- This can also be a function(bufnr, is_in_an_word_file) - condition = "markdown", - - subcommands = { -- Defines subcommands - -- Repeat the definition cycle again - my_subcommand = { - args = 2, -- Force two arguments to be supplied - -- The identifying name of this command - -- Every "endpoint" must have a name associated with it - name = "my.command", - - -- If your command takes in arguments versus - -- subcommands you can make a table of tables with - -- completion for those arguments here. - -- This table is optional. - complete = { - { "first_completion1", "first_completion2" }, - { "second_completion1", "second_completion2" }, - }, - - -- We do not define a subcommands table here because we don't have any more subcommands - -- Creating an empty subcommands table will cause errors so don't bother - }, - }, - }, - }) - end - - -- Afterwards, you want to subscribe to the corresponding event: - - M.events.subscribed = { - ["cmd"] = { - ["my.command"] = true, -- Has the same name as our "name" variable had in the "data" table - }, - } - - -- There's also another way to define your own custom commands that's a lot more automated. Such automation can be achieved - -- by putting your code in a special directory. That directory is in base.cmd.commands. Creating your mod in this directory - -- will allow users to easily enable you as a "command init" without much hassle. - - -- To enable a command in the commands/ directory, do this: - - require("word").setup({ - load = { - cmd = { - config = { - load = { - "some.cmd", -- The name of a valid command - }, - }, - }, - }, - }) - - -- And that's it! You're good to go. - -- Want to find out more? Read the wiki entry! https://github.com/nvim-word/word/wiki/word-Command - end, -} - -return M diff --git a/lua/word/mod/time/init.lua-E b/lua/word/mod/time/init.lua-E deleted file mode 100644 index 6d67dfd..0000000 --- a/lua/word/mod/time/init.lua-E +++ /dev/null @@ -1,476 +0,0 @@ ---[[ - file: time - title: Hassle-Free Dates - summary: Parses and handles dates in word. - internal: true - --- -`core.time` is an internal init specifically designed -to handle complex dates. It exposes two functions: `parse_date(string) -> date|string` -and `to_lua_date(date) -> osdate`. - -## Keybinds - -This init exposes the following keybinds (see [`core.keybinds`](@core.keybinds) for instructions on -mapping them): - -- `word.time.insert-date` - Insert date at cursor position (called from normal mode) -- `word.time.insert-date.insert-mode` - Insert date at cursor position (called from insert mode) ---]] - -local d = require("word") -local lib, inits, utils = d.lib, d.mod, d.utils - -local init = inits.create("time") - --- NOTE: Maybe encapsulate whole date parser in a single PEG grammar? -local _, time_regex = pcall(vim.re.compile, [[{%d%d?} ":" {%d%d} ("." {%d%d?})?]]) - -local timezone_list = { - "ACDT", - "ACST", - "ACT", - "ACWST", - "ADT", - "AEDT", - "AEST", - "AET", - "AFT", - "AKDT", - "AKST", - "ALMT", - "AMST", - "AMT", - "ANAT", - "AQTT", - "ART", - "AST", - "AWST", - "AZOST", - "AZOT", - "AZT", - "BNT", - "BIOT", - "BIT", - "BOT", - "BRST", - "BRT", - "BST", - "BTT", - "CAT", - "CCT", - "CDT", - "CEST", - "CET", - "CHADT", - "CHAST", - "CHOT", - "CHOST", - "CHST", - "CHUT", - "CIST", - "CKT", - "CLST", - "CLT", - "COST", - "COT", - "CST", - "CT", - "CVT", - "CWST", - "CXT", - "DAVT", - "DDUT", - "DFT", - "EASST", - "EAST", - "EAT", - "ECT", - "EDT", - "EEST", - "EET", - "EGST", - "EGT", - "EST", - "ET", - "FET", - "FJT", - "FKST", - "FKT", - "FNT", - "GALT", - "GAMT", - "GET", - "GFT", - "GILT", - "GIT", - "GMT", - "GST", - "GYT", - "HDT", - "HAEC", - "HST", - "HKT", - "HMT", - "HOVST", - "HOVT", - "ICT", - "IDLW", - "IDT", - "IOT", - "IRDT", - "IRKT", - "IRST", - "IST", - "JST", - "KALT", - "KGT", - "KOST", - "KRAT", - "KST", - "LHST", - "LINT", - "MAGT", - "MART", - "MAWT", - "MDT", - "MET", - "MEST", - "MHT", - "MIST", - "MIT", - "MMT", - "MSK", - "MST", - "MUT", - "MVT", - "MYT", - "NCT", - "NDT", - "NFT", - "NOVT", - "NPT", - "NST", - "NT", - "NUT", - "NZDT", - "NZST", - "OMST", - "ORAT", - "PDT", - "PET", - "PETT", - "PGT", - "PHOT", - "PHT", - "PHST", - "PKT", - "PMDT", - "PMST", - "PONT", - "PST", - "PWT", - "PYST", - "PYT", - "RET", - "ROTT", - "SAKT", - "SAMT", - "SAST", - "SBT", - "SCT", - "SDT", - "SGT", - "SLST", - "SRET", - "SRT", - "SST", - "SYOT", - "TAHT", - "THA", - "TFT", - "TJT", - "TKT", - "TLT", - "TMT", - "TRT", - "TOT", - "TVT", - "ULAST", - "ULAT", - "UTC", - "UYST", - "UYT", - "UZT", - "VET", - "VLAT", - "VOLT", - "VOST", - "VUT", - "WAKT", - "WAST", - "WAT", - "WEST", - "WET", - "WIB", - "WIT", - "WITA", - "WGST", - "WGT", - "WST", - "YAKT", - "YEKT", -} - ----@alias Date {weekday: {name: string, number: number}?, day: number?, month: {name: string, number: number}?, year: number?, timezone: string?, time: {hour: number, minute: number, second: number?}?} - ----@class time -init.public = { - --- Converts a parsed date with `parse_date` to a lua date. - ---@param parsed_date Date #The date to convert - ---@return osdate #A Lua date - to_lua_date = function(parsed_date) - local now = os.date("*t") --[[@as osdate]] - local parsed = os.time(vim.tbl_deep_extend("force", now, { - day = parsed_date.day, - month = parsed_date.month and parsed_date.month.number or nil, - year = parsed_date.year, - hour = parsed_date.time and parsed_date.time.hour, - min = parsed_date.time and parsed_date.time.minute, - sec = parsed_date.time and parsed_date.time.second, - }) --[[@as osdateparam]]) - return os.date("*t", parsed) --[[@as osdate]] - end, - - --- Converts a lua `osdate` to a word date. - ---@param osdate osdate #The date to convert - ---@param include_time boolean? #Whether to include the time (hh::mm.ss) in the output. - ---@return Date #The converted date - to_date = function(osdate, include_time) - -- TODO: Extract into a function to get weekdays (have to hot recalculate every time because the user may change locale - local weekdays = {} - for i = 1, 7 do - table.insert(weekdays, os.date("%A", os.time({ year = 2000, month = 5, day = i })):lower()) ---@diagnostic disable-line -- TODO: type error workaround - end - - local months = {} - for i = 1, 12 do - table.insert(months, os.date("%B", os.time({ year = 2000, month = i, day = 1 })):lower()) ---@diagnostic disable-line -- TODO: type error workaround - end - - -- os.date("*t") returns wday with Sunday as 1, needs to be - -- converted to Monday as 1 - local converted_weekday = lib.number_wrap(osdate.wday - 1, 1, 7) - - return init.private.tostringable_date({ - weekday = osdate.wday and { - number = converted_weekday, - name = lib.title(weekdays[converted_weekday]), - } or nil, - day = osdate.day, - month = osdate.month and { - number = osdate.month, - name = lib.title(months[osdate.month]), - } or nil, - year = osdate.year, - time = osdate.hour and setmetatable({ - hour = osdate.hour, - minute = osdate.min or 0, - second = osdate.sec or 0, - }, { - __tostring = function() - if not include_time then - return "" - end - - return tostring(osdate.hour) - .. ":" - .. tostring(string.format("%02d", osdate.min)) - .. (osdate.sec ~= 0 and ("." .. tostring(osdate.sec)) or "") - end, - }) or nil, - }) - end, - - --- Parses a date and returns a table representing the date - ---@param input string #The input which should follow the date specification found in the word spec. - ---@return Date|string #The data extracted from the input or an error message - parse_date = function(input) - local weekdays = {} - for i = 1, 7 do - table.insert(weekdays, os.date("%A", os.time({ year = 2000, month = 5, day = i })):lower()) ---@diagnostic disable-line -- TODO: type error workaround - end - - local months = {} - for i = 1, 12 do - table.insert(months, os.date("%B", os.time({ year = 2000, month = i, day = 1 })):lower()) ---@diagnostic disable-line -- TODO: type error workaround - end - - local output = {} - - for word in vim.gsplit(input, "%s+") do - if Word:len() == 0 then - goto continue - end - - if Word:match("^-?%d%d%d%d+$") then - output.year = tonumber(word) - elseif Word:match("^%d+%w*$") then - output.day = tonumber(Word:match("%d+")) - elseif vim.list_contains(timezone_list, Word:upper()) then - output.timezone = Word:upper() - else - do - local hour, minute, second = vim.re.match(word, time_regex) - - if hour and minute then - output.time = setmetatable({ - hour = tonumber(hour), - minute = tonumber(minute), - second = second and tonumber(second) or nil, - }, { - __tostring = function() - return word - end, - }) - - goto continue - end - end - - do - local valid_months = {} - - -- Check for month abbreviation - for i, month in ipairs(months) do - if vim.startswith(month, Word:lower()) then - valid_months[month] = i - end - end - - local count = vim.tbl_count(valid_months) - if count > 1 then - return "Ambiguous month name! Possible interpretations: " - .. table.concat(vim.tbl_keys(valid_months), ",") - elseif count == 1 then - local valid_month_name, valid_month_number = next(valid_months) - - output.month = { - name = lib.title(valid_month_name), - number = valid_month_number, - } - - goto continue - end - end - - do - word = Word:match("^([^,]+),?$") - - local valid_weekdays = {} - - -- Check for weekday abbreviation - for i, weekday in ipairs(weekdays) do - if vim.startswith(weekday, Word:lower()) then - valid_weekdays[weekday] = i - end - end - - local count = vim.tbl_count(valid_weekdays) - if count > 1 then - return "Ambiguous weekday name! Possible interpretations: " - .. table.concat(vim.tbl_keys(valid_weekdays), ",") - elseif count == 1 then - local valid_weekday_name, valid_weekday_number = next(valid_weekdays) - - output.weekday = { - name = lib.title(valid_weekday_name), - number = valid_weekday_number, - } - - goto continue - end - end - - return "Unidentified string: `" - .. word - .. "` - make sure your locale and language are set correctly if you are using a language other than English!" - end - - ::continue:: - end - - return init.private.tostringable_date(output) - end, - - insert_date = function(insert_mode) - local function callback(input) - if input == "" or not input then - return - end - - local output - - if type(input) == "table" then - output = tostring(init.public.to_date(input)) - else - output = init.public.parse_date(input) - - if type(output) == "string" then - utils.notify(output, vim.log.levels.ERROR) - - vim.ui.input({ - prompt = "Date: ", - default = input, - }, callback) - - return - end - - output = tostring(output) - end - - vim.api.nvim_put({ "{@ " .. output .. "}" }, "c", false, true) - - if insert_mode then - vim.cmd.startinsert() - end - end - - if inits.is_mod_loaded("ui.calendar") then - vim.cmd.stopinsert() - inits.get_mod("calendar").select_date({ callback = vim.schedule_wrap(callback) }) - else - vim.ui.input({ - prompt = "Date: ", - }, callback) - end - end, -} - -init.private = { - tostringable_date = function(date_table) - return setmetatable(date_table, { - __tostring = function() - local function d(str) - return str and (tostring(str) .. " ") or "" - end - - return vim.trim( - d(date_table.weekday and date_table.weekday.name) - .. d(date_table.day) - .. d(date_table.month and date_table.month.name) - .. d(date_table.year and string.format("%04d", date_table.year)) - .. d(date_table.time and tostring(date_table.time)) - .. d(date_table.timezone) - ) - end, - }) - end, -} - -init.load = function() - vim.keymap.set("", "(word.time.insert-date)", lib.wrap(init.public.insert_date, false)) - vim.keymap.set("i", "(word.time.insert-date.insert-mode)", lib.wrap(init.public.insert_date, true)) -end - -return init diff --git a/word-scm-1.rockspec-E b/word-scm-1.rockspec-E deleted file mode 100644 index 2118fd7..0000000 --- a/word-scm-1.rockspec-E +++ /dev/null @@ -1,62 +0,0 @@ -local MODREV, SPECREV = "scm", "-1" -rockspec_format = "3.0" -package = "word.lua" -version = MODREV .. SPECREV -source = { - url = "git://github.com/clpi/word.lua", - tag = nil -} - -description = { - summary = "Extensibility of org, comfort of markdown, for everyone", - detailed = [[ - Extensibility of org, comfort of markdown, for everyone - ]], - description = [[ - Extensibility of org, comfort of markdown, for everyone - ]], - homepage = "https://github.com/clpi/word.lua", - maintainer = "https://github.com/clpi", - labels = { - "wiki", "neovim", "notes", "org", "markdown", "nvim" - }, - license = "MIT", -} - -if MODREV == "scm" then - source = { - url = "git://github.com/clpi/word.lua", - tag = nil, - branch = "master" - } -end - -dependencies = { - "lua == 5.1", - "nvim-nio ~> 1.7", - "plenary.nvim == 0.1.4", - "nui.nvim == 0.3.0", - "pathlib.nvim ~> 2.2", -} - -test_dependencies = { - "nlua", - "nvim-treesitter == 0.9.2", -} - --- test = { --- type = "command", --- command = "scripts/test.sh" --- } - -build = { - type = "builtin", - install = { - bin = { - "bin/word", - } - }, - copy_directories = { - "doc", - } -}