Advertisement
Msizz

SimpleSpy [v3][Skire's Version][Codebox]

Jan 18th, 2024
490
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 18.99 KB | None | 0 0
  1. --[[
  2. higlight.lua v0.0.1 by exxtremewa#9394
  3.  
  4. Features:
  5. - uses the power of fancy syntax detection algorithms to convert a frame into a syntax highlighted high quality code box
  6. - is cool
  7. ]]
  8.  
  9. local TextService = game:GetService("TextService")
  10. local RunService = game:GetService("RunService")
  11. --- The Highlight class
  12. --- @class Highlight
  13. local Highlight = {}
  14.  
  15. -- PRIVATE METHODS/PROPERTIES --
  16.  
  17. --[[
  18. Char Object:
  19. {
  20. Char: string -- A single character
  21. Color: Color3 -- The intended color of the char
  22. Line: number -- The line number
  23. }
  24. ]]
  25.  
  26. local parentFrame
  27. local scrollingFrame
  28. local textFrame
  29. local lineNumbersFrame
  30. local lines = {}
  31.  
  32. --- Contents of the table- array of char objects
  33. local tableContents = {}
  34.  
  35. local line = 0
  36. local largestX = 0
  37.  
  38. local lineSpace = 15
  39. local font = Enum.Font.Ubuntu
  40. local textSize = 14
  41.  
  42. local backgroundColor = Color3.fromRGB(18, 18, 18)
  43. local operatorColor = getgenv().operatorColor
  44. local functionColor = getgenv().functionColor
  45. local stringColor = getgenv().stringColor
  46. local numberColor = getgenv().numberColor
  47. local booleanColor = getgenv().booleanColor
  48. local objectColor = getgenv().objectColor
  49. local defaultColor = getgenv().defaultColor
  50. local commentColor = getgenv().commentColor
  51. local lineNumberColor = getgenv().lineNumberColor
  52. local genericColor = getgenv().genericColor
  53.  
  54. local operators = {"^(function)[^%w_]", "^(local)[^%w_]", "^(if)[^%w_]", "^(for)[^%w_]", "^(while)[^%w_]", "^(then)[^%w_]", "^(do)[^%w_]", "^(else)[^%w_]", "^(elseif)[^%w_]", "^(return)[^%w_]", "^(end)[^%w_]", "^(continue)[^%w_]", "^(and)[^%w_]", "^(not)[^%w_]", "^(or)[^%w_]", "[^%w_](or)[^%w_]", "[^%w_](and)[^%w_]", "[^%w_](not)[^%w_]", "[^%w_](continue)[^%w_]", "[^%w_](function)[^%w_]", "[^%w_](local)[^%w_]", "[^%w_](if)[^%w_]", "[^%w_](for)[^%w_]", "[^%w_](while)[^%w_]", "[^%w_](then)[^%w_]", "[^%w_](do)[^%w_]", "[^%w_](else)[^%w_]", "[^%w_](elseif)[^%w_]", "[^%w_](return)[^%w_]", "[^%w_](end)[^%w_]"}
  55. --- In this case, patterns could not be used, so just the string characters are provided
  56. local strings = {{"\"", "\""}, {"'", "'"}, {"%[%[", "%]%]", true}}
  57. local comments = {"%-%-%[%[[^%]%]]+%]?%]?", "(%-%-[^\n]+)"}
  58. local functions = {"[^%w_]([%a_][%a%d_]*)%s*%(", "^([%a_][%a%d_]*)%s*%(", "[:%.%(%[%p]([%a_][%a%d_]*)%s*%("}
  59. local numbers = {"[^%w_](%d+[eE]?%d*)", "[^%w_](%.%d+[eE]?%d*)", "[^%w_](%d+%.%d+[eE]?%d*)", "^(%d+[eE]?%d*)", "^(%.%d+[eE]?%d*)", "^(%d+%.%d+[eE]?%d*)"}
  60. local booleans = {"[^%w_](true)", "^(true)", "[^%w_](false)", "^(false)", "[^%w_](nil)", "^(nil)"}
  61. local objects = {"[^%w_:]([%a_][%a%d_]*):", "^([%a_][%a%d_]*):"}
  62. local other = {"[^_%s%w=>~<%-%+%*]", ">", "~", "<", "%-", "%+", "=", "%*"}
  63. local offLimits = {}
  64.  
  65. --- Determines if index is in a string
  66. function isOffLimits(index)
  67. for _, v in pairs(offLimits) do
  68. if index >= v[1] and index <= v[2] then
  69. return true
  70. end
  71. end
  72. return false
  73. end
  74.  
  75. --- Find iterator
  76. -- function gfind(str, pattern)
  77. -- local start = 0
  78. -- return function()
  79. -- local findStart, findEnd = str:find(pattern, start)
  80. -- if findStart and findEnd ~= #str then
  81. -- start = findEnd + 1
  82. -- return findStart, findEnd
  83. -- else
  84. -- return nil
  85. -- end
  86. -- end
  87. -- end
  88.  
  89. --- Find iterator
  90. function gfind(str, pattern)
  91. return coroutine.wrap(function()
  92. local start = 0
  93. while true do
  94. local findStart, findEnd = str:find(pattern, start)
  95. if findStart and findEnd ~= #str then
  96. start = findEnd + 1
  97. coroutine.yield(findStart, findEnd)
  98. else
  99. return
  100. end
  101. end
  102. end)
  103. end
  104.  
  105. --- Finds and highlights comments with `commentColor`
  106. function renderComments()
  107. local str = Highlight:getRaw()
  108. local step = 1
  109. for _, pattern in pairs(comments) do
  110. for commentStart, commentEnd in gfind(str, pattern) do
  111. if step % 1000 == 0 then
  112. RunService.Heartbeat:Wait()
  113. end
  114. step += 1
  115. if not isOffLimits(commentStart) then
  116. for i = commentStart, commentEnd do
  117. table.insert(offLimits, {commentStart, commentEnd})
  118. if tableContents[i] then
  119. tableContents[i].Color = commentColor
  120. end
  121. end
  122. end
  123. end
  124. end
  125. end
  126.  
  127. -- Finds and highlights strings with `stringColor`
  128. function renderStrings()
  129. local stringType
  130. local stringEndType
  131. local ignoreBackslashes
  132. local stringStart
  133. local stringEnd
  134. local offLimitsIndex
  135. local skip = false
  136. for i, char in pairs(tableContents) do
  137. if stringType then
  138. char.Color = stringColor
  139. local possibleString = ""
  140. for k = stringStart, i do
  141. possibleString = possibleString .. tableContents[k].Char
  142. end
  143. if char.Char:match(stringEndType) and not not ignoreBackslashes or (possibleString:match("(\\*)" .. stringEndType .. "$") and #possibleString:match("(\\*)" .. stringEndType .. "$") % 2 == 0) then
  144. skip = true
  145. stringType = nil
  146. stringEndType = nil
  147. ignoreBackslashes = nil
  148. stringEnd = i
  149. offLimits[offLimitsIndex][2] = stringEnd
  150. end
  151. end
  152. if not skip then
  153. for _, v in pairs(strings) do
  154. if char.Char:match(v[1]) and not isOffLimits(i) then
  155. stringType = v[1]
  156. stringEndType = v[2]
  157. ignoreBackslashes = v[3]
  158. char.Color = stringColor
  159. stringStart = i
  160. offLimitsIndex = #offLimits + 1
  161. offLimits[offLimitsIndex] = {stringStart, math.huge}
  162. end
  163. end
  164. end
  165. skip = false
  166. end
  167. end
  168.  
  169. --- Highlights the specified patterns with the specified color
  170. --- @param patternArray string[]
  171. ---@param color userdata
  172. function highlightPattern(patternArray, color)
  173. local str = Highlight:getRaw()
  174. local step = 1
  175. for _, pattern in pairs(patternArray) do
  176. for findStart, findEnd in gfind(str, pattern) do
  177. if step % 1000 == 0 then
  178. RunService.Heartbeat:Wait()
  179. end
  180. step += 1
  181. if not isOffLimits(findStart) and not isOffLimits(findEnd) then
  182. for i = findStart, findEnd do
  183. if tableContents[i] then
  184. tableContents[i].Color = color
  185. end
  186. end
  187. end
  188. end
  189. end
  190. end
  191.  
  192. --- Automatically replaces reserved chars with escape chars
  193. --- @param s string
  194. function autoEscape(s)
  195. for i = 0, #s do
  196. local char = string.sub(s, i, i)
  197. if char == "<" then
  198. s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&lt;", string.sub(s, i + 1, -1))
  199. i += 3
  200. elseif char == ">" then
  201. s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&gt;", string.sub(s, i + 1, -1))
  202. i += 3
  203. elseif char == '"' then
  204. s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&quot;", string.sub(s, i + 1, -1))
  205. i += 5
  206. elseif char == "'" then
  207. s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&apos;", string.sub(s, i + 1, -1))
  208. i += 5
  209. elseif char == "&" then
  210. s = string.format("%s%s%s", string.sub(s, 0, i - 1), "&amp;", string.sub(s, i + 1, -1))
  211. i += 4
  212. end
  213. end
  214. -- s:gsub("<", "&lt;")
  215. -- s:gsub(">", "&gt;")
  216. -- s:gsub('"', "&quot;")
  217. -- s:gsub("'", "&apos;")
  218. -- s:gsub("&", "&amp;")
  219. return s
  220. end
  221.  
  222. --- Main function for syntax highlighting tableContents
  223. function render()
  224. offLimits = {}
  225. lines = {}
  226. textFrame:ClearAllChildren()
  227. lineNumbersFrame:ClearAllChildren()
  228.  
  229. highlightPattern(functions, functionColor)
  230. highlightPattern(numbers, numberColor)
  231. highlightPattern(operators, operatorColor)
  232. highlightPattern(objects, objectColor)
  233. highlightPattern(booleans, booleanColor)
  234. highlightPattern(other, genericColor)
  235. renderComments()
  236. renderStrings()
  237.  
  238. local lastColor
  239. local lineStr = ""
  240. local rawStr = ""
  241. largestX = 0
  242. line = 1
  243.  
  244. for i = 1, #tableContents + 1 do
  245. local char = tableContents[i]
  246. if i == #tableContents + 1 or char.Char == "\n" then
  247. lineStr = lineStr .. (lastColor and "</font>" or "")
  248.  
  249. local lineText = Instance.new("TextLabel")
  250. local x = TextService:GetTextSize(rawStr, textSize, font, Vector2.new(math.huge, math.huge)).X + 60
  251.  
  252. if x > largestX then
  253. largestX = x
  254. end
  255.  
  256. lineText.TextXAlignment = Enum.TextXAlignment.Left
  257. lineText.TextYAlignment = Enum.TextYAlignment.Top
  258. lineText.Position = UDim2.new(0, 0, 0, line * lineSpace - lineSpace / 2)
  259. lineText.Size = UDim2.new(0, x, 0, textSize)
  260. lineText.RichText = true
  261. lineText.Font = font
  262. lineText.TextSize = textSize
  263. lineText.BackgroundTransparency = 1
  264. lineText.Text = lineStr
  265. lineText.Parent = textFrame
  266.  
  267. if i ~= #tableContents + 1 then
  268. local lineNumber = Instance.new("TextLabel")
  269. lineNumber.Text = line
  270. lineNumber.Font = font
  271. lineNumber.TextSize = textSize
  272. lineNumber.Size = UDim2.new(1, 0, 0, lineSpace)
  273. lineNumber.TextXAlignment = Enum.TextXAlignment.Right
  274. lineNumber.TextColor3 = lineNumberColor
  275. lineNumber.Position = UDim2.new(0, 0, 0, line * lineSpace - lineSpace / 2)
  276. lineNumber.BackgroundTransparency = 1
  277. lineNumber.Parent = lineNumbersFrame
  278. end
  279.  
  280. lineStr = ""
  281. rawStr = ""
  282. lastColor = nil
  283. line += 1
  284. updateZIndex()
  285. updateCanvasSize()
  286. if line % 5 == 0 then
  287. RunService.Heartbeat:Wait()
  288. end
  289. elseif char.Char == " " then
  290. lineStr = lineStr .. char.Char
  291. rawStr = rawStr .. char.Char
  292. elseif char.Char == "\t" then
  293. lineStr = lineStr .. string.rep(" ", 4)
  294. rawStr = rawStr .. char.Char
  295. else
  296. if char.Color == lastColor then
  297. lineStr = lineStr .. autoEscape(char.Char)
  298. else
  299. lineStr = lineStr .. string.format('%s<font color="rgb(%d,%d,%d)">', lastColor and "</font>" or "", char.Color.R * 255, char.Color.G * 255, char.Color.B * 255)
  300. lineStr = lineStr .. autoEscape(char.Char)
  301. lastColor = char.Color
  302. end
  303. rawStr = rawStr .. char.Char
  304. end
  305.  
  306. -- local v = tableContents[i]
  307. -- if not lines[v.Line] or #lines[v.Line] <= 200 then
  308. -- local textBox = Instance.new("TextLabel")
  309. -- local size = TextService:GetTextSize(v.Char, 14, Enum.Font.Arial, Vector2.new(math.huge, math.huge))
  310. -- local lineSizeX = 0
  311. -- if not lines[v.Line] then
  312. -- lines[v.Line] = {}
  313. -- end
  314. -- if v.Char == "\n" then
  315. -- textBox.Text = ""
  316. -- game:GetService("RunService").Heartbeat:Wait()
  317. -- elseif v.Char:match("\t") then
  318. -- v.Char = "\t____"
  319. -- textBox.Text = "\t____"
  320. -- textBox.TextTransparency = 1
  321. -- elseif v.Char:match(" ") then
  322. -- v.Char = " |"
  323. -- textBox.Text = " -"
  324. -- textBox.TextTransparency = 1
  325. -- else
  326. -- textBox.Text = v.Char
  327. -- end
  328. -- if not lines[v.Line] then
  329. -- lines[v.Line] = {}
  330. -- end
  331. -- for _, c in pairs(lines[v.Line]) do
  332. -- lineSizeX = lineSizeX + TextService:GetTextSize(c.Char, 14, Enum.Font.Arial, Vector2.new(math.huge, math.huge)).X
  333. -- end
  334. -- textBox.TextColor3 = v.Color
  335. -- textBox.Size = UDim2.new(0, size.X, 0, size.Y)
  336. -- textBox.TextXAlignment = Enum.TextXAlignment.Left
  337. -- textBox.TextYAlignment = Enum.TextYAlignment.Top
  338. -- textBox.Position = UDim2.new(0, lineSizeX, 0, v.Line * lineSpace - lineSpace / 2)
  339. -- textBox.BackgroundTransparency = 1
  340. -- v.TextBox = textBox
  341. -- table.insert(lines[v.Line], v)
  342. -- textBox.Parent = textFrame
  343. -- end
  344. end
  345. updateZIndex()
  346. updateCanvasSize()
  347. end
  348.  
  349. function onFrameSizeChange()
  350. local newSize = parentFrame.AbsoluteSize
  351. scrollingFrame.Size = UDim2.new(0, newSize.X, 0, newSize.Y)
  352. end
  353.  
  354. function updateCanvasSize()
  355. -- local codeSize = Vector2.new(TextService:GetTextSize(Highlight:getRaw(), textSize, font, Vector2.new(math.huge, math.huge)).X + 60, #lines * lineSpace + 60)
  356. scrollingFrame.CanvasSize = UDim2.new(0, largestX, 0, line * lineSpace)
  357. end
  358.  
  359. function updateZIndex()
  360. for _, v in pairs(parentFrame:GetDescendants()) do
  361. if v:IsA("GuiObject") then
  362. v.ZIndex = parentFrame.ZIndex
  363. end
  364. end
  365. end
  366.  
  367. -- PUBLIC METHODS --
  368.  
  369. --- Runs when a new object is instantiated
  370. --- @param frame userdata
  371. function Highlight:init(frame)
  372. if typeof(frame) == "Instance" and frame:IsA("Frame") then
  373. frame:ClearAllChildren()
  374.  
  375. parentFrame = frame
  376. scrollingFrame = Instance.new("ScrollingFrame")
  377. textFrame = Instance.new("Frame")
  378. lineNumbersFrame = Instance.new("Frame")
  379.  
  380. local parentSize = frame.AbsoluteSize
  381. scrollingFrame.Name = "HIGHLIGHT_IDE"
  382. scrollingFrame.Size = UDim2.new(0, parentSize.X, 0, parentSize.Y)
  383. scrollingFrame.BackgroundColor3 = backgroundColor
  384. scrollingFrame.BorderSizePixel = 0
  385. scrollingFrame.ScrollBarThickness = 4
  386.  
  387. textFrame.Name = ""
  388. textFrame.Size = UDim2.new(1, -40, 1, 0)
  389. textFrame.Position = UDim2.new(0, 40, 0, 0)
  390. textFrame.BackgroundTransparency = 1
  391.  
  392. lineNumbersFrame.Name = ""
  393. lineNumbersFrame.Size = UDim2.new(0, 25, 1, 0)
  394. lineNumbersFrame.BackgroundTransparency = 1
  395.  
  396. textFrame.Parent = scrollingFrame
  397. lineNumbersFrame.Parent = scrollingFrame
  398. scrollingFrame.Parent = parentFrame
  399.  
  400. render()
  401. parentFrame:GetPropertyChangedSignal("AbsoluteSize"):Connect(onFrameSizeChange)
  402. parentFrame:GetPropertyChangedSignal("ZIndex"):Connect(updateZIndex)
  403. else
  404. error("Initialization error: argument " .. typeof(frame) .. " is not a Frame Instance")
  405. end
  406. end
  407.  
  408. --- Sets the raw text of the code box (\n = new line, \t converted to spaces)
  409. --- @param raw string
  410. function Highlight:setRaw(raw)
  411. raw = raw .. "\n"
  412. tableContents = {}
  413. local line = 1
  414. for i = 1, #raw do
  415. table.insert(tableContents, {
  416. Char = raw:sub(i, i),
  417. Color = defaultColor,
  418. -- Line = line
  419. })
  420. if i % 1000 == 0 then
  421. RunService.Heartbeat:Wait()
  422. end
  423. -- if raw:sub(i, i) == "\n" then
  424. -- line = line + 1
  425. -- end
  426. end
  427. render()
  428. end
  429.  
  430. --- Returns the (string) raw text of the code box (\n = new line). This includes placeholder characters so it should only be used internally.
  431. --- @return string
  432. function Highlight:getRaw()
  433. local result = ""
  434. for _, char in pairs(tableContents) do
  435. result = result .. char.Char
  436. end
  437. return result
  438. end
  439.  
  440. --- Returns the (string) text of the code box (\n = new line)
  441. --- @return string
  442. function Highlight:getString()
  443. local result = ""
  444. for _, char in pairs(tableContents) do
  445. result = result .. char.Char:sub(1, 1)
  446. end
  447. return result
  448. end
  449.  
  450. --- Returns the (char[]) array that holds all the lines in order as strings
  451. --- @return table[]
  452. function Highlight:getTable()
  453. return tableContents
  454. end
  455.  
  456. --- Returns the (int) number of lines in the code box
  457. --- @return number
  458. function Highlight:getSize()
  459. return #tableContents
  460. end
  461.  
  462. --- Returns the (string) line of the specified line number
  463. --- @param line number
  464. --- @return string
  465. function Highlight:getLine(line)
  466. local currentline = 0
  467. local rightLine = false
  468. local result = ""
  469. for _, v in pairs(tableContents) do
  470. currentline = currentline + 1
  471. if v.Char == "\n" and not rightLine then
  472. rightLine = true
  473. end
  474. if rightLine and v.Char ~= "\n" then
  475. result = result .. v.Char
  476. elseif rightLine then
  477. break
  478. end
  479. end
  480. return result
  481. end
  482.  
  483. --- Replaces the specified line number with the specified string (\n will overwrite further lines)
  484. --- @param line number
  485. ---@param text string
  486. function Highlight:setLine(line, text)
  487. if #tableContents and line >= tableContents[#tableContents].Line then
  488. for i = tableContents[#tableContents].Line, line do
  489. table.insert(tableContents, {
  490. Char = "\n",
  491. Line = i,
  492. Color = defaultColor
  493. })
  494. local str = Highlight:getRaw()
  495. str = str:sub(0, #str) .. text
  496. Highlight:setRaw(str)
  497. return
  498. end
  499. elseif not #tableContents then
  500. return
  501. end
  502. local str = Highlight:getRaw()
  503. local lastStart = 0
  504. local currentLine = 0
  505. for i in gfind(str, "\n") do
  506. currentLine = currentLine + 1
  507. if line == currentLine then
  508. str = str:sub(0, lastStart) .. text .. str:sub(i, #str)
  509. Highlight:setRaw(str)
  510. return
  511. end
  512. end
  513. error("Unable to set line")
  514. end
  515.  
  516. --- Inserts a line made from the specified string and moves all existing lines down (\n will insert further lines)
  517. --- @param line number
  518. ---@param text string
  519. function Highlight:insertLine(line, text)
  520. if #tableContents and line >= tableContents[#tableContents].Line then
  521. Highlight:setLine(line, text)
  522. elseif not #tableContents then
  523. return
  524. end
  525. local str = Highlight:getRaw()
  526. local lastStart = 0
  527. local currentLine = 0
  528. for i in gfind(str, "\n") do
  529. currentLine = currentLine + 1
  530. if line == currentLine then
  531. str = str:sub(0, lastStart) .. "\n" .. text .. "\n" .. str:sub(i, #str)
  532. Highlight:setRaw(str)
  533. return
  534. end
  535. end
  536. error("Unable to insert line")
  537. end
  538.  
  539. -- CONSTRUCTOR --
  540.  
  541. local constructor = {}
  542. --- responsible for instantiation
  543. function constructor.new(...)
  544. local class = Highlight
  545. local new = {}
  546. class.__index = class
  547. setmetatable(new, class)
  548. new:init(...)
  549. return new
  550. end
  551.  
  552. return constructor
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement