...
This commit is contained in:
		
							
								
								
									
										184
									
								
								rhai_engine/rhaibook/engine/metadata/definitions.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										184
									
								
								rhai_engine/rhaibook/engine/metadata/definitions.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,184 @@
 | 
			
		||||
Generate Definition Files for Language Server
 | 
			
		||||
=============================================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
Rhai's [language server][lsp] works with IDEs to provide integrated support for the Rhai scripting language.
 | 
			
		||||
 | 
			
		||||
Functions and [modules] registered with an [`Engine`] can output their [metadata][functions metadata]
 | 
			
		||||
into _definition files_ which are used by the [language server][lsp].
 | 
			
		||||
 | 
			
		||||
Definitions are generated via the `Engine::definitions` and `Engine::definitions_with_scope` API.
 | 
			
		||||
 | 
			
		||||
This API requires the [`metadata`] and [`internals`] feature.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Configurable Options
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
The `Definitions` type supports the following options in a fluent method-chaining style.
 | 
			
		||||
 | 
			
		||||
| Option                                                              | Method                      | Default |
 | 
			
		||||
| ------------------------------------------------------------------- | --------------------------- | :-----: |
 | 
			
		||||
| Write headers in definition files?                                  | `with_headers`              | `false` |
 | 
			
		||||
| Include [standard packages][built-in packages] in definition files? | `include_standard_packages` | `true`  |
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
engine
 | 
			
		||||
    .definitions()
 | 
			
		||||
    .with_headers(true)                     // write headers in all files
 | 
			
		||||
    .include_standard_packages(false)       // skip standard packages
 | 
			
		||||
    .write_to_dir("path/to/my/definitions")
 | 
			
		||||
    .unwrap();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Example
 | 
			
		||||
-------
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
use rhai::{Engine, Scope};
 | 
			
		||||
use rhai::plugin::*;
 | 
			
		||||
 | 
			
		||||
// Plugin module: 'general_kenobi'
 | 
			
		||||
#[export_module]
 | 
			
		||||
pub mod general_kenobi {
 | 
			
		||||
    use std::convert::TryInto;
 | 
			
		||||
 | 
			
		||||
    /// Returns a string where "hello there" is repeated 'n' times.
 | 
			
		||||
    pub fn hello_there(n: i64) -> String {
 | 
			
		||||
        "hello there ".repeat(n.try_into().unwrap())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Create scripting engine
 | 
			
		||||
let mut engine = Engine::new();
 | 
			
		||||
 | 
			
		||||
// Create custom Scope
 | 
			
		||||
let mut scope = Scope::new();
 | 
			
		||||
 | 
			
		||||
// This variable will also show up in the generated definition file.
 | 
			
		||||
scope.push("hello_there", "hello there");
 | 
			
		||||
 | 
			
		||||
// Static module namespaces will generate independent definition files.
 | 
			
		||||
engine.register_static_module(
 | 
			
		||||
        "general_kenobi",
 | 
			
		||||
        exported_module!(general_kenobi).into()
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
// Custom operators will also show up in the generated definition file.
 | 
			
		||||
engine.register_custom_operator("minus", 100).unwrap();
 | 
			
		||||
engine.register_fn("minus", |a: i64, b: i64| a - b);
 | 
			
		||||
 | 
			
		||||
engine.run_with_scope(&mut scope,
 | 
			
		||||
        "hello_there = general_kenobi::hello_there(4 minus 2);"
 | 
			
		||||
)?;
 | 
			
		||||
 | 
			
		||||
// Output definition files in the specified directory.
 | 
			
		||||
engine
 | 
			
		||||
    .definitions()
 | 
			
		||||
    .write_to_dir("path/to/my/definitions")
 | 
			
		||||
    .unwrap();
 | 
			
		||||
 | 
			
		||||
// Output definition files in the specified directory.
 | 
			
		||||
// Variables in the provided 'Scope' are included.
 | 
			
		||||
engine
 | 
			
		||||
    .definitions_with_scope(&scope)
 | 
			
		||||
    .write_to_dir("path/to/my/definitions")
 | 
			
		||||
    .unwrap();
 | 
			
		||||
 | 
			
		||||
// Output a single definition file with everything merged.
 | 
			
		||||
// Variables in the provided 'Scope' are included.
 | 
			
		||||
engine
 | 
			
		||||
    .definitions_with_scope(&scope)
 | 
			
		||||
    .write_to_file("path/to/my/definitions/all_in_one.d.rhai")
 | 
			
		||||
    .unwrap();
 | 
			
		||||
 | 
			
		||||
// Output functions metadata to a JSON string.
 | 
			
		||||
// Functions in standard packages are skipped and not included.
 | 
			
		||||
let json = engine
 | 
			
		||||
    .definitions()
 | 
			
		||||
    .include_standard_packages(false)   // skip standard packages
 | 
			
		||||
    .unwrap();
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Definition Files
 | 
			
		||||
----------------
 | 
			
		||||
 | 
			
		||||
The generated definition files will look like the following.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
┌───────────────────────┐
 | 
			
		||||
│ general_kenobi.d.rhai │
 | 
			
		||||
└───────────────────────┘
 | 
			
		||||
 | 
			
		||||
module general_kenobi;
 | 
			
		||||
 | 
			
		||||
/// Returns a string where "hello there" is repeated 'n' times.
 | 
			
		||||
fn hello_there(n: int) -> String;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌──────────────────┐
 | 
			
		||||
│ __scope__.d.rhai │
 | 
			
		||||
└──────────────────┘
 | 
			
		||||
 | 
			
		||||
module static;
 | 
			
		||||
 | 
			
		||||
let hello_there;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌───────────────────┐
 | 
			
		||||
│ __static__.d.rhai │
 | 
			
		||||
└───────────────────┘
 | 
			
		||||
 | 
			
		||||
module static;
 | 
			
		||||
 | 
			
		||||
op minus(int, int) -> int;
 | 
			
		||||
 | 
			
		||||
        :
 | 
			
		||||
        :
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌────────────────────┐
 | 
			
		||||
│ __builtin__.d.rhai │
 | 
			
		||||
└────────────────────┘
 | 
			
		||||
 | 
			
		||||
module static;
 | 
			
		||||
 | 
			
		||||
        :
 | 
			
		||||
        :
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
┌──────────────────────────────┐
 | 
			
		||||
│ __builtin-operators__.d.rhai │
 | 
			
		||||
└──────────────────────────────┘
 | 
			
		||||
 | 
			
		||||
module static;
 | 
			
		||||
 | 
			
		||||
        :
 | 
			
		||||
        :
 | 
			
		||||
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
All-in-One Definition File
 | 
			
		||||
--------------------------
 | 
			
		||||
 | 
			
		||||
`Definitions::write_to_file` generates a single definition file with everything merged in, like the following.
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
module static;
 | 
			
		||||
 | 
			
		||||
op minus(int, int) -> int;
 | 
			
		||||
 | 
			
		||||
        :
 | 
			
		||||
        :
 | 
			
		||||
 | 
			
		||||
module general_kenobi {
 | 
			
		||||
    /// Returns a string where "hello there" is repeated 'n' times.
 | 
			
		||||
    fn hello_there(n: int) -> String;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
let hello_there;
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										147
									
								
								rhai_engine/rhaibook/engine/metadata/export_to_json.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								rhai_engine/rhaibook/engine/metadata/export_to_json.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,147 @@
 | 
			
		||||
Export Functions Metadata to JSON
 | 
			
		||||
=================================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
`Engine::gen_fn_metadata_to_json`<br/>`Engine::gen_fn_metadata_with_ast_to_json`
 | 
			
		||||
--------------------------------------------------------------------------------
 | 
			
		||||
 | 
			
		||||
As part of a _reflections_ API, `Engine::gen_fn_metadata_to_json` and the corresponding
 | 
			
		||||
`Engine::gen_fn_metadata_with_ast_to_json` export the full list of [custom types] and
 | 
			
		||||
[functions metadata] in JSON format.
 | 
			
		||||
 | 
			
		||||
~~~admonish warning.small "Requires `metadata`"
 | 
			
		||||
 | 
			
		||||
The [`metadata`] feature is required for this API, which also pulls in the
 | 
			
		||||
[`serde_json`](https://crates.io/crates/serde_json) crate.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
### Sources
 | 
			
		||||
 | 
			
		||||
Functions and [custom types] from the following sources are included:
 | 
			
		||||
 | 
			
		||||
1. Script-defined functions in an [`AST`] (for `Engine::gen_fn_metadata_with_ast_to_json`)
 | 
			
		||||
2. Native Rust functions registered into the global namespace via the `Engine::register_XXX` API
 | 
			
		||||
3. [Custom types] registered into the global namespace via the `Engine::register_type_with_name` API
 | 
			
		||||
4. _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) and [custom types] in static modules
 | 
			
		||||
   registered via `Engine::register_static_module`
 | 
			
		||||
5. Native Rust functions and [custom types] in external [packages] registered via `Engine::register_global_module`
 | 
			
		||||
6. Native Rust functions and [custom types] in [built-in packages] (optional)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
JSON Schema
 | 
			
		||||
-----------
 | 
			
		||||
 | 
			
		||||
The JSON schema used to hold metadata is very simple, containing a nested structure of
 | 
			
		||||
`modules`, a list of `customTypes` and a list of `functions`.
 | 
			
		||||
 | 
			
		||||
### Module Schema
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "doc": "//! Module documentation",
 | 
			
		||||
 | 
			
		||||
    "modules":
 | 
			
		||||
    {
 | 
			
		||||
        "sub_module_1": /* namespace 'sub_module_1' */
 | 
			
		||||
        {
 | 
			
		||||
            "modules":
 | 
			
		||||
            {
 | 
			
		||||
                "sub_sub_module_A": /* namespace 'sub_module_1::sub_sub_module_A' */
 | 
			
		||||
                {
 | 
			
		||||
                    "doc": "//! Module documentation can also occur in any sub-module",
 | 
			
		||||
 | 
			
		||||
                    "customTypes": /* custom types exported in 'sub_module_1::sub_sub_module_A' */
 | 
			
		||||
                    [
 | 
			
		||||
                        { ... custom type metadata ... },
 | 
			
		||||
                        { ... custom type metadata ... },
 | 
			
		||||
                        { ... custom type metadata ... }
 | 
			
		||||
                        ...
 | 
			
		||||
                    ],
 | 
			
		||||
                    "functions": /* functions exported in 'sub_module_1::sub_sub_module_A' */
 | 
			
		||||
                    [
 | 
			
		||||
                        { ... function metadata ... },
 | 
			
		||||
                        { ... function metadata ... },
 | 
			
		||||
                        { ... function metadata ... },
 | 
			
		||||
                        { ... function metadata ... }
 | 
			
		||||
                        ...
 | 
			
		||||
                    ]
 | 
			
		||||
                },
 | 
			
		||||
                "sub_sub_module_B": /* namespace 'sub_module_1::sub_sub_module_B' */
 | 
			
		||||
                {
 | 
			
		||||
                    ...
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        "sub_module_2": /* namespace 'sub_module_2' */
 | 
			
		||||
        {
 | 
			
		||||
            ...
 | 
			
		||||
        },
 | 
			
		||||
        ...
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    "customTypes": /* custom types registered globally */
 | 
			
		||||
    [
 | 
			
		||||
        { ... custom type metadata ... },
 | 
			
		||||
        { ... custom type metadata ... },
 | 
			
		||||
        { ... custom type metadata ... },
 | 
			
		||||
        ...
 | 
			
		||||
    ],
 | 
			
		||||
 | 
			
		||||
    "functions": /* functions registered globally or in the 'AST' */
 | 
			
		||||
    [
 | 
			
		||||
        { ... function metadata ... },
 | 
			
		||||
        { ... function metadata ... },
 | 
			
		||||
        { ... function metadata ... },
 | 
			
		||||
        { ... function metadata ... },
 | 
			
		||||
        ...
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Custom Type Metadata Schema
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "typeName": "alloc::string::String",    /* name of Rust type */
 | 
			
		||||
    "displayName": "MyType",
 | 
			
		||||
    "docComments":  /* omitted if none */
 | 
			
		||||
    [
 | 
			
		||||
        "/// My super-string type.",
 | 
			
		||||
        ...
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
### Function Metadata Schema
 | 
			
		||||
 | 
			
		||||
```json
 | 
			
		||||
{
 | 
			
		||||
    "baseHash": 9876543210,     /* partial hash with only number of parameters */
 | 
			
		||||
    "fullHash": 1234567890,     /* full hash with actual parameter types */
 | 
			
		||||
    "namespace": "internal" | "global",
 | 
			
		||||
    "access": "public" | "private",
 | 
			
		||||
    "name": "fn_name",
 | 
			
		||||
    "isAnonymous": false,
 | 
			
		||||
    "type": "native" | "script",
 | 
			
		||||
    "numParams": 42,            /* number of parameters */
 | 
			
		||||
    "params":                   /* omitted if no parameters */
 | 
			
		||||
    [
 | 
			
		||||
        { "name": "param_1", "type": "type_1" },
 | 
			
		||||
        { "name": "param_2" },  /* no type name */
 | 
			
		||||
        { "type": "type_3" },   /* no parameter name */
 | 
			
		||||
        ...
 | 
			
		||||
    ],
 | 
			
		||||
    "thisType": "this_type",    /* omitted if none */
 | 
			
		||||
    "returnType": "ret_type",   /* omitted if () or unknown */
 | 
			
		||||
    "signature": "[private] fn_name(param_1: type_1, param_2, _: type_3) -> ret_type",
 | 
			
		||||
    "docComments":              /* omitted if none */
 | 
			
		||||
    [
 | 
			
		||||
        "/// doc-comment line 1",
 | 
			
		||||
        "/// doc-comment line 2",
 | 
			
		||||
        "/** doc-comment block */",
 | 
			
		||||
        ...
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
							
								
								
									
										93
									
								
								rhai_engine/rhaibook/engine/metadata/gen_fn_sig.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								rhai_engine/rhaibook/engine/metadata/gen_fn_sig.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
Get Native Function Signatures
 | 
			
		||||
==============================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
`Engine::gen_fn_signatures`
 | 
			
		||||
---------------------------
 | 
			
		||||
 | 
			
		||||
As part of a _reflections_ API, `Engine::gen_fn_signatures` returns a list of function _signatures_
 | 
			
		||||
(as `Vec<String>`), each corresponding to a particular native function available to that [`Engine`] instance.
 | 
			
		||||
 | 
			
		||||
> _name_ `(`_param 1_`:`_type 1_`,` _param 2_`:`_type 2_`,` ... `,` _param n_`:`_type n_`) ->` _return type_
 | 
			
		||||
 | 
			
		||||
The [`metadata`] feature must be used to turn on this API.
 | 
			
		||||
 | 
			
		||||
### Sources
 | 
			
		||||
 | 
			
		||||
Functions from the following sources are included, in order:
 | 
			
		||||
 | 
			
		||||
1. Native Rust functions registered into the global namespace via the `Engine::register_XXX` API
 | 
			
		||||
2. _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules
 | 
			
		||||
   registered via `Engine::register_static_module`.
 | 
			
		||||
3. Native Rust functions in external [packages] registered via `Engine::register_global_module`
 | 
			
		||||
4. Native Rust functions in [built-in packages] (optional)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Functions Metadata
 | 
			
		||||
------------------
 | 
			
		||||
 | 
			
		||||
Beware, however, that not all function signatures contain parameters and return value information.
 | 
			
		||||
 | 
			
		||||
### `Engine::register_XXX`
 | 
			
		||||
 | 
			
		||||
For instance, functions registered via `Engine::register_XXX` contain no information on the names of
 | 
			
		||||
parameter because Rust simply does not make such metadata available natively.
 | 
			
		||||
 | 
			
		||||
Type names, however, _are_ provided.
 | 
			
		||||
 | 
			
		||||
A function registered under the name `foo` with three parameters.
 | 
			
		||||
 | 
			
		||||
> `foo(_: i64, _: char, _: &str) -> String`
 | 
			
		||||
 | 
			
		||||
An [operator] function. Notice that function names do not need to be valid identifiers.
 | 
			
		||||
 | 
			
		||||
> `+=(_: &mut i64, _: i64)`
 | 
			
		||||
 | 
			
		||||
A [property setter][getters/setters].
 | 
			
		||||
Notice that function names do not need to be valid identifiers.
 | 
			
		||||
In this case, the first parameter should be `&mut T` of the custom type and the return value is `()`:
 | 
			
		||||
 | 
			
		||||
> `set$prop(_: &mut TestStruct, _: i64)`
 | 
			
		||||
 | 
			
		||||
### Script-Defined Functions
 | 
			
		||||
 | 
			
		||||
Script-defined [function] signatures contain parameter names.
 | 
			
		||||
Since _all_ parameters, as well as the return value, are [`Dynamic`] the types are simply not shown.
 | 
			
		||||
 | 
			
		||||
> `foo(x, y, z)`
 | 
			
		||||
 | 
			
		||||
is probably defined simply as:
 | 
			
		||||
 | 
			
		||||
```rust
 | 
			
		||||
/// This is a doc-comment, included in this function's metadata.
 | 
			
		||||
fn foo(x, y, z) {
 | 
			
		||||
    ...
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
which is really the same as:
 | 
			
		||||
 | 
			
		||||
> `foo(x: Dynamic, y: Dynamic, z: Dynamic) -> Result<Dynamic, Box<EvalAltResult>>`
 | 
			
		||||
 | 
			
		||||
### Plugin Functions
 | 
			
		||||
 | 
			
		||||
Functions defined in [plugin modules] are the best.
 | 
			
		||||
They contain all metadata describing the functions, including [doc-comments].
 | 
			
		||||
 | 
			
		||||
For example, a plugin function `combine`:
 | 
			
		||||
 | 
			
		||||
> `/// This is a doc-comment, included in this function's metadata.`  
 | 
			
		||||
> `combine(list: &mut MyStruct<i64>, num: usize, name: &str) -> bool`
 | 
			
		||||
 | 
			
		||||
Notice that function names do not need to be valid identifiers.
 | 
			
		||||
 | 
			
		||||
For example, an [operator] defined as a [fallible function] in a [plugin module] via
 | 
			
		||||
`#[rhai_fn(name="+=", return_raw)]` returns `Result<bool, Box<EvalAltResult>>`:
 | 
			
		||||
 | 
			
		||||
> `+=(list: &mut MyStruct<i64>, value: &str) -> Result<bool, Box<EvalAltResult>>`
 | 
			
		||||
 | 
			
		||||
For example, a [property getter][getters/setters] defined in a [plugin module]:
 | 
			
		||||
 | 
			
		||||
> `get$prop(obj: &mut MyStruct<i64>) -> String`
 | 
			
		||||
							
								
								
									
										49
									
								
								rhai_engine/rhaibook/engine/metadata/index.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								rhai_engine/rhaibook/engine/metadata/index.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,49 @@
 | 
			
		||||
Functions and Custom Types Metadata
 | 
			
		||||
===================================
 | 
			
		||||
 | 
			
		||||
{{#include ../../links.md}}
 | 
			
		||||
 | 
			
		||||
~~~admonish warning.small "Requires `metadata`"
 | 
			
		||||
 | 
			
		||||
Exporting metadata requires the [`metadata`] feature.
 | 
			
		||||
~~~
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Functions
 | 
			
		||||
---------
 | 
			
		||||
 | 
			
		||||
The _metadata_ of a [function] means all relevant information related to a function's
 | 
			
		||||
definition including:
 | 
			
		||||
 | 
			
		||||
1. Its callable name
 | 
			
		||||
 | 
			
		||||
2. Its access mode (public or [private][`private`])
 | 
			
		||||
 | 
			
		||||
3. Its parameter names and types (if any)
 | 
			
		||||
 | 
			
		||||
4. Its return value and type (if any)
 | 
			
		||||
 | 
			
		||||
5. Its nature (i.e. native Rust or Rhai-scripted)
 | 
			
		||||
 | 
			
		||||
6. Its [namespace][function namespace] ([module] or global)
 | 
			
		||||
 | 
			
		||||
7. Its purpose, in the form of [doc-comments]
 | 
			
		||||
 | 
			
		||||
8. Usage notes, warnings, examples etc., in the form of [doc-comments]
 | 
			
		||||
 | 
			
		||||
A function's _signature_ encapsulates the first four pieces of information in a single concise line
 | 
			
		||||
of definition:
 | 
			
		||||
 | 
			
		||||
> `[private]` _name_ `(`_param 1_`:`_type 1_`,` _param 2_`:`_type 2_`,` ... `,` _param n_`:`_type n_`) ->` _return type_
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
Custom Types
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
The _metadata_ of a [custom type] include:
 | 
			
		||||
 | 
			
		||||
1. Its full Rust type name
 | 
			
		||||
 | 
			
		||||
2. Its pretty-print _display name_ (which can be the same as its Rust type name)
 | 
			
		||||
 | 
			
		||||
3. Its purpose, in the form of [doc-comments]
 | 
			
		||||
		Reference in New Issue
	
	Block a user