This repository was archived by the owner on Aug 31, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 72
Expand file tree
/
Copy pathrevsecommoneditorbehavior.livecodescript
More file actions
3962 lines (3317 loc) · 135 KB
/
revsecommoneditorbehavior.livecodescript
File metadata and controls
3962 lines (3317 loc) · 135 KB
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
script "com.livecode.scripteditor.behavior.editorcommon"
local sObjectId
# The following variables store information used for the undo / redo system.
local sTextOperationOffsets
local sTextOperationNewTexts
local sTextOperationOldTexts
local sTextOperationIndex
local sTextOperationTop
local sTextGroupLabels
local sTextGroupLengths
local sTextGroupIndex
local sTextGroupTop
local sTextFormatKeywordMap
local sTextMark
local sLastSelectedChunk
local sLastNonEmptySelection
# Stores an array of cached object scripts for switching back to objects with unsaved changes
local sScriptCache
# OK-2009-01-17 : Bug 7169 - Store whether scripts are "dirty" or not, i.e whether the user has actually
# modified them, as opposed to they were modified by the script editor. (This can happen in the case of template scripts)
local sDirty
# Stores requests to update the script editor in reponse to a selection changed
local sSelectionUpdateRequest
# Stores requests to update the script editor panes
local sPaneUpdateRequest
# Stores the id of the last request to update the gutter, if there is one pending.
local sGutterUpdateRequest
# We also need to store whether the last update request involved text changing and
# whether it required the compilation errors to be updated. This is because it may be
# cancelled by a subsequent request that didn't require these things, resulting in the update
# being lost.
local sGutterUpdateRequestDetails
# Stores that an arrowKey is pressed from rawKeyDown
local sArrowKeyPressed
local sPlaceholders
local sEditChunks
local sEditPlaceholder
constant kPlaceholderDefaultSearchLines = 20
constant kPlaceholderDefaultBackgroundColor = "240,240,240"
# 2017-08-01 bhall2001 previous vScroll position of the Editor field
local sVScroll
# 2017-08-01 bhall2001 Toggles between true/false on each up arrow key pressed
# Note: up arrow key sends 2 scrollBarDrag messages. Used as flag
# to process only 1 of the messages.
local sSkipUpArrow = false
# OK-2009-01-17 : Bug 7169
command setDirty pObject, pValue
put pValue into sDirty[pObject]
end setDirty
command getDirty pObject
return sDirty[pObject]
end getDirty
command setObjectID pObjectID
if pObjectID is not empty then
local tRuggedID
put revRuggedId(pObjectID) into tRuggedID
if tRuggedID is not sObjectID then
put tRuggedID into sObjectID
if exists(sObjectId) and \
revEnvironmentEditionProperty("autocomplete") then
ideAutocompleteIntrospectMessagePath sObjectId
end if
end if
else
put empty into sObjectID
end if
delete variable sPlaceholders
delete variable sEditChunks
delete variable sEditPlaceholder
end setObjectID
function getObjectID
return sObjectID
end getObjectID
command updateObjectID pOldObjectID, pNewObjectID
# If there was a previous object and we are changing its id, then we have to update all the script locals
# that may contain references to the old object. This is rather ugly, but it allows things like undo to work
# even when objects are moved.
# Script cache, stores unapplied scripts when tabs are changed
updateArrayKey sScriptCache, pOldObjectID, pNewObjectID
# Undo stuff
updateMultiItemArrayKey sTextOperationOffsets, pOldObjectID, pNewObjectID, 44, 1
updateMultiItemArrayKey sTextOperationOldTexts, pOldObjectID, pNewObjectID, 44, 1
updateMultiItemArrayKey sTextOperationNewTexts, pOldObjectID, pNewObjectID, 44, 1
updateArrayKey sTextOperationTop, pOldObjectID, pNewObjectID
updateArrayKey sTextOperationIndex, pOldObjectID, pNewObjectID
updateMultiItemArrayKey sTextGroupLabels, pOldObjectID, pNewObjectID, 44, 1
updateMultiItemArrayKey sTextGroupLengths, pOldObjectID, pNewObjectID, 44, 1
updateArrayKey sTextGroupIndex, pOldObjectID, pNewObjectID
updateArrayKey sTextGroupTop, pOldObjectID, pNewObjectID
updateArrayKey sTextMark, pOldObjectID, pNewObjectID
end updateObjectID
# Parameters
# pObjectId : reference to the object to set the script to. Must be one of the target objects of the script editor.
# Description
# Clears the script cache for the specified object. This should be called when the object is being closed. The cache also
# includes selection information.
command clearCache pObject, pDontCheckId
local tObject
if pDontCheckId then
put pObject into tObject
else
put revRuggedId(pObject) into tObject
end if
delete variable sScriptCache[tObject]
if revEnvironmentEditionProperty("autocomplete") then
ideAutocompleteClearObjectCache tObject
end if
end clearCache
# Parameters
# pArray : reference to an array. This gets modified
# pOldKey : one of the array keys
# pNewKey : the new key to replace pOldKey with
# Description
# Removes the element pOldKey from the array, and creates
# a new element pNewKey with the same data. Essentially renaming the key.
# If the element is empty, does nothing.
private command updateArrayKey @pArray, pOldKey, pNewKey
local tData
if pArray[pOldKey] is not empty then
put pArray[pOldKey] into tData
delete variable pArray[pOldKey]
put tData into pArray[pNewKey]
end if
end updateArrayKey
# Parameters
# pArray : reference to an array. This gets modified
# pOldKey : one of the array keys
# pNewKey : the new key to replace pOldKey with
# pDelimiter : an ascii char code that delimits dimensions in the array's key. If empty, comma is assumed.
# pItemNumer : which item number of the key needs to match pOldKey
# Description
# Removes all keys with the specified dimension matching pOldKey from the array
# and replaces them with equivalent keys matching pNewKey.
private command updateMultiItemArrayKey @pArray, pOldKey, pNewKey, pDelimiter, pItemNumber
local tData
if pDelimiter is not empty then
set the itemDelimiter to numToChar(pDelimiter)
end if
# Create a list of keys that need to be modified
local tKeys
repeat for each line tKey in the keys of pArray
if item pItemNumber of tKey is pOldKey then
put tKey & return after tKeys
end if
end repeat
delete the last char of tKeys
if tKeys is empty then
exit updateMultiItemArrayKey
end if
# Apply the modification to the keys
repeat for each line tKey in tKeys
local tNewKey
put tKey into tNewKey
put pNewKey into item pItemNumber of tNewKey
put pArray[tKey] into pArray[tNewKey]
delete variable pArray[tKey]
end repeat
end updateMultiItemArrayKey
command textInitialize
put empty into sTextOperationOffsets
put empty into sTextOperationNewTexts
put empty into sTextOperationOldTexts
put 0 into sTextOperationIndex
put 0 into sTextOperationTop
put empty into sTextGroupLabels
put empty into sTextGroupLengths
put 0 into sTextGroupIndex
put 0 into sTextGroupTop
put empty into sTextMark
textFormatInitialize
textMark "Insert"
end textInitialize
command getLastSelection
return slastNonEmptySelection
end getLastSelection
command setLastSelectedChunk pSelection
put pSelection into sLastSelectedChunk
end setLastSelectedChunk
command getLastSelectedChunk
return sLastSelectedChunk
end getLastSelectedChunk
command getLastSelectedWord
local tFrom, tTo
put item 1 of sLastSelectedChunk into tFrom
put item 2 of sLastSelectedChunk into tTo
# If there is a non empty selection, just return that.
if tTo > tFrom then
return char tFrom to tTo of the text of getScriptField()
end if
# If the selection is in the middle of a word, then return that.
# First loop back from the selected character to find the first part of the word. Keep going until we find a character
# that is either whitespace, or a bracket.
local tWordDivider
put merge("^[\[[quote]]|\[|\]\(|\)|\s]$") into tWordDivider
local tBefore
repeat with x = tTo down to 1
get char x of the text of getScriptField()
if matchText(it, tWordDivider) then
exit repeat
end if
put it before tBefore
end repeat
local tAfter
repeat with x = tTo + 1 to the number of chars of the text of getScriptField()
get char x of the text of getScriptField()
if matchText(it, tWordDivider) then
exit repeat
end if
put it after tAfter
end repeat
local tWord
put tBefore & tAfter into tWord
return tWord
end getLastSelectedWord
# Description
# Returns whether or not undo is available in the current object. I.e. the script has been edited since it was opened in this
# instance of the script editor.
command undoAvailable
if sTextGroupIndex[sObjectId] is 0 then
return false
else
return true
end if
end undoAvailable
# Description
# Returns whether or not redo is available in the current object. I.e. something has been undone since it was opened in
# this instance of the script editor.
command redoAvailable
if sTextGroupIndex[sObjectId] is sTextGroupTop[sObjectId] then
return false
else
return true
end if
end redoAvailable
on textMark pLabel
put pLabel into sTextMark[sObjectId]
end textMark
on textBeginGroup pLabel, pObject
local tObject
if pObject is empty then
put sObjectId into tObject
else
put pObject into tObject
end if
add 1 to sTextGroupIndex[tObject]
put pLabel into sTextGroupLabels[tObject,sTextGroupIndex[tObject]]
put 0 into sTextGroupLengths[tObject,sTextGroupIndex[tObject]]
repeat with tIndex = sTextGroupIndex[tObject] + 1 to sTextGroupTop[tObject]
delete variable sTextGroupLabels[tObject,tIndex]
delete variable sTextGroupLengths[tObject,tIndex]
end repeat
repeat with tIndex = sTextOperationIndex[tObject] + 1 to sTextOperationTop[tObject]
delete variable sTextOperationOffsets[tObject,tIndex]
delete variable sTextOperationNewTexts[tObject,tIndex]
delete variable sTextOperationOldTexts[tObject,tIndex]
end repeat
put sTextOperationIndex[sObjectId] into sTextOperationTop[tObject]
put sTextGroupIndex[sObjectId] into sTextGroupTop[tObject]
put empty into sTextMark[tObject]
end textBeginGroup
on textEndGroup
if sTextGroupLengths[sObjectId,sTextGroupIndex[sObjectId]] = 0 then
throw "empty_text_group_inserted"
end if
textMark "Insert"
end textEndGroup
private function __GetPreference pPreference, pDefault
try
dispatch function "sePrefGet" to stack "revNewScriptEditor" with pPreference
if the result is not empty then
return the result
end if
end try
return pDefault
end __GetPreference
-- Text Formatting Code
command textFormatInitialize
-- permit use in environment with no dependencies
local tTabDepth
put __GetPreference("editor,tabdepth", 3) into tTabDepth
put "0," & tTabDepth into sTextFormatKeywordMap["try"]
put "0," & tTabDepth into sTextFormatKeywordMap["switch"]
put "0," & tTabDepth into sTextFormatKeywordMap["if"]
put -tTabDepth & ",0" into sTextFormatKeywordMap["endif"]
put -tTabDepth & comma & tTabDepth into sTextFormatKeywordMap["elseif"]
put "0," & tTabDepth into sTextFormatKeywordMap["repeat"]
put "0," & tTabDepth into sTextFormatKeywordMap["on"]
put "0," & tTabDepth into sTextFormatKeywordMap["function"]
put "0," & tTabDepth into sTextFormatKeywordMap["setprop"]
put "0," & tTabDepth into sTextFormatKeywordMap["getprop"]
put "0," & tTabDepth into sTextFormatKeywordMap["command"]
put "0," & tTabDepth into sTextFormatKeywordMap["private"]
put "0," & tTabDepth into sTextFormatKeywordMap["before"]
put "0," & tTabDepth into sTextFormatKeywordMap["after"]
put -tTabDepth & comma & tTabDepth into sTextFormatKeywordMap["else"]
put -tTabDepth & comma & tTabDepth into sTextFormatKeywordMap["case"]
put -tTabDepth & comma & tTabDepth into sTextFormatKeywordMap["default"]
put -tTabDepth & comma & tTabDepth into sTextFormatKeywordMap["catch"]
put -tTabDepth & comma & tTabDepth into sTextFormatKeywordMap["finally"]
put -tTabDepth & comma & tTabDepth into sTextFormatKeywordMap["catch"]
--put -tTabDepth & comma & tTabDepth into sTextFormatKeywordMap["break"]
put 0 & comma & 0 into sTextFormatKeywordMap["break"]
put -tTabDepth & ",0" into sTextFormatKeywordMap["end"]
end textFormatInitialize
private function textFormatIndentLineAdds pLine
local tToken
put token 1 of pLine into tToken
if tToken is "if" then
if "else" is among the words of pLine then
return 0
else if token -1 of pLine is not "then" then
return 0
else
return item 2 of sTextFormatKeywordMap["if"]
end if
else if tToken is "else" then
if token 2 of pLine is "if" then
if "else" is among the words of word 3 to -1 of pLine then
return 0
else if token -1 of pLine is not "then" then
return 0
else
return item 2 of sTextFormatKeywordMap["elseif"]
end if
else if token 2 of pLine is not empty then
return 0
else
return item 2 of sTextFormatKeywordMap["else"]
end if
else if token 1 of pLine is empty then
return 0
else if token 1 of pLine is "then" and "else" is among the tokens of pline then
return 0
## Bug 10467 - else in a comment was causing incorrect indentation
## Check for "else" in line without comments
--else if token 1 of pLine is not "else" and "else" is among the words of pLine then
else if token 1 of pLine is not "else" and "else" is among the words of lineStripComments(pLine) then
-- permit use in environment witn no dependencies
local tTabDepth
put __GetPreference("editor,tabdepth", 3) into tTabDepth
return -tTabDepth
else if token -1 of pLine is "then" then
# Either a weirdly formatted if structure, or the condition of the if contained a line continuation character.
# In this case, behave as though pLine is a normal if.
return item 2 of sTextFormatKeywordMap["if"]
else
return item 2 of sTextFormatKeywordMap[tToken]
end if
return 0
end textFormatIndentLineAdds
private function textFormatIndentLineRemoves pPreviousLine, pLine
local tToken
put token 1 of pLine into tToken
if tToken is "else" then
if token 2 of pLine is "if" then
if "then" is among the words of pPreviousLine and token -1 of pPreviousLine is not "then" then
return 0
else
return item 1 of sTextFormatKeywordMap["elseif"]
end if
else
if "then" is among the words of pPreviousLine and token -1 of pPreviousLine is not "then" \
and "else" is among the tokens of pPreviousLine and token 1 of pPreviousLine is "if" and token -1 of pLine is "else" then
return item 1 of sTextFormatKeywordMap["else"]
end if
if "then" is among the words of pPreviousLine and token -1 of pPreviousLine is not "then" \
and token 1 of pPreviousLine is "if" and token -1 of pLine is "else" then
return 0
end if
if "then" is among the words of pPreviousLine and token -1 of pPreviousLine is not "then" then
return 0
else
return item 1 of sTextFormatKeywordMap["else"]
end if
end if
else if tToken is empty then
# Comments and empty lines do not remove any formatting
return 0
else if tToken is "end" and token 2 of pLine is "switch" and token 1 of pPreviousLine is not "switch" then
# Special case for end switch if there is a preceeding "break", "default" or other statement, we need to remove
# double the normal indent.
return (2 * item 1 of sTextFormatKeywordMap["end"])
else if tToken is "case" then
# A case statement removes indent from the previous line in all cases except if the
# previous line was the beginning of the parent switch structure.
if token 1 of pPreviousLine is "switch" then
return 0
else
return item 1 of sTextFormatKeywordMap["case"]
end if
# OK-2009-04-28 : Bug 8016 - Special case required for "default" to make switches format correctly.
else if tToken is "default" then
if token 1 of pPreviousLine is "switch" then
return 0
else
return item 1 of sTextFormatKeywordMap["default"]
end if
else if tToken is "end" and token 2 of pLine is not among the words of "if switch repeat try" and not lineIsContinued(pLine) then
# Handler ends always remove all indentation as they can't be nested
# OK-2009-02-16 : Bug 7707 - We can't assume the previous line was correctly formatted or script may be deleted.
# Instead we simplify this by simply chopping off whatever indentation it did have.
--return min(-(the number of chars of textFormatGetLineIndent(pPreviousLine)), item 1 of sTextFormatKeywordMap[tToken])
return item 1 of sTextFormatKeywordMap["end"]
else
# We can't assume that the previous line was correctly indented
# because the script might have been edited from outside this script editor. Therefore
# we have to ensure that a line doesnt try to remove more formating than the previous
# line actually had. Otherwise non-whitespace chars may be deleted.
if the number of chars of textFormatGetLineIndent(pPreviousLine) + the number of chars of textFormatGetLineIndent(pLine) + textFormatIndentLineAdds(pPreviousLine) < abs(item 1 of sTextFormatKeywordMap[tToken]) then
return -(the number of chars of textFormatGetLineIndent(pPreviousLine))
else
return item 1 of sTextFormatKeywordMap[tToken]
end if
end if
end textFormatIndentLineRemoves
private function textFormatGetLineIndent pLine
local tResult
repeat for each char tChar in pLine
if tChar is space then
put space after tResult
else
return tResult
end if
end repeat
return tResult
end textFormatGetLineIndent
-- all chars after a continuation are treated as a comment
-- this will work fine until someone uses format with multiple
-- \"\" but there's not much getting around that
private function lineIsContinued pLine
put lineStripComments(pLine) into pLine
split pLine by quote
repeat with tIndex = 1 to the number of elements of pLine step 2
if pLine[tIndex] contains "\" then
return true
end if
end repeat
return false
end lineIsContinued
private function textFormatGetContinuationIndent pLastLineNumber
# In order to calculate the indentation we need to loop back until we find the start of the continuation,
# constructing a string which is the equivalent of the continued line in a single line. We then use this to calculate
# the indent of the last line
end textFormatGetContinuationIndent
private function combineContinuedLine pLastLineNumber, @pTextLines
local tContinuation
put lineStripComments(pTextLines[pLastLineNumber]) into tContinuation
local tIndex
repeat with tIndex = (pLastLineNumber - 1) down to 1
if not lineIsContinued( pTextLines[tIndex]) then
exit repeat
end if
put lineStripComments(pTextLines[tIndex]) before tContinuation
end repeat
replace "\" with empty in tContinuation
return tContinuation
end combineContinuedLine
constant kContinuationIndent = 6
private function textFormatLine pLine, pTextLines, @xPreviousLine
local tResult
# OK-2009-01-30 : Bug 7051 - Deal better with continuation characters.
local tCurrentLineIsContinued
local tPreviousLineWasContinued
local tPreviousPreviousLineWasContinued
# Continuations can only happen with consecutive lines, there can't be comments or empty lines in between,
# so we simply use the two previous lines to work out if they are continued or not. If these lines are empty
# or are comments, then the result will be that they are not continuations anyway which is correct.
put lineIsContinued(pTextLines[pLine - 1]) into tPreviousLineWasContinued
put lineIsContinued(pTextLines[pLine - 2]) into tPreviousPreviousLineWasContinued
put lineIsContinued(pTextLines[pLine]) into tCurrentLineIsContinued
# Get the previous non-empty line
if xPreviousLine is 0 and pLine > 1 then
put pLine into xPreviousLine
repeat while xPreviousLine > 1
subtract 1 from xPreviousLine
if token 1 of pTextLines[xPreviousLine] is not empty then
exit repeat
end if
end repeat
end if
local tPreviousLine
local tPreviousLineWasCombined = false
if xPreviousLine > 0 then
# This is the case where we have reached the end of a continued line. Here, we treat the continued line
# as a single entity in order to calculate its indentation properties correctly for the line after it.
if lineIsContinued(pTextLines[xPreviousLine - 1]) then
put true into tPreviousLineWasCombined
put combineContinuedLine(xPreviousLine, pTextLines) into tPreviousLine
else
put pTextLines[xPreviousLine] into tPreviousLine
end if
# Get the current indent of the previous line
local tPreviousLineIndent
put textFormatGetLineIndent(tPreviousLine) into tPreviousLineIndent
end if
# Get the current indent of the line
local tCurrentLineIndent
put textFormatGetLineIndent(pTextLines[pLine]) into tCurrentLineIndent
# Work out how much indentation the current line should remove from the previous line
local tIndentCurrentLineRemoves
if tPreviousLineWasContinued then
put 0 into tIndentCurrentLineRemoves
else
put textFormatIndentLineRemoves(tPreviousLine, pTextLines[pLine]) into tIndentCurrentLineRemoves
end if
# Work out how much indentation the previous line should add to the current line.
local tIndentPreviousLineAdds
if xPreviousLine is 0 then
put 0 into tIndentPreviousLineAdds
else if tPreviousLineWasContinued then
# we always add the continuation indent because we combined the continued lines
put kContinuationIndent into tIndentPreviousLineAdds
else
put textFormatIndentLineAdds(tPreviousLine) into tIndentPreviousLineAdds
end if
local tCurrentIndent
repeat (the number of chars of tPreviousLineIndent + tIndentCurrentLineRemoves + tIndentPreviousLineAdds)
put space after tCurrentIndent
end repeat
put ((the number of chars of tPreviousLineIndent + tIndentCurrentLineRemoves + tIndentPreviousLineAdds) - the number of chars of tCurrentLineIndent) into tResult
-- Finally, calculate the expected next indent.
local tNewIndent
put tCurrentIndent into tNewIndent
local tIndentCurrentLineAdds
if tCurrentLineIsContinued and tPreviousLineWasContinued then
put 0 into tIndentCurrentLineAdds
else if tCurrentLineIsContinued and (not tPreviousLineWasContinued) then
put kContinuationIndent into tIndentCurrentLineAdds
else if (not tCurrentLineIsContinued) and tPreviousLineWasContinued then
put -kContinuationIndent into tIndentCurrentLineAdds
else
put textFormatIndentLineAdds(pTextLines[pLine]) into tIndentCurrentLineAdds
end if
if tPreviousLineWasContinued then
local tCombinedLine
put tPreviousLine && pTextLines[pLine] into tCombinedLine
replace "\" with empty in tCombinedLine
add textFormatIndentLineAdds(tCombinedLine) to tIndentCurrentLineAdds
end if
if tIndentCurrentLineAdds < 0 then
repeat -tIndentCurrentLineAdds times
delete char 1 of tNewIndent
end repeat
else
repeat tIndentCurrentLineAdds times
put space after tNewIndent
end repeat
end if
put comma & tNewIndent after tResult
if token 1 of pTextLines[pLine] is not empty then
put pLine into xPreviousLine
end if
return tResult
end textFormatLine
# Returns
# The chunk of pLine from the beginning of the first word to the end.
private function firstWordToEnd pLine
local tOffset
put offset(word 1 of pLine, pLine) into tOffset
return char tOffset to -1 of pLine
end firstWordToEnd
# Formats the specified script snippet.
command scriptFormatSnippet pScript
textFormatInitialize
local tNewScript
put textFormatSelection(pScript) into tNewScript
return tNewScript
end scriptFormatSnippet
# Parameters
# pText : the text to format
private function textFormatSelection pText
local tResult
local tPreviousLine = 0
local tTextLines
put pText into tTextLines
split tTextLines by return
get textFormatLine(1, tTextLines, tPreviousLine)
put firstWordToEnd(tTextLines[1]) into tTextLines[1]
put tTextLines[1] into tResult
local tIndex
repeat with tIndex = 2 to the number of elements of tTextLines
put item 2 of it & firstWordToEnd(tTextLines[tIndex]) into tTextLines[tIndex]
get textFormatLine(tIndex, tTextLines, tPreviousLine)
if item 1 of it < 0 then
repeat -(item 1 of it) times
if char 1 of line -1 of tTextLines[tIndex] is space then
delete char 1 of tTextLines[tIndex]
end if
end repeat
else if item 1 of it > 0 then
repeat item 1 of it times
put space before tTextLines[tIndex]
end repeat
end if
put return & tTextLines[tIndex] after tResult
end repeat
if line -1 of pText = "" then put cr after tResult
return tResult
end textFormatSelection
private function handlerTypes
return "command,on,function,setprop,getprop,before,after"
end handlerTypes
private function textFormat pLineNumber, pLineCount, pText
-- Find the range to format.
-- 1. Search down until we find a handler (a) or the end of one (b).
-- 2. Based on 1, in case a, search up until we find the end of any other handler, or the beginning of the script.
-- In case b we need to search for the start of that handler.
local tEnd
local tText
put pText into tText
split tText by return
put pLineNumber into tEnd
# Search down to find what the handler name is and whether pLineNumber is the first line of the handler.
local tHandler
local tFirstLine
put true into tFirstLine
local tContinuation, tCurrentLineContinues
put lineIsContinued(tText[pLineNumber-1]) into tContinuation
local tLineIndex
repeat with tLineIndex = pLineNumber to the number of elements of tText
put lineIsContinued(tText[tLineIndex]) into tCurrentLineContinues
if token 1 of tText[tLineIndex] is "end" and token 2 of tText[tLineIndex] is not among the items of "if,repeat,switch,try" and not tContinuation and not tCurrentLineContinues then
put true into tHandler[token 2 of tText[tLineIndex]]
if pLineCount < 0 then
exit repeat
end if
else if token 1 of tText[tLineIndex] is among the items of handlerTypes() and not tFirstLine then
if pLineCount < 0 then
exit repeat
end if
exit repeat
else if token 1 of tText[tLineIndex] is "private" and token 2 of tText[tLineIndex] is among the items of handlerTypes() and not tFirstLine then
if pLineCount < 0 then
exit repeat
end if
exit repeat
else
put tCurrentLineContinues into tContinuation
end if
if tFirstLine then put false into tFirstLine
subtract 1 from pLineCount
add 1 to tEnd
end repeat
# Search up to find the beginning of the handler, or the beginning of the script if not found.
local tStart
repeat with tLineIndex = pLineNumber down to 1
put tLineIndex into tStart
if tHandler is an array then
if token 1 of tText[tLineIndex] is among the items of handlerTypes() and tHandler[token 2 of tText[tLineIndex]] then
exit repeat
else if token 1 of tText[tLineIndex] is "private" and token 2 of tText[tLineIndex] is among the items of handlerTypes() and tHandler[token 3 of tText[tLineIndex]] then
exit repeat
end if
else if token 1 of tText[tLineIndex] is "end" and token 2 of tText[tLineIndex] is not among the items of "if,repeat,switch,catch" then
exit repeat
end if
end repeat
if tHandler is not an array and tStart is not 1 then
add 1 to tStart
end if
if tEnd > the number of elements of tText then
put the number of elements of tText into tEnd
end if
-- Now format lines tStart to tEnd of pText, and return the result.
local tStartChar
if tStart is 1 then
put 1 into tStartChar
else
put the number of chars of line 1 to (tStart - 1) of pText into tStartChar
add 2 to tStartChar
end if
local tOldText
put line tStart to tEnd of pText into tOldText
local tResult
put tStartChar into tResult["startchar"]
put tOldText into tResult["oldtext"]
put textFormatSelection(tOldText) into tResult["newtext"]
return tResult
end textFormat
function textGetScript
return the text of field "Script" of me
end textGetScript
function getScriptField
return the long id of field "Script" of me
end getScriptField
on textSetSelection pAfterChar
select after char pAfterChar of field "Script" of me
end textSetSelection
# Parameters
# pOldText : the text replaced in an operation
# pNewText : the replacing text in an operation
# Returns
# Whether or not a new undo group is needed for the specified
# operation.
private function textReplaceNewGroupNeeded pOldText, pNewText
# If the invoker of the replacement operation has specified that an undo group
# is needed, always create one
if sTextMark[sObjectId] is not empty then
return true
end if
# If the number of chars of either the string replaced or replacing string is above one
# this means it must be a deletion of a selection or a cut or paste. This should always
# require a new group
if the number of chars of pOldText > 1 or the number of chars of pNewText > 1 then
return true
end if
# If either the text being replaced or the replacing text contain return chars, new group
if pOldText contains return or pNewText contains return then
return true
end if
return false
end textReplaceNewGroupNeeded
# Parameters
# pOffset : the char number of where the replacement should start
# pOldText : the text that is being replaced
# pNewText : the text that is being inserted
# pObject : the long id of the object to replace in. If not given, the current object is assumed.
# Description
# This is the point through which all standard editing operations on scripts are sent through.
# Any change made via this function will be added to the undo system. This is called when the
# user types keys, formats text, cuts, pastes etc.
command textReplace pOffset, pOldText, pNewText, pObject, pDontGroup, pDontSelect
lock screen
local tObject
if pObject is empty then
put sObjectId into tObject
else
put pObject into tObject
end if
local tBracketCompletionType
__BracketCompletion pOffset, pOldText, pNewText
put it into tBracketCompletionType
local tSelection
if tBracketCompletionType is "wrap" then
put the selectedChunk into tSelection
add 1 to word 2 of tSelection
add 1 to word 4 of tSelection
end if
if pDontGroup is not true and textReplaceNewGroupNeeded(pOldText, pNewText) then
textBeginGroup sTextMark[tObject], tObject
end if
add 1 to sTextOperationIndex[tObject]
add 1 to sTextGroupLengths[tObject,sTextGroupIndex[tObject]]
local tEditPlaceholder
put sEditChunks is an array and sEditPlaceholder is not empty and \
pOffset >= sEditChunks[1]["start"] and \
pOffset + length(pOldText) - 1 <= sEditChunks[1]["end"] and \
return is not in pNewText into tEditPlaceholder
if tEditPlaceholder and \
sPlaceholders[sEditPlaceholder]["classes"][1] is "identifier" then
put comma is not in pNewText and \
";" is not in pNewText and \
space is not in pNewText and \
quote is not in pNewText into tEditPlaceholder
end if
if not tEditPlaceholder then
put pOffset into sTextOperationOffsets[tObject,sTextOperationIndex[tObject]]
put pOldText into sTextOperationOldTexts[tObject,sTextOperationIndex[tObject]]
put pNewText into sTextOperationNewTexts[tObject,sTextOperationIndex[tObject]]
if sEditPlaceholder is not empty then
__ClearCurrentPlaceholder true
end if
end if
# When the script has been edited, the breakpoints should be suspended until it is applied again.
# Because this command is called as the user types, we could a send in time for this, as its not that urgent.
try
revDebuggerSuspendBreakpoints tObject
end try
# If the specified object to replace in is the current object being edited, we perform the replace
# directly on the script editing field using textReplaceRaw. Otherwise we perform it on the
# script cache stored for the object.
if tObject is empty or revRuggedId(tObject) is sObjectId then
local tSelectedLength, tNewLength
put length(pOldText) into tSelectedLength
put length(pNewText) into tNewLength
if tEditPlaceholder then
local tFirstCharOffset, tLastCharOffset
put pOffset-sEditChunks[1]["start"] into tFirstCharOffset
put sEditChunks[1]["end"]-pOffset + tSelectedLength into tLastCharOffset
local tToAdd = 0
local tIndex
repeat with tIndex = 1 to the number of elements of sEditChunks
add tToAdd to sEditChunks[tIndex]["start"]
add tToAdd to sEditChunks[tIndex]["end"]
textReplaceRaw sEditChunks[tIndex]["start"]+tFirstCharOffset, pOldText, pNewText
add tNewLength - tSelectedLength to tToAdd
add tNewLength - tSelectedLength to sEditChunks[tIndex]["end"]
set the metadata of char sEditChunks[tIndex]["start"] to sEditChunks[tIndex]["end"] of field "script" of me to sEditPlaceholder
if tIndex is 1 then
set the backgroundColor of char sEditChunks[tIndex]["start"] to sEditChunks[tIndex]["end"] of field "script" of me to empty
else
set the backgroundColor of char sEditChunks[tIndex]["start"] to sEditChunks[tIndex]["end"] of field "script" of me to \
the effective hiliteColor of field "script" of me
end if
put sEditChunks[tIndex]["start"]+tFirstCharOffset into sTextOperationOffsets[tObject,sTextOperationIndex[tObject]]
put pOldText into sTextOperationOldTexts[tObject,sTextOperationIndex[tObject]]
put pNewText into sTextOperationNewTexts[tObject,sTextOperationIndex[tObject]]
add 1 to sTextOperationIndex[tObject]
add 1 to sTextGroupLengths[tObject,sTextGroupIndex[tObject]]
end repeat
put true into sPlaceholders[sEditPlaceholder]["edited"]
if not pDontSelect then
if tSelection is not empty then
select tSelection
else if pNewText is empty then
select char pOffset to pOffset-1 of field "script" of me
else if the length of pNewText is 1 then
select char pOffset+1 to pOffset of field "script" of me
else if tBracketCompletionType is "pair" then
select after char pOffset of field "script" of me
end if
end if
else
-- clear highlighted bracket background color
__ClearHighlights
textReplaceRaw pOffset, pOldText, pNewText
if not pDontSelect then
if tSelection is not empty then
select tSelection
else if tBracketCompletionType is "pair" then
select after char pOffset of field "script" of me
end if
end if
end if
if not pDontSelect then
selectionUpdateRequest
end if
-- when formatting or pasting we don't want autocomplete to pop up
if the number of lines of pNewText <= 1 then
__UpdateAutoCompleteList sEditPlaceholder
end if
else
local tCache
put sScriptCache[revRuggedId(tObject)] into tCache
if tCache is empty then
put the script of tObject into tCache
end if
textReplaceRawInVariable tCache, pOffset, pOldText, pNewText
put tCache into sScriptCache[revRuggedId(tObject)]
end if
set the caseSensitive to true
if pOldText is not pNewText then
# OK-2009-01-17 : Bug 7169 - Flag the object as dirty when it is modifed by this method only.
# The textReplaceRaw etc functions should not do this as they do not represent a user modifying
# the script.
setDirty tObject, true
end if
unlock screen
end textReplace
# Parameters
# pOffset : the char number to begin the replacement at
# @rScript : the text to perform the replacement in, gets mutated