@nghyane/arcane 0.1.13 → 0.1.14
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 +48 -113
- package/src/modes/components/bordered-loader.ts +1 -1
- package/src/modes/components/branch-summary-message.ts +13 -10
- package/src/modes/components/compaction-summary-message.ts +14 -13
- 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 +49 -92
- 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/frontmatter.md +1 -1
- package/src/prompts/agents/init.md +2 -2
- package/src/prompts/agents/librarian.md +30 -21
- package/src/prompts/agents/oracle.md +9 -2
- package/src/prompts/agents/reviewer.md +15 -49
- package/src/prompts/agents/task.md +17 -9
- package/src/prompts/compaction/branch-summary-context.md +1 -1
- package/src/prompts/compaction/branch-summary-preamble.md +1 -1
- package/src/prompts/compaction/branch-summary.md +4 -1
- package/src/prompts/compaction/compaction-short-summary.md +1 -1
- package/src/prompts/compaction/compaction-summary-context.md +1 -1
- package/src/prompts/compaction/compaction-summary.md +4 -1
- package/src/prompts/compaction/compaction-turn-prefix.md +1 -1
- package/src/prompts/compaction/compaction-update-summary.md +1 -1
- package/src/prompts/memories/consolidation.md +1 -1
- package/src/prompts/memories/read_path.md +1 -1
- package/src/prompts/memories/stage_one_input.md +1 -1
- package/src/prompts/memories/stage_one_system.md +1 -1
- package/src/prompts/review-request.md +1 -1
- package/src/prompts/system/agent-creation-architect.md +1 -1
- package/src/prompts/system/agent-creation-user.md +1 -1
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/file-operations.md +1 -1
- package/src/prompts/system/subagent-system-prompt.md +2 -2
- package/src/prompts/system/summarization-system.md +1 -1
- package/src/prompts/system/system-prompt.md +163 -178
- package/src/prompts/system/title-system.md +1 -1
- package/src/prompts/system/ttsr-interrupt.md +1 -1
- package/src/prompts/system/verification-reminder.md +6 -0
- package/src/prompts/system/web-search.md +1 -1
- 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