summaryrefslogtreecommitdiff
path: root/cli/lsp/tsc.rs
diff options
context:
space:
mode:
authorKitson Kelly <me@kitsonkelly.com>2021-02-05 05:53:02 +1100
committerGitHub <noreply@github.com>2021-02-05 05:53:02 +1100
commitb77fcbc518428429e39f5ba94e41fcd0418ee7a0 (patch)
tree0051cc7e5ac0c8f83278de093a2be7209cf9fcfc /cli/lsp/tsc.rs
parent644a7ff2d70cbd8bfba4c87b75a047e79830c4b6 (diff)
feat(lsp): add TS quick fix code actions (#9396)
Diffstat (limited to 'cli/lsp/tsc.rs')
-rw-r--r--cli/lsp/tsc.rs115
1 files changed, 114 insertions, 1 deletions
diff --git a/cli/lsp/tsc.rs b/cli/lsp/tsc.rs
index 3fee900c6..579979b06 100644
--- a/cli/lsp/tsc.rs
+++ b/cli/lsp/tsc.rs
@@ -354,7 +354,7 @@ impl From<ScriptElementKind> for lsp::CompletionItemKind {
}
}
-#[derive(Debug, Clone, Deserialize)]
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
#[serde(rename_all = "camelCase")]
pub struct TextSpan {
pub start: u32,
@@ -710,6 +710,90 @@ impl DocumentHighlights {
}
}
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct TextChange {
+ span: TextSpan,
+ new_text: String,
+}
+
+impl TextChange {
+ pub fn as_text_edit(
+ &self,
+ line_index: &LineIndex,
+ ) -> lsp::OneOf<lsp::TextEdit, lsp::AnnotatedTextEdit> {
+ lsp::OneOf::Left(lsp::TextEdit {
+ range: self.span.to_range(line_index),
+ new_text: self.new_text.clone(),
+ })
+ }
+}
+
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct FileTextChanges {
+ file_name: String,
+ text_changes: Vec<TextChange>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ is_new_file: Option<bool>,
+}
+
+impl FileTextChanges {
+ pub async fn to_text_document_edit<F, Fut, V>(
+ &self,
+ index_provider: &F,
+ version_provider: &V,
+ ) -> Result<lsp::TextDocumentEdit, AnyError>
+ where
+ F: Fn(ModuleSpecifier) -> Fut + Clone,
+ Fut: Future<Output = Result<LineIndex, AnyError>>,
+ V: Fn(ModuleSpecifier) -> Option<i32>,
+ {
+ let specifier = ModuleSpecifier::resolve_url(&self.file_name)?;
+ let line_index = index_provider(specifier.clone()).await?;
+ let edits = self
+ .text_changes
+ .iter()
+ .map(|tc| tc.as_text_edit(&line_index))
+ .collect();
+ Ok(lsp::TextDocumentEdit {
+ text_document: lsp::OptionalVersionedTextDocumentIdentifier {
+ uri: specifier.as_url().clone(),
+ version: version_provider(specifier),
+ },
+ edits,
+ })
+ }
+}
+
+#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq)]
+#[serde(rename_all = "camelCase")]
+pub struct CodeFixAction {
+ pub description: String,
+ pub changes: Vec<FileTextChanges>,
+ // These are opaque types that should just be passed back when applying the
+ // action.
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub commands: Option<Vec<Value>>,
+ pub fix_name: String,
+ // It appears currently that all fixIds are strings, but the protocol
+ // specifies an opaque type, the problem is that we need to use the id as a
+ // hash key, and `Value` does not implement hash (and it could provide a false
+ // positive depending on JSON whitespace, so we deserialize it but it might
+ // break in the future)
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub fix_id: Option<String>,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ pub fix_all_description: Option<String>,
+}
+
+#[derive(Debug, Clone, Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct CombinedCodeActions {
+ pub changes: Vec<FileTextChanges>,
+ pub commands: Option<Vec<Value>>,
+}
+
#[derive(Debug, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct ReferenceEntry {
@@ -1215,8 +1299,12 @@ pub enum RequestMethod {
FindRenameLocations((ModuleSpecifier, u32, bool, bool, bool)),
/// Retrieve the text of an assets that exists in memory in the isolate.
GetAsset(ModuleSpecifier),
+ /// Retrieve code fixes for a range of a file with the provided error codes.
+ GetCodeFixes((ModuleSpecifier, u32, u32, Vec<String>)),
/// Get completion information at a given position (IntelliSense).
GetCompletions((ModuleSpecifier, u32, UserPreferences)),
+ /// Retrieve the combined code fixes for a fix id for a module.
+ GetCombinedCodeFix((ModuleSpecifier, Value)),
/// Get declaration information for a specific position.
GetDefinition((ModuleSpecifier, u32)),
/// Return diagnostics for given file.
@@ -1231,6 +1319,8 @@ pub enum RequestMethod {
GetQuickInfo((ModuleSpecifier, u32)),
/// Get document references for a specific position.
GetReferences((ModuleSpecifier, u32)),
+ /// Get the diagnostic codes that support some form of code fix.
+ GetSupportedCodeFixes,
}
impl RequestMethod {
@@ -1263,6 +1353,25 @@ impl RequestMethod {
"method": "getAsset",
"specifier": specifier,
}),
+ RequestMethod::GetCodeFixes((
+ specifier,
+ start_pos,
+ end_pos,
+ error_codes,
+ )) => json!({
+ "id": id,
+ "method": "getCodeFixes",
+ "specifier": specifier,
+ "startPosition": start_pos,
+ "endPosition": end_pos,
+ "errorCodes": error_codes,
+ }),
+ RequestMethod::GetCombinedCodeFix((specifier, fix_id)) => json!({
+ "id": id,
+ "method": "getCombinedCodeFix",
+ "specifier": specifier,
+ "fixId": fix_id,
+ }),
RequestMethod::GetCompletions((specifier, position, preferences)) => {
json!({
"id": id,
@@ -1317,6 +1426,10 @@ impl RequestMethod {
"specifier": specifier,
"position": position,
}),
+ RequestMethod::GetSupportedCodeFixes => json!({
+ "id": id,
+ "method": "getSupportedCodeFixes",
+ }),
}
}
}