-
Notifications
You must be signed in to change notification settings - Fork 1
/
highlighter.lua
271 lines (226 loc) · 7.93 KB
/
highlighter.lua
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
local types = loadstring(game:HttpGet("https://raw.githubusercontent.com/hello-n-bye/internal-executor-project/main/types.lua"))()
local utility = loadstring(game:HttpGet("https://raw.githubusercontent.com/hello-n-bye/internal-executor-project/main/utility.lua"))()
local theme = loadstring(game:HttpGet("https://raw.githubusercontent.com/hello-n-bye/internal-executor-project/main/theme.lua"))()
local Highlighter = {
defaultLexer = loadstring(game:HttpGet("https://raw.githubusercontent.com/hello-n-bye/internal-executor-project/main/lexer.lua"))() :: types.Lexer,
_textObjectData = {} :: { [types.TextObject]: types.ObjectData },
_cleanups = {} :: { [types.TextObject]: () -> () },
}
function Highlighter._getLabelingInfo(textObject: types.TextObject)
local data = Highlighter._textObjectData[textObject]
if not data then
return
end
local src = utility.convertTabsToSpaces(utility.removeControlChars(textObject.Text))
local numLines = #string.split(src, "\n")
if numLines == 0 then
return
end
local textBounds = utility.getTextBounds(textObject)
local textHeight = textBounds.Y / numLines
return {
data = data,
numLines = numLines,
textBounds = textBounds,
textHeight = textHeight,
innerAbsoluteSize = utility.getInnerAbsoluteSize(textObject),
textColor = theme.getColor("iden"),
textFont = textObject.FontFace,
textSize = textObject.TextSize,
labelSize = UDim2.new(1, 0, 0, math.ceil(textHeight)),
}
end
function Highlighter._alignLabels(textObject: types.TextObject)
local labelingInfo = Highlighter._getLabelingInfo(textObject)
if not labelingInfo then
return
end
for lineNumber, lineLabel in labelingInfo.data.Labels do
-- Align line label
lineLabel.TextColor3 = labelingInfo.textColor
lineLabel.FontFace = labelingInfo.textFont
lineLabel.TextSize = labelingInfo.textSize
lineLabel.Size = labelingInfo.labelSize
lineLabel.Position =
UDim2.fromScale(0, labelingInfo.textHeight * (lineNumber - 1) / labelingInfo.innerAbsoluteSize.Y)
end
end
function Highlighter._populateLabels(props: types.HighlightProps)
local textObject = props.textObject
local src = utility.convertTabsToSpaces(utility.removeControlChars(props.src or textObject.Text))
local lexer = props.lexer or Highlighter.defaultLexer
local customLang = props.customLang
local forceUpdate = props.forceUpdate
local data = Highlighter._textObjectData[textObject]
if (data == nil) or (data.Text == src) then
if forceUpdate ~= true then
return
end
end
textObject.Text = src
local lineLabels = data.Labels
local previousLines = data.Lines
local lines = string.split(src, "\n")
data.Lines = lines
data.Text = src
data.Lexer = lexer
data.CustomLang = customLang
if src == "" then
for l = 1, #lineLabels do
if lineLabels[l].Text == "" then
continue
end
lineLabels[l].Text = ""
end
return
end
local idenColor = theme.getColor("iden")
local labelingInfo = Highlighter._getLabelingInfo(textObject)
local richTextBuffer, bufferIndex, lineNumber = table.create(5), 0, 1
for token: types.TokenName, content: string in lexer.scan(src) do
local Color = if customLang and customLang[content]
then theme.getColor("custom")
else theme.getColor(token) or idenColor
local tokenLines = string.split(utility.sanitizeRichText(content), "\n")
for l, tokenLine in tokenLines do
local lineLabel = lineLabels[lineNumber]
if not lineLabel then
local newLabel = Instance.new("TextLabel")
newLabel.Name = "Line_" .. lineNumber
newLabel.AutoLocalize = false
newLabel.RichText = true
newLabel.BackgroundTransparency = 1
newLabel.Text = ""
newLabel.TextXAlignment = Enum.TextXAlignment.Left
newLabel.TextYAlignment = Enum.TextYAlignment.Top
newLabel.TextColor3 = labelingInfo.textColor
newLabel.FontFace = labelingInfo.textFont
newLabel.TextSize = labelingInfo.textSize
newLabel.Size = labelingInfo.labelSize
newLabel.Position =
UDim2.fromScale(0, labelingInfo.textHeight * (lineNumber - 1) / labelingInfo.innerAbsoluteSize.Y)
newLabel.Parent = textObject.SyntaxHighlights
lineLabels[lineNumber] = newLabel
lineLabel = newLabel
end
if l > 1 then
if forceUpdate or lines[lineNumber] ~= previousLines[lineNumber] then
lineLabels[lineNumber].Text = table.concat(richTextBuffer)
end
lineNumber += 1
bufferIndex = 0
table.clear(richTextBuffer)
end
if forceUpdate or lines[lineNumber] ~= previousLines[lineNumber] then
bufferIndex += 1
if Color ~= idenColor and string.find(tokenLine, "[%S%C]") then
richTextBuffer[bufferIndex] = theme.getColoredRichText(Color, tokenLine)
else
richTextBuffer[bufferIndex] = tokenLine
end
end
end
end
if richTextBuffer[1] and lineLabels[lineNumber] then
lineLabels[lineNumber].Text = table.concat(richTextBuffer)
end
for l = lineNumber + 1, #lineLabels do
if lineLabels[l].Text == "" then
continue
end
lineLabels[l].Text = ""
end
end
function Highlighter.highlight(props: types.HighlightProps): () -> ()
local textObject = props.textObject
local src = utility.convertTabsToSpaces(utility.removeControlChars(props.src or textObject.Text))
local lexer = props.lexer or Highlighter.defaultLexer
local customLang = props.customLang
if Highlighter._cleanups[textObject] then
Highlighter._populateLabels(props)
Highlighter._alignLabels(textObject)
return Highlighter._cleanups[textObject]
end
textObject.RichText = false
textObject.Text = src
textObject.TextXAlignment = Enum.TextXAlignment.Left
textObject.TextYAlignment = Enum.TextYAlignment.Top
textObject.BackgroundColor3 = theme.getColor("background")
textObject.TextColor3 = theme.getColor("iden")
textObject.TextTransparency = 0.5
local lineFolder = textObject:FindFirstChild("SyntaxHighlights")
if lineFolder == nil then
local newLineFolder = Instance.new("Folder")
newLineFolder.Name = "SyntaxHighlights"
newLineFolder.Parent = textObject
lineFolder = newLineFolder
end
local data = {
Text = "",
Labels = {},
Lines = {},
Lexer = lexer,
CustomLang = customLang,
}
Highlighter._textObjectData[textObject] = data
local connections: { [string]: RBXScriptConnection } = {}
local function cleanup()
lineFolder:Destroy()
Highlighter._textObjectData[textObject] = nil
Highlighter._cleanups[textObject] = nil
for _key, connection in connections do
connection:Disconnect()
end
table.clear(connections)
end
Highlighter._cleanups[textObject] = cleanup
connections["AncestryChanged"] = textObject.AncestryChanged:Connect(function()
if textObject.Parent then
return
end
cleanup()
end)
connections["TextChanged"] = textObject:GetPropertyChangedSignal("Text"):Connect(function()
Highlighter._populateLabels(props)
end)
connections["TextBoundsChanged"] = textObject:GetPropertyChangedSignal("TextBounds"):Connect(function()
Highlighter._alignLabels(textObject)
end)
connections["AbsoluteSizeChanged"] = textObject:GetPropertyChangedSignal("AbsoluteSize"):Connect(function()
Highlighter._alignLabels(textObject)
end)
connections["FontFaceChanged"] = textObject:GetPropertyChangedSignal("FontFace"):Connect(function()
Highlighter._alignLabels(textObject)
end)
Highlighter._populateLabels(props)
Highlighter._alignLabels(textObject)
return cleanup
end
function Highlighter.refresh(): ()
for textObject, data in Highlighter._textObjectData do
for _, lineLabel in data.Labels do
lineLabel.TextColor3 = theme.getColor("iden")
end
Highlighter.highlight({
textObject = textObject,
forceUpdate = true,
src = data.Text,
lexer = data.Lexer,
customLang = data.CustomLang,
})
end
end
function Highlighter.setTokenColors(colors: types.TokenColors): ()
theme.setColors(colors)
Highlighter.refresh()
end
function Highlighter.getTokenColor(tokenName: types.TokenName): Color3
return theme.getColor(tokenName)
end
function Highlighter.matchStudioSettings(): ()
local applied = theme.matchStudioSettings(Highlighter.refresh)
if applied then
Highlighter.refresh()
end
end
return Highlighter