Stylua formatting (#785)

Uses Stylua to format all existing Lua files, and adds a CI check in
`lint` to pin this improvement. Excludes formatting dependencies, of
course.
This commit is contained in:
boatbomber
2023-09-18 18:39:46 -04:00
committed by GitHub
parent 840e9bedb2
commit 0f8e1625d5
52 changed files with 595 additions and 549 deletions

View File

@@ -42,7 +42,7 @@ jobs:
run: cargo test --locked --verbose
lint:
name: Rustfmt and Clippy
name: Rustfmt, Clippy, & Stylua
runs-on: ubuntu-latest
steps:
@@ -62,6 +62,9 @@ jobs:
with:
version: 'v0.2.7'
- name: Stylua
run: stylua --check plugin/src
- name: Rustfmt
run: cargo fmt -- --check

View File

@@ -1,4 +1,5 @@
[tools]
rojo = "rojo-rbx/rojo@7.3.0"
selene = "Kampfkarren/selene@0.20.0"
selene = "Kampfkarren/selene@0.25.0"
stylua = "JohnnyMorganz/stylua@0.18.2"
run-in-roblox = "rojo-rbx/run-in-roblox@0.3.0"

View File

@@ -24,15 +24,17 @@ end
local function rejectWrongProtocolVersion(infoResponseBody)
if infoResponseBody.protocolVersion ~= Config.protocolVersion then
local message = (
"Found a Rojo dev server, but it's using a different protocol version, and is incompatible." ..
"\nMake sure you have matching versions of both the Rojo plugin and server!" ..
"\n\nYour client is version %s, with protocol version %s. It expects server version %s." ..
"\nYour server is version %s, with protocol version %s." ..
"\n\nGo to https://github.com/rojo-rbx/rojo for more details."
"Found a Rojo dev server, but it's using a different protocol version, and is incompatible."
.. "\nMake sure you have matching versions of both the Rojo plugin and server!"
.. "\n\nYour client is version %s, with protocol version %s. It expects server version %s."
.. "\nYour server is version %s, with protocol version %s."
.. "\n\nGo to https://github.com/rojo-rbx/rojo for more details."
):format(
Version.display(Config.version), Config.protocolVersion,
Version.display(Config.version),
Config.protocolVersion,
Config.expectedServerVersionString,
infoResponseBody.serverVersion, infoResponseBody.protocolVersion
infoResponseBody.serverVersion,
infoResponseBody.protocolVersion
)
return Promise.reject(message)
@@ -59,14 +61,11 @@ local function rejectWrongPlaceId(infoResponseBody)
end
local message = (
"Found a Rojo server, but its project is set to only be used with a specific list of places." ..
"\nYour place ID is %s, but needs to be one of these:" ..
"\n%s" ..
"\n\nTo change this list, edit 'servePlaceIds' in your .project.json file."
):format(
tostring(game.PlaceId),
table.concat(idList, "\n")
)
"Found a Rojo server, but its project is set to only be used with a specific list of places."
.. "\nYour place ID is %s, but needs to be one of these:"
.. "\n%s"
.. "\n\nTo change this list, edit 'servePlaceIds' in your .project.json file."
):format(tostring(game.PlaceId), table.concat(idList, "\n"))
return Promise.reject(message)
end
@@ -141,10 +140,7 @@ end
function ApiContext:read(ids)
local url = ("%s/api/read/%s"):format(self.__baseUrl, table.concat(ids, ","))
return Http.get(url)
:andThen(rejectFailedRequests)
:andThen(Http.Response.json)
:andThen(function(body)
return Http.get(url):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
if body.sessionId ~= self.__sessionId then
return Promise.reject("Server changed ID")
end
@@ -189,10 +185,7 @@ function ApiContext:write(patch)
body = Http.jsonEncode(body)
return Http.post(url, body)
:andThen(rejectFailedRequests)
:andThen(Http.Response.json)
:andThen(function(body)
return Http.post(url, body):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
Log.info("Write response: {:?}", body)
return body
@@ -203,8 +196,7 @@ function ApiContext:retrieveMessages()
local url = ("%s/api/subscribe/%s"):format(self.__baseUrl, self.__messageCursor)
local function sendRequest()
local request = Http.get(url)
:catch(function(err)
local request = Http.get(url):catch(function(err)
if err.type == Http.Error.Kind.Timeout and self.__connected then
return sendRequest()
end
@@ -222,10 +214,7 @@ function ApiContext:retrieveMessages()
end)
end
return sendRequest()
:andThen(rejectFailedRequests)
:andThen(Http.Response.json)
:andThen(function(body)
return sendRequest():andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
if body.sessionId ~= self.__sessionId then
return Promise.reject("Server changed ID")
end
@@ -241,10 +230,7 @@ end
function ApiContext:open(id)
local url = ("%s/api/open/%s"):format(self.__baseUrl, id)
return Http.post(url, "")
:andThen(rejectFailedRequests)
:andThen(Http.Response.json)
:andThen(function(body)
return Http.post(url, ""):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
if body.sessionId ~= self.__sessionId then
return Promise.reject("Server changed ID")
end

View File

@@ -23,12 +23,10 @@ end
function Checkbox:didUpdate(lastProps)
if lastProps.active ~= self.props.active then
self.motor:setGoal(
Flipper.Spring.new(self.props.active and 1 or 0, {
self.motor:setGoal(Flipper.Spring.new(self.props.active and 1 or 0, {
frequency = 6,
dampingRatio = 1.1,
})
)
}))
end
end
@@ -52,13 +50,14 @@ function Checkbox:render()
BackgroundTransparency = 1,
[Roact.Event.Activated] = function()
if self.props.locked then return end
if self.props.locked then
return
end
self.props.onClick()
end,
}, {
StateTip = e(Tooltip.Trigger, {
text =
(if self.props.locked then "[LOCKED] " else "")
text = (if self.props.locked then "[LOCKED] " else "")
.. (if self.props.active then "Enabled" else "Disabled"),
}),
@@ -89,7 +88,9 @@ function Checkbox:render()
size = UDim2.new(1, 0, 1, 0),
}, {
Icon = e("ImageLabel", {
Image = if self.props.locked then Assets.Images.Checkbox.Locked else Assets.Images.Checkbox.Inactive,
Image = if self.props.locked
then Assets.Images.Checkbox.Locked
else Assets.Images.Checkbox.Inactive,
ImageColor3 = theme.Inactive.IconColor,
ImageTransparency = self.props.transparency,

View File

@@ -36,12 +36,10 @@ function Dropdown:didUpdate(prevProps)
})
end
self.openMotor:setGoal(
Flipper.Spring.new(self.state.open and 1 or 0, {
self.openMotor:setGoal(Flipper.Spring.new(self.state.open and 1 or 0, {
frequency = 6,
dampingRatio = 1.1,
})
)
}))
end
function Dropdown:render()
@@ -52,10 +50,7 @@ function Dropdown:render()
local width = -1
for i, option in self.props.options do
local text = tostring(option or "")
local textSize = TextService:GetTextSize(
text, 15, Enum.Font.GothamMedium,
Vector2.new(math.huge, 20)
)
local textSize = TextService:GetTextSize(text, 15, Enum.Font.GothamMedium, Vector2.new(math.huge, 20))
if textSize.X > width then
width = textSize.X
end
@@ -74,7 +69,9 @@ function Dropdown:render()
Font = Enum.Font.GothamMedium,
[Roact.Event.Activated] = function()
if self.props.locked then return end
if self.props.locked then
return
end
self:setState({
open = false,
})
@@ -96,7 +93,9 @@ function Dropdown:render()
BackgroundTransparency = 1,
[Roact.Event.Activated] = function()
if self.props.locked then return end
if self.props.locked then
return
end
self:setState({
open = not self.state.open,
})
@@ -136,7 +135,8 @@ function Dropdown:render()
TextTransparency = self.props.transparency,
}),
}),
Options = if self.state.open then e(SlicedImage, {
Options = if self.state.open
then e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = theme.BackgroundColor,
position = UDim2.new(1, 0, 1, 3),
@@ -169,7 +169,8 @@ function Dropdown:render()
}),
Roact.createFragment(optionButtons),
}),
}) else nil,
})
else nil,
})
end)
end

