diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 045cee3266..063cbd1747 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -10,7 +10,10 @@ "problemMatcher": { "owner": "typescript", "pattern": "$tsc", - "fileLocation": ["relative", "${workspaceRoot}/editors/code"] + "fileLocation": [ + "relative", + "${workspaceRoot}/editors/code" + ] }, "path": "editors/code/" }, @@ -18,30 +21,40 @@ "label": "Build Lsp", "type": "shell", "command": "cargo build", - "problemMatcher": { - "owner": "rust", - "fileLocation": ["relative", "${workspaceRoot}"], - "pattern": [ - { - "regexp": "^(warning|warn|error)(?:\\[(.*?)\\])?: (.*)$", - "severity": 1, - "code": 2, - "message": 3 - }, - { - "regexp": "^[\\s->=]*(.*?):(\\d*):(\\d*)\\s*$", - "file": 1, - "line": 2, - "column": 3 - } - ] - } + "problemMatcher": "$rustc" }, { "label": "Build All", "group": "build", - "dependsOn": ["Build Extension", "Build Lsp"], + "dependsOn": [ + "Build Extension", + "Build Lsp" + ], "problemMatcher": [] + }, + { + "label": "cargo watch", + "group": "build", + "isBackground": true, + "type": "shell", + "command": "cargo", + "args": [ + "watch" + ], + "problemMatcher": "$rustc-watch" + }, + { + "label": "cargo watch tests", + "group": "build", + "isBackground": true, + "type": "shell", + "command": "cargo", + "args": [ + "watch", + "-x", + "check --tests" + ], + "problemMatcher": "$rustc-watch" } ] -} +} \ No newline at end of file diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index bf08f5203a..66a625c6c1 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -550,7 +550,7 @@ The Some variant fn test_hover_infer_associated_method_result() { let (analysis, position) = single_file_with_position( " - struct Thing { x: u32 }; + struct Thing { x: u32 } impl Thing { fn new() -> Thing { @@ -616,7 +616,7 @@ The Some variant fn test_hover_self() { let (analysis, position) = single_file_with_position( " - struct Thing { x: u32 }; + struct Thing { x: u32 } impl Thing { fn new() -> Self { Self<|> { x: 0 } @@ -630,7 +630,7 @@ The Some variant let (analysis, position) = single_file_with_position( " - struct Thing { x: u32 }; + struct Thing { x: u32 } impl Thing { fn new() -> Self<|> { Self { x: 0 } @@ -644,7 +644,7 @@ The Some variant let (analysis, position) = single_file_with_position( " - enum Thing { A }; + enum Thing { A } impl Thing { pub fn new() -> Self<|> { Thing::A @@ -658,7 +658,7 @@ The Some variant let (analysis, position) = single_file_with_position( " - enum Thing { A }; + enum Thing { A } impl Thing { pub fn thing(a: Self<|>) { } diff --git a/crates/ra_lsp_server/src/init.rs b/crates/ra_lsp_server/src/init.rs index 0b7a47a0b9..1b77e03125 100644 --- a/crates/ra_lsp_server/src/init.rs +++ b/crates/ra_lsp_server/src/init.rs @@ -1,39 +1,61 @@ -use serde::{Deserialize, Deserializer}; - -/// Client provided initialization options -#[derive(Deserialize, Clone, Copy, Debug)] -#[serde(rename_all = "camelCase")] -pub struct InitializationOptions { - /// Whether the client supports our custom highlighting publishing decorations. - /// This is different to the highlightingOn setting, which is whether the user - /// wants our custom highlighting to be used. - /// - /// Defaults to `true` - #[serde(default = "bool_true", deserialize_with = "nullable_bool_true")] - pub publish_decorations: bool, - - /// Whether or not the workspace loaded notification should be sent - /// - /// Defaults to `true` - #[serde(default = "bool_true", deserialize_with = "nullable_bool_true")] - pub show_workspace_loaded: bool, -} - -impl Default for InitializationOptions { - fn default() -> InitializationOptions { - InitializationOptions { publish_decorations: true, show_workspace_loaded: true } - } -} - -fn bool_true() -> bool { - true -} - -/// Deserializes a null value to a bool true by default -fn nullable_bool_true<'de, D>(deserializer: D) -> Result -where - D: Deserializer<'de>, -{ - let opt = Option::deserialize(deserializer)?; - Ok(opt.unwrap_or(true)) -} +use serde::{Deserialize, Deserializer}; + +/// Client provided initialization options +#[derive(Deserialize, Clone, Copy, Debug, PartialEq, Eq)] +#[serde(rename_all = "camelCase", default)] +pub struct InitializationOptions { + /// Whether the client supports our custom highlighting publishing decorations. + /// This is different to the highlightingOn setting, which is whether the user + /// wants our custom highlighting to be used. + /// + /// Defaults to `false` + #[serde(deserialize_with = "nullable_bool_false")] + pub publish_decorations: bool, + + /// Whether or not the workspace loaded notification should be sent + /// + /// Defaults to `true` + #[serde(deserialize_with = "nullable_bool_true")] + pub show_workspace_loaded: bool, +} + +impl Default for InitializationOptions { + fn default() -> InitializationOptions { + InitializationOptions { publish_decorations: false, show_workspace_loaded: true } + } +} + +/// Deserializes a null value to a bool false by default +fn nullable_bool_false<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let opt = Option::deserialize(deserializer)?; + Ok(opt.unwrap_or(false)) +} + +/// Deserializes a null value to a bool true by default +fn nullable_bool_true<'de, D>(deserializer: D) -> Result +where + D: Deserializer<'de>, +{ + let opt = Option::deserialize(deserializer)?; + Ok(opt.unwrap_or(true)) +} + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn deserialize_init_options_defaults() { + // check that null == default for both fields + let default = InitializationOptions::default(); + assert_eq!(default, serde_json::from_str(r#"{}"#).unwrap()); + assert_eq!( + default, + serde_json::from_str(r#"{"publishDecorations":null, "showWorkspaceLoaded":null}"#) + .unwrap() + ); + } +} diff --git a/crates/ra_parser/src/grammar/expressions/atom.rs b/crates/ra_parser/src/grammar/expressions/atom.rs index 9f282c74d3..53bb26c5ff 100644 --- a/crates/ra_parser/src/grammar/expressions/atom.rs +++ b/crates/ra_parser/src/grammar/expressions/atom.rs @@ -93,6 +93,11 @@ pub(super) fn atom_expr(p: &mut Parser, r: Restrictions) -> Option<(CompletedMar } } + ASYNC_KW if la == L_CURLY => { + let m = p.start(); + p.bump(); + block_expr(p, Some(m)) + } MATCH_KW => match_expr(p), UNSAFE_KW if la == L_CURLY => { let m = p.start(); diff --git a/crates/ra_parser/src/grammar/items.rs b/crates/ra_parser/src/grammar/items.rs index ab9d2de906..a057c81675 100644 --- a/crates/ra_parser/src/grammar/items.rs +++ b/crates/ra_parser/src/grammar/items.rs @@ -86,9 +86,15 @@ pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem { } let mut has_mods = false; - // modifiers - has_mods |= p.eat(CONST_KW); + // modifiers + // test_err async_without_semicolon + // fn foo() { let _ = async {} } + has_mods |= p.eat(CONST_KW); + if p.at(ASYNC_KW) && p.nth(1) != L_CURLY { + p.eat(ASYNC_KW); + has_mods = true; + } // test_err unsafe_block_in_mod // fn foo(){} unsafe { } fn bar(){} if p.at(UNSAFE_KW) && p.nth(1) != L_CURLY { @@ -110,6 +116,9 @@ pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem { // items let kind = match p.current() { + // test async_fn + // async fn foo() {} + // test extern_fn // extern fn foo() {} diff --git a/crates/ra_parser/src/syntax_kind/generated.rs b/crates/ra_parser/src/syntax_kind/generated.rs index 0eed44ecfd..03247ae38c 100644 --- a/crates/ra_parser/src/syntax_kind/generated.rs +++ b/crates/ra_parser/src/syntax_kind/generated.rs @@ -66,6 +66,7 @@ pub enum SyntaxKind { SHR, SHLEQ, SHREQ, + ASYNC_KW, USE_KW, FN_KW, STRUCT_KW, @@ -233,6 +234,7 @@ use self::SyntaxKind::*; impl SyntaxKind { pub fn is_keyword(self) -> bool { match self { + | ASYNC_KW | USE_KW | FN_KW | STRUCT_KW @@ -403,6 +405,7 @@ impl SyntaxKind { SHR => &SyntaxInfo { name: "SHR" }, SHLEQ => &SyntaxInfo { name: "SHLEQ" }, SHREQ => &SyntaxInfo { name: "SHREQ" }, + ASYNC_KW => &SyntaxInfo { name: "ASYNC_KW" }, USE_KW => &SyntaxInfo { name: "USE_KW" }, FN_KW => &SyntaxInfo { name: "FN_KW" }, STRUCT_KW => &SyntaxInfo { name: "STRUCT_KW" }, @@ -570,6 +573,7 @@ impl SyntaxKind { } pub fn from_keyword(ident: &str) -> Option { let kw = match ident { + "async" => ASYNC_KW, "use" => USE_KW, "fn" => FN_KW, "struct" => STRUCT_KW, diff --git a/crates/ra_syntax/src/grammar.ron b/crates/ra_syntax/src/grammar.ron index b7a2d1c01b..66f1339c10 100644 --- a/crates/ra_syntax/src/grammar.ron +++ b/crates/ra_syntax/src/grammar.ron @@ -59,6 +59,7 @@ Grammar( [">>=", "SHREQ"], ], keywords: [ + "async", "use", "fn", "struct", diff --git a/crates/ra_syntax/tests/data/lexer/0011_keywords.rs b/crates/ra_syntax/tests/data/lexer/0011_keywords.rs index e6bf64d4d6..1e91bff4e7 100644 --- a/crates/ra_syntax/tests/data/lexer/0011_keywords.rs +++ b/crates/ra_syntax/tests/data/lexer/0011_keywords.rs @@ -1,3 +1,3 @@ -fn use struct trait enum impl true false as extern crate +async fn use struct trait enum impl true false as extern crate mod pub self super in where for loop while if match const static mut type ref let else move return diff --git a/crates/ra_syntax/tests/data/lexer/0011_keywords.txt b/crates/ra_syntax/tests/data/lexer/0011_keywords.txt index d6a1abe8ac..22c00eefb6 100644 --- a/crates/ra_syntax/tests/data/lexer/0011_keywords.txt +++ b/crates/ra_syntax/tests/data/lexer/0011_keywords.txt @@ -1,3 +1,5 @@ +ASYNC_KW 5 "async" +WHITESPACE 1 " " FN_KW 2 "fn" WHITESPACE 1 " " USE_KW 3 "use" diff --git a/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.rs b/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.rs new file mode 100644 index 0000000000..9a423248c2 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.rs @@ -0,0 +1 @@ +fn foo() { let _ = async {} } diff --git a/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.txt b/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.txt new file mode 100644 index 0000000000..bb9a2d0295 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/err/0007_async_without_semicolon.txt @@ -0,0 +1,31 @@ +SOURCE_FILE@[0; 30) + FN_DEF@[0; 29) + FN_KW@[0; 2) + WHITESPACE@[2; 3) + NAME@[3; 6) + IDENT@[3; 6) "foo" + PARAM_LIST@[6; 8) + L_PAREN@[6; 7) + R_PAREN@[7; 8) + WHITESPACE@[8; 9) + BLOCK@[9; 29) + L_CURLY@[9; 10) + WHITESPACE@[10; 11) + LET_STMT@[11; 27) + LET_KW@[11; 14) + WHITESPACE@[14; 15) + PLACEHOLDER_PAT@[15; 16) + UNDERSCORE@[15; 16) + WHITESPACE@[16; 17) + EQ@[17; 18) + WHITESPACE@[18; 19) + BLOCK_EXPR@[19; 27) + ASYNC_KW@[19; 24) + WHITESPACE@[24; 25) + BLOCK@[25; 27) + L_CURLY@[25; 26) + R_CURLY@[26; 27) + err: `expected SEMI` + WHITESPACE@[27; 28) + R_CURLY@[28; 29) + WHITESPACE@[29; 30) diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.rs b/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.rs new file mode 100644 index 0000000000..f4adcb62b3 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.rs @@ -0,0 +1 @@ +async fn foo() {} diff --git a/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.txt b/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.txt new file mode 100644 index 0000000000..d1a706ecc3 --- /dev/null +++ b/crates/ra_syntax/tests/data/parser/inline/ok/0124_async_fn.txt @@ -0,0 +1,16 @@ +SOURCE_FILE@[0; 18) + FN_DEF@[0; 17) + ASYNC_KW@[0; 5) + WHITESPACE@[5; 6) + FN_KW@[6; 8) + WHITESPACE@[8; 9) + NAME@[9; 12) + IDENT@[9; 12) "foo" + PARAM_LIST@[12; 14) + L_PAREN@[12; 13) + R_PAREN@[13; 14) + WHITESPACE@[14; 15) + BLOCK@[15; 17) + L_CURLY@[15; 16) + R_CURLY@[16; 17) + WHITESPACE@[17; 18) diff --git a/editors/code/package.json b/editors/code/package.json index 47eaac878a..0a1e84b4ab 100644 --- a/editors/code/package.json +++ b/editors/code/package.json @@ -215,8 +215,8 @@ "${workspaceRoot}" ], "background": { - "beginsPattern": "^\\[Running ", - "endsPattern": "^(\\[Finished running\\]|To learn more, run the command again with --verbose\\.)$" + "beginsPattern": "^\\[Running\\b", + "endsPattern": "^\\[Finished running\\b" }, "pattern": "$rustc" }