@@ -2,6 +2,7 @@ module main
2
2
3
3
import gx
4
4
import os
5
+ import time
5
6
6
7
fn (mut ved Ved) draw () {
7
8
mut view := ved.view
@@ -154,21 +155,29 @@ fn (ved &Ved) split_x(i int) int {
154
155
155
156
fn (mut ved Ved) draw_split (i int , split_from int ) {
156
157
view := ved.views[i]
157
- ved.is_ml_comment = false
158
+ // Determine initial comment state for the first visible line
159
+ // (handle /**/ comments blocks that start before current page)
160
+ mut current_is_ml_comment := false
161
+ if view.hl_on {
162
+ current_is_ml_comment = ved.determine_ml_comment_state (view, view.from)
163
+ }
164
+ ext := os.file_ext (view.path) // Cache extension
165
+ mcomment := get_mcomment_by_ext (ext) // Cache delimiters
166
+
158
167
split_width := ved.split_width ()
159
168
split_x := split_width * (i - split_from)
160
169
// Vertical split line
161
170
ved.gg.draw_line (split_x, ved.cfg.line_height + 1 , split_x, ved.win_height, ved.cfg.split_color)
162
171
// Lines
163
- mut line_nr := 1 // relative y
172
+ mut line_nr_rel := 1 // relative y on screen
164
173
for j := view.from; j < view.from + ved.page_height && j < view.lines.len; j++ {
165
174
line := view.lines[j]
166
175
if line.len > 5000 {
167
176
println ('line len too big! views[${i} ].lines[${j} ] (${line.len} ) path=${ved.view.path} ' )
168
177
continue
169
178
}
170
179
x := split_x + view.padding_left
171
- y := line_nr * ved.cfg.line_height
180
+ y := line_nr_rel * ved.cfg.line_height
172
181
// Error bg
173
182
if view.error_y == j {
174
183
ved.gg.draw_rect_filled (x + 10 , y - 1 , split_width - view.padding_left - 10 ,
@@ -201,7 +210,7 @@ fn (mut ved Ved) draw_split(i int, split_from int) {
201
210
}
202
211
mut s := line[nr_tabs..] // tabs have been skipped, remove them from the string
203
212
if s == '' {
204
- line_nr ++
213
+ line_nr_rel ++
205
214
continue
206
215
}
207
216
// Number of chars to display in this view
@@ -230,16 +239,51 @@ fn (mut ved Ved) draw_split(i int, split_from int) {
230
239
s = s[..max]
231
240
}
232
241
}
242
+
233
243
if view.hl_on {
234
- // println('line="$s" nrtabs=$nr_tabs line_x=$line_x')
235
- ved.draw_text_line (line_x, y, s, os.file_ext (view.path))
244
+ // Handle multi page /**/
245
+ start_comment_pos := s.index (mcomment.start1 .str () + mcomment.start2 .str ()) or { - 1 }
246
+ end_comment_pos := s.index (mcomment.end1 .str () + mcomment.end2 .str ()) or { - 1 }
247
+ if current_is_ml_comment {
248
+ if end_comment_pos != - 1 { // Comment ends on this line
249
+ // Draw comment part
250
+ comment_part := s[..end_comment_pos + 2 ]
251
+ ved.gg.draw_text (line_x, y, comment_part, ved.cfg.comment_cfg)
252
+ // Draw rest normally
253
+ normal_part := s[end_comment_pos + 2 ..]
254
+ if normal_part.len > 0 {
255
+ normal_part_x := line_x + comment_part.len * ved.cfg.char_width // Adjust based on actual rendered width if needed
256
+ ved.draw_text_line_standard_syntax (normal_part_x, y, normal_part,
257
+ ext)
258
+ }
259
+ current_is_ml_comment = false // Update state for next line
260
+ } else { // Entire line is inside the comment
261
+ ved.gg.draw_text (line_x, y, s, ved.cfg.comment_cfg)
262
+ // current_is_ml_comment remains true
263
+ }
264
+ } else { // current_is_ml_comment is false
265
+ if start_comment_pos != - 1
266
+ && (end_comment_pos == - 1 || end_comment_pos < start_comment_pos) { // Comment starts here and continues
267
+ // Draw normal part before / *
268
+ normal_part := s[..start_comment_pos]
269
+ if normal_part.len > 0 {
270
+ ved.draw_text_line_standard_syntax (line_x, y, normal_part, ext)
271
+ }
272
+ // Draw comment part from / * onwards
273
+ comment_part := s[start_comment_pos..]
274
+ comment_part_x := line_x + normal_part.len * ved.cfg.char_width // Adjust based on actual rendered width if needed
275
+ ved.gg.draw_text (comment_part_x, y, comment_part, ved.cfg.comment_cfg)
276
+ current_is_ml_comment = true // Update state for next line
277
+ } else { // No multiline comment start OR it's a single-line /* ... */
278
+ // Use the standard highlighter for the whole line
279
+ ved.draw_text_line_standard_syntax (line_x, y, s, ext)
280
+ // current_is_ml_comment remains false
281
+ }
282
+ }
236
283
} else {
237
284
ved.gg.draw_text (line_x, y, s, ved.cfg.txt_cfg)
238
285
}
239
- // if old_len != s.len {
240
- // ved.draw_text_line(line_x, y + 1, '!!!')
241
- // }
242
- line_nr++
286
+ line_nr_rel++
243
287
}
244
288
}
245
289
@@ -257,7 +301,10 @@ fn (mut ved Ved) add_chunk(typ ChunkKind, start int, end int) {
257
301
ved.chunks << chunk
258
302
}
259
303
260
- fn (mut ved Ved) draw_text_line (x int , y int , line string , ext string ) {
304
+ // Handles syntax highlighting for a line assuming it does *not* start within a multi-line comment
305
+ // and does *not* start a multi-line comment that continues to the next line.
306
+ // It handles single-line comments (//, #), strings, keywords, literals, and single-line /* ... */ comments.
307
+ fn (mut ved Ved) draw_text_line_standard_syntax (x int , y int , line string , ext string ) {
261
308
// mcomment := get_mcomment_by_ext(os.file_ext(ved.view.path))
262
309
mcomment := get_mcomment_by_ext (ext)
263
310
// Red/green test hack
@@ -274,74 +321,97 @@ fn (mut ved Ved) draw_text_line(x int, y int, line string, ext string) {
274
321
ved.chunks = []
275
322
cur_syntax := ved.syntaxes[ved.current_syntax_idx] or { Syntax{} }
276
323
// TODO use runes for everything to fix keyword + 2+ byte rune words
277
- for i := 0 ; i < line.len; i++ {
324
+
325
+ mut i := 0 // Use mut i instead of for loop index to allow manual increment
326
+ for i < line.len {
278
327
start := i
279
328
// Comment // #
280
329
if i > 0 && line[i - 1 ] == `/` && line[i] == `/` {
281
330
ved.add_chunk (.a_comment, start - 1 , line.len)
331
+ i = line.len // End the loop
282
332
break
283
333
}
284
334
if line[i] == `#` {
285
335
ved.add_chunk (.a_comment, start, line.len)
336
+ i = line.len // End the loop
286
337
break
287
338
}
288
- // Comment /*
289
- // (unless it's /* line */ which is a single line)
290
- if i > 0 && line[i - 1 ] == mcomment.start1 && line[i] == mcomment.start2
291
- && ! (line[line.len - 2 ] == mcomment.end1 && line[line.len - 1 ] == mcomment.end2 ) {
292
- // All after /* is a comment
293
- ved.add_chunk (.a_comment, start, line.len )
294
- ved. is_ml_comment = true
295
- break
296
- }
297
- // End of /**/
298
- if i > 0 && line[i - 1 ] == mcomment.end 1 && line[i] == mcomment.end 2 {
299
- // All before */ is still a comment
300
- ved. add_chunk (.a_comment, 0 , start + 1 )
301
- ved. is_ml_comment = false
302
- break
339
+
340
+ // Single line Comment /* ... */
341
+ if i < line.len - 1 && line[i] == mcomment.start1 && line[i + 1 ] == mcomment.start2 {
342
+ end_pos := line. index_after ( mcomment.end1 . str () + mcomment.end2 . str (), i + 2 ) or { - 1 }
343
+ if end_pos != - 1 {
344
+ ved.add_chunk (.a_comment, start, end_pos + 2 )
345
+ i = end_pos + 2 // Move past the comment
346
+ continue
347
+ } else {
348
+ // This case (start found but no end on this line) should be handled by the new logic in draw_split.
349
+ // If we reach here, it means draw_split decided this line doesn't start a *continuing* multiline comment.
350
+ // Treat the '/*' as normal text by just advancing `i`.
351
+ i + = 1
352
+ continue
353
+ }
303
354
}
304
- // String
355
+ // String '...'
305
356
if line[i] == `'` {
306
- i++
307
- for i < line.len - 1 && line[i] != `'` {
308
- i++
309
- }
310
- if i > = line.len {
311
- i = line.len - 1
357
+ mut end := i + 1
358
+ for end < line.len && line[end] != `'` {
359
+ // Handle escaped quote \'
360
+ if line[end] == `\\ ` && end + 1 < line.len && line[end + 1 ] == `'` {
361
+ end++ // Skip escaped quote
362
+ }
363
+ end++
312
364
}
313
- ved.add_chunk (.a_string, start, i + 1 )
365
+ if end > = line.len {
366
+ end = line.len - 1
367
+ } else {
368
+ end + = 1
369
+ } // include closing quote
370
+ ved.add_chunk (.a_string, start, end)
371
+ i = end // Move past the string
372
+ continue
314
373
}
374
+ // String "..."
315
375
if line[i] == `"` {
316
- i++
317
- for i < line.len - 1 && line[i] != `"` {
318
- i++
376
+ mut end := i + 1
377
+ for end < line.len && line[end] != `"` {
378
+ // Handle escaped quote \"
379
+ if line[end] == `\\ ` && end + 1 < line.len && line[end + 1 ] == `"` {
380
+ end++ // Skip escaped quote
381
+ }
382
+ end++
319
383
}
320
- if i > = line.len {
321
- i = line.len - 1
322
- }
323
- ved.add_chunk (.a_string, start, i + 1 )
384
+ if end > = line.len {
385
+ end = line.len - 1
386
+ } else {
387
+ end + = 1
388
+ } // include closing quote
389
+ ved.add_chunk (.a_string, start, end)
390
+ i = end // Move past the string
391
+ continue
324
392
}
325
393
// Key
326
- for i < line.len && is_alpha_underscore (int (line[i])) {
327
- i++
328
- }
329
- word := line[start..i]
330
- // println('word="$word"')
331
- if word in cur_syntax.literals {
332
- // println('$word is key')
333
- ved.add_chunk (.a_lit, start, i)
334
- // println('adding key. len=$ved.chunks.len')
335
- } else if word in cur_syntax.keywords {
336
- // println('$word is key')
337
- ved.add_chunk (.a_key, start, i)
338
- // println('adding key. len=$ved.chunks.len')
394
+ if is_alpha_underscore (int (line[i])) {
395
+ mut end := i + 1
396
+ for end < line.len && is_alpha_underscore (int (line[end])) {
397
+ end++
398
+ }
399
+ word := line[start..end]
400
+ if word in cur_syntax.literals {
401
+ ved.add_chunk (.a_lit, start, end)
402
+ } else if word in cur_syntax.keywords {
403
+ ved.add_chunk (.a_key, start, end)
404
+ }
405
+ // If it's not a keyword or literal, it will be drawn as normal text later.
406
+ i = end // Move past the word
407
+ continue
339
408
}
409
+
410
+ // If none of the above matched, advance by one character
411
+ i++
340
412
}
341
- if ved.is_ml_comment {
342
- ved.gg.draw_text (x, y, line, ved.cfg.comment_cfg)
343
- return
344
- }
413
+
414
+ // --- Keep the original chunk drawing logic ---
345
415
if ved.chunks.len == 0 {
346
416
// println('no chunks')
347
417
ved.gg.draw_text (x, y, line, ved.cfg.txt_cfg)
@@ -352,7 +422,7 @@ fn (mut ved Ved) draw_text_line(x int, y int, line string, ext string) {
352
422
// TODO use runes
353
423
// runes := msg.runes.slice_fast(chunk.pos, chunk.end)
354
424
// txt := join_strings(runes)
355
- for i , chunk in ved.chunks {
425
+ for j , chunk in ved.chunks {
356
426
// println('chunk #$i start=$chunk.start end=$chunk.end typ=$chunk.typ')
357
427
// Initial text chunk (not necessarily initial, but the one right before current chunk,
358
428
// since we don't have a seperate chunk for text)
@@ -372,11 +442,12 @@ fn (mut ved Ved) draw_text_line(x int, y int, line string, ext string) {
372
442
ved.gg.draw_text (x + chunk.start * ved.cfg.char_width, y, s, cfg)
373
443
pos = chunk.end
374
444
// Final text chunk
375
- if i == ved.chunks.len - 1 && chunk.end < line.len {
376
- final := line[chunk.end..line.len ]
445
+ if j == ved.chunks.len - 1 && chunk.end < line.len {
446
+ final := line[chunk.end..]
377
447
ved.gg.draw_text (x + pos * ved.cfg.char_width, y, final, ved.cfg.txt_cfg)
378
448
}
379
449
}
450
+ // --- End of original chunk drawing logic ---
380
451
}
381
452
382
453
fn (ved &Ved) draw_cursor (cursor_x int , y int ) {
0 commit comments