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,18 +140,15 @@ 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)
if body.sessionId ~= self.__sessionId then
return Promise.reject("Server changed ID")
end
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
assert(validateApiRead(body))
assert(validateApiRead(body))
return body
end)
return body
end)
end
function ApiContext:write(patch)
@@ -189,28 +185,24 @@ function ApiContext:write(patch)
body = Http.jsonEncode(body)
return Http.post(url, body)
:andThen(rejectFailedRequests)
:andThen(Http.Response.json)
:andThen(function(body)
Log.info("Write response: {:?}", body)
return Http.post(url, body):andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
Log.info("Write response: {:?}", body)
return body
end)
return body
end)
end
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)
if err.type == Http.Error.Kind.Timeout and self.__connected then
return sendRequest()
end
local request = Http.get(url):catch(function(err)
if err.type == Http.Error.Kind.Timeout and self.__connected then
return sendRequest()
end
return Promise.reject(err)
end)
return Promise.reject(err)
end)
Log.trace("Tracking request {}", request)
self.__activeRequests[request] = true
@@ -222,35 +214,29 @@ function ApiContext:retrieveMessages()
end)
end
return sendRequest()
:andThen(rejectFailedRequests)
:andThen(Http.Response.json)
:andThen(function(body)
if body.sessionId ~= self.__sessionId then
return Promise.reject("Server changed ID")
end
return sendRequest():andThen(rejectFailedRequests):andThen(Http.Response.json):andThen(function(body)
if body.sessionId ~= self.__sessionId then
return Promise.reject("Server changed ID")
end
assert(validateApiSubscribe(body))
assert(validateApiSubscribe(body))
self:setMessageCursor(body.messageCursor)
self:setMessageCursor(body.messageCursor)
return body.messages
end)
return body.messages
end)
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)
if body.sessionId ~= self.__sessionId then
return Promise.reject("Server changed ID")
end
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
return nil
end)
return nil
end)
end
return ApiContext

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, {
frequency = 6,
dampingRatio = 1.1,
})
)
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

@@ -22,7 +22,7 @@ function CodeLabel:didMount()
end
function CodeLabel:didUpdate()
self:updateHighlights()
self:updateHighlights()
end
function CodeLabel:updateHighlights()

View File

@@ -36,12 +36,10 @@ function Dropdown:didUpdate(prevProps)
})
end
self.openMotor:setGoal(
Flipper.Spring.new(self.state.open and 1 or 0, {
frequency = 6,
dampingRatio = 1.1,
})
)
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,
})
@@ -88,7 +85,7 @@ function Dropdown:render()
end
return e("ImageButton", {
Size = UDim2.new(0, width+50, 0, 28),
Size = UDim2.new(0, width + 50, 0, 28),
Position = self.props.position,
AnchorPoint = self.props.anchorPoint,
LayoutOrder = self.props.layoutOrder,
@@ -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,40 +135,42 @@ function Dropdown:render()
TextTransparency = self.props.transparency,
}),
}),
Options = if self.state.open then e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = theme.BackgroundColor,
position = UDim2.new(1, 0, 1, 3),
size = self.openBinding:map(function(a)
return UDim2.new(1, 0, a*math.min(3, #self.props.options), 0)
end),
anchorPoint = Vector2.new(1, 0),
}, {
Border = e(SlicedImage, {
slice = Assets.Slices.RoundedBorder,
color = theme.BorderColor,
transparency = self.props.transparency,
size = UDim2.new(1, 0, 1, 0),
}),
ScrollingFrame = e(ScrollingFrame, {
size = UDim2.new(1, -4, 1, -4),
position = UDim2.new(0, 2, 0, 2),
transparency = self.props.transparency,
contentSize = self.contentSize,
Options = if self.state.open
then e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = theme.BackgroundColor,
position = UDim2.new(1, 0, 1, 3),
size = self.openBinding:map(function(a)
return UDim2.new(1, 0, a * math.min(3, #self.props.options), 0)
end),
anchorPoint = Vector2.new(1, 0),
}, {
Layout = e("UIListLayout", {
VerticalAlignment = Enum.VerticalAlignment.Top,
FillDirection = Enum.FillDirection.Vertical,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 0),
[Roact.Change.AbsoluteContentSize] = function(object)
self.setContentSize(object.AbsoluteContentSize)
end,
Border = e(SlicedImage, {
slice = Assets.Slices.RoundedBorder,
color = theme.BorderColor,
transparency = self.props.transparency,
size = UDim2.new(1, 0, 1, 0),
}),
Roact.createFragment(optionButtons),
}),
}) else nil,
ScrollingFrame = e(ScrollingFrame, {
size = UDim2.new(1, -4, 1, -4),
position = UDim2.new(0, 2, 0, 2),
transparency = self.props.transparency,
contentSize = self.contentSize,
}, {
Layout = e("UIListLayout", {
VerticalAlignment = Enum.VerticalAlignment.Top,
FillDirection = Enum.FillDirection.Vertical,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 0),
[Roact.Change.AbsoluteContentSize] = function(object)
self.setContentSize(object.AbsoluteContentSize)
end,
}),
Roact.createFragment(optionButtons),
}),
})
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

