1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # This module is used to model Nit source-file and locations in source-file
20 # A raw text Nit source file
22 # The path of the source
25 # The content of the source
28 # Create a new sourcefile using a filename and a stream
29 init(filename
: String, stream
: IStream)
31 self.filename
= filename
32 string
= stream
.read_all
36 # Create a new sourcefile using a dummy filename and a given content
37 init from_string
(filename
: String, string
: String)
39 self.filename
= filename
44 # Position of each line start
45 var line_starts
: Array[Int] = new Array[Int]
48 # A location inside a source file
51 redef type OTHER: Location
53 var file
: nullable SourceFile
59 init(f
: nullable SourceFile, line_s
: Int, line_e
: Int, column_s
: Int, column_e
: Int) do
63 column_start
= column_s
67 # The index in the start character in the source
68 fun pstart
: Int do return file
.line_starts
[line_start-1
] + column_start-1
70 # The index on the end character in the source
71 fun pend
: Int do return file
.line_starts
[line_end-1
] + column_end-1
73 # The verbatim associated text in the source-file
76 var res
= self.text_cache
77 if res
!= null then return res
79 var pstart
= self.pstart
81 res
= l
.file
.string
.substring
(pstart
, pend-pstart
+1)
86 private var text_cache
: nullable String
88 init with_file
(f
: SourceFile) do init(f
,0,0,0,0)
90 redef fun ==(other
: nullable Object): Bool do
91 if other
== null then return false
92 if not other
isa Location then return false
94 if other
.file
!= file
then return false
95 if other
.line_start
!= line_start
then return false
96 if other
.line_end
!= line_end
then return false
97 if other
.column_start
!= column_start
then return false
98 if other
.column_end
!= column_end
then return false
103 fun located_in
(loc
: nullable Location): Bool do
104 if loc
== null then return false
106 if line_start
< loc
.line_start
then return false
107 if line_start
> loc
.line_end
then return false
109 if line_end
> loc
.line_end
then return false
111 if line_start
== loc
.line_start
then
112 if column_start
< loc
.column_start
then return false
113 if column_start
> loc
.column_end
then return false
116 if line_end
== loc
.line_end
and column_end
> loc
.column_end
then return false
121 redef fun to_s
: String do
124 file_part
= file
.filename
125 if file
.filename
.length
> 0 then file_part
+= ":"
128 if line_start
== line_end
then
129 if column_start
== column_end
then
130 return "{file_part}{line_start},{column_start}"
132 return "{file_part}{line_start},{column_start}--{column_end}"
135 return "{file_part}{line_start},{column_start}--{line_end},{column_end}"
139 fun relative_to
(loc
: nullable Location): String do
140 var relative
: Location
141 if loc
!= null and loc
.file
== self.file
then
142 relative
= new Location(null, self.line_start
, self.line_end
, self.column_start
, self.column_end
)
144 relative
= new Location(self.file
, self.line_start
, self.line_end
, self.column_start
, self.column_end
)
149 redef fun <(other
: OTHER): Bool do
150 if self == other
then return false
151 if self.located_in
(other
) then return true
152 if other
.located_in
(self) then return false
154 if line_start
!= other
.line_start
then return line_start
< other
.line_start
155 if column_start
!= other
.column_start
then return column_start
< other
.column_start
156 if line_end
!= other
.line_end
then return line_end
< other
.line_end
158 return column_end
< other
.column_end
161 # Return the associated line with the location highlihted with color and a carret under the starting position
162 # `color` must be and terminal escape sequence used as `"{escape}[{color}m;"`
164 # * `"1;31"` for bright red
165 # * `"0;32"` for green
166 fun colored_line
(color
: String): String
170 var col
= "{esc}[{color}m"
174 var line_start
= l
.file
.line_starts
[i-1
]
175 var line_end
= line_start
176 var string
= l
.file
.string
177 while line_end
+1 < string
.length
and string
.chars
[line_end
+1] != '\n' and string
.chars
[line_end
+1] != '\r' do
180 var lstart
= string
.substring
(line_start
, l
.column_start
- 1)
182 if i
!= l
.line_end
then
183 cend
= line_end
- line_start
+ 1
189 if line_start
+ cend
<= string
.length
then
190 lmid
= string
.substring
(line_start
+ l
.column_start
- 1, cend
- l
.column_start
+ 1)
191 lend
= string
.substring
(line_start
+ cend
, line_end
- line_start
- cend
+ 1)
196 var indent
= new Buffer
197 for j
in [line_start
..line_start
+l
.column_start-1
[ do
198 if string
.chars
[j
] == '\t' then
204 return "\t{lstart}{col}{lmid}{def}{lend}\n\t{indent}^"