Skip to content

Commit

Permalink
[typespec-vscode] expose linter rule documentation url in codefixes (#…
Browse files Browse the repository at this point in the history
…5131)

Modifications

best-practices:
1、Add a URL to the rule file to point to the relevant document.(for
testing)

compiler:
1、A new codefix file has been added to handle the Linter Rule doc URL.
2、Added OPEN_RULE_DOC command type.
3、If the diagnostic URL is not empty, add a codefix and process the send
request in the server file.

typespec-vscode:
1、Define the request and open the received URL document.

samples:
1、Add lint-related configurations to the yaml configuration file to
define a lowercase model type in the tsp file for functional testing.
Such as "model foo {}".

Related issues:#3043

---------

Co-authored-by: Rodge Fu <[email protected]>
  • Loading branch information
mzhongl524 and RodgeFu authored Dec 11, 2024
1 parent 8bbdf96 commit 6ffff89
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
# Change versionKind to one of: internal, fix, dependencies, feature, deprecation, breaking
changeKind: fix
packages:
- typespec-vscode
---

Support 'See Document' quick action to view the details of linter rules
71 changes: 71 additions & 0 deletions packages/typespec-vscode/src/code-action-provider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import vscode from "vscode";
import { OPEN_URL_COMMAND } from "./vscode-command.js";

export function createCodeActionProvider() {
return vscode.languages.registerCodeActionsProvider(
"typespec",
new TypeSpecCodeActionProvider(),
{
providedCodeActionKinds: TypeSpecCodeActionProvider.providedCodeActionKinds,
},
);
}

/**
* Provides code actions corresponding to diagnostic problems.
*/
export class TypeSpecCodeActionProvider implements vscode.CodeActionProvider {
public static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix];

provideCodeActions(
_document: vscode.TextDocument,
_range: vscode.Range | vscode.Selection,
context: vscode.CodeActionContext,
_token: vscode.CancellationToken,
): vscode.CodeAction[] {
// for each diagnostic entry that has the matching `code`, create a code action command
// A CodeAction will only be created if it is a TypeSpec diagnostic and code is an object and has a target attribute
// target attribute is the URL to open

// target is a Uri type, which corresponds to diagnostic.codeDescription.href in compiler
// When target is empty, it does not exist in the code object, so the code action will not be created
const actions: vscode.CodeAction[] = [];
context.diagnostics.forEach((diagnostic) => {
if (
diagnostic.source === "TypeSpec" &&
diagnostic.code &&
typeof diagnostic.code === "object" &&
"target" in diagnostic.code &&
"value" in diagnostic.code
) {
actions.push(
this.createOpenUrlCodeAction(
diagnostic,
diagnostic.code.target.toString(),
diagnostic.code.value.toString(),
),
);
}
});
return actions;
}

private createOpenUrlCodeAction(
diagnostic: vscode.Diagnostic,
url: string,
codeActionTitle: string,
): vscode.CodeAction {
// 'vscode.CodeActionKind.Empty' does not generate a Code Action menu, You must use 'vscode.CodeActionKind.QuickFix'
const action = new vscode.CodeAction(
`See documentation for "${codeActionTitle}"`,
vscode.CodeActionKind.QuickFix,
);
action.command = {
command: OPEN_URL_COMMAND,
title: diagnostic.message,
arguments: [url],
};
action.diagnostics = [diagnostic];
return action;
}
}
5 changes: 5 additions & 0 deletions packages/typespec-vscode/src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import vscode, { commands, ExtensionContext } from "vscode";
import { createCodeActionProvider } from "./code-action-provider.js";
import { SettingName } from "./const.js";
import { ExtensionLogListener } from "./log/extension-log-listener.js";
import logger from "./log/logger.js";
import { TypeSpecLogOutputChannel } from "./log/typespec-log-output-channel.js";
import { createTaskProvider } from "./task-provider.js";
import { TspLanguageClient } from "./tsp-language-client.js";
import { createCommandOpenUrl } from "./vscode-command.js";

let client: TspLanguageClient | undefined;
/**
Expand All @@ -17,6 +19,9 @@ logger.registerLogListener("extension-log", new ExtensionLogListener(outputChann
export async function activate(context: ExtensionContext) {
context.subscriptions.push(createTaskProvider());

context.subscriptions.push(createCodeActionProvider());
context.subscriptions.push(createCommandOpenUrl());

context.subscriptions.push(
commands.registerCommand("typespec.showOutputChannel", () => {
outputChannel.show(true /*preserveFocus*/);
Expand Down
15 changes: 15 additions & 0 deletions packages/typespec-vscode/src/vscode-command.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import vscode from "vscode";
import logger from "./log/logger.js";

export const OPEN_URL_COMMAND = "typespec.openUrl";

export function createCommandOpenUrl() {
return vscode.commands.registerCommand(OPEN_URL_COMMAND, (url: string) => {
// Although vscode has already dealt with the problem of wrong URL, try catch is still added here.
try {
vscode.env.openExternal(vscode.Uri.parse(url));
} catch (error) {
logger.error(`Failed to open URL: ${url}`, [error as any]);
}
});
}

0 comments on commit 6ffff89

Please sign in to comment.