@@ -10,11 +10,15 @@ local StudioPluginContext = require(script.Parent.StudioPluginContext)
local e = Roact.createElement
local StudioPluginAction = Roact.Component:extend("StudioPluginAction")
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, {
plugin = plugin,
}))
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, {
toolbar = toolbar,
}))
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, {
plugin = plugin,
}))
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

@@ -38,22 +38,22 @@ end
function TextInput:render()
return Theme.with(function(theme)
theme = theme.TextInput
theme = theme.TextInput
local bindingHover = bindingUtil.deriveProperty(self.binding, "hover")
local bindingEnabled = bindingUtil.deriveProperty(self.binding, "enabled")
return e(SlicedImage, {
slice = Assets.Slices.RoundedBorder,
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor),
transparency = self.props.transparency,
return e(SlicedImage, {
slice = Assets.Slices.RoundedBorder,
color = bindingUtil.mapLerp(bindingEnabled, theme.Enabled.BorderColor, theme.Disabled.BorderColor),
transparency = self.props.transparency,
size = self.props.size or UDim2.new(1, 0, 1, 0),
position = self.props.position,
layoutOrder = self.props.layoutOrder,
anchorPoint = self.props.anchorPoint,
}, {
HoverOverlay = e(SlicedImage, {
size = self.props.size or UDim2.new(1, 0, 1, 0),
position = self.props.position,
layoutOrder = self.props.layoutOrder,
anchorPoint = self.props.anchorPoint,
}, {
HoverOverlay = e(SlicedImage, {
slice = Assets.Slices.RoundedBackground,
color = theme.ActionFillColor,
transparency = Roact.joinBindings({
@@ -67,36 +67,40 @@ function TextInput:render()
size = UDim2.new(1, 0, 1, 0),
zIndex = -1,
}),
Input = e("TextBox", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Text = self.props.text,
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),
TextSize = 18,
TextEditable = self.props.enabled,
ClearTextOnFocus = self.props.clearTextOnFocus,
Input = e("TextBox", {
BackgroundTransparency = 1,
Size = UDim2.fromScale(1, 1),
Text = self.props.text,
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
),
TextSize = 18,
TextEditable = self.props.enabled,
ClearTextOnFocus = self.props.clearTextOnFocus,
[Roact.Event.MouseEnter] = function()
self.motor:setGoal({
hover = Flipper.Spring.new(1, SPRING_PROPS),
})
end,
[Roact.Event.MouseEnter] = function()
self.motor:setGoal({
hover = Flipper.Spring.new(1, SPRING_PROPS),
})
end,
[Roact.Event.MouseLeave] = function()
self.motor:setGoal({
hover = Flipper.Spring.new(0, SPRING_PROPS),
})
end,
[Roact.Event.MouseLeave] = function()
self.motor:setGoal({
hover = Flipper.Spring.new(0, SPRING_PROPS),
})
end,
[Roact.Event.FocusLost] = function(rbx)
self.props.onEntered(rbx.Text)
end,
}),
Children = Roact.createFragment(self.props[Roact.Children]),
})
[Roact.Event.FocusLost] = function(rbx)
self.props.onEntered(rbx.Text)
end,
}),
Children = Roact.createFragment(self.props[Roact.Children]),
})
end)
end

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,28 +37,24 @@ function Notification:init()
end
function Notification:dismiss()
self.motor:setGoal(
Flipper.Spring.new(0, {
frequency = 5,
dampingRatio = 1,
})
)
self.motor:setGoal(Flipper.Spring.new(0, {
frequency = 5,
dampingRatio = 1,
}))
end
function Notification:didMount()
self.motor:setGoal(
Flipper.Spring.new(1, {
frequency = 3,
dampingRatio = 1,
})
)
self.motor:setGoal(Flipper.Spring.new(1, {
frequency = 3,
dampingRatio = 1,
}))
self.props.soundPlayer:play(Assets.Sounds.Notification)
self.timeout = task.spawn(function()
local clock = os.clock()
local seen = false
while task.wait(1/10) do
while task.wait(1 / 10) do
local now = os.clock()
local dt = now - clock
clock = now
@@ -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,28 +174,30 @@ function Notification:render()
LayoutOrder = 1,
BackgroundTransparency = 1,
}),
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),
BackgroundTransparency = 1,
}, {
Layout = e("UIListLayout", {
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Right,
VerticalAlignment = Enum.VerticalAlignment.Center,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 5),
}),
Buttons = Roact.createFragment(actionButtons),
}) else nil,
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),
BackgroundTransparency = 1,
}, {
Layout = e("UIListLayout", {
FillDirection = Enum.FillDirection.Horizontal,
HorizontalAlignment = Enum.HorizontalAlignment.Right,
VerticalAlignment = Enum.VerticalAlignment.Center,
SortOrder = Enum.SortOrder.LayoutOrder,
Padding = UDim.new(0, 5),
}),
Buttons = Roact.createFragment(actionButtons),
})
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, {
transparency = transparency,
}))
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, {
frequency = 6,
dampingRatio = 1,
})
)
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,16 +169,15 @@ 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"
)
else "")
else "")
.. "</i>"
end
@@ -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
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,40 +81,39 @@ function Setting:render()
end,
}),
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,
transparency = self.props.transparency,
onClick = function(option)
Settings:set(self.props.id, option)
end,
})
else
e(Checkbox, {
locked = self.props.locked,
active = self.state.setting,
transparency = self.props.transparency,
onClick = function()
local currentValue = Settings:get(self.props.id)
Settings:set(self.props.id, not currentValue)
end,
}),
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,
transparency = self.props.transparency,
onClick = function(option)
Settings:set(self.props.id, option)
end,
})
else e(Checkbox, {
locked = self.props.locked,
active = self.state.setting,
transparency = self.props.transparency,
onClick = function()
local currentValue = Settings:get(self.props.id)
Settings:set(self.props.id, not currentValue)
end,
}),
Reset = if self.props.onReset then e(IconButton, {
icon = Assets.Images.Icons.Reset,
iconSize = 24,
color = theme.BackButtonColor,
transparency = self.props.transparency,
visible = self.props.showReset,
layoutOrder = -1,
Reset = if self.props.onReset
then e(IconButton, {
icon = Assets.Images.Icons.Reset,
iconSize = 24,
color = theme.BackButtonColor,
transparency = self.props.transparency,
visible = self.props.showReset,
layoutOrder = -1,
onClick = self.props.onReset,
}) else nil,
onClick = self.props.onReset,
})
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

