Merge branch 'master' of https://github.com/boatbomber/rojo into headless-api

This commit is contained in:
boatbomber 2024-01-02 12:55:57 -05:00
commit e066846456
10 changed files with 83 additions and 77 deletions

View file

@ -12,11 +12,12 @@ on:
jobs: jobs:
build: build:
name: Build and Test name: Build and Test
runs-on: ubuntu-latest runs-on: ${{ matrix.os }}
strategy: strategy:
matrix: matrix:
rust_version: [stable, 1.70.0] rust_version: [stable, 1.70.0]
os: [ubuntu-latest, windows-latest, macos-latest]
steps: steps:
- uses: actions/checkout@v3 - uses: actions/checkout@v3

View file

@ -2,8 +2,12 @@
## Unreleased Changes ## Unreleased Changes
* Added headless API for Studio companion plugins. ([#631]) * Added headless API for Studio companion plugins. ([#631])
* Improved the visualization for array properties like Tags ([#829])
* Significantly improved performance of `rojo serve`, `rojo build --watch`, and `rojo sourcemap --watch` on macOS. [#830]
[#631]: https://github.com/rojo-rbx/rojo/pull/631 [#631]: https://github.com/rojo-rbx/rojo/pull/631
[#829]: https://github.com/rojo-rbx/rojo/pull/829
[#830]: https://github.com/rojo-rbx/rojo/pull/830
## [7.4.0-rc3] - October 25, 2023 ## [7.4.0-rc3] - October 25, 2023
* Changed `sourcemap --watch` to only generate the sourcemap when it's necessary ([#800]) * Changed `sourcemap --watch` to only generate the sourcemap when it's necessary ([#800])
@ -142,7 +146,6 @@
* Add buttons for navigation on the Connected page ([#722]) * Add buttons for navigation on the Connected page ([#722])
### Fixes ### Fixes
* Significantly improved performance of `rojo serve` and `rojo build` on macOS. [#783]
* Significantly improved performance of `rojo sourcemap` ([#668]) * Significantly improved performance of `rojo sourcemap` ([#668])
* Fixed the diff visualizer of connected sessions. ([#674]) * Fixed the diff visualizer of connected sessions. ([#674])
* Fixed disconnected session activity. ([#675]) * Fixed disconnected session activity. ([#675])
@ -176,7 +179,6 @@
[#770]: https://github.com/rojo-rbx/rojo/pull/770 [#770]: https://github.com/rojo-rbx/rojo/pull/770
[#771]: https://github.com/rojo-rbx/rojo/pull/771 [#771]: https://github.com/rojo-rbx/rojo/pull/771
[#774]: https://github.com/rojo-rbx/rojo/pull/774 [#774]: https://github.com/rojo-rbx/rojo/pull/774
[#783]: https://github.com/rojo-rbx/rojo/pull/783
[rbx-dom#299]: https://github.com/rojo-rbx/rbx-dom/pull/299 [rbx-dom#299]: https://github.com/rojo-rbx/rbx-dom/pull/299
[rbx-dom#296]: https://github.com/rojo-rbx/rbx-dom/pull/296 [rbx-dom#296]: https://github.com/rojo-rbx/rbx-dom/pull/296

1
Cargo.lock generated
View file

@ -1852,7 +1852,6 @@ dependencies = [
"log", "log",
"maplit", "maplit",
"memofs", "memofs",
"notify",
"num_cpus", "num_cpus",
"opener", "opener",
"paste", "paste",

View file

@ -69,7 +69,6 @@ hyper = { version = "0.14.13", features = ["server", "tcp", "http1"] }
jod-thread = "0.1.2" jod-thread = "0.1.2"
log = "0.4.14" log = "0.4.14"
maplit = "1.0.2" maplit = "1.0.2"
notify = "4.0.17"
num_cpus = "1.15.0" num_cpus = "1.15.0"
opener = "0.5.0" opener = "0.5.0"
rayon = "1.7.0" rayon = "1.7.0"

View file

@ -1,7 +1,9 @@
# memofs Changelog # memofs Changelog
## Unreleased Changes ## Unreleased Changes
* Changed the `StdBackend` file watcher to use `PollWatcher` on macOS. * Changed `StdBackend` file watching component to use minimal recursive watches. [#830]
[#830]: https://github.com/rojo-rbx/rojo/pull/830
## 0.2.0 (2021-08-23) ## 0.2.0 (2021-08-23)
* Updated to `crossbeam-channel` 0.5.1. * Updated to `crossbeam-channel` 0.5.1.

View file

@ -1,38 +1,24 @@
use std::io; use std::path::{Path, PathBuf};
use std::path::Path;
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
use std::{collections::HashSet, io};
use crossbeam_channel::Receiver; use crossbeam_channel::Receiver;
use notify::{DebouncedEvent, RecursiveMode, Watcher}; use notify::{watcher, DebouncedEvent, RecommendedWatcher, RecursiveMode, Watcher};
#[cfg(target_os = "macos")]
use notify::PollWatcher;
#[cfg(not(target_os = "macos"))]
use notify::{watcher, RecommendedWatcher};
use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent}; use crate::{DirEntry, Metadata, ReadDir, VfsBackend, VfsEvent};
/// `VfsBackend` that uses `std::fs` and the `notify` crate. /// `VfsBackend` that uses `std::fs` and the `notify` crate.
pub struct StdBackend { pub struct StdBackend {
// We use PollWatcher on macos because using the KQueue watcher
// can cause some gnarly performance problems.
#[cfg(target_os = "macos")]
watcher: PollWatcher,
#[cfg(not(target_os = "macos"))]
watcher: RecommendedWatcher, watcher: RecommendedWatcher,
watcher_receiver: Receiver<VfsEvent>, watcher_receiver: Receiver<VfsEvent>,
watches: HashSet<PathBuf>,
} }
impl StdBackend { impl StdBackend {
pub fn new() -> StdBackend { pub fn new() -> StdBackend {
let (notify_tx, notify_rx) = mpsc::channel(); let (notify_tx, notify_rx) = mpsc::channel();
#[cfg(target_os = "macos")]
let watcher = PollWatcher::new(notify_tx, Duration::from_millis(50)).unwrap();
#[cfg(not(target_os = "macos"))]
let watcher = watcher(notify_tx, Duration::from_millis(50)).unwrap(); let watcher = watcher(notify_tx, Duration::from_millis(50)).unwrap();
let (tx, rx) = crossbeam_channel::unbounded(); let (tx, rx) = crossbeam_channel::unbounded();
@ -63,6 +49,7 @@ impl StdBackend {
Self { Self {
watcher, watcher,
watcher_receiver: rx, watcher_receiver: rx,
watches: HashSet::new(),
} }
} }
} }
@ -112,12 +99,22 @@ impl VfsBackend for StdBackend {
} }
fn watch(&mut self, path: &Path) -> io::Result<()> { fn watch(&mut self, path: &Path) -> io::Result<()> {
if self.watches.contains(path)
|| path
.ancestors()
.any(|ancestor| self.watches.contains(ancestor))
{
Ok(())
} else {
self.watches.insert(path.to_path_buf());
self.watcher self.watcher
.watch(path, RecursiveMode::NonRecursive) .watch(path, RecursiveMode::Recursive)
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner)) .map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))
} }
}
fn unwatch(&mut self, path: &Path) -> io::Result<()> { fn unwatch(&mut self, path: &Path) -> io::Result<()> {
self.watches.remove(path);
self.watcher self.watcher
.unwatch(path) .unwatch(path)
.map_err(|inner| io::Error::new(io::ErrorKind::Other, inner)) .map_err(|inner| io::Error::new(io::ErrorKind::Other, inner))

View file

@ -53,8 +53,23 @@ local function DisplayValue(props)
elseif next(props.value) == nil then elseif next(props.value) == nil then
-- If it's empty, show empty braces -- If it's empty, show empty braces
textRepresentation = "{}" textRepresentation = "{}"
elseif next(props.value) == 1 then
-- We don't need to support mixed tables, so checking the first key is enough
-- to determine if it's a simple array
local out, i = table.create(#props.value), 0
for k, v in props.value do
i += 1
-- Wrap strings in quotes
if type(v) == "string" then
v = '"' .. v .. '"'
end
out[i] = tostring(v)
end
textRepresentation = "{ " .. table.concat(out, ", ") .. " }"
else else
-- If it has children, list them out -- Otherwise, show the table contents as a dictionary
local out, i = {}, 0 local out, i = {}, 0
for k, v in pairs(props.value) do for k, v in pairs(props.value) do
i += 1 i += 1

View file

@ -13,7 +13,7 @@ function gatherAssetUrlsRecursive(currentTable, currentUrls)
if typeof(value) == "string" then if typeof(value) == "string" then
table.insert(currentUrls, value) table.insert(currentUrls, value)
elseif typeof(value) == "table" then elseif typeof(value) == "table" then
gatherAssetUrlsRecursive(value) gatherAssetUrlsRecursive(value, currentUrls)
end end
end end

View file

@ -1,6 +1,5 @@
use std::{ use std::{
fs, fs,
path::PathBuf,
sync::{Arc, Mutex}, sync::{Arc, Mutex},
}; };
@ -126,14 +125,41 @@ impl JobThreadContext {
// For a given VFS event, we might have many changes to different parts // For a given VFS event, we might have many changes to different parts
// of the tree. Calculate and apply all of these changes. // of the tree. Calculate and apply all of these changes.
let applied_patches = match event { let applied_patches = match event {
VfsEvent::Write(path) => { VfsEvent::Create(path) | VfsEvent::Remove(path) | VfsEvent::Write(path) => {
if path.is_dir() { let mut tree = self.tree.lock().unwrap();
return; let mut applied_patches = Vec::new();
// Find the nearest ancestor to this path that has
// associated instances in the tree. This helps make sure
// that we handle additions correctly, especially if we
// receive events for descendants of a large tree being
// created all at once.
let mut current_path = path.as_path();
let affected_ids = loop {
let ids = tree.get_ids_at_path(&current_path);
log::trace!("Path {} affects IDs {:?}", current_path.display(), ids);
if !ids.is_empty() {
break ids.to_vec();
} }
on_vfs_event(path, &self.tree, &self.vfs)
log::trace!("Trying parent path...");
match current_path.parent() {
Some(parent) => current_path = parent,
None => break Vec::new(),
} }
VfsEvent::Create(path) | VfsEvent::Remove(path) => { };
on_vfs_event(path, &self.tree, &self.vfs)
for id in affected_ids {
if let Some(patch) = compute_and_apply_changes(&mut tree, &self.vfs, id) {
if !patch.is_empty() {
applied_patches.push(patch);
}
}
}
applied_patches
} }
_ => { _ => {
log::warn!("Unhandled VFS event: {:?}", event); log::warn!("Unhandled VFS event: {:?}", event);
@ -236,45 +262,6 @@ impl JobThreadContext {
} }
} }
// Find the nearest ancestor to this path that has
// associated instances in the tree. This helps make sure
// that we handle additions correctly, especially if we
// receive events for descendants of a large tree being
// created all at once.
fn on_vfs_event(
path: PathBuf,
tree: &Arc<Mutex<RojoTree>>,
vfs: &Arc<Vfs>,
) -> Vec<AppliedPatchSet> {
let mut tree = tree.lock().unwrap();
let mut applied_patches = Vec::new();
let mut current_path = path.as_path();
let affected_ids = loop {
let ids = tree.get_ids_at_path(current_path);
log::trace!("Path {} affects IDs {:?}", current_path.display(), ids);
if !ids.is_empty() {
break ids.to_vec();
}
log::trace!("Trying parent path...");
match current_path.parent() {
Some(parent) => current_path = parent,
None => break Vec::new(),
}
};
for id in affected_ids {
if let Some(patch) = compute_and_apply_changes(&mut tree, vfs, id) {
if !patch.is_empty() {
applied_patches.push(patch);
}
}
}
applied_patches
}
fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: Ref) -> Option<AppliedPatchSet> { fn compute_and_apply_changes(tree: &mut RojoTree, vfs: &Vfs, id: Ref) -> Option<AppliedPatchSet> {
let metadata = tree let metadata = tree
.get_metadata(id) .get_metadata(id)

View file

@ -66,7 +66,11 @@ impl TestServeSession {
let source_path = Path::new(SERVE_TESTS_PATH).join(name); let source_path = Path::new(SERVE_TESTS_PATH).join(name);
let dir = tempdir().expect("Couldn't create temporary directory"); let dir = tempdir().expect("Couldn't create temporary directory");
let project_path = dir.path().join(name); let project_path = dir
.path()
.canonicalize()
.expect("Couldn't canonicalize temporary directory path")
.join(name);
let source_is_file = fs::metadata(&source_path).unwrap().is_file(); let source_is_file = fs::metadata(&source_path).unwrap().is_file();