View File

@@ -38,15 +38,11 @@ function IconButton:render()
[Roact.Event.Activated] = self.props.onClick,
[Roact.Event.MouseEnter] = function()
self.motor:setGoal(
Flipper.Spring.new(1, HOVER_SPRING_PROPS)
)
self.motor:setGoal(Flipper.Spring.new(1, HOVER_SPRING_PROPS))
end,
[Roact.Event.MouseLeave] = function()
self.motor:setGoal(
Flipper.Spring.new(0, HOVER_SPRING_PROPS)
)
self.motor:setGoal(Flipper.Spring.new(0, HOVER_SPRING_PROPS))
end,
}, {
Icon = e("ImageLabel", {

View File

@@ -42,7 +42,6 @@ local function DisplayValue(props)
Position = UDim2.new(0, 25, 0, 0),
}),
})
elseif t == "table" then
-- Showing a memory address for tables is useless, so we want to show the best we can
local textRepresentation = nil
@@ -62,10 +61,10 @@ local function DisplayValue(props)
-- Wrap strings in quotes
if type(k) == "string" then
k = "\"" .. k .. "\""
k = '"' .. k .. '"'
end
if type(v) == "string" then
v = "\"" .. v .. "\""
v = '"' .. v .. '"'
end
out[i] = string.format("[%s] = %s", tostring(k), tostring(v))

View File

@@ -43,9 +43,14 @@ end
function PatchVisualizer:render()
local patchTree = self.props.patchTree
if patchTree == nil and self.props.patch ~= nil then
patchTree = PatchTree.build(self.props.patch, self.props.instanceMap, self.props.changeListHeaders or { "Property", "Current", "Incoming" })
patchTree = PatchTree.build(
self.props.patch,
self.props.instanceMap,
self.props.changeListHeaders or { "Property", "Current", "Incoming" }
)
if self.props.unappliedPatch then
patchTree = PatchTree.updateMetadata(patchTree, self.props.patch, self.props.instanceMap, self.props.unappliedPatch)
patchTree =
PatchTree.updateMetadata(patchTree, self.props.patch, self.props.instanceMap, self.props.unappliedPatch)
end
end

View File

@@ -14,7 +14,11 @@ local StudioPluginAction = Roact.Component:extend("StudioPluginAction")
function StudioPluginAction:init()
self.pluginAction = self.props.plugin:CreatePluginAction(
self.props.name, self.props.title, self.props.description, self.props.icon, self.props.bindable
self.props.name,
self.props.title,
self.props.description,
self.props.icon,
self.props.bindable
)
self.pluginAction.Triggered:Connect(self.props.onTriggered)
@@ -31,9 +35,12 @@ end
local function StudioPluginActionWrapper(props)
return e(StudioPluginContext.Consumer, {
render = function(plugin)
return e(StudioPluginAction, Dictionary.merge(props, {
return e(
StudioPluginAction,
Dictionary.merge(props, {
plugin = plugin,
}))
})
)
end,
})
end

View File

@@ -38,7 +38,10 @@ function StudioPluginGui:init()
minimumSize.Y
)
local pluginGui = self.props.plugin:CreateDockWidgetPluginGui(if self.props.isEphemeral then HttpService:GenerateGUID(false) else self.props.id, dockWidgetPluginGuiInfo)
local pluginGui = self.props.plugin:CreateDockWidgetPluginGui(
if self.props.isEphemeral then HttpService:GenerateGUID(false) else self.props.id,
dockWidgetPluginGuiInfo
)
pluginGui.Name = self.props.id
pluginGui.Title = self.props.title

