-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhtmlizer__define.pro
2053 lines (1709 loc) · 59.9 KB
/
htmlizer__define.pro
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
;h+
; (c) 2018 Harris Geospatial Solutions, Inc.
;
; Licensed under MIT. See LICENSE.txt for additional details and information.
;h-
;+
;
; Object that colorizes IDL code with CSS classes for use in
; web pages. Code uses regular expressions and some fancy logic behind the
; scenes to duplicate the same syntax highlighting that you see in the IDL
; workbench. See the main level program at the bottom of the file for an
; example of how to use the code.
;
; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
;-
;+
; :Private:
;
; :Description:
; Procedure to replace any HTML custom characters in the strings.
;
; :Params:
; str: in, required, type=string
; IDL code string to process.
;
;
;
; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
;-
pro replaceHTMLChars, str
compile_opt idl2, hidden
;replace any occurrences of the special character "&" with its HTML escape sequence:
;check this first so that we don't mess up our other repalcers
ampPos = strpos(str,'&')
if (ampPos ne -1) then begin
str = str.replace('&', '&')
endif
;replace any occurrences of the special character "<" with its HTML escape sequence:
ltPos = strpos(str,'<')
while ltPos ne -1 do begin
;check to see if "<" is associated with font modifier HTML tags:
strAfter=strmid(str,ltPos+1,4)
if (strAfter EQ 'font') or (strAfter EQ '/fon') then begin ;HTML tags; ignore and look again:
ltPos = strpos(str,'<',ltPos+1)
endif else begin ;legitimate IDL minimum operator; replace with HTML escape sequence:
str = strmid(str,0,ltPos) + '<' + strmid(str,ltPos+1)
;look again on the current line for any other occurrences of "<":
ltPos = strpos(str,'<',ltPos+1)
endelse
endwhile
;replace any occurrences of the special character ">" with its HTML escape sequence:
gtPos = strpos(str,'>')
while (gtPos ne -1) do begin
;check to see if ">" is associated with font modifier HTML tags:
strBefore=strmid(str,gtPos-2,2)
if (strBefore eq '">') then begin ;HTML tags; ignore and look again:
gtPos = strpos(str,'>',gtPos+1)
endif else begin ;legitimate IDL maximum operator; replace with HTML escape sequence:
str = strmid(str,0,gtPos) + '>' + strmid(str,gtPos+1)
;look again on the current line for any other occurrences of ">":
gtPos = strpos(str,'>',gtPos+1)
endelse
endwhile
end
pro update_replacer, $
inName, replacer, toolDatabase,$
SEARCH_REPLACE = search_replace,$
CONTROL = control,$
FUNC = func,$
PROC = proc,$
SYSV = sysv,$
METHOD = method,$
TOOLTIPS = tooltips,$
DOCS_LINKS = docs_links,$
CUSTOM_TOOLTIPS = custom_tooltips,$
BASELINK = baselink
compile_opt idl2, hidden
;duplicate input name
name = inName
;set our outputs to null strings
out_link = ''
out_tt = ''
;flag for being in the database or not
inDB = 0
;check if we need to replace our search item or not
if keyword_set(search_replace) then begin
;custom changes for printing
if search_replace.haskey(strlowcase(name)) then begin
dbSearch = strupcase(search_replace[strlowcase(name)])
endif else begin
dbSearch = strupcase(name)
endelse
endif else begin
dbSearch = strupcase(name)
endelse
;figure out what our search term needs to be for our database
;depending on the type of item the searches are a little different fromone another
case (1) of
;function method
keyword_set(method):begin
name = '::' + dbSearch
inDB = toolDatabase.hasKey(name)
end
;system variable
keyword_set(sysv):begin
name = dbSearch
inDB = toolDatabase.hasKey(name)
end
;control statements
keyword_set(control):begin
name = dbSearch
inDB = toolDatabase.hasKey(name)
if ~inDb then begin
name += + '...'
inDB = toolDatabase.hasKey(name)
endif
end
;procedures
keyword_set(procedure):begin
;duplaicate routiens start with the name and then Procedure afterwards (i.e. plot and plot())
name = dbSearch + ' Procedure"'
inDB = toolDatabase.hasKey(name)
if ~inDB then begin
name = dbSearch
inDB = toolDatabase.hasKey(name)
endif
end
;everything else, including functions
else:begin
name = dbSearch
inDB = toolDatabase.hasKey(name)
end
endcase
;see if we found the entry we were searching for
if (inDB) then begin
lookup = toolDatabase[name]
tt = lookup.tooltip
link = lookup.link
useLink = baselink
endif else begin
;check if we are in a custom tooltip
if keyword_set(custom_tooltips) then begin
if keyword_set(name) then begin
;remove procedure
name = name.replace(' procedure', '')
;check if we are a function
if keyword_set(func) then begin
name += '()'
endif
;check if we have a match
if custom_tooltips.hasKey('tooltips') then begin
;extract tooltips
tts = custom_tooltips['tooltips']
;check if we have a match
if tts.hasKey(name) then begin
useLink = custom_tooltips['baselink']
tt = tts[name, 'tooltip']
link = tts[name, 'relative-path']
inDB = 1
endif
endif
endif
endif
endelse
;check how we need to modify our strings
if (inDB) then begin
if keyword_set(tooltips) AND keyword_set(tt) then begin
replace = replacer + 'idl_tt" idl_tt="' + tt + '">' + inName + "</font>"
endif else begin
replace = replacer + '">' + inName + '</font>'
endelse
if keyword_set(docs_links) AND keyword_set(link) then begin
replace = '<a class="idl_docs_link" href="' + useLink + link + '" target="_blank">' + replace + '</a>'
endif
replacer = replace
endif else begin
replacer = replacer + '">' + inName + '</font>'
endelse
end
;+
; :Private:
;
; :Description:
; Function that splits strings into sections that we do and don't process
;
; :Params:
; str: in, required, type=string
; The IDL string that needs to be split
;
; :Keywords:
; PROCESS: out, requried, type=bytarr
; A byte array with 1/0 flags for processing or no processing.
;
; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
;-
function splitString, str, PROCESS = process, STRING_START = string_start, ALREADY_SPLIT = already_split
compile_opt idl2, hidden
;preallocate arrays to hole the
parts = strarr((strlen(str)/2 + 1) > 2)
process = bytarr((strlen(str)/2 + 1) > 2) + 1b
string_start = bytarr((strlen(str)/2 + 1) > 2)
string_start[0] = 1
;check if we don't want to color our line
if (strpos(str, '!NOCOLOR') eq 0) then begin
str = strmid(str, 9)
replaceHTMLChars, str
process = [0]
string_start = [1]
return, [str]
endif
;check if we have console output
if (strpos(str, '!CONSOLE') eq 0) then begin
str = ' ' + strmid(str, 9)
replaceHTMLChars, str
process = [0]
string_start = [1]
return, [str]
endif
;check if we have a bold line
boldPos = strpos(str, '!BOLD')
if (boldPos ne -1) then begin
str = strmid(str, boldPos + 6)
replaceHTMLChars, str
str = '<font class="idl_bold">' + str + '</font>'
process = [0]
string_start = [1]
return, [str]
endif
;check if we have a gray line
grayPos = strpos(str, '!GRAY')
if (grayPos ne -1) then begin
str = strmid(str, grayPos + 6)
replaceHTMLChars, str
str = '<font class="idl_gray">' + str + '</font>'
process = [0]
string_start = [1]
return, [str]
endif
;first, split everything into paren or not by looking for
;string of the form "(expression).something"
posParen = stregex(str, '\(.*\)\.', LENGTH = lParen)
if (posParen eq 0) AND ~keyword_set(already_split) then begin
;split!
front = strmid(str, 0, posParen + lParen - 2)
back = strmid(str, posParen + lParen - 2)
;process front and back
parts = [ $
splitString(front, PROCESS = processFront, STRING_START = startFront, /ALREADY_SPLIT), $
splitString(back, PROCESS = processBack, STRING_START = startBack, /ALREADY_SPLIT) $
]
;join process flags
process = [processFront, processBack]
;set flags for if we are the start of the string or not
string_start = [startFront*0, startBack]
endif else begin
;counter for how many parts we really have
nparts = 0
;get the positions of our splitters
positions = [strpos(str, ';'), strpos(str, '"'), strpos(str, "'")]
idx_ok = where(positions ne -1, count_ok)
;nothing to split
if (count_ok eq 0) then begin
process = [1]
string_start = [1]
replaceHTMLChars, str
return, [str]
endif else begin
;iterate!
while (count_ok gt 0) do begin
;figure out what we are dealing with here
case min(positions[idx_ok]) of
;semi-colon
positions[0]:begin
type = 0b
startPos = positions[0]
endPos = strlen(str)
end
;double quote
positions[1]:begin
type = 1b
startPos = positions[1]
endPos = strpos(strmid(str, startPos+1), '"')
if (endPos eq -1) then begin
endPos = strlen(str)
endif
end
;single-quote
positions[2]:begin
type = 2b
startPos = positions[2]
endPos = strpos(strmid(str, startPos+1), "'")
if (endPos eq -1) then begin
endPos = strlen(str)
endif
end
endcase
;save the beginning of the string
parts[nparts] = strmid(str, 0, startPos)
;increment our counter
nparts++
;get the mid portion of our string
mid = strmid(str, startPos, endPos+2)
;replace any bad characters that might be in the strings
replaceHTMLChars, mid
if (type eq 1) or (type eq 2) then begin
if (strpos(mid, "'") ne -1) then mid = mid.replace("'", ''')
if (strpos(mid, '"') ne -1) then mid = mid.replace('"', '"')
endif
case type of
0: type = 'class="idl_comment"'
1: type = 'class="idl_str"'
2: type = 'class="idl_str"'
endcase
;save the part of our string that we don't want to process
parts[nparts] = '<font ' + type + '>' + mid + '</font>'
process[nparts] = 0
;update our string component
str = strmid(str, startpos + endPos + 2)
;increment our counter
nparts ++
;get the positions of our splitters
positions = [strpos(str, ';'), strpos(str, '"'), strpos(str, "'")]
idx_ok = where(positions gt 0, count_ok)
endwhile
;check if we need to save the last part of the strings
if (strtrim(str,2) ne '') then begin
parts[nparts] = str
nparts++
endif
endelse
foreach part, parts, i do begin
if ~process[i] then continue
if (i eq (nparts-1)) then break
replaceHTMLChars, part
parts[i] = part
endforeach
;get the good parts of our arrays
parts = parts[0:nparts-1]
process = process[0:nparts-1]
string_start = string_start[0:nparts-1]
endelse
;return our parts
return, parts
end
;+
; :Private:
;
; :Description:
; Procedure to split strings that are to be processed and remove the
; HTML tags from processing.
;
; :Params:
; strs: in, required, type=strarr
; The array of strings that represent each part of the string.
; process: in, required, type=bytarr
; The array of 1/0 flags for if a section is processed or not
;
;
;
; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
;-
pro secondSplit, strs, process, string_start
compile_opt idl2, hidden
;find each expression
expressions = ['IDL&[a-z0-9_]+;', 'ENVI&[a-z0-9_]+;', '&[a-z0-9_]+;']
nExp = 3
foreach pFlag, process, i do begin
if ~pFlag then continue
str = strs[i]
;loop over each expression that we are looking for, special for IDL and ENVI prompts
foreach exp, expressions, j do begin
;search with regex
pos = stregex(str, exp, LENGTH = length)
;make sure that we found something
if (pos ne -1) then begin
;extract pieces of our strings
front = strmid(str, 0, pos)
sub = strmid(str, pos, length)
back = strmid(str, pos + length)
;check if the sub doesn't start with '&' which means we bold
;it for the prompts
if (j lt (nExp-1)) then begin
sub = '<font class="idl_bold">' + sub + '</font>'
endif
;find which strings to use by excluding null strings
add = [front, sub, back]
addP = [1, 0, 1]
addS = [0, 0, 0]
idxOk = where(add ne '', countOk)
;process the string accordingly
if (countOk gt 0) then begin
case (1) of
;only one element
(n_elements(process) eq 1): begin
strs = add[idxOk]
process = addP[idxOk]
string_start = addS[idxOk]
end
;first element of the array
(i eq 0):begin
strs = [add[idxOk], strs[(i+1)<(n_elements(process)):-1]]
process = [addP[idxOk], process[(i+1)<(n_elements(process)):-1]]
string_start = [addS[idxOk], string_start[(i+1)<(n_elements(process)):-1]]
end
;last element of the array
(i eq (n_elements(process)-1)): begin
strs = [strs[0:i-1], add[idxOk]]
process = [process[0:i-1], addP[idxOk]]
string_start = [string_start[0:i-1], addS[idxOk]]
end
;otherwise in the middle
else:begin
strs = [strs[0:i-1], add[idxOk], strs[(i+1)<(n_elements(process)):-1]]
process = [process[0:i-1], addP[idxOk], process[(i+1)<(n_elements(process)):-1]]
string_start = [string_start[0:i-1], addS[idxOk], string_start[(i+1)<(n_elements(process)):-1]]
end
endcase
endif
;stop looping because we found something in our string and should not
;process anymore
break
endif
endforeach
endforeach
end
;+
; :Description:
; Simple init method for our object that reads in the CSV file. Idea
; is to read in the file once and re-use the HTMLIzer in multiple places.
;
;
; :Private:
; CUSTOM_TOOLTIPS: in, optional, type=orderedhash, private
; Functionality that allows for custom tooltips when creating
; documentation for packages built with the IDL Package Creator.
;
;
; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
;-
function HTMLizer::init, CUSTOM_TOOLTIPS = custom_tooltips
compile_opt idl2, hidden
;set the default base link for the object
self.BASELINK = 'https://www.harrisgeospatial.com/docs/'
;make sure IDL version is at least 5.5 or newer, ignore beta and development build versions
;of IDL because string to float conversion will fail:
betaTest = strpos(strlowcase(!VERSION.RELEASE),'beta')
buildTest = strpos(strlowcase(!VERSION.RELEASE),'build')
;check to make sure the version of IDL running is 5.4 or newer:
if (betaTest EQ -1) and (buildTest EQ -1) then begin
version=float(!VERSION.RELEASE)
if (version LT 8.4) then begin
message, 'IDL_TO_HTML is only supported in IDL version 8.4 or newer.'
endif
endif
;get the current directory
thisdir = file_dirname(routine_filepath())
;check if we already have an htmlizer
defsysv, '!htmlizer', EXISTS = exists
if ~exists then begin
;check if our tooltips exist or not and create the super-fast-access hash
tooltips = thisdir + path_sep() + 'idl_tooltips.sav'
if ~file_test(tooltips) then begin
htmlizer_sav_to_hash
endif
;restore the tooltips
restore, tooltips
;verify we have routines
if ~isa(tooltips, 'hash') then begin
message, 'idl_tooltips.sav does not contain a variable called "tooltips", required!'
endif
endif else begin
if ~obj_valid(!htmlizer) then begin
;check if our tooltips exist or not and create the super-fast-access hash
tooltips = thisdir + path_sep() + 'idl_tooltips.sav'
if ~file_test(tooltips) then begin
htmlizer_sav_to_hash
endif
;restore the tooltips
restore, tooltips
;verify we have routines
if ~isa(tooltips, 'hash') then begin
message, 'idl_tooltips.sav does not contain a variable called "tooltips", required!'
endif
endif else begin
tooltips = !htmlizer
endelse
endelse
;save our database as an object property
self.ROUTINE_DB = tooltips
;check for custom tooltips
if keyword_set(custom_tooltips) then self.CUSTOM_DB = custom_tooltips
;obtain list of current System Procedures from ROUTINE_INFO:
sysProcedures = [strlowcase(routine_info(/SYSTEM)), 'return']
;only keep object methods
idx_keep = where(strpos(sysProcedures,':') eq -1, count_keep)
if (count_keep gt 0) then begin
sysProcedures = temporary(sysProcedures[idx_keep])
endif
;obtain list of current System Functions from ROUTINE_INFO:
sysFunctions = strlowcase(routine_info(/SYSTEM, /FUNCTIONS))
idx_keep = where(strpos(sysFunctions,':') eq -1, count_keep)
if (count_keep gt 0) then begin
sysFunctions = temporary(sysFunctions[idx_keep])
endif
;add modifications to our things we want to search for
sysFunctions = [sysFunctions, 'hash', 'orderedhash', 'list', 'idltask']
sysFunctions = temporary(sysFunctions.sort())
;add our program control statements
;do this because we need a way to delineate between procedures and these
programControl = ['and', 'begin', 'break', 'case', 'common', 'compile_opt',$
'continue', 'do', 'else', 'end', 'endcase', 'endelse', 'endfor', 'endforeach', $
'endif', 'endrep', 'endswitch', 'endwhile', 'eq', 'for', 'foreach', $
'forward_function', 'function', 'ge', 'goto', 'gt', 'if', 'inherits', $
'le', 'lt', 'mod', 'ne', 'not', 'of', 'on_ioerror', 'or', 'pro', 'repeat',$
'switch', 'then', 'until', 'while', 'xor']
;create a data structure to remember our variables
datNames = orderedhash()
datNames['SYSPROCEDURES'] = sysProcedures
datNames['SYSFUNCTIONS'] = sysFunctions
datNames['PROGRAMCONTROL'] = programControl
;save to object definition
self.SEARCH_FOR = datNames
;check if we already have an htmlizer
defsysv, '!htmlizer', EXISTS = exists
if ~exists then begin
defsysv, '!htmlizer', tooltips
endif
return, 1
end
;+
; :Description:
; Routine for cleaning up the HTMLizer object.
;
;
; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
;-
pro HTMLizer::Cleanup
compile_opt idl2
obj_destroy, self
end
;+
;
; :Private:
;
; :Description:
; Internal routine to process a string.
;
; :Params:
; text
;
; :Keywords:
; CONTINUATION
; TOOLTIPS
; DOCS_LINKS
; STRING_START
; IDL_CONSOLE
; STR_ORIG
;
; :Author: Zachary Norman - GitHub: [znorman-harris](https://github.com/znorman-harris)
;-
pro htmlizer::ProcessString, text, $
CONTINUATION = continuation,$
TOOLTIPS = tooltips,$
DOCS_LINKS = docs_links,$
STRING_START = string_start,$
IDL_CONSOLE = idl_console,$
STR_ORIG = str_orig
compile_opt idl2, hidden
;pre-allocate an array to hold our string contents for us
strings = strarr(2)
process = bytarr(2) + 1b
;base link for our tool tips
baseLink = self.BASELINK
;get what we want to search for
findThese = self.SEARCH_FOR
;get information from our object
sysProcedures = findThese['SYSPROCEDURES']
sysFunctions = findThese['SYSFUNCTIONS']
programControl = findThese['PROGRAMCONTROL']
;custom hashes tha tconatin key/value pairs with what we need to search
;for in our tool database for everything to work correctly with tooltips
;and links to the documentation center
;hash to save the custom database searches for sys procedures
sysProSearch = hash()
sysProSearch['print'] = 'PRINT/PRINTF'
sysProSearch['printf'] = 'PRINT/PRINTF'
;hash to save the custom database searches for sys functions
sysFuncSearch = hash()
;hash to save the custom database searches for lib procedures
libProSearch = hash()
;hash to save the custom database searches for lib functions
libFuncSearch = hash()
;hash to save the custom database searches for program controls
progConSearch = hash()
;hash to save the custom database searches for system variables
sysvSearch = hash()
;hash to save the custom database searches for function methods
funcMethodSearch = hash()
;hash to save the custom database searches for function methods
proMethodSearch = hash()
;hash for custom database searched with control statements
idl_control = hash()
;control statements that a procedure can come after
okPro = ['then', 'else', 'do']
;get our tool database
toolDatabase = self.ROUTINE_DB
;replace any bad characters in the substring
replaceHTMLChars, text
;add our first string to work with
strings[0] = text
process[0] = 1
nparts = 1
;check if we don't want to color our line
if text.startswith('!NOCOLOR') then begin
text = strmid(text, 9)
return
endif
;check if we have console output
if text.startswith('!CONSOLE') then begin
text = ' ' + strmid(text, 9)
return
endif
;check if we have a bold line
if (strpos(strtrim(text,2), '!BOLD') eq 0) then begin
text = '<font class="idl_bold">' + text.replace('!BOLD', '') + '</font>'
return
endif
;check if we have a gray line
if (strpos(strtrim(text,2), '!GRAY') eq 0) then begin
text = '<font class="idl_gray">' + text.replace('!GRAY', '') + '</font>'
return
endif
;flag if we ahve an equal sign or not
eqStart = strpos(text, '=')
if (eqStart ne -1) then begin
hasEqual = 1
endif else begin
hasEqual = 0
endelse
;check for executive commands
;check for function methods with periods
exp = '\.[a-z_][a-z0-9_]*'
replacer = '<font class="idl_exec '
foreach text, strings, i do begin
;skip conditions
if ~process[i] OR (strtrim(text,2) eq '') then continue
; i.e. this.that, THIS = 5, THAT = 6
start = stregex(text, exp, LENGTH = length, /FOLD_CASE)
;skip if not found
if (start eq -1) then continue
;get the character before only if we are not at the beginning of the string
if (start gt 0) then begin
if (strtrim(strmid(text, start-1, 1),2) ne '') then continue
endif
;fix position
start+=1
length-=1
;get procedure method
chars = strmid(text, start, length)
;refresh our replacer
new = replacer + '">' + chars + '</font>'
;save our updates
if (i eq 0) then begin
strings = [strmid(text, 0, start), new, strmid(text, start+length), strings[i+1:-1]]
process = [1, 0, 1, process[i+1:-1]]
string_start = 0
endif else begin
strings = [strings[0:(i-1)>0], strmid(text, 0, start), new, strmid(text, start+length), strings[i+1:-1]]
process = [process[0:(i-1)>0], 1, 0, 1, process[i+1:-1]]
endelse
;stop processing because we can only have one xecutive command at a time
break
endforeach
;check for function methods with periods
exp = '\.[a-z_][a-z0-9_$]*\('
replacer = '<font class="idl_lib_func '
foreach text, strings, i do begin
;skip conditions
if ~process[i] OR (strtrim(text,2) eq '') then continue
; i.e. this.that, THIS = 5, THAT = 6
start = stregex(text, exp, LENGTH = length, /FOLD_CASE)
;skip if not found
if (start eq -1) then continue
;fix position
start+=1
length-=2
;get procedure method
chars = strmid(text, start, length)
;refresh our replacer
new = replacer
update_replacer, $
chars, new, toolDatabase,$
SEARCH_REPLACE = funcMethodSearch,$
/METHOD,$
/FUNC,$
TOOLTIPS = tooltips,$
DOCS_LINKS = docs_links,$
CUSTOM_TOOLTIPS = self.CUSTOM_DB,$
BASELINK = baselink
;save our updates
if (i eq 0) then begin
;extract the string
strings = [strmid(text, 0, start), new, strmid(text, start+length), strings[i+1:-1]]
process = [1, 0, 1, process[i+1:-1]]
endif else begin
strings = [strings[0:(i-1)>0], strmid(text, 0, start), new, strmid(text, start+length), strings[i+1:-1]]
process = [process[0:(i-1)>0], 1, 0, 1, process[i+1:-1]]
endelse
;increment the number for our counter
i+=1
endforeach
;check for function methods with dashes and greater than signs
exp = '->[a-z_][a-z0-9_$]*\('
replacer = '<font class="idl_lib_func '
foreach text, strings, i do begin
;skip conditions
if ~process[i] OR (strtrim(text,2) eq '') then continue
; i.e. this.that, THIS = 5, THAT = 6
start = stregex(text, exp, LENGTH = length, /FOLD_CASE)
;skip if not found
if (start eq -1) then continue
;fix position
start+=5
length-=6
;get procedure method
chars = strmid(text, start, length)
;refresh our replacer
new = replacer
update_replacer, $
chars, new, toolDatabase,$
SEARCH_REPLACE = funcMethodSearch,$
/METHOD,$
/FUNC,$
TOOLTIPS = tooltips,$
DOCS_LINKS = docs_links,$
CUSTOM_TOOLTIPS = self.CUSTOM_DB,$
BASELINK = baselink
;save our updates
if (i eq 0) then begin
strings = [strmid(text, 0, start), new, strmid(text, start+length), strings[i+1:-1]]
process = [1, 0, 1, process[i+1:-1]]
endif else begin
strings = [strings[0:(i-1)>0], strmid(text, 0, start), new, strmid(text, start+length), strings[i+1:-1]]
process = [process[0:(i-1)>0], 1, 0, 1, process[i+1:-1]]
endelse
;increment the number for our counter
i+=1
endforeach
;check for procedure methods with dots
exp = '\.[a-z_][a-z0-9_$]*'
replacer = '<font class="idl_lib_pro '
foreach text, strings, i do begin
;skip if line continuation
if keyword_set(continuation) then continue
;only check first part of string
if ~keyword_set(string_start) then continue
;skip conditions
if ~process[i] OR (strtrim(text,2) eq '') OR keyword_set(continuation) then continue
; i.e. this.that, THIS = 5, THAT = 6
start = stregex(text, exp, LENGTH = length, /FOLD_CASE)
;skip if not found
if (start eq -1) then continue
;get the next character
nextChar = strmid(text,start+length,1)
;make sure we are not a property
if (total(nextChar eq ['.']) gt 0) then begin
; i.e. this.that, THIS = 5, THAT = 6
add = start+length
start = stregex(strmid(text,start+length), exp, LENGTH = length, /FOLD_CASE)
;skip if not found
if (start eq -1) then continue
;increment start
start += add
endif
;find the comma and equal sign
eqStart = strpos(text, '=')
commaStart = strpos(text, ',')
;make sure we are left of the equal sign if we have one
;and that there is a comma present after the found string
if (eqStart ne -1) then begin
if (commaStart eq -1) then continue
if (eqStart le start) OR (eqStart lt commaStart) then continue
endif
;check our comma
if (commaStart ne -1) then begin
if (start gt commaStart) then continue
endif
;if IDL console and no comma present skip because we cannot
;delineate between variables (i.e. strucure, dict) or procedures
if keyword_set(idl_console) AND (commaStart eq -1) then continue
;check to make sure that, if there are strings after the name that there is
;a comma present. otherwise we probably have a property
if (strtrim(strmid(text, start + length + 1),2) ne '') AND (commaStart eq -1) then continue
;get original starting point
startOrig = start
;fix position
start+=1
length-=1
;get procedure method
chars = strmid(text, start, length)
;check if we really have an executive command being set
;this will be when the string starts with '.' or when there
;is a space in front of the period
if (startorig eq 0) then begin
exec = 1
endif else begin
if (strmid(text, startorig-1, 1) eq ' ') then begin
exec = 1
endif else begin
exec = 0
endelse
endelse
;check if property or a
if (exec) then begin
new = '<font class="idl_exec">' + chars + '</font>'
endif else begin
;refresh our replacer
new = replacer
update_replacer, $
chars, new, toolDatabase,$
SEARCH_REPLACE = proMethodSearch,$
/METHOD,$
TOOLTIPS = tooltips,$
DOCS_LINKS = docs_links,$
CUSTOM_TOOLTIPS = self.CUSTOM_DB,$
BASELINK = baselink
endelse
;save our updates
if (i eq 0) then begin
strings = [strmid(text, 0, start), new, strmid(text, start+length), strings[i+1:-1]]
process = [1, 0, 1, process[i+1:-1]]
string_start = 0
endif else begin
strings = [strings[0:(i-1)>0], strmid(text, 0, start), new, strmid(text, start+length), strings[i+1:-1]]
process = [process[0:(i-1)>0], 1, 0, 1, process[i+1:-1]]
endelse
;increment the number for our counter
i+=1