Skip to content

Commit 0747ea0

Browse files
timholyc42f
andauthored
Add lineno to SourceFile (#191)
Co-authored-by: c42f <[email protected]>
1 parent dae2d23 commit 0747ea0

File tree

2 files changed

+37
-15
lines changed

2 files changed

+37
-15
lines changed

src/source_files.jl

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
"""
2-
SourceFile(code [; filename=nothing])
2+
SourceFile(code [; filename=nothing, first_line=1])
33
4-
A UTF-8 source code string with associated file name and indexing structures.
4+
A UTF-8 source code string with associated file name and line number.
5+
6+
`SourceFile` stores the character positions of line starts to facilitate indexing.
57
"""
68
struct SourceFile
79
# We use `code::String` for now but it could be some other UTF-8 based
@@ -11,11 +13,12 @@ struct SourceFile
1113
# https://en.wikipedia.org/wiki/Rope_(data_structure)
1214
code::String
1315
filename::Union{Nothing,String}
16+
first_line::Int
1417
# String index of start of every line
1518
line_starts::Vector{Int}
1619
end
1720

18-
function SourceFile(code::AbstractString; filename=nothing)
21+
function SourceFile(code::AbstractString; filename=nothing, first_line=1)
1922
line_starts = Int[1]
2023
for i in eachindex(code)
2124
# The line is considered to start after the `\n`
@@ -25,31 +28,33 @@ function SourceFile(code::AbstractString; filename=nothing)
2528
if isempty(code) || last(code) != '\n'
2629
push!(line_starts, ncodeunits(code)+1)
2730
end
28-
SourceFile(code, filename, line_starts)
31+
SourceFile(code, filename, first_line, line_starts)
2932
end
3033

31-
function SourceFile(; filename)
32-
SourceFile(read(filename, String); filename=filename)
34+
function SourceFile(; filename, kwargs...)
35+
SourceFile(read(filename, String); filename=filename, kwargs...)
3336
end
3437

3538
# Get line number of the given byte within the code
36-
function source_line(source::SourceFile, byte_index)
37-
line = searchsortedlast(source.line_starts, byte_index)
38-
return (line < lastindex(source.line_starts)) ? line : line-1
39+
function source_line_index(source::SourceFile, byte_index)
40+
lineidx = searchsortedlast(source.line_starts, byte_index)
41+
return (lineidx < lastindex(source.line_starts)) ? lineidx : lineidx-1
3942
end
43+
_source_line(source::SourceFile, lineidx) = lineidx + source.first_line - 1
44+
source_line(source::SourceFile, byte_index) = _source_line(source, source_line_index(source, byte_index))
4045

4146
"""
4247
Get line number and character within the line at the given byte index.
4348
"""
4449
function source_location(source::SourceFile, byte_index)
45-
line = source_line(source, byte_index)
46-
i = source.line_starts[line]
50+
lineidx = source_line_index(source, byte_index)
51+
i = source.line_starts[lineidx]
4752
column = 1
4853
while i < byte_index
4954
i = nextind(source.code, i)
5055
column += 1
5156
end
52-
line, column
57+
_source_line(source, lineidx), column
5358
end
5459

5560
"""
@@ -58,9 +63,9 @@ Get byte range of the source line at byte_index, buffered by
5863
"""
5964
function source_line_range(source::SourceFile, byte_index;
6065
context_lines_before=0, context_lines_after=0)
61-
line = source_line(source, byte_index)
62-
fbyte = source.line_starts[max(line-context_lines_before, 1)]
63-
lbyte = source.line_starts[min(line+1+context_lines_after, end)] - 1
66+
lineidx = source_line_index(source, byte_index)
67+
fbyte = source.line_starts[max(lineidx-context_lines_before, 1)]
68+
lbyte = source.line_starts[min(lineidx+1+context_lines_after, end)] - 1
6469
fbyte,lbyte
6570
end
6671

test/source_files.jl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,21 @@
99
@test source_location(SourceFile("a\nb\n"), 3) == (2,1)
1010
@test source_location(SourceFile("a\nb\n"), 4) == (2,2)
1111
@test source_location(SourceFile("a\nb\n"), 5) == (2,3)
12+
13+
@test source_location(SourceFile("a"; first_line=7), 1) == (7,1)
14+
@test source_location(SourceFile("a"; first_line=7), 2) == (7,2)
15+
16+
@test source_location(SourceFile("a\n"; first_line=7), 2) == (7,2)
17+
@test source_location(SourceFile("a\n"; first_line=7), 3) == (7,3)
18+
19+
@test source_location(SourceFile("a\nb\n"; first_line=7), 2) == (7,2)
20+
@test source_location(SourceFile("a\nb\n"; first_line=7), 3) == (8,1)
21+
@test source_location(SourceFile("a\nb\n"; first_line=7), 4) == (8,2)
22+
@test source_location(SourceFile("a\nb\n"; first_line=7), 5) == (8,3)
23+
24+
mktemp() do path, io
25+
write(io, "a\n")
26+
@test source_location(SourceFile(; filename=path), 1) == (1,1)
27+
@test source_location(SourceFile(; filename=path, first_line=7), 1) == (7,1)
28+
end
1229
end

0 commit comments

Comments
 (0)