View File

@@ -18,12 +18,8 @@ StudioToggleButton.defaultProps = {
}
function StudioToggleButton:init()
local button = self.props.toolbar:CreateButton(
self.props.name,
self.props.tooltip,
self.props.icon,
self.props.text
)
local button =
self.props.toolbar:CreateButton(self.props.name, self.props.tooltip, self.props.icon, self.props.text)
button.Click:Connect(function()
if self.props.onClick then
@@ -61,9 +57,12 @@ end
local function StudioToggleButtonWrapper(props)
return e(StudioToolbarContext.Consumer, {
render = function(toolbar)
return e(StudioToggleButton, Dictionary.merge(props, {
return e(
StudioToggleButton,
Dictionary.merge(props, {
toolbar = toolbar,
}))
})
)
end,
})
end

View File

@@ -36,9 +36,12 @@ end
local function StudioToolbarWrapper(props)
return e(StudioPluginContext.Consumer, {
render = function(plugin)
return e(StudioToolbar, Dictionary.merge(props, {
return e(
StudioToolbar,
Dictionary.merge(props, {
plugin = plugin,
}))
})
)
end,
})
end

View File

@@ -41,10 +41,8 @@ end
function TextButton:render()
return Theme.with(function(theme)
local textSize = TextService:GetTextSize(
self.props.text, 18, Enum.Font.GothamSemibold,
Vector2.new(math.huge, math.huge)
)
local textSize =
TextService:GetTextSize(self.props.text, 18, Enum.Font.GothamSemibold, Vector2.new(math.huge, math.huge))
local style = self.props.style
@@ -124,7 +122,11 @@ function TextButton:render()
Background = style == "Solid" and e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BackgroundColor, theme.Disabled.BackgroundColor),
color = bindingUtil.mapLerp(
bindingEnabled,
theme.Enabled.BackgroundColor,
theme.Disabled.BackgroundColor
),
transparency = self.props.transparency,
size = UDim2.new(1, 0, 1, 0),

View File

@@ -74,7 +74,11 @@ function TextInput:render()
PlaceholderText = self.props.placeholder,
Font = Enum.Font.GothamMedium,
TextColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Disabled.TextColor, theme.Enabled.TextColor),
PlaceholderColor3 = bindingUtil.mapLerp(bindingEnabled, theme.Disabled.PlaceholderColor, theme.Enabled.PlaceholderColor),
PlaceholderColor3 = bindingUtil.mapLerp(
bindingEnabled,
theme.Disabled.PlaceholderColor,
theme.Enabled.PlaceholderColor
),
TextSize = 18,
TextEditable = self.props.enabled,
ClearTextOnFocus = self.props.clearTextOnFocus,

View File

@@ -22,12 +22,16 @@ local TooltipContext = Roact.createContext({})
local function Popup(props)
local textSize = TextService:GetTextSize(
props.Text, 16, Enum.Font.GothamMedium, Vector2.new(math.min(props.parentSize.X, 160), math.huge)
props.Text,
16,
Enum.Font.GothamMedium,
Vector2.new(math.min(props.parentSize.X, 160), math.huge)
) + TEXT_PADDING + (Vector2.one * 2)
local trigger = props.Trigger:getValue()
local spaceBelow = props.parentSize.Y - (trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y - Y_OVERLAP + TAIL_SIZE)
local spaceBelow = props.parentSize.Y
- (trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y - Y_OVERLAP + TAIL_SIZE)
local spaceAbove = trigger.AbsolutePosition.Y + Y_OVERLAP - TAIL_SIZE
-- If there's not enough space below, and there's more space above, then show the tooltip above the trigger
@@ -39,7 +43,10 @@ local function Popup(props)
if displayAbove then
Y = math.max(trigger.AbsolutePosition.Y - TAIL_SIZE - textSize.Y + Y_OVERLAP, 0)
else
Y = math.min(trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y + TAIL_SIZE - Y_OVERLAP, props.parentSize.Y - textSize.Y)
Y = math.min(
trigger.AbsolutePosition.Y + trigger.AbsoluteSize.Y + TAIL_SIZE - Y_OVERLAP,
props.parentSize.Y - textSize.Y
)
end
return Theme.with(function(theme)
@@ -64,17 +71,9 @@ local function Popup(props)
Tail = e("ImageLabel", {
ZIndex = 100,
Position =
if displayAbove then
UDim2.new(
0, math.clamp(props.Position.X - X, 6, textSize.X-6),
1, -1
)
else
UDim2.new(
0, math.clamp(props.Position.X - X, 6, textSize.X-6),
0, -TAIL_SIZE+1
),
Position = if displayAbove
then UDim2.new(0, math.clamp(props.Position.X - X, 6, textSize.X - 6), 1, -1)
else UDim2.new(0, math.clamp(props.Position.X - X, 6, textSize.X - 6), 0, -TAIL_SIZE + 1),
Size = UDim2.fromOffset(TAIL_SIZE, TAIL_SIZE),
AnchorPoint = Vector2.new(0.5, 0),
Rotation = if displayAbove then 180 else 0,
@@ -90,7 +89,7 @@ local function Popup(props)
ImageColor3 = theme.BorderedContainer.BorderColor,
ImageTransparency = props.transparency,
}),
})
}),
})
end)
end
@@ -200,9 +199,10 @@ function Trigger:isHovering()
local size = rbx.AbsoluteSize
local mousePos = self.mousePos
return
mousePos.X >= pos.X and mousePos.X <= pos.X + size.X
and mousePos.Y >= pos.Y and mousePos.Y <= pos.Y + size.Y
return mousePos.X >= pos.X
and mousePos.X <= pos.X + size.X
and mousePos.Y >= pos.Y
and mousePos.Y <= pos.Y + size.Y
end
return false
end
@@ -236,7 +236,9 @@ end
function Trigger:render()
local function recalculate(rbx)
local widget = rbx:FindFirstAncestorOfClass("DockWidgetPluginGui")
if not widget then return end
if not widget then
return
end
self.mousePos = widget:GetRelativeMousePosition()
self:managePopup()

