Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rojo unexpectedly trims trailing newline characters when syncing to Studio. #899

Closed
Noobot9k opened this issue Apr 5, 2024 · 7 comments · Fixed by #903
Closed

Rojo unexpectedly trims trailing newline characters when syncing to Studio. #899

Noobot9k opened this issue Apr 5, 2024 · 7 comments · Fixed by #903
Labels
regression This bug wasn't a bug in a previous version of Rojo scope: cli Relevant to the Rojo CLI type: bug Something happens that shouldn't happen

Comments

@Noobot9k
Copy link

Noobot9k commented Apr 5, 2024

My code in Visual Studio Code:
image
Note that there are two lines in the document, with the second one being blank.

My code when synced to Roblox Studio:

image
Note that there is only one line in the document. The blank line is missing.

This makes doing a comparison between script.sources a problem in some cases such as mine. Some may argue there shouldn't be a trailing newline here, but more predictable behavior would be more ideal to me. It is up to the developer to follow good programming practices.

Workaround:
Add a second trailing empty line in Visual Studio Code so there will be at least one in Roblox Studio.

Let me know if I put this in the wrong place.

@Dekkonot Dekkonot added type: bug Something happens that shouldn't happen regression This bug wasn't a bug in a previous version of Rojo status: needs info We need more info to act on this. labels Apr 9, 2024
@Dekkonot
Copy link
Member

Dekkonot commented Apr 9, 2024

Just to clarify, are you running Rojo 7.4.1? If so, would it be possible for you to check if this happens on 7.4.0?

I think this is probably related to #854, but it's surprising behavior for the implementation we're using so I'd like to make sure.

@kennethloeffler
Copy link
Member

kennethloeffler commented Apr 9, 2024

This is indeed related to a detail of Lines:

pub fn read_to_string_lf_normalized<P: AsRef<Path>>(&self, path: P) -> io::Result<Arc<String>> {
let path = path.as_ref();
let contents = self.inner.lock().unwrap().read_to_string(path)?;
Ok(contents.lines().collect::<Vec<&str>>().join("\n").into())
}

When the iterator encounters a lone newline, it returns an empty string: Rust playground.

We can probably resolve this by doing something like

 pub fn read_to_string_lf_normalized<P: AsRef<Path>>(&self, path: P) -> io::Result<Arc<String>> { 
     let path = path.as_ref(); 
     let contents = self.inner.lock().unwrap().read_to_string(path)?; 
  
-    Ok(contents.lines().collect::<Vec<&str>>().join("\n").into())
+    Ok(contents.replace("\r\n", "\n").into())
 } 

@Noobot9k
Copy link
Author

Just to clarify, are you running Rojo 7.4.1? If so, would it be possible for you to check if this happens on 7.4.0?

I think this is probably related to #854, but it's surprising behavior for the implementation we're using so I'd like to make sure.

Yes, I'm using 7.4.1. I'm pretty new to Rojo so I'm not sure how to downgrade. Could you link me to a resource to learn how?

@Dekkonot
Copy link
Member

Yes, I'm using 7.4.1. I'm pretty new to Rojo so I'm not sure how to downgrade. Could you link me to a resource to learn how?

There's no need to do so now (Ken has identified that this is indeed an issue with our implementation).

For future reference though, it depends upon how you installed Rojo. If you used a toolchain manager like Aftman or Foreman, you'd just change the version in the relevant configuration file. Otherwise you'd have to go to the releases page and download an earlier version from there.

@Dekkonot Dekkonot added scope: cli Relevant to the Rojo CLI and removed status: needs info We need more info to act on this. labels Apr 11, 2024
@Dekkonot
Copy link
Member

We can probably resolve this by doing something like

Sounds like a good idea to me, though I'd be worried about performance. The underlying implementation for String::replace seems to use String::push_str with no preallocation done, which seems... not ideal. Wonder if it's at all possible to implement it more efficiently.

@kennethloeffler
Copy link
Member

kennethloeffler commented Apr 16, 2024

We can probably resolve this by doing something like

Sounds like a good idea to me, though I'd be worried about performance. The underlying implementation for String::replace seems to use String::push_str with no preallocation done, which seems... not ideal. Wonder if it's at all possible to implement it more efficiently.

I benchmarked the two approaches, and on my machine, the str::lines approach is actually slower than str::replace, and when str::replace is given LF input (i.e. there are no matches), very significantly so:

violin

Here is the code I used to benchmark:

use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};

pub fn benchmark_string_normalization(c: &mut Criterion) {
    let mut group = c.benchmark_group("Line Ending Normalization");

    for i in [
        ("lf_input", "Oh My Goodness\n".repeat(2000).as_str()),
        ("crlf_input", "Oh My Goodness\r\n".repeat(2000).as_str()),
    ] {
        group.bench_with_input(BenchmarkId::new("string_replace", i.0), i.1, |b, s| {
            b.iter_batched(
                || s.to_string(),
                |s| {
                    let _ = s.replace("\r\n", "\n");
                },
                BatchSize::SmallInput,
            )
        });

        group.bench_with_input(BenchmarkId::new("string_lines", i.0), i.1, |b, s| {
            b.iter_batched(
                || s.to_string(),
                |s| {
                    s.lines().collect::<Vec<&str>>().join("\n");
                },
                BatchSize::SmallInput,
            )
        });
    }

    group.finish();
}

criterion_group!(benches, benchmark_string_normalization);
criterion_main!(benches);

@Dekkonot
Copy link
Member

I guess that actually makes sense, but it's good to see the data on it. In that case I have no qualms about just using String::replace.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
regression This bug wasn't a bug in a previous version of Rojo scope: cli Relevant to the Rojo CLI type: bug Something happens that shouldn't happen
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants