Skip to content

Commit

Permalink
Merge in settings.lua
Browse files Browse the repository at this point in the history
  • Loading branch information
Ajaymamtora committed Dec 10, 2024
1 parent eb53996 commit 1dd9549
Show file tree
Hide file tree
Showing 6 changed files with 420 additions and 8 deletions.
129 changes: 129 additions & 0 deletions lua/neoconf/editor.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
local Config = require("neoconf.config")
local Json = require("neoconf.utils.json")
local Settings = require("neoconf.settings")
local Util = require("neoconf.util")

local M = {}

-- Cache directory for temporary files
M.TMP_DIR = vim.fn.stdpath("cache") .. "/.neoconf_tmp"

---@class EditorOptions
---@field on_save fun()|nil Called after saving settings
---@field on_close fun()|nil Called when closing the editor

---Create a temporary JSON file
---@return string filepath
function M.create_temp_file()
vim.fn.mkdir(M.TMP_DIR, "p")
local random_name = vim.fn.system("openssl rand -hex 8"):gsub("\n", "")
return M.TMP_DIR .. "/" .. random_name .. ".json"
end

---Open settings in a new buffer
---@param settings table The settings to edit
---@param opts EditorOptions|nil
---@return number bufnr
function M.open(settings, opts)
opts = vim.tbl_deep_extend("force", {
on_save = function() end,
on_close = function() end,
}, opts or {})

local temp_file = M.create_temp_file()

-- Ensure empty settings are handled as an empty object
if settings == nil or next(settings) == nil then
settings = vim.empty_dict() -- Use vim.empty_dict() to force object notation
end

local formatted = Json.encode(settings, { sort = true })

-- Write formatted content to temp file
local file = io.open(temp_file, "w")
if file then
file:write(formatted)
file:close()
else
error("Failed to write temporary file: " .. temp_file)
end

-- Open in new tab
vim.cmd("tabnew " .. temp_file)
local buf = vim.api.nvim_get_current_buf()

-- Mark as temporary buffer
vim.api.nvim_buf_set_var(buf, "is_temp_settings_buffer", true)
vim.api.nvim_buf_set_var(buf, "temp_file_path", temp_file)

-- Setup buffer
vim.api.nvim_buf_set_option(buf, "filetype", "json")
vim.api.nvim_buf_set_option(buf, "bufhidden", "wipe")

-- Save keymap
vim.keymap.set("n", "<leader>s", function()
M.save_current_buffer()
opts.on_save()
end, { buffer = buf, noremap = true, silent = true })

-- Clean up on buffer close
vim.api.nvim_create_autocmd("BufUnload", {
buffer = buf,
once = true,
callback = function()
os.remove(temp_file)
opts.on_close()
end,
})

return buf
end

---Save the current buffer settings
function M.save_current_buffer()
local buf = vim.api.nvim_get_current_buf()

-- Verify this is a settings buffer
if not pcall(vim.api.nvim_buf_get_var, buf, "is_temp_settings_buffer") then
Util.error("This is not a temporary settings buffer")
return
end

-- Get current content
local lines = vim.api.nvim_buf_get_lines(buf, 0, -1, false)
local content = table.concat(lines, "\n")

-- Validate JSON
local ok, decoded = pcall(vim.json.decode, content)
if not ok then
Util.error("Invalid JSON. Please check your changes")
return
end

-- Write to local config
local success = Settings.write_local(decoded)
if success then
Util.info("Settings saved successfully")
Settings.refresh()
vim.api.nvim_buf_delete(buf, { force = true })
end
end

---Close all temporary settings buffers
function M.close_all()
local closed = 0
for _, buf in ipairs(vim.api.nvim_list_bufs()) do
local is_temp = pcall(vim.api.nvim_buf_get_var, buf, "is_temp_settings_buffer")
if is_temp then
local file = vim.api.nvim_buf_get_name(buf)
os.remove(file)
vim.api.nvim_buf_delete(buf, { force = true })
closed = closed + 1
end
end
if closed > 0 then
Util.info(string.format("Closed %d temporary settings buffer(s)", closed))
end
end

return M
117 changes: 117 additions & 0 deletions lua/neoconf/init.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
local Settings = require("neoconf.settings")
local Util = require("neoconf.util")

local M = {}

function M.setup(opts)
Expand All @@ -19,4 +22,118 @@ function M.get(key, defaults, opts)
return require("neoconf.workspace").get(opts).settings:get(key, { defaults = defaults })
end

---Toggle a boolean value at a specific settings path
---@param path string The dot-separated path to the setting
---@return boolean|nil new_value The new value after toggling
---@return string|nil error
function M.toggle(path)
local settings = Settings.get_local(vim.uv.cwd())
local current = settings:get(path)

-- If value doesn't exist, start with false
if type(current) ~= "boolean" then
current = false
end

-- Create new settings with toggled value
local new_settings = {}
local parts = vim.split(path, ".", { plain = true })
local node = new_settings
for i = 1, #parts - 1 do
node[parts[i]] = {}
node = node[parts[i]]
end
node[parts[#parts]] = not current

-- Write to local settings
local success = Settings.write_local(new_settings)
if not success then
return nil, "Failed to write settings"
end

Settings.refresh()
return not current
end

---Toggle a string value in a table at a specific settings path
---@param str string The string to toggle
---@param path string The dot-separated path to the array
---@return boolean|nil success
---@return string|nil error
function M.toggle_string_in_table(str, path)
local settings = Settings.get_local(vim.uv.cwd())
local current = settings:get(path)

if type(current) ~= "table" then
current = {}
end

-- Toggle string in array
local found = false
for i, v in ipairs(current) do
if v == str then
table.remove(current, i)
found = true
break
end
end

if not found then
table.insert(current, str)
end

-- Create new settings
local new_settings = {}
local parts = vim.split(path, ".", { plain = true })
local node = new_settings
for i = 1, #parts - 1 do
node[parts[i]] = {}
node = node[parts[i]]
end
node[parts[#parts]] = current

-- Write to local settings
local success = Settings.write_local(new_settings)
if not success then
return nil, "Failed to write settings"
end

Settings.refresh()
return true
end

---Helper function to toggle LSP inlay hints
---@return boolean|nil new_state
function M.toggle_inlay_hints()
local new_state = M.toggle("lsp.inlay_hint")
if new_state ~= nil then
vim.lsp.inlay_hint.enable(0, new_state)
Util.info("Inlay hints " .. (new_state and "enabled" or "disabled"))
end
return new_state
end

---Helper function to toggle autoformatting
---@return boolean|nil new_state
function M.toggle_autoformat()
local new_state = M.toggle("autoformat")
if new_state ~= nil then
Util.info("Autoformat " .. (new_state and "enabled" or "disabled"))
end
return new_state
end

---Print the current state of a property
---@param property_name string The name to display in the message
---@param path string|nil The settings path (defaults to property_name)
function M.print_property_state(property_name, path)
local settings = Settings.get_local(vim.uv.cwd())
local value = settings:get(path or property_name)
if value ~= nil then
Util.info(string.format("'%s' is currently set to: %s", property_name, tostring(value)))
else
Util.warn(string.format("'%s' is not set", property_name))
end
end

return M
10 changes: 10 additions & 0 deletions lua/neoconf/settings.lua
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,16 @@ function M.get_global()
return settings
end

---Write settings to local config file
---@param content table The settings to write
---@return boolean success
function M.write_local(content)
local root_dir = require("neoconf.workspace").find_root({})
local local_file = root_dir .. "/" .. require("neoconf.config").options.local_settings

return require("neoconf.utils.json").write(local_file, content, { sort = true, format = false })
end

function M.refresh()
local Workspace = require("neoconf.workspace")
-- Clear the internal state
Expand Down
10 changes: 4 additions & 6 deletions lua/neoconf/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,11 @@ function M.notify(msg, level)
vim.notify(msg, level, {
title = "settings.nvim",
on_open = function(win)
vim.api.nvim_set_option_value("conceallevel", 3, {
win = win,
scope = "local",
})
local buf = vim.api.nvim_win_get_buf(win)
vim.api.nvim_set_option_value("filetype", "markdown", { buf = buf, scope = "local" })
vim.api.nvim_set_option_value("spell", false, { buf = buf, scope = "local" })
-- Set window option
vim.wo[win].conceallevel = 3
-- Set buffer options
vim.bo[buf].filetype = "markdown"
end,
})
end
Expand Down
97 changes: 97 additions & 0 deletions lua/neoconf/utils/json.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
local uv = vim.uv or vim.loop

local M = {}

---@class JsonEncodeOptions
---@field sort boolean Sort object keys
---@field format boolean Format JSON output

---Encode a Lua table to JSON
---@param value any The value to encode
---@param opts JsonEncodeOptions|nil
---@return string
function M.encode(value, opts)
opts = vim.tbl_extend("force", { sort = false, format = true }, opts or {})

-- Basic JSON encode
local ok, json = pcall(vim.json.encode, value)
if not ok then
error("Failed to encode JSON: " .. json)
end

-- Format with jq if requested
if opts.format or opts.sort then
local args = { "jq" }
if opts.sort then
table.insert(args, "--sort-keys")
end
table.insert(args, ".")

local result = vim.fn.system(args, json)
if vim.v.shell_error == 0 then
return result
end
-- Fallback to unformatted JSON if jq fails
return json
end

return json
end

---Write JSON to a file
---@param filepath string
---@param content any
---@param opts JsonEncodeOptions|nil
---@return boolean success, string? error
function M.write(filepath, content, opts)
-- Encode content
local ok, data = pcall(M.encode, content, opts)
if not ok then
return false, "Failed to encode JSON"
end

-- Write to file
local fd = uv.fs_open(filepath, "w", 438) -- 0666 octal
if not fd then
return false, "Could not open file for writing"
end

local success = pcall(function()
uv.fs_write(fd, data, 0)
uv.fs_close(fd)
end)

if not success then
return false, "Failed to write file"
end

return true
end

---Read JSON from a file
---@param filepath string
---@return table|nil content
---@return string|nil error
function M.read(filepath)
local fd = uv.fs_open(filepath, "r", 438)
if not fd then
return nil, "Could not open file"
end

local stat = uv.fs_fstat(fd)
local data = uv.fs_read(fd, stat.size, 0)
uv.fs_close(fd)

if not data then
return nil, "Could not read file"
end

local ok, content = pcall(vim.json.decode, data)
if not ok then
return nil, "Failed to decode JSON"
end

return content
end

return M
Loading

0 comments on commit 1dd9549

Please sign in to comment.