View File

@@ -24,9 +24,7 @@ function TouchRipple:init()
})
self.binding = bindingUtil.fromMotor(self.motor)
self.position, self.setPosition = Roact.createBinding(
Vector2.new(0, 0)
)
self.position, self.setPosition = Roact.createBinding(Vector2.new(0, 0))
end
function TouchRipple:reset()
@@ -43,10 +41,7 @@ function TouchRipple:calculateRadius(position)
local container = self.ref.current
if container then
local corner = Vector2.new(
math.floor((1 - position.X) + 0.5),
math.floor((1 - position.Y) + 0.5)
)
local corner = Vector2.new(math.floor((1 - position.X) + 0.5), math.floor((1 - position.Y) + 0.5))
local size = container.AbsoluteSize
local ratio = size / math.min(size.X, size.Y)
@@ -93,10 +88,7 @@ function TouchRipple:render()
input:GetPropertyChangedSignal("UserInputState"):Connect(function()
local userInputState = input.UserInputState
if
userInputState == Enum.UserInputState.Cancel
or userInputState == Enum.UserInputState.End
then
if userInputState == Enum.UserInputState.Cancel or userInputState == Enum.UserInputState.End then
self.motor:setGoal({
opacity = Flipper.Spring.new(0, {
frequency = 5,
@@ -127,8 +119,10 @@ function TouchRipple:render()
local containerAspect = containerSize.X / containerSize.Y
return UDim2.new(
currentSize / math.max(containerAspect, 1), 0,
currentSize * math.min(containerAspect, 1), 0
currentSize / math.max(containerAspect, 1),
0,
currentSize * math.min(containerAspect, 1),
0
)
end
end),

View File

@@ -37,21 +37,17 @@ function Notification:init()
end
function Notification:dismiss()
self.motor:setGoal(
Flipper.Spring.new(0, {
self.motor:setGoal(Flipper.Spring.new(0, {
frequency = 5,
dampingRatio = 1,
})
)
}))
end
function Notification:didMount()
self.motor:setGoal(
Flipper.Spring.new(1, {
self.motor:setGoal(Flipper.Spring.new(1, {
frequency = 3,
dampingRatio = 1,
})
)
}))
self.props.soundPlayer:play(Assets.Sounds.Notification)
@@ -90,12 +86,7 @@ function Notification:render()
return 1 - value
end)
local textBounds = TextService:GetTextSize(
self.props.text,
15,
Enum.Font.GothamMedium,
Vector2.new(350, 700)
)
local textBounds = TextService:GetTextSize(self.props.text, 15, Enum.Font.GothamMedium, Vector2.new(350, 700))
local actionButtons = {}
local buttonsX = 0
@@ -116,7 +107,9 @@ function Notification:render()
})
buttonsX += TextService:GetTextSize(
action.text, 18, Enum.Font.GothamMedium,
action.text,
18,
Enum.Font.GothamMedium,
Vector2.new(math.huge, math.huge)
).X + 30
@@ -156,7 +149,7 @@ function Notification:render()
Contents = e("Frame", {
Size = UDim2.new(0, 35 + contentX, 1, -paddingY),
Position = UDim2.new(0, 0, 0, paddingY / 2),
BackgroundTransparency = 1
BackgroundTransparency = 1,
}, {
Logo = e("ImageLabel", {
ImageTransparency = transparency,
@@ -181,7 +174,8 @@ function Notification:render()
LayoutOrder = 1,
BackgroundTransparency = 1,
}),
Actions = if self.props.actions then e("Frame", {
Actions = if self.props.actions
then e("Frame", {
Size = UDim2.new(1, -40, 0, 35),
Position = UDim2.new(1, 0, 1, 0),
AnchorPoint = Vector2.new(1, 1),
@@ -195,14 +189,15 @@ function Notification:render()
Padding = UDim.new(0, 5),
}),
Buttons = Roact.createFragment(actionButtons),
}) else nil,
})
else nil,
}),
Padding = e("UIPadding", {
PaddingLeft = UDim.new(0, 17),
PaddingRight = UDim.new(0, 15),
}),
})
}),
})
end)
end

View File

@@ -15,7 +15,7 @@ local Page = Roact.Component:extend("Page")
function Page:init()
self:setState({
rendered = self.props.active
rendered = self.props.active,
})
self.motor = Flipper.SingleMotor.new(self.props.active and 1 or 0)
@@ -51,20 +51,21 @@ function Page:render()
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
}, {
Component = e(self.props.component, Dictionary.merge(self.props, {
Component = e(
self.props.component,
Dictionary.merge(self.props, {
transparency = transparency,
}))
})
),
})
end
function Page:didUpdate(lastProps)
if self.props.active ~= lastProps.active then
self.motor:setGoal(
Flipper.Spring.new(self.props.active and 1 or 0, {
self.motor:setGoal(Flipper.Spring.new(self.props.active and 1 or 0, {
frequency = 6,
dampingRatio = 1,
})
)
}))
end
end

View File

