Skip to content

Commit 0e63fdc

Browse files
committed
fix: Add command to open tool repository
1 parent ecfa9a2 commit 0e63fdc

File tree

10 files changed

+238
-54
lines changed

10 files changed

+238
-54
lines changed

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,10 @@
319319
"command": "mise.openMenu",
320320
"title": "Mise: Open Menu"
321321
},
322+
{
323+
"command": "mise.openToolRepository",
324+
"title": "Mise: Open Tool Repository"
325+
},
322326
{
323327
"command": "mise.selectWorkspaceFolder",
324328
"title": "Mise: Select Workspace Folder"
@@ -520,6 +524,11 @@
520524
"when": "view == miseToolsView && viewItem == tool-installed",
521525
"group": "2_modification"
522526
},
527+
{
528+
"command": "mise.openToolWebsite",
529+
"when": "view == miseToolsView",
530+
"group": "2_modification"
531+
},
523532
{
524533
"command": "mise.installTool",
525534
"when": "view == miseToolsView && viewItem == tool-notinstalled",

src/commands.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ export const MISE_WATCH_TASK = "mise.watchTask";
2727
export const MISE_FMT = "mise.fmt";
2828
export const MISE_EDIT_SETTING = "mise.editSetting";
2929
export const MISE_SELECT_WORKSPACE_FOLDER = "mise.selectWorkspaceFolder";
30+
export const MISE_OPEN_TOOL_REPOSITORY = "mise.openToolRepository";

src/miseService.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -759,6 +759,16 @@ export class MiseService {
759759
}
760760
}
761761

