-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdisplay.cpp
3543 lines (3224 loc) · 164 KB
/
display.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//
// Copyright 2015-2022 by Kevin L. Goodwin [[email protected]]; All rights reserved
//
// This file is part of K.
//
// K is free software: you can redistribute it and/or modify it under the
// terms of the GNU General Public License as published by the Free Software
// Foundation, either version 3 of the License, or (at your option) any later
// version.
//
// K is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along
// with K. If not, see <http://www.gnu.org/licenses/>.
//
#include "ed_main.h"
#include <stdexcept>
#if defined(_WIN32)
#define IS_LINUX 0
#define FULL_DB 0
#else
#define IS_LINUX 1
#define FULL_DB 0
#endif
#if DBGHILITE
void DbgHilite_( char ch, PCChar func ) {
DBG( "%c%s SavedHL=[%p] FileHL=[%p] (%s)"
, ch
, func
, s_savedHiLiteList.dl_first
, g_CurView()->pFBuf->d_pHiLiteList.dl_first
, g_CurView()->pFBuf->Name()
);
}
#define DbgHilite( c ) DbgHilite_( c, __func__ )
#else
#define DbgHilite( c )
#endif
/*
the OLD refresh model
a series of APIs
DispNeeds ... RefreshStatLn()
DispNeeds ... RefreshVerticalCursorHilite()
DispNeeds ... RefreshAllLinesCurWin()
DispNeeds ... RefreshAllLinesAllWindows()
DispNeeds ... RefreshCurWin() called only in FBUF::PutFocusOn
DispNeeds ... RefreshTotal()
called by code that modifies the corresponding "stuff".
This is sub-optimal: since lines to refresh are not enumerated; it's
either all or nothing.
Yes, the low-level video driver only pushes display deltas thru the I/O
path, but I'm also trying to minimize display-generation overhead, AND
have the system be "self-updating" (i.e. hooks in the low-level
(file/cursor) modify functions will accumulate a set of pending events
which will be (minimally) executed when display-update time comes.
Also, HiliteAddin's need specific, accurate information about changes in
order to "do their thing" correctly and (again) optimally.
the NEW refresh model (ideal)
file modifying API are called, and "the system" keeps track of the range
of pending changes and notifies (or is used to notify) the display update
system about what it needs to do
*/
class LineColorvals {
enum { END_MARKER=0 }; // one simplifying assumption: color==0 is not used (used for EOS)
typedef std::array<colorval_t,BUFBYTES> acv;
acv d_acv;
public:
bool inRange( acv::size_type ix ) const { return ix < d_acv.size(); }
colorval_t colorAt( acv::size_type ix ) const { return d_acv[ ix ]; }
auto cols() const {
const auto rv( std::distance( d_acv.cbegin(), std::find( d_acv.cbegin(), d_acv.cend(), END_MARKER ) ) );
return static_cast<acv::size_type>( rv > 0 ? rv : 0 );
}
LineColorvals() { d_acv.fill( END_MARKER ); }
int runLength( acv::size_type ix ) const {
const auto ix0( ix );
if( inRange( ix0 ) ) {
const auto color0( colorAt( ix0 ) );
for( ++ix ; inRange( ix ) && colorAt( ix ) == color0 ; ++ix ) {}
}
return ix - ix0;
}
void PutColorval( acv::size_type ix, acv::size_type len, colorval_t color ) {
const auto past( std::min( ix+len, d_acv.size() ) );
for( ; ix < past ; ++ix ) {
d_acv[ ix ] = color;
}
}
void Cat( const LineColorvals &rhs ) { 0 && DBG( "CAT[%3" PR_SIZET "]", cols() );
const auto srclen( rhs.cols() );
const auto dix( cols() );
const auto count( std::min( srclen, d_acv.size() - dix ) );
for( auto iy(0) ; iy < count ; ++iy ) {
d_acv[ dix + iy ] = rhs.colorAt( iy );
}
}
};
class LineColorsClipped {
const View &d_view ;
LineColorvals &d_lcvs ;
const int d_idxWinLeft ; // LineColorvals ix of leftmost visible char
const COL d_colWinLeft ;
const int d_width ;
public:
LineColorsClipped( const View &view, LineColorvals &lcvs, int idxWinLeft, int colWinLeft, int width )
: d_view ( view )
, d_lcvs ( lcvs )
, d_idxWinLeft ( idxWinLeft )
, d_colWinLeft ( colWinLeft )
, d_width ( width )
{ 0 && DBG( "%s iWL=%d cWL=%d width=%d", __func__, idxWinLeft, colWinLeft, width );
}
COL GetMinColInDisp() const { return d_colWinLeft; }
COL GetMaxColInDisp() const { return d_colWinLeft+d_width-1; }
void PutColorval( int col, int len, colorval_t color ) { 0 && DBG( "%s a: %3d L %3d", __func__, col, len );
if( col > d_colWinLeft+d_width || col + len < d_colWinLeft ) { return; }
const auto colMin( std::max( col , d_colWinLeft ) ); 0 && DBG( "%s b: %3d L %3d", __func__, col, len );
const auto colMax( std::min( col+len-1, GetMaxColInDisp() ) );
const auto ixMin( colMin - d_colWinLeft + d_idxWinLeft );
const auto ixMax( colMax - d_colWinLeft + d_idxWinLeft ); 0 && DBG( "%s c: %3d L %3d", __func__, ixMin, ixMax );
d_lcvs.PutColorval( ixMin, ixMax - ixMin+1, color );
}
void PutColorTblIdx( int col, int len, ColorTblIdx colorIdx ) { PutColorval( col, len, d_view.ColorIdxToColorval( colorIdx ) ); }
};
#if VARIABLE_WINBORDER
STATIC_CONST struct
{ // H -> both horiz edges, V -> both vertical edges, T -> top edge, B -> bottom edge, L -> left edge, R -> right edge
uint8_t HV_ , LV_ , RV_ , _V_ , H__ , HT_ , HB_ ;
} wbc[] =
{
{ uint8_t('Å'), uint8_t('´'), uint8_t('Ã'), uint8_t('³'), uint8_t('Ä'), uint8_t('Á'), uint8_t('Â') }, // 0
{ uint8_t('×'), uint8_t('¶'), uint8_t('Ç'), uint8_t('º'), uint8_t('Ä'), uint8_t('Ð'), uint8_t('Ò') }, // 1
{ uint8_t('Ø'), uint8_t('µ'), uint8_t('Æ'), uint8_t('³'), uint8_t('Í'), uint8_t('Ï'), uint8_t('Ñ') }, // 2
{ uint8_t('Î'), uint8_t('¹'), uint8_t('Ì'), uint8_t('º'), uint8_t('Í'), uint8_t('Ê'), uint8_t('Ë') }, // 3
{ uint8_t('°'), uint8_t('°'), uint8_t('°'), uint8_t('°'), uint8_t('°'), uint8_t('°'), uint8_t('°') }, // 4
{ uint8_t('±'), uint8_t('±'), uint8_t('±'), uint8_t('±'), uint8_t('±'), uint8_t('±'), uint8_t('±') }, // 5
{ uint8_t('²'), uint8_t('²'), uint8_t('²'), uint8_t('²'), uint8_t('²'), uint8_t('²'), uint8_t('²') }, // 6
{ uint8_t('Û'), uint8_t('Û'), uint8_t('Û'), uint8_t('Û'), uint8_t('Û'), uint8_t('Û'), uint8_t('Û') }, // 7
};
GLOBAL_VAR int g_swiWBCidx = 3;
int Max_wbc_idx() { return ELEMENTS(wbc)-1; }
#define HV_ (wbc[g_swiWBCidx].HV_)
#define LV_ (wbc[g_swiWBCidx].LV_)
#define RV_ (wbc[g_swiWBCidx].RV_)
#define _V_ (wbc[g_swiWBCidx]._V_)
#define H__ (wbc[g_swiWBCidx].H__)
#define HT_ (wbc[g_swiWBCidx].HT_)
#define HB_ (wbc[g_swiWBCidx].HB_)
#else
enum WinBorderChars
{ // H -> both horiz edges, V -> both vertical edges, T -> top edge, B -> bottom edge, L -> left edge, R -> right edge
#define LINEDRAW_WIN 8
#if (0==LINEDRAW_WIN)
HV_=uint8_t('Å'), LV_=uint8_t('´'), RV_=uint8_t('Ã'), _V_=uint8_t('³'), H__=uint8_t('Ä'), HT_=uint8_t('Á'), HB_=uint8_t('Â'), // "ÚÄ¿ÀٳôÂÁÅ",
#elif (1==LINEDRAW_WIN)
HV_=uint8_t('×'), LV_=uint8_t('¶'), RV_=uint8_t('Ç'), _V_=uint8_t('º'), H__=uint8_t('Ä'), HT_=uint8_t('Ð'), HB_=uint8_t('Ò'), // "ÖÄ·Ó½ºÇ¶ÒÐ×",
#elif (2==LINEDRAW_WIN)
HV_=uint8_t('Ø'), LV_=uint8_t('µ'), RV_=uint8_t('Æ'), _V_=uint8_t('³'), H__=uint8_t('Í'), HT_=uint8_t('Ï'), HB_=uint8_t('Ñ'), // "Õ͸Ծ³ÆµÑÏØ"
#elif (3==LINEDRAW_WIN)
HV_=uint8_t('Î'), LV_=uint8_t('¹'), RV_=uint8_t('Ì'), _V_=uint8_t('º'), H__=uint8_t('Í'), HT_=uint8_t('Ê'), HB_=uint8_t('Ë'), // "ÉͻȼºÌ¹ËÊÎ",
#elif (4==LINEDRAW_WIN)
HV_=uint8_t('°'), LV_=HV_, RV_=HV_, _V_=HV_, H__=HV_, HT_=HV_, HB_=HV_, // "°°°°°°°°°°°"
#elif (5==LINEDRAW_WIN)
HV_=uint8_t('±'), LV_=HV_, RV_=HV_, _V_=HV_, H__=HV_, HT_=HV_, HB_=HV_, // "±±±±±±±±±±±"
#elif (6==LINEDRAW_WIN)
HV_=uint8_t('²'), LV_=HV_, RV_=HV_, _V_=HV_, H__=HV_, HT_=HV_, HB_=HV_, // "²²²²²²²²²²²"
#elif (7==LINEDRAW_WIN)
HV_=uint8_t('Û'), LV_=HV_, RV_=HV_, _V_=HV_, H__=HV_, HT_=HV_, HB_=HV_, // "ÛÛÛÛÛÛÛÛÛÛÛ"
#elif (8==LINEDRAW_WIN)
HV_=uint8_t(' '), LV_=HV_, RV_=HV_, _V_=HV_, H__=HV_, HT_=HV_, HB_=HV_, // " "
#endif
#undef LINEDRAW_WIN
};
#endif
constexpr auto DBG_HL_EVENT( false );
//-----------------------------------------------------------------------------------------------------------
STATIC_FXN char GenAltHiliteColor( const colorval_t color ) {
// algorithm ATTEMPTS to choose a _readable_ hilite-alternative-color for a given color
const auto fgColor( get_fg(color) );
const auto bgColor( get_bg(color) );
if( bg::BLK == bgColor ) { return fb( fgColor, xor_bg(bgColor, bg::BLU) ); }
const auto fgBrite( is_fghi(fgColor) );
const auto bgBrite( is_bghi(bgColor) );
if( fgBrite && bgBrite ) { return fb( fgColor , xor_bg(bgColor, bg::hi) ); }
else if( fgBrite && !bgBrite ) { return fb( fgColor , xor_bg(bgColor, bg::hi) ); }
else if( !fgBrite && bgBrite ) { return fb( xor_fg(fgColor, fg::hi), bgColor ); }
else { return fb( fgColor , xor_bg(bgColor, bg::hi) ); }
}
//--------------------------------------------------------------------------------------------------------------------------
STATIC_FXN inline PFTypeSetting IdxNodeToFTS( RbNode *pNd ) { return static_cast<PFTypeSetting>( rb_val(pNd) ); } // type-safe conversion function
struct FTypeSetting {
enum { SD=0 };
std::string d_ftypeName; // rbtree key
std::string d_hiliteName;
constexpr STATIC_VAR auto d_colors_ELEMENTS = to_underlying(ColorTblIdx::VIEW_COLOR_COUNT);
colorval_t d_colors[ d_colors_ELEMENTS ];
char d_eolCommentDelim[5]; // the longest eol-comment I know of is "rem " ...
void Update();
stref ftypeName() const { return d_ftypeName ; }
stref hiliteName() const { return d_hiliteName; } // HiliteAddins_Init() uses to map specific hilites
FTypeSetting( stref ext )
: d_ftypeName( ext )
{ 0 && DBG( "%s CTOR: '%" PR_BSR "' ----------------------------------------------", __func__, BSR(d_ftypeName) );
Update();
}
~FTypeSetting() {}
};
STATIC_VAR RbTree *s_FTS_idx;
STATIC_FXN int Show_FTypeSettings() {
if( FTypeSetting::SD ) { DBG( "%s+ ----------------------------------------------", __func__ );
rb_traverse( pNd, s_FTS_idx ) {
const auto pFTS( IdxNodeToFTS( pNd ) ); DBG( "%s %" PR_BSR "=%" PR_BSR, __func__, BSR(pFTS->ftypeName()), BSR(pFTS->hiliteName()) );
} DBG( "%s- ----------------------------------------------", __func__ );
}
return 1;
}
void FTypeSetting::Update() {
Catbuf<120> kybuf;
const auto w0( kybuf.Wref() );
const auto w1( w0.cpy( "filesettings.ftype_map." ) );
auto w2( w1.cpy( d_ftypeName ) ); // NB! sets up following if expression's kybuf.c_str()! Cannot be folded into if expression!
if( !LuaCtxt_Edit::TblKeyExists( kybuf.c_str() ) ) {
w2 = w1.cpy( "unknown" );
}
w2.cpy( ".eolCommentDelim" );
LuaCtxt_Edit::Tbl2S( BSOB(d_eolCommentDelim), kybuf.c_str(), "" ); SD && DBG( "%s: %s = %s", __func__, kybuf.c_str(), d_eolCommentDelim );
0 && DBG( "%s: w0 %" PR_SIZET ", w1 %" PR_SIZET ", w2 %" PR_SIZET, __func__, w0.len(), w1.len(), w2.len() );
{
w2.cpy( ".hilite" );
char hiliteNmBuf[21];
LuaCtxt_Edit::Tbl2S( BSOB(hiliteNmBuf), kybuf.c_str(), "" ); SD && DBG( "%s: %s = %s", __func__, kybuf.c_str(), d_eolCommentDelim );
d_hiliteName.assign( hiliteNmBuf );
}
{
STATIC_CONST struct { PCChar pLuaName; ColorTblIdx ofs; colorval_t dflt; } s_color2Lua[] = { // contents init'd from $KINIT:k.filesettings
#define SINIT( nm, idx, dflt ) { #nm, idx, dflt }
SINIT( txt , ColorTblIdx::TXT , fb( fg::YEL, bg::BLU ) ),
SINIT( hil , ColorTblIdx::HIL , fb( fg::WHT, bg::CYN ) ),
SINIT( sel , ColorTblIdx::SEL , fb( fg::WHT, bg::GRN ) ),
SINIT( wuc , ColorTblIdx::WUC , fb( fg::GRN, bg::WHT ) ),
SINIT( cxy , ColorTblIdx::CXY , fb( fg::WHT, bg::RED ) ),
SINIT( cpp , ColorTblIdx::CPP , fb( fg::BRN, bg::BLK ) ),
SINIT( com , ColorTblIdx::COM , fb( fg::MGR, bg::BLK ) ),
SINIT( str , ColorTblIdx::STR , fb( fg::BRN, bg::BLU ) ),
#undef SINIT
}; static_assert( ELEMENTS( s_color2Lua ) == d_colors_ELEMENTS, "ELEMENTS( s_color2Lua ) != d_colors_ELEMENTS" );
const auto w3( w2.cpy( ".colors." ) );
const colorval_t orVal( fb(g_fBrightFg ? fg::hi : fg::lo, bg::lo ) );
for( const auto &c2L : s_color2Lua ) {
const auto ofs = to_underlying( c2L.ofs );
w3.cpy( c2L.pLuaName );
d_colors[ ofs ] = orVal | LuaCtxt_Edit::Tbl2Int( kybuf.c_str(), c2L.dflt ); SD && DBG( "%s: %s = 0x%02X%s", __func__, kybuf.c_str(), d_colors[ ofs ], d_colors[ ofs ]==c2L.dflt?" (C++dflt)":"" );
}
}
// d_colors[ ColorTblIdx::CXY ] = GenAltHiliteColor( d_colors[ ColorTblIdx::TXT ] );
}
void InitFTypeSettings() { FTypeSetting::SD && DBG( "%s+ ----------------------------------------------", __func__ );
STATIC_VAR RbCtrl s_FTS_idx_RbCtrl = { AllocNZ_, Free_, };
s_FTS_idx = rb_alloc_tree( &s_FTS_idx_RbCtrl ); FTypeSetting::SD && DBG( "%s- ----------------------------------------------", __func__ );
}
STATIC_FXN void DeleteFTS( void *pData, void *pExtra ) { FTypeSetting::SD && DBG( "%s+ ----------------------------------------------", __func__ );
auto pFTS( static_cast<PFTypeSetting>(pData) );
delete pFTS; FTypeSetting::SD && DBG( "%s- ----------------------------------------------", __func__ );
}
void CloseFTypeSettings() {
rb_dealloc_treev( s_FTS_idx, nullptr, DeleteFTS );
}
STATIC_FXN PCFTypeSetting Get_FTypeSetting( stref ftype ) { FTypeSetting::SD && DBG( "%s+ ---------------------------------------------- PROBING [%" PR_BSR "]", __func__, BSR(ftype) );
int equal;
auto pNd( rb_find_gte_sri( &equal, s_FTS_idx, ftype ) );
if( equal ) { FTypeSetting::SD && DBG( "%s FOUND [%" PR_BSR "]", __func__, BSR(ftype) );
return IdxNodeToFTS( pNd );
}
auto pNew( new FTypeSetting( ftype ) ); FTypeSetting::SD && DBG( "%s CREATING [%" PR_BSR "]", __func__, BSR(ftype) );
rb_insert_before( s_FTS_idx, pNd, pNew->d_ftypeName.c_str(), pNew );
return pNew;
}
void FBUF::SetFType() {
if( !GetFTypeSettings() ) {
d_ftypeStruct = ::Get_FTypeSetting( FBOP::DeduceFType( this ) ); 1 && (d_ftypeStruct == nullptr) && DBG( "%s null FTypeSettings! %s", __func__, this->Name() );
}
}
void Reread_FTypeSettings() { FTypeSetting::SD && DBG( "%s+ ----------------------------------------------", __func__ );
rb_traverse( pNd, s_FTS_idx ) {
const auto pFTS( IdxNodeToFTS( pNd ) ); FTypeSetting::SD && DBG( "%s [%" PR_BSR "]", __func__, BSR(pFTS->ftypeName()) );
pFTS->Update();
} FTypeSetting::SD && DBG( "%s- ----------------------------------------------", __func__ );
}
colorval_t View::ColorIdxToColorval( ColorTblIdx cti ) const {
auto colorIdx = to_underlying(cti);
if( colorIdx >= 0 ) {
if( colorIdx < FTypeSetting::d_colors_ELEMENTS ) {
constexpr auto unknownColor( fb(fg::LCY, bg::BLU) );
const auto pFTS( CFBuf()->GetFTypeSettings() );
return pFTS ? pFTS->d_colors[ colorIdx ] : unknownColor;
}
STATIC_CONST colorval_t *s_colorVars[] = {
&g_colorInfo ,
&g_colorStatus ,
&g_colorWndBorder ,
&g_colorError ,
}; static_assert( ELEMENTS(s_colorVars) == (to_underlying(ColorTblIdx::COLOR_COUNT) - FTypeSetting::d_colors_ELEMENTS), "ELEMENTS(s_colorVars) == ColorTblIdx::COLOR_COUNT" );
colorIdx -= FTypeSetting::d_colors_ELEMENTS;
if( colorIdx < ELEMENTS(s_colorVars) ) {
return *s_colorVars[ colorIdx ];
}
}
constexpr auto unknownColor( fb(fg::LPK,bg::RED) );
return unknownColor;
}
stref FBUF::FTypeName() const { return d_ftypeStruct ? d_ftypeStruct->ftypeName() : ""; }
//-----------------------------------------------------------------------------------------------------------
#define KSTRCMP( kstr, sz ) memcmp( kstr, sz, KSTRLEN(kstr) )
/*
design notes, HiliteAddin system
1. I'm lazy: I want the most effect for the least effort.
2. In _some_ hiliting situations, as an exception, I'm not a perfectionist.
3. I'm not a trained Computer Scientist. The idea of writing a sufficiently
correct and robust parser to perform full syntax hiliting for any HLL is
sufficiently daunting to me that I won't attempt it. The amount of
(complex) code needed is beyond my bandwidth to write and/or maintain.
4. 201407: I (partially) bit the bullet on #3, and from scratch wrote a
simple generic parser framework, HiliteAddin_StreamParse, for comments
and literal strings presently covering (in separate derivative
implementations) C/C++, Lua, Python. Development of full language
parsing (to the statement/block/keyword level) remains unlikely; I just
wanted very reliable _low-lighting_ of comments to facilitate reading
"enterprise" codebases which often contain vast amounts of gratuitous
boilerplate comments (e.g. AutoDuck/Doxygen function comment headers).
A more likely future development is a search mode that ignores
comments as parsed herein.
20071014 kgoodwin
*/
class HiliteAddin { // prototype base/interface class: public HiliteAddin
protected:
View &d_view ;
public:
HiliteAddin( PView pView ) : d_view( *pView ) {}
virtual bool VWorthKeeping() { return true; }
virtual PCChar Name() const = 0;
virtual ~HiliteAddin() {}
/* update/event-receive methods: any useful incarnation of HiliteAddin must
implement at least one of these, which is used to optimally update object
state which is derived from the event's info */
virtual void VCursorMoved( bool fUpdtWUC ) {}
virtual void VFbufLinesChanged( LINE yMin, LINE yMax ) {}
virtual void VWinResized() {}
/* output methods: the _purpose_ of HiliteAddin objects; how they manifest
themselves at the user level (by changing the color of displayed FBUF content).
retval of bool HilitLine...(): true means stop hiliting after this */
virtual bool VHilitLine ( LINE yLine, COL xIndent, LineColorsClipped &alcc ) { return false; }
virtual bool VHilitLineSegs( LINE yLine, LineColorsClipped &alcc ) { return false; }
virtual size_t VGetStreamParse( LINE yLine, hl_rgn_t *&hlrt ) { return 0; /* number of valid entries in hlrt */ }
DLinkEntry <HiliteAddin> dlinkAddins;
protected:
PCFBUF CFBuf() const { return d_view.CFBuf(); }
bool LineIs_LineCompile( LINE yLine ) const { return d_view.LineCompile_Valid() && yLine == d_view.Get_LineCompile(); }
const Point &Cursor() const { return d_view.Cursor(); }
const Point &Origin() const { return d_view.Origin(); }
LINE ViewLines() const { return d_view.ViewLines(); }
LINE ViewCols() const { return d_view.ViewCols() ; }
LINE MinVisibleFbufLine() const { return d_view.MinVisibleFbufLine(); }
LINE MaxVisibleFbufLine() const { return d_view.MaxVisibleFbufLine(); }
bool isActiveWindow() const { return d_view.isActive(); }
colorval_t ColorIdxToColorval( ColorTblIdx colorIdx ) const { return d_view.ColorIdxToColorval( colorIdx ); }
NO_COPYCTOR(HiliteAddin);
NO_ASGN_OPR(HiliteAddin);
void DispNeedsRedrawAllLines() { d_view.RedrawAllVisbleLines(); }
};
//--------------------------------------------------------------------------
class HiliteAddin_Pbal : public HiliteAddin {
void VCursorMoved( bool fUpdtWUC ) override;
bool VHilitLineSegs( LINE yLine, LineColorsClipped &alcc ) override;
public:
HiliteAddin_Pbal( PView pView )
: HiliteAddin( pView )
, d_fPbalMatchValid( false )
{}
~HiliteAddin_Pbal() {}
PCChar Name() const override { return "PBal"; }
private:
bool d_fPbalMatchValid;
Point d_ptPbalMatch;
};
void HiliteAddin_Pbal::VCursorMoved( bool fUpdtWUC ) {
const auto prevPbalValid( d_fPbalMatchValid );
d_fPbalMatchValid = d_view.PBalFindMatching( true, &d_ptPbalMatch );
if( d_fPbalMatchValid || prevPbalValid ) { // turning ON, turning OFF, _or_ MOVING?
DispNeedsRedrawAllLinesAllWindows(); // BUGBUG need API to select lines/ranges of curWindow
}
}
bool HiliteAddin_Pbal::VHilitLineSegs( LINE yLine, LineColorsClipped &alcc ) {
if( d_fPbalMatchValid && d_ptPbalMatch.lin == yLine ) { 0 && DBG( "HiliteAddin_Pbal::HilitLine %d %d", d_ptPbalMatch.lin, yLine );
alcc.PutColorTblIdx( d_ptPbalMatch.col, 1, ColorTblIdx::WND );
}
return false;
}
/* 20190202 kgoodwin
sole instance of WucState (s_wucState) is used to implement
all_window_WUC_hiliting: any call to
HiliteAddin_WordUnderCursor::VHilitLineSegs() (for current View of _any_ Win)
references the last-set WUC in s_wucState. */
class WucState {
public:
/* 20110516 kgoodwin
sb_t is a envp-style sequence of WUC needles so that calc'd derivatives of
the actual WUC can be highlighted. In particular, the MWDT elfdump disasm
shows 0xhexaddress in instruction operands, but each instruction's address is
shown as hexaddress (and I want the latter to be highlighted when the former
is WUC). */
typedef StringsBuf<BUFBYTES> sb_t;
private:
sb_t d_sb;
std::string d_stSel; // d_stSel content must look like StringsBuf content, which means an extra/2nd NUL marks the end of the last string
PCView d_wucSrc = nullptr; // never dereferenced, only compared (indirectly) vs. g_CurView()
public:
WucState() {}
~WucState() {}
void SetNewWuc( stref src, LINE lin, COL col, PCView wucSrc );
void VCursorMoved( bool fUpdtWUC, View &view );
bool VHilitLineSegs( View &view, LINE yLine, LineColorsClipped &alcc ) const;
private:
const sb_t &get_sb () const { return d_sb; }
const std::string &get_stSel () const { return d_stSel; }
PCView get_wucSrc () const { return d_wucSrc; }
void PrimeRefresh() const { DispNeedsRedrawAllLinesAllWindows(); } // all_window_WUC_hiliting demands ...AllWindows() version
} s_wucState;
void WucState::SetNewWuc( stref src, LINE lin, COL col, PCView wucSrc ) {
d_wucSrc = wucSrc;
d_stSel.clear();
if( eq( d_sb.data(), src ) ) { DBG_HL_EVENT && DBG("unch->%s", d_sb.data() );
PrimeRefresh(); // we're setting the same WUC: redraw in case cursor was off-any-WUC (all WUC's HL'd) and is now
return; // over a WUC (in which case WUC-UC (under-cursor) is now HL'd, and needs to become un-HL'd)
}
d_sb.clear(); // aaa aaa aaa aaa
stref wuc( d_sb.AddString( src ) );
if( wuc.empty() || lin < 0 ) { DBG_HL_EVENT && DBG( "%s toolong", __func__);
PrimeRefresh();
return;
} DBG_HL_EVENT && DBG( "wuc=%" PR_BSR, BSR(wuc) );
{ // add leaf-name of compound thing clss::xWUCX clss::xWUCX xWUCX xWUCX clss.xWUCX clss.xWUCX
STATIC_CONST std::array<stref,2> compound_seps{ "::", "." };
auto maxIx( eosr );
for( auto sep : compound_seps ) {
const auto rfrv( wuc.rfind( sep ) ); // find last instance of sep
if( rfrv != eosr ) { maxIx = (maxIx == eosr) ? rfrv : std::max( maxIx, rfrv ); }
} // NB: rfind returns "index [from start of wuc] of first ch of match", which is the LAST char of the match char in wuc ...
if( maxIx != eosr ) { // when looked at from a non-reverse perspective; thus regardless of key length, the index
d_sb.AddString( wuc.substr( maxIx + 1 ) ); // of the first char after the match is always (maxIx + 1)
}
}
if( !wuc.starts_with( "-D" ) ) { // experimental
char scratch[81]; bcat( bcpy( scratch, "-D" ), scratch, wuc ); d_sb.AddString( scratch );
}
else {
if( wuc.length() > 2 ) { d_sb.AddString( wuc.substr( 2 ) ); }
}
if( !wuc.starts_with( "$" )) { // experimental
char scratch[81];
d_sb.AddString( stref( scratch, bcat( bcpy( scratch, "$" ), scratch, wuc ) ) );
d_sb.AddString( stref( scratch, bcat( bcat( bcpy( scratch, "$(" ), scratch, wuc ), scratch, ")" ) ) );
d_sb.AddString( stref( scratch, bcat( bcat( bcpy( scratch, "${" ), scratch, wuc ), scratch, "}" ) ) );
}
if( 1 ) { // GCCARM variations: funcname -> __funcname_veneer
STATIC_CONST char vnr_pfx[] = { "__" }; CompileTimeAssert( 2 == KSTRLEN(vnr_pfx) );
STATIC_CONST char vnr_sfx[] = { "_veneer" }; CompileTimeAssert( 7 == KSTRLEN(vnr_sfx) );
const auto vnr_fx_len( KSTRLEN(vnr_pfx)+KSTRLEN(vnr_sfx) );
if( (wuc.length() > vnr_fx_len) && wuc.starts_with( vnr_pfx ) && wuc.ends_with( vnr_sfx ) ) {
d_sb.AddString( wuc.substr( KSTRLEN(vnr_pfx), wuc.length() - vnr_fx_len ) );
}
else {
char scratch[61];
if( (wuc.length() > 1) && (wuc.length() < sizeof(scratch)-vnr_fx_len) && isalpha( wuc[0] ) ) {
d_sb.AddString( stref( scratch, bcat( bcat( bcpy( scratch, vnr_pfx ), scratch, wuc ), scratch, vnr_sfx ) ) );
}
}
}
constexpr size_t MAX_WUC_HEXNUM_LEN(16); // up to 64-bit
if( MAX_WUC_HEXNUM_LEN > 0 ) { // hexnum variations: 0xdeadbeef, deadbeef, 0xdead_beef (old MWDT elfdump format)
constexpr auto SUPPORT_0xdead_beef(true); // 32-bit only
if( (wuc.length() > 2) && 0==strnicmp_LenOfFirstStr( "0x", wuc ) ) { // 0xhexnum ?
auto hex( wuc ); hex.remove_prefix( 2 );
const auto xrun( consec_xdigits( hex ) );
if( hex.length() == xrun ) { // no hex.length() <= MAX_WUC_HEXNUM_LEN check here since no intermed buffer required
d_sb.AddString( hex );
} // 0x123 123 0x123 123 0x01234 01234 0x01234 01234 0x0123456789abcde 0123456789abcde
else if( SUPPORT_0xdead_beef && (9==hex.length()) && (4==xrun) && ('_'==hex[4]) && (4==consec_xdigits( hex.data()+5 )) ) {
char kb[] = { hex[0], hex[1], hex[2], hex[3], hex[5], hex[6], hex[7], hex[8], 0 };
d_sb.AddString( kb );
}
}
else if( (MAX_WUC_HEXNUM_LEN > 0)
&& (wuc.length() <= MAX_WUC_HEXNUM_LEN)
&& (wuc.length() == consec_xdigits( wuc )) // hexnum?
) { // 0x12345678 12345678 0x12345678 12345678 0x0123456789abcdef 0123456789abcdef 0x0123456789abcdef 0123456789abcdef
char kb[3+MAX_WUC_HEXNUM_LEN] = { '0', 'x', chNUL }; // 3=sizeof(chNUL)+strlen("0x")
scat( BSOB(kb), wuc );
d_sb.AddString( kb );
if( SUPPORT_0xdead_beef && 8==wuc.length() ) {
auto ix( 6 );
kb[ix++] = '_';
kb[ix++] = wuc[4];
kb[ix++] = wuc[5];
kb[ix++] = wuc[6];
kb[ix++] = wuc[7];
kb[ix++] = 0;
d_sb.AddString( kb );
}
}
}
if( DBG_HL_EVENT ) {
auto ix(0);
for( auto pNeedle(d_sb.data()) ; *pNeedle ; ) {
const stref needle( pNeedle );
DBG( "needle[%d] %" PR_BSR "'", ix++, BSR(needle) );
pNeedle += needle.length() + 1;
}
}
PrimeRefresh(); // DBG_HL_EVENT && DBG( "WUC='%s'", wuc );
}
stref GetWordUnderPoint( PCFBUF pFBuf, Point *cursor ) { enum { SD=0 };
const auto yCursor( cursor->lin );
const auto xCursor( cursor->col );
const auto rl( pFBuf->PeekRawLine( yCursor ) );
if( !rl.empty() ) { SD && DBG( "newln=%" PR_BSR, BSR(rl) );
IdxCol_cached conv( pFBuf->TabWidth(), rl ); // abc abc
if( xCursor < conv.cols() ) {
const auto ixC( conv.c2ci( xCursor ) );
if( isWordChar( rl[ixC] ) ) {
const auto ixFirst ( IdxFirstHJCh ( rl, ixC ) );
const auto ixPastLast( FirstNonWordOrEnd( rl, ixC ) ); SD && DBG( "ix[%" PR_SIZET "/%" PR_SIZET "/%" PR_SIZET "]", ixFirst, ixC, ixPastLast );
const auto xMin( conv.i2c( ixFirst ) );
const auto xMax( conv.i2c( ixPastLast-1 ) ); SD && DBG( "x[%d..%d]", xMin, xMax );
const auto wordCols ( xMax - xMin + 1 );
// this degree of paranoia only matters if the definition of a WORD includes a tab
const auto wordChars( ixPastLast - ixFirst ); SD && wordCols != wordChars && DBG( "%s wordCols=%d != wordChars=%" PR_PTRDIFFT, __func__, wordCols, wordChars );
// return everything
cursor->col = xMin;
return stref( rl.data() + ixFirst, wordChars );
}
}
}
return stref();
}
std::string GetDQuotedStringUnderPoint( PCFBUF pFBuf, const Point &cursor ) {
/*
search upstream for a ["quote-word boundary]; if found, search downstream
for a [word-quote boundary"]; it might straddle a line-break; return the
space-normalized contatenation of the text between these two boundaries.
NB: The DQuotedString can span NO MORE THAN TWO (adjacent) LINES
*/
std::string rv;
const auto yCursor( cursor.lin );
const auto xCursor( cursor.col );
const auto rlpt( pFBuf->PeekRawLine( yCursor ) );
if( !rlpt.empty() ) { 0 && DBG( "newln=%" PR_BSR, BSR(rlpt) );
const auto tw( pFBuf->TabWidth() ); // abc abc
const auto ixPt( CaptiveIdxOfCol( tw, rlpt, xCursor ) );
auto cat_rv = [&]( stref st ) { DBG( "cat_rv=%" PR_BSR "'", BSR(st) );
for( const auto ch : st ) {
if( isspace( ch ) && (rv.length() == 0 || isspace(rv.back())) ) {
}
else {
rv.push_back( ch );
}
}
};
{
auto ixUpstream = [&]( stref rl, const COL ixC ) {
for( auto ix(ixC) ; ix > 0 ; --ix ) {
if( !isspace( rl[ix] ) && ('"'==rl[ix-1]) ) {
const auto rlUp( rl.substr( ix, ixC-ix+1 ) ); DBG( "up=%" PR_BSR "'", BSR(rlUp) );
cat_rv( rlUp );
return true;
}
}
return false;
};
if( !ixUpstream( rlpt, ixPt ) && yCursor > 0 ) {
const auto rlp( pFBuf->PeekRawLine( yCursor-1 ) );
ixUpstream( rlp, rlp.length()-1 );
cat_rv( " " );
cat_rv( rlpt.substr(0,ixPt) );
}
if( rv.empty() ) { return ""; } // ********** upstream done
}
/*
"hello
there"
*/
{
auto ixDnstream = [&]( stref rl, const COL ixC ) { 0&&DBG( "dn0 %d [%" PR_BSR "]", ixC, BSR(rl) );
for( auto ix(ixC) ; ix < rl.length()-1 ; ++ix ) { 0&&DBG( "dn[%d]>%c", ixC, rl[ixC] );
if( !isspace( rl[ix] ) && ('"'==rl[ix+1]) ) {
const auto rlDn( rl.substr(ixC,ix-ixC+1) ); DBG( "dn=%" PR_BSR "'", BSR(rlDn) );
cat_rv( rlDn );
return true;
}
}
return false;
};
if( !ixDnstream( rlpt, ixPt+1 ) ) {
cat_rv( " " );
ixDnstream( pFBuf->PeekRawLine( yCursor+1 ), 0 );
}
}
}
return rv;
}
#ifdef fn_dquc
bool ARG::dquc() {
const auto st( GetDQuotedStringUnderPoint( g_CurFBuf(), g_Cursor() ) );
Msg( "%" PR_BSR "'", BSR(st) );
return true;
}
#endif
void WucState::VCursorMoved( bool fUpdtWUC, View &view ) { 0 && DBG( "%s fUpdtWuc=%d V=%p", __func__, fUpdtWUC, &view );
const auto boxstr_sel( view.GetWucOfSelection() );
if( !IsStringBlank( boxstr_sel ) ) {
if( !eq( d_stSel, boxstr_sel ) ) {
d_stSel.assign( boxstr_sel );
d_stSel.push_back( 0 ); // d_stSel content must look like StringsBuf content, which means an extra/2nd NUL marks the end of the last string
0 && DBG( "BOXSTR=%s|", d_stSel.c_str() );
d_sb.clear();
PrimeRefresh();
}
}
else if( fUpdtWUC ) {
const auto yCursor( view.Cursor().lin );
auto start( view.Cursor() );
const auto wuc( GetWordUnderPoint( view.CFBuf(), &start ) );
if( !wuc.empty() ) {
if( wuc.length() >= g_iWucMinLen ) { // WUC is long enough?
SetNewWuc( wuc, start.lin, start.col, &view );
}
}
else { // not on a word...
if( !d_sb.empty() ) { // ...but we are highlighting a non-sel WUC, so must redraw in case cursor...
PrimeRefresh(); // ...just departed the WUC (must transition from not- to highlighted)
}
}
}
}
class HiliteAddin_WordUnderCursor : public HiliteAddin {
void VCursorMoved( bool fUpdtWUC ) override;
// void VFbufLinesChanged( LINE yMin, LINE yMax ) { /* d_wucbuf[0] = '\0'; */ }
bool VHilitLineSegs( LINE yLine, LineColorsClipped &alcc ) override;
public:
HiliteAddin_WordUnderCursor( PView pView )
: HiliteAddin( pView )
{}
~HiliteAddin_WordUnderCursor() {}
PCChar Name() const override { return "WUC"; }
};
void HiliteAddin_WordUnderCursor::VCursorMoved( bool fUpdtWUC ) {
s_wucState.VCursorMoved( fUpdtWUC, d_view );
}
STATIC_FXN bool HilitWucLineSegs
( PCFBUF fb
, const WucState::sb_t &d_sb
, const std::string &d_stSel
, const Point &cursor
, LINE yLine
, LineColorsClipped &alcc
) {
const auto keyStart( !d_sb.empty() ? d_sb.data() : (d_stSel.empty() ? nullptr : d_stSel.c_str()) );
if( keyStart ) {
const auto rlAll( fb->PeekRawLine( yLine ) );
if( !rlAll.empty() ) {
const auto tw( fb->TabWidth() );
IdxCol_cached convAll( tw, rlAll );
const auto minIxClip( convAll.c2fi( alcc.GetMinColInDisp() ) );
if( minIxClip < rlAll.length() ) {
decltype( rlAll.length() ) maxNeedleLen( 0 );
{
for( auto pNeedle(keyStart) ; *pNeedle ; ) {
const stref needle( pNeedle ); 0 && DBG( "needle %" PR_BSR "'", BSR(needle) );
maxNeedleLen = std::max( maxNeedleLen, needle.length() );
pNeedle += needle.length() + 1;
}
}
const auto xMaxToDisp( alcc.GetMaxColInDisp() );
const auto maxIxClip( convAll.c2fi( xMaxToDisp ) + (maxNeedleLen-1) );
const auto maxLen( std::min( rlAll.length(), maxIxClip+1 ) ); // +1 to conv ix to length
const stref rl( rlAll.data(), maxLen );
IdxCol_cached conv( tw, rl );
for( size_t ixStart( minIxClip > (maxNeedleLen-1) ? minIxClip - (maxNeedleLen-1) : 0 ) ; ixStart < rl.length() ; ) {
auto ixFirstMatch( eosr ); auto mlen( 0u ); bool fMatchesFirstNeedle( false ); bool fTestingFirstNeedle( true );
auto haystack( rl ); haystack.remove_prefix( ixStart );
for( auto pNeedle(keyStart) ; *pNeedle ; ) {
const stref needle( pNeedle );
auto ixFind( haystack.find( needle ) );
if( ixFind != eosr ) {
ixFind += ixStart;
if( ixFirstMatch == eosr || ixFind < ixFirstMatch ) {
fMatchesFirstNeedle = fTestingFirstNeedle;
ixFirstMatch = ixFind; mlen = needle.length();
}
} // xWUCX xWUCX xWUCX clss::xWUCX clss::xWUCX objx.xWUCX objy.xWUCX objx.xWUCX xWUCX xWUCX xWUCX xWUCX
pNeedle += needle.length() + 1; fTestingFirstNeedle = false;
}
if( ixFirstMatch == eosr ) { break; }
const auto xFound( conv.i2c( ixFirstMatch ) );
0&&(xFound > xMaxToDisp) && DBG( "xFound > xMaxToDisp (%d > %d) maxNeedleLen=%" PR_BSRSIZET "", xFound, xMaxToDisp, maxNeedleLen );
if( xFound > xMaxToDisp ) { break; } // hit if undisplayable match found in (xMaxToDisp..xMaxToDisp+(maxNeedleLen-1)] (maxNeedleLen may be longer than you think)
if( d_stSel.c_str() == keyStart // is a selection pseudo-WUC?
|| // or a true WUC (only match _whole words_ matching d_wucbuf)
( (ixFirstMatch == 0 || !isWordChar(rl[ixFirstMatch ]) || !isWordChar(rl[ixFirstMatch-1 ])) // char to left of match is non-word
&& (ixFirstMatch+mlen == rl.length() || !isWordChar(rl[ixFirstMatch+mlen-1]) || !isWordChar(rl[ixFirstMatch+mlen])) // char to right of match is non-word
&& (yLine != cursor.lin || cursor.col < xFound || cursor.col > xFound + mlen - 1) // DON'T hilite actual WUC (it's visually annoying)
)
) {
alcc.PutColorTblIdx( xFound, mlen, fMatchesFirstNeedle ? ColorTblIdx::HIL : ColorTblIdx::WUC );
ixStart = ixFirstMatch + mlen; // xWUCXxWUCXxWUCXxWUCX
}
else {
ixStart = ixFirstMatch + 1; // since "true WUC" check above prevented a WUC from being hilited, resume search just after start of this non-hilited "match"
}
}
}
}
}
return false;
}
bool WucState::VHilitLineSegs( View &view, LINE yLine, LineColorsClipped &alcc ) const {
const auto fIgnoreCursorWuc( &view == get_wucSrc() ); 0 && DBG( "%s: igCurWUC=%d v=%p", __func__, fIgnoreCursorWuc, &view );
const Point &cursor = fIgnoreCursorWuc ? view.Cursor() : g_PtInvalid;
return HilitWucLineSegs( view.CFBuf(), get_sb(), get_stSel(), cursor, yLine, alcc );
}
bool HiliteAddin_WordUnderCursor::VHilitLineSegs( LINE yLine, LineColorsClipped &alcc ) {
return s_wucState.VHilitLineSegs( d_view, yLine, alcc );
}
struct CppCondEntry_t {
stref nm;
cppc val;
};
STATIC_FXN cppc IsCppConditional( stref src, int *pxMin ) { // *pxMin indexes into src[] which is RAW line text
const auto o1( FirstNonBlankOrEnd( src, 0 ) ); if( o1 >= src.length() || !('#' == src[o1]) ) { return cppcNone; }
const auto o2( FirstNonBlankOrEnd( src, o1 + 1 ) ); if( o2 >= src.length() || !isWordChar( src[o2]) ) { return cppcNone; }
const auto o3( FirstNonWordOrEnd ( src, o2 + 1 ) ); const auto word( src.substr( o2, o3-o2 ) );
STATIC_CONST CppCondEntry_t cond_keywds[] = {
{ "if" , cppcIf },
{ "ifdef" , cppcIf },
{ "ifndef" , cppcIf },
{ "else" , cppcElse },
{ "elif" , cppcElif },
{ "endif" , cppcEnd },
};
for( const auto &cc : cond_keywds ) {
if( eq( word, cc.nm ) ) {
*pxMin = o1;
return cc.val;
}
}
return cppcNone;
}
STATIC_FXN cppc IsGnuMakeConditional( stref src, int *pxMin ) { // *pxMin indexes into src[] which is RAW line text
if( !src.empty() && src[0] == HTAB ) { return cppcNone; }
const auto o2( FirstNonBlankOrEnd( src, 0 ) ); if( o2 >= src.length() || !isWordChar( src[o2] ) ) { return cppcNone; }
const auto o3( FirstNonWordOrEnd ( src, o2 + 1 ) ); const auto word( src.substr( o2, o3-o2 ) );
STATIC_CONST CppCondEntry_t cond_keywds[] = {
{ "ifeq" , cppcIf },
{ "ifneq" , cppcIf },
{ "ifdef" , cppcIf },
{ "ifndef" , cppcIf },
{ "else" , cppcElif }, // gmake else has elseif behavior
{ "endif" , cppcEnd },
{ "define" , cppcIf }, // not truly cond, but syntactically ...
{ "endef" , cppcEnd }, // ... same and worth hiliting
};
for( const auto &cc : cond_keywds ) {
if( eq( word, cc.nm ) ) {
*pxMin = o2;
return cc.val;
}
}
return cppcNone;
}
cppc FBOP::IsCppConditional( PCFBUF fb, LINE yLine ) {
const auto rl( fb->PeekRawLine( yLine ) );
COL xPound;
return ::IsCppConditional( rl, &xPound );
}
class HiliteAddin_cond_CPP : public HiliteAddin {
void VFbufLinesChanged( LINE yMin, LINE yMax ) override { refresh( yMin, yMax ); }
bool VHilitLine ( LINE yLine, COL xIndent, LineColorsClipped &alcc ) override;
void VWinResized() override {
d_PerViewableLine.resize( ViewLines() );
d_need_refresh = true;
}
virtual cppc IsCppConditional( stref src, int *pxPound ) { return ::IsCppConditional( src, pxPound ); }
public:
HiliteAddin_cond_CPP( PView pView )
: HiliteAddin( pView ) {
d_PerViewableLine.resize( ViewLines() );
d_need_refresh = true;
}
~HiliteAddin_cond_CPP() {}
PCChar Name() const override { return "cond_CPP"; }
private:
bool d_need_refresh = false;
struct PerViewableLineInfo {
struct {
cppc acppc;
COL xPound;
COL xMax;
int level_ix;
} line;
struct {
int containing_level_idx;
LINE yMin;
LINE yMax;
COL xBox;
} level;
};
std::vector<PerViewableLineInfo> d_PerViewableLine ;
int close_level( int level_ix, int yLast );
void refresh( LINE, LINE );
};
int HiliteAddin_cond_CPP::close_level( int level_ix, int yLast ) {
if( level_ix < 0 ) { return -1; } // CID128055
auto &level( d_PerViewableLine[ level_ix ].level );
level.yMax = yLast;
for( auto ixLine(level.yMin) ; ixLine <= level.yMax ; ++ixLine ) {
auto &line( d_PerViewableLine[ ixLine ].line );
level.xBox = std::max( level.xBox, line.xMax+1 );
}
// push out xBox of containing levels so they don't collide with this level's xBox
for( auto bump(0) ; level_ix > -1 ; level_ix = d_PerViewableLine[ level_ix ].level.containing_level_idx, bump += 2 ) {
auto &cont_level( d_PerViewableLine[ level_ix ].level );
cont_level.xBox = std::max( cont_level.xBox, level.xBox + bump );
}
return level.containing_level_idx;
}
void HiliteAddin_cond_CPP::refresh( LINE, LINE ) {
// pass 1: fill in line.{ xMax, cppc?, xPound? }
auto maxUnIfdEnds(0);
{
auto not_elses(0), elses(0);
auto upDowns(0);
auto fb( CFBuf() );
const auto tw( fb->TabWidth() );
for( auto iy(0) ; iy < ViewLines() ; ++iy ) {
auto &line( d_PerViewableLine[ iy ].line );
const auto yFile( MinVisibleFbufLine() + iy );
const auto rl( fb->PeekRawLine( yFile ) );
IdxCol_nocache conv( tw, rl );
line.xMax = conv.i2c( rl.length() );
line.acppc = IsCppConditional( rl, &line.xPound );
if( cppcNone != line.acppc ) {
line.xPound = conv.i2c( line.xPound );
switch( line.acppc ) {
break; default:
break; case cppcIf : --upDowns;
++not_elses;
break; case cppcEnd : ++upDowns; maxUnIfdEnds = std::max( maxUnIfdEnds, upDowns );
++not_elses;
break; case cppcElif: ++elses;
break; case cppcElse: ++elses;
}
}
}
if( 0 == not_elses && elses > 0 ) {
maxUnIfdEnds = 1;
}
}
// pass 2:
auto level_alloc_idx(-1); // increases monotonically
auto level_idx(-1); // goes up and down
while( level_idx < maxUnIfdEnds-1 ) {
auto &level( d_PerViewableLine[ (level_idx = ++level_alloc_idx) ].level );
level.containing_level_idx = level_idx-1; level.yMin = 0; level.yMax = 0; level.xBox = 0;
}
for( auto iy(0) ; iy < ViewLines() ; ++iy ) {
auto &line( d_PerViewableLine[ iy ].line );
line.level_ix = level_idx;
switch( line.acppc ) {
case cppcIf :{const auto containing_level_idx( level_idx );
auto &level = d_PerViewableLine[ (line.level_ix = level_idx = ++level_alloc_idx) ].level;
level.containing_level_idx = containing_level_idx; level.yMin = iy; level.yMax = iy; level.xBox = 0;
} break;
case cppcEnd : level_idx = close_level( level_idx, iy ); break;
default : break;
}
}
while( level_idx > -1 ) {
level_idx = close_level( level_idx, ViewLines()-1 );
}
d_need_refresh = false;
}
bool HiliteAddin_cond_CPP::VHilitLine( LINE yLine, COL xIndent, LineColorsClipped &alcc ) {
try {
if( d_need_refresh ) { // BUGBUG fix this!!!!!!!!!!
refresh( 0, 0 );
}
const auto lineInWindow( yLine - MinVisibleFbufLine() );
Assert( lineInWindow >= 0 && lineInWindow < ViewLines() );
const auto &line( d_PerViewableLine[ lineInWindow ].line );
// highlight any CPPcond that occurs on this line
if( cppcNone != line.acppc ) {
alcc.PutColorTblIdx( line.xPound, d_PerViewableLine[ line.level_ix ].level.xBox - line.xPound, ColorTblIdx::CPP );
}
// continue any "surrounds" of other highlit CPPconds above/below
for( auto level_idx(line.level_ix) ; level_idx > -1 ; level_idx = d_PerViewableLine[ level_idx ].level.containing_level_idx ) {
const auto xBar( d_PerViewableLine[ level_idx ].level.xBox );
alcc.PutColorTblIdx( xBar, 1, ColorTblIdx::CPP );
}
}
catch( const std::out_of_range& exc ) {
Msg( "%s caught std::out_of_range %s", __func__, exc.what() );
}
catch( ... ) {
Msg( "%s caught other exception", __func__ );
}
return false;
}
#if 1
#if 1
#else