提交 80b08b4c 编写于 作者: E Erich Gamma

Add code lenses to run/debug a script

上级 26e5a55c
......@@ -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"
}
/*---------------------------------------------------------------------------------------------
* 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') {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册