762+
async miseToolInfo(toolName: string) {
763+
if (!this.getMiseBinaryPath()) {
764+
return;
765+
}
766+
const { stdout } = await this.cache.execCmd({
767+
command: `tool "${toolName.replace(/"/g, '\\"')}" --json`,
768+
});
769+
return JSON.parse(stdout) as MiseToolInfo;
770+
}
771+
762772
async miseRegistry() {
763773
if (!this.getMiseBinaryPath()) {
764774
return [];

src/providers/toolsProvider.ts

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
MISE_INSTALL_TOOL,
99
MISE_OPEN_FILE,
1010
MISE_OPEN_TOOL_DEFINITION,
11+
MISE_OPEN_TOOL_REPOSITORY,
1112
MISE_RELOAD,
1213
MISE_REMOVE_TOOL,
1314
MISE_USE_TOOL,
@@ -27,6 +28,7 @@ import {
2728
} from "../utils/fileUtils";
2829
import { logger } from "../utils/logger";
2930
import { findToolPosition } from "../utils/miseFileParser";
31+
import { getWebsiteForTool } from "../utils/miseUtilts";
3032
import { CONFIGURABLE_EXTENSIONS_BY_TOOL_NAME } from "../utils/supportedExtensions";
3133

3234
type TreeItem = ToolsSourceItem | ToolItem;
@@ -467,6 +469,41 @@ export function registerToolsCommands(
467469
},
468470
),
469471

472+
vscode.commands.registerCommand(
473+
MISE_OPEN_TOOL_REPOSITORY,
474+
async (providedTool: MiseTool | ToolItem | undefined) => {
475+
let tool = providedTool;
476+
if (!tool) {
477+
const tools = await miseService.getCurrentTools();
478+
const toolNames = tools.map((tool) => tool.name);
479+
const selectedToolName = await vscode.window.showQuickPick(
480+
toolNames,
481+
{ canPickMany: false, placeHolder: "Select a tool to open" },
482+
);
483+
tool = tools.find((tool) => tool.name === selectedToolName);
484+
}
485+
486+
if (tool instanceof ToolItem) {
487+
tool = tool.tool;
488+
}
489+
490+
if (!tool) {
491+
return;
492+
}
493+
494+
const toolInfo = await miseService.miseToolInfo(tool.name);
495+
if (!toolInfo) {
496+
return;
497+
}
498+
499+
const uri = await getWebsiteForTool(toolInfo);
500+
if (!uri) {
501+
return;
502+
}
503+
vscode.env.openExternal(vscode.Uri.parse(uri));
504+
},
505+
),
506+
470507
vscode.commands.registerCommand(
471508
MISE_CONFIGURE_SDK_PATH,
472509
async (toolName: string | undefined) => {

src/types.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,16 @@ type MiseSettingInfo = {
4242
value: string | number | boolean | string[] | number[] | boolean[] | object;
4343
source?: string;
4444
};
45+
46+
type MiseToolInfo = {
47+
backend: string;
48+
description: string;
49+
installed_versions: string[];
50+
requested_versions: string[];
51+
active_versions: string[];
52+
config_source: { type: string; path: string };
53+
tool_options: {
54+
os: string;
55+
install_env: Record<string, string>;
56+
};
57+
};

src/utils/miseUtilts.ts

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,50 @@ export function getDefaultForType(type?: string): unknown {
126126
case "array":
127127
return [];
128128
default:
129-
return "";
129+
return undefined;
130130
}
131131
}
132+
133+
export const getWebsiteForTool = async (toolInfo: MiseToolInfo) => {
134+
if (!toolInfo?.backend) {
135+
return;
136+
}
137+
138+
// backendName:repo
139+
const [backendName, repo] = toolInfo.backend.split(":");
140+
if (!repo) {
141+
return;
142+
}
143+
144+
if (
145+
repo.includes("/") &&
146+
(backendName === "ubi" ||
147+
backendName === "aqua" ||
148+
backendName === "asdf" ||
149+
backendName === "vfox")
150+
) {
151+
if (repo?.startsWith("https://")) {
152+
return repo;
153+
}
154+
return `https://github.com/${repo}`;
155+
}
156+
157+
if (backendName === "core") {
158+
return `https://mise.jdx.dev/lang/${repo}`;
159+
}
160+
161+
if (backendName === "npm") {
162+
return `https://www.npmjs.com/package/${repo}`;
163+
}
164+
165+
if (backendName === "asdf") {
166+
const res = await fetch(
167+
`https://raw.githubusercontent.com/asdf-vm/asdf-plugins/refs/heads/master/plugins/${repo}`,
168+
);
169+
const data = await res.text();
170+
const url = data.match(/(https:\/\/github.com\/[^"]+)/);
171+
if (url?.[1] && typeof url[1] === "string") {
172+
return url[1];
173+
}
174+
}
175+
};

src/webviews/Settings.tsx

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,10 @@ import { useQuery, useQueryClient } from "@tanstack/react-query";
22
import { VscodeCheckbox } from "@vscode-elements/react-elements";
33
import { type FlattenedProperty, getDefaultForType } from "../utils/miseUtilts";
44
import CustomTable from "./components/CustomTable";
5+
import { FileLink } from "./components/FileLink";
56
import { IconButton } from "./components/IconButton";
67
import { useWebviewStore } from "./store";
7-
import {
8-
toDisplayPath,
9-
useEditSettingMutation,
10-
useOpenFileMutation,
11-
vscodeClient,
12-
} from "./webviewVsCodeApi";
8+
import { useEditSettingMutation, vscodeClient } from "./webviewVsCodeApi";
139

1410
export const Settings = () => {
1511
const showModifiedOnly = useWebviewStore(
@@ -18,7 +14,6 @@ export const Settings = () => {
1814
const setShowModifiedOnly = useWebviewStore(
1915
(state) => state.setShowModifiedSettingsOnly,
2016
);
21-
const openFileMutation = useOpenFileMutation();
2217
const queryClient = useQueryClient();
2318

2419
const settingsQuery = useQuery({
@@ -175,7 +170,7 @@ export const Settings = () => {
175170
}}
176171
/>
177172
</div>
178-
{actual !== defaultValue && (
173+
{actual !== defaultValue && defaultValue !== undefined && (
179174
<div
180175
style={{ display: "flex", alignItems: "center", gap: 4 }}
181176
>
@@ -189,16 +184,7 @@ export const Settings = () => {
189184
<div
190185
style={{ display: "flex", alignItems: "center", gap: 4 }}
191186
>
192-
source{" "}
193-
<a
194-
href={`file://${source}`}
195-
onClick={(e) => {
196-
e.preventDefault();
197-
openFileMutation.mutate(source);
198-
}}
199-
>
200-
{toDisplayPath(source || "")}
201-
</a>
187+
source: <FileLink filePath={source} />
202188
</div>
203189
)}
204190
</div>

src/webviews/Tools.tsx

Lines changed: 95 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
22
import { VscodeButton, VscodeCheckbox } from "@vscode-elements/react-elements";
33
import CustomTable from "./components/CustomTable";
4+
import { FileLink } from "./components/FileLink";
45
import { IconButton } from "./components/IconButton";
56
import { useWebviewStore } from "./store";
67
import {
@@ -9,6 +10,8 @@ import {
910
vscodeClient,
1011
} from "./webviewVsCodeApi";
1112

13+
import React, { useState } from "react";
14+
1215
const styles = {
1316
infoPanel: { padding: "10px" },
1417
header: { marginBottom: "10px" },
@@ -27,15 +30,43 @@ const styles = {
2730
},
2831
value: {
2932
fontSize: "14px",
30-
wordBreak: "break-all",
33+
wordBreak: "break-word",
3134
color: "var(--vscode-foreground)",
3235
},
36+
toggleButton: {
37+
display: "flex",
38+
alignItems: "center",
39+
gap: "4px",
40+
color: "var(--vscode-textLink-foreground)",
41+
cursor: "pointer",
42+
background: "none",
43+
border: "none",
44+
padding: "4px 0",
45+
marginTop: "0px",
46+
},
47+
pathContainer: {
48+
display: "flex",
49+
flexDirection: "column" as const,
50+
gap: "6px",
51+
},
52+
pathItem: {
53+
display: "flex",
54+
alignItems: "center",
55+
},
56+
pathInfo: {
57+
marginLeft: "8px",
58+
opacity: 0.8,
59+
},
3360
} as const;
3461

3562
const ToolInfo = ({
3663
onClose,
3764
selectedTool,
38-
}: { selectedTool: MiseTool; onClose: () => void }) => {
65+
}: {
66+
selectedTool: MiseTool;
67+
onClose: () => void;
68+
}) => {
69+
const [showAll, setShowAll] = useState(false);
3970
const trackedConfigQuery = useQuery(trackedConfigsQueryOptions);
4071
const configs = trackedConfigQuery.data || [];
4172

@@ -47,6 +78,11 @@ const ToolInfo = ({
4778
return Object.keys(config.tools).includes(selectedTool.name);
4879
});
4980

81+
const displayedConfigs = showAll
82+
? configsWithTool
83+
: configsWithTool.slice(0, 3);
84+
const hasMore = configsWithTool.length > 3;
85+
5086
return (
5187
<div style={styles.infoPanel}>
5288
<div
@@ -61,7 +97,7 @@ const ToolInfo = ({
6197
<i className="codicon codicon-tools" />
6298
{selectedTool.name}
6399
</div>
64-
<IconButton iconName={"close"} onClick={onClose} />
100+
<IconButton iconName="close" onClick={onClose} />
65101
</div>
66102

67103
<div>
@@ -112,14 +148,37 @@ const ToolInfo = ({
112148
{configsWithTool.length > 0 && (
113149
<div>
114150
<p style={styles.label}>Used in</p>
115-
<div style={styles.value}>
116-
{configsWithTool
117-
.map((config) => {
118-
// @ts-ignore
119-
const toolInfo = config.tools[selectedTool.name];
120-
return `${toDisplayPath(config.path)} (${JSON.stringify(toolInfo)})`;
121-
})
122-
.join(", ")}
151+
<div style={styles.pathContainer}>
152+
{displayedConfigs.map((config) => {
153+
// @ts-ignore
154+
const toolInfo = config.tools[selectedTool.name];
155+
return (
156+
<span
157+
key={config.path + selectedTool.name}
158+
style={styles.pathItem}
159+
>
160+
<FileLink filePath={config.path} />
161+
<span style={styles.pathInfo}>
162+
({JSON.stringify(toolInfo)})
163+
</span>
164+
</span>
165+
);
166+
})}
167+
168+
{hasMore && (
169+
<button
170+
type={"button"}
171+
onClick={() => setShowAll(!showAll)}
172+
style={styles.toggleButton}
173+
>
174+
<i
175+
className={`codicon codicon-chevron-${showAll ? "up" : "down"}`}
176+
/>
177+
{showAll
178+
? "Show less"
179+
: `Show ${configsWithTool.length - 3} more`}
180+
</button>
181+
)}
123182
</div>
124183
</div>
125184
)}
@@ -246,10 +305,28 @@ export const Tools = () => {
246305
mutationFn: () => vscodeClient.request({ mutationKey: ["pruneTools"] }),
247306
});
248307

308+
const trackedConfigQuery = useQuery(trackedConfigsQueryOptions);
309+
const configs = trackedConfigQuery.data || [];
310+
311+
if (!selectedTool) {
312+
return null;
313+
}
314+
315+
const configsWithTool = configs.filter((config) => {
316+
return Object.keys(config.tools).includes(selectedTool.name);
317+
});
318+
249319
const outdatedToolsQuery = useQuery({
250320
queryKey: ["outdatedTools"],
251-
queryFn: ({ queryKey }) =>
252-
vscodeClient.request({ queryKey }) as Promise<Array<MiseToolUpdate>>,
321+
queryFn: ({ queryKey }) => {
322+
if (!navigator.onLine) {
323+
return [];
324+
}
325+
326+
return vscodeClient.request({ queryKey }) as Promise<
327+
Array<MiseToolUpdate>
328+
>;
329+
},
253330
});
254331

255332
if (toolsQuery.isError) {
@@ -285,7 +362,11 @@ export const Tools = () => {
285362
/>
286363
)}
287364
<CustomTable
288-
style={{ height: window.innerHeight - (selectedTool ? 280 : 40) }}
365+
style={{
366+
height:
367+
window.innerHeight -
368+
(selectedTool ? (configsWithTool?.length > 3 ? 320 : 280) : 40),
369+
}}
289370
filterRowElement={
290371
<div
291372
style={{

0 commit comments

Comments
 (0)