initial commit
This commit is contained in:
commit
4037e24558
11 changed files with 420 additions and 0 deletions
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
/import-cost/
|
||||||
|
doc/tags
|
||||||
|
vim.toml
|
||||||
|
selene.toml
|
||||||
4
.stylua.toml
Normal file
4
.stylua.toml
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
quote_style = "AutoPreferSingle"
|
||||||
|
call_parentheses = "None"
|
||||||
|
indent_type = "Spaces"
|
||||||
|
column_width = 80
|
||||||
49
doc/import-cost.txt
Normal file
49
doc/import-cost.txt
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
*import-cost.nvim*
|
||||||
|
|
||||||
|
Author: Barrett Ruth <https://barrett-ruth.github.io>
|
||||||
|
Homepage: <https://github.com/barrett-ruth/import-cost.nvim>
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
INTRODUCTION *import-cost.nvim*
|
||||||
|
|
||||||
|
import-cost.nvim displays the costs of javascript imports inside neovim.
|
||||||
|
It works with ES6 and CommonJS modules in any javascript, javascriptreact,
|
||||||
|
typescript, or typescriptreact files.
|
||||||
|
|
||||||
|
Author: Barrett Ruth <https://barrett-ruth.github.io>
|
||||||
|
|
||||||
|
===============================================================================
|
||||||
|
SETUP *import-cost.setup()*
|
||||||
|
>lua
|
||||||
|
require('import-cost').setup(config)
|
||||||
|
<
|
||||||
|
Module setup.
|
||||||
|
|
||||||
|
Parameters: ~
|
||||||
|
|
||||||
|
{config} `(table)`: Table containing configuration for import-cost.
|
||||||
|
|
||||||
|
Usage: ~
|
||||||
|
>lua
|
||||||
|
require('import-cost').setup({
|
||||||
|
-- Filetype to attach to
|
||||||
|
filetypes = {
|
||||||
|
'javascript',
|
||||||
|
'javascriptreact',
|
||||||
|
'typescript',
|
||||||
|
'typescriptreact',
|
||||||
|
},
|
||||||
|
format = {
|
||||||
|
-- Format string for bytes/kilobytes in virtual text
|
||||||
|
byte_format = '%.2f b',
|
||||||
|
kb_format = '%.2f kb',
|
||||||
|
-- Virtual text format (remove second "%s" to ignore gzipped size)
|
||||||
|
virtual_text = '%s (gzipped: %s)',
|
||||||
|
},
|
||||||
|
-- Highlight of virtual text —
|
||||||
|
-- a highlight group to link to or table as specified by nvim_set_hl()
|
||||||
|
highlight = 'Comment',
|
||||||
|
})
|
||||||
|
<
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
vim:tw=80:ts=8:ft=help:
|
||||||
17
install.sh
Normal file
17
install.sh
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
git clone 'git@github.com:wix/import-cost.git' || (echo 'Failed to clone wix/import-cost' && exit)
|
||||||
|
|
||||||
|
echo 'Install import-cost with which package manager? [npm/yarn]'
|
||||||
|
read -r installer
|
||||||
|
|
||||||
|
if [ "$installer" != 'npm' ] && [ "$installer" != 'yarn' ]; then
|
||||||
|
echo "Please enter either 'npm' or 'yarn'"
|
||||||
|
exit
|
||||||
|
fi
|
||||||
|
|
||||||
|
cd import-cost || exit
|
||||||
|
eval "$installer install"
|
||||||
|
cd ..
|
||||||
|
|
||||||
|
cp src/index.js import-cost
|
||||||
64
lua/import-cost.lua
Normal file
64
lua/import-cost.lua
Normal 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
104
lua/import-cost/extmark.lua
Normal 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
|
||||||
3
lua/import-cost/import.lua
Normal file
3
lua/import-cost/import.lua
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
local M = {}
|
||||||
|
|
||||||
|
return M
|
||||||
48
lua/import-cost/job.lua
Normal file
48
lua/import-cost/job.lua
Normal 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
39
lua/import-cost/util.lua
Normal 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
|
||||||
36
readme.md
Normal file
36
readme.md
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# import-cost.nvim
|
||||||
|
|
||||||
|
Display the costs of javascript imports inside neovim with the power of
|
||||||
|
[import-cost](https://github.com/wix/import-cost)
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Install regularly with your package manager
|
||||||
|
2. Run the install script inside the directory:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
sh install.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Example configuration with [packer.nvim](https://github.com/wbthomason/packer.nvim):
|
||||||
|
|
||||||
|
```lua
|
||||||
|
use {
|
||||||
|
'barrett-ruth/import-cost.nvim',
|
||||||
|
run = 'sh install.sh'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
```lua
|
||||||
|
require('import-cost').setup(opts)
|
||||||
|
```
|
||||||
|
|
||||||
|
See `:h import-cost` for more information
|
||||||
|
|
||||||
|
## TODO
|
||||||
|
|
||||||
|
- Automatic setup
|
||||||
52
src/index.js
Normal file
52
src/index.js
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
const { cleanup, importCost, Lang } = require("import-cost");
|
||||||
|
|
||||||
|
process.stdin.setEncoding("utf8");
|
||||||
|
|
||||||
|
const receive = async () => {
|
||||||
|
let result = "";
|
||||||
|
|
||||||
|
for await (const chunk of process.stdin) result += chunk;
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Pad data with '|' for combined chunks to be parsable
|
||||||
|
const give = (data) =>
|
||||||
|
process.nextTick(() => process.stdout.write(`${JSON.stringify(data)}|`));
|
||||||
|
|
||||||
|
const init = async () => {
|
||||||
|
const [path, filetype] = process.argv.slice(2);
|
||||||
|
const lang =
|
||||||
|
filetype.substring(0, "typescript".length) === "typescript"
|
||||||
|
? Lang.TYPESCRIPT
|
||||||
|
: Lang.JAVASCRIPT;
|
||||||
|
const contents = await receive();
|
||||||
|
|
||||||
|
const emitter = importCost(path, contents, lang);
|
||||||
|
|
||||||
|
emitter.on("error", (error) => {
|
||||||
|
give({ type: "error", error });
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
|
||||||
|
emitter.on("calculated", ({ line, string, size, gzip }) => {
|
||||||
|
give({
|
||||||
|
type: "calculated",
|
||||||
|
data: { line, string, size, gzip },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Send done to ensure job stdin stays open
|
||||||
|
emitter.on("done", (_) => {
|
||||||
|
give({
|
||||||
|
type: "done",
|
||||||
|
});
|
||||||
|
|
||||||
|
cleanup();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
init();
|
||||||
|
} catch {}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue