mirror of
https://github.com/FuelLabs/sway.git
synced 2025-08-31 07:37:50 +00:00

## Description
Closes https://github.com/FuelLabs/sway/issues/4645
This PR lets the user generate doc comments for ABI methods. Previously
it was working for functions and function implementations of ABI/trait,
but not on the ABI/trait definition itself.
Since the output is exactly the same for FunctionDecl to TraitFn, I
wanted to do this in a way where they could share the code. I ended up
writing a trait called `FunctionSignature` that both types implement
that exposes `parameters()` and `return_type()`. This allows me to use
the same code for code actions and I suspect it will come in handy in
the future.
One problem was that `FunctionDecl`'s return_type was `TypeArgument`
while `TraitFn` had its return type as `TypeId` with an additional
`return_type_span` field. I changed it so that `TraitFn`'s return_type
is now `TypeArgument` and removed `return_type_span`.
I also needed to add a `span` field to `TraitFn` since previously its
`span()` was returning only the span of the function name, not the whole
function. From what I could see, this doesn't impact the spans of any
existing errors.
### Testing
I added an integration test to the LSP for this scenario.
### Example

## Checklist
- [x] I have linked to any relevant issues.
- [x] I have commented my code, particularly in hard-to-understand
areas.
- [ ] I have updated the documentation where relevant (API docs, the
reference, and the Sway book).
- [x] I have added tests that prove my fix is effective or that my
feature works.
- [x] I have added (or requested a maintainer to add) the necessary
`Breaking*` or `New Feature` labels where relevant.
- [x] I have done my best to ensure that my PR adheres to [the Fuel Labs
Code Review
Standards](https://github.com/FuelLabs/rfcs/blob/master/text/code-standards/external-contributors.md).
- [x] I have requested a review from the relevant team or maintainers.
509 lines
15 KiB
Rust
509 lines
15 KiB
Rust
//! This file contains methods used for simulating LSP code action json-rpc notifications and requests.
|
|
//! The methods are used to build and send requests and notifications to the LSP service
|
|
//! and assert the expected responses.
|
|
|
|
use crate::integration::lsp::{build_request_with_id, call_request};
|
|
use assert_json_diff::assert_json_eq;
|
|
use serde_json::json;
|
|
use sway_lsp::server::Backend;
|
|
use tower_lsp::{
|
|
jsonrpc::{Request, Response},
|
|
lsp_types::*,
|
|
LspService,
|
|
};
|
|
|
|
pub(crate) async fn code_action_abi_request(
|
|
service: &mut LspService<Backend>,
|
|
uri: &Url,
|
|
) -> Request {
|
|
let params = json!({
|
|
"textDocument": {
|
|
"uri": uri,
|
|
},
|
|
"range" : {
|
|
"start":{
|
|
"line": 27,
|
|
"character": 4
|
|
},
|
|
"end":{
|
|
"line": 27,
|
|
"character": 9
|
|
},
|
|
},
|
|
"context": {
|
|
"diagnostics": [],
|
|
"triggerKind": 2
|
|
}
|
|
});
|
|
let code_action = build_request_with_id("textDocument/codeAction", params, 1);
|
|
let response = call_request(service, code_action.clone()).await;
|
|
let uri_string = uri.to_string();
|
|
let expected = Response::from_ok(
|
|
1.into(),
|
|
json!([{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string: [
|
|
{
|
|
"newText": "\nimpl FooABI for Contract {\n fn main() -> u64 {}\n}\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 31
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 31
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate impl for `FooABI`"
|
|
}]),
|
|
);
|
|
assert_json_eq!(expected, response.ok().unwrap());
|
|
code_action
|
|
}
|
|
|
|
pub(crate) async fn code_action_function_request(
|
|
service: &mut LspService<Backend>,
|
|
uri: &Url,
|
|
) -> Request {
|
|
let params = json!({
|
|
"textDocument": {
|
|
"uri": uri,
|
|
},
|
|
"range" : {
|
|
"start": {
|
|
"line": 18,
|
|
"character": 4
|
|
},
|
|
"end": {
|
|
"line": 18,
|
|
"character": 4
|
|
}
|
|
},
|
|
"context": {
|
|
"diagnostics": [],
|
|
"triggerKind": 2
|
|
}
|
|
});
|
|
let code_action = build_request_with_id("textDocument/codeAction", params, 1);
|
|
let response = call_request(service, code_action.clone()).await;
|
|
let uri_string = uri.to_string();
|
|
let expected = Response::from_ok(
|
|
1.into(),
|
|
json!([
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string: [
|
|
{
|
|
"newText": "/// Add a brief description.\n/// \n/// ### Additional Information\n/// \n/// Provide information beyond the core purpose or functionality.\n/// \n/// ### Reverts\n/// \n/// * List any cases where the function will revert\n/// \n/// ### Number of Storage Accesses\n/// \n/// * Reads: `0`\n/// * Writes: `0`\n/// * Clears: `0`\n/// \n/// ### Examples\n/// \n/// ```sway\n/// let x = test();\n/// ```\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 18
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 18
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate a documentation template"
|
|
}
|
|
]),
|
|
);
|
|
assert_json_eq!(expected, response.ok().unwrap());
|
|
code_action
|
|
}
|
|
|
|
pub(crate) async fn code_action_trait_fn_request(
|
|
service: &mut LspService<Backend>,
|
|
uri: &Url,
|
|
) -> Request {
|
|
let params = json!({
|
|
"textDocument": {
|
|
"uri": uri,
|
|
},
|
|
"range" : {
|
|
"start": {
|
|
"line": 10,
|
|
"character": 10
|
|
},
|
|
"end": {
|
|
"line": 10,
|
|
"character": 10
|
|
}
|
|
},
|
|
"context": {
|
|
"diagnostics": [],
|
|
"triggerKind": 2
|
|
}
|
|
});
|
|
let code_action = build_request_with_id("textDocument/codeAction", params, 1);
|
|
let response = call_request(service, code_action.clone()).await;
|
|
let uri_string = uri.to_string();
|
|
let expected = Response::from_ok(
|
|
1.into(),
|
|
json!([
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string: [
|
|
{
|
|
"newText": " /// Add a brief description.\n /// \n /// ### Additional Information\n /// \n /// Provide information beyond the core purpose or functionality.\n /// \n /// ### Returns\n /// \n /// * [Empty] - Add description here\n /// \n /// ### Reverts\n /// \n /// * List any cases where the function will revert\n /// \n /// ### Number of Storage Accesses\n /// \n /// * Reads: `0`\n /// * Writes: `0`\n /// * Clears: `0`\n /// \n /// ### Examples\n /// \n /// ```sway\n /// let x = test_function();\n /// ```\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 10
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 10
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate a documentation template"
|
|
}
|
|
]),
|
|
);
|
|
assert_json_eq!(expected, response.ok().unwrap());
|
|
code_action
|
|
}
|
|
|
|
pub(crate) async fn code_action_struct_request(
|
|
service: &mut LspService<Backend>,
|
|
uri: &Url,
|
|
) -> Request {
|
|
let params = json!({
|
|
"textDocument": {
|
|
"uri": uri,
|
|
},
|
|
"range" : {
|
|
"start": {
|
|
"line": 19,
|
|
"character": 11
|
|
},
|
|
"end": {
|
|
"line": 19,
|
|
"character": 11
|
|
}
|
|
},
|
|
"context": {
|
|
"diagnostics": [],
|
|
"triggerKind": 2
|
|
}
|
|
});
|
|
let code_action = build_request_with_id("textDocument/codeAction", params, 1);
|
|
let response = call_request(service, code_action.clone()).await;
|
|
let uri_string = uri.to_string();
|
|
let expected = Response::from_ok(
|
|
1.into(),
|
|
json!([
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string.clone(): [
|
|
{
|
|
"newText": "\nimpl Data {\n \n}\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 25
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 25
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate impl for `Data`"
|
|
},
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string.clone(): [
|
|
{
|
|
"newText": "\nimpl Data {\n fn new(value: NumberOrString, address: u64) -> Self { Self { value, address } }\n}\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 25
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 25
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate `new`"
|
|
},
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string: [
|
|
{
|
|
"newText": "/// Add a brief description.\n/// \n/// ### Additional Information\n/// \n/// Provide information beyond the core purpose or functionality.\n", "range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 19
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 19
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate a documentation template"
|
|
}
|
|
]),
|
|
);
|
|
assert_json_eq!(expected, response.ok().unwrap());
|
|
code_action
|
|
}
|
|
|
|
pub(crate) async fn code_action_struct_type_params_request(
|
|
service: &mut LspService<Backend>,
|
|
uri: &Url,
|
|
) -> Request {
|
|
let params = json!({
|
|
"textDocument": {
|
|
"uri": uri
|
|
},
|
|
"range": {
|
|
"start": {
|
|
"line": 4,
|
|
"character": 9
|
|
},
|
|
"end": {
|
|
"line": 4,
|
|
"character": 9
|
|
}
|
|
},
|
|
"context": {
|
|
"diagnostics": [],
|
|
"triggerKind": 2
|
|
}
|
|
});
|
|
let code_action = build_request_with_id("textDocument/codeAction", params, 1);
|
|
let response = call_request(service, code_action.clone()).await;
|
|
let uri_string = uri.to_string();
|
|
let expected = Response::from_ok(
|
|
1.into(),
|
|
json!([
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string.clone(): [
|
|
{
|
|
"newText": "\nimpl<T> Data<T> {\n \n}\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 7
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 7
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate impl for `Data`"
|
|
},
|
|
{
|
|
"data": uri,
|
|
"disabled": {
|
|
"reason": "Struct Data already has a `new` function"
|
|
},
|
|
"edit": {
|
|
"changes": {
|
|
uri_string.clone(): [
|
|
{
|
|
"newText": " fn new(value: T) -> Self { Self { value } }\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 9
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 9
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate `new`"
|
|
},
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string: [
|
|
{
|
|
"newText": "/// Add a brief description.\n/// \n/// ### Additional Information\n/// \n/// Provide information beyond the core purpose or functionality.\n", "range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 4
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 4
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate a documentation template"
|
|
}
|
|
]),
|
|
);
|
|
assert_json_eq!(expected, response.ok().unwrap());
|
|
code_action
|
|
}
|
|
|
|
pub(crate) async fn code_action_struct_existing_impl_request(
|
|
service: &mut LspService<Backend>,
|
|
uri: &Url,
|
|
) -> Request {
|
|
let params = json!({
|
|
"textDocument": {
|
|
"uri": uri
|
|
},
|
|
"range": {
|
|
"start": {
|
|
"line": 2,
|
|
"character": 7
|
|
},
|
|
"end": {
|
|
"line": 2,
|
|
"character": 7
|
|
}
|
|
},
|
|
"context": {
|
|
"diagnostics": [],
|
|
"triggerKind": 2
|
|
}
|
|
});
|
|
let code_action = build_request_with_id("textDocument/codeAction", params, 1);
|
|
let response = call_request(service, code_action.clone()).await;
|
|
let uri_string = uri.to_string();
|
|
let expected = Response::from_ok(
|
|
1.into(),
|
|
json!([
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string.clone(): [
|
|
{
|
|
"newText": "\nimpl A {\n \n}\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 6
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 6
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate impl for `A`"
|
|
},
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string.clone(): [
|
|
{
|
|
"newText": " fn new(a: u64, b: u64) -> Self { Self { a, b } }\n",
|
|
"range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 8
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 8
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate `new`"
|
|
},
|
|
{
|
|
"data": uri,
|
|
"edit": {
|
|
"changes": {
|
|
uri_string: [
|
|
{
|
|
"newText": "/// Add a brief description.\n/// \n/// ### Additional Information\n/// \n/// Provide information beyond the core purpose or functionality.\n", "range": {
|
|
"end": {
|
|
"character": 0,
|
|
"line": 2
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 2
|
|
}
|
|
}
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"kind": "refactor",
|
|
"title": "Generate a documentation template"
|
|
}
|
|
]),
|
|
);
|
|
assert_json_eq!(expected, response.ok().unwrap());
|
|
code_action
|
|
}
|