|
| 1 | +import * as vscode from "vscode"; |
| 2 | + |
| 3 | +import type { FeatureLifecycle } from "$extension/lsp-client/languageClientFeature"; |
| 4 | + |
| 5 | +const PARTICIPANT_ID = "sac-language-support.sac"; |
| 6 | + |
| 7 | +function chatResult(command: string): vscode.ChatResult { |
| 8 | + return { |
| 9 | + metadata: { |
| 10 | + command, |
| 11 | + }, |
| 12 | + }; |
| 13 | +} |
| 14 | + |
| 15 | +function buildDefaultResponse(prompt: string): string { |
| 16 | + const normalizedPrompt = prompt.trim(); |
| 17 | + if (normalizedPrompt.length === 0) { |
| 18 | + return "Share what you want to do with SaC code, for example format guards, explain diagnostics, or review overloading."; |
| 19 | + } |
| 20 | + |
| 21 | + return [ |
| 22 | + "I can help with SaC-specific workflows:", |
| 23 | + "", |
| 24 | + "- `/sac-diagnose`: explain likely compiler issues and suggest targeted fixes", |
| 25 | + "- `/sac-format`: apply SaC formatting conventions (including multiline guard style)", |
| 26 | + "- `/sac-overload`: design or review overload changes safely", |
| 27 | + "", |
| 28 | + `Prompt summary: ${normalizedPrompt}`, |
| 29 | + ].join("\n"); |
| 30 | +} |
| 31 | + |
| 32 | +export class ChatParticipantFeature implements FeatureLifecycle { |
| 33 | + private participant: vscode.ChatParticipant | undefined; |
| 34 | + |
| 35 | + public async activate(): Promise<void> { |
| 36 | + const enabled = vscode.workspace.getConfiguration("sac").get<boolean>("features.chatParticipant.enable", true); |
| 37 | + if (!enabled) { |
| 38 | + return; |
| 39 | + } |
| 40 | + |
| 41 | + const handler: vscode.ChatRequestHandler = async (request, _context, stream): Promise<vscode.ChatResult> => { |
| 42 | + if (request.command === "sac-diagnose") { |
| 43 | + stream.markdown( |
| 44 | + "SaC diagnostics workflow:\n\n1. Run `sac2c` with minimal flags first.\n2. Fix the first parser/type error before secondary errors.\n3. Re-run and iterate until the first blocking error is resolved.", |
| 45 | + ); |
| 46 | + return chatResult("sac-diagnose"); |
| 47 | + } |
| 48 | + |
| 49 | + if (request.command === "sac-format") { |
| 50 | + stream.markdown( |
| 51 | + "SaC guard formatting:\n\n```sac\nint safe_div(int a, int b)\n | b != 0\n , a >= 0\n{\n return a / b;\n}\n```\n", |
| 52 | + ); |
| 53 | + return chatResult("sac-format"); |
| 54 | + } |
| 55 | + |
| 56 | + if (request.command === "sac-overload") { |
| 57 | + stream.markdown( |
| 58 | + "Overloading checklist:\n\n- Keep overload family semantics aligned.\n- Add the narrowest overload needed.\n- Call out ambiguous conversion risks.", |
| 59 | + ); |
| 60 | + return chatResult("sac-overload"); |
| 61 | + } |
| 62 | + |
| 63 | + stream.markdown(buildDefaultResponse(request.prompt)); |
| 64 | + return chatResult("default"); |
| 65 | + }; |
| 66 | + |
| 67 | + this.participant = vscode.chat.createChatParticipant(PARTICIPANT_ID, handler); |
| 68 | + this.participant.iconPath = new vscode.ThemeIcon("symbol-key"); |
| 69 | + this.participant.followupProvider = { |
| 70 | + provideFollowups(result: vscode.ChatResult): vscode.ChatFollowup[] { |
| 71 | + const command = typeof result.metadata?.command === "string" ? result.metadata.command : "default"; |
| 72 | + |
| 73 | + if (command === "sac-diagnose") { |
| 74 | + return [{ prompt: "show a minimal fix strategy", label: "Suggest a minimal fix strategy" }]; |
| 75 | + } |
| 76 | + |
| 77 | + if (command === "sac-format") { |
| 78 | + return [{ prompt: "apply guard formatting to my function", label: "Format guard lines" }]; |
| 79 | + } |
| 80 | + |
| 81 | + return [ |
| 82 | + { prompt: "diagnose this SaC compiler error", label: "Diagnose compiler error" }, |
| 83 | + { prompt: "format this SaC function", label: "Format SaC function" }, |
| 84 | + ]; |
| 85 | + }, |
| 86 | + }; |
| 87 | + } |
| 88 | + |
| 89 | + public async deactivate(): Promise<void> { |
| 90 | + this.participant?.dispose(); |
| 91 | + this.participant = undefined; |
| 92 | + } |
| 93 | +} |
0 commit comments