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 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 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 end function M.save_or_create_dispatcher() 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 content == "" then vim.notify("Memo is empty, not sending.", vim.log.levels.WARN) return 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 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.page_size, 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_" .. vim.fn.strftime("%s")) M.setup_buffer_for_editing() end -- 【新增】创建居中浮动窗口的辅助函数 local function create_float_window(buf) local width = math.floor(vim.o.columns * (config.window.width or 0.8)) local height = math.floor(vim.o.lines * (config.window.height or 0.8)) -- 计算居中位置 local row = math.floor((vim.o.lines - height) / 2) local col = math.floor((vim.o.columns - width) / 2) local opts = { relative = "editor", row = row, col = col, width = width, height = height, style = "minimal", border = config.window.border or "rounded", title = " Memos ", title_pos = "center", } return vim.api.nvim_open_win(buf, true, opts) end function M.show_memos_list(filter) current_filter = filter local should_create_buf = true -- 检查 buffer 是否存在且有效 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(false, true) -- 改为 false, true (unlisted, scratch) vim.api.nvim_buf_set_name(buf_id, "MemosList") 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 -- 检查该 buffer 是否已经在一个窗口中打开 local win_id = vim.fn.bufwinid(buf_id) if win_id ~= -1 then vim.api.nvim_set_current_win(win_id) else -- 【修改】根据配置决定打开方式 if config.window and config.window.enable_float then create_float_window(buf_id) else -- 传统方式:直接切换到该 buffer vim.api.nvim_set_current_buf(buf_id) 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.page_size, 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) -- 【修改】这个函数现在可以处理单个按键(字符串)或多个按键(table) local function set_keymap(keys, command) if not keys then return end if type(keys) == "table" then -- 如果是 table,就为里面的每个按键都设置映射 for _, key in ipairs(keys) do if key and key ~= "" then vim.api.nvim_buf_set_keymap(buf_id, "n", key, command, { noremap = true, silent = true }) end end else -- 如果只是字符串,就按原来的方式设置 if keys and keys ~= "" then vim.api.nvim_buf_set_keymap(buf_id, "n", keys, command, { noremap = true, silent = true }) end 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 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.page_size, 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 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 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 end function M.search_memos() vim.ui.input({ prompt = "Search Memos: " }, function(input) M.show_memos_list(input or "") end) end return M