@@ -5,7 +5,7 @@ local isDevBuild = script.Parent.Parent:FindFirstChild("ROJO_DEV_BUILD") ~= nil
return strict("Config", {
isDevBuild = isDevBuild,
codename = "Epiphany",
version = {7, 3, 0},
version = { 7, 3, 0 },
expectedServerVersionString = "7.2 or newer",
protocolVersion = 4,
defaultHost = "localhost",

View File

@@ -30,4 +30,4 @@ end
return {
None = None,
merge = merge,
}
}

View File

@@ -66,7 +66,7 @@ function InstanceMap:__fmtDebug(output)
for id, instance in pairs(self.fromIds) do
local label = string.format("%s (%s)", instance:GetFullName(), instance.ClassName)
table.insert(entries, {id, label})
table.insert(entries, { id, label })
end
table.sort(entries, function(a, b)

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(
"- Add instance %q (ClassName %q) to %s",
virtualInstance.Name, virtualInstance.ClassName, parentDisplayName))
table.insert(
statements,
string.format(
"- Add instance %q (ClassName %q) to %s",
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

@@ -83,11 +83,11 @@ return function()
ClassName = "Model",
Name = "Child",
Parent = "ROOT",
Children = {"GRANDCHILD"},
Children = { "GRANDCHILD" },
Properties = {},
}
patch.added["GRANDCHILD"] = {
patch.added["GRANDCHILD"] = {
Id = "GRANDCHILD",
ClassName = "Part",
Name = "Grandchild",
@@ -193,4 +193,4 @@ return function()
assert(newChild ~= nil, "expected child to be present")
assert(newChild == child, "expected child to be preserved")
end)
end
end

View File

@@ -30,10 +30,11 @@ local function decodeValue(encodedValue, instanceMap)
local ok, decodedValue = RbxDom.EncodedValue.decode(encodedValue)
if not ok then
return false, Error.new(Error.CannotDecodeValue, {
encodedValue = encodedValue,
innerError = decodedValue,
})
return false,
Error.new(Error.CannotDecodeValue, {
encodedValue = encodedValue,
innerError = decodedValue,
})
end
return true, 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
@@ -62,7 +64,7 @@ local function trueEquals(a, b): boolean
-- For CFrames, compare to components with epsilon of 0.0001 to avoid floating point inequality
elseif typeA == "CFrame" and typeB == "CFrame" then
local aComponents, bComponents = {a:GetComponents()}, {b:GetComponents()}
local aComponents, bComponents = { a:GetComponents() }, { b:GetComponents() }
for i, aComponent in aComponents do
if not fuzzyEq(aComponent, bComponents[i], 0.0001) then
return false
@@ -72,7 +74,7 @@ local function trueEquals(a, b): boolean
-- For Vector3s, compare to components with epsilon of 0.0001 to avoid floating point inequality
elseif typeA == "Vector3" and typeB == "Vector3" then
local aComponents, bComponents = {a.X, a.Y, a.Z}, {b.X, b.Y, b.Z}
local aComponents, bComponents = { a.X, a.Y, a.Z }, { b.X, b.Y, b.Z }
for i, aComponent in aComponents do
if not fuzzyEq(aComponent, bComponents[i], 0.0001) then
return false
@@ -82,14 +84,13 @@ local function trueEquals(a, b): boolean
-- For Vector2s, compare to components with epsilon of 0.0001 to avoid floating point inequality
elseif typeA == "Vector2" and typeB == "Vector2" then
local aComponents, bComponents = {a.X, a.Y}, {b.X, b.Y}
local aComponents, bComponents = { a.X, a.Y }, { b.X, b.Y }
for i, aComponent in aComponents do
if not fuzzyEq(aComponent, bComponents[i], 0.0001) then
return false
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

@@ -264,7 +264,7 @@ return function()
ClassName = "Folder",
Name = "Folder",
Properties = {},
Children = {"CHILD"},
Children = { "CHILD" },
},
CHILD = {

View File

@@ -14,17 +14,19 @@ 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, {
className = instance.ClassName,
propertyName = propertyName,
})
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, {
className = instance.ClassName,
propertyName = propertyName,
})
return false,
Error.new(Error.UnreadableProperty, {
className = instance.ClassName,
propertyName = propertyName,
})
end
local success, valueOrErr = descriptor:read(instance)
@@ -35,23 +37,26 @@ 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, {
className = instance.ClassName,
propertyName = propertyName,
})
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, {
className = instance.ClassName,
propertyName = propertyName,
})
end
return false, Error.new(Error.OtherPropertyError, {
className = instance.ClassName,
propertyName = propertyName,
})
end
return true, valueOrErr

