Skip to content

Commit

Permalink
feat: add diagnostics feature to the language server VSCODE-375 (#493)
Browse files Browse the repository at this point in the history
* feat: add diagnostics feature to the language server VSCODE-375

* test: add diagnostic suite and hopefully fix telemetry suit

* test: try to replace property with sandbox

* test: simplify the agg exported telemetry test

* refactor: provide fix for new dbs and address other pr comments

* refactor: remove unused function

* fix: do not highlight use in the middle of the string

* refactor: remove trim

* fix: remove extra space

* fix: do not find use diagnostic issue when use in the middle of other command

* refactor: checking for multiple conditions with startsWith
  • Loading branch information
alenakhineika authored Mar 17, 2023
1 parent 3467e40 commit c31a9c5
Show file tree
Hide file tree
Showing 14 changed files with 888 additions and 252 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/test-and-build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ jobs:
uses: actions/[email protected]
with:
# Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0
node-version: ^14.17.5
node-version: ^16.16.0

- name: Run node-gyp bug workaround script
run: |
Expand Down
3 changes: 3 additions & 0 deletions src/commands/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ enum EXTENSION_COMMANDS {
MDB_RUN_ALL_PLAYGROUND_BLOCKS = 'mdb.runAllPlaygroundBlocks',
MDB_RUN_ALL_OR_SELECTED_PLAYGROUND_BLOCKS = 'mdb.runPlayground',

MDB_FIX_THIS_INVALID_INTERACTIVE_SYNTAX = 'mdb.fixThisInvalidInteractiveSyntax',
MDB_FIX_ALL_INVALID_INTERACTIVE_SYNTAX = 'mdb.fixAllInvalidInteractiveSyntax',

MDB_EXPORT_TO_PYTHON = 'mdb.exportToPython',
MDB_EXPORT_TO_JAVA = 'mdb.exportToJava',
MDB_EXPORT_TO_CSHARP = 'mdb.exportToCsharp',
Expand Down
20 changes: 18 additions & 2 deletions src/editors/editorsController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { EJSON } from 'bson';
import ActiveConnectionCodeLensProvider from './activeConnectionCodeLensProvider';
import ExportToLanguageCodeLensProvider from './exportToLanguageCodeLensProvider';
import PlaygroundSelectedCodeActionProvider from './playgroundSelectedCodeActionProvider';
import PlaygroundDiagnosticsCodeActionProvider from './playgroundDiagnosticsCodeActionProvider';
import ConnectionController from '../connectionController';
import CollectionDocumentsCodeLensProvider from './collectionDocumentsCodeLensProvider';
import CollectionDocumentsOperationsStore from './collectionDocumentsOperationsStore';
Expand Down Expand Up @@ -84,6 +85,7 @@ export function getViewCollectionDocumentsUri(
*/
export default class EditorsController {
_playgroundSelectedCodeActionProvider: PlaygroundSelectedCodeActionProvider;
_playgroundDiagnosticsCodeActionProvider: PlaygroundDiagnosticsCodeActionProvider;
_connectionController: ConnectionController;
_playgroundController: PlaygroundController;
_collectionDocumentsOperationsStore =
Expand All @@ -110,7 +112,8 @@ export default class EditorsController {
playgroundResultViewProvider: PlaygroundResultProvider,
activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider,
exportToLanguageCodeLensProvider: ExportToLanguageCodeLensProvider,
codeActionProvider: PlaygroundSelectedCodeActionProvider,
playgroundSelectedCodeActionProvider: PlaygroundSelectedCodeActionProvider,
playgroundDiagnosticsCodeActionProvider: PlaygroundDiagnosticsCodeActionProvider,
editDocumentCodeLensProvider: EditDocumentCodeLensProvider
) {
this._connectionController = connectionController;
Expand Down Expand Up @@ -141,7 +144,10 @@ export default class EditorsController {
new CollectionDocumentsCodeLensProvider(
this._collectionDocumentsOperationsStore
);
this._playgroundSelectedCodeActionProvider = codeActionProvider;
this._playgroundSelectedCodeActionProvider =
playgroundSelectedCodeActionProvider;
this._playgroundDiagnosticsCodeActionProvider =
playgroundDiagnosticsCodeActionProvider;

vscode.workspace.onDidCloseTextDocument((e) => {
const uriParams = new URLSearchParams(e.uri.query);
Expand Down Expand Up @@ -436,6 +442,16 @@ export default class EditorsController {
}
)
);
this._context.subscriptions.push(
vscode.languages.registerCodeActionsProvider(
'javascript',
this._playgroundDiagnosticsCodeActionProvider,
{
providedCodeActionKinds:
PlaygroundDiagnosticsCodeActionProvider.providedCodeActionKinds,
}
)
);
}

deactivate(): void {
Expand Down
40 changes: 38 additions & 2 deletions src/editors/playgroundController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ import {
ExportToLanguageAddons,
ExportToLanguageNamespace,
ExportToLanguageMode,
ThisDiagnosticFix,
AllDiagnosticFixes,
} from '../types/playgroundType';
import PlaygroundResultProvider, {
PLAYGROUND_RESULT_SCHEME,
Expand Down Expand Up @@ -125,7 +127,7 @@ export default class PlaygroundController {
playgroundResultViewProvider: PlaygroundResultProvider,
activeConnectionCodeLensProvider: ActiveConnectionCodeLensProvider,
exportToLanguageCodeLensProvider: ExportToLanguageCodeLensProvider,
codeActionProvider: PlaygroundSelectedCodeActionProvider,
playgroundSelectedCodeActionProvide: PlaygroundSelectedCodeActionProvider,
explorerController: ExplorerController
) {
this._connectionController = connectionController;
Expand All @@ -138,7 +140,8 @@ export default class PlaygroundController {
vscode.window.createOutputChannel('Playground output');
this._activeConnectionCodeLensProvider = activeConnectionCodeLensProvider;
this._exportToLanguageCodeLensProvider = exportToLanguageCodeLensProvider;
this._playgroundSelectedCodeActionProvider = codeActionProvider;
this._playgroundSelectedCodeActionProvider =
playgroundSelectedCodeActionProvide;
this._explorerController = explorerController;

this._connectionController.addEventListener(
Expand Down Expand Up @@ -272,9 +275,15 @@ export default class PlaygroundController {
const workspaceFolder = vscode.workspace.workspaceFolders?.[0];
const filePath = workspaceFolder?.uri.fsPath || os.homedir();

// We count open untitled playground files to use this number as part of a new playground path.
const numberUntitledPlaygrounds = vscode.workspace.textDocuments.filter(
(doc) => isPlayground(doc.uri)
).length;

// We need a secondary `mongodb` extension otherwise VSCode will
// suggest playground-1.js name when saving playground to the disk.
// Users can open playgrounds from the disk
// and we need a way to distinguish this files from regular JS files.
const fileName = path.join(
filePath,
`playground-${numberUntitledPlaygrounds + 1}.mongodb.js`
Expand All @@ -293,6 +302,8 @@ export default class PlaygroundController {
await vscode.workspace.applyEdit(edit);

// Actually show the editor.
// We open playgrounds by URI to use the secondary `mongodb` extension
// as an identifier that distinguishes them from regular JS files.
const document = await vscode.workspace.openTextDocument(documentUri);

// Focus new text document.
Expand Down Expand Up @@ -639,6 +650,31 @@ export default class PlaygroundController {
return this._evaluatePlayground();
}

async fixThisInvalidInteractiveSyntax({
documentUri,
range,
fix,
}: ThisDiagnosticFix) {
const edit = new vscode.WorkspaceEdit();
edit.replace(documentUri, range, fix);
await vscode.workspace.applyEdit(edit);
return true;
}

async fixAllInvalidInteractiveSyntax({
documentUri,
diagnostics,
}: AllDiagnosticFixes) {
const edit = new vscode.WorkspaceEdit();

for (const { range, fix } of diagnostics) {
edit.replace(documentUri, range, fix);
}

await vscode.workspace.applyEdit(edit);
return true;
}

async openPlayground(filePath: string): Promise<boolean> {
try {
const document = await vscode.workspace.openTextDocument(filePath);
Expand Down
89 changes: 89 additions & 0 deletions src/editors/playgroundDiagnosticsCodeActionProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
import * as vscode from 'vscode';

import type { Diagnostic } from 'vscode-languageserver/node';

import EXTENSION_COMMANDS from '../commands';
import DIAGNOSTIC_CODES from './../language/diagnosticCodes';

export default class PlaygroundDiagnosticsCodeActionProvider
implements vscode.CodeActionProvider
{
_onDidChangeCodeCodeAction: vscode.EventEmitter<void> =
new vscode.EventEmitter<void>();

static readonly providedCodeActionKinds = [vscode.CodeActionKind.QuickFix];

constructor() {
vscode.workspace.onDidChangeConfiguration(() => {
this._onDidChangeCodeCodeAction.fire();
});
}

readonly onDidChangeCodeLenses: vscode.Event<void> =
this._onDidChangeCodeCodeAction.event;

provideCodeActions(
document: vscode.TextDocument,
_range: vscode.Range | vscode.Selection,
context: vscode.CodeActionContext
): vscode.ProviderResult<(vscode.CodeAction | vscode.Command)[]> {
const fixCodeActions: vscode.CodeAction[] = [];
const diagnostics = context.diagnostics as unknown as Diagnostic[];

for (const diagnostic of diagnostics) {
switch (diagnostic.code) {
case DIAGNOSTIC_CODES.invalidInteractiveSyntaxes:
{
const fix = new vscode.CodeAction(
'Fix this interactive syntax problem',
vscode.CodeActionKind.QuickFix
);
fix.command = {
command:
EXTENSION_COMMANDS.MDB_FIX_THIS_INVALID_INTERACTIVE_SYNTAX,
title: 'Fix invalid interactive syntax',
arguments: [
{
documentUri: document.uri,
range: diagnostic.range,
fix: diagnostic.data?.fix,
},
],
};
fixCodeActions.push(fix);
}
break;
default:
break;
}
}

const allDiagnostics = vscode.languages
.getDiagnostics(document.uri)
.filter((d) => d.code === DIAGNOSTIC_CODES.invalidInteractiveSyntaxes);

if (allDiagnostics.length > 1) {
const fix = new vscode.CodeAction(
'Fix all interactive syntax problems',
vscode.CodeActionKind.QuickFix
);

fix.command = {
command: EXTENSION_COMMANDS.MDB_FIX_ALL_INVALID_INTERACTIVE_SYNTAX,
title: 'Fix invalid interactive syntax',
arguments: [
{
documentUri: document.uri,
diagnostics: allDiagnostics.map((d) => ({
range: d.range,
fix: (d as Diagnostic).data?.fix,
})),
},
],
};
fixCodeActions.push(fix);
}

return fixCodeActions;
}
}
5 changes: 5 additions & 0 deletions src/language/diagnosticCodes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
enum DIAGNOSTIC_CODES {
invalidInteractiveSyntaxes = 'playground.invalidInteractiveSyntaxes',
}

export default DIAGNOSTIC_CODES;
Loading

0 comments on commit c31a9c5

Please sign in to comment.