From bb2181234ae0551f7fbbd080d7a074e686a364e0 Mon Sep 17 00:00:00 2001
From: Alex Crichton
Date: Thu, 5 Jun 2014 23:01:01 -0700
Subject: [PATCH 1/2] doc: Turn off special features for rustdoc tests
These were only used for the markdown tests, and there's no reason they should
be distinct from the other tests.
---
src/doc/guide-macros.md | 35 ++++++++++++++++++++++++++---------
src/doc/guide-unsafe.md | 1 +
src/doc/rust.md | 3 +++
src/doc/tutorial.md | 3 +++
src/librustdoc/markdown.rs | 2 +-
src/librustdoc/test.rs | 23 +++++------------------
6 files changed, 39 insertions(+), 28 deletions(-)
diff --git a/src/doc/guide-macros.md b/src/doc/guide-macros.md
index b86a6aa1b619c..45745c7b7bc7a 100644
--- a/src/doc/guide-macros.md
+++ b/src/doc/guide-macros.md
@@ -11,7 +11,7 @@ which both pattern-match on their input and both return early in one case,
doing nothing otherwise:
~~~~
-# enum T { SpecialA(uint), SpecialB(uint) };
+# enum T { SpecialA(uint), SpecialB(uint) }
# fn f() -> uint {
# let input_1 = SpecialA(0);
# let input_2 = SpecialA(0);
@@ -37,7 +37,8 @@ lightweight custom syntax extensions, themselves defined using the
the pattern in the above code:
~~~~
-# enum T { SpecialA(uint), SpecialB(uint) };
+# #![feature(macro_rules)]
+# enum T { SpecialA(uint), SpecialB(uint) }
# fn f() -> uint {
# let input_1 = SpecialA(0);
# let input_2 = SpecialA(0);
@@ -55,6 +56,7 @@ early_return!(input_1 SpecialA);
early_return!(input_2 SpecialB);
# return 0;
# }
+# fn main() {}
~~~~
Macros are defined in pattern-matching style: in the above example, the text
@@ -155,7 +157,8 @@ separator token (a comma-separated list could be written `$(...),*`), and `+`
instead of `*` to mean "at least one".
~~~~
-# enum T { SpecialA(uint),SpecialB(uint),SpecialC(uint),SpecialD(uint)};
+# #![feature(macro_rules)]
+# enum T { SpecialA(uint),SpecialB(uint),SpecialC(uint),SpecialD(uint)}
# fn f() -> uint {
# let input_1 = SpecialA(0);
# let input_2 = SpecialA(0);
@@ -175,6 +178,7 @@ early_return!(input_1, [SpecialA|SpecialC|SpecialD]);
early_return!(input_2, [SpecialB]);
# return 0;
# }
+# fn main() {}
~~~~
### Transcription
@@ -215,9 +219,10 @@ solves the problem.
Now consider code like the following:
~~~~
-# enum T1 { Good1(T2, uint), Bad1};
+# #![feature(macro_rules)]
+# enum T1 { Good1(T2, uint), Bad1}
# struct T2 { body: T3 }
-# enum T3 { Good2(uint), Bad2};
+# enum T3 { Good2(uint), Bad2}
# fn f(x: T1) -> uint {
match x {
Good1(g1, val) => {
@@ -232,6 +237,7 @@ match x {
_ => return 0 // default value
}
# }
+# fn main() {}
~~~~
All the complicated stuff is deeply indented, and the error-handling code is
@@ -240,6 +246,7 @@ a match, but with a syntax that suits the problem better. The following macro
can solve the problem:
~~~~
+# #![feature(macro_rules)]
macro_rules! biased_match (
// special case: `let (x) = ...` is illegal, so use `let x = ...` instead
( ($e:expr) ~ ($p:pat) else $err:stmt ;
@@ -261,9 +268,9 @@ macro_rules! biased_match (
)
)
-# enum T1 { Good1(T2, uint), Bad1};
+# enum T1 { Good1(T2, uint), Bad1}
# struct T2 { body: T3 }
-# enum T3 { Good2(uint), Bad2};
+# enum T3 { Good2(uint), Bad2}
# fn f(x: T1) -> uint {
biased_match!((x) ~ (Good1(g1, val)) else { return 0 };
binds g1, val )
@@ -273,6 +280,7 @@ biased_match!((g1.body) ~ (Good2(result) )
// complicated stuff goes here
return result + val;
# }
+# fn main() {}
~~~~
This solves the indentation problem. But if we have a lot of chained matches
@@ -280,6 +288,8 @@ like this, we might prefer to write a single macro invocation. The input
pattern we want is clear:
~~~~
+# #![feature(macro_rules)]
+# fn main() {}
# macro_rules! b(
( $( ($e:expr) ~ ($p:pat) else $err:stmt ; )*
binds $( $bind_res:ident ),*
@@ -301,14 +311,18 @@ process the semicolon-terminated lines, one-by-one. So, we want the following
input patterns:
~~~~
+# #![feature(macro_rules)]
# macro_rules! b(
( binds $( $bind_res:ident ),* )
# => (0))
+# fn main() {}
~~~~
...and:
~~~~
+# #![feature(macro_rules)]
+# fn main() {}
# macro_rules! b(
( ($e :expr) ~ ($p :pat) else $err :stmt ;
$( ($e_rest:expr) ~ ($p_rest:pat) else $err_rest:stmt ; )*
@@ -322,6 +336,8 @@ The resulting macro looks like this. Note that the separation into
piece of syntax (the `let`) which we only want to transcribe once.
~~~~
+# #![feature(macro_rules)]
+# fn main() {
macro_rules! biased_match_rec (
// Handle the first layer
@@ -365,9 +381,9 @@ macro_rules! biased_match (
)
-# enum T1 { Good1(T2, uint), Bad1};
+# enum T1 { Good1(T2, uint), Bad1}
# struct T2 { body: T3 }
-# enum T3 { Good2(uint), Bad2};
+# enum T3 { Good2(uint), Bad2}
# fn f(x: T1) -> uint {
biased_match!(
(x) ~ (Good1(g1, val)) else { return 0 };
@@ -376,6 +392,7 @@ biased_match!(
// complicated stuff goes here
return result + val;
# }
+# }
~~~~
This technique applies to many cases where transcribing a result all at once is not possible.
diff --git a/src/doc/guide-unsafe.md b/src/doc/guide-unsafe.md
index e0a48682963b4..1431c8a5c9ae4 100644
--- a/src/doc/guide-unsafe.md
+++ b/src/doc/guide-unsafe.md
@@ -523,6 +523,7 @@ vectors provided from C, using idiomatic Rust practices.
```
#![no_std]
+#![feature(globs)]
# extern crate libc;
extern crate core;
diff --git a/src/doc/rust.md b/src/doc/rust.md
index b690c2eb98347..3715edff373f0 100644
--- a/src/doc/rust.md
+++ b/src/doc/rust.md
@@ -1260,6 +1260,8 @@ a = Cat;
Enumeration constructors can have either named or unnamed fields:
~~~~
+# #![feature(struct_variant)]
+# fn main() {
enum Animal {
Dog (String, f64),
Cat { name: String, weight: f64 }
@@ -1267,6 +1269,7 @@ enum Animal {
let mut a: Animal = Dog("Cocoa".to_string(), 37.2);
a = Cat { name: "Spotty".to_string(), weight: 2.7 };
+# }
~~~~
In this example, `Cat` is a _struct-like enum variant_,
diff --git a/src/doc/tutorial.md b/src/doc/tutorial.md
index 917704a2faacd..3b4164ffbc618 100644
--- a/src/doc/tutorial.md
+++ b/src/doc/tutorial.md
@@ -774,6 +774,7 @@ fn point_from_direction(dir: Direction) -> Point {
Enum variants may also be structs. For example:
~~~~
+# #![feature(struct_variant)]
use std::f64;
# struct Point { x: f64, y: f64 }
# fn square(x: f64) -> f64 { x * x }
@@ -789,6 +790,7 @@ fn area(sh: Shape) -> f64 {
}
}
}
+# fn main() {}
~~~~
> *Note:* This feature of the compiler is currently gated behind the
@@ -3046,6 +3048,7 @@ use farm::{chicken, cow};
2. Import everything in a module with a wildcard:
~~~
+# #![feature(globs)]
use farm::*;
# mod farm {
# pub fn cow() { println!("Bat-chicken? What a stupid name!") }
diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs
index 961c92940be81..500d17c9f9a57 100644
--- a/src/librustdoc/markdown.rs
+++ b/src/librustdoc/markdown.rs
@@ -173,7 +173,7 @@ pub fn render(input: &str, mut output: Path, matches: &getopts::Matches) -> int
pub fn test(input: &str, libs: HashSet, mut test_args: Vec) -> int {
let input_str = load_or_return!(input, 1, 2);
- let mut collector = Collector::new(input.to_string(), libs, true, true);
+ let mut collector = Collector::new(input.to_string(), libs, true);
find_testable_code(input_str.as_slice(), &mut collector);
test_args.unshift("rustdoctest".to_string());
testing::test_main(test_args.as_slice(), collector.tests);
diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs
index ed53b2ac314a8..1434c3eb07d53 100644
--- a/src/librustdoc/test.rs
+++ b/src/librustdoc/test.rs
@@ -91,7 +91,6 @@ pub fn run(input: &str,
let mut collector = Collector::new(krate.name.to_string(),
libs,
- false,
false);
collector.fold_crate(krate);
@@ -103,8 +102,8 @@ pub fn run(input: &str,
}
fn runtest(test: &str, cratename: &str, libs: HashSet, should_fail: bool,
- no_run: bool, loose_feature_gating: bool) {
- let test = maketest(test, cratename, loose_feature_gating);
+ no_run: bool) {
+ let test = maketest(test, cratename);
let input = driver::StrInput(test.to_string());
let sessopts = config::Options {
@@ -201,18 +200,12 @@ fn runtest(test: &str, cratename: &str, libs: HashSet, should_fail: bool,
}
}
-fn maketest(s: &str, cratename: &str, loose_feature_gating: bool) -> String {
+pub fn maketest(s: &str, cratename: &str) -> String {
let mut prog = String::from_str(r"
#![deny(warnings)]
#![allow(unused_variable, dead_assignment, unused_mut, attribute_usage, dead_code)]
");
- if loose_feature_gating {
- // FIXME #12773: avoid inserting these when the tutorial & manual
- // etc. have been updated to not use them so prolifically.
- prog.push_str("#![feature(macro_rules, globs, struct_variant, managed_boxes) ]\n");
- }
-
if !s.contains("extern crate") {
if s.contains(cratename) {
prog.push_str(format!("extern crate {};\n",
@@ -238,13 +231,11 @@ pub struct Collector {
use_headers: bool,
current_header: Option,
cratename: String,
-
- loose_feature_gating: bool
}
impl Collector {
pub fn new(cratename: String, libs: HashSet,
- use_headers: bool, loose_feature_gating: bool) -> Collector {
+ use_headers: bool) -> Collector {
Collector {
tests: Vec::new(),
names: Vec::new(),
@@ -253,8 +244,6 @@ impl Collector {
use_headers: use_headers,
current_header: None,
cratename: cratename,
-
- loose_feature_gating: loose_feature_gating
}
}
@@ -268,7 +257,6 @@ impl Collector {
self.cnt += 1;
let libs = self.libs.clone();
let cratename = self.cratename.to_string();
- let loose_feature_gating = self.loose_feature_gating;
debug!("Creating test {}: {}", name, test);
self.tests.push(testing::TestDescAndFn {
desc: testing::TestDesc {
@@ -281,8 +269,7 @@ impl Collector {
cratename.as_slice(),
libs,
should_fail,
- no_run,
- loose_feature_gating);
+ no_run);
}),
});
}
From 66ffe8b387a88ab402ec07a489e540f2972b2736 Mon Sep 17 00:00:00 2001
From: Alex Crichton
Date: Fri, 6 Jun 2014 09:12:18 -0700
Subject: [PATCH 2/2] rustdoc: Submit examples to play.rust-lang.org
This grows a new option inside of rustdoc to add the ability to submit examples
to an external website. If the `--markdown-playground-url` command line option
or crate doc attribute `html_playground_url` is present, then examples will have
a button on hover to submit the code to the playground specified.
This commit enables submission of example code to play.rust-lang.org. The code
submitted is that which is tested by rustdoc, not necessarily the exact code
shown in the example.
Closes #14654
---
mk/docs.mk | 4 ++-
src/doc/footer.inc | 2 ++
src/doc/rust.css | 13 ++++++++++
src/libcollections/lib.rs | 3 ++-
src/libcore/lib.rs | 3 ++-
src/libgetopts/lib.rs | 3 ++-
src/libglob/lib.rs | 4 +--
src/libgreen/lib.rs | 3 ++-
src/liblog/lib.rs | 3 ++-
src/libnum/lib.rs | 3 ++-
src/librand/lib.rs | 3 ++-
src/libregex/lib.rs | 3 ++-
src/librustdoc/html/highlight.rs | 13 +++++++---
src/librustdoc/html/layout.rs | 13 ++++++++--
src/librustdoc/html/markdown.rs | 35 ++++++++++++++++++++++-----
src/librustdoc/html/render.rs | 15 ++++++++++--
src/librustdoc/html/static/main.css | 13 +++++++++-
src/librustdoc/html/static/main.js | 2 +-
src/librustdoc/html/static/playpen.js | 29 ++++++++++++++++++++++
src/librustdoc/lib.rs | 4 ++-
src/librustdoc/markdown.rs | 13 +++++++++-
src/librustdoc/test.rs | 24 ++++++++++++------
src/libserialize/lib.rs | 3 ++-
src/libstd/lib.rs | 3 ++-
src/libsync/lib.rs | 3 ++-
src/libterm/lib.rs | 3 ++-
src/libtime/lib.rs | 3 ++-
src/liburl/lib.rs | 3 ++-
src/libuuid/lib.rs | 3 ++-
29 files changed, 186 insertions(+), 43 deletions(-)
create mode 100644 src/librustdoc/html/static/playpen.js
diff --git a/mk/docs.mk b/mk/docs.mk
index 90f85079464bb..dab40cb165431 100644
--- a/mk/docs.mk
+++ b/mk/docs.mk
@@ -43,7 +43,9 @@ L10N_LANGS := ja
# The options are passed to the documentation generators.
RUSTDOC_HTML_OPTS_NO_CSS = --markdown-before-content=doc/version_info.html \
- --markdown-in-header=doc/favicon.inc --markdown-after-content=doc/footer.inc
+ --markdown-in-header=doc/favicon.inc \
+ --markdown-after-content=doc/footer.inc \
+ --markdown-playground-url='http://play.rust-lang.org/'
RUSTDOC_HTML_OPTS = $(RUSTDOC_HTML_OPTS_NO_CSS) --markdown-css rust.css
diff --git a/src/doc/footer.inc b/src/doc/footer.inc
index a103a4908f6bf..4e7d60586f23d 100644
--- a/src/doc/footer.inc
+++ b/src/doc/footer.inc
@@ -5,3 +5,5 @@ or the MIT license, at your opt
This file may not be copied, modified, or distributed except according to those terms.
+
+
diff --git a/src/doc/rust.css b/src/doc/rust.css
index d60dd54a67d14..3957231a195a5 100644
--- a/src/doc/rust.css
+++ b/src/doc/rust.css
@@ -313,6 +313,19 @@ table th {
padding: 5px;
}
+/* Code snippets */
+
+.rusttest { display: none; }
+pre.rust { position: relative; }
+pre.rust a { transform: scaleX(-1); }
+.test-arrow {
+ display: inline-block;
+ position: absolute;
+ top: 0;
+ right: 10px;
+ font-size: 150%;
+}
+
@media (min-width: 1170px) {
pre {
font-size: 15px;
diff --git a/src/libcollections/lib.rs b/src/libcollections/lib.rs
index 2004285ecb91b..a65c06107ce34 100644
--- a/src/libcollections/lib.rs
+++ b/src/libcollections/lib.rs
@@ -17,7 +17,8 @@
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
#![feature(macro_rules, managed_boxes, default_type_params, phase, globs)]
#![no_std]
diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs
index 6aa07415e9cc7..2ccf431fc22e1 100644
--- a/src/libcore/lib.rs
+++ b/src/libcore/lib.rs
@@ -50,7 +50,8 @@
#![crate_type = "rlib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
#![no_std]
#![feature(globs, macro_rules, managed_boxes, phase, simd)]
diff --git a/src/libgetopts/lib.rs b/src/libgetopts/lib.rs
index 0311c3339241a..25cf47a06b176 100644
--- a/src/libgetopts/lib.rs
+++ b/src/libgetopts/lib.rs
@@ -84,7 +84,8 @@
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
#![feature(globs, phase)]
#![deny(missing_doc)]
#![deny(deprecated_owned_vector)]
diff --git a/src/libglob/lib.rs b/src/libglob/lib.rs
index 86753fbb811cb..f3e1da77ce5c7 100644
--- a/src/libglob/lib.rs
+++ b/src/libglob/lib.rs
@@ -29,8 +29,8 @@
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
-
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
#![deny(deprecated_owned_vector)]
use std::cell::Cell;
diff --git a/src/libgreen/lib.rs b/src/libgreen/lib.rs
index 31fd8950c804c..c75d69480ce0d 100644
--- a/src/libgreen/lib.rs
+++ b/src/libgreen/lib.rs
@@ -203,7 +203,8 @@
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
// NB this does *not* include globs, please keep it that way.
#![feature(macro_rules, phase)]
diff --git a/src/liblog/lib.rs b/src/liblog/lib.rs
index ff5805599690d..daacf8b3c4761 100644
--- a/src/liblog/lib.rs
+++ b/src/liblog/lib.rs
@@ -111,7 +111,8 @@ if logging is disabled, none of the components of the log will be executed.
#![crate_type = "dylib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
#![feature(macro_rules)]
#![deny(missing_doc, deprecated_owned_vector)]
diff --git a/src/libnum/lib.rs b/src/libnum/lib.rs
index 29cf769ffc9d8..fae21e80f3072 100644
--- a/src/libnum/lib.rs
+++ b/src/libnum/lib.rs
@@ -50,7 +50,8 @@
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
#![deny(deprecated_owned_vector)]
diff --git a/src/librand/lib.rs b/src/librand/lib.rs
index 3ed086e9b13cc..7a12dcf9f7f31 100644
--- a/src/librand/lib.rs
+++ b/src/librand/lib.rs
@@ -21,7 +21,8 @@
#![crate_type = "rlib"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
#![feature(macro_rules, phase, globs)]
#![no_std]
diff --git a/src/libregex/lib.rs b/src/libregex/lib.rs
index a48760913c161..44c206162ab25 100644
--- a/src/libregex/lib.rs
+++ b/src/libregex/lib.rs
@@ -360,7 +360,8 @@
#![license = "MIT/ASL2"]
#![doc(html_logo_url = "http://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png",
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
- html_root_url = "http://doc.rust-lang.org/")]
+ html_root_url = "http://doc.rust-lang.org/",
+ html_playground_url = "http://play.rust-lang.org/")]
#![feature(macro_rules, phase)]
#![deny(missing_doc, deprecated_owned_vector)]
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index f544e1e0973f3..3c9358b03a983 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -25,7 +25,7 @@ use html::escape::Escape;
use t = syntax::parse::token;
/// Highlights some source code, returning the HTML output.
-pub fn highlight(src: &str, class: Option<&str>) -> String {
+pub fn highlight(src: &str, class: Option<&str>, id: Option<&str>) -> String {
debug!("highlighting: ================\n{}\n==============", src);
let sess = parse::new_parse_sess();
let fm = parse::string_to_filemap(&sess,
@@ -36,6 +36,7 @@ pub fn highlight(src: &str, class: Option<&str>) -> String {
doit(&sess,
lexer::StringReader::new(&sess.span_diagnostic, fm),
class,
+ id,
&mut out).unwrap();
str::from_utf8_lossy(out.unwrap().as_slice()).to_string()
}
@@ -47,11 +48,17 @@ pub fn highlight(src: &str, class: Option<&str>) -> String {
/// it's used. All source code emission is done as slices from the source map,
/// not from the tokens themselves, in order to stay true to the original
/// source.
-fn doit(sess: &parse::ParseSess, mut lexer: lexer::StringReader, class: Option<&str>,
+fn doit(sess: &parse::ParseSess, mut lexer: lexer::StringReader,
+ class: Option<&str>, id: Option<&str>,
out: &mut Writer) -> io::IoResult<()> {
use syntax::parse::lexer::Reader;
- try!(write!(out, "\n", class.unwrap_or("")));
+ try!(write!(out, " try!(write!(out, "id='{}' ", id)),
+ None => {}
+ }
+ try!(write!(out, "class='rust {}'>\n", class.unwrap_or("")));
let mut last = BytePos(0);
let mut is_attribute = false;
let mut is_macro = false;
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 80653878247fa..e2fa57148c2c7 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -16,6 +16,7 @@ pub struct Layout {
pub logo: String,
pub favicon: String,
pub krate: String,
+ pub playground_url: String,
}
pub struct Page<'a> {
@@ -108,11 +109,13 @@ r##"
+ {play_js}