diff --git a/lua/oil/columns.lua b/lua/oil/columns.lua index 7ac894d..ca0e978 100644 --- a/lua/oil/columns.lua +++ b/lua/oil/columns.lua @@ -27,11 +27,16 @@ local function get_column(adapter, defn) return all_columns[name] or adapter.get_column(name) end ----@param scheme string +---@param adapter_or_scheme string|oil.Adapter ---@return oil.ColumnSpec[] -M.get_supported_columns = function(scheme) +M.get_supported_columns = function(adapter_or_scheme) + local adapter + if type(adapter_or_scheme) == "string" then + adapter = config.get_adapter_by_scheme(adapter_or_scheme) + else + adapter = adapter_or_scheme + end local ret = {} - local adapter = config.get_adapter_by_scheme(scheme) for _, def in ipairs(config.columns) do if get_column(adapter, def) then table.insert(ret, def) diff --git a/lua/oil/config.lua b/lua/oil/config.lua index f239f71..8e869ee 100644 --- a/lua/oil/config.lua +++ b/lua/oil/config.lua @@ -138,9 +138,12 @@ M.get_trash_url = function() return M.adapter_to_scheme.files .. fs.os_to_posix_path(M.trash) end ----@param scheme string +---@param scheme nil|string ---@return nil|oil.Adapter M.get_adapter_by_scheme = function(scheme) + if not scheme then + return nil + end if not vim.endswith(scheme, "://") then local pieces = vim.split(scheme, "://", { plain = true }) if #pieces <= 2 then diff --git a/lua/oil/init.lua b/lua/oil/init.lua index d32ce7d..f870b2c 100644 --- a/lua/oil/init.lua +++ b/lua/oil/init.lua @@ -5,7 +5,7 @@ local M = {} ---@class oil.Entry ---@field name string ---@field type oil.EntryType ----@field id nil|string Will be nil if it hasn't been persisted to disk yet +---@field id nil|integer Will be nil if it hasn't been persisted to disk yet ---@alias oil.EntryType "file"|"directory"|"socket"|"link" ---@alias oil.TextChunk string|string[] @@ -29,15 +29,12 @@ local M = {} ---@return nil|oil.Entry M.get_entry_on_line = function(bufnr, lnum) local columns = require("oil.columns") - local config = require("oil.config") local parser = require("oil.mutator.parser") local util = require("oil.util") if vim.bo[bufnr].filetype ~= "oil" then return nil end - local bufname = vim.api.nvim_buf_get_name(0) - local scheme = util.parse_url(bufname) - local adapter = config.get_adapter_by_scheme(scheme) + local adapter = util.get_adapter(bufnr) if not adapter then return nil end @@ -46,15 +43,15 @@ M.get_entry_on_line = function(bufnr, lnum) if not line then return nil end - local column_defs = columns.get_supported_columns(scheme) - local parsed_entry, entry = parser.parse_line(adapter, line, column_defs) - if parsed_entry then - if entry then - return util.export_entry(entry) + local column_defs = columns.get_supported_columns(adapter) + local result = parser.parse_line(adapter, line, column_defs) + if result then + if result.entry then + return util.export_entry(result.entry) else return { - name = parsed_entry.name, - type = parsed_entry._type, + name = result.data.name, + type = result.data._type, } end end diff --git a/lua/oil/mutator/parser.lua b/lua/oil/mutator/parser.lua index f185749..c314ec5 100644 --- a/lua/oil/mutator/parser.lua +++ b/lua/oil/mutator/parser.lua @@ -38,27 +38,40 @@ local function parsedir(name) return name, isdir end +---@class oil.ParseResult +---@field data table Parsed entry data +---@field ranges table Locations of the various columns +---@field entry nil|oil.InternalEntry If the entry already exists + ---Parse a single line in a buffer ---@param adapter oil.Adapter ---@param line string ---@param column_defs oil.ColumnSpec[] ----@return nil|table Parsed entry data ----@return nil|oil.InternalEntry If the entry already exists ----@return nil|string Error message +---@return nil|oil.ParseResult +---@return nil|string Error M.parse_line = function(adapter, line, column_defs) local ret = {} + local ranges = {} + local start = 1 local value, rem = line:match("^/(%d+) (.+)$") if not value then - return nil, nil, "Malformed ID at start of line" + return nil, "Malformed ID at start of line" end + ranges.id = { start, value:len() + 1 } + start = ranges.id[2] + 1 ret.id = tonumber(value) for _, def in ipairs(column_defs) do local name = util.split_config(def) + local range = { start } + local start_len = string.len(rem) value, rem = columns.parse_col(adapter, rem, def) - if not value then - return nil, nil, string.format("Parsing %s failed", name) + if not value or not rem then + return nil, string.format("Parsing %s failed", name) end ret[name] = value + range[2] = range[1] + start_len - string.len(rem) - 1 + ranges[name] = range + start = range[2] + 1 end local name = rem if name then @@ -70,8 +83,9 @@ M.parse_line = function(adapter, line, column_defs) ret._type = isdir and "directory" or "file" end local entry = cache.get_entry_by_id(ret.id) + ranges.name = { start, start + string.len(rem) - 1 } if not entry then - return ret + return { data = ret, ranges = ranges } end -- Parse the symlink syntax @@ -81,8 +95,9 @@ M.parse_line = function(adapter, line, column_defs) local name_pieces = vim.split(ret.name, " -> ", { plain = true }) if #name_pieces ~= 2 then ret.name = "" - return ret + return { data = ret, ranges = ranges } end + ranges.name = { start, start + string.len(name_pieces[1]) - 1 } ret.name = parsedir(vim.trim(name_pieces[1])) ret.link_target = name_pieces[2] ret._type = "link" @@ -93,7 +108,7 @@ M.parse_line = function(adapter, line, column_defs) ret._type = entry[FIELD.type] end - return ret, entry + return { data = ret, entry = entry, ranges = ranges } end ---@param bufnr integer @@ -113,8 +128,8 @@ M.parse = function(bufnr) return diffs, errors end local scheme, path = util.parse_url(bufname) - local column_defs = columns.get_supported_columns(scheme) local parent_url = scheme .. path + local column_defs = columns.get_supported_columns(adapter) local children = cache.list_url(parent_url) local lines = vim.api.nvim_buf_get_lines(bufnr, 0, -1, true) local original_entries = {} @@ -137,8 +152,8 @@ M.parse = function(bufnr) end for i, line in ipairs(lines) do if line:match("^/%d+") then - local parsed_entry, entry, err = M.parse_line(adapter, line, column_defs) - if not parsed_entry then + local result, err = M.parse_line(adapter, line, column_defs) + if not result or err then table.insert(errors, { message = err, lnum = i - 1, @@ -146,6 +161,8 @@ M.parse = function(bufnr) }) goto continue end + local parsed_entry = result.data + local entry = result.entry if not parsed_entry.name or parsed_entry.name:match("/") or not entry then local message if not parsed_entry.name then diff --git a/lua/oil/view.lua b/lua/oil/view.lua index dca3844..13ba336 100644 --- a/lua/oil/view.lua +++ b/lua/oil/view.lua @@ -244,11 +244,33 @@ M.initialize = function(bufnr) group = "Oil", buffer = bufnr, callback = function() + local oil = require("oil") + local parser = require("oil.mutator.parser") + + -- Force the cursor to be after the (concealed) ID at the beginning of the line + local adapter = util.get_adapter(bufnr) + if adapter then + local cur = vim.api.nvim_win_get_cursor(0) + local line = vim.api.nvim_buf_get_lines(bufnr, cur[1] - 1, cur[1], true)[1] + local column_defs = columns.get_supported_columns(adapter) + local result = parser.parse_line(adapter, line, column_defs) + if result and result.data then + local min_col = result.ranges.id[2] + 1 + if cur[2] < min_col then + vim.api.nvim_win_set_cursor(0, { cur[1], min_col }) + end + end + end + + -- Debounce and update the preview window if timer then timer:again() return end timer = vim.loop.new_timer() + if not timer then + return + end timer:start(10, 100, function() timer:stop() timer:close() @@ -257,7 +279,6 @@ M.initialize = function(bufnr) if vim.api.nvim_get_current_buf() ~= bufnr then return end - local oil = require("oil") local entry = oil.get_cursor_entry() if entry then local winid = util.get_preview_win() @@ -397,7 +418,6 @@ local function render_buffer(bufnr, opts) end ---@private ----@param adapter oil.Adapter ---@param entry oil.InternalEntry ---@param column_defs table[] ---@param col_width integer[]