From cb9fadd5eb0259e78b17e966c85f76d4533bd8f3 Mon Sep 17 00:00:00 2001 From: Elflare <271374942@qq.com> Date: Sun, 6 Jul 2025 21:26:21 +0800 Subject: [PATCH] fix bugs about create and save new memo --- lua/memos/ui.lua | 548 +++++++++++++++++++++++------------------------ 1 file changed, 270 insertions(+), 278 deletions(-) diff --git a/lua/memos/ui.lua b/lua/memos/ui.lua index 4632fb0..a8280b5 100644 --- a/lua/memos/ui.lua +++ b/lua/memos/ui.lua @@ -1,326 +1,318 @@ -local api = require('memos.api') -local config = require('memos').config +local api = require("memos.api") +local config = require("memos").config local M = {} --- 存放状态的变量 local memos_cache = {} local buf_id = nil local current_page_token = nil local current_user = nil local current_filter = nil --- =================================================================== --- 首先定义所有会被其他函数调用的 local "辅助" 函数 --- =================================================================== - --- 渲染 Memos 列表到 buffer -local function render_memos(data, append) - vim.schedule(function() - if not buf_id or not vim.api.nvim_buf_is_valid(buf_id) then - return - end - local new_memos = data.memos or {} - current_page_token = data.nextPageToken or "" - if append then - memos_cache = vim.list_extend(memos_cache, new_memos) - else - memos_cache = new_memos - end - local lines = {} - local k = config.keymaps.list - if #memos_cache == 0 then - local help = string.format("No memos found. Press '%s' to refresh, '%s' to add, or '%s' to quit.", - k.refresh_list, k.add_memo, k.quit) - table.insert(lines, help) - else - for i, memo in ipairs(memos_cache) do - local first_line = memo.content:match("^[^\n]*") - local display_time = memo.displayTime:sub(1, 10) - table.insert(lines, string.format("%d. [%s] %s", i, display_time, first_line)) - end - end - if current_page_token ~= "" then - table.insert(lines, "...") - table.insert(lines, string.format("(Press '%s' to load more)", k.next_page)) - end - vim.api.nvim_buf_set_option(buf_id, 'modifiable', true) - vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines) - vim.api.nvim_buf_set_option(buf_id, 'modifiable', false) - end) +function M.render_memos(data, append) + vim.schedule(function() + if not buf_id or not vim.api.nvim_buf_is_valid(buf_id) then + return + end + local new_memos = data.memos or {} + current_page_token = data.nextPageToken or "" + if append then + memos_cache = vim.list_extend(memos_cache, new_memos) + else + memos_cache = new_memos + end + local lines = {} + local k = config.keymaps.list + if #memos_cache == 0 then + local help = string.format( + "No memos found. Press '%s' to refresh, '%s' to add, or '%s' to quit.", + k.refresh_list, + k.add_memo, + k.quit + ) + table.insert(lines, help) + else + for i, memo in ipairs(memos_cache) do + local first_line = memo.content:match("^[^\n]*") + local display_time = memo.displayTime:sub(1, 10) + table.insert(lines, string.format("%d. [%s] %s", i, display_time, first_line)) + end + end + if current_page_token ~= "" then + table.insert(lines, "...") + table.insert(lines, string.format("(Press '%s' to load more)", k.next_page)) + end + vim.api.nvim_buf_set_option(buf_id, "modifiable", true) + vim.api.nvim_buf_set_lines(buf_id, 0, -1, false, lines) + vim.api.nvim_buf_set_option(buf_id, "modifiable", false) + end) +end + +function M.setup_buffer_for_editing() + vim.bo.buftype = "nofile" + vim.bo.bufhidden = "wipe" + vim.bo.swapfile = false + vim.bo.buflisted = true + vim.bo.filetype = "markdown" + + vim.b.memos_original_content = table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), "\n") + + local save_key_string = "" + if config.keymaps.buffer.save and config.keymaps.buffer.save ~= "" then + save_key_string = string.format(" or %s", config.keymaps.buffer.save) + end + + if vim.b.memos_memo_name then + vim.notify("Editing memo. Use :MemosSave" .. save_key_string .. " to save.") + else + vim.notify("📝 New memo. Use :MemosSave" .. save_key_string .. " to create.") + end + + vim.api.nvim_buf_create_user_command(0, "MemosSave", 'lua require("memos.ui").save_or_create_dispatcher()', {}) + if config.keymaps.buffer.save and config.keymaps.buffer.save ~= "" then + vim.api.nvim_buf_set_keymap( + 0, + "n", + config.keymaps.buffer.save, + "MemosSave", + { noremap = true, silent = true } + ) + end + + if config.auto_save then + local group = vim.api.nvim_create_augroup("MemosAutoSave", { clear = true }) + vim.api.nvim_create_autocmd("InsertLeave", { + group = group, + buffer = 0, + callback = function() + M.check_and_auto_save() + end, + }) + vim.api.nvim_create_autocmd("CursorHold", { + group = group, + buffer = 0, + callback = function() + M.check_and_auto_save() + end, + }) + end +end + +function M.open_memo_for_edit(memo, open_cmd) + local first_line = memo.content:match("^[^\n]*") + local buffer_name = "memos/" + .. memo.name:gsub("memos/", "") + .. "/" + .. first_line:gsub("[/\\]", "_"):sub(1, 50) + .. ".md" + local existing_bufnr = vim.fn.bufnr(buffer_name) + + if existing_bufnr ~= -1 and vim.api.nvim_buf_is_loaded(existing_bufnr) then + local win_id = vim.fn.bufwinid(existing_bufnr) + if win_id ~= -1 then + vim.api.nvim_set_current_win(win_id) + else + vim.api.nvim_set_current_buf(existing_bufnr) + end + else + vim.cmd(open_cmd) + vim.api.nvim_buf_set_name(0, buffer_name) + vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(memo.content, "\n")) + vim.b.memos_memo_name = memo.name + M.setup_buffer_for_editing() + end end --- 设置编辑 buffer 的通用函数 --- 【新增】检查内容是否变化并自动保存的函数 function M.check_and_auto_save() - -- 检查是否存在原始内容快照 - if vim.b.memos_original_content == nil then return end - - local current_content = table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n') - if vim.b.memos_original_content ~= current_content then - M.save_or_create_dispatcher() - end + if vim.b.memos_original_content == nil then + return + end + local current_content = table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), "\n") + if vim.b.memos_original_content ~= current_content then + M.save_or_create_dispatcher() + end end --- 【修改】setup_buffer_for_editing 现在使用全新的自动保存逻辑 -local function setup_buffer_for_editing() - vim.bo.buftype = 'nofile' - vim.bo.bufhidden = 'wipe' - vim.bo.swapfile = false - vim.bo.buflisted = true - vim.bo.filetype = 'markdown' - - -- 【新增】在 buffer 打开时,立即创建内容快照 - vim.b.memos_original_content = table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n') - - local save_key_string = "" - if config.keymaps.buffer.save and config.keymaps.buffer.save ~= "" then - save_key_string = string.format(" or %s", config.keymaps.buffer.save) - end - - if vim.b.memos_memo_name then - vim.notify("Editing memo. Use :MemosSave" .. save_key_string .. " to save.") - else - vim.notify("📝 New memo. Use :MemosSave" .. save_key_string .. " to create.") - end - - vim.api.nvim_buf_create_user_command(0, 'MemosSave', 'lua require("memos.ui").save_or_create_dispatcher()', {}) - if config.keymaps.buffer.save and config.keymaps.buffer.save ~= "" then - vim.api.nvim_buf_set_keymap(0, 'n', config.keymaps.buffer.save, 'MemosSave', { noremap = true, silent = true }) - end - - -- 【修改】设置新的自动命令 - if config.auto_save then - local group = vim.api.nvim_create_augroup('MemosAutoSave', { clear = true }) - -- 离开插入模式时检查 - vim.api.nvim_create_autocmd('InsertLeave', { - group = group, - buffer = 0, - callback = M.check_and_auto_save, - }) - -- 光标静止时检查 - vim.api.nvim_create_autocmd('CursorHold', { - group = group, - buffer = 0, - callback = M.check_and_auto_save, - }) - end -end - --- 【修改】保存成功后,需要更新内容快照 function M.save_or_create_dispatcher() - local memo_name = vim.b.memos_memo_name - local current_content = table.concat(vim.api.nvim_buf_get_lines(0, 0, -1, false), '\n') + local bufnr_to_save = vim.api.nvim_get_current_buf() + local memo_name = vim.b.memos_memo_name + local content = table.concat(vim.api.nvim_buf_get_lines(bufnr_to_save, 0, -1, false), "\n") - if current_content == '' then - vim.notify("Memo is empty, not sending.", vim.log.levels.WARN) - return - end + if content == "" then + vim.notify("Memo is empty, not sending.", vim.log.levels.WARN) + return + end - if memo_name then - api.update_memo(memo_name, current_content, function(success) - if success then - vim.schedule(function() - vim.notify("✅ Memo updated successfully!") - -- 更新快照为最新内容 - vim.b.memos_original_content = current_content - end) - M.refresh_list_silently() - end - end) - else - api.create_memo(current_content, function(new_memo) - if new_memo and new_memo.name then - vim.schedule(function() - vim.notify("✅ Memo created successfully!") - vim.cmd('bdelete!') - open_memo_for_edit(new_memo, 'enew') - M.refresh_list_silently() - end) - else - vim.schedule(function() vim.notify("❌ Failed to create memo.", vim.log.levels.ERROR) end) - end - end) - end + if memo_name then + -- 更新逻辑 (保持不变,工作正常) + api.update_memo(memo_name, content, function(success) + if success then + vim.schedule(function() + vim.notify("✅ Memo updated successfully!") + if vim.api.nvim_buf_is_valid(bufnr_to_save) then + vim.b[bufnr_to_save].memos_original_content = content + end + end) + M.refresh_list_silently() + end + end) + else + api.create_memo(content, function(new_memo) + if new_memo and new_memo.name then + vim.schedule(function() + vim.notify("✅ Memo created successfully!") + M.show_memos_list(current_filter) + -- 立即重新打开刚刚创建的 memo,进入编辑模式 + vim.schedule(function() + M.open_memo_for_edit(new_memo, "enew") + end) + end) + else + vim.schedule(function() + vim.notify("❌ Failed to create memo.", vim.log.levels.ERROR) + end) + end + end) + end end --- 打开一个已存在的 Memo -local function open_memo_for_edit(memo, open_cmd) - -- 1. 根据 memo 信息,构造一个唯一的 buffer 名称 - local first_line = memo.content:match("^[^\n]*") - local buffer_name = "memos/" .. memo.name:gsub("memos/", "") .. "/" .. first_line:gsub("[/\\]", "_"):sub(1, 50) .. - ".md" - - -- 2. 检查这个名字的 buffer 是否已经存在 - local existing_bufnr = vim.fn.bufnr(buffer_name) - - if existing_bufnr ~= -1 and vim.api.nvim_buf_is_loaded(existing_bufnr) then - -- 3. 如果已存在,则找到它所在的窗口并跳转过去 - local win_id = vim.fn.bufwinid(existing_bufnr) - if win_id ~= -1 then - vim.api.nvim_set_current_win(win_id) - else - -- 如果窗口已关闭但 buffer 仍在,则在当前窗口打开它 - vim.api.nvim_set_current_buf(existing_bufnr) - end - else - -- 4. 如果不存在,才执行我们之前的创建新窗口的逻辑 - vim.cmd(open_cmd) - vim.api.nvim_buf_set_name(0, buffer_name) - vim.api.nvim_buf_set_lines(0, 0, -1, false, vim.split(memo.content, '\n')) - vim.b.memos_memo_name = memo.name - setup_buffer_for_editing() - end -end - --- =================================================================== --- 然后定义所有对外暴露的 M.xxx 函数,确保它们能访问到上面的 local 函数 --- =================================================================== - --- 【修正】将此函数移到 render_memos 定义之后,解决报错 function M.refresh_list_silently() - if not current_user or not current_user.name then - return - end - api.list_memos(current_user.name, current_filter, config.pageSize, nil, function(data) - render_memos(data, false) - end) + if not current_user or not current_user.name then + return + end + api.list_memos(current_user.name, current_filter, config.pageSize, nil, function(data) + M.render_memos(data, false) + end) end - function M.create_memo_in_buffer() - vim.cmd('enew') - vim.b.memos_memo_name = nil - vim.api.nvim_buf_set_name(0, "memos/new_memo.md") - setup_buffer_for_editing() + vim.cmd("enew") + vim.b.memos_memo_name = nil + -- 使用一个带时间戳的、独一无二的临时名字,防止冲突 + vim.api.nvim_buf_set_name(0, "memos/new_memo_" .. vim.fn.strftime("%s")) + M.setup_buffer_for_editing() end function M.show_memos_list(filter) - current_filter = filter - local should_create_buf = true - if buf_id and vim.api.nvim_buf_is_valid(buf_id) then - should_create_buf = false - else - buf_id = vim.api.nvim_create_buf(true, true) -- listed = true, scratch = true - vim.api.nvim_buf_set_name(buf_id, " Memos") - vim.bo[buf_id].buftype = 'nofile' - vim.bo[buf_id].swapfile = false - vim.bo[buf_id].filetype = 'memos_list' - vim.bo[buf_id].modifiable = false - end + current_filter = filter + local should_create_buf = true + if buf_id and vim.api.nvim_buf_is_valid(buf_id) then + should_create_buf = false + else + buf_id = vim.api.nvim_create_buf(true, true) + vim.api.nvim_buf_set_name(buf_id, " Memos") + vim.bo[buf_id].buftype = "nofile" + vim.bo[buf_id].swapfile = false + vim.bo[buf_id].filetype = "memos_list" + vim.bo[buf_id].modifiable = false + end - -- 【修正】确保跳转到列表窗口,并且是在当前窗口打开 - local win_id = vim.fn.bufwinid(buf_id) - if win_id ~= -1 then - vim.api.nvim_set_current_win(win_id) - else - vim.api.nvim_set_current_buf(buf_id) - end + local win_id = vim.fn.bufwinid(buf_id) + if win_id ~= -1 then + vim.api.nvim_set_current_win(win_id) + else + vim.api.nvim_set_current_buf(buf_id) + end - vim.schedule(function() - vim.notify("Getting user info...") - end) - api.get_current_user(function(user) - if user and user.name then - current_user = user - vim.schedule(function() - vim.notify("Fetching memos for " .. user.name .. "...") - end) - api.list_memos(user.name, current_filter, config.pageSize, nil, function(data) - render_memos(data, false) - end) - else - vim.schedule(function() - vim.notify("Could not get user, aborting fetch.", vim.log.levels.ERROR) - end) - end - end) + vim.schedule(function() + vim.notify("Getting user info...") + end) + api.get_current_user(function(user) + if user and user.name then + current_user = user + vim.schedule(function() + vim.notify("Fetching memos for " .. user.name .. "...") + end) + api.list_memos(user.name, current_filter, config.pageSize, nil, function(data) + M.render_memos(data, false) + end) + else + vim.schedule(function() + vim.notify("Could not get user, aborting fetch.", vim.log.levels.ERROR) + end) + end + end) - local function set_keymap(key, command) - if key and key ~= "" then - vim.api.nvim_buf_set_keymap(buf_id, 'n', key, command, { - noremap = true, - silent = true - }) - end - end + local function set_keymap(key, command) + if key and key ~= "" then + vim.api.nvim_buf_set_keymap(buf_id, "n", key, command, { noremap = true, silent = true }) + end + end - if config.keymaps and config.keymaps.list then - local list_keymaps = config.keymaps.list - set_keymap(list_keymaps.edit_memo, 'lua require("memos.ui").edit_selected_memo()') - set_keymap(list_keymaps.vsplit_edit_memo, 'lua require("memos.ui").edit_selected_memo_in_vsplit()') - set_keymap(list_keymaps.quit, 'bdelete!') - set_keymap(list_keymaps.search_memos, 'lua require("memos.ui").search_memos()') - set_keymap(list_keymaps.refresh_list, 'lua require("memos.ui").show_memos_list()') - set_keymap(list_keymaps.next_page, 'lua require("memos.ui").load_next_page()') - set_keymap(list_keymaps.add_memo, 'lua require("memos.ui").create_memo_in_buffer()') - set_keymap(list_keymaps.delete_memo, 'lua require("memos.ui").confirm_delete_memo()') - set_keymap(list_keymaps.delete_memo_visual, 'lua require("memos.ui").confirm_delete_memo()') - end + if config.keymaps and config.keymaps.list then + local list_keymaps = config.keymaps.list + set_keymap(list_keymaps.edit_memo, 'lua require("memos.ui").edit_selected_memo()') + set_keymap(list_keymaps.vsplit_edit_memo, 'lua require("memos.ui").edit_selected_memo_in_vsplit()') + set_keymap(list_keymaps.quit, "bdelete!") + set_keymap(list_keymaps.search_memos, 'lua require("memos.ui").search_memos()') + set_keymap(list_keymaps.refresh_list, 'lua require("memos.ui").show_memos_list()') + set_keymap(list_keymaps.next_page, 'lua require("memos.ui").load_next_page()') + set_keymap(list_keymaps.add_memo, 'lua require("memos.ui").create_memo_in_buffer()') + set_keymap(list_keymaps.delete_memo, 'lua require("memos.ui").confirm_delete_memo()') + set_keymap(list_keymaps.delete_memo_visual, 'lua require("memos.ui").confirm_delete_memo()') + end end function M.load_next_page() - if not current_page_token or current_page_token == "" then - vim.notify("No more pages to load.", vim.log.levels.INFO) - return - end - if not current_user or not current_user.name then - vim.notify("User info not available.", vim.log.levels.WARN) - return - end - vim.schedule(function() - vim.notify("Loading next page...") - end) - api.list_memos(current_user.name, current_filter, config.pageSize, current_page_token, function(data) - render_memos(data, true) - end) + if not current_page_token or current_page_token == "" then + vim.notify("No more pages to load.", vim.log.levels.INFO) + return + end + if not current_user or not current_user.name then + vim.notify("User info not available.", vim.log.levels.WARN) + return + end + vim.schedule(function() + vim.notify("Loading next page...") + end) + api.list_memos(current_user.name, current_filter, config.pageSize, current_page_token, function(data) + M.render_memos(data, true) + end) end function M.edit_selected_memo() - local line_num = vim.api.nvim_win_get_cursor(0)[1] - local selected_memo = memos_cache[line_num] - if selected_memo then - open_memo_for_edit(selected_memo, 'enew') - end + local line_num = vim.api.nvim_win_get_cursor(0)[1] + local selected_memo = memos_cache[line_num] + if selected_memo then + M.open_memo_for_edit(selected_memo, "enew") + end end function M.edit_selected_memo_in_vsplit() - local line_num = vim.api.nvim_win_get_cursor(0)[1] - local selected_memo = memos_cache[line_num] - if selected_memo then - open_memo_for_edit(selected_memo, 'vsplit | enew') - end + local line_num = vim.api.nvim_win_get_cursor(0)[1] + local selected_memo = memos_cache[line_num] + if selected_memo then + M.open_memo_for_edit(selected_memo, "vsplit | enew") + end end function M.confirm_delete_memo() - local line_num = vim.api.nvim_win_get_cursor(0)[1] - local selected_memo = memos_cache[line_num] - if not selected_memo then - return - end - local choice = vim.fn.confirm("Delete this memo?\n[" .. selected_memo.content:sub(1, 50) .. "...]", "&Yes\n&No", 2) - if choice == 1 then - api.delete_memo(selected_memo.name, function(success) - if success then - vim.schedule(function() - vim.notify("✅ Memo deleted.") - M.show_memos_list(current_filter) - end) - else - vim.schedule(function() - vim.notify("❌ Failed to delete memo.", vim.log.levels.ERROR) - end) - end - end) - end + local line_num = vim.api.nvim_win_get_cursor(0)[1] + local selected_memo = memos_cache[line_num] + if not selected_memo then + return + end + local choice = vim.fn.confirm("Delete this memo?\n[" .. selected_memo.content:sub(1, 50) .. "...]", "&Yes\n&No", 2) + if choice == 1 then + api.delete_memo(selected_memo.name, function(success) + if success then + vim.schedule(function() + vim.notify("✅ Memo deleted.") + M.show_memos_list(current_filter) + end) + else + vim.schedule(function() + vim.notify("❌ Failed to delete memo.", vim.log.levels.ERROR) + end) + end + end) + end end function M.search_memos() - vim.ui.input({ - prompt = "Search Memos: " - }, function(input) - M.show_memos_list(input or "") - end) + vim.ui.input({ prompt = "Search Memos: " }, function(input) + M.show_memos_list(input or "") + end) end return M