View Source LogicalFile.Section (LogicalFile v1.0.4)

A Section represents lines of text from a backing file that represent a range of logical line numbers within a LogicalFile.

Fields

  • source_path the fully qualified file name of the backing file that the Section represents.
  • range the range of logical line numbers the Section represents
  • lines a list of the lines in the Section
  • offset a value that transforms a logical line number to a local line number within the backing file.

In the simple case a Section represents the entire contents of a backing file. However, a Section can be split and moved (for example when another Section is inserted within its range). Here the offset is adjusted to allow the conversion of logical line numbers to local line numbers in the backing file.

Link to this section Summary

Functions

first_line_number/1 returns the first logical line number of the specified Section.

last_line_number/1 returns the last logical line number of the specified Section.

line/2 returns a String containing the contents of logical line number lno which is expected to be within the range the Section represents.

line_matching/2 takes a Section and either a predicate function or a regular expression and returns a tuple {logical_line_no, line} representing the first line from the Section that matches.

lines_matching/2 takes a Section and either a predicate function or a regular expression and returns a list of tuples of the form {logical_lno, line} for each line that matches.

new/1 creates a new Section representing lines from the specified file.

resolve_line/2 takes a Section and a logical line number line that is expected to be within the range of the Section and returns a tuple {file, line} representing the file backing the Section and the corresponding local line number within the Section

set_offset/2 replaces the line number offset of the specified Section.

set_range/2 replaces the logical line number range of the specified Section.

shift/2 takes a Section and a number of lines to offset the section by_lines and returns a new Section containing the same lines with the logical line number range and offset shifted appropriately.

size/1 returns the number of lines in the specified Section.

split/2 takes a Section and a logical line number at_line expected to be within the Section and returns a tuple {before_section, after_section} derived by splitting the contents of the Section at the specified line.

splittable?/1 takes a Section and determines whether it is splittable. In general it's not splittable if it contains only one line.

total_size/1 returns the number of lines contained in the given list of Sections.

update_line/3 takes a Section a logical number number expected to be within the Section and a function. It replaces that line with the result of calling the function with the existing line.

Link to this section Functions

Link to this function

first_line_number(section)

View Source

first_line_number/1 returns the first logical line number of the specified Section.

Link to this function

last_line_number(section)

View Source

last_line_number/1 returns the last logical line number of the specified Section.

line/2 returns a String containing the contents of logical line number lno which is expected to be within the range the Section represents.

Examples

iex> alias LogicalFile.Section
iex> section = Section.new("test/support/main.source")
iex> assert "%(include.source)" = Section.line(section, 6)
Link to this function

line_matching(section, pred_fn)

View Source

line_matching/2 takes a Section and either a predicate function or a regular expression and returns a tuple {logical_line_no, line} representing the first line from the Section that matches.

Examples

iex> alias LogicalFile.Section
iex> section = Section.new("test/support/main.source")
iex> include = ~r/%((?<file>.*))/
iex> assert {6, "%(include.source)"} = Section.line_matching(section, fn line -> String.length(line) > 5 end)
iex> assert {6, "%(include.source)"} = Section.line_matching(section, include)
Link to this function

lines_matching(section, fun)

View Source

lines_matching/2 takes a Section and either a predicate function or a regular expression and returns a list of tuples of the form {logical_lno, line} for each line that matches.

Examples

iex> alias LogicalFile.Section
iex> section = Section.new("test/support/commented.source")
iex> assert [{3, "%% nothing here"}, {6, "%% or here"}] = Section.lines_matching(section, fn line ->
...>  String.starts_with?(line, "%%")
...> end)
iex> assert [{1, "one"}, {2, "two"}, {8, "six"}] = Section.lines_matching(section, fn line -> String.length(line) <4 end)

new/1 creates a new Section representing lines from the specified file.

new/4 creates a new Section representing the contents of the file specified by source_path and representing a particular range of logical line numbers and their offset new/4 for more information.

a range of lines with an offset. he offset determines how the line numbers in the range are converted into lines in the source file. For example if the offset is -5 then line 10 will correspond to line 5 of the source file.

Examples

iex> alias LogicalFile.Section
iex> section = Section.new("test/support/main.source")
iex> assert "test/support/main.source" = section.source_path
iex> assert 11 = Section.size(section)
iex> assert 1..11 = section.range
iex> assert 0 = section.offset

iex> alias LogicalFile.Section
iex> %Section{source_path: source_path, range: range, lines: lines} =
...>  Section.new("foo.source", 1..6, ["one", "two", "three", "four", "five", "six"])
iex> assert "foo.source" = source_path
iex> assert 1..6 = range
iex> assert 6 = Enum.count(lines)

iex> alias LogicalFile.Section
iex> %Section{offset: offset} = Section.new("foo.source", 1..2, ["one", "two"], -7)
iex> assert -7 = offset

iex> alias LogicalFile.Section
iex> assert_raise(RuntimeError, fn ->
...> %Section{} =
...>  Section.new("foo.source", 1..5, ["one", "two", "three", "four"])
...> end)
Link to this function