@@ -85,7 +85,7 @@ function ConfirmingPage:render()
onClick = self.props.onAbort,
}, {
Tip = e(Tooltip.Trigger, {
text = "Stop the connection process"
text = "Stop the connection process",
}),
}),
@@ -98,7 +98,7 @@ function ConfirmingPage:render()
onClick = self.props.onReject,
}, {
Tip = e(Tooltip.Trigger, {
text = "Push Studio changes to the Rojo server"
text = "Push Studio changes to the Rojo server",
}),
})
else nil,
@@ -111,7 +111,7 @@ function ConfirmingPage:render()
onClick = self.props.onAccept,
}, {
Tip = e(Tooltip.Trigger, {
text = "Pull Rojo server changes to Studio"
text = "Pull Rojo server changes to Studio",
}),
}),
@@ -169,7 +169,7 @@ function ConfirmingPage:render()
oldText = self.state.oldSource,
newText = self.state.newSource,
})
}),
}),
}),
}),

View File

@@ -21,7 +21,17 @@ local StringDiffVisualizer = require(Plugin.App.Components.StringDiffVisualizer)
local e = Roact.createElement
local AGE_UNITS = { {31556909, "year"}, {2629743, "month"}, {604800, "week"}, {86400, "day"}, {3600, "hour"}, {60, "minute"}, }
local AGE_UNITS = {
{ 31556909, "year" },
{ 2629743, "month" },
{ 604800, "week" },
{ 86400, "day" },
{ 3600, "hour" },
{
60,
"minute",
},
}
function timeSinceText(elapsed: number): string
if elapsed < 3 then
return "just now"
@@ -159,12 +169,11 @@ function ConnectedPage:getChangeInfoText()
local elapsed = os.time() - patchData.timestamp
local unapplied = PatchSet.countChanges(patchData.unapplied)
return
"<i>Synced "
return "<i>Synced "
.. timeSinceText(elapsed)
.. (if unapplied > 0 then
string.format(
", <font color=\"#FF8E3C\">but %d change%s failed to apply</font>",
.. (if unapplied > 0
then string.format(
', <font color="#FF8E3C">but %d change%s failed to apply</font>',
unapplied,
unapplied == 1 and "" or "s"
)
@@ -297,7 +306,7 @@ function ConnectedPage:render()
onClick = self.props.onNavigateSettings,
}, {
Tip = e(Tooltip.Trigger, {
text = "View and modify plugin settings"
text = "View and modify plugin settings",
}),
}),
@@ -309,7 +318,7 @@ function ConnectedPage:render()
onClick = self.props.onDisconnect,
}, {
Tip = e(Tooltip.Trigger, {
text = "Disconnect from the Rojo sync server"
text = "Disconnect from the Rojo sync server",
}),
}),
@@ -427,7 +436,7 @@ function ConnectedPage:render()
oldText = self.state.oldSource,
newText = self.state.newSource,
})
}),
}),
}),
}),

View File

@@ -50,7 +50,9 @@ function Error:render()
local containerSize = object.AbsoluteSize - ERROR_PADDING * 2
local textBounds = TextService:GetTextSize(
self.props.errorMessage, 16, Enum.Font.Code,
self.props.errorMessage,
16,
Enum.Font.Code,
Vector2.new(containerSize.X, math.huge)
)
@@ -60,12 +62,13 @@ function Error:render()
ErrorMessage = Theme.with(function(theme)
return e("TextBox", {
[Roact.Event.InputBegan] = function(rbx, input)
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then return end
if input.UserInputType ~= Enum.UserInputType.MouseButton1 then
return
end
rbx.SelectionStart = 0
rbx.CursorPosition = #rbx.Text + 1
end,
Text = self.props.errorMessage,
TextEditable = false,
Font = Enum.Font.Code,
@@ -126,7 +129,7 @@ function ErrorPage:render()
onClick = self.props.onClose,
}, {
Tip = e(Tooltip.Trigger, {
text = "Dismiss message"
text = "Dismiss message",
}),
}),

View File

@@ -46,7 +46,7 @@ local function AddressEntry(props)
if props.onHostChange ~= nil then
props.onHostChange(object.Text)
end
end
end,
}),
Port = e("TextBox", {
@@ -120,7 +120,7 @@ function NotConnectedPage:render()
onClick = self.props.onNavigateSettings,
}, {
Tip = e(Tooltip.Trigger, {
text = "View and modify plugin settings"
text = "View and modify plugin settings",
}),
}),
@@ -132,7 +132,7 @@ function NotConnectedPage:render()
onClick = self.props.onConnect,
}, {
Tip = e(Tooltip.Trigger, {
text = "Connect to a Rojo sync server"
text = "Connect to a Rojo sync server",
}),
}),

View File

