ts_bridge/protocol/text_document/
code_action_resolve.rs1use anyhow::{Context, Result};
10use lsp_types::{CodeAction, CodeActionKind};
11use serde_json::{Value, json};
12
13use crate::protocol::text_document::code_action::{
14 CodeActionData, FixAllData, OrganizeImportsData, organize_imports_payload,
15 workspace_edit_from_tsserver_changes,
16};
17use crate::protocol::{AdapterResult, RequestSpec};
18use crate::rpc::{Priority, Route};
19
20pub fn handle(mut action: CodeAction) -> Option<RequestSpec> {
21 let data = action.data.take()?;
22 let data: CodeActionData = serde_json::from_value(data).ok()?;
23
24 match data {
25 CodeActionData::FixAll(fix_all) => build_fix_all_request(action, fix_all),
26 CodeActionData::OrganizeImports(data) => build_organize_imports_request(action, data),
27 }
28}
29
30fn build_fix_all_request(action: CodeAction, fix_all: FixAllData) -> Option<RequestSpec> {
31 let request = json!({
32 "command": "getCombinedCodeFix",
33 "arguments": {
34 "scope": {
35 "type": "file",
36 "args": {
37 "file": fix_all.file,
38 }
39 },
40 "fixId": fix_all.fix_id,
41 }
42 });
43
44 let context = serde_json::to_value(action).ok()?;
45
46 Some(RequestSpec {
47 route: Route::Syntax,
48 payload: request,
49 priority: Priority::Low,
50 on_response: Some(adapt_fix_all_response),
51 response_context: Some(context),
52 })
53}
54
55fn build_organize_imports_request(
56 action: CodeAction,
57 data: OrganizeImportsData,
58) -> Option<RequestSpec> {
59 let request = organize_imports_payload(&data.file);
60 let context = serde_json::to_value(action).ok()?;
61
62 Some(RequestSpec {
63 route: Route::Syntax,
64 payload: request,
65 priority: Priority::Low,
66 on_response: Some(adapt_organize_imports_response),
67 response_context: Some(context),
68 })
69}
70
71fn adapt_fix_all_response(payload: &Value, context: Option<&Value>) -> Result<AdapterResult> {
72 let mut action: CodeAction =
73 serde_json::from_value(context.cloned().context("missing code action context")?)?;
74 let body = payload
75 .get("body")
76 .context("tsserver fix-all missing body")?;
77 let combined = body
78 .get("changes")
79 .or_else(|| body.get("FileChanges"))
80 .and_then(|value| value.as_array())
81 .cloned()
82 .unwrap_or_default();
83
84 if let Some(edit) = workspace_edit_from_tsserver_changes(&combined) {
85 action.edit = Some(edit);
86 }
87
88 if action.kind.is_none() {
89 action.kind = Some(CodeActionKind::SOURCE_FIX_ALL);
90 }
91
92 Ok(AdapterResult::ready(serde_json::to_value(action)?))
93}
94
95fn adapt_organize_imports_response(
96 payload: &Value,
97 context: Option<&Value>,
98) -> Result<AdapterResult> {
99 let mut action: CodeAction =
100 serde_json::from_value(context.cloned().context("missing code action context")?)?;
101 let changes = payload
102 .get("body")
103 .and_then(|value| value.as_array())
104 .cloned()
105 .unwrap_or_default();
106
107 if let Some(edit) = workspace_edit_from_tsserver_changes(&changes) {
108 action.edit = Some(edit);
109 }
110
111 if action.kind.is_none() {
112 action.kind = Some(CodeActionKind::SOURCE_ORGANIZE_IMPORTS);
113 }
114
115 Ok(AdapterResult::ready(serde_json::to_value(action)?))
116}