mirror of
https://github.com/Microsoft/vscode
synced 2024-08-28 05:19:39 +00:00
Add code lenses to run/debug a script
This commit is contained in:
parent
26e5a55cd8
commit
80b08b4c7f
|
@ -59,10 +59,6 @@
|
|||
"dark": "resources/dark/continue.svg"
|
||||
}
|
||||
},
|
||||
{
|
||||
"command": "npm.runScriptFromSource",
|
||||
"title": "%command.runScriptFromSource%"
|
||||
},
|
||||
{
|
||||
"command": "npm.debugScript",
|
||||
"title": "%command.debug%",
|
||||
|
@ -122,13 +118,6 @@
|
|||
"group": "navigation"
|
||||
}
|
||||
],
|
||||
"editor/context": [
|
||||
{
|
||||
"command": "npm.runScriptFromSource",
|
||||
"when": "resourceFilename == 'package.json'",
|
||||
"group": "navigation@+1"
|
||||
}
|
||||
],
|
||||
"view/item/context": [
|
||||
{
|
||||
"command": "npm.openScript",
|
||||
|
|
|
@ -15,6 +15,5 @@
|
|||
"command.run": "Run",
|
||||
"command.debug": "Debug",
|
||||
"command.openScript": "Open",
|
||||
"command.runInstall": "Run Install",
|
||||
"command.runScriptFromSource": "Run Script"
|
||||
"command.runInstall": "Run Install"
|
||||
}
|
||||
|
|
73
extensions/npm/src/lenses.ts
Normal file
73
extensions/npm/src/lenses.ts
Normal file
|
@ -0,0 +1,73 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import {
|
||||
ExtensionContext, CodeLensProvider, TextDocument, commands, ProviderResult, CodeLens, CancellationToken,
|
||||
workspace, tasks, Range, Command
|
||||
} from 'vscode';
|
||||
import {
|
||||
createTask, startDebugging, findAllScriptRanges, extractDebugArgFromScript
|
||||
} from './tasks';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class NpmLenseProvider implements CodeLensProvider {
|
||||
private extensionContext: ExtensionContext;
|
||||
|
||||
constructor(context: ExtensionContext) {
|
||||
const subscriptions = context.subscriptions;
|
||||
this.extensionContext = context;
|
||||
context.subscriptions.push(commands.registerCommand('npm.runScriptFromLense', this.runScriptFromLense, this));
|
||||
context.subscriptions.push(commands.registerCommand('npm.debugScriptFromLense', this.debugScriptFromLense, this));
|
||||
}
|
||||
|
||||
public provideCodeLenses(document: TextDocument, token: CancellationToken): ProviderResult<CodeLens[]> {
|
||||
let result = findAllScriptRanges(document.getText());
|
||||
let lenses: CodeLens[] = [];
|
||||
|
||||
result.forEach((value, key) => {
|
||||
let start = document.positionAt(value[0]);
|
||||
let end = document.positionAt(value[0] + value[1]);
|
||||
let lens: CodeLens;
|
||||
let command: Command = {
|
||||
command: 'npm.runScriptFromLense',
|
||||
title: localize('run', "Run"),
|
||||
arguments: [document, key]
|
||||
};
|
||||
lens = new CodeLens(new Range(start, end), command);
|
||||
lenses.push(lens);
|
||||
let debugArgs = extractDebugArgFromScript(value[2]);
|
||||
if (debugArgs) {
|
||||
command = {
|
||||
command: 'npm.debugScriptFromLense',
|
||||
title: localize('debug', "Debug"),
|
||||
arguments: [document, key, debugArgs[0], debugArgs[1]]
|
||||
};
|
||||
lens = new CodeLens(new Range(start, end), command);
|
||||
lenses.push(lens);
|
||||
}
|
||||
});
|
||||
return lenses;
|
||||
}
|
||||
|
||||
public runScriptFromLense(document: TextDocument, script: string) {
|
||||
let uri = document.uri;
|
||||
let folder = workspace.getWorkspaceFolder(uri);
|
||||
if (folder) {
|
||||
let task = createTask(script, `run ${script}`, folder, uri);
|
||||
tasks.executeTask(task);
|
||||
}
|
||||
}
|
||||
|
||||
public debugScriptFromLense(document: TextDocument, script: string, protocol: string, port: number) {
|
||||
let uri = document.uri;
|
||||
let folder = workspace.getWorkspaceFolder(uri);
|
||||
if (folder) {
|
||||
startDebugging(script, protocol, port, folder);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,17 +9,17 @@ import * as vscode from 'vscode';
|
|||
|
||||
import { addJSONProviders } from './features/jsonContributions';
|
||||
import { NpmScriptsTreeDataProvider } from './npmView';
|
||||
import { provideNpmScripts, invalidateScriptsCache, findScriptAtPosition, createTask } from './tasks';
|
||||
import { provideNpmScripts, invalidateScriptsCache } from './tasks';
|
||||
|
||||
import * as nls from 'vscode-nls';
|
||||
import { NpmLenseProvider } from './lenses';
|
||||
|
||||
let taskProvider: vscode.Disposable | undefined;
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext): Promise<void> {
|
||||
taskProvider = registerTaskProvider(context);
|
||||
const treeDataProvider = registerExplorer(context);
|
||||
registerLenseProvider(context);
|
||||
|
||||
configureHttpRequest();
|
||||
vscode.workspace.onDidChangeConfiguration((e) => {
|
||||
configureHttpRequest();
|
||||
|
@ -36,7 +36,6 @@ export async function activate(context: vscode.ExtensionContext): Promise<void>
|
|||
}
|
||||
});
|
||||
context.subscriptions.push(addJSONProviders(httpRequest.xhr));
|
||||
context.subscriptions.push(vscode.commands.registerCommand('npm.runScriptFromSource', runScriptFromSource));
|
||||
}
|
||||
|
||||
function registerTaskProvider(context: vscode.ExtensionContext): vscode.Disposable | undefined {
|
||||
|
@ -70,34 +69,23 @@ function registerExplorer(context: vscode.ExtensionContext): NpmScriptsTreeDataP
|
|||
return undefined;
|
||||
}
|
||||
|
||||
function registerLenseProvider(context: vscode.ExtensionContext) {
|
||||
if (vscode.workspace.workspaceFolders) {
|
||||
let npmSelector: vscode.DocumentSelector = {
|
||||
language: 'json',
|
||||
scheme: 'file',
|
||||
pattern: '**/package.json'
|
||||
};
|
||||
let provider = new NpmLenseProvider(context);
|
||||
context.subscriptions.push(vscode.languages.registerCodeLensProvider(npmSelector, provider));
|
||||
}
|
||||
}
|
||||
|
||||
function configureHttpRequest() {
|
||||
const httpSettings = vscode.workspace.getConfiguration('http');
|
||||
httpRequest.configure(httpSettings.get<string>('proxy', ''), httpSettings.get<boolean>('proxyStrictSSL', true));
|
||||
}
|
||||
|
||||
async function runScriptFromSource() {
|
||||
let editor = vscode.window.activeTextEditor;
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
let document = editor.document;
|
||||
let contents = document.getText();
|
||||
let selection = editor.selection;
|
||||
let offset = document.offsetAt(selection.anchor);
|
||||
let script = findScriptAtPosition(contents, offset);
|
||||
if (script) {
|
||||
let uri = document.uri;
|
||||
let folder = vscode.workspace.getWorkspaceFolder(uri);
|
||||
if (folder) {
|
||||
let task = createTask(script, `run ${script}`, folder, uri);
|
||||
vscode.tasks.executeTask(task);
|
||||
}
|
||||
} else {
|
||||
let message = localize('noScriptFound', 'Could not find a script at the selection.');
|
||||
vscode.window.showErrorMessage(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function deactivate(): void {
|
||||
if (taskProvider) {
|
||||
taskProvider.dispose();
|
||||
|
|
|
@ -6,14 +6,14 @@
|
|||
|
||||
import * as path from 'path';
|
||||
import {
|
||||
DebugConfiguration, Event, EventEmitter, ExtensionContext, Task,
|
||||
Event, EventEmitter, ExtensionContext, Task,
|
||||
TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri,
|
||||
WorkspaceFolder, commands, debug, window, workspace, tasks, Selection, TaskGroup
|
||||
WorkspaceFolder, commands, window, workspace, tasks, Selection, TaskGroup
|
||||
} from 'vscode';
|
||||
import { visit, JSONVisitor } from 'jsonc-parser';
|
||||
import {
|
||||
NpmTaskDefinition, getPackageJsonUriFromTask, getScripts,
|
||||
isWorkspaceFolder, getPackageManager, getTaskName, createTask
|
||||
isWorkspaceFolder, getTaskName, createTask, extractDebugArgFromScript, startDebugging
|
||||
} from './tasks';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
|
@ -162,25 +162,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
|
|||
}
|
||||
|
||||
private extractDebugArg(scripts: any, task: Task): [string, number] | undefined {
|
||||
let script: string = scripts[task.name];
|
||||
|
||||
// matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect,
|
||||
// --inspect=1234, --inspect-brk, --inspect-brk=1234,
|
||||
// --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234
|
||||
let match = script.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/);
|
||||
|
||||
if (match) {
|
||||
if (match[6]) {
|
||||
return [match[1], parseInt(match[6])];
|
||||
}
|
||||
if (match[1] === 'inspect') {
|
||||
return [match[1], 9229];
|
||||
}
|
||||
if (match[1] === 'debug') {
|
||||
return [match[1], 5858];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
return extractDebugArgFromScript(scripts[task.name]);
|
||||
}
|
||||
|
||||
private async debugScript(script: NpmScript) {
|
||||
|
@ -193,7 +175,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
|
|||
return;
|
||||
}
|
||||
|
||||
let debugArg = await this.extractDebugArg(scripts, task);
|
||||
let debugArg = this.extractDebugArg(scripts, task);
|
||||
if (!debugArg) {
|
||||
let message = localize('noDebugOptions', 'Could not launch "{0}" for debugging because the scripts lacks a node debug option, e.g. "--inspect-brk".', task.name);
|
||||
let learnMore = localize('learnMore', 'Learn More');
|
||||
|
@ -204,29 +186,7 @@ export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
|
|||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let protocol = 'inspector';
|
||||
if (debugArg[0] === 'debug') {
|
||||
protocol = 'legacy';
|
||||
}
|
||||
|
||||
let packageManager = getPackageManager(script.getFolder());
|
||||
const config: DebugConfiguration = {
|
||||
type: 'node',
|
||||
request: 'launch',
|
||||
name: `Debug ${task.name}`,
|
||||
runtimeExecutable: packageManager,
|
||||
runtimeArgs: [
|
||||
'run-script',
|
||||
task.name,
|
||||
],
|
||||
port: debugArg[1],
|
||||
protocol: protocol
|
||||
};
|
||||
|
||||
if (isWorkspaceFolder(task.scope)) {
|
||||
debug.startDebugging(task.scope, config);
|
||||
}
|
||||
startDebugging(task.name, debugArg[0], debugArg[1], script.getFolder());
|
||||
}
|
||||
|
||||
private scriptNotValid(task: Task) {
|
||||
|
|
|
@ -4,7 +4,10 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
'use strict';
|
||||
|
||||
import { TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace } from 'vscode';
|
||||
import {
|
||||
TaskDefinition, Task, TaskGroup, WorkspaceFolder, RelativePattern, ShellExecution, Uri, workspace,
|
||||
DebugConfiguration, debug
|
||||
} from 'vscode';
|
||||
import * as path from 'path';
|
||||
import * as fs from 'fs';
|
||||
import * as minimatch from 'minimatch';
|
||||
|
@ -162,7 +165,7 @@ function isExcluded(folder: WorkspaceFolder, packageJsonUri: Uri) {
|
|||
}
|
||||
|
||||
function isDebugScript(script: string): boolean {
|
||||
let match = script.match(/--(inspect|debug)(-brk)?(=(\d*))?/);
|
||||
let match = script.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/);
|
||||
return match !== null;
|
||||
}
|
||||
|
||||
|
@ -269,6 +272,52 @@ async function readFile(file: string): Promise<string> {
|
|||
});
|
||||
}
|
||||
|
||||
export function extractDebugArgFromScript(scriptValue: string): [string, number] | undefined {
|
||||
// matches --debug, --debug=1234, --debug-brk, debug-brk=1234, --inspect,
|
||||
// --inspect=1234, --inspect-brk, --inspect-brk=1234,
|
||||
// --inspect=localhost:1245, --inspect=127.0.0.1:1234, --inspect=[aa:1:0:0:0]:1234, --inspect=:1234
|
||||
let match = scriptValue.match(/--(inspect|debug)(-brk)?(=((\[[0-9a-fA-F:]*\]|[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+|[a-zA-Z0-9\.]*):)?(\d+))?/);
|
||||
|
||||
if (match) {
|
||||
if (match[6]) {
|
||||
return [match[1], parseInt(match[6])];
|
||||
}
|
||||
if (match[1] === 'inspect') {
|
||||
return [match[1], 9229];
|
||||
}
|
||||
if (match[1] === 'debug') {
|
||||
return [match[1], 5858];
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function startDebugging(scriptName: string, protocol: string, port: number, folder: WorkspaceFolder) {
|
||||
let p = 'inspector';
|
||||
if (protocol === 'debug') {
|
||||
p = 'legacy';
|
||||
}
|
||||
|
||||
let packageManager = getPackageManager(folder);
|
||||
const config: DebugConfiguration = {
|
||||
type: 'node',
|
||||
request: 'launch',
|
||||
name: `Debug ${scriptName}`,
|
||||
runtimeExecutable: packageManager,
|
||||
runtimeArgs: [
|
||||
'run-script',
|
||||
scriptName,
|
||||
],
|
||||
port: port,
|
||||
protocol: p
|
||||
};
|
||||
|
||||
if (folder) {
|
||||
debug.startDebugging(folder, config);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export type StringMap = { [s: string]: string; };
|
||||
|
||||
async function findAllScripts(buffer: string): Promise<StringMap> {
|
||||
|
@ -304,45 +353,39 @@ async function findAllScripts(buffer: string): Promise<StringMap> {
|
|||
return scripts;
|
||||
}
|
||||
|
||||
export function findScriptAtPosition(buffer: string, offset: number): string | undefined {
|
||||
export function findAllScriptRanges(buffer: string): Map<string, [number, number, string]> {
|
||||
var scripts: Map<string, [number, number, string]> = new Map();
|
||||
let script: string | undefined = undefined;
|
||||
let inScripts = false;
|
||||
let scriptStart: number | undefined;
|
||||
|
||||
let visitor: JSONVisitor = {
|
||||
onError(_error: ParseErrorCode, _offset: number, _length: number) {
|
||||
// TODO: inform user about the parse error
|
||||
},
|
||||
onObjectEnd() {
|
||||
if (inScripts) {
|
||||
inScripts = false;
|
||||
scriptStart = undefined;
|
||||
}
|
||||
},
|
||||
onLiteralValue(value: any, nodeOffset: number, nodeLength: number) {
|
||||
if (inScripts && scriptStart) {
|
||||
if (offset >= scriptStart && offset < nodeOffset + nodeLength) {
|
||||
// found the script
|
||||
inScripts = false;
|
||||
} else {
|
||||
script = undefined;
|
||||
}
|
||||
onLiteralValue(value: any, offset: number, length: number) {
|
||||
if (script) {
|
||||
scripts.set(script, [offset, length, value]);
|
||||
script = undefined;
|
||||
}
|
||||
},
|
||||
onObjectProperty(property: string, nodeOffset: number, nodeLength: number) {
|
||||
onObjectProperty(property: string, offset: number, length: number) {
|
||||
if (property === 'scripts') {
|
||||
inScripts = true;
|
||||
}
|
||||
else if (inScripts) {
|
||||
scriptStart = nodeOffset;
|
||||
script = property;
|
||||
}
|
||||
}
|
||||
};
|
||||
visit(buffer, visitor);
|
||||
return script;
|
||||
return scripts;
|
||||
}
|
||||
|
||||
|
||||
export async function getScripts(packageJsonUri: Uri): Promise<StringMap | undefined> {
|
||||
|
||||
if (packageJsonUri.scheme !== 'file') {
|
||||
|
|
Loading…
Reference in a new issue