@@ -81,11 +81,9 @@ function Setting:render()
end,
}),
Input =
if self.props.input ~= nil then
self.props.input
elseif self.props.options ~= nil then
e(Dropdown, {
Input = if self.props.input ~= nil
then self.props.input
elseif self.props.options ~= nil then e(Dropdown, {
locked = self.props.locked,
options = self.props.options,
active = self.state.setting,
@@ -94,8 +92,7 @@ function Setting:render()
Settings:set(self.props.id, option)
end,
})
else
e(Checkbox, {
else e(Checkbox, {
locked = self.props.locked,
active = self.state.setting,
transparency = self.props.transparency,
@@ -105,7 +102,8 @@ function Setting:render()
end,
}),
Reset = if self.props.onReset then e(IconButton, {
Reset = if self.props.onReset
then e(IconButton, {
icon = Assets.Images.Icons.Reset,
iconSize = 24,
color = theme.BackButtonColor,
@@ -114,7 +112,8 @@ function Setting:render()
layoutOrder = -1,
onClick = self.props.onReset,
}) else nil,
})
else nil,
}),
Text = e("Frame", {
@@ -122,7 +121,8 @@ function Setting:render()
BackgroundTransparency = 1,
}, {
Name = e("TextLabel", {
Text = (if self.props.experimental then "<font color=\"#FF8E3C\">⚠ </font>" else "") .. self.props.name,
Text = (if self.props.experimental then '<font color="#FF8E3C">⚠ </font>' else "")
.. self.props.name,
Font = Enum.Font.GothamBold,
TextSize = 17,
TextColor3 = theme.Setting.NameColor,
@@ -137,7 +137,8 @@ function Setting:render()
}),
Description = e("TextLabel", {
Text = (if self.props.experimental then "<font color=\"#FF8E3C\">[Experimental] </font>" else "") .. self.props.description,
Text = (if self.props.experimental then '<font color="#FF8E3C">[Experimental] </font>' else "")
.. self.props.description,
Font = Enum.Font.Gotham,
LineHeight = 1.2,
TextSize = 14,
@@ -151,10 +152,14 @@ function Setting:render()
containerSize = self.containerSize,
inputSize = self.inputSize,
}):map(function(values)
local desc = (if self.props.experimental then "[Experimental] " else "") .. self.props.description
local desc = (if self.props.experimental then "[Experimental] " else "")
.. self.props.description
local offset = values.inputSize.X + 5
local textBounds = getTextBounds(
desc, 14, Enum.Font.Gotham, 1.2,
desc,
14,
Enum.Font.Gotham,
1.2,
Vector2.new(values.containerSize.X - offset, math.huge)
)
return UDim2.new(1, -offset, 0, textBounds.Y)

View File

@@ -49,7 +49,7 @@ local function Navbar(props)
onClick = props.onBack,
}, {
Tip = e(Tooltip.Trigger, {
text = "Back"
text = "Back",
}),
}),
@@ -63,7 +63,7 @@ local function Navbar(props)
Size = UDim2.new(1, 0, 1, 0),
BackgroundTransparency = 1,
})
}),
})
end)
end
@@ -138,7 +138,10 @@ function SettingsPage:render()
Settings:set("largeChangesConfirmationThreshold", math.clamp(number, 1, 999))
else
-- Force text back to last valid value
Settings:set("largeChangesConfirmationThreshold", Settings:get("largeChangesConfirmationThreshold"))
Settings:set(
"largeChangesConfirmationThreshold",
Settings:get("largeChangesConfirmationThreshold")
)
end
end,
}),

View File

