Skip to content

Commit cbaeb9f

Browse files
authored
fix: filetype detection freeze with binary files (#339)
* fix: filetype detection freeze with binary files Filetype detection used to read the first line for shebang and the last line for modeline detection. This was really slow with large binary files that don't contain newlines. Add Path:readbyterange(offset, length) to allow reading a specific range and reimplement detect_from_modeline and detect_from_shebang to work with the first 256 and last 256 bytes of a file. Fixes #307 and closes #322. * test: readbyterange
1 parent 0d66015 commit cbaeb9f

3 files changed

Lines changed: 54 additions & 4 deletions

File tree

lua/plenary/filetype.lua

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -130,19 +130,21 @@ filetype.detect_from_name = function(filepath)
130130
end
131131

132132
filetype.detect_from_modeline = function(filepath)
133-
local tail = Path:new(filepath):tail(1)
133+
local tail = Path:new(filepath):readbyterange(-256, 256)
134134
if not tail then
135135
return ""
136136
end
137-
return filetype._parse_modeline(tail)
137+
local lines = vim.split(tail, "\n")
138+
return filetype._parse_modeline(lines[#lines])
138139
end
139140

140141
filetype.detect_from_shebang = function(filepath)
141-
local head = Path:new(filepath):head(1)
142+
local head = Path:new(filepath):readbyterange(0, 256)
142143
if not head then
143144
return ""
144145
end
145-
return filetype._parse_shebang(head)
146+
local lines = vim.split("", "\n")
147+
return filetype._parse_shebang(lines[1])
146148
end
147149

148150
--- Detect a filetype from a path.

lua/plenary/path.lua

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -844,4 +844,36 @@ function Path:iter()
844844
end
845845
end
846846

847+
function Path:readbyterange(offset, length)
848+
self = check_self(self)
849+
850+
local fd = uv.fs_open(self:expand(), "r", 438)
851+
if not fd then
852+
return
853+
end
854+
local stat = assert(uv.fs_fstat(fd))
855+
if stat.type ~= "file" then
856+
uv.fs_close(fd)
857+
return nil
858+
end
859+
860+
if offset < 0 then
861+
offset = stat.size + offset
862+
end
863+
864+
data = ""
865+
while #data < length do
866+
local read_chunk = assert(uv.fs_read(fd, length - #data, offset))
867+
if #read_chunk == 0 then
868+
break
869+
end
870+
data = data .. read_chunk
871+
offset = offset + #read_chunk
872+
end
873+
874+
assert(uv.fs_close(fd))
875+
876+
return data
877+
end
878+
847879
return Path

tests/plenary/path_spec.lua

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,22 @@ SOFTWARE.]]
684684
assert.are.same(should, data)
685685
end)
686686
end)
687+
688+
describe("readbyterange", function()
689+
it("should read bytes at given offset", function()
690+
local p = Path:new "LICENSE"
691+
local data = p:readbyterange(13, 10)
692+
local should = "Copyright "
693+
assert.are.same(should, data)
694+
end)
695+
696+
it("supports negative offset", function()
697+
local p = Path:new "LICENSE"
698+
local data = p:readbyterange(-10, 10)
699+
local should = "SOFTWARE.\n"
700+
assert.are.same(should, data)
701+
end)
702+
end)
687703
end)
688704

689705
-- function TestPath:testIsDir()

0 commit comments

Comments
 (0)