View File

@@ -47,4 +47,4 @@ local function hydrate(instanceMap, virtualInstances, rootId, rootInstance)
end
end
return hydrate
return hydrate

View File

@@ -91,7 +91,7 @@ return function()
ClassName = "Folder",
Name = "Root",
Properties = {},
Children = {"CHILD1", "CHILD2"},
Children = { "CHILD1", "CHILD2" },
},
CHILD1 = {
@@ -126,4 +126,4 @@ return function()
expect(knownInstances.fromIds["CHILD1"]).to.equal(child1)
expect(knownInstances.fromIds["CHILD2"]).to.equal(child2)
end)
end
end

View File

@@ -80,7 +80,7 @@ return function()
ClassName = "Folder",
Name = "Parent",
Properties = {},
Children = {"CHILD"},
Children = { "CHILD" },
},
CHILD = {
@@ -112,7 +112,7 @@ return function()
ClassName = "Folder",
Name = "Parent",
Properties = {},
Children = {"CHILD"},
Children = { "CHILD" },
},
CHILD = {
@@ -147,7 +147,7 @@ return function()
Properties = {
Value = {
Type = "Vector3",
Value = {1, 2, 3},
Value = { 1, 2, 3 },
},
},
Children = {},
@@ -182,7 +182,7 @@ return function()
ClassName = "Folder",
Name = "Root",
Properties = {},
Children = {"CHILD"},
Children = { "CHILD" },
},
CHILD = {
@@ -247,7 +247,7 @@ return function()
ClassName = "Folder",
Name = "Root",
Properties = {},
Children = {"CHILD_A", "CHILD_B"},
Children = { "CHILD_A", "CHILD_B" },
},
CHILD_A = {
@@ -297,7 +297,7 @@ return function()
Ref = "CHILD",
},
},
Children = {"CHILD"},
Children = { "CHILD" },
},
CHILD = {

View File

@@ -21,26 +21,29 @@ local function setProperty(instance, propertyName, value)
end
if descriptor.scriptability == "None" or descriptor.scriptability == "Read" then
return false, Error.new(Error.UnwritableProperty, {
className = instance.ClassName,
propertyName = propertyName,
})
return false,
Error.new(Error.UnwritableProperty, {
className = instance.ClassName,
propertyName = propertyName,
})
end
local ok, err = descriptor:write(instance, 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, {
className = instance.ClassName,
propertyName = propertyName,
})
end
return false, Error.new(Error.OtherPropertyError, {
className = instance.ClassName,
propertyName = propertyName,
})
end
return true

View File

@@ -77,15 +77,13 @@ function ServeSession.new(options)
local connections = {}
local connection = StudioService
:GetPropertyChangedSignal("ActiveScript")
:Connect(function()
local activeScript = StudioService.ActiveScript
local connection = StudioService:GetPropertyChangedSignal("ActiveScript"):Connect(function()
local activeScript = StudioService.ActiveScript
if activeScript ~= nil then
self:__onActiveScriptChanged(activeScript)
end
end)
if activeScript ~= nil then
self:__onActiveScriptChanged(activeScript)
end
end)
table.insert(connections, connection)
self = {
@@ -139,16 +137,16 @@ 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()
self:__setStatus(Status.Connected, serverInfo.projectName)
return self:__initialSync(serverInfo):andThen(function()
self:__setStatus(Status.Connected, serverInfo.projectName)
return self:__mainSyncLoop()
end)
return self:__mainSyncLoop()
end)
end)
:catch(function(err)
if self.__status ~= Status.Disconnected then
@@ -211,97 +209,93 @@ function ServeSession:__onActiveScriptChanged(activeScript)
end
function ServeSession:__initialSync(serverInfo)
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)
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)
-- For any instances that line up with the Rojo server's view, start
-- tracking them in the reconciler.
Log.trace("Matching existing Roblox instances to Rojo IDs")
self.__reconciler:hydrate(readResponseBody.instances, serverInfo.rootInstanceId, game)
-- For any instances that line up with the Rojo server's view, start
-- tracking them in the reconciler.
Log.trace("Matching existing Roblox instances to Rojo IDs")
self.__reconciler:hydrate(readResponseBody.instances, serverInfo.rootInstanceId, game)
-- 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
)
-- 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)
if not success then
Log.error("Could not compute a diff to catch up to the Rojo server: {:#?}", catchUpPatch)
end
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
-- 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.
return Promise.reject(
"Cannot sync a model as a place."
for _, update in catchUpPatch.updated do
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.
return Promise.reject(
"Cannot sync a model as a place."
.. "\nEnsure Rojo is serving a project file that has a DataModel at the root of its tree and try again."
.. "\nSee project file docs: https://rojo.space/docs/v7/project-format/"
)
)
end
end
Log.trace("Computed hydration patch: {:#?}", debugPatch(catchUpPatch))
local userDecision = "Accept"
if self.__userConfirmCallback ~= nil then
userDecision = self.__userConfirmCallback(self.__instanceMap, catchUpPatch, serverInfo)
end
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
local inversePatch = PatchSet.newEmpty()
-- Send back the current properties
for _, change in catchUpPatch.updated do
local instance = self.__instanceMap.fromIds[change.id]
if not instance then
continue
end
local update = encodePatchUpdate(instance, change.id, change.changedProperties)
table.insert(inversePatch.updated, update)
end
-- Add the removed instances back to Rojo
-- selene:allow(empty_if, unused_variable)
for _, instance in catchUpPatch.removed do
-- TODO: Generate ID for our instance and add it to inversePatch.added
end
-- Remove the additions we've rejected
for id, _change in catchUpPatch.added do
table.insert(inversePatch.removed, id)
end
Log.trace("Computed hydration patch: {:#?}", debugPatch(catchUpPatch))
self.__apiContext:write(inversePatch)
elseif userDecision == "Accept" then
local unappliedPatch = self.__reconciler:applyPatch(catchUpPatch)
local userDecision = "Accept"
if self.__userConfirmCallback ~= nil then
userDecision = self.__userConfirmCallback(self.__instanceMap, catchUpPatch, serverInfo)
if not PatchSet.isEmpty(unappliedPatch) then
Log.warn(
"Could not apply all changes requested by the Rojo server:\n{}",
PatchSet.humanSummary(self.__instanceMap, unappliedPatch)
)
end
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
local inversePatch = PatchSet.newEmpty()
-- Send back the current properties
for _, change in catchUpPatch.updated do
local instance = self.__instanceMap.fromIds[change.id]
if not instance then continue end
local update = encodePatchUpdate(instance, change.id, change.changedProperties)
table.insert(inversePatch.updated, update)
end
-- Add the removed instances back to Rojo
-- selene:allow(empty_if, unused_variable)
for _, instance in catchUpPatch.removed do
-- TODO: Generate ID for our instance and add it to inversePatch.added
end
-- Remove the additions we've rejected
for id, _change in catchUpPatch.added do
table.insert(inversePatch.removed, id)
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))
end
end
end)
end
end)
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

