-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfile_read.lua
More file actions
151 lines (129 loc) · 3.97 KB
/
Copy pathfile_read.lua
File metadata and controls
151 lines (129 loc) · 3.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
-- MCP Tool: file_read
-- Read content from one or more files with optional line ranges
-- Supports single file, batch files, and line range extraction
-- Entry kind: function.lua
local json = require("json")
local projects = require("projects")
--- Split content into lines, preserving empty lines
local function split_lines(content)
local lines = {}
local pos = 1
while pos <= #content do
local nl = string.find(content, "\n", pos, true)
if nl then
table.insert(lines, string.sub(content, pos, nl - 1))
pos = nl + 1
else
table.insert(lines, string.sub(content, pos))
pos = #content + 1
end
end
-- If content ends with newline, there's an empty trailing entry
if #content > 0 and string.sub(content, -1) == "\n" then
-- The last insert was the text before final \n, which is correct
end
return lines
end
--- Read a single file, optionally a line range
local function read_single(vol, path, start_line, end_line)
if not path or path == "" then
return nil, "path is required"
end
local content, err = vol:readfile(path)
if err then
return nil, tostring(err)
end
-- Full file read
if not start_line and not end_line then
return content, nil
end
-- Line range extraction
local lines = split_lines(content)
local total = #lines
local s = start_line or 1
local e = end_line or total
if s < 1 then s = 1 end
if e > total then e = total end
if s > total then
return nil, string.format("startLine %d exceeds total lines %d", s, total)
end
local result = {}
local width = #tostring(e)
table.insert(result, string.format(
"=== %s (lines %d-%d of %d) ===", path, s, e, total
))
table.insert(result, "")
for i = s, e do
table.insert(result, string.format(
"%" .. width .. "d | %s", i, lines[i] or ""
))
end
return table.concat(result, "\n"), nil
end
--- Read multiple files in batch
local function read_batch(vol, paths)
local output = {}
local errors = 0
for _, path in ipairs(paths) do
local content, err = vol:readfile(path)
if err then
table.insert(output, "=== File: " .. path .. " [ERROR] ===")
table.insert(output, tostring(err))
errors = errors + 1
else
table.insert(output, "=== File: " .. path .. " ===")
table.insert(output, content)
end
table.insert(output, "")
end
return table.concat(output, "\n"), errors
end
local function call(arguments)
local vol, err = projects.resolve(arguments.project)
if err then
return "Error: " .. err
end
-- Collect all paths
local paths = {}
if arguments.path and arguments.path ~= "" then
table.insert(paths, arguments.path)
end
if arguments.paths then
for _, p in ipairs(arguments.paths) do
if p and p ~= "" then
table.insert(paths, p)
end
end
end
if #paths == 0 then
return "Error: provide either 'path' or 'paths' parameter"
end
-- Line range only for single file
local has_range = arguments.startLine or arguments.endLine
if has_range and #paths > 1 then
return "Error: line range (startLine/endLine) only applies to single file requests"
end
-- Single file
if #paths == 1 and not arguments.paths then
local content, read_err = read_single(
vol, paths[1], arguments.startLine, arguments.endLine
)
if read_err then
return "Error: " .. read_err
end
return content
end
-- Batch
-- Deduplicate
local seen = {}
local unique = {}
for _, p in ipairs(paths) do
if not seen[p] then
seen[p] = true
table.insert(unique, p)
end
end
local content, _ = read_batch(vol, unique)
return content
end
return { call = call }