From d8d0c45bc348f3e99913584a659f77799e36dc8e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?L=C3=BCdecke=20Sascha?= Date: Fri, 7 Aug 2026 08:39:11 +0200 Subject: [PATCH] Retired stopwatch --- init.lua | 2 - lua/stopwatch.lua | 398 ---------------------------------------------- 2 files changed, 400 deletions(-) delete mode 100644 lua/stopwatch.lua diff --git a/init.lua b/init.lua index f71b138..47af72d 100644 --- a/init.lua +++ b/init.lua @@ -15,8 +15,6 @@ require('lsp') require('keybindings') require('layout') -require('stopwatch').setup({ big_display = true, update_ms = 1000 }) - if vim.loop.os_gethostname() == 'slaptop' then require('diary') end diff --git a/lua/stopwatch.lua b/lua/stopwatch.lua deleted file mode 100644 index 291b218..0000000 --- a/lua/stopwatch.lua +++ /dev/null @@ -1,398 +0,0 @@ --- ~/.config/nvim/lua/stopwatch.lua --- Visible stopwatch for Neovim with centered big display and clickable buttons -local M = {} -local uv = vim.loop - -local state = { - running = false, - start_hr = 0, - elapsed_ns = 0, - name = nil, - timer = nil, - bufnr = nil, - win = nil, - opts = nil, - big_mode = false, - button_spans = nil, -} - -local function ns_to_time(ns) - local s = ns / 1e9 - local h = math.floor(s / 3600) - s = s - h * 3600 - local m = math.floor(s / 60) - local sec = s - m * 60 - local secs = math.floor(sec) - local ms = math.floor((sec - secs) * 1000) - if h > 0 then - return string.format("%02d:%02d:%02d.%03d", h, m, secs, ms) - else - return string.format("%02d:%02d.%03d", m, secs, ms) - end -end - -local BIG_DIGITS = { - ["0"] = {" ███ ","█ █","█ █","█ █"," ███ "}, - ["1"] = {" █ "," ██ "," █ "," █ "," ███ "}, - ["2"] = {" ███ ","█ █"," ██ "," ██ ","█████"}, - ["3"] = {" ███ "," █"," ██ "," █"," ███ "}, - ["4"] = {"█ █ ","█ █ ","█████"," █ "," █ "}, - ["5"] = {"█████","█ ","████ "," █","████ "}, - ["6"] = {" ███ ","█ ","████ ","█ █"," ███ "}, - ["7"] = {"█████"," █"," █ "," █ "," █ "}, - ["8"] = {" ███ ","█ █"," ███ ","█ █"," ███ "}, - ["9"] = {" ███ ","█ █"," ████"," █"," ███ "}, - [":"] = {" "," ░ "," "," ░ "," "}, - [" "] = {" "," "," "," "," "}, - ["-"] = {" "," ","█████"," "," "}, - ["."] = {" "," "," "," "," ░ "}, -} - -local function big_time_lines(timestr) - local rows = {"","","","",""} - for i = 1, #timestr do - local ch = timestr:sub(i,i) - local pat = BIG_DIGITS[ch] or BIG_DIGITS[" "] - for r = 1,5 do - rows[r] = rows[r] .. pat[r] .. " " - end - end - return rows -end - -local function get_elapsed_ns() - if state.running then - return state.elapsed_ns + (uv.hrtime() - state.start_hr) - else - return state.elapsed_ns - end -end - -local function ensure_win() - if state.win and vim.api.nvim_win_is_valid(state.win) then return end - if not state.bufnr or not vim.api.nvim_buf_is_valid(state.bufnr) then - state.bufnr = vim.api.nvim_create_buf(false, true) - vim.api.nvim_buf_set_option(state.bufnr, "bufhidden", "wipe") - vim.api.nvim_buf_set_option(state.bufnr, "modifiable", false) - vim.api.nvim_buf_set_option(state.bufnr, "filetype", "stopwatch") - end - - local width = state.opts.width or 28 - local height = 1 - if state.opts.big_display or state.big_mode then - local chars = state.opts.big_chars or 8 - local char_width = 6 - width = chars * char_width - height = 8 - height = height + 1 -- plus button line - else - height = 1 + 1 -- one line time + button line - end - - local col = (vim.o.columns - width) - (state.opts.margin_right or 2) - local row = state.opts.row or 1 - - local win_opts = { - relative = "editor", - anchor = "NW", - row = row, - col = math.max(0, col), - width = math.min(width, vim.o.columns - 1), - height = height, - style = "minimal", - focusable = true, -- <-- must be focusable for clicks to work reliably - noautocmd = true, - } - - if state.win and vim.api.nvim_win_is_valid(state.win) then - pcall(vim.api.nvim_win_close, state.win, true) - end - state.win = vim.api.nvim_open_win(state.bufnr, false, win_opts) - vim.api.nvim_win_set_option(state.win, "winblend", state.opts.winblend or 5) - vim.api.nvim_buf_set_option(state.bufnr, "buftype", "nofile") - vim.api.nvim_buf_set_option(state.bufnr, "wrap", false) - - -- buffer-local mouse mapping: LeftMouse triggers click handler - pcall(function() - vim.api.nvim_buf_set_keymap(state.bufnr, "n", "", "lua require('stopwatch')._on_click()", {nowait=true, noremap=true, silent=true}) - end) -end - -local function close_win() - if state.win and vim.api.nvim_win_is_valid(state.win) then - pcall(vim.api.nvim_win_close, state.win, true) - end - state.win = nil -end - -local function build_button_line() - local btns = { "> Toggle", "O Reset", "X Hide" } - local sep = " " - local line = table.concat(btns, sep) - local spans = {} - local cur = 1 - for i, b in ipairs(btns) do - local s = line:find(b, cur, true) - if s then - local e = s + #b - 1 - table.insert(spans, {name = b, s = s, e = e}) - cur = e + #sep + 1 - end - end - return line, spans -end - -local function update_win() - if not state.opts.visible then return end - ensure_win() - if not state.bufnr or not vim.api.nvim_buf_is_valid(state.bufnr) then return end - - local lines = {} - local label = state.name and ("[" .. state.name .. "] ") or "" - - if state.opts.big_display or state.big_mode then - -- compute time string - local total_s = math.floor(get_elapsed_ns() / 1e9) - local h = math.floor(total_s / 3600) - local m = math.floor((total_s % 3600) / 60) - local s = total_s % 60 - local timestr - if h > 0 then - timestr = string.format("%02d:%02d:%02d", h, m, s) - else - timestr = string.format("%02d:%02d", m, s) - end - - local b_lines = big_time_lines(timestr) - -- center horizontally inside window by adding left padding - local win_w = vim.api.nvim_win_get_width(state.win) - local char_width = 6 -- matches BIG_DIGITS width+gap - local displayed_w = #timestr * char_width - local leftpad = math.max(0, math.floor((win_w - displayed_w) / 2)) - local pad = string.rep(" ", leftpad) - - table.insert(lines, "") -- top blank line - for _, l in ipairs(b_lines) do table.insert(lines, pad .. l) end - table.insert(lines, "") -- blank between time and buttons - local btn_line, spans = build_button_line() - btn_line = pad .. btn_line - -- adjust spans by leftpad - for _, sp in ipairs(spans) do - sp.s = sp.s + leftpad - sp.e = sp.e + leftpad - end - table.insert(lines, btn_line) - table.insert(lines, "") -- bottom blank - state.button_spans = spans - - else - local status_sym = state.running and "⏱ " or "⏸ " - local txt = status_sym .. label .. ns_to_time(get_elapsed_ns()) - local win_w = vim.api.nvim_win_get_width(state.win) - local strw = vim.fn.strwidth(txt) - local leftpad = math.max(0, math.floor((win_w - strw) / 2)) - local pad = string.rep(" ", leftpad) - - table.insert(lines, "") -- top blank - table.insert(lines, pad .. txt) - table.insert(lines, "") -- blank between time and buttons - local btn_line, spans = build_button_line() - btn_line = pad .. btn_line - for _, sp in ipairs(spans) do - sp.s = sp.s + leftpad - sp.e = sp.e + leftpad - end - table.insert(lines, btn_line) - table.insert(lines, "") -- bottom blank - state.button_spans = spans - end - - vim.api.nvim_buf_set_option(state.bufnr, "modifiable", true) - vim.api.nvim_buf_set_lines(state.bufnr, 0, -1, false, lines) - vim.api.nvim_buf_set_option(state.bufnr, "modifiable", false) -end - -local function start_timer_loop() - if state.timer then return end - state.timer = uv.new_timer() - state.timer:start(0, state.opts.update_ms or 250, vim.schedule_wrap(function() - if state.win and not vim.api.nvim_win_is_valid(state.win) then - state.win = nil - end - update_win() - end)) -end - -local function stop_timer_loop() - if state.timer then - pcall(function() state.timer:stop() end) - state.timer:close() - state.timer = nil - end -end - -function M.start(name) - if state.running then return end - state.name = name or state.name - state.start_hr = uv.hrtime() - state.elapsed_ns - state.running = true - if state.opts.visible then ensure_win() end - start_timer_loop() - update_win() -end - -function M.stop() - if not state.running then return end - state.elapsed_ns = get_elapsed_ns() - state.running = false - stop_timer_loop() - update_win() - if state.opts.log_on_stop then - local log_path = state.opts.log_path or (vim.fn.stdpath("data") .. "/stopwatch_log.md") - local time_str = os.date("%Y-%m-%d %H:%M:%S") - local name = state.name or "-" - local duration = ns_to_time(state.elapsed_ns) - local line = string.format("- %s | %s | %s\n", time_str, name, duration) - local f = io.open(log_path, "a") - if f then f:write(line) f:close() end - end -end - - --- Pause (anhalten, aber nicht als "Stop"/Log) -function M.pause() - if not state.running then return end - state.elapsed_ns = get_elapsed_ns() - state.running = false - stop_timer_loop() - update_win() -end - --- Resume (fortsetzen) -function M.resume() - if state.running then return end - state.start_hr = uv.hrtime() - state.elapsed_ns - state.running = true - start_timer_loop() - update_win() -end - --- Toggle zwischen Pause und Resume (für Button/Command) -function M.toggle(name) - if state.running then - M.pause() - else - -- optional: wenn name übergeben wurde, setze neuen Namen - if name and name ~= "" then state.name = name end - M.resume() - end -end - - -function M.reset() - state.running = false - state.elapsed_ns = 0 - state.start_hr = 0 - stop_timer_loop() - update_win() -end - -function M.show() - state.opts.visible = true - ensure_win() - start_timer_loop() - update_win() -end - -function M.hide() - state.opts.visible = false - close_win() - stop_timer_loop() -end - -function M.log_now() - local log_path = state.opts.log_path or (vim.fn.stdpath("data") .. "/stopwatch_log.md") - local time_str = os.date("%Y-%m-%d %H:%M:%S") - local name = state.name or "-" - local duration = ns_to_time(get_elapsed_ns()) - local line = string.format("- %s | %s | %s\n", time_str, name, duration) - local f = io.open(log_path, "a") - if f then f:write(line) f:close() end -end - -function M.status() - local elapsed = get_elapsed_ns() - local prefix = state.running and "⏱" or "⏸" - local name = state.name and (" " .. state.name) or "" - return prefix .. name .. " " .. ns_to_time(elapsed) -end - -function M.toggle_big() - state.big_mode = not state.big_mode - update_win() -end - --- Mouse click handler -function M._on_click() - -- only proceed when our floating window is focused - local cur = vim.api.nvim_get_current_win() - if cur ~= state.win then return end - - local pos = vim.fn.getmousepos() - if not pos then return end - local click_line = pos.line - local col = pos.column - - -- button line is the second-to-last line because we add a bottom blank line - local total = vim.api.nvim_buf_line_count(state.bufnr) - local btn_line_index = total - 1 - if click_line ~= btn_line_index then return end - if not state.button_spans then return end - - for _, sp in ipairs(state.button_spans) do - if col >= sp.s and col <= sp.e then - local name = sp.name - if name:find("Toggle",1,true) then M.toggle() - elseif name:find("Hide",1,true) then M.hide() - elseif name:find("Reset",1,true) then M.reset() end - - -- visual feedback - pcall(function() - local ns = vim.api.nvim_create_namespace("stopwatch_click") - vim.api.nvim_buf_add_highlight(state.bufnr, ns, "Visual", btn_line_index-1, sp.s-1, sp.e) - vim.defer_fn(function() vim.api.nvim_buf_clear_namespace(state.bufnr, ns, 0, -1) end, 120) - end) - break - end - end -end - -function M.setup(opts) - opts = opts or {} - state.opts = vim.tbl_extend("keep", opts, { - visible = true, - update_ms = 250, - width = 28, - row = 1, - margin_right = 2, - winblend = 5, - log_on_stop = false, - log_path = nil, - big_display = false, - big_chars = 8, - }) - state.big_mode = state.opts.big_display or false - - vim.api.nvim_create_user_command("StopwatchStart", function(opts) M.start(opts.args ~= "" and opts.args or nil) end, { nargs = "?" }) - vim.api.nvim_create_user_command("StopwatchStop", function() M.stop() end, {}) - vim.api.nvim_create_user_command("StopwatchToggle", function(opts) M.toggle(opts.args ~= "" and opts.args or nil) end, { nargs = "?" }) - vim.api.nvim_create_user_command("StopwatchReset", function() M.reset() end, {}) - vim.api.nvim_create_user_command("StopwatchShow", function() M.show() end, {}) - vim.api.nvim_create_user_command("StopwatchHide", function() M.hide() end, {}) - vim.api.nvim_create_user_command("StopwatchLog", function() M.log_now() end, {}) - vim.api.nvim_create_user_command("StopwatchToggleBig", function() M.toggle_big() end, {}) - - if state.opts.visible then ensure_win() update_win() end -end - -return M