@@ -43,4 +43,4 @@ function Version.display(version)
return output
end
return Version
return Version

View File

@@ -2,27 +2,27 @@ return function()
local Version = require(script.Parent.Version)
it("should compare equal versions", function()
expect(Version.compare({1, 2, 3}, {1, 2, 3})).to.equal(0)
expect(Version.compare({0, 4, 0}, {0, 4})).to.equal(0)
expect(Version.compare({0, 0, 123}, {0, 0, 123})).to.equal(0)
expect(Version.compare({26}, {26})).to.equal(0)
expect(Version.compare({26, 42}, {26, 42})).to.equal(0)
expect(Version.compare({1, 0, 0}, {1})).to.equal(0)
expect(Version.compare({ 1, 2, 3 }, { 1, 2, 3 })).to.equal(0)
expect(Version.compare({ 0, 4, 0 }, { 0, 4 })).to.equal(0)
expect(Version.compare({ 0, 0, 123 }, { 0, 0, 123 })).to.equal(0)
expect(Version.compare({ 26 }, { 26 })).to.equal(0)
expect(Version.compare({ 26, 42 }, { 26, 42 })).to.equal(0)
expect(Version.compare({ 1, 0, 0 }, { 1 })).to.equal(0)
end)
it("should compare newer, older versions", function()
expect(Version.compare({1}, {0})).to.equal(1)
expect(Version.compare({1, 1}, {1, 0})).to.equal(1)
expect(Version.compare({ 1 }, { 0 })).to.equal(1)
expect(Version.compare({ 1, 1 }, { 1, 0 })).to.equal(1)
end)
it("should compare different major versions", function()
expect(Version.compare({1, 3, 2}, {2, 2, 1})).to.equal(-1)
expect(Version.compare({1, 2}, {2, 1})).to.equal(-1)
expect(Version.compare({1}, {2})).to.equal(-1)
expect(Version.compare({ 1, 3, 2 }, { 2, 2, 1 })).to.equal(-1)
expect(Version.compare({ 1, 2 }, { 2, 1 })).to.equal(-1)
expect(Version.compare({ 1 }, { 2 })).to.equal(-1)
end)
it("should compare different minor versions", function()
expect(Version.compare({1, 2, 3}, {1, 3, 2})).to.equal(-1)
expect(Version.compare({50, 1}, {50, 2})).to.equal(-1)
expect(Version.compare({ 1, 2, 3 }, { 1, 3, 2 })).to.equal(-1)
expect(Version.compare({ 50, 1 }, { 50, 2 })).to.equal(-1)
end)
end

