mirror of
https://github.com/DioxusLabs/dioxus
synced 2024-11-10 06:34:20 +00:00
* Fix #2104: fmt incorrectly using 1-indexing for columns * Clippy...
This commit is contained in:
parent
2dc6cecf2e
commit
d8942a255b
17 changed files with 756 additions and 40 deletions
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -1982,8 +1982,8 @@ name = "dioxus-autofmt"
|
|||
version = "0.5.0-alpha.2"
|
||||
dependencies = [
|
||||
"dioxus-rsx",
|
||||
"prettier-please",
|
||||
"pretty_assertions",
|
||||
"prettyplease",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"serde",
|
||||
|
@ -2045,7 +2045,7 @@ dependencies = [
|
|||
"mlua",
|
||||
"notify",
|
||||
"open",
|
||||
"prettier-please",
|
||||
"prettyplease",
|
||||
"rayon",
|
||||
"reqwest",
|
||||
"rsx-rosetta",
|
||||
|
@ -6670,16 +6670,6 @@ version = "0.1.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
|
||||
|
||||
[[package]]
|
||||
name = "prettier-please"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22020dfcf177fcc7bf5deaf7440af371400c67c0de14c399938d8ed4fb4645d3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"syn 2.0.52",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pretty_assertions"
|
||||
version = "1.4.0"
|
||||
|
|
10
Cargo.toml
10
Cargo.toml
|
@ -89,16 +89,10 @@ rustc-hash = "1.1.0"
|
|||
wasm-bindgen = "0.2.92"
|
||||
html_parser = "0.7.0"
|
||||
thiserror = "1.0.40"
|
||||
prettyplease = { package = "prettier-please", version = "0.2", features = [
|
||||
"verbatim",
|
||||
] }
|
||||
manganis-cli-support = { version = "0.2.1", features = [
|
||||
"html",
|
||||
] }
|
||||
prettyplease = { version = "0.2.16", features = ["verbatim"] }
|
||||
manganis-cli-support = { version = "0.2.1", features = ["html"] }
|
||||
manganis = { version = "0.2.1" }
|
||||
|
||||
interprocess = { version = "1.2.1" }
|
||||
# interprocess = { git = "https://github.com/kotauskas/interprocess" }
|
||||
|
||||
lru = "0.12.2"
|
||||
async-trait = "0.1.77"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{ifmt_to_string, writer::Location, Writer};
|
||||
use crate::{ifmt_to_string, prettier_please::unparse_expr, writer::Location, Writer};
|
||||
use dioxus_rsx::*;
|
||||
use quote::ToTokens;
|
||||
use std::fmt::{Result, Write};
|
||||
|
@ -164,7 +164,7 @@ impl Writer<'_> {
|
|||
let name = &field.name;
|
||||
match &field.content {
|
||||
ContentField::ManExpr(exp) => {
|
||||
let out = prettyplease::unparse_expr(exp);
|
||||
let out = unparse_expr(exp);
|
||||
let mut lines = out.split('\n').peekable();
|
||||
let first = lines.next().unwrap();
|
||||
write!(self.out, "{name}: {first}")?;
|
||||
|
@ -186,7 +186,7 @@ impl Writer<'_> {
|
|||
write!(self.out, "{}", e.to_token_stream())?;
|
||||
}
|
||||
ContentField::OnHandlerRaw(exp) => {
|
||||
let out = prettyplease::unparse_expr(exp);
|
||||
let out = unparse_expr(exp);
|
||||
let mut lines = out.split('\n').peekable();
|
||||
let first = lines.next().unwrap();
|
||||
write!(self.out, "{name}: {first}")?;
|
||||
|
@ -228,7 +228,7 @@ impl Writer<'_> {
|
|||
ContentField::Formatted(s) => ifmt_to_string(s).len() ,
|
||||
ContentField::Shorthand(e) => e.to_token_stream().to_string().len(),
|
||||
ContentField::OnHandlerRaw(exp) | ContentField::ManExpr(exp) => {
|
||||
let formatted = prettyplease::unparse_expr(exp);
|
||||
let formatted = unparse_expr(exp);
|
||||
let len = if formatted.contains('\n') {
|
||||
10000
|
||||
} else {
|
||||
|
@ -242,7 +242,7 @@ impl Writer<'_> {
|
|||
|
||||
match manual_props {
|
||||
Some(p) => {
|
||||
let content = prettyplease::unparse_expr(p);
|
||||
let content = unparse_expr(p);
|
||||
if content.len() + attr_len > 80 {
|
||||
return 100000;
|
||||
}
|
||||
|
@ -264,7 +264,7 @@ impl Writer<'_> {
|
|||
We want to normalize the expr to the appropriate indent level.
|
||||
*/
|
||||
|
||||
let formatted = prettyplease::unparse_expr(exp);
|
||||
let formatted = unparse_expr(exp);
|
||||
|
||||
let mut lines = formatted.lines();
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::{ifmt_to_string, Writer};
|
||||
use crate::{ifmt_to_string, prettier_please::unparse_expr, Writer};
|
||||
use dioxus_rsx::*;
|
||||
use proc_macro2::Span;
|
||||
use quote::ToTokens;
|
||||
|
@ -112,7 +112,7 @@ impl Writer<'_> {
|
|||
ShortOptimization::Oneliner => {
|
||||
write!(self.out, " ")?;
|
||||
|
||||
self.write_attributes(attributes, key, true)?;
|
||||
self.write_attributes(brace, attributes, key, true)?;
|
||||
|
||||
if !children.is_empty() && (!attributes.is_empty() || key.is_some()) {
|
||||
write!(self.out, ", ")?;
|
||||
|
@ -132,7 +132,7 @@ impl Writer<'_> {
|
|||
if !attributes.is_empty() || key.is_some() {
|
||||
write!(self.out, " ")?;
|
||||
}
|
||||
self.write_attributes(attributes, key, true)?;
|
||||
self.write_attributes(brace, attributes, key, true)?;
|
||||
|
||||
if !children.is_empty() && (!attributes.is_empty() || key.is_some()) {
|
||||
write!(self.out, ",")?;
|
||||
|
@ -145,7 +145,7 @@ impl Writer<'_> {
|
|||
}
|
||||
|
||||
ShortOptimization::NoOpt => {
|
||||
self.write_attributes(attributes, key, false)?;
|
||||
self.write_attributes(brace, attributes, key, false)?;
|
||||
|
||||
if !children.is_empty() && (!attributes.is_empty() || key.is_some()) {
|
||||
write!(self.out, ",")?;
|
||||
|
@ -166,6 +166,7 @@ impl Writer<'_> {
|
|||
|
||||
fn write_attributes(
|
||||
&mut self,
|
||||
brace: &Brace,
|
||||
attributes: &[AttributeType],
|
||||
key: &Option<IfmtInput>,
|
||||
sameline: bool,
|
||||
|
@ -187,9 +188,11 @@ impl Writer<'_> {
|
|||
|
||||
while let Some(attr) = attr_iter.next() {
|
||||
self.out.indent_level += 1;
|
||||
|
||||
if !sameline {
|
||||
self.write_comments(attr.start())?;
|
||||
self.write_attr_comments(brace, attr.start())?;
|
||||
}
|
||||
|
||||
self.out.indent_level -= 1;
|
||||
|
||||
if !sameline {
|
||||
|
@ -229,7 +232,7 @@ impl Writer<'_> {
|
|||
write!(
|
||||
self.out,
|
||||
"if {condition} {{ ",
|
||||
condition = prettyplease::unparse_expr(condition),
|
||||
condition = unparse_expr(condition),
|
||||
)?;
|
||||
self.write_attribute_value(value)?;
|
||||
write!(self.out, " }}")?;
|
||||
|
@ -241,7 +244,7 @@ impl Writer<'_> {
|
|||
write!(self.out, "{value}",)?;
|
||||
}
|
||||
ElementAttrValue::AttrExpr(value) => {
|
||||
let out = prettyplease::unparse_expr(value);
|
||||
let out = unparse_expr(value);
|
||||
let mut lines = out.split('\n').peekable();
|
||||
let first = lines.next().unwrap();
|
||||
|
||||
|
@ -308,7 +311,7 @@ impl Writer<'_> {
|
|||
|
||||
fn write_spread_attribute(&mut self, attr: &Expr) -> Result {
|
||||
write!(self.out, "..")?;
|
||||
write!(self.out, "{}", prettyplease::unparse_expr(attr))?;
|
||||
write!(self.out, "{}", unparse_expr(attr))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -27,12 +27,13 @@ impl Writer<'_> {
|
|||
// If the expr is multiline, we want to collect all of its lines together and write them out properly
|
||||
// This involves unshifting the first line if it's aligned
|
||||
let first_line = &self.src[start.line - 1];
|
||||
write!(self.out, "{}", &first_line[start.column - 1..].trim_start())?;
|
||||
write!(self.out, "{}", &first_line[start.column..].trim_start())?;
|
||||
|
||||
let prev_block_indent_level = self.out.indent.count_indents(first_line);
|
||||
|
||||
for (id, line) in self.src[start.line..end.line].iter().enumerate() {
|
||||
writeln!(self.out)?;
|
||||
|
||||
// check if this is the last line
|
||||
let line = {
|
||||
if id == (end.line - start.line) - 1 {
|
||||
|
|
|
@ -17,6 +17,7 @@ mod component;
|
|||
mod element;
|
||||
mod expr;
|
||||
mod indent;
|
||||
mod prettier_please;
|
||||
mod writer;
|
||||
|
||||
pub use indent::{IndentOptions, IndentType};
|
||||
|
|
66
packages/autofmt/src/prettier_please.rs
Normal file
66
packages/autofmt/src/prettier_please.rs
Normal file
|
@ -0,0 +1,66 @@
|
|||
use prettyplease::unparse;
|
||||
use syn::{Expr, File, Item};
|
||||
|
||||
/// Unparse an expression back into a string
|
||||
///
|
||||
/// This creates a new temporary file, parses the expression into it, and then formats the file.
|
||||
/// This is a bit of a hack, but dtonlay doesn't want to support this very simple usecase, forcing us to clone the expr
|
||||
pub fn unparse_expr(expr: &Expr) -> String {
|
||||
let file = wrapped(expr);
|
||||
let wrapped = unparse(&file);
|
||||
unwrapped(wrapped)
|
||||
}
|
||||
|
||||
// Split off the fn main and then cut the tabs off the front
|
||||
fn unwrapped(raw: String) -> String {
|
||||
raw.strip_prefix("fn main() {\n")
|
||||
.unwrap()
|
||||
.strip_suffix("}\n")
|
||||
.unwrap()
|
||||
.lines()
|
||||
.map(|line| line.strip_prefix(" ").unwrap()) // todo: set this to tab level
|
||||
.collect::<Vec<_>>()
|
||||
.join("\n")
|
||||
}
|
||||
|
||||
fn wrapped(expr: &Expr) -> File {
|
||||
File {
|
||||
shebang: None,
|
||||
attrs: vec![],
|
||||
items: vec![
|
||||
//
|
||||
Item::Verbatim(quote::quote! {
|
||||
fn main() {
|
||||
#expr
|
||||
}
|
||||
}),
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unparses_raw() {
|
||||
let expr = syn::parse_str("1 + 1").unwrap();
|
||||
let unparsed = unparse(&wrapped(&expr));
|
||||
assert_eq!(unparsed, "fn main() {\n 1 + 1\n}\n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unparses_completely() {
|
||||
let expr = syn::parse_str("1 + 1").unwrap();
|
||||
let unparsed = unparse_expr(&expr);
|
||||
assert_eq!(unparsed, "1 + 1");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn weird_ifcase() {
|
||||
let contents = r##"
|
||||
fn main() {
|
||||
move |_| timer.with_mut(|t| if t.started_at.is_none() { Some(Instant::now()) } else { None })
|
||||
}
|
||||
"##;
|
||||
|
||||
let expr: File = syn::parse_file(contents).unwrap();
|
||||
let out = unparse(&expr);
|
||||
println!("{}", out);
|
||||
}
|
|
@ -1,3 +1,4 @@
|
|||
use crate::prettier_please::unparse_expr;
|
||||
use dioxus_rsx::{AttributeType, BodyNode, ElementAttrValue, ForLoop, IfChain};
|
||||
use proc_macro2::{LineColumn, Span};
|
||||
use quote::ToTokens;
|
||||
|
@ -5,7 +6,7 @@ use std::{
|
|||
collections::{HashMap, VecDeque},
|
||||
fmt::{Result, Write},
|
||||
};
|
||||
use syn::{spanned::Spanned, Expr};
|
||||
use syn::{spanned::Spanned, token::Brace, Expr};
|
||||
|
||||
use crate::buffer::Buffer;
|
||||
use crate::ifmt_to_string;
|
||||
|
@ -61,8 +62,26 @@ impl<'a> Writer<'a> {
|
|||
Some(self.out.buf)
|
||||
}
|
||||
|
||||
pub fn write_attr_comments(&mut self, brace: &Brace, attr_span: Span) -> Result {
|
||||
// There's a chance this line actually shares the same line as the previous
|
||||
// Only write comments if the comments actually belong to this line
|
||||
//
|
||||
// to do this, we check if the attr span starts on the same line as the brace
|
||||
// if it doesn't, we write the comments
|
||||
let brace_line = brace.span.span().start().line;
|
||||
let attr_line = attr_span.start().line;
|
||||
|
||||
if brace_line != attr_line {
|
||||
self.write_comments(attr_span)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn write_comments(&mut self, child: Span) -> Result {
|
||||
// collect all comments upwards
|
||||
// make sure we don't collect the comments of the node that we're currently under.
|
||||
|
||||
let start = child.start();
|
||||
let line_start = start.line - 1;
|
||||
|
||||
|
@ -149,7 +168,7 @@ impl<'a> Writer<'a> {
|
|||
let len = if let std::collections::hash_map::Entry::Vacant(e) =
|
||||
self.cached_formats.entry(location)
|
||||
{
|
||||
let formatted = prettyplease::unparse_expr(tokens);
|
||||
let formatted = unparse_expr(tokens);
|
||||
let len = if formatted.contains('\n') {
|
||||
10000
|
||||
} else {
|
||||
|
@ -207,7 +226,7 @@ impl<'a> Writer<'a> {
|
|||
pub fn retrieve_formatted_expr(&mut self, expr: &Expr) -> &str {
|
||||
self.cached_formats
|
||||
.entry(Location::new(expr.span().start()))
|
||||
.or_insert_with(|| prettyplease::unparse_expr(expr))
|
||||
.or_insert_with(|| unparse_expr(expr))
|
||||
.as_str()
|
||||
}
|
||||
|
||||
|
@ -216,7 +235,7 @@ impl<'a> Writer<'a> {
|
|||
self.out,
|
||||
"for {} in {} {{",
|
||||
forloop.pat.clone().into_token_stream(),
|
||||
prettyplease::unparse_expr(&forloop.expr)
|
||||
unparse_expr(&forloop.expr)
|
||||
)?;
|
||||
|
||||
if forloop.body.is_empty() {
|
||||
|
@ -249,7 +268,7 @@ impl<'a> Writer<'a> {
|
|||
self.out,
|
||||
"{} {} {{",
|
||||
if_token.to_token_stream(),
|
||||
prettyplease::unparse_expr(cond)
|
||||
unparse_expr(cond)
|
||||
)?;
|
||||
|
||||
self.write_body_indented(then_branch)?;
|
||||
|
|
|
@ -45,4 +45,5 @@ twoway![
|
|||
tiny,
|
||||
tinynoopt,
|
||||
trailing_expr,
|
||||
many_exprs,
|
||||
];
|
||||
|
|
195
packages/autofmt/tests/samples/many_exprs.rsx
Normal file
195
packages/autofmt/tests/samples/many_exprs.rsx
Normal file
|
@ -0,0 +1,195 @@
|
|||
#![allow(dead_code, unused)]
|
||||
use dioxus::desktop::use_window;
|
||||
use dioxus::prelude::*;
|
||||
use std::{
|
||||
process::exit,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::time::sleep;
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::desktop().launch(app);
|
||||
}
|
||||
|
||||
struct WindowPreferences {
|
||||
always_on_top: bool,
|
||||
with_decorations: bool,
|
||||
exiting: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Default for WindowPreferences {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
with_decorations: true,
|
||||
always_on_top: false,
|
||||
exiting: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowPreferences {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Timer {
|
||||
hours: u8,
|
||||
minutes: u8,
|
||||
seconds: u8,
|
||||
started_at: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn duration(&self) -> Duration {
|
||||
Duration::from_secs(
|
||||
(self.hours as u64 * 60 + self.minutes as u64) * 60 + self.seconds as u64,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const UPD_FREQ: Duration = Duration::from_millis(100);
|
||||
|
||||
fn exit_button(
|
||||
delay: Duration,
|
||||
label: fn(Signal<Option<Instant>>, Duration) -> Option<VNode>,
|
||||
) -> Element {
|
||||
let mut trigger: Signal<Option<Instant>> = use_signal(|| None);
|
||||
use_future(move || async move {
|
||||
loop {
|
||||
sleep(UPD_FREQ).await;
|
||||
if let Some(true) = trigger.read().map(|e| e.elapsed() > delay) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
let stuff: Option<VNode> = rsx! {
|
||||
button {
|
||||
onmouseup: move |_| {
|
||||
trigger.set(None);
|
||||
},
|
||||
onmousedown: move |_| {
|
||||
trigger.set(Some(Instant::now()));
|
||||
},
|
||||
width: 100,
|
||||
{label(trigger, delay)}
|
||||
}
|
||||
};
|
||||
stuff
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let mut timer = use_signal(Timer::new);
|
||||
let mut window_preferences = use_signal(WindowPreferences::new);
|
||||
|
||||
use_future(move || async move {
|
||||
loop {
|
||||
sleep(UPD_FREQ).await;
|
||||
timer.with_mut(|t| {
|
||||
if let Some(started_at) = t.started_at {
|
||||
if t.duration().saturating_sub(started_at.elapsed()) == Duration::ZERO {
|
||||
t.started_at = None;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
{
|
||||
let millis = timer.with(|t| t.duration().saturating_sub(t.started_at.map(|x| x.elapsed()).unwrap_or(Duration::ZERO)).as_millis());
|
||||
format!("{:02}:{:02}:{:02}.{:01}",
|
||||
millis / 1000 / 3600 % 3600,
|
||||
millis / 1000 / 60 % 60,
|
||||
millis / 1000 % 60,
|
||||
millis / 100 % 10)
|
||||
}
|
||||
}
|
||||
div {
|
||||
input {
|
||||
r#type: "number",
|
||||
min: 0,
|
||||
max: 99,
|
||||
value: format!("{:02}", timer.read().hours),
|
||||
oninput: move |e| {
|
||||
timer.write().hours = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
r#type: "number",
|
||||
min: 0,
|
||||
max: 59,
|
||||
value: format!("{:02}", timer.read().minutes),
|
||||
oninput: move |e| {
|
||||
timer.write().minutes = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
r#type: "number",
|
||||
min: 0,
|
||||
max: 59,
|
||||
value: format!("{:02}", timer.read().seconds),
|
||||
oninput: move |e| {
|
||||
timer.write().seconds = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
id: "start_stop",
|
||||
onclick: move |_| {
|
||||
timer
|
||||
.with_mut(|t| {
|
||||
t
|
||||
.started_at = if t.started_at.is_none() {
|
||||
Some(Instant::now())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
},
|
||||
{ timer.with(|t| if t.started_at.is_none() { "Start" } else { "Stop" }) }
|
||||
}
|
||||
div { id: "app",
|
||||
button {
|
||||
onclick: move |_| {
|
||||
let decorations = window_preferences.read().with_decorations;
|
||||
use_window().set_decorations(!decorations);
|
||||
window_preferences.write().with_decorations = !decorations;
|
||||
},
|
||||
{
|
||||
format!("with decorations{}", if window_preferences.read().with_decorations { " ✓" } else { "" }).to_string()
|
||||
}
|
||||
}
|
||||
button {
|
||||
onclick: move |_| {
|
||||
window_preferences
|
||||
.with_mut(|wp| {
|
||||
use_window().set_always_on_top(!wp.always_on_top);
|
||||
wp.always_on_top = !wp.always_on_top;
|
||||
})
|
||||
},
|
||||
width: 100,
|
||||
{
|
||||
format!("always on top{}", if window_preferences.read().always_on_top { " ✓" } else { "" })
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
exit_button(
|
||||
Duration::from_secs(3),
|
||||
|trigger, delay| rsx! {
|
||||
{format!("{:0.1?}", trigger.read().map(|inst| (delay.as_secs_f32() - inst.elapsed().as_secs_f32()))) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -26,3 +26,6 @@ twoway!("multi-tab" => multi_tab (IndentOptions::new(IndentType::Tabs, 4, false)
|
|||
|
||||
twoway!("multiexpr-4sp" => multiexpr_4sp (IndentOptions::new(IndentType::Spaces, 4, false)));
|
||||
twoway!("multiexpr-tab" => multiexpr_tab (IndentOptions::new(IndentType::Tabs, 4, false)));
|
||||
twoway!("multiexpr-many" => multiexpr_many (IndentOptions::new(IndentType::Spaces, 4, false)));
|
||||
twoway!("simple-combo-expr" => simple_combo_expr (IndentOptions::new(IndentType::Spaces, 4, false)));
|
||||
twoway!("oneline-expand" => online_expand (IndentOptions::new(IndentType::Spaces, 4, false)));
|
||||
|
|
195
packages/autofmt/tests/wrong/multiexpr-many.rsx
Normal file
195
packages/autofmt/tests/wrong/multiexpr-many.rsx
Normal file
|
@ -0,0 +1,195 @@
|
|||
#![allow(dead_code, unused)]
|
||||
use dioxus::desktop::use_window;
|
||||
use dioxus::prelude::*;
|
||||
use std::{
|
||||
process::exit,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::time::sleep;
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::desktop().launch(app);
|
||||
}
|
||||
|
||||
struct WindowPreferences {
|
||||
always_on_top: bool,
|
||||
with_decorations: bool,
|
||||
exiting: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Default for WindowPreferences {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
with_decorations: true,
|
||||
always_on_top: false,
|
||||
exiting: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowPreferences {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Timer {
|
||||
hours: u8,
|
||||
minutes: u8,
|
||||
seconds: u8,
|
||||
started_at: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn duration(&self) -> Duration {
|
||||
Duration::from_secs(
|
||||
(self.hours as u64 * 60 + self.minutes as u64) * 60 + self.seconds as u64,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const UPD_FREQ: Duration = Duration::from_millis(100);
|
||||
|
||||
fn exit_button(
|
||||
delay: Duration,
|
||||
label: fn(Signal<Option<Instant>>, Duration) -> Option<VNode>,
|
||||
) -> Element {
|
||||
let mut trigger: Signal<Option<Instant>> = use_signal(|| None);
|
||||
use_future(move || async move {
|
||||
loop {
|
||||
sleep(UPD_FREQ).await;
|
||||
if let Some(true) = trigger.read().map(|e| e.elapsed() > delay) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
let stuff: Option<VNode> = rsx! {
|
||||
button {
|
||||
onmouseup: move |_| {
|
||||
trigger.set(None);
|
||||
},
|
||||
onmousedown: move |_| {
|
||||
trigger.set(Some(Instant::now()));
|
||||
},
|
||||
width: 100,
|
||||
{label(trigger, delay)}
|
||||
}
|
||||
};
|
||||
stuff
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let mut timer = use_signal(Timer::new);
|
||||
let mut window_preferences = use_signal(WindowPreferences::new);
|
||||
|
||||
use_future(move || async move {
|
||||
loop {
|
||||
sleep(UPD_FREQ).await;
|
||||
timer.with_mut(|t| {
|
||||
if let Some(started_at) = t.started_at {
|
||||
if t.duration().saturating_sub(started_at.elapsed()) == Duration::ZERO {
|
||||
t.started_at = None;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
rsx! {
|
||||
div {
|
||||
{
|
||||
let millis = timer.with(|t| t.duration().saturating_sub(t.started_at.map(|x| x.elapsed()).unwrap_or(Duration::ZERO)).as_millis());
|
||||
format!("{:02}:{:02}:{:02}.{:01}",
|
||||
millis / 1000 / 3600 % 3600,
|
||||
millis / 1000 / 60 % 60,
|
||||
millis / 1000 % 60,
|
||||
millis / 100 % 10)
|
||||
}
|
||||
}
|
||||
div {
|
||||
input {
|
||||
r#type: "number",
|
||||
min: 0,
|
||||
max: 99,
|
||||
value: format!("{:02}", timer.read().hours),
|
||||
oninput: move |e| {
|
||||
timer.write().hours = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
r#type: "number",
|
||||
min: 0,
|
||||
max: 59,
|
||||
value: format!("{:02}", timer.read().minutes),
|
||||
oninput: move |e| {
|
||||
timer.write().minutes = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
r#type: "number",
|
||||
min: 0,
|
||||
max: 59,
|
||||
value: format!("{:02}", timer.read().seconds),
|
||||
oninput: move |e| {
|
||||
timer.write().seconds = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
id: "start_stop",
|
||||
onclick: move |_| {
|
||||
timer
|
||||
.with_mut(|t| {
|
||||
t
|
||||
.started_at = if t.started_at.is_none() {
|
||||
Some(Instant::now())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
})
|
||||
},
|
||||
{ timer.with(|t| if t.started_at.is_none() { "Start" } else { "Stop" }) }
|
||||
}
|
||||
div { id: "app",
|
||||
button {
|
||||
onclick: move |_| {
|
||||
let decorations = window_preferences.read().with_decorations;
|
||||
use_window().set_decorations(!decorations);
|
||||
window_preferences.write().with_decorations = !decorations;
|
||||
},
|
||||
{
|
||||
format!("with decorations{}", if window_preferences.read().with_decorations { " ✓" } else { "" }).to_string()
|
||||
}
|
||||
}
|
||||
button {
|
||||
onclick: move |_| {
|
||||
window_preferences
|
||||
.with_mut(|wp| {
|
||||
use_window().set_always_on_top(!wp.always_on_top);
|
||||
wp.always_on_top = !wp.always_on_top;
|
||||
})
|
||||
},
|
||||
width: 100,
|
||||
{
|
||||
format!("always on top{}", if window_preferences.read().always_on_top { " ✓" } else { "" })
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
exit_button(
|
||||
Duration::from_secs(3),
|
||||
|trigger, delay| rsx! {
|
||||
{format!("{:0.1?}", trigger.read().map(|inst| (delay.as_secs_f32() - inst.elapsed().as_secs_f32()))) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
164
packages/autofmt/tests/wrong/multiexpr-many.wrong.rsx
Normal file
164
packages/autofmt/tests/wrong/multiexpr-many.wrong.rsx
Normal file
|
@ -0,0 +1,164 @@
|
|||
#![allow(dead_code, unused)]
|
||||
use dioxus::desktop::use_window;
|
||||
use dioxus::prelude::*;
|
||||
use std::{
|
||||
process::exit,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
use tokio::time::sleep;
|
||||
|
||||
fn main() {
|
||||
LaunchBuilder::desktop().launch(app);
|
||||
}
|
||||
|
||||
struct WindowPreferences {
|
||||
always_on_top: bool,
|
||||
with_decorations: bool,
|
||||
exiting: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Default for WindowPreferences {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
with_decorations: true,
|
||||
always_on_top: false,
|
||||
exiting: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WindowPreferences {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Timer {
|
||||
hours: u8,
|
||||
minutes: u8,
|
||||
seconds: u8,
|
||||
started_at: Option<Instant>,
|
||||
}
|
||||
|
||||
impl Timer {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
fn duration(&self) -> Duration {
|
||||
Duration::from_secs(
|
||||
(self.hours as u64 * 60 + self.minutes as u64) * 60 + self.seconds as u64,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
const UPD_FREQ: Duration = Duration::from_millis(100);
|
||||
|
||||
fn exit_button(
|
||||
delay: Duration,
|
||||
label: fn(Signal<Option<Instant>>, Duration) -> Option<VNode>,
|
||||
) -> Element {
|
||||
let mut trigger: Signal<Option<Instant>> = use_signal(|| None);
|
||||
use_future(move || async move {
|
||||
loop {
|
||||
sleep(UPD_FREQ).await;
|
||||
if let Some(true) = trigger.read().map(|e| e.elapsed() > delay) {
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
});
|
||||
let stuff: Option<VNode> = rsx! {
|
||||
button {
|
||||
onmouseup: move |_| {
|
||||
trigger.set(None);
|
||||
},
|
||||
onmousedown: move |_| {
|
||||
trigger.set(Some(Instant::now()));
|
||||
},
|
||||
width: 100,
|
||||
{label(trigger, delay)},
|
||||
}
|
||||
};
|
||||
stuff
|
||||
}
|
||||
|
||||
fn app() -> Element {
|
||||
let mut timer = use_signal(Timer::new);
|
||||
let mut window_preferences = use_signal(WindowPreferences::new);
|
||||
|
||||
use_future(move || async move {
|
||||
loop {
|
||||
sleep(UPD_FREQ).await;
|
||||
timer.with_mut(|t| {
|
||||
if let Some(started_at) = t.started_at {
|
||||
if t.duration().saturating_sub(started_at.elapsed()) == Duration::ZERO {
|
||||
t.started_at = None;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
rsx! {
|
||||
div {{
|
||||
let millis = timer.with(|t| t.duration().saturating_sub(t.started_at.map(|x| x.elapsed()).unwrap_or(Duration::ZERO)).as_millis());
|
||||
format!("{:02}:{:02}:{:02}.{:01}",
|
||||
millis / 1000 / 3600 % 3600,
|
||||
millis / 1000 / 60 % 60,
|
||||
millis / 1000 % 60,
|
||||
millis / 100 % 10)
|
||||
}}
|
||||
div {
|
||||
input { r#type: "number", min: 0, max: 99, value: format!("{:02}",timer.read().hours), oninput: move |e| {
|
||||
timer.write().hours = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
input { r#type: "number", min: 0, max: 59, value: format!("{:02}",timer.read().minutes), oninput: move |e| {
|
||||
timer.write().minutes = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
|
||||
input { r#type: "number", min: 0, max: 59, value: format!("{:02}",timer.read().seconds), oninput: move |e| {
|
||||
timer.write().seconds = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
button {
|
||||
id: "start_stop",
|
||||
onclick: move |_| timer.with_mut(|t| t.started_at = if t.started_at.is_none() { Some(Instant::now()) } else { None } ),
|
||||
{ timer.with(|t| if t.started_at.is_none() { "Start" } else { "Stop" }) },
|
||||
}
|
||||
div { id: "app",
|
||||
button { onclick: move |_| {
|
||||
let decorations = window_preferences.read().with_decorations;
|
||||
use_window().set_decorations(!decorations);
|
||||
window_preferences.write().with_decorations = !decorations;
|
||||
}, {
|
||||
format!("with decorations{}", if window_preferences.read().with_decorations { " ✓" } else { "" }).to_string()
|
||||
}
|
||||
}
|
||||
button {
|
||||
onclick: move |_| {
|
||||
window_preferences.with_mut(|wp| {
|
||||
use_window().set_always_on_top(!wp.always_on_top);
|
||||
wp.always_on_top = !wp.always_on_top;
|
||||
})},
|
||||
width: 100,
|
||||
{
|
||||
format!("always on top{}", if window_preferences.read().always_on_top { " ✓" } else { "" })
|
||||
}
|
||||
}
|
||||
}
|
||||
{
|
||||
exit_button(
|
||||
Duration::from_secs(3),
|
||||
|trigger, delay| rsx! {
|
||||
{format!("{:0.1?}", trigger.read().map(|inst| (delay.as_secs_f32() - inst.elapsed().as_secs_f32()))) }
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
18
packages/autofmt/tests/wrong/oneline-expand.rsx
Normal file
18
packages/autofmt/tests/wrong/oneline-expand.rsx
Normal file
|
@ -0,0 +1,18 @@
|
|||
fn main() {
|
||||
rsx! {
|
||||
button {
|
||||
id: "start_stop",
|
||||
onclick: move |_| {
|
||||
timer
|
||||
.with_mut(|t| {
|
||||
t
|
||||
.started_at = if t.started_at.is_none() {
|
||||
Some(Instant::now())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
packages/autofmt/tests/wrong/oneline-expand.wrong.rsx
Normal file
8
packages/autofmt/tests/wrong/oneline-expand.wrong.rsx
Normal file
|
@ -0,0 +1,8 @@
|
|||
fn main() {
|
||||
rsx! {
|
||||
button {
|
||||
id: "start_stop",
|
||||
onclick: move |_| timer.with_mut(|t| t.started_at = if t.started_at.is_none() { Some(Instant::now()) } else { None } )
|
||||
}
|
||||
}
|
||||
}
|
35
packages/autofmt/tests/wrong/simple-combo-expr.rsx
Normal file
35
packages/autofmt/tests/wrong/simple-combo-expr.rsx
Normal file
|
@ -0,0 +1,35 @@
|
|||
fn main() {
|
||||
rsx! {
|
||||
div {
|
||||
{
|
||||
let millis = timer.with(|t| t.duration().saturating_sub(t.started_at.map(|x| x.elapsed()).unwrap_or(Duration::ZERO)).as_millis());
|
||||
format!("{:02}:{:02}:{:02}.{:01}",
|
||||
millis / 1000 / 3600 % 3600,
|
||||
millis / 1000 / 60 % 60,
|
||||
millis / 1000 % 60,
|
||||
millis / 100 % 10)
|
||||
}
|
||||
}
|
||||
div {
|
||||
input {
|
||||
r#type: "number",
|
||||
min: 0,
|
||||
max: 99,
|
||||
value: format!("{:02}", timer.read().hours),
|
||||
oninput: move |e| {
|
||||
timer.write().hours = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
// some comment
|
||||
input {
|
||||
r#type: "number",
|
||||
min: 0,
|
||||
max: 99,
|
||||
value: format!("{:02}", timer.read().hours),
|
||||
oninput: move |e| {
|
||||
timer.write().hours = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
23
packages/autofmt/tests/wrong/simple-combo-expr.wrong.rsx
Normal file
23
packages/autofmt/tests/wrong/simple-combo-expr.wrong.rsx
Normal file
|
@ -0,0 +1,23 @@
|
|||
fn main() {
|
||||
rsx! {
|
||||
div {{
|
||||
let millis = timer.with(|t| t.duration().saturating_sub(t.started_at.map(|x| x.elapsed()).unwrap_or(Duration::ZERO)).as_millis());
|
||||
format!("{:02}:{:02}:{:02}.{:01}",
|
||||
millis / 1000 / 3600 % 3600,
|
||||
millis / 1000 / 60 % 60,
|
||||
millis / 1000 % 60,
|
||||
millis / 100 % 10)
|
||||
}}
|
||||
div {
|
||||
input { r#type: "number", min: 0, max: 99, value: format!("{:02}",timer.read().hours), oninput: move |e| {
|
||||
timer.write().hours = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
// some comment
|
||||
input { r#type: "number", min: 0, max: 99, value: format!("{:02}",timer.read().hours), oninput: move |e| {
|
||||
timer.write().hours = e.value().parse().unwrap_or(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue