@nghyane/arcane 0.1.13 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +28 -0
- package/package.json +21 -70
- package/scripts/format-prompts.ts +1 -3
- package/src/cli/args.ts +2 -7
- package/src/cli/config-cli.ts +1 -1
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/setup-cli.ts +1 -1
- package/src/cli/update-cli.ts +1 -1
- package/src/cli/web-search-cli.ts +1 -1
- package/src/cli.ts +0 -1
- package/src/commands/config.ts +1 -1
- package/src/commands/grep.ts +1 -1
- package/src/commands/jupyter.ts +1 -1
- package/src/commands/plugin.ts +1 -1
- package/src/commands/setup.ts +1 -1
- package/src/commands/shell.ts +1 -1
- package/src/commands/ssh.ts +1 -1
- package/src/commands/stats.ts +1 -1
- package/src/commands/update.ts +1 -1
- package/src/config/model-registry.ts +3 -4
- package/src/config/model-resolver.ts +36 -9
- package/src/config/prompt-templates.ts +1 -9
- package/src/config/settings-schema.ts +32 -88
- package/src/config/settings.ts +3 -4
- package/src/debug/index.ts +1 -1
- package/src/debug/log-formatting.ts +1 -1
- package/src/debug/log-viewer.ts +2 -2
- package/src/discovery/helpers.ts +13 -3
- package/src/exa/index.ts +1 -35
- package/src/exa/render.ts +30 -190
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/custom-tools/loader.ts +1 -1
- package/src/extensibility/custom-tools/types.ts +5 -1
- package/src/extensibility/custom-tools/wrapper.ts +1 -1
- package/src/extensibility/extensions/runner.ts +1 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/extensions/wrapper.ts +7 -15
- package/src/extensibility/hooks/runner.ts +1 -1
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +1 -1
- package/src/index.ts +13 -13
- package/src/lsp/index.ts +77 -24
- package/src/lsp/render.ts +34 -583
- package/src/lsp/types.ts +3 -3
- package/src/lsp/utils.ts +1 -1
- package/src/main.ts +1 -1
- package/src/mcp/tool-bridge.ts +1 -24
- package/src/modes/components/assistant-message.ts +7 -7
- package/src/modes/components/bash-execution.ts +50 -112
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/branch-summary-message.ts +16 -10
- package/src/modes/components/compaction-summary-message.ts +20 -12
- package/src/modes/components/context-group.ts +106 -0
- package/src/modes/components/custom-message.ts +4 -5
- package/src/modes/components/diff.ts +2 -2
- package/src/modes/components/dynamic-border.ts +1 -1
- package/src/modes/components/extensions/extension-dashboard.ts +1 -1
- package/src/modes/components/extensions/extension-list.ts +1 -1
- package/src/modes/components/extensions/inspector-panel.ts +1 -1
- package/src/modes/components/footer.ts +2 -2
- package/src/modes/components/history-search.ts +1 -1
- package/src/modes/components/hook-editor.ts +1 -1
- package/src/modes/components/hook-input.ts +1 -1
- package/src/modes/components/hook-message.ts +4 -5
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/components/index.ts +0 -2
- package/src/modes/components/keybinding-hints.ts +1 -1
- package/src/modes/components/login-dialog.ts +1 -1
- package/src/modes/components/mcp-add-wizard.ts +1 -1
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/oauth-selector.ts +1 -1
- package/src/modes/components/plugin-settings.ts +1 -1
- package/src/modes/components/python-execution.ts +51 -91
- package/src/modes/components/queue-mode-selector.ts +1 -1
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/settings-defs.ts +5 -10
- package/src/modes/components/settings-selector.ts +1 -1
- package/src/modes/components/show-images-selector.ts +1 -1
- package/src/modes/components/skill-message.ts +4 -4
- package/src/modes/components/status-line/segments.ts +2 -2
- package/src/modes/components/status-line/separators.ts +1 -1
- package/src/modes/components/status-line-segment-editor.ts +1 -1
- package/src/modes/components/status-line.ts +1 -1
- package/src/modes/components/theme-selector.ts +1 -1
- package/src/modes/components/thinking-selector.ts +1 -1
- package/src/modes/components/todo-display.ts +2 -4
- package/src/modes/components/todo-reminder.ts +4 -4
- package/src/modes/components/tool-execution.ts +118 -440
- package/src/modes/components/tool-image-display.ts +107 -0
- package/src/modes/components/tree-selector.ts +2 -2
- package/src/modes/components/ttsr-notification.ts +4 -17
- package/src/modes/components/user-message-selector.ts +1 -1
- package/src/modes/components/user-message.ts +9 -10
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +58 -187
- package/src/modes/controllers/extension-ui-controller.ts +1 -1
- package/src/modes/controllers/input-controller.ts +3 -1
- package/src/modes/controllers/mcp-command-controller.ts +1 -1
- package/src/modes/controllers/selector-controller.ts +3 -26
- package/src/modes/controllers/ssh-command-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +3 -7
- package/src/modes/print-mode.ts +5 -5
- package/src/modes/rpc/rpc-mode.ts +1 -1
- package/src/modes/types.ts +1 -2
- package/src/modes/utils/ui-helpers.ts +34 -32
- package/src/patch/edit-tool.ts +742 -0
- package/src/patch/index.ts +32 -898
- package/src/patch/schemas.ts +208 -0
- package/src/patch/shared.ts +83 -151
- package/src/prompts/agents/explore.md +22 -37
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/agents/librarian.md +29 -20
- package/src/prompts/agents/oracle.md +9 -2
- package/src/prompts/agents/reviewer.md +14 -48
- package/src/prompts/agents/task.md +16 -8
- package/src/prompts/compaction/branch-summary.md +4 -1
- package/src/prompts/compaction/compaction-summary.md +4 -1
- package/src/prompts/system/subagent-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +162 -178
- package/src/prompts/system/verification-reminder.md +6 -0
- package/src/sdk.ts +0 -9
- package/src/session/agent-session.ts +244 -1459
- package/src/session/model-controller.ts +406 -0
- package/src/session/retry-utils.ts +71 -0
- package/src/session/session-manager.ts +22 -186
- package/src/session/session-types.ts +312 -0
- package/src/session/stats.ts +387 -0
- package/src/session/streaming-edit.ts +258 -0
- package/src/session/ttsr.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +0 -8
- package/src/stt/recorder.ts +2 -2
- package/src/system-prompt.ts +1 -14
- package/src/task/agents.ts +7 -33
- package/src/task/executor.ts +50 -438
- package/src/task/index.ts +104 -71
- package/src/task/progress-tracker.ts +390 -0
- package/src/task/render.ts +371 -187
- package/src/task/subprocess-tool-registry.ts +1 -1
- package/src/task/types.ts +14 -47
- package/src/tools/ask.ts +31 -42
- package/src/tools/bash-interactive.ts +2 -2
- package/src/tools/bash-interceptor.ts +2 -2
- package/src/tools/bash-normalize.ts +1 -1
- package/src/tools/bash-skill-urls.ts +2 -2
- package/src/tools/bash.ts +87 -136
- package/src/tools/browser.ts +54 -84
- package/src/tools/create-tools.ts +186 -0
- package/src/tools/default-renderer.ts +104 -0
- package/src/tools/explore.ts +11 -10
- package/src/tools/fetch.ts +24 -114
- package/src/tools/find.ts +48 -132
- package/src/tools/gemini-image.ts +5 -15
- package/src/tools/github.ts +450 -0
- package/src/tools/grep.ts +43 -179
- package/src/tools/index.ts +35 -198
- package/src/tools/json-tree.ts +3 -3
- package/src/tools/librarian.ts +18 -18
- package/src/tools/list-limit.ts +2 -2
- package/src/tools/notebook.ts +35 -87
- package/src/tools/oracle.ts +25 -25
- package/src/tools/output-meta.ts +89 -4
- package/src/tools/output-utils.ts +2 -2
- package/src/tools/python.ts +86 -637
- package/src/tools/read.ts +36 -119
- package/src/tools/reviewer-tool.ts +19 -21
- package/src/tools/search-code.ts +128 -0
- package/src/tools/ssh.ts +67 -126
- package/src/tools/subagent-tool.ts +197 -123
- package/src/tools/todo-write.ts +15 -31
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/undo-edit.ts +30 -67
- package/src/tools/write.ts +78 -127
- package/src/tui/code-cell.ts +4 -4
- package/src/tui/file-list.ts +2 -2
- package/src/tui/output-block.ts +1 -1
- package/src/tui/status-line.ts +1 -1
- package/src/tui/tree-list.ts +2 -2
- package/src/tui/types.ts +1 -1
- package/src/tui/utils.ts +1 -1
- package/src/{tools → ui}/render-utils.ts +87 -126
- package/src/utils/external-editor.ts +4 -4
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/index.ts +30 -0
- package/src/utils/tools-manager.ts +9 -19
- package/src/web/github-client.ts +290 -0
- package/src/web/scrapers/github.ts +11 -62
- package/src/web/search/auth.ts +1 -3
- package/src/web/search/index.ts +82 -46
- package/src/web/search/provider.ts +11 -16
- package/src/web/search/providers/grep.ts +160 -0
- package/src/web/search/render.ts +48 -235
- package/src/web/search/types.ts +1 -1
- package/src/commands/commit.ts +0 -36
- package/src/commit/agentic/agent.ts +0 -311
- package/src/commit/agentic/fallback.ts +0 -96
- package/src/commit/agentic/index.ts +0 -359
- package/src/commit/agentic/prompts/analyze-file.md +0 -22
- package/src/commit/agentic/prompts/session-user.md +0 -25
- package/src/commit/agentic/prompts/split-confirm.md +0 -1
- package/src/commit/agentic/prompts/system.md +0 -38
- package/src/commit/agentic/state.ts +0 -69
- package/src/commit/agentic/tools/analyze-file.ts +0 -118
- package/src/commit/agentic/tools/git-file-diff.ts +0 -194
- package/src/commit/agentic/tools/git-hunk.ts +0 -50
- package/src/commit/agentic/tools/git-overview.ts +0 -84
- package/src/commit/agentic/tools/index.ts +0 -56
- package/src/commit/agentic/tools/propose-changelog.ts +0 -128
- package/src/commit/agentic/tools/propose-commit.ts +0 -154
- package/src/commit/agentic/tools/recent-commits.ts +0 -81
- package/src/commit/agentic/tools/split-commit.ts +0 -280
- package/src/commit/agentic/topo-sort.ts +0 -44
- package/src/commit/agentic/trivial.ts +0 -51
- package/src/commit/agentic/validation.ts +0 -200
- package/src/commit/analysis/conventional.ts +0 -165
- package/src/commit/analysis/index.ts +0 -4
- package/src/commit/analysis/scope.ts +0 -242
- package/src/commit/analysis/summary.ts +0 -112
- package/src/commit/analysis/validation.ts +0 -66
- package/src/commit/changelog/detect.ts +0 -37
- package/src/commit/changelog/generate.ts +0 -110
- package/src/commit/changelog/index.ts +0 -234
- package/src/commit/changelog/parse.ts +0 -44
- package/src/commit/cli.ts +0 -93
- package/src/commit/git/diff.ts +0 -148
- package/src/commit/git/errors.ts +0 -9
- package/src/commit/git/index.ts +0 -211
- package/src/commit/git/operations.ts +0 -54
- package/src/commit/index.ts +0 -5
- package/src/commit/map-reduce/index.ts +0 -64
- package/src/commit/map-reduce/map-phase.ts +0 -178
- package/src/commit/map-reduce/reduce-phase.ts +0 -145
- package/src/commit/map-reduce/utils.ts +0 -9
- package/src/commit/message.ts +0 -11
- package/src/commit/model-selection.ts +0 -69
- package/src/commit/pipeline.ts +0 -243
- package/src/commit/prompts/analysis-system.md +0 -148
- package/src/commit/prompts/analysis-user.md +0 -38
- package/src/commit/prompts/changelog-system.md +0 -50
- package/src/commit/prompts/changelog-user.md +0 -18
- package/src/commit/prompts/file-observer-system.md +0 -24
- package/src/commit/prompts/file-observer-user.md +0 -8
- package/src/commit/prompts/reduce-system.md +0 -50
- package/src/commit/prompts/reduce-user.md +0 -17
- package/src/commit/prompts/summary-retry.md +0 -3
- package/src/commit/prompts/summary-system.md +0 -38
- package/src/commit/prompts/summary-user.md +0 -13
- package/src/commit/prompts/types-description.md +0 -2
- package/src/commit/types.ts +0 -109
- package/src/commit/utils/exclusions.ts +0 -42
- package/src/mcp/render.ts +0 -123
- package/src/modes/components/agent-dashboard.ts +0 -1130
- package/src/modes/components/codemode-group.ts +0 -369
- package/src/modes/components/read-tool-group.ts +0 -119
- package/src/modes/components/visual-truncate.ts +0 -63
- package/src/prompts/system/subagent-user-prompt.md +0 -8
- package/src/prompts/tools/ask.md +0 -44
- package/src/prompts/tools/bash.md +0 -24
- package/src/prompts/tools/browser.md +0 -33
- package/src/prompts/tools/calculator.md +0 -12
- package/src/prompts/tools/explore.md +0 -29
- package/src/prompts/tools/fetch.md +0 -16
- package/src/prompts/tools/find.md +0 -18
- package/src/prompts/tools/gemini-image.md +0 -23
- package/src/prompts/tools/grep.md +0 -28
- package/src/prompts/tools/hashline.md +0 -232
- package/src/prompts/tools/librarian.md +0 -24
- package/src/prompts/tools/lsp.md +0 -28
- package/src/prompts/tools/oracle.md +0 -26
- package/src/prompts/tools/patch.md +0 -74
- package/src/prompts/tools/python.md +0 -66
- package/src/prompts/tools/read.md +0 -36
- package/src/prompts/tools/replace.md +0 -38
- package/src/prompts/tools/reviewer.md +0 -41
- package/src/prompts/tools/ssh.md +0 -51
- package/src/prompts/tools/task-summary.md +0 -28
- package/src/prompts/tools/task.md +0 -146
- package/src/prompts/tools/todo-write.md +0 -65
- package/src/prompts/tools/undo-edit.md +0 -7
- package/src/prompts/tools/web-search.md +0 -19
- package/src/prompts/tools/write.md +0 -18
- package/src/task/batch.ts +0 -102
- package/src/task/discovery.ts +0 -126
- package/src/task/parallel.ts +0 -84
- package/src/task/template.ts +0 -32
- package/src/tools/calculator.ts +0 -537
- package/src/tools/jtd-to-typescript.ts +0 -198
- package/src/tools/renderers.ts +0 -60
- package/src/tools/tool-result.ts +0 -86
- /package/src/{modes/theme → theme}/dark.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-dracula.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-gruvbox.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-solarized.json +0 -0
- /package/src/{modes/theme → theme}/defaults/dark-tokyo-night.json +0 -0
- /package/src/{modes/theme → theme}/defaults/index.ts +0 -0
- /package/src/{modes/theme → theme}/defaults/light-catppuccin.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-github.json +0 -0
- /package/src/{modes/theme → theme}/defaults/light-solarized.json +0 -0
- /package/src/{modes/theme → theme}/light.json +0 -0
- /package/src/{modes/theme → theme}/mermaid-cache.ts +0 -0
- /package/src/{modes/theme → theme}/theme-schema.json +0 -0
- /package/src/{modes/theme → theme}/theme.ts +0 -0
package/src/lsp/render.ts
CHANGED
|
@@ -1,26 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* LSP Tool TUI Rendering
|
|
3
|
-
*
|
|
4
|
-
* Renders LSP tool calls and results in the TUI with:
|
|
5
|
-
* - Syntax-highlighted hover information
|
|
6
|
-
* - Color-coded diagnostics by severity
|
|
7
|
-
* - Grouped references and symbols
|
|
8
|
-
* - Collapsible/expandable views
|
|
9
3
|
*/
|
|
10
4
|
import type { RenderResultOptions } from "@nghyane/arcane-agent";
|
|
11
|
-
import { type HighlightColors, highlightCode as nativeHighlightCode, supportsLanguage } from "@nghyane/arcane-natives";
|
|
12
5
|
import { type Component, Text } from "@nghyane/arcane-tui";
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
formatExpandHint,
|
|
16
|
-
formatMoreItems,
|
|
17
|
-
formatStatusIcon,
|
|
18
|
-
shortenPath,
|
|
19
|
-
TRUNCATE_LENGTHS,
|
|
20
|
-
truncateToWidth,
|
|
21
|
-
} from "../tools/render-utils";
|
|
6
|
+
import type { Theme } from "../theme/theme";
|
|
22
7
|
import { renderStatusLine } from "../tui";
|
|
23
|
-
import {
|
|
8
|
+
import { formatCount, formatErrorMessage, shortenPath, TRUNCATE_LENGTHS, truncateToWidth } from "../ui/render-utils";
|
|
24
9
|
import type { LspParams, LspToolDetails } from "./types";
|
|
25
10
|
|
|
26
11
|
// =============================================================================
|
|
@@ -97,593 +82,59 @@ export function renderCall(args: LspParams, _options: RenderResultOptions, theme
|
|
|
97
82
|
// =============================================================================
|
|
98
83
|
|
|
99
84
|
/**
|
|
100
|
-
* Render LSP tool result
|
|
101
|
-
* Detects hover, diagnostics, references, symbols, etc. and formats accordingly.
|
|
85
|
+
* Render LSP tool result as a single dim status line.
|
|
102
86
|
*/
|
|
103
87
|
export function renderResult(
|
|
104
88
|
result: { content: Array<{ type: string; text?: string }>; details?: LspToolDetails; isError?: boolean },
|
|
105
|
-
|
|
89
|
+
_options: RenderResultOptions,
|
|
106
90
|
theme: Theme,
|
|
107
91
|
args?: LspParams & { file?: string; files?: string[] },
|
|
108
92
|
): Component {
|
|
109
93
|
const content = result.content?.[0];
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
94
|
+
const text = content?.type === "text" ? (content.text ?? "") : "";
|
|
95
|
+
|
|
96
|
+
if (result.isError || text.startsWith("Error:")) {
|
|
97
|
+
const errorText = result.content?.find(c => c.type === "text")?.text || "Unknown error";
|
|
98
|
+
return new Text(formatErrorMessage(errorText, theme), 0, 0);
|
|
114
99
|
}
|
|
115
100
|
|
|
116
|
-
const
|
|
117
|
-
const
|
|
101
|
+
const request = args ?? result.details?.request;
|
|
102
|
+
const action = (request?.action ?? result.details?.action ?? "request").replace(/_/g, " ");
|
|
103
|
+
const meta: string[] = [];
|
|
118
104
|
|
|
119
|
-
//
|
|
120
|
-
const codeBlockMatch = text.match(/```(\w*)\n([\s\S]*?)```/);
|
|
105
|
+
// Extract counts from result text
|
|
121
106
|
const errorMatch = text.match(/(\d+)\s+error\(s\)/);
|
|
122
107
|
const warningMatch = text.match(/(\d+)\s+warning\(s\)/);
|
|
123
108
|
const refMatch = text.match(/(\d+)\s+reference\(s\)/);
|
|
124
|
-
const
|
|
125
|
-
const hasStatusError = text.includes(theme.status.error);
|
|
109
|
+
const renameMatch = text.match(/Applied.*?(\d+)\s+file/);
|
|
126
110
|
|
|
127
|
-
|
|
128
|
-
const request = args ?? result.details?.request;
|
|
129
|
-
const requestLines: string[] = [];
|
|
111
|
+
let description = "";
|
|
130
112
|
if (request?.file) {
|
|
131
|
-
|
|
113
|
+
description = shortenPath(request.file);
|
|
114
|
+
if (request.line !== undefined) description += `:${request.line}`;
|
|
132
115
|
} else if (request?.files?.length === 1) {
|
|
133
|
-
|
|
116
|
+
description = shortenPath(request.files[0]);
|
|
134
117
|
} else if (request?.files?.length) {
|
|
135
|
-
|
|
136
|
-
}
|
|
137
|
-
if (request?.line !== undefined) {
|
|
138
|
-
const col = request.column !== undefined ? `:${request.column}` : "";
|
|
139
|
-
requestLines.push(theme.fg("dim", `line ${request.line}${col}`));
|
|
140
|
-
}
|
|
141
|
-
if (request?.end_line !== undefined) {
|
|
142
|
-
const endCol = request.end_character !== undefined ? `:${request.end_character}` : "";
|
|
143
|
-
requestLines.push(theme.fg("dim", `end ${request.end_line}${endCol}`));
|
|
144
|
-
}
|
|
145
|
-
if (request?.query) requestLines.push(theme.fg("dim", `query: ${request.query}`));
|
|
146
|
-
if (request?.new_name) requestLines.push(theme.fg("dim", `new name: ${request.new_name}`));
|
|
147
|
-
if (request?.apply !== undefined) requestLines.push(theme.fg("dim", `apply: ${request.apply ? "true" : "false"}`));
|
|
148
|
-
if (request?.include_declaration !== undefined) {
|
|
149
|
-
requestLines.push(theme.fg("dim", `include declaration: ${request.include_declaration ? "true" : "false"}`));
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const outputBlock = new CachedOutputBlock();
|
|
153
|
-
|
|
154
|
-
return {
|
|
155
|
-
render(width: number): string[] {
|
|
156
|
-
// Read mutable state at render time
|
|
157
|
-
const { expanded, isPartial, spinnerFrame } = options;
|
|
158
|
-
|
|
159
|
-
// Determine label, state, bodyLines based on type + current expanded
|
|
160
|
-
let label = "Result";
|
|
161
|
-
let state: "success" | "warning" | "error" = "success";
|
|
162
|
-
let bodyLines: string[] = [];
|
|
163
|
-
|
|
164
|
-
if (codeBlockMatch) {
|
|
165
|
-
label = "Hover";
|
|
166
|
-
bodyLines = renderHover(codeBlockMatch, text, lines, expanded, theme);
|
|
167
|
-
} else if (errorMatch || warningMatch || hasStatusError) {
|
|
168
|
-
label = "Diagnostics";
|
|
169
|
-
const errorCount = errorMatch ? Number.parseInt(errorMatch[1], 10) : 0;
|
|
170
|
-
const warnCount = warningMatch ? Number.parseInt(warningMatch[1], 10) : 0;
|
|
171
|
-
state = errorCount > 0 ? "error" : warnCount > 0 ? "warning" : "success";
|
|
172
|
-
bodyLines = renderDiagnostics(errorMatch, warningMatch, lines, expanded, theme);
|
|
173
|
-
} else if (refMatch) {
|
|
174
|
-
label = "References";
|
|
175
|
-
bodyLines = renderReferences(refMatch, lines, expanded, theme);
|
|
176
|
-
} else if (symbolsMatch) {
|
|
177
|
-
label = "Symbols";
|
|
178
|
-
bodyLines = renderSymbols(symbolsMatch, lines, expanded, theme);
|
|
179
|
-
} else {
|
|
180
|
-
label = "Response";
|
|
181
|
-
bodyLines = renderGeneric(text, lines, expanded, theme);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const actionLabel = (request?.action ?? result.details?.action ?? label.toLowerCase()).replace(/_/g, " ");
|
|
185
|
-
const status = isPartial ? "running" : result.isError ? "error" : "success";
|
|
186
|
-
const icon = formatStatusIcon(status, theme, spinnerFrame);
|
|
187
|
-
const header = `${icon} LSP ${actionLabel}`;
|
|
188
|
-
|
|
189
|
-
return outputBlock.render(
|
|
190
|
-
{
|
|
191
|
-
header,
|
|
192
|
-
state,
|
|
193
|
-
sections: [
|
|
194
|
-
...(requestLines.length > 0 ? [{ lines: requestLines }] : []),
|
|
195
|
-
{ label: theme.fg("toolTitle", "Response"), lines: bodyLines },
|
|
196
|
-
],
|
|
197
|
-
width,
|
|
198
|
-
applyBg: false,
|
|
199
|
-
},
|
|
200
|
-
theme,
|
|
201
|
-
);
|
|
202
|
-
},
|
|
203
|
-
invalidate() {
|
|
204
|
-
outputBlock.invalidate();
|
|
205
|
-
},
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
// =============================================================================
|
|
210
|
-
// Hover Rendering
|
|
211
|
-
// =============================================================================
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
* Render hover information with syntax-highlighted code blocks.
|
|
215
|
-
*/
|
|
216
|
-
function renderHover(
|
|
217
|
-
codeBlockMatch: RegExpMatchArray,
|
|
218
|
-
fullText: string,
|
|
219
|
-
_lines: string[],
|
|
220
|
-
expanded: boolean,
|
|
221
|
-
theme: Theme,
|
|
222
|
-
): string[] {
|
|
223
|
-
const lang = codeBlockMatch[1] || "";
|
|
224
|
-
const code = codeBlockMatch[2].trim();
|
|
225
|
-
const codeStart = codeBlockMatch.index ?? 0;
|
|
226
|
-
const beforeCode = fullText.slice(0, codeStart).trimEnd();
|
|
227
|
-
const afterCode = fullText.slice(fullText.indexOf("```", 3) + 3).trim();
|
|
228
|
-
|
|
229
|
-
const codeLines = highlightCode(code, lang, theme);
|
|
230
|
-
const icon = theme.styledSymbol("status.info", "accent");
|
|
231
|
-
const langLabel = lang ? theme.fg("mdCodeBlockBorder", ` ${lang}`) : "";
|
|
232
|
-
|
|
233
|
-
if (expanded) {
|
|
234
|
-
const h = theme.boxSharp.horizontal;
|
|
235
|
-
const v = theme.boxSharp.vertical;
|
|
236
|
-
const top = `${theme.boxSharp.topLeft}${h.repeat(3)}`;
|
|
237
|
-
const bottom = `${theme.boxSharp.bottomLeft}${h.repeat(3)}`;
|
|
238
|
-
let output = `${icon}${langLabel}`;
|
|
239
|
-
if (beforeCode) {
|
|
240
|
-
for (const line of beforeCode.split("\n")) {
|
|
241
|
-
output += `\n ${theme.fg("muted", line)}`;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
output += `\n ${theme.fg("mdCodeBlockBorder", top)}`;
|
|
245
|
-
for (const line of codeLines) {
|
|
246
|
-
output += `\n ${theme.fg("mdCodeBlockBorder", v)} ${line}`;
|
|
247
|
-
}
|
|
248
|
-
output += `\n ${theme.fg("mdCodeBlockBorder", bottom)}`;
|
|
249
|
-
if (afterCode) {
|
|
250
|
-
output += `\n ${theme.fg("muted", afterCode)}`;
|
|
251
|
-
}
|
|
252
|
-
return output.split("\n");
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
// Collapsed view
|
|
256
|
-
const firstCodeLine = codeLines[0] || "";
|
|
257
|
-
const hasMore = codeLines.length > 1 || Boolean(afterCode) || Boolean(beforeCode);
|
|
258
|
-
const expandHint = formatExpandHint(theme, expanded, hasMore);
|
|
259
|
-
|
|
260
|
-
let output = `${icon}${langLabel}${expandHint}`;
|
|
261
|
-
if (beforeCode) {
|
|
262
|
-
const preview = truncateToWidth(beforeCode, TRUNCATE_LENGTHS.TITLE);
|
|
263
|
-
output += `\n ${theme.fg("dim", theme.tree.branch)} ${theme.fg("muted", preview)}`;
|
|
264
|
-
}
|
|
265
|
-
const h = theme.boxSharp.horizontal;
|
|
266
|
-
const v = theme.boxSharp.vertical;
|
|
267
|
-
const bottom = `${theme.boxSharp.bottomLeft}${h.repeat(3)}`;
|
|
268
|
-
output += `\n ${theme.fg("mdCodeBlockBorder", v)} ${firstCodeLine}`;
|
|
269
|
-
|
|
270
|
-
if (codeLines.length > 1) {
|
|
271
|
-
output += `\n ${theme.fg("mdCodeBlockBorder", v)} ${theme.fg("muted", `… ${codeLines.length - 1} more lines`)}`;
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
if (afterCode) {
|
|
275
|
-
const docPreview = truncateToWidth(afterCode, TRUNCATE_LENGTHS.TITLE);
|
|
276
|
-
output += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", docPreview)}`;
|
|
277
|
-
} else {
|
|
278
|
-
output += `\n ${theme.fg("mdCodeBlockBorder", bottom)}`;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
return output.split("\n");
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Syntax highlight code using native highlighter.
|
|
286
|
-
*/
|
|
287
|
-
function highlightCode(codeText: string, language: string, theme: Theme): string[] {
|
|
288
|
-
const validLang = language && supportsLanguage(language) ? language : undefined;
|
|
289
|
-
try {
|
|
290
|
-
const colors: HighlightColors = {
|
|
291
|
-
comment: theme.getFgAnsi("syntaxComment"),
|
|
292
|
-
keyword: theme.getFgAnsi("syntaxKeyword"),
|
|
293
|
-
function: theme.getFgAnsi("syntaxFunction"),
|
|
294
|
-
variable: theme.getFgAnsi("syntaxVariable"),
|
|
295
|
-
string: theme.getFgAnsi("syntaxString"),
|
|
296
|
-
number: theme.getFgAnsi("syntaxNumber"),
|
|
297
|
-
type: theme.getFgAnsi("syntaxType"),
|
|
298
|
-
operator: theme.getFgAnsi("syntaxOperator"),
|
|
299
|
-
punctuation: theme.getFgAnsi("syntaxPunctuation"),
|
|
300
|
-
inserted: theme.getFgAnsi("toolDiffAdded"),
|
|
301
|
-
deleted: theme.getFgAnsi("toolDiffRemoved"),
|
|
302
|
-
};
|
|
303
|
-
return nativeHighlightCode(codeText, validLang, colors).split("\n");
|
|
304
|
-
} catch {
|
|
305
|
-
return codeText.split("\n");
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// =============================================================================
|
|
310
|
-
// Diagnostics Rendering
|
|
311
|
-
// =============================================================================
|
|
312
|
-
|
|
313
|
-
function formatDiagnosticLocation(file: string, line: string | number, col: string | number, theme: Theme): string {
|
|
314
|
-
const lang = getLanguageFromPath(file);
|
|
315
|
-
const icon = theme.fg("muted", theme.getLangIcon(lang));
|
|
316
|
-
return `${icon} ${file}:${line}:${col}`;
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Render diagnostics with color-coded severity.
|
|
321
|
-
*/
|
|
322
|
-
function renderDiagnostics(
|
|
323
|
-
errorMatch: RegExpMatchArray | null,
|
|
324
|
-
warningMatch: RegExpMatchArray | null,
|
|
325
|
-
lines: string[],
|
|
326
|
-
expanded: boolean,
|
|
327
|
-
theme: Theme,
|
|
328
|
-
): string[] {
|
|
329
|
-
const errorCount = errorMatch ? Number.parseInt(errorMatch[1], 10) : 0;
|
|
330
|
-
const warnCount = warningMatch ? Number.parseInt(warningMatch[1], 10) : 0;
|
|
331
|
-
|
|
332
|
-
const icon =
|
|
333
|
-
errorCount > 0
|
|
334
|
-
? theme.styledSymbol("status.error", "error")
|
|
335
|
-
: warnCount > 0
|
|
336
|
-
? theme.styledSymbol("status.warning", "warning")
|
|
337
|
-
: theme.styledSymbol("status.success", "success");
|
|
338
|
-
|
|
339
|
-
const meta: string[] = [];
|
|
340
|
-
if (errorCount > 0) meta.push(`${errorCount} error${errorCount !== 1 ? "s" : ""}`);
|
|
341
|
-
if (warnCount > 0) meta.push(`${warnCount} warning${warnCount !== 1 ? "s" : ""}`);
|
|
342
|
-
if (meta.length === 0) meta.push("No issues");
|
|
343
|
-
|
|
344
|
-
const diagLines = lines.filter(l => l.includes(theme.status.error) || /:\d+:\d+/.test(l));
|
|
345
|
-
const parsedDiagnostics = diagLines
|
|
346
|
-
.map(line => parseDiagnosticLine(line))
|
|
347
|
-
.filter((diag): diag is ParsedDiagnostic => diag !== null);
|
|
348
|
-
const fallbackDiagnostics: RawDiagnostic[] = diagLines.map(line => ({ raw: line.trim() }));
|
|
349
|
-
|
|
350
|
-
if (expanded) {
|
|
351
|
-
let output = `${icon} ${theme.fg("dim", meta.join(theme.sep.dot))}`;
|
|
352
|
-
const items: DiagnosticItem[] = parsedDiagnostics.length > 0 ? parsedDiagnostics : fallbackDiagnostics;
|
|
353
|
-
for (let i = 0; i < items.length; i++) {
|
|
354
|
-
const item = items[i];
|
|
355
|
-
const isLast = i === items.length - 1;
|
|
356
|
-
const branch = isLast ? theme.tree.last : theme.tree.branch;
|
|
357
|
-
const detailPrefix = isLast ? " " : `${theme.tree.vertical} `;
|
|
358
|
-
if ("raw" in item) {
|
|
359
|
-
output += `\n ${theme.fg("dim", branch)} ${theme.fg("muted", item.raw)}`;
|
|
360
|
-
continue;
|
|
361
|
-
}
|
|
362
|
-
const severityColor = severityToColor(item.severity);
|
|
363
|
-
const location = formatDiagnosticLocation(item.file, item.line, item.col, theme);
|
|
364
|
-
output += `\n ${theme.fg("dim", branch)} ${theme.fg(severityColor, location)} ${theme.fg(
|
|
365
|
-
"dim",
|
|
366
|
-
`[${item.severity}]`,
|
|
367
|
-
)}`;
|
|
368
|
-
if (item.message) {
|
|
369
|
-
output += `\n ${theme.fg("dim", detailPrefix)}${theme.fg(
|
|
370
|
-
"muted",
|
|
371
|
-
truncateToWidth(item.message, TRUNCATE_LENGTHS.LINE),
|
|
372
|
-
)}`;
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
return output.split("\n");
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Collapsed view
|
|
379
|
-
const previewItems: DiagnosticItem[] =
|
|
380
|
-
parsedDiagnostics.length > 0 ? parsedDiagnostics.slice(0, 3) : fallbackDiagnostics.slice(0, 3);
|
|
381
|
-
const remaining =
|
|
382
|
-
(parsedDiagnostics.length > 0 ? parsedDiagnostics.length : fallbackDiagnostics.length) - previewItems.length;
|
|
383
|
-
const expandHint = formatExpandHint(theme, expanded, remaining > 0);
|
|
384
|
-
let output = `${icon} ${theme.fg("dim", meta.join(theme.sep.dot))}${expandHint}`;
|
|
385
|
-
for (let i = 0; i < previewItems.length; i++) {
|
|
386
|
-
const item = previewItems[i];
|
|
387
|
-
const isLast = i === previewItems.length - 1 && remaining <= 0;
|
|
388
|
-
const branch = isLast ? theme.tree.last : theme.tree.branch;
|
|
389
|
-
if ("raw" in item) {
|
|
390
|
-
output += `\n ${theme.fg("dim", branch)} ${theme.fg("muted", item.raw)}`;
|
|
391
|
-
continue;
|
|
392
|
-
}
|
|
393
|
-
const severityColor = severityToColor(item.severity);
|
|
394
|
-
const location = formatDiagnosticLocation(item.file, item.line, item.col, theme);
|
|
395
|
-
const message = item.message
|
|
396
|
-
? ` ${theme.fg("muted", truncateToWidth(item.message, TRUNCATE_LENGTHS.CONTENT))}`
|
|
397
|
-
: "";
|
|
398
|
-
output += `\n ${theme.fg("dim", branch)} ${theme.fg(severityColor, location)}${message}`;
|
|
399
|
-
}
|
|
400
|
-
if (remaining > 0) {
|
|
401
|
-
output += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", `… ${remaining} more`)}`;
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
return output.split("\n");
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// =============================================================================
|
|
408
|
-
// References Rendering
|
|
409
|
-
// =============================================================================
|
|
410
|
-
|
|
411
|
-
/**
|
|
412
|
-
* Render references grouped by file.
|
|
413
|
-
*/
|
|
414
|
-
function renderReferences(refMatch: RegExpMatchArray, lines: string[], expanded: boolean, theme: Theme): string[] {
|
|
415
|
-
const refCount = Number.parseInt(refMatch[1], 10);
|
|
416
|
-
const icon =
|
|
417
|
-
refCount > 0 ? theme.styledSymbol("status.success", "success") : theme.styledSymbol("status.warning", "warning");
|
|
418
|
-
|
|
419
|
-
const locLines = lines.filter(l => /^\s*\S+:\d+:\d+/.test(l));
|
|
420
|
-
|
|
421
|
-
// Group by file
|
|
422
|
-
const byFile = new Map<string, Array<[string, string]>>();
|
|
423
|
-
for (const loc of locLines) {
|
|
424
|
-
const match = loc.trim().match(/^(.+):(\d+):(\d+)$/);
|
|
425
|
-
if (match) {
|
|
426
|
-
const [, file, line, col] = match;
|
|
427
|
-
if (!byFile.has(file)) byFile.set(file, []);
|
|
428
|
-
byFile.get(file)!.push([line, col]);
|
|
429
|
-
}
|
|
118
|
+
description = `${request.files.length} files`;
|
|
430
119
|
}
|
|
431
120
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
const fileBranch = isLastFile ? theme.tree.last : theme.tree.branch;
|
|
444
|
-
const fileCont = isLastFile ? " " : `${theme.tree.vertical} `;
|
|
445
|
-
|
|
446
|
-
const fileMeta = `${locs.length} reference${locs.length !== 1 ? "s" : ""}`;
|
|
447
|
-
output += `\n ${theme.fg("dim", fileBranch)} ${theme.fg("accent", file)} ${theme.fg("dim", fileMeta)}`;
|
|
448
|
-
|
|
449
|
-
if (maxLocsPerFile > 0) {
|
|
450
|
-
const locsToShow = locs.slice(0, maxLocsPerFile);
|
|
451
|
-
for (let li = 0; li < locsToShow.length; li++) {
|
|
452
|
-
const [line, col] = locsToShow[li];
|
|
453
|
-
const isLastLoc = li === locsToShow.length - 1 && locs.length <= maxLocsPerFile;
|
|
454
|
-
const locBranch = isLastLoc ? theme.tree.last : theme.tree.branch;
|
|
455
|
-
const locCont = isLastLoc ? " " : `${theme.tree.vertical} `;
|
|
456
|
-
output += `\n ${theme.fg("dim", fileCont)}${theme.fg("dim", locBranch)} ${theme.fg(
|
|
457
|
-
"muted",
|
|
458
|
-
`line ${line}, col ${col}`,
|
|
459
|
-
)}`;
|
|
460
|
-
if (expanded) {
|
|
461
|
-
const context = `at ${file}:${line}:${col}`;
|
|
462
|
-
output += `\n ${theme.fg("dim", fileCont)}${theme.fg("dim", locCont)}${theme.fg(
|
|
463
|
-
"muted",
|
|
464
|
-
truncateToWidth(context, TRUNCATE_LENGTHS.LINE),
|
|
465
|
-
)}`;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
if (locs.length > maxLocsPerFile) {
|
|
469
|
-
output += `\n ${theme.fg("dim", fileCont)}${theme.fg("dim", theme.tree.last)} ${theme.fg(
|
|
470
|
-
"muted",
|
|
471
|
-
`… ${locs.length - maxLocsPerFile} more`,
|
|
472
|
-
)}`;
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
|
|
477
|
-
if (files.length > maxFiles) {
|
|
478
|
-
output += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg(
|
|
479
|
-
"muted",
|
|
480
|
-
formatMoreItems(files.length - maxFiles, "file"),
|
|
481
|
-
)}`;
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
return output;
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
if (expanded) {
|
|
488
|
-
return renderGrouped(files.length, 3, false).split("\n");
|
|
121
|
+
if (errorMatch) meta.push(`${errorMatch[1]} errors`);
|
|
122
|
+
if (warningMatch) meta.push(`${warningMatch[1]} warnings`);
|
|
123
|
+
if (refMatch) {
|
|
124
|
+
meta.push(formatCount("ref", Number.parseInt(refMatch[1], 10)));
|
|
125
|
+
const fileSet = new Set(
|
|
126
|
+
text
|
|
127
|
+
.split("\n")
|
|
128
|
+
.map(l => l.match(/^\s*(\S+):\d+:\d+/)?.[1])
|
|
129
|
+
.filter(Boolean),
|
|
130
|
+
);
|
|
131
|
+
if (fileSet.size > 0) meta.push(formatCount("file", fileSet.size));
|
|
489
132
|
}
|
|
133
|
+
if (renameMatch) meta.push(`${renameMatch[1]} files`);
|
|
134
|
+
if (request?.new_name && request?.query) meta.push(`${request.query} → ${request.new_name}`);
|
|
490
135
|
|
|
491
|
-
return
|
|
136
|
+
return new Text(renderStatusLine({ icon: "success", title: `LSP ${action}`, description, meta }, theme), 0, 0);
|
|
492
137
|
}
|
|
493
138
|
|
|
494
139
|
// =============================================================================
|
|
495
|
-
//
|
|
496
|
-
// =============================================================================
|
|
497
|
-
|
|
498
|
-
/**
|
|
499
|
-
* Render document symbols in a hierarchical tree.
|
|
500
|
-
*/
|
|
501
|
-
function renderSymbols(symbolsMatch: RegExpMatchArray, lines: string[], expanded: boolean, theme: Theme): string[] {
|
|
502
|
-
const fileName = symbolsMatch[1];
|
|
503
|
-
const icon = theme.styledSymbol("status.info", "accent");
|
|
504
|
-
|
|
505
|
-
interface SymbolInfo {
|
|
506
|
-
name: string;
|
|
507
|
-
line: string;
|
|
508
|
-
indent: number;
|
|
509
|
-
icon: string;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
const symbolLines = lines.filter(l => l.includes("@") && l.includes("line"));
|
|
513
|
-
const symbols: SymbolInfo[] = [];
|
|
514
|
-
|
|
515
|
-
for (const line of symbolLines) {
|
|
516
|
-
const indent = line.match(/^(\s*)/)?.[1].length ?? 0;
|
|
517
|
-
const symMatch = line.trim().match(/^(\S+)\s+(.+?)\s*@\s*line\s*(\d+)/);
|
|
518
|
-
if (symMatch) {
|
|
519
|
-
symbols.push({ icon: symMatch[1], name: symMatch[2], line: symMatch[3], indent });
|
|
520
|
-
}
|
|
521
|
-
}
|
|
522
|
-
|
|
523
|
-
const isLastSibling = (i: number): boolean => {
|
|
524
|
-
const myIndent = symbols[i].indent;
|
|
525
|
-
for (let j = i + 1; j < symbols.length; j++) {
|
|
526
|
-
const nextIndent = symbols[j].indent;
|
|
527
|
-
if (nextIndent === myIndent) return false;
|
|
528
|
-
if (nextIndent < myIndent) return true;
|
|
529
|
-
}
|
|
530
|
-
return true;
|
|
531
|
-
};
|
|
532
|
-
|
|
533
|
-
const getPrefix = (i: number): string => {
|
|
534
|
-
const myIndent = symbols[i].indent;
|
|
535
|
-
if (myIndent === 0) return " ";
|
|
536
|
-
|
|
537
|
-
let prefix = " ";
|
|
538
|
-
for (let level = 2; level <= myIndent; level += 2) {
|
|
539
|
-
let ancestorIdx = -1;
|
|
540
|
-
for (let j = i - 1; j >= 0; j--) {
|
|
541
|
-
if (symbols[j].indent === level - 2) {
|
|
542
|
-
ancestorIdx = j;
|
|
543
|
-
break;
|
|
544
|
-
}
|
|
545
|
-
}
|
|
546
|
-
if (ancestorIdx >= 0 && isLastSibling(ancestorIdx)) {
|
|
547
|
-
prefix += " ";
|
|
548
|
-
} else {
|
|
549
|
-
prefix += `${theme.tree.vertical} `;
|
|
550
|
-
}
|
|
551
|
-
}
|
|
552
|
-
return prefix;
|
|
553
|
-
};
|
|
554
|
-
|
|
555
|
-
const topLevelCount = symbols.filter(s => s.indent === 0).length;
|
|
556
|
-
|
|
557
|
-
if (expanded) {
|
|
558
|
-
let output = `${icon} ${theme.fg("dim", `in ${fileName}`)}`;
|
|
559
|
-
|
|
560
|
-
for (let i = 0; i < symbols.length; i++) {
|
|
561
|
-
const sym = symbols[i];
|
|
562
|
-
const prefix = getPrefix(i);
|
|
563
|
-
const isLast = isLastSibling(i);
|
|
564
|
-
const branch = isLast ? theme.tree.last : theme.tree.branch;
|
|
565
|
-
const detailPrefix = isLast ? " " : `${theme.tree.vertical} `;
|
|
566
|
-
output += `\n${prefix}${theme.fg("dim", branch)} ${theme.fg("accent", sym.icon)} ${theme.fg("accent", sym.name)}`;
|
|
567
|
-
output += `\n${prefix}${theme.fg("dim", detailPrefix)}${theme.fg("muted", `line ${sym.line}`)}`;
|
|
568
|
-
}
|
|
569
|
-
return output.split("\n");
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// Collapsed: show first 3 top-level symbols
|
|
573
|
-
const topLevel = symbols.filter(s => s.indent === 0).slice(0, 3);
|
|
574
|
-
const hasMoreSymbols = symbols.length > topLevel.length;
|
|
575
|
-
const expandHint = formatExpandHint(theme, expanded, hasMoreSymbols);
|
|
576
|
-
let output = `${icon} ${theme.fg("dim", `in ${fileName}`)}${expandHint}`;
|
|
577
|
-
for (let i = 0; i < topLevel.length; i++) {
|
|
578
|
-
const sym = topLevel[i];
|
|
579
|
-
const isLast = i === topLevel.length - 1 && topLevelCount <= 3;
|
|
580
|
-
const branch = isLast ? theme.tree.last : theme.tree.branch;
|
|
581
|
-
output += `\n ${theme.fg("dim", branch)} ${theme.fg("accent", sym.icon)} ${theme.fg("accent", sym.name)} ${theme.fg(
|
|
582
|
-
"muted",
|
|
583
|
-
`line ${sym.line}`,
|
|
584
|
-
)}`;
|
|
585
|
-
}
|
|
586
|
-
if (topLevelCount > 3) {
|
|
587
|
-
output += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg("muted", `… ${topLevelCount - 3} more`)}`;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
return output.split("\n");
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
// =============================================================================
|
|
594
|
-
// Generic Rendering
|
|
595
|
-
// =============================================================================
|
|
596
|
-
|
|
597
|
-
/**
|
|
598
|
-
* Generic fallback rendering for unknown result types.
|
|
599
|
-
*/
|
|
600
|
-
function renderGeneric(text: string, lines: string[], expanded: boolean, theme: Theme): string[] {
|
|
601
|
-
const hasError = text.includes("Error:") || text.includes(theme.status.error);
|
|
602
|
-
const hasSuccess = text.includes(theme.status.success) || text.includes("Applied");
|
|
603
|
-
|
|
604
|
-
const icon =
|
|
605
|
-
hasError && !hasSuccess
|
|
606
|
-
? theme.styledSymbol("status.error", "error")
|
|
607
|
-
: hasSuccess && !hasError
|
|
608
|
-
? theme.styledSymbol("status.success", "success")
|
|
609
|
-
: theme.styledSymbol("status.info", "accent");
|
|
610
|
-
|
|
611
|
-
if (expanded) {
|
|
612
|
-
let output = `${icon} ${theme.fg("dim", "Output")}`;
|
|
613
|
-
for (let i = 0; i < lines.length; i++) {
|
|
614
|
-
const isLast = i === lines.length - 1;
|
|
615
|
-
const branch = isLast ? theme.tree.last : theme.tree.branch;
|
|
616
|
-
output += `\n ${theme.fg("dim", branch)} ${lines[i]}`;
|
|
617
|
-
}
|
|
618
|
-
return output.split("\n");
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
const firstLine = lines[0] || "No output";
|
|
622
|
-
const expandHint = formatExpandHint(theme, expanded, lines.length > 1);
|
|
623
|
-
let output = `${icon} ${theme.fg("dim", truncateToWidth(firstLine, TRUNCATE_LENGTHS.TITLE))}${expandHint}`;
|
|
624
|
-
|
|
625
|
-
if (lines.length > 1) {
|
|
626
|
-
const previewLines = lines.slice(1, 4);
|
|
627
|
-
for (let i = 0; i < previewLines.length; i++) {
|
|
628
|
-
const isLast = i === previewLines.length - 1 && lines.length <= 4;
|
|
629
|
-
const branch = isLast ? theme.tree.last : theme.tree.branch;
|
|
630
|
-
output += `\n ${theme.fg("dim", branch)} ${theme.fg(
|
|
631
|
-
"dim",
|
|
632
|
-
truncateToWidth(previewLines[i].trim(), TRUNCATE_LENGTHS.CONTENT),
|
|
633
|
-
)}`;
|
|
634
|
-
}
|
|
635
|
-
if (lines.length > 4) {
|
|
636
|
-
output += `\n ${theme.fg("dim", theme.tree.last)} ${theme.fg(
|
|
637
|
-
"muted",
|
|
638
|
-
formatMoreItems(lines.length - 4, "line"),
|
|
639
|
-
)}`;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
return output.split("\n");
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
// =============================================================================
|
|
647
|
-
// Parsing Helpers
|
|
648
|
-
// =============================================================================
|
|
649
|
-
|
|
650
|
-
interface ParsedDiagnostic {
|
|
651
|
-
file: string;
|
|
652
|
-
line: string;
|
|
653
|
-
col: string;
|
|
654
|
-
severity: string;
|
|
655
|
-
message: string;
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
interface RawDiagnostic {
|
|
659
|
-
raw: string;
|
|
660
|
-
}
|
|
661
|
-
|
|
662
|
-
type DiagnosticItem = ParsedDiagnostic | RawDiagnostic;
|
|
663
|
-
|
|
664
|
-
function parseDiagnosticLine(line: string): ParsedDiagnostic | null {
|
|
665
|
-
const match = line.trim().match(/^(.*):(\d+):(\d+)\s+\[(\w+)\]\s*(.*)$/);
|
|
666
|
-
if (!match) return null;
|
|
667
|
-
const [, file, lineNum, colNum, severity, message] = match;
|
|
668
|
-
return { file, line: lineNum, col: colNum, severity: severity.toLowerCase(), message };
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
function severityToColor(severity: string): "error" | "warning" | "accent" | "dim" {
|
|
672
|
-
switch (severity) {
|
|
673
|
-
case "error":
|
|
674
|
-
return "error";
|
|
675
|
-
case "warning":
|
|
676
|
-
return "warning";
|
|
677
|
-
case "info":
|
|
678
|
-
return "accent";
|
|
679
|
-
default:
|
|
680
|
-
return "dim";
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
export const lspToolRenderer = {
|
|
685
|
-
renderCall,
|
|
686
|
-
renderResult,
|
|
687
|
-
mergeCallAndResult: true,
|
|
688
|
-
inline: true,
|
|
689
|
-
};
|
|
140
|
+
// Hover Rendering
|
package/src/lsp/types.ts
CHANGED
|
@@ -10,14 +10,14 @@ export const lspSchema = Type.Object({
|
|
|
10
10
|
action: StringEnum(["diagnostics", "definition", "references", "hover", "symbols", "rename", "status", "reload"], {
|
|
11
11
|
description: "LSP operation",
|
|
12
12
|
}),
|
|
13
|
-
files: Type.Optional(Type.Array(Type.String({ description: "File
|
|
14
|
-
file: Type.Optional(Type.String({ description: "File path" })),
|
|
13
|
+
files: Type.Optional(Type.Array(Type.String(), { description: "File paths for diagnostics" })),
|
|
14
|
+
file: Type.Optional(Type.String({ description: "File path for symbol operations" })),
|
|
15
15
|
line: Type.Optional(Type.Number({ description: "Line number (1-indexed)" })),
|
|
16
16
|
column: Type.Optional(Type.Number({ description: "Column number (1-indexed)" })),
|
|
17
17
|
end_line: Type.Optional(Type.Number({ description: "End line for range (1-indexed)" })),
|
|
18
18
|
end_character: Type.Optional(Type.Number({ description: "End column for range (1-indexed)" })),
|
|
19
19
|
query: Type.Optional(Type.String({ description: "Search query or SSR pattern" })),
|
|
20
|
-
new_name: Type.Optional(Type.String({ description: "New name for rename" })),
|
|
20
|
+
new_name: Type.Optional(Type.String({ description: "New name for rename operation" })),
|
|
21
21
|
apply: Type.Optional(Type.Boolean({ description: "Apply edits (default: true)" })),
|
|
22
22
|
include_declaration: Type.Optional(Type.Boolean({ description: "Include declaration in refs (default: true)" })),
|
|
23
23
|
});
|
package/src/lsp/utils.ts
CHANGED