diff --git a/code-actions-sample/README.md b/code-actions-sample/README.md index 68a867aa..20c285de 100644 --- a/code-actions-sample/README.md +++ b/code-actions-sample/README.md @@ -4,11 +4,16 @@ This sample shows how to provide code actions in the the editor. Code actions ar The sample uses the [`CodeActionProvider`](https://code.visualstudio.com/api/references/vscode-api#CodeActionProvider) api to implement a simple set of code actions that convert `:)` to a smiley emoji in markdown files: -![](example.gif) +![Emoji code actions](example.gif) +Second example of code action provider shows how to associate code actions with diagnostic (e.g. compilation) problems. + +![Diagnostic code actions](example_diagnostic.gif) ## VS Code API ### `vscode` module - [`languages.registerCodeActionsProvider`](https://code.visualstudio.com/api/references/vscode-api#languages.registerCodeActionsProvider) +- [`Diagnostic.code`](https://code.visualstudio.com/api/references/vscode-api#Diagnostic) +- [`CodeActionContext.diagnostics`](https://code.visualstudio.com/api/references/vscode-api#CodeActionContext) diff --git a/code-actions-sample/example_diagnostic.gif b/code-actions-sample/example_diagnostic.gif new file mode 100644 index 00000000..2d4830ad Binary files /dev/null and b/code-actions-sample/example_diagnostic.gif differ diff --git a/code-actions-sample/src/diagnostics.ts b/code-actions-sample/src/diagnostics.ts new file mode 100644 index 00000000..680da884 --- /dev/null +++ b/code-actions-sample/src/diagnostics.ts @@ -0,0 +1,67 @@ +/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ + +/** To demonstrate code actions associated with Diagnostics problems, this file provides a mock diagnostics entries. */ + +import * as vscode from 'vscode'; + +/** Code that is used to associate diagnostic entries with code actions. */ +export const EMOJI_MENTION = 'emoji_mention'; + +/** String to detect in the text document. */ +const EMOJI = 'emoji'; + +/** + * Analyzes the text document for problems. + * This demo diagnostic problem provider finds all mentions of 'emoji'. + * @param doc text document to analyze + * @param emojiDiagnostics diagnostic collection + */ +export function refreshDiagnostics(doc: vscode.TextDocument, emojiDiagnostics: vscode.DiagnosticCollection): void { + let diagnostics: vscode.Diagnostic[] = []; + + for (let lineIndex = 0; lineIndex < doc.lineCount; lineIndex++) { + const lineOfText = doc.lineAt(lineIndex); + if (lineOfText.text.includes(EMOJI)) { + diagnostics.push(createDiagnostic(doc, lineOfText, lineIndex)); + } + } + + emojiDiagnostics.set(doc.uri, diagnostics); +} + +function createDiagnostic(doc: vscode.TextDocument, lineOfText: vscode.TextLine, lineIndex: number): vscode.Diagnostic { + // find where in the line of thet the 'emoji' is mentioned + let index = lineOfText.text.indexOf(EMOJI); + + // create range that represents, where in the document the word is + let range = new vscode.Range(lineIndex, index, lineIndex, index + EMOJI.length); + + let diagnostic = new vscode.Diagnostic(range, "When you say 'emoji', do you want to find out more?", + vscode.DiagnosticSeverity.Information); + diagnostic.code = EMOJI_MENTION; + return diagnostic; +} + +export function subscribeToDocumentChanges(context: vscode.ExtensionContext, emojiDiagnostics: vscode.DiagnosticCollection): void { + if (vscode.window.activeTextEditor) { + refreshDiagnostics(vscode.window.activeTextEditor.document, emojiDiagnostics); + } + context.subscriptions.push( + vscode.window.onDidChangeActiveTextEditor(editor => { + if (editor) { + refreshDiagnostics(editor.document, emojiDiagnostics); + } + }) + ); + + context.subscriptions.push( + vscode.workspace.onDidChangeTextDocument(e => refreshDiagnostics(e.document, emojiDiagnostics)) + ); + + context.subscriptions.push( + vscode.workspace.onDidCloseTextDocument(doc => emojiDiagnostics.delete(doc.uri)) + ); + +} \ No newline at end of file diff --git a/code-actions-sample/src/extension.ts b/code-actions-sample/src/extension.ts index 1149c3f2..2279301f 100644 --- a/code-actions-sample/src/extension.ts +++ b/code-actions-sample/src/extension.ts @@ -3,12 +3,37 @@ *--------------------------------------------------------*/ import * as vscode from 'vscode'; +import { subscribeToDocumentChanges, EMOJI_MENTION } from './diagnostics'; + +const COMMAND = 'code-actions-sample.command'; + +var emojiDiagnostics: vscode.DiagnosticCollection; export function activate(context: vscode.ExtensionContext) { context.subscriptions.push( vscode.languages.registerCodeActionsProvider('markdown', new Emojizer(), { providedCodeActionKinds: Emojizer.providedCodeActionKinds })); + + emojiDiagnostics = vscode.languages.createDiagnosticCollection("emoji"); + + subscribeToDocumentChanges(context, emojiDiagnostics); + + context.subscriptions.push( + vscode.languages.registerCodeActionsProvider('markdown', new Emojinfo(), { + providedCodeActionKinds: Emojinfo.providedCodeActionKinds + }) + ); + + context.subscriptions.push( + vscode.commands.registerCommand(COMMAND, () => vscode.env.openExternal(vscode.Uri.parse('https://unicode.org/emoji/charts-12.0/full-emoji-list.html'))) + ); +} + +export function deactivate(context: vscode.ExtensionContext) { + if (emojiDiagnostics) { + emojiDiagnostics.dispose(); + } } /** @@ -34,10 +59,13 @@ export class Emojizer implements vscode.CodeActionProvider { const replaceWithSmileyHankyFix = this.createFix(document, range, '💩'); + const commandAction = this.createCommand(); + return [ replaceWithSmileyCatFix, replaceWithSmileyFix, - replaceWithSmileyHankyFix + replaceWithSmileyHankyFix, + commandAction ]; } @@ -53,5 +81,35 @@ export class Emojizer implements vscode.CodeActionProvider { fix.edit.replace(document.uri, new vscode.Range(range.start, range.start.translate(0, 2)), emoji); return fix; } + + private createCommand(): vscode.CodeAction { + const action = new vscode.CodeAction('Learn more...', vscode.CodeActionKind.Empty); + action.command = { command: COMMAND, title: 'Learn more about emojis', tooltip: 'This will open the unicode emoji page.' }; + return action; + } } +/** + * Provides code actions corresponding to diagnostic problems. + */ +export class Emojinfo 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 + return context.diagnostics + .filter(diagnostic => diagnostic.code === EMOJI_MENTION) + .map(diagnostic => this.createCommandCodeAction(diagnostic)); + } + + private createCommandCodeAction(diagnostic: vscode.Diagnostic): vscode.CodeAction { + const action = new vscode.CodeAction('Learn more...', vscode.CodeActionKind.QuickFix); + action.command = { command: COMMAND, title: 'Learn more about emojis', tooltip: 'This will open the unicode emoji page.' }; + action.diagnostics = [diagnostic]; + action.isPreferred = true; + return action; + } +} \ No newline at end of file