View File

@@ -50,4 +50,4 @@ local function createSignal()
}
end
return createSignal
return createSignal

View File

@@ -5,23 +5,23 @@
--]]
return {
["0"] = true, -- Local file
["95206881"] = true, -- Baseplate
["6560363541"] = true, -- Classic Baseplate
["95206192"] = true, -- Flat Terrain
["13165709401"] = true, -- Modern City
["520390648"] = true, -- Village
["203810088"] = true, -- Castle
["366130569"] = true, -- Suburban
["215383192"] = true, -- Racing
["264719325"] = true, -- Pirate Island
["203812057"] = true, -- Obby
["379736082"] = true, -- Starting Place
["301530843"] = true, -- Line Runner
["92721754"] = true, -- Capture The Flag
["301529772"] = true, -- Team/FFA Arena
["203885589"] = true, -- Combat
["10275826693"] = true, -- Concert
["5353920686"] = true, -- Move It Simulator
["6936227200"] = true, -- Mansion Of Wonder
["0"] = true, -- Local file
["95206881"] = true, -- Baseplate
["6560363541"] = true, -- Classic Baseplate
["95206192"] = true, -- Flat Terrain
["13165709401"] = true, -- Modern City
["520390648"] = true, -- Village
["203810088"] = true, -- Castle
["366130569"] = true, -- Suburban
["215383192"] = true, -- Racing
["264719325"] = true, -- Pirate Island
["203812057"] = true, -- Obby
["379736082"] = true, -- Starting Place
["301530843"] = true, -- Line Runner
["92721754"] = true, -- Capture The Flag
["301529772"] = true, -- Team/FFA Arena
["203885589"] = true, -- Combat
["10275826693"] = true, -- Concert
["5353920686"] = true, -- Move It Simulator
["6936227200"] = true, -- Mansion Of Wonder
}

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

View File

@@ -21,4 +21,4 @@ return function(nameOrTarget, target)
else
return strictInner("<unnamed table>", target)
end
end
end