Skip to content

Commit

Permalink
fix: improve window entry parsing (#102)
Browse files Browse the repository at this point in the history
Content.parse_line now receives a list of original entries.
Parsed entries must now include whether it has been "modified" or not.
Path.is_joinable was performing the opposite logic.
Scope and Container content now make assumptions while parsing that the content is unmodifiable
  • Loading branch information
cbochs authored Mar 4, 2024
1 parent d1d9fdf commit 374e72a
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 82 deletions.
29 changes: 17 additions & 12 deletions lua/grapple/container_content.lua
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ function ContainerContent:entities()
return string.lower(cont_a.id) < string.lower(cont_b.id)
end

---@type grapple.tag_container[]
local containers = vim.tbl_values(self.tag_manager.containers)
table.sort(containers, by_id)

Expand Down Expand Up @@ -102,7 +103,14 @@ function ContainerContent:create_entry(entity, index)

-- A string representation of the index
local id = string.format("/%03d", index)
local rel_id = vim.fn.fnamemodify(container.id, ":~")

-- Don't try to modify IDs which are not paths, like "global"
local rel_id
if Path.is_absolute(container.id) then
rel_id = vim.fn.fnamemodify(container.id, ":~")
else
rel_id = container.id
end

-- In compliance with "grapple" syntax
local line = string.format("%s %s", id, rel_id)
Expand Down Expand Up @@ -144,21 +152,18 @@ function ContainerContent:create_entry(entity, index)
return entry
end

---Safety: assume that the content is unmodifiable and the ID
---can always be parsed
---@param line string
---@param original_entries grapple.window.entry[]
---@return grapple.window.parsed_entry
function ContainerContent:parse_line(line)
local id, container_id = string.match(line, "^/(%d+) (%S*)")
local index = tonumber(id)
function ContainerContent:parse_line(line, original_entries)
local id, _ = string.match(line, "^/(%d+) (%S+)")
local index = assert(tonumber(id))

---@type grapple.window.parsed_entry
local entry = {
---@type grapple.scope_content.data
data = {
id = Path.fs_absolute(container_id),
},
index = index,
line = line,
}
---@diagnostic disable-next-line: assign-type-mismatch
local entry = vim.deepcopy(original_entries[index])

return entry
end
Expand Down
10 changes: 5 additions & 5 deletions lua/grapple/path.lua
Original file line number Diff line number Diff line change
Expand Up @@ -309,11 +309,11 @@ end
---@param path string
---@return boolean
function Path.is_joinable(path)
return vim.startswith(path, "./")
or vim.startswith(path, "../")
or vim.startswith(path, "~")
or Path.is_uri(path)
or Path.is_absolute(path)
return not vim.startswith(path, "./")
and not vim.startswith(path, "../")
and not vim.startswith(path, "~")
and not Path.is_uri(path)
and not Path.is_absolute(path)
end

---@param ... string
Expand Down
18 changes: 8 additions & 10 deletions lua/grapple/scope_content.lua
Original file line number Diff line number Diff line change
Expand Up @@ -148,20 +148,18 @@ function ScopeContent:create_entry(entity, index)
return entry
end

---Safety: assume that the content is unmodifiable and the ID
---can always be parsed
---@param line string
---@param original_entries grapple.window.entry[]
---@return grapple.window.parsed_entry
function ScopeContent:parse_line(line)
local id, name = string.match(line, "^/(%d+) (%S*)")
local index = tonumber(id)
function ScopeContent:parse_line(line, original_entries)
local id, _ = string.match(line, "^/(%d+) (%S+) %S+")
local index = assert(tonumber(id))

---@type grapple.window.parsed_entry
local entry = {
data = {
name = name,
},
index = index,
line = line,
}
---@diagnostic disable-next-line: assign-type-mismatch
local entry = vim.deepcopy(original_entries[index])

return entry
end
Expand Down
101 changes: 50 additions & 51 deletions lua/grapple/tag_content.lua
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ function TagContent:create_entry(entity, index)

---@type grapple.window.entry
local entry = {
---@class grapple.tag.content.data
---@class grapple.tag_content.data
data = {
path = tag.path,
name = tag.name,
Expand Down Expand Up @@ -196,72 +196,81 @@ function TagContent:create_entry(entity, index)
end

---@param line string
---@param original_entries grapple.window.entry[]
---@return grapple.window.parsed_entry
function TagContent:parse_line(line)
function TagContent:parse_line(line, original_entries)
local App = require("grapple.app")
local app = App.get()

---@diagnostic disable-next-line: unused-local
local id, icon, index, path
local icon

local id, index, path, original_entry

-- In compliance with "grapple" syntax
if app.settings.icons then
---@diagnostic disable-next-line: unused-local
id, icon, path = string.match(line, "^/(%d+) (%S+) (%S*)")
id, icon, path = string.match(line, "^/(%d+) (%S+) %s*(%S*)")
else
id, path = string.match(line, "^/(%d+) (%S*)")
id, path = string.match(line, "^/(%d+) %s*(%S*)")
end

if id then
index = assert(tonumber(id))
path = path
original_entry = original_entries[index]
else
-- Parse as a new entry when an ID is not present
index = nil
path = line
path = vim.trim(line)
original_entry = nil
end

-- Remove whitespace around path before parsing
path = vim.trim(path)
-- Create an empty parsed entry, assume modified
---@type grapple.window.parsed_entry
local entry = {
---@type grapple.tag_content.data
data = {
path = nil,
name = nil,
cursor = nil,
},
line = line,
modified = true,
index = index,
}

-- Don't parse an empty path or line
if path == "" then
---@type grapple.window.parsed_entry
local entry = {
data = {
path = nil,
},
line = line,
index = index,
}

return entry
end

-- We shouldn't try to join with the scope path if:
-- 1. The path starts with "~", "./", or "../"
-- 2. The path is absolute or a URI
if not Path.is_joinable(path) then
if Path.is_joinable(path) then
path = Path.join(self.scope.path, path)
end

path = Path.fs_absolute(path)

---@type grapple.window.parsed_entry
local entry = {
data = {
path = path,
},
line = line,
index = index,
}
if original_entry and original_entry.data.path == path then
---@type grapple.window.parsed_entry
---@diagnostic disable-next-line: assign-type-mismatch
entry = vim.deepcopy(original_entries[index])
entry.modified = false

return entry
end

entry.data.path = path

return entry
end

---@class grapple.tag.content.change
---@field action "insert" | "move" | "remove"
---@field priority integer
---@field opts grapple.tag.container.insert | grapple.tag.container.move | grapple.tag.container.get
---@field opts grapple.options

---@param original grapple.window.entry[]
---@param modified grapple.window.parsed_entry[]
Expand All @@ -281,37 +290,27 @@ function TagContent:diff(original, modified)
end

for i, entry in ipairs(vim.tbl_filter(has_path, modified)) do
---@type grapple.tag.content.data
local data = entry.data

if not data.path then
goto continue
end

local name, cursor
if entry.index then
local original_entry = original[entry.index]
---@type grapple.tag_content.data
local data

---@type grapple.tag.content.data
local original_data = original_entry.data

if original_data.path == data.path then
name = original_data.name
cursor = original_data.cursor
end
if not entry.modified then
data = original[entry.index].data
else
data = entry.data
end

table.insert(changes, {
---@type grapple.tag.content.change
local change = {
action = "insert",
opts = {
path = entry.data.path,
name = name,
cursor = cursor,
path = data.path,
name = data.name,
cursor = data.cursor,
index = i,
},
})
}

::continue::
table.insert(changes, change)
end

return changes
Expand Down
9 changes: 5 additions & 4 deletions lua/grapple/window.lua
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ end
---@class grapple.window.parsed_entry
---@field data any
---@field line string
---@field modified boolean
---@field index? integer
---@field min_col? integer
---@field highlights? grapple.vim.highlight[]
Expand All @@ -234,7 +235,7 @@ function Window:parse_lines()
local parsed_entries = {}

for _, line in ipairs(lines) do
local entry = self.content:parse_line(line)
local entry = self.content:parse_line(line, self.entries)
table.insert(parsed_entries, entry)
end

Expand All @@ -244,7 +245,7 @@ end
---@param line string
---@return integer min_col
function Window:minimum_column(line)
local parsed_entry = self.content:parse_line(line)
local parsed_entry = self.content:parse_line(line, self.entries)

local index = parsed_entry.index
if not index then
Expand Down Expand Up @@ -497,7 +498,7 @@ end
---@return grapple.window.parsed_entry
function Window:current_entry()
local current_line = self:current_line()
local entry = self.content:parse_line(current_line)
local entry = self.content:parse_line(current_line, self.entries)
return entry
end

Expand All @@ -513,7 +514,7 @@ function Window:entry(opts)
return nil, string.format("no entry for index: %s", opts.index)
end

local entry = self.content:parse_line(line)
local entry = self.content:parse_line(line, self.entries)

return entry, nil
end
Expand Down

0 comments on commit 374e72a

Please sign in to comment.