bugfix: highlight search patterns on the right line
[lines.love.git] / source_file.lua
blob3eaf6c34c1d36275aba00d662ec0708602277768
1 -- primitives for saving to file and loading from file
3 function file_exists(filename)
4 local infile = App.open_for_reading(filename)
5 if infile then
6 infile:close()
7 return true
8 else
9 return false
10 end
11 end
13 function load_from_disk(State)
14 local infile = App.open_for_reading(State.filename)
15 State.lines = load_from_file(infile)
16 if infile then infile:close() end
17 end
19 function load_from_file(infile)
20 local result = {}
21 if infile then
22 local infile_next_line = infile:lines() -- works with both Lua files and LÖVE Files (https://www.love2d.org/wiki/File)
23 while true do
24 local line = infile_next_line()
25 if line == nil then break end
26 if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
27 table.insert(result, load_drawing(infile_next_line))
28 else
29 table.insert(result, {mode='text', data=line})
30 end
31 end
32 end
33 if #result == 0 then
34 table.insert(result, {mode='text', data=''})
35 end
36 return result
37 end
39 function save_to_disk(State)
40 local outfile = App.open_for_writing(State.filename)
41 if outfile == nil then
42 error('failed to write to "'..State.filename..'"')
43 end
44 for _,line in ipairs(State.lines) do
45 if line.mode == 'drawing' then
46 store_drawing(outfile, line)
47 else
48 outfile:write(line.data)
49 outfile:write('\n')
50 end
51 end
52 outfile:close()
53 end
55 function load_drawing(infile_next_line)
56 local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
57 while true do
58 local line = infile_next_line()
59 assert(line)
60 if line == '```' then break end
61 local shape = json.decode(line)
62 if shape.mode == 'freehand' then
63 -- no changes needed
64 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
65 local name = shape.p1.name
66 shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
67 drawing.points[shape.p1].name = name
68 name = shape.p2.name
69 shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
70 drawing.points[shape.p2].name = name
71 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
72 for i,p in ipairs(shape.vertices) do
73 local name = p.name
74 shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
75 drawing.points[shape.vertices[i]].name = name
76 end
77 elseif shape.mode == 'circle' or shape.mode == 'arc' then
78 local name = shape.center.name
79 shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
80 drawing.points[shape.center].name = name
81 elseif shape.mode == 'deleted' then
82 -- ignore
83 else
84 print(shape.mode)
85 assert(false)
86 end
87 table.insert(drawing.shapes, shape)
88 end
89 return drawing
90 end
92 function store_drawing(outfile, drawing)
93 outfile:write('```lines\n')
94 for _,shape in ipairs(drawing.shapes) do
95 if shape.mode == 'freehand' then
96 outfile:write(json.encode(shape))
97 outfile:write('\n')
98 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
99 local line = json.encode({mode=shape.mode, p1=drawing.points[shape.p1], p2=drawing.points[shape.p2]})
100 outfile:write(line)
101 outfile:write('\n')
102 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
103 local obj = {mode=shape.mode, vertices={}}
104 for _,p in ipairs(shape.vertices) do
105 table.insert(obj.vertices, drawing.points[p])
107 local line = json.encode(obj)
108 outfile:write(line)
109 outfile:write('\n')
110 elseif shape.mode == 'circle' then
111 outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius}))
112 outfile:write('\n')
113 elseif shape.mode == 'arc' then
114 outfile:write(json.encode({mode=shape.mode, center=drawing.points[shape.center], radius=shape.radius, start_angle=shape.start_angle, end_angle=shape.end_angle}))
115 outfile:write('\n')
116 elseif shape.mode == 'deleted' then
117 -- ignore
118 else
119 print(shape.mode)
120 assert(false)
123 outfile:write('```\n')
126 -- for tests
127 function load_array(a)
128 local result = {}
129 local next_line = ipairs(a)
130 local i,line,drawing = 0, ''
131 while true do
132 i,line = next_line(a, i)
133 if i == nil then break end
134 --? print(line)
135 if line == '```lines' then -- inflexible with whitespace since these files are always autogenerated
136 --? print('inserting drawing')
137 i, drawing = load_drawing_from_array(next_line, a, i)
138 --? print('i now', i)
139 table.insert(result, drawing)
140 else
141 --? print('inserting text')
142 local line_info = {mode='text'}
143 line_info.data = line
144 table.insert(result, line_info)
147 if #result == 0 then
148 table.insert(result, {mode='text', data=''})
150 return result
153 function load_drawing_from_array(iter, a, i)
154 local drawing = {mode='drawing', h=256/2, points={}, shapes={}, pending={}}
155 local line
156 while true do
157 i, line = iter(a, i)
158 assert(i)
159 --? print(i)
160 if line == '```' then break end
161 local shape = json.decode(line)
162 if shape.mode == 'freehand' then
163 -- no changes needed
164 elseif shape.mode == 'line' or shape.mode == 'manhattan' then
165 local name = shape.p1.name
166 shape.p1 = Drawing.find_or_insert_point(drawing.points, shape.p1.x, shape.p1.y, --[[large width to minimize overlap]] 1600)
167 drawing.points[shape.p1].name = name
168 name = shape.p2.name
169 shape.p2 = Drawing.find_or_insert_point(drawing.points, shape.p2.x, shape.p2.y, --[[large width to minimize overlap]] 1600)
170 drawing.points[shape.p2].name = name
171 elseif shape.mode == 'polygon' or shape.mode == 'rectangle' or shape.mode == 'square' then
172 for i,p in ipairs(shape.vertices) do
173 local name = p.name
174 shape.vertices[i] = Drawing.find_or_insert_point(drawing.points, p.x,p.y, --[[large width to minimize overlap]] 1600)
175 drawing.points[shape.vertices[i]].name = name
177 elseif shape.mode == 'circle' or shape.mode == 'arc' then
178 local name = shape.center.name
179 shape.center = Drawing.find_or_insert_point(drawing.points, shape.center.x,shape.center.y, --[[large width to minimize overlap]] 1600)
180 drawing.points[shape.center].name = name
181 elseif shape.mode == 'deleted' then
182 -- ignore
183 else
184 print(shape.mode)
185 assert(false)
187 table.insert(drawing.shapes, shape)
189 return i, drawing
192 function is_absolute_path(path)
193 local os_path_separator = package.config:sub(1,1)
194 if os_path_separator == '/' then
195 -- POSIX systems permit backslashes in filenames
196 return path:sub(1,1) == '/'
197 elseif os_path_separator == '\\' then
198 if path:sub(2,2) == ':' then return true end -- DOS drive letter followed by volume separator
199 local f = path:sub(1,1)
200 return f == '/' or f == '\\'
201 else
202 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
206 function is_relative_path(path)
207 return not is_absolute_path(path)
210 function dirname(path)
211 local os_path_separator = package.config:sub(1,1)
212 if os_path_separator == '/' then
213 -- POSIX systems permit backslashes in filenames
214 return path:match('.*/') or './'
215 elseif os_path_separator == '\\' then
216 return path:match('.*[/\\]') or './'
217 else
218 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
222 function test_dirname()
223 check_eq(dirname('a/b'), 'a/', 'F - test_dirname')
224 check_eq(dirname('x'), './', 'F - test_dirname/current')
227 function basename(path)
228 local os_path_separator = package.config:sub(1,1)
229 if os_path_separator == '/' then
230 -- POSIX systems permit backslashes in filenames
231 return string.gsub(path, ".*/(.*)", "%1")
232 elseif os_path_separator == '\\' then
233 return string.gsub(path, ".*[/\\](.*)", "%1")
234 else
235 error('What OS is this? LÖVE reports that the path separator is "'..os_path_separator..'"')
239 function empty(h)
240 for _,_ in pairs(h) do
241 return false
243 return true