initial commit

This commit is contained in:
Barrett Ruth 2022-12-31 14:04:00 -06:00
commit 4037e24558
11 changed files with 420 additions and 0 deletions

64
lua/import-cost.lua Normal file
View file

@ -0,0 +1,64 @@
local M = {}
local function is_ic_buf(bufnr)
local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype')
return vim.tbl_contains(M.config.filetypes, filetype)
end
local function au(events, cb)
vim.api.nvim_create_autocmd(events, {
callback = function(opts)
if is_ic_buf(opts.buf) then
cb(opts)
end
end,
group = M.aug_id,
})
end
M.config = {
filetypes = {
'javascript',
'javascriptreact',
'typescript',
'typescriptreact',
},
format = {
byte_format = '%.2f b',
kb_format = '%.2f kb',
virtual_text = '%s (gzipped: %s)',
},
highlight = 'Comment',
}
M.setup = function(user_config)
M.config = vim.tbl_deep_extend('force', M.config, user_config or {})
M.ns_id = vim.api.nvim_create_namespace 'ImportCost'
M.script_path = vim.fn.fnamemodify(debug.getinfo(1).source:sub(2), ':h:h')
.. '/import-cost/index.js'
vim.api.nvim_set_hl(
0,
'ImportCostVirtualText',
---@diagnostic disable-next-line: param-type-mismatch
type(M.config.highlight) == 'string' and { link = M.config.highlight }
or M.config.highlight
)
M.aug_id = vim.api.nvim_create_augroup('ImportCost', {})
local extmark = require 'import-cost.extmark'
au({ 'BufEnter', 'BufWritePost' }, function(opts)
extmark.set_missing_extmarks(opts.buf)
end)
au('TextChanged', function(opts)
extmark.update_extmarks(opts.buf)
end)
end
return M

104
lua/import-cost/extmark.lua Normal file
View file

@ -0,0 +1,104 @@
local M = {}
local ic, job, util =
require 'import-cost', require 'import-cost.job', require 'import-cost.util'
local visible_strings = {}
local function update_visible_strings(bufnr, data, extmark_id)
if not visible_strings[bufnr] then
visible_strings[bufnr] = {}
end
visible_strings[bufnr][data.string] = data
visible_strings[bufnr][data.string].extmark_id = extmark_id
end
local function string_visible(bufnr, string)
return visible_strings[bufnr] and visible_strings[bufnr][string]
end
function M.set_missing_extmarks(bufnr)
local path = vim.api.nvim_buf_get_name(bufnr)
local filetype = vim.api.nvim_buf_get_option(bufnr, 'filetype')
local cmd = { 'node', ic.script_path, path, filetype }
local job_id = vim.fn.jobstart(cmd, {
on_stdout = function(_, stdout, _)
if not stdout or stdout[1] == '' then
return
end
local chunks = vim.split(stdout[1], '|', { trimempty = true })
for _, chunk in ipairs(chunks) do
local data = util.parse_data(chunk)
if util.is_ok(data) then
local string = util.normalize_string(data.string)
if not string_visible(bufnr, string) then
data.string = string
local extmark_id = job.set_extmark(bufnr, data)
update_visible_strings(bufnr, data, extmark_id)
end
end
end
end,
})
job.stop_prev_job(job_id, bufnr)
job.send_buf_contents(job_id, bufnr)
end
function M.clear_extmarks(bufnr)
if visible_strings[bufnr] then
visible_strings[bufnr] = nil
end
vim.api.nvim_buf_clear_namespace(bufnr, ic.ns_id, 0, -1)
end
function M.update_extmarks(bufnr)
local buffer_strings = M.move_existing_extmarks(bufnr)
M.delete_remaining_extmarks(bufnr, buffer_strings)
M.set_missing_extmarks(bufnr)
end
function M.move_existing_extmarks(bufnr)
local buffer_strings = {}
for nr, raw_string in ipairs(vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)) do
if util.is_import_string(raw_string) then
local string = util.normalize_string(raw_string)
local data = visible_strings[bufnr][string]
buffer_strings[string] = true
if data and data.string == string then
if data.line ~= nr then
data.line = nr
job.set_extmark(bufnr, data, data.extmark_id)
end
end
end
end
return buffer_strings
end
function M.delete_remaining_extmarks(bufnr, buffer_strings)
for string, data in pairs(visible_strings[bufnr]) do
if not buffer_strings[string] then
vim.api.nvim_buf_del_extmark(bufnr, ic.ns_id, data.extmark_id)
visible_strings[bufnr][string] = nil
end
end
end
return M

View file

@ -0,0 +1,3 @@
local M = {}
return M

48
lua/import-cost/job.lua Normal file
View file

@ -0,0 +1,48 @@
local M = {}
local ic = require 'import-cost'
local format = ic.config.format
local job_cache = {}
local format_bytes = function(bytes)
if bytes < 1024 then
return string.format(format.byte_format, bytes)
end
return string.format(format.kb_format, 0.0009765625 * bytes)
end
function M.set_extmark(bufnr, data, extmark_id)
local line, size, gzip =
data.line - 1, format_bytes(data.size), format_bytes(data.gzip)
local virt_text = string.format(format.virtual_text, size, gzip)
return vim.api.nvim_buf_set_extmark(bufnr, ic.ns_id, line, -1, {
id = extmark_id,
virt_text = {
{
virt_text,
'ImportCostVirtualText',
},
},
})
end
function M.send_buf_contents(job_id, bufnr)
local contents = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true)
vim.fn.chansend(job_id, contents)
vim.fn.chanclose(job_id, 'stdin')
end
function M.stop_prev_job(job_id, bufnr)
if job_cache[bufnr] then
vim.fn.jobstop(job_cache[bufnr])
end
job_cache[bufnr] = job_id
end
return M

39
lua/import-cost/util.lua Normal file
View file

@ -0,0 +1,39 @@
local M = {}
function M.is_import_string(string)
return string:sub(1, 6) == 'import' or string:sub(1, 5) == 'const'
end
function M.normalize_string(raw_string)
local string = raw_string
-- pad with semicolon
if string:sub(-1, -1) ~= ';' then
string = string .. ';'
end
-- stylua: ignore
string = string
:match('.-;') -- extract first statement
:gsub(' as %S+', '') -- remove aliases
:gsub("'", '"') -- swap single for double quotes
:gsub('%s+', '') -- remove whitespace
return string
end
function M.parse_data(chunk)
local ok, json = pcall(vim.fn.json_decode, chunk)
if not ok or not json then
return
end
return json.data
end
function M.is_ok(data)
return data and data.size
end
return M