lib/standard/stream: Renamed streams for more explicit denomination
[nit.git] / contrib / pep8analysis / src / location.nit
1 # This file is part of NIT ( http://www.nitlanguage.org ).
2 #
3 # Copyright 2009 Jean-Sebastien Gelinas <calestar@gmail.com>
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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.
16
17 # This module is used to model Nit source-file and locations in source-file
18 module location
19
20 # A raw text Nit source file
21 class SourceFile
22 # The path of the source
23 var filename: String
24
25 # The content of the source
26 var string: String
27
28 # Create a new sourcefile using a filename and a stream
29 init(filename: String, stream: Reader)
30 do
31 self.filename = filename
32 string = stream.read_all
33 line_starts[0] = 0
34 end
35
36 # Create a new sourcefile using a dummy filename and a given content
37 init from_string(filename: String, string: String)
38 do
39 self.filename = filename
40 self.string = string
41 line_starts[0] = 0
42 end
43
44 # Position of each line start
45 var line_starts: Array[Int] = new Array[Int]
46 end
47
48 # A location inside a source file
49 class Location
50 super Comparable
51 redef type OTHER: Location
52
53 var file: nullable SourceFile
54 var line_start: Int
55 var line_end: Int
56 var column_start: Int
57 var column_end: Int
58
59 init(f: nullable SourceFile, line_s: Int, line_e: Int, column_s: Int, column_e: Int) do
60 file = f
61 line_start = line_s
62 line_end = line_e
63 column_start = column_s
64 column_end = column_e
65 end
66
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
69
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
72
73 # The verbatim associated text in the source-file
74 fun text: String
75 do
76 var res = self.text_cache
77 if res != null then return res
78 var l = self
79 var pstart = self.pstart
80 var pend = self.pend
81 res = l.file.string.substring(pstart, pend-pstart+1)
82 self.text_cache = res
83 return res
84 end
85
86 private var text_cache: nullable String
87
88 init with_file(f: SourceFile) do init(f,0,0,0,0)
89
90 redef fun ==(other: nullable Object): Bool do
91 if other == null then return false
92 if not other isa Location then return false
93
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
99
100 return true
101 end
102
103 fun located_in(loc: nullable Location): Bool do
104 if loc == null then return false
105
106 if line_start < loc.line_start then return false
107 if line_start > loc.line_end then return false
108
109 if line_end > loc.line_end then return false
110
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
114 end
115
116 if line_end == loc.line_end and column_end > loc.column_end then return false
117
118 return true
119 end
120
121 redef fun to_s: String do
122 var file_part = ""
123 if file != null then
124 file_part = file.filename
125 if file.filename.length > 0 then file_part += ":"
126 end
127
128 if line_start == line_end then
129 if column_start == column_end then
130 return "{file_part}{line_start},{column_start}"
131 else
132 return "{file_part}{line_start},{column_start}--{column_end}"
133 end
134 else
135 return "{file_part}{line_start},{column_start}--{line_end},{column_end}"
136 end
137 end
138
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)
143 else
144 relative = new Location(self.file, self.line_start, self.line_end, self.column_start, self.column_end)
145 end
146 return relative.to_s
147 end
148
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
153
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
157
158 return column_end < other.column_end
159 end
160
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;"`
163 # * `"0;31"` for red
164 # * `"1;31"` for bright red
165 # * `"0;32"` for green
166 fun colored_line(color: String): String
167 do
168 var esc = 27.ascii
169 var def = "{esc}[0m"
170 var col = "{esc}[{color}m"
171
172 var l = self
173 var i = l.line_start
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[line_end+1] != '\n' and string[line_end+1] != '\r' do
178 line_end += 1
179 end
180 var lstart = string.substring(line_start, l.column_start - 1)
181 var cend
182 if i != l.line_end then
183 cend = line_end - line_start + 1
184 else
185 cend = l.column_end
186 end
187 var lmid
188 var lend
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)
192 else
193 lmid = ""
194 lend = ""
195 end
196 var indent = new FlatBuffer
197 for j in [line_start..line_start+l.column_start-1[ do
198 if string[j] == '\t' then
199 indent.add '\t'
200 else
201 indent.add ' '
202 end
203 end
204 return "\t{lstart}{col}{lmid}{def}{lend}\n\t{indent}^"
205 end
206 end
207