mirror of
https://github.com/boxpositron/absolute-vim.git
synced 2026-02-28 03:30:36 +00:00
feat(lua): add utility functions to detect Lua, OS, Python environment, TypeScript environment, Git root, and safe function invocation.
This commit is contained in:
39
lua/absolute/utils/detect-lua.lua
Normal file
39
lua/absolute/utils/detect-lua.lua
Normal file
@@ -0,0 +1,39 @@
|
||||
function GetLuaPath(version)
|
||||
local path = vim.fn.expand("$HOME") .. "/.luarocks/share/lua/" .. version .. "/?.lua;"
|
||||
path = path .. vim.fn.expand("$HOME") .. "/.luarocks/share/lua/" .. version .. "/?/init.lua;"
|
||||
return path
|
||||
end
|
||||
|
||||
function GetInstalledLuaVersion()
|
||||
-- List the directory contents of the lua rocks directory
|
||||
local rocks = vim.fn.glob(vim.fn.expand("$HOME") .. "/.luarocks/share/lua/")
|
||||
|
||||
-- Split the string into a table
|
||||
|
||||
rocks = vim.split(rocks, "\n")
|
||||
|
||||
-- Sort the table highest to lowest
|
||||
|
||||
table.sort(rocks, function(a, b)
|
||||
return tonumber(a) > tonumber(b)
|
||||
end)
|
||||
|
||||
return rocks
|
||||
end
|
||||
|
||||
function GetLatestLuaVersion()
|
||||
local rocks = GetInstalledLuaVersion()
|
||||
local preferredVersion = rocks[1]
|
||||
|
||||
local path = GetLuaPath(preferredVersion)
|
||||
|
||||
return path
|
||||
end
|
||||
|
||||
M = {}
|
||||
|
||||
M.GetLuaPath = GetLuaPath
|
||||
M.GetInstalledLuaVersion = GetInstalledLuaVersion
|
||||
M.GetLatestLuaVersion = GetLatestLuaVersion
|
||||
|
||||
return M
|
||||
43
lua/absolute/utils/detect-os.lua
Normal file
43
lua/absolute/utils/detect-os.lua
Normal file
@@ -0,0 +1,43 @@
|
||||
SUPPORTED_OS = {
|
||||
WINDOWS = "Windows",
|
||||
UNIX = "Unix",
|
||||
MACOS = "macOS",
|
||||
}
|
||||
|
||||
local function isMacOS()
|
||||
-- Try to execute `sw_vers` command which is specific to macOS
|
||||
local result = os.execute("sw_vers > /dev/null 2>&1")
|
||||
|
||||
-- `os.execute` returns true on success for Lua versions 5.2 and above
|
||||
-- For Lua 5.1 and below, it returns the exit code of the command. On success, it should be zero.
|
||||
-- Adjust the check if you're using Lua 5.1 or below:
|
||||
-- if result == 0 then
|
||||
if result then
|
||||
return true
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
local function detectOS()
|
||||
if os.getenv("OS") ~= nil then
|
||||
return os.getenv("OS") -- This can be useful in Windows environments
|
||||
elseif os.getenv("PATH"):find(";") then
|
||||
return SUPPORTED_OS.WINDOWS
|
||||
else
|
||||
local is_macos = isMacOS()
|
||||
|
||||
if is_macos then
|
||||
return SUPPORTED_OS.MACOS
|
||||
else
|
||||
return SUPPORTED_OS.UNIX
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
M = {}
|
||||
|
||||
M.detect = detectOS
|
||||
M.supported = SUPPORTED_OS
|
||||
|
||||
return M
|
||||
47
lua/absolute/utils/detect-python-env.lua
Normal file
47
lua/absolute/utils/detect-python-env.lua
Normal file
@@ -0,0 +1,47 @@
|
||||
function CheckPoetryVirtualEnv()
|
||||
local poetry = vim.fn.system("poetry env info -p 2>/dev/null")
|
||||
if poetry == "" then
|
||||
return nil
|
||||
end
|
||||
return poetry
|
||||
end
|
||||
|
||||
function ResolvePythonEnvironment()
|
||||
-- Check if asdf is installed
|
||||
|
||||
-- Get system global python env
|
||||
local python = vim.fn.system("which python3")
|
||||
-- Check of virtual environments exists
|
||||
local virtual = os.getenv("VIRTUAL_ENV") or CheckPoetryVirtualEnv()
|
||||
|
||||
if virtual == nil then
|
||||
return {
|
||||
"--python-executable",
|
||||
python,
|
||||
}
|
||||
end
|
||||
|
||||
return {
|
||||
"--python-executable",
|
||||
virtual .. "/bin/python",
|
||||
}
|
||||
end
|
||||
|
||||
function ResolvePythonEnvironmentAsString()
|
||||
local env = ResolvePythonEnvironment()
|
||||
if env == nil then
|
||||
return ""
|
||||
end
|
||||
|
||||
local path = table.concat(env, " ")
|
||||
|
||||
return path
|
||||
end
|
||||
|
||||
M = {}
|
||||
|
||||
M.ResolvePythonEnvironmentAsString = ResolvePythonEnvironmentAsString
|
||||
M.ResolvePythonEnvironment = ResolvePythonEnvironment
|
||||
M.CheckPoetryVirtualEnv = CheckPoetryVirtualEnv
|
||||
|
||||
return M
|
||||
67
lua/absolute/utils/detect-typescript-env.lua
Normal file
67
lua/absolute/utils/detect-typescript-env.lua
Normal file
@@ -0,0 +1,67 @@
|
||||
M = {}
|
||||
local function trim(s)
|
||||
return s:gsub("^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
||||
local ResolveTypescriptServer = function()
|
||||
-- Find the package.json file
|
||||
local viable = vim.fn.system("find . -name 'package.json' -maxdepth 3 | xargs dirname")
|
||||
viable = trim(viable)
|
||||
|
||||
if viable == "" then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Check for lock files
|
||||
local find_npm_result = trim(vim.fn.system("find . -name 'package-lock.json' -maxdepth 3 | wc -l"))
|
||||
local find_pnpm_result = trim(vim.fn.system("find . -name 'pnpm-lock.yaml' -maxdepth 3 | wc -l"))
|
||||
local find_yarn_result = trim(vim.fn.system("find . -name 'yarn.lock' -maxdepth 3 | wc -l"))
|
||||
|
||||
local is_npm = find_npm_result == "1"
|
||||
local is_pnpm = find_pnpm_result == "1"
|
||||
local is_yarn = find_yarn_result == "1"
|
||||
|
||||
-- if no package lock files are found, end early
|
||||
if not is_npm and not is_pnpm and not is_yarn then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Determine the preferred package manager
|
||||
local ppm = "pnpm" -- Default to pnpm
|
||||
if is_npm then
|
||||
ppm = "npm"
|
||||
end
|
||||
if is_yarn then
|
||||
ppm = "yarn"
|
||||
end
|
||||
|
||||
-- Resolve TypeScript server path
|
||||
local base_command = ppm .. " list typescript --dir " .. viable .. " --json"
|
||||
local base_result = trim(vim.fn.system(base_command))
|
||||
|
||||
if base_result == "" then
|
||||
vim.notify("Failed to run " .. base_command, vim.log.levels.ERROR)
|
||||
return nil
|
||||
end
|
||||
|
||||
local typescript_path = nil
|
||||
|
||||
if ppm == "pnpm" or ppm == "npm" or ppm == "yarn" then
|
||||
local ok, parsed = pcall(vim.fn.json_decode, base_result)
|
||||
if ok and parsed and #parsed > 0 and parsed[1].path then
|
||||
typescript_path = parsed[1].path .. "/node_modules/typescript/lib"
|
||||
end
|
||||
end
|
||||
|
||||
if not typescript_path or typescript_path == "" then
|
||||
vim.notify("TypeScript not found. Please install TypeScript globally or in the project.", vim.log.levels.ERROR)
|
||||
return nil
|
||||
end
|
||||
|
||||
return typescript_path
|
||||
end
|
||||
|
||||
M.ResolveTypescriptServer = ResolveTypescriptServer
|
||||
M.trim = trim
|
||||
|
||||
return M
|
||||
10
lua/absolute/utils/get-git-root.lua
Normal file
10
lua/absolute/utils/get-git-root.lua
Normal file
@@ -0,0 +1,10 @@
|
||||
function GetProjectRoot()
|
||||
local git_root = vim.fn.systemlist("git rev-parse --show-toplevel")[1]
|
||||
if git_root == nil then
|
||||
return vim.fn.getcwd()
|
||||
else
|
||||
return git_root
|
||||
end
|
||||
end
|
||||
|
||||
return GetProjectRoot
|
||||
15
lua/absolute/utils/safe-invoke.lua
Normal file
15
lua/absolute/utils/safe-invoke.lua
Normal file
@@ -0,0 +1,15 @@
|
||||
function SafeInvoke(func)
|
||||
-- safely invoke a functions
|
||||
-- if the function returns an error, return an empty string
|
||||
-- otherwise return the result of the function
|
||||
|
||||
local ok, result = pcall(func)
|
||||
|
||||
if ok then
|
||||
return result
|
||||
else
|
||||
return ""
|
||||
end
|
||||
end
|
||||
|
||||
return SafeInvoke
|
||||
127
lua/absolute/utils/telescope_image_preview.lua
Normal file
127
lua/absolute/utils/telescope_image_preview.lua
Normal file
@@ -0,0 +1,127 @@
|
||||
-- Sourced from github
|
||||
-- https://github.com/3rd/image.nvim/issues/183#issuecomment-2572606511
|
||||
|
||||
M = {}
|
||||
|
||||
local supported_images = { "svg", "png", "jpg", "jpeg", "gif", "webp", "avif" }
|
||||
local from_entry = require("telescope.from_entry")
|
||||
local Path = require("plenary.path")
|
||||
local conf = require("telescope.config").values
|
||||
local Previewers = require("telescope.previewers")
|
||||
|
||||
local previewers = require("telescope.previewers")
|
||||
local image_api = require("image")
|
||||
|
||||
local is_image_preview = false
|
||||
local image = nil
|
||||
local last_file_path = ""
|
||||
|
||||
local is_supported_image = function(filepath)
|
||||
local split_path = vim.split(filepath:lower(), ".", { plain = true })
|
||||
local extension = split_path[#split_path]
|
||||
return vim.tbl_contains(supported_images, extension)
|
||||
end
|
||||
|
||||
local delete_image = function()
|
||||
if not image then
|
||||
return
|
||||
end
|
||||
|
||||
image:clear()
|
||||
|
||||
is_image_preview = false
|
||||
end
|
||||
|
||||
local create_image = function(filepath, winid, bufnr)
|
||||
image = image_api.hijack_buffer(filepath, winid, bufnr)
|
||||
|
||||
if not image then
|
||||
return
|
||||
end
|
||||
|
||||
vim.schedule(function()
|
||||
image:render()
|
||||
end)
|
||||
|
||||
is_image_preview = true
|
||||
end
|
||||
|
||||
local function defaulter(f, default_opts)
|
||||
default_opts = default_opts or {}
|
||||
return {
|
||||
new = function(opts)
|
||||
if conf.preview == false and not opts.preview then
|
||||
return false
|
||||
end
|
||||
opts.preview = type(opts.preview) ~= "table" and {} or opts.preview
|
||||
if type(conf.preview) == "table" then
|
||||
for k, v in pairs(conf.preview) do
|
||||
opts.preview[k] = vim.F.if_nil(opts.preview[k], v)
|
||||
end
|
||||
end
|
||||
return f(opts)
|
||||
end,
|
||||
__call = function()
|
||||
local ok, err = pcall(f(default_opts))
|
||||
if not ok then
|
||||
error(debug.traceback(err))
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
-- NOTE: Add teardown to cat previewer to clear image when close Telescope
|
||||
local file_previewer = defaulter(function(opts)
|
||||
opts = opts or {}
|
||||
local cwd = opts.cwd or vim.fn.getcwd()
|
||||
return Previewers.new_buffer_previewer({
|
||||
title = "File Preview",
|
||||
dyn_title = function(_, entry)
|
||||
return Path:new(from_entry.path(entry, true)):normalize(cwd)
|
||||
end,
|
||||
|
||||
get_buffer_by_name = function(_, entry)
|
||||
return from_entry.path(entry, true)
|
||||
end,
|
||||
|
||||
define_preview = function(self, entry, _)
|
||||
local p = from_entry.path(entry, true)
|
||||
if p == nil or p == "" then
|
||||
return
|
||||
end
|
||||
|
||||
conf.buffer_previewer_maker(p, self.state.bufnr, {
|
||||
bufname = self.state.bufname,
|
||||
winid = self.state.winid,
|
||||
preview = opts.preview,
|
||||
})
|
||||
end,
|
||||
|
||||
teardown = function(_)
|
||||
if is_image_preview then
|
||||
delete_image()
|
||||
end
|
||||
end,
|
||||
})
|
||||
end, {})
|
||||
|
||||
local buffer_previewer_maker = function(filepath, bufnr, opts)
|
||||
-- NOTE: Clear image when preview other file
|
||||
if is_image_preview and last_file_path ~= filepath then
|
||||
delete_image()
|
||||
end
|
||||
|
||||
last_file_path = filepath
|
||||
|
||||
if is_supported_image(filepath) then
|
||||
filepath = string.gsub(filepath, " ", "%%20"):gsub("\\", "")
|
||||
create_image(filepath, opts.winid, bufnr)
|
||||
else
|
||||
previewers.buffer_previewer_maker(filepath, bufnr, opts)
|
||||
end
|
||||
end
|
||||
|
||||
M.buffer_previewer_maker = buffer_previewer_maker
|
||||
M.file_previewer = file_previewer.new
|
||||
|
||||
return M
|
||||
Reference in New Issue
Block a user