mirror of
https://github.com/rojo-rbx/rojo.git
synced 2026-04-20 12:45:05 +00:00
205 lines
5.9 KiB
Lua
205 lines
5.9 KiB
Lua
local Rojo = script:FindFirstAncestor("Rojo")
|
|
local Plugin = Rojo.Plugin
|
|
local Packages = Rojo.Packages
|
|
|
|
local Roact = require(Packages.Roact)
|
|
local Log = require(Packages.Log)
|
|
local Highlighter = require(Packages.Highlighter)
|
|
local StringDiff = require(script:FindFirstChild("StringDiff"))
|
|
|
|
local Timer = require(Plugin.Timer)
|
|
local Theme = require(Plugin.App.Theme)
|
|
local getTextBoundsAsync = require(Plugin.App.getTextBoundsAsync)
|
|
|
|
local CodeLabel = require(Plugin.App.Components.CodeLabel)
|
|
local BorderedContainer = require(Plugin.App.Components.BorderedContainer)
|
|
local ScrollingFrame = require(Plugin.App.Components.ScrollingFrame)
|
|
|
|
local e = Roact.createElement
|
|
|
|
local StringDiffVisualizer = Roact.Component:extend("StringDiffVisualizer")
|
|
|
|
function StringDiffVisualizer:init()
|
|
self.scriptBackground, self.setScriptBackground = Roact.createBinding(Color3.fromRGB(0, 0, 0))
|
|
self.contentSize, self.setContentSize = Roact.createBinding(Vector2.new(0, 0))
|
|
|
|
-- Ensure that the script background is up to date with the current theme
|
|
self.themeChangedConnection = settings().Studio.ThemeChanged:Connect(function()
|
|
task.defer(function()
|
|
-- Defer to allow Highlighter to process the theme change first
|
|
self:updateScriptBackground()
|
|
end)
|
|
end)
|
|
|
|
self:updateScriptBackground()
|
|
|
|
self:setState({
|
|
add = {},
|
|
remove = {},
|
|
})
|
|
end
|
|
|
|
function StringDiffVisualizer:willUnmount()
|
|
self.themeChangedConnection:Disconnect()
|
|
end
|
|
|
|
function StringDiffVisualizer:updateScriptBackground()
|
|
local backgroundColor = Highlighter.getTokenColor("background")
|
|
if backgroundColor ~= self.scriptBackground:getValue() then
|
|
self.setScriptBackground(backgroundColor)
|
|
end
|
|
end
|
|
|
|
function StringDiffVisualizer:didUpdate(previousProps)
|
|
if previousProps.oldString ~= self.props.oldString or previousProps.newString ~= self.props.newString then
|
|
local add, remove = self:calculateDiffLines()
|
|
self:setState({
|
|
add = add,
|
|
remove = remove,
|
|
})
|
|
end
|
|
end
|
|
|
|
function StringDiffVisualizer:calculateContentSize(theme)
|
|
local oldString, newString = self.props.oldString, self.props.newString
|
|
|
|
local oldStringBounds = getTextBoundsAsync(oldString, theme.Font.Code, theme.TextSize.Code, math.huge)
|
|
local newStringBounds = getTextBoundsAsync(newString, theme.Font.Code, theme.TextSize.Code, math.huge)
|
|
|
|
self.setContentSize(
|
|
Vector2.new(math.max(oldStringBounds.X, newStringBounds.X), math.max(oldStringBounds.Y, newStringBounds.Y))
|
|
)
|
|
end
|
|
|
|
function StringDiffVisualizer:calculateDiffLines()
|
|
Timer.start("StringDiffVisualizer:calculateDiffLines")
|
|
local oldString, newString = self.props.oldString, self.props.newString
|
|
|
|
-- Diff the two texts
|
|
local startClock = os.clock()
|
|
local diffs = StringDiff.findDiffs(oldString, newString)
|
|
local stopClock = os.clock()
|
|
|
|
Log.trace(
|
|
"Diffing {} byte and {} byte strings took {} microseconds and found {} diff sections",
|
|
#oldString,
|
|
#newString,
|
|
math.round((stopClock - startClock) * 1000 * 1000),
|
|
#diffs
|
|
)
|
|
|
|
-- Determine which lines to highlight
|
|
local add, remove = {}, {}
|
|
|
|
local oldLineNum, newLineNum = 1, 1
|
|
for _, diff in diffs do
|
|
local actionType, text = diff.actionType, diff.value
|
|
local lines = select(2, string.gsub(text, "\n", "\n"))
|
|
|
|
if actionType == StringDiff.ActionTypes.Equal then
|
|
oldLineNum += lines
|
|
newLineNum += lines
|
|
elseif actionType == StringDiff.ActionTypes.Insert then
|
|
if lines > 0 then
|
|
local textLines = string.split(text, "\n")
|
|
for i, textLine in textLines do
|
|
if string.match(textLine, "%S") then
|
|
add[newLineNum + i - 1] = true
|
|
end
|
|
end
|
|
else
|
|
if string.match(text, "%S") then
|
|
add[newLineNum] = true
|
|
end
|
|
end
|
|
newLineNum += lines
|
|
elseif actionType == StringDiff.ActionTypes.Delete then
|
|
if lines > 0 then
|
|
local textLines = string.split(text, "\n")
|
|
for i, textLine in textLines do
|
|
if string.match(textLine, "%S") then
|
|
remove[oldLineNum + i - 1] = true
|
|
end
|
|
end
|
|
else
|
|
if string.match(text, "%S") then
|
|
remove[oldLineNum] = true
|
|
end
|
|
end
|
|
oldLineNum += lines
|
|
else
|
|
Log.warn("Unknown diff action: {} {}", actionType, text)
|
|
end
|
|
end
|
|
|
|
Timer.stop()
|
|
return add, remove
|
|
end
|
|
|
|
function StringDiffVisualizer:render()
|
|
local oldString, newString = self.props.oldString, self.props.newString
|
|
|
|
return Theme.with(function(theme)
|
|
self:calculateContentSize(theme)
|
|
|
|
return e(BorderedContainer, {
|
|
size = self.props.size,
|
|
position = self.props.position,
|
|
anchorPoint = self.props.anchorPoint,
|
|
transparency = self.props.transparency,
|
|
}, {
|
|
Background = e("Frame", {
|
|
Size = UDim2.new(1, 0, 1, 0),
|
|
Position = UDim2.new(0, 0, 0, 0),
|
|
BorderSizePixel = 0,
|
|
BackgroundColor3 = self.scriptBackground,
|
|
ZIndex = -10,
|
|
}, {
|
|
UICorner = e("UICorner", {
|
|
CornerRadius = UDim.new(0, 5),
|
|
}),
|
|
}),
|
|
Separator = e("Frame", {
|
|
Size = UDim2.new(0, 2, 1, 0),
|
|
Position = UDim2.new(0.5, 0, 0, 0),
|
|
AnchorPoint = Vector2.new(0.5, 0),
|
|
BorderSizePixel = 0,
|
|
BackgroundColor3 = theme.BorderedContainer.BorderColor,
|
|
BackgroundTransparency = 0.5,
|
|
}),
|
|
Old = e(ScrollingFrame, {
|
|
position = UDim2.new(0, 2, 0, 2),
|
|
size = UDim2.new(0.5, -7, 1, -4),
|
|
scrollingDirection = Enum.ScrollingDirection.XY,
|
|
transparency = self.props.transparency,
|
|
contentSize = self.contentSize,
|
|
}, {
|
|
Source = e(CodeLabel, {
|
|
size = UDim2.new(1, 0, 1, 0),
|
|
position = UDim2.new(0, 0, 0, 0),
|
|
text = oldString,
|
|
lineBackground = theme.Diff.Background.Remove,
|
|
markedLines = self.state.remove,
|
|
}),
|
|
}),
|
|
New = e(ScrollingFrame, {
|
|
position = UDim2.new(0.5, 5, 0, 2),
|
|
size = UDim2.new(0.5, -7, 1, -4),
|
|
scrollingDirection = Enum.ScrollingDirection.XY,
|
|
transparency = self.props.transparency,
|
|
contentSize = self.contentSize,
|
|
}, {
|
|
Source = e(CodeLabel, {
|
|
size = UDim2.new(1, 0, 1, 0),
|
|
position = UDim2.new(0, 0, 0, 0),
|
|
text = newString,
|
|
lineBackground = theme.Diff.Background.Add,
|
|
markedLines = self.state.add,
|
|
}),
|
|
}),
|
|
})
|
|
end)
|
|
end
|
|
|
|
return StringDiffVisualizer
|