new(source_path, range, lines, offset \\ 0)

View Source
Link to this function

resolve_line(section, line)

View Source

resolve_line/2 takes a Section and a logical line number line that is expected to be within the range of the Section and returns a tuple {file, line} representing the file backing the Section and the corresponding local line number within the Section

number Maps a line number coming from a source map that may include many sections into a line number relative to the section. For example a section may represent source included from another file.

E.g. File 1 contains 20 lines & File 2 contains 10 lines if we insert File 2 we get a structure like:

Lines 1..10 => File 1: Lines 1..10 Lines 11..20 => File 2: Lines 1..10 Lines 21..30 => File 1: Lines 11..20

If we ask for line 15 this maps to File 2, line 5. This means file 2 is offset from the map by -10. If we ask for line 25 this maps to file 1 line 15, again offset by -10.

Examples

iex> alias LogicalFile.Section
iex> section =
...>  Section.new("test/support/main.source")
...>  |> Section.set_range(21..30)
...>  |> Section.set_offset(-10)
iex> assert {"test/support/main.source", 15} = Section.resolve_line(section, 25)
Link to this function

set_offset(section, new_offset)

View Source

set_offset/2 replaces the line number offset of the specified Section.

Link to this function

set_range(section, new_range)

View Source

set_range/2 replaces the logical line number range of the specified Section.

Link to this function

shift(section, by_lines)

View Source

shift/2 takes a Section and a number of lines to offset the section by_lines and returns a new Section containing the same lines with the logical line number range and offset shifted appropriately.

Examples

iex> alias LogicalFile.Section
iex> section =
...>  Section.new("foo.source", 1..4, ["one", "two", "three", "four"])
...>  |> Section.shift(10)
iex> assert 11..14 = section.range
iex> assert -10 = section.offset

size/1 returns the number of lines in the specified Section.

Examples

iex> alias LogicalFile.Section
iex> section = Section.new("foo.source", 1..4, ["one", "two", "three", "four"])
iex> assert 4 = Section.size(section)

split/2 takes a Section and a logical line number at_line expected to be within the Section and returns a tuple {before_section, after_section} derived by splitting the contents of the Section at the specified line.

The before_section contains all lines up to, but not including, the specified line, the after_section contains all lines from the specified line to the end of the Section.

Note: It is illegal to attempt to split a Section containing one line, or to set the split point on the first or last line of a Section. In any of these cases an exception is raised.

Examples

iex> alias LogicalFile.Section
iex> section = Section.new("foo.source", 1..6, ["one", "two", "three", "four", "five", "six"])
iex> {%Section{} = first, %Section{} = second} = Section.split(section, 4)
iex> assert "foo.source" = first.source_path
iex> assert 1..3 = first.range
iex> assert ["one", "two", "three"] = first.lines
iex> assert "foo.source" = second.source_path
iex> assert 4..6 = second.range
iex> assert ["four", "five", "six"] = second.lines

iex> alias LogicalFile.Section
iex> assert_raise(RuntimeError, fn ->
...>  section = Section.new("foo.source", 1..4, ["one", "two", "three", "four"])
...>  Section.split(section, 0)
...> end)

iex> alias LogicalFile.Section
iex> section = Section.new("foo.source", 1..3, ["alpha", "beta", "delta"]) |> Section.shift(36)
iex> {s1, s2} = Section.split(section, 38)
iex> assert %Section{range: 37..37, offset: -36, lines: ["alpha"]} = s1
iex> assert %Section{range: 38..39, offset: -36, lines: ["beta", "delta"]} = s2

iex> alias LogicalFile.Section
iex> section = Section.new("bar.source", 29..33, ["         ", "   ", "", "end", ""], -4)
iex> {s1, s2} = Section.split(section, 30)
iex> assert %Section{range: 29..29, offset: -4, lines: ["         "]} = s1
iex> assert %Section{range: 30..33, offset: -4, lines: ["   ", "", "end", ""]} = s2

splittable?/1 takes a Section and determines whether it is splittable. In general it's not splittable if it contains only one line.

Examples

iex> alias LogicalFile.Section
iex> section1 = Section.new("bar.source", 1..1, ["one"])
iex> section2 = Section.new("foo.source", 1..2, ["one", "two"])
iex> assert not Section.splittable?(section1)
iex> assert Section.splittable?(section2)

total_size/1 returns the number of lines contained in the given list of Sections.

Examples

iex> alias LogicalFile.Section
iex> section1 = Section.new("foo.source", 1..4, ["one", "two", "three", "four"])
iex> section2 = Section.new("bar.source", 5..7, ["alpha", "beta", "delta"])
iex> assert 7 = Section.total_size([section1, section2])
Link to this function

update_line(section, lno, fun)

View Source

update_line/3 takes a Section a logical number number expected to be within the Section and a function. It replaces that line with the result of calling the function with the existing line.

Examples

iex> alias LogicalFile.Section
iex> section = Section.new("test/support/main.source")
...>  |> Section.update_line(6, fn line -> String.duplicate(" ", String.length(line)) end)
iex> assert "                 " = Section.line(section, 6)