all repos

gopher.nvim @ fbf6441f3d713b03fa3877a917f4a4dcef6d9362

Minimalistic plugin for Go development
10 files changed, 213 insertions(+), 31 deletions(-)
feat: add logger (#64)

* refactor(health): keep in mind new way of health check (#63)

* feat(log): add logger module

* refactor(utils): remove unused code

* refactor(log, utils): get plugin name from config

* refactor(logger): add some type annotations

* refactor(utils): log notifications

* feat: LOGGERâ„¢

* feat(config): TYPES

* refactor(log): dont give a thing about var that is not even declared

* feat(log): add easy way to open log

* refactor(log): some types

* update types

* docs: regen

* fix(log): make setting log level by config work

* feat(iferr): write error to log file if occur

* feat(gotests): add logger
Author: Smirnov Oleksandr ss2316544@gmail.com
Committed by: GitHub noreply@github.com
Committed at: 2024-06-15 12:18:26 +0300
Parent: 65fa148
M doc/gopher.nvim.txt

@@ -20,13 +20,15 @@ Setup `nvim-dap` for Go......................................|gopher.nvim-dap|

------------------------------------------------------------------------------ *gopher.nvim-setup* - `gopher.setup` + `gopher.setup`({user_config}) Setup function. This method simply merges default configs with opts table. You can read more about configuration at |gopher.nvim-config| Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults| Usage ~ `require("gopher").setup {}` (replace `{}` with your `config` table) +Parameters ~ +{user_config} gopher.Config ------------------------------------------------------------------------------ *gopher.nvim-install-deps*

@@ -50,6 +52,10 @@ >lua

local default_config = { --minidoc_replace_end + -- log level, you might consider using DEBUG or TRACE for degugging the plugin + ---@type number + log_level = vim.log.levels.INFO, + -- user specified paths to binaries ---@class gopher.ConfigCommand commands = {

@@ -80,12 +86,6 @@ }

< Class ~ {gopher.Config} - ------------------------------------------------------------------------------- - *config.setup()* - `config.setup`({user_config}) -Parameters ~ -{user_config} `(optional)` gopher.Config ==============================================================================
M lua/gopher/_utils/init.lua

@@ -1,23 +1,15 @@

+local c = require "gopher.config" +local log = require "gopher._utils.log" local utils = {} -local TITLE = "gopher.nvim" - ----@param t table ----@return boolean -function utils.is_tbl_empty(t) - if t == nil then - return true - end - return next(t) == nil -end - ---@param msg string ---@param lvl number function utils.deferred_notify(msg, lvl) vim.defer_fn(function() vim.notify(msg, lvl, { - title = TITLE, + title = c.___plugin_name, }) + log.debug(msg) end, 0) end

@@ -26,8 +18,9 @@ ---@param lvl? number

function utils.notify(msg, lvl) lvl = lvl or vim.log.levels.INFO vim.notify(msg, lvl, { - title = TITLE, + title = c.___plugin_name, }) + log.debug(msg) end -- safe require
A lua/gopher/_utils/log.lua

@@ -0,0 +1,170 @@

+-- thanks https://github.com/tjdevries/vlog.nvim +-- and https://github.com/williamboman/mason.nvim +-- for the code i have stolen(or have inspected by idk) +local c = require "gopher.config" + +---@class Gopher.Logger +---@field get_outfile fun():string +---@field trace fun(...) +---@field fmt_trace fun(...) +---@field debug fun(...) +---@field fmt_debug fun(...) +---@field info fun(...) +---@field fmt_info fun(...) +---@field warn fun(...) +---@field fmt_warn fun(...) +---@field error fun(...) +---@field fmt_error fun(...) + +local config = { + -- Name of the plugin. Prepended to log messages + name = c.___plugin_name, + + -- Should print the output to neovim while running + -- values: 'sync','async',false + use_console = vim.env.GOPHER_VERBOSE_LOGS == "1", + + -- Should highlighting be used in console (using echohl) + highlights = true, + + -- Should write to a file + use_file = true, + + -- Level configuration + modes = { + { name = "trace", hl = "Comment", level = vim.log.levels.TRACE }, + { name = "debug", hl = "Comment", level = vim.log.levels.DEBUG }, + { name = "info", hl = "None", level = vim.log.levels.INFO }, + { name = "warn", hl = "WarningMsg", level = vim.log.levels.WARN }, + { name = "error", hl = "ErrorMsg", level = vim.log.levels.ERROR }, + }, + + -- Can limit the number of decimals displayed for floats + float_precision = 0.01, +} + +---@type Gopher.Logger +---@diagnostic disable-next-line: missing-fields +local log = {} + +---@return string +function log.get_outfile() + return table.concat { + (vim.fn.has "nvim-0.8.0" == 1) and vim.fn.stdpath "log" or vim.fn.stdpath "cache", + ("/%s.log"):format(config.name), + } +end + +-- selene: allow(incorrect_standard_library_use) +local unpack = unpack or table.unpack + +do + local round = function(x, increment) + increment = increment or 1 + x = x / increment + return (x > 0 and math.floor(x + 0.5) or math.ceil(x - 0.5)) * increment + end + + local tbl_has_tostring = function(tbl) + local mt = getmetatable(tbl) + return mt and mt.__tostring ~= nil + end + + local make_string = function(...) + local t = {} + for i = 1, select("#", ...) do + local x = select(i, ...) + + if type(x) == "number" and config.float_precision then + x = tostring(round(x, config.float_precision)) + elseif type(x) == "table" and not tbl_has_tostring(x) then + x = vim.inspect(x) + else + x = tostring(x) + end + + t[#t + 1] = x + end + return table.concat(t, " ") + end + + local log_at_level = function(level_config, message_maker, ...) + -- Return early if we're below the current_log_level + -- + -- the log level source get from config directly because otherwise it doesnt work + if level_config.level < c.log_level then + return + end + local nameupper = level_config.name:upper() + + local msg = message_maker(...) + local info = debug.getinfo(2, "Sl") + local lineinfo = info.short_src .. ":" .. info.currentline + + -- Output to console + if config.use_console then + local log_to_console = function() + local console_string = + string.format("[%-6s%s] %s: %s", nameupper, os.date "%H:%M:%S", lineinfo, msg) + + if config.highlights and level_config.hl then + vim.cmd(string.format("echohl %s", level_config.hl)) + end + + local split_console = vim.split(console_string, "\n") + for _, v in ipairs(split_console) do + local formatted_msg = string.format("[%s] %s", config.name, vim.fn.escape(v, [["\]])) + + ---@diagnostic disable-next-line: param-type-mismatch + local ok = pcall(vim.cmd, string.format([[echom "%s"]], formatted_msg)) + if not ok then + vim.api.nvim_out_write(msg .. "\n") + end + end + + if config.highlights and level_config.hl then + vim.cmd "echohl NONE" + end + end + if config.use_console == "sync" and not vim.in_fast_event() then + log_to_console() + else + vim.schedule(log_to_console) + end + end + + -- Output to log file + if config.use_file then + local fp = assert(io.open(log.get_outfile(), "a")) + local str = string.format("[%-6s%s] %s: %s\n", nameupper, os.date(), lineinfo, msg) + fp:write(str) + fp:close() + end + end + + for _, x in ipairs(config.modes) do + -- log.info("these", "are", "separated") + log[x.name] = function(...) ---@diagnostic disable-line: assign-type-mismatch + return log_at_level(x, make_string, ...) + end + + -- log.fmt_info("These are %s strings", "formatted") + log[("fmt_%s"):format(x.name)] = function(...) ---@diagnostic disable-line: assign-type-mismatch + return log_at_level(x, function(...) + local passed = { ... } + local fmt = table.remove(passed, 1) + local inspected = {} + for _, v in ipairs(passed) do + if type(v) == "table" and tbl_has_tostring(v) then + table.insert(inspected, v) + else + table.insert(inspected, vim.inspect(v)) + end + end + return string.format(fmt, unpack(inspected)) + end, ...) + end + end +end + +return log
M lua/gopher/comment.lua

@@ -3,6 +3,8 @@ ---@tag gopher.nvim-comments

---@usage Execute `:GoCmt` to generate a comment for the current function/method/struct/etc on this line. ---@text This module provides a way to generate comments for Go code. +local log = require "gopher._utils.log" + local function generate(row, col) local ts_utils = require "gopher._utils.ts" local comment, ns = nil, nil

@@ -37,6 +39,8 @@

return function() local row, col = unpack(vim.api.nvim_win_get_cursor(0)) local comment, ns = generate(row + 1, col + 1) + + log.debug("generated comment: " .. comment) vim.api.nvim_win_set_cursor(0, { ns.dim.s.r,
M lua/gopher/config.lua

@@ -29,6 +29,10 @@ ---@class gopher.Config

local default_config = { --minidoc_replace_end + -- log level, you might consider using DEBUG or TRACE for degugging the plugin + ---@type number + log_level = vim.log.levels.INFO, + -- user specified paths to binaries ---@class gopher.ConfigCommand commands = {

@@ -62,7 +66,16 @@ ---@type gopher.Config

---@private local _config = default_config +-- I am kinda secret so don't tell anyone about me +-- even dont use me +-- +-- if you don't belive me that i am secret see +-- the line below it says @private +---@private +_config.___plugin_name = "gopher.nvim" ---@diagnostic disable-line: inject-field + ---@param user_config? gopher.Config +---@private function config.setup(user_config) _config = vim.tbl_deep_extend("force", default_config, user_config or {}) end
M lua/gopher/gotests.lua

@@ -43,6 +43,7 @@ local c = require "gopher.config"

local ts_utils = require "gopher._utils.ts" local r = require "gopher._utils.runner" local u = require "gopher._utils" +local log = require "gopher._utils.log" local gotests = {} ---@param args table

@@ -64,6 +65,8 @@ end

table.insert(args, "-w") table.insert(args, vim.fn.expand "%") + + log.debug("generating tests with args: ", args) return r.sync(c.commands.gotests, { args = args,
M lua/gopher/iferr.lua

@@ -4,6 +4,7 @@ ---@text if you're using `iferr` tool, this module provides a way to automatically insert `if err != nil` check.

---@usage execute `:GoIfErr` near any err variable to insert the check local c = require "gopher.config" +local log = require "gopher._utils.log" local iferr = {} -- That's Lua implementation: github.com/koron/iferr

@@ -14,6 +15,7 @@

local data = vim.fn.systemlist((c.commands.iferr .. " -pos " .. boff), vim.fn.bufnr "%") if vim.v.shell_error ~= 0 then error("iferr failed: " .. data) + log.error("failed. output: " .. data) end vim.fn.append(pos, data)
M lua/gopher/init.lua

@@ -9,6 +9,7 @@ --- Table of Contents

---@tag gopher.nvim-table-of-contents ---@toc +local log = require "gopher._utils.log" local tags = require "gopher.struct_tags" local tests = require "gopher.gotests" local gocmd = require("gopher._utils.runner.gocmd").run

@@ -21,7 +22,12 @@ --- You can read more about configuration at |gopher.nvim-config|

--- Calling this function is optional, if you ok with default settings. Look |gopher.nvim.config-defaults| --- ---@usage `require("gopher").setup {}` (replace `{}` with your `config` table) -gopher.setup = require("gopher.config").setup +---@param user_config gopher.Config +gopher.setup = function(user_config) + log.debug "setting up config" + require("gopher.config").setup(user_config) + log.debug(vim.inspect(user_config)) +end ---@toc_entry Install dependencies ---@tag gopher.nvim-install-deps
M plugin/gopher.vim

@@ -11,3 +11,4 @@ command! -nargs=* GoGenerate :lua require"gopher".generate(<f-args>)

command! GoCmt :lua require"gopher".comment() command! GoIfErr :lua require"gopher".iferr() command! GoInstallDeps :lua require"gopher".install_deps() +command! GopherLog :lua vim.cmd("tabnew " .. require("gopher._utils.log").get_outfile())
M spec/units/utils_spec.lua

@@ -1,16 +1,6 @@

describe("gopher._utils", function() local u = require "gopher._utils" - describe(".is_tbl_empty()", function() - it("it is empty", function() - assert.are.same(true, u.is_tbl_empty {}) - end) - - it("it is not empty", function() - assert.are.same(false, u.is_tbl_empty { first = "1", second = 2 }) - end) - end) - describe(".sreq()", function() it("can require existing module", function() assert.are.same(require "gopher", u.sreq "gopher")