@@ -90,7 +90,7 @@ local lightTheme = strict("LightTheme", {
},
AddressEntry = {
TextColor = Color3.fromHex("000000"),
PlaceholderColor = Color3.fromHex("8C8C8C")
PlaceholderColor = Color3.fromHex("8C8C8C"),
},
BorderedContainer = {
BorderColor = Color3.fromHex("CBCBCB"),
@@ -200,7 +200,7 @@ local darkTheme = strict("DarkTheme", {
},
AddressEntry = {
TextColor = Color3.fromHex("FFFFFF"),
PlaceholderColor = Color3.fromHex("8B8B8B")
PlaceholderColor = Color3.fromHex("8B8B8B"),
},
BorderedContainer = {
BorderColor = Color3.fromHex("535353"),
@@ -235,7 +235,7 @@ local darkTheme = strict("DarkTheme", {
},
Header = {
LogoColor = BRAND_COLOR,
VersionColor = Color3.fromHex("D3D3D3")
VersionColor = Color3.fromHex("D3D3D3"),
},
Notification = {
InfoColor = Color3.fromHex("FFFFFF"),

View File

@@ -469,13 +469,17 @@ function App:startSession()
if confirmationBehavior == "Initial" then
-- Only confirm if we haven't synced this project yet this session
if self.knownProjects[serverInfo.projectName] then
Log.trace("Accepting patch without confirmation because project has already been connected and behavior is set to Initial")
Log.trace(
"Accepting patch without confirmation because project has already been connected and behavior is set to Initial"
)
return "Accept"
end
elseif confirmationBehavior == "Large Changes" then
-- Only confirm if the patch impacts many instances
if PatchSet.countInstances(patch) < Settings:get("largeChangesConfirmationThreshold") then
Log.trace("Accepting patch without confirmation because patch is small and behavior is set to Large Changes")
Log.trace(
"Accepting patch without confirmation because patch is small and behavior is set to Large Changes"
)
return "Accept"
end
elseif confirmationBehavior == "Unlisted PlaceId" then
@@ -483,7 +487,9 @@ function App:startSession()
if serverInfo.expectedPlaceIds then
local isListed = table.find(serverInfo.expectedPlaceIds, game.PlaceId) ~= nil
if isListed then
Log.trace("Accepting patch without confirmation because placeId is listed and behavior is set to Unlisted PlaceId")
Log.trace(
"Accepting patch without confirmation because placeId is listed and behavior is set to Unlisted PlaceId"
)
return "Accept"
end
end
@@ -657,7 +663,8 @@ function App:render()
}),
Settings = createPageElement(AppStatus.Settings, {
syncActive = self.serveSession ~= nil and self.serveSession:getStatus() == ServeSession.Status.Connected,
syncActive = self.serveSession ~= nil
and self.serveSession:getStatus() == ServeSession.Status.Connected,
onBack = function()
self:setState({

View File

@@ -54,7 +54,7 @@ local Assets = {
[32] = "rbxassetid://3088713341",
[64] = "rbxassetid://4918677124",
[128] = "rbxassetid://2600845734",
[500] = "rbxassetid://2609138523"
[500] = "rbxassetid://2609138523",
},
},
Sounds = {

View File

@@ -26,7 +26,9 @@ local function deepEqual(a: any, b: any): boolean
end
for key, value in b do
if checkedKeys[key] then continue end
if checkedKeys[key] then
continue
end
if deepEqual(value, a[key]) == false then
return false
end
@@ -65,9 +67,7 @@ end
Tells whether the given PatchSet is empty.
]]
function PatchSet.isEmpty(patchSet)
return next(patchSet.removed) == nil and
next(patchSet.added) == nil and
next(patchSet.updated) == nil
return next(patchSet.removed) == nil and next(patchSet.added) == nil and next(patchSet.updated) == nil
end
--[[
@@ -342,9 +342,15 @@ function PatchSet.humanSummary(instanceMap, patchSet)
end
end
table.insert(statements, string.format(
table.insert(
statements,
string.format(
"- Add instance %q (ClassName %q) to %s",
virtualInstance.Name, virtualInstance.ClassName, parentDisplayName))
virtualInstance.Name,
virtualInstance.ClassName,
parentDisplayName
)
)
end
for _, update in ipairs(patchSet.updated) do
@@ -374,9 +380,10 @@ function PatchSet.humanSummary(instanceMap, patchSet)
displayName = "[unknown instance]"
end
table.insert(statements, string.format(
"- Update properties on %s: %s",
displayName, table.concat(updatedProperties, ",")))
table.insert(
statements,
string.format("- Update properties on %s: %s", displayName, table.concat(updatedProperties, ","))
)
end
return table.concat(statements, "\n")

View File

@@ -401,7 +401,9 @@ end
-- Creates a deep copy of a tree for immutability purposes in Roact
function PatchTree.clone(tree)
if not tree then return end
if not tree then
return
end
local newTree = Tree.new()
tree:forEach(function(node)

View File

@@ -30,7 +30,8 @@ local function decodeValue(encodedValue, instanceMap)
local ok, decodedValue = RbxDom.EncodedValue.decode(encodedValue)
if not ok then
return false, Error.new(Error.CannotDecodeValue, {
return false,
Error.new(Error.CannotDecodeValue, {
encodedValue = encodedValue,
innerError = decodedValue,
})

View File

@@ -37,7 +37,9 @@ local function trueEquals(a, b): boolean
end
end
for key, value in pairs(b) do
if checkedKeys[key] then continue end
if checkedKeys[key] then
continue
end
if not trueEquals(value, a[key]) then
return false
end
@@ -89,7 +91,6 @@ local function trueEquals(a, b): boolean
end
end
return true
end
return false
@@ -154,7 +155,13 @@ local function diff(instanceMap, virtualInstances, rootId)
if ok then
if not trueEquals(existingValue, decodedValue) then
Log.debug("{}.{} changed from '{}' to '{}'", instance:GetFullName(), propertyName, existingValue, decodedValue)
Log.debug(
"{}.{} changed from '{}' to '{}'",
instance:GetFullName(),
propertyName,
existingValue,
decodedValue
)
changedProperties[propertyName] = virtualValue
end
else

View File

@@ -14,14 +14,16 @@ local function getProperty(instance, propertyName)
-- A good example of a property like this is `Model.ModelInPrimary`, which
-- is serialized but not reflected to Lua.
if descriptor == nil then
return false, Error.new(Error.UnknownProperty, {
return false,
Error.new(Error.UnknownProperty, {
className = instance.ClassName,
propertyName = propertyName,
})
end
if descriptor.scriptability == "None" or descriptor.scriptability == "Write" then
return false, Error.new(Error.UnreadableProperty, {
return false,
Error.new(Error.UnreadableProperty, {
className = instance.ClassName,
propertyName = propertyName,
})
@@ -35,20 +37,23 @@ local function getProperty(instance, propertyName)
-- If we don't have permission to read a property, we can chalk that up
-- to our database being out of date and the engine being right.
if err.kind == RbxDom.Error.Kind.Roblox and err.extra:find("lacking permission") then
return false, Error.new(Error.LackingPropertyPermissions, {
return false,
Error.new(Error.LackingPropertyPermissions, {
className = instance.ClassName,
propertyName = propertyName,
})
end
if err.kind == RbxDom.Error.Kind.Roblox and err.extra:find("is not a valid member of") then
return false, Error.new(Error.UnknownProperty, {
return false,
Error.new(Error.UnknownProperty, {
className = instance.ClassName,
propertyName = propertyName,
})
end
return false, Error.new(Error.OtherPropertyError, {
return false,
Error.new(Error.OtherPropertyError, {
className = instance.ClassName,
propertyName = propertyName,
})

View File

@@ -21,7 +21,8 @@ local function setProperty(instance, propertyName, value)
end
if descriptor.scriptability == "None" or descriptor.scriptability == "Read" then
return false, Error.new(Error.UnwritableProperty, {
return false,
Error.new(Error.UnwritableProperty, {
className = instance.ClassName,
propertyName = propertyName,
})
@@ -31,13 +32,15 @@ local function setProperty(instance, propertyName, value)
if not ok then
if err.kind == RbxDom.Error.Kind.Roblox and err.extra:find("lacking permission") then
return false, Error.new(Error.LackingPropertyPermissions, {
return false,
Error.new(Error.LackingPropertyPermissions, {
className = instance.ClassName,
propertyName = propertyName,
})
end
return false, Error.new(Error.OtherPropertyError, {
return false,
Error.new(Error.OtherPropertyError, {
className = instance.ClassName,
propertyName = propertyName,
})

View File

@@ -77,9 +77,7 @@ function ServeSession.new(options)
local connections = {}
local connection = StudioService
:GetPropertyChangedSignal("ActiveScript")
:Connect(function()
local connection = StudioService:GetPropertyChangedSignal("ActiveScript"):Connect(function()
local activeScript = StudioService.ActiveScript
if activeScript ~= nil then
@@ -139,12 +137,12 @@ end
function ServeSession:start()
self:__setStatus(Status.Connecting)
self.__apiContext:connect()
self.__apiContext
:connect()
:andThen(function(serverInfo)
self:__applyGameAndPlaceId(serverInfo)
return self:__initialSync(serverInfo)
:andThen(function()
return self:__initialSync(serverInfo):andThen(function()
self:__setStatus(Status.Connected, serverInfo.projectName)
return self:__mainSyncLoop()
@@ -211,8 +209,7 @@ function ServeSession:__onActiveScriptChanged(activeScript)
end
function ServeSession:__initialSync(serverInfo)
return self.__apiContext:read({ serverInfo.rootInstanceId })
:andThen(function(readResponseBody)
return self.__apiContext:read({ serverInfo.rootInstanceId }):andThen(function(readResponseBody)
-- Tell the API Context that we're up-to-date with the version of
-- the tree defined in this response.
self.__apiContext:setMessageCursor(readResponseBody.messageCursor)
@@ -225,21 +222,15 @@ function ServeSession:__initialSync(serverInfo)
-- Calculate the initial patch to apply to the DataModel to catch us
-- up to what Rojo thinks the place should look like.
Log.trace("Computing changes that plugin needs to make to catch up to server...")
local success, catchUpPatch = self.__reconciler:diff(
readResponseBody.instances,
serverInfo.rootInstanceId,
game
)
local success, catchUpPatch =
self.__reconciler:diff(readResponseBody.instances, serverInfo.rootInstanceId, game)
if not success then
Log.error("Could not compute a diff to catch up to the Rojo server: {:#?}", catchUpPatch)
end
for _, update in catchUpPatch.updated do
if
update.id == self.__instanceMap.fromInstances[game]
and update.changedClassName ~= nil
then
if update.id == self.__instanceMap.fromInstances[game] and update.changedClassName ~= nil then
-- Non-place projects will try to update the classname of game from DataModel to
-- something like Folder, ModuleScript, etc. This would fail, so we exit with a clear
-- message instead of crashing.
@@ -260,7 +251,6 @@ function ServeSession:__initialSync(serverInfo)
if userDecision == "Abort" then
return Promise.reject("Aborted Rojo sync operation")
elseif userDecision == "Reject" and self.__twoWaySync then
-- The user wants their studio DOM to write back to their Rojo DOM
-- so we will reverse the patch and send it back
@@ -270,7 +260,9 @@ function ServeSession:__initialSync(serverInfo)
-- Send back the current properties
for _, change in catchUpPatch.updated do
local instance = self.__instanceMap.fromIds[change.id]
if not instance then continue end
if not instance then
continue
end
local update = encodePatchUpdate(instance, change.id, change.changedProperties)
table.insert(inversePatch.updated, update)
@@ -286,13 +278,14 @@ function ServeSession:__initialSync(serverInfo)
end
self.__apiContext:write(inversePatch)
elseif userDecision == "Accept" then
local unappliedPatch = self.__reconciler:applyPatch(catchUpPatch)
if not PatchSet.isEmpty(unappliedPatch) then
Log.warn("Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch))
Log.warn(
"Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch)
)
end
end
end)
@@ -301,7 +294,8 @@ end
function ServeSession:__mainSyncLoop()
return Promise.new(function(resolve, reject)
while self.__status == Status.Connected do
local success, result = self.__apiContext:retrieveMessages()
local success, result = self.__apiContext
:retrieveMessages()
:andThen(function(messages)
if self.__status == Status.Disconnected then
-- In the time it took to retrieve messages, we disconnected
@@ -315,11 +309,14 @@ function ServeSession:__mainSyncLoop()
local unappliedPatch = self.__reconciler:applyPatch(message)
if not PatchSet.isEmpty(unappliedPatch) then
Log.warn("Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch))
Log.warn(
"Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch)
)
end
end
end):await()
end)
:await()
if self.__status == Status.Disconnected then
-- If we are no longer connected after applying, we stop silently

View File

@@ -56,11 +56,7 @@ local ApiSubscribeResponse = t.interface({
})
local ApiError = t.interface({
kind = t.union(
t.literal("NotFound"),
t.literal("BadRequest"),
t.literal("InternalError")
),
kind = t.union(t.literal("NotFound"), t.literal("BadRequest"), t.literal("InternalError")),
details = t.string,
})

View File

@@ -17,7 +17,7 @@ Log.setLogLevelThunk(function()
end)
local app = Roact.createElement(App, {
plugin = plugin
plugin = plugin,
})
local tree = Roact.mount(app, game:GetService("CoreGui"), "Rojo UI")

View File

@@ -1,4 +1,3 @@
local Packages = script.Parent.Parent.Packages
local Fmt = require(Packages.Fmt)
@@ -17,10 +16,10 @@ else
message = Fmt.fmt(message, ...)
local fullMessage = string.format(
"Rojo detected an invariant violation within itself:\n" ..
"%s\n\n" ..
"This is a bug in Rojo. Please file an issue:\n" ..
"https://github.com/rojo-rbx/rojo/issues",
"Rojo detected an invariant violation within itself:\n"
.. "%s\n\n"
.. "This is a bug in Rojo. Please file an issue:\n"
.. "https://github.com/rojo-rbx/rojo/issues",
message
)

View File

@@ -2,12 +2,10 @@
local plugin = plugin or script:FindFirstAncestorWhichIsA("Plugin")
local widget = nil
if plugin then
widget = plugin:CreateDockWidgetPluginGui("Rojo_soundPlayer", DockWidgetPluginGuiInfo.new(
Enum.InitialDockState.Float,
false, true,
10, 10,
10, 10
))
widget = plugin:CreateDockWidgetPluginGui(
"Rojo_soundPlayer",
DockWidgetPluginGuiInfo.new(Enum.InitialDockState.Float, false, true, 10, 10, 10, 10)
)
widget.Name = "Rojo_soundPlayer"
widget.Title = "Rojo Sound Player"
end
@@ -22,7 +20,9 @@ function SoundPlayer.new(settings)
end
function SoundPlayer:play(soundId)
if self.settings and self.settings:get("playSounds") == false then return end
if self.settings and self.settings:get("playSounds") == false then
return
end
local sound = Instance.new("Sound")
sound.SoundId = soundId