mirror of
https://github.com/coder/code-server.git
synced 2025-08-02 22:58:50 +08:00
Merge commit 'be3e8236086165e5e45a5a10783823874b3f3ebd' as 'lib/vscode'
This commit is contained in:
300
lib/vscode/extensions/npm/src/npmView.ts
Normal file
300
lib/vscode/extensions/npm/src/npmView.ts
Normal file
@ -0,0 +1,300 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { JSONVisitor, visit } from 'jsonc-parser';
|
||||
import * as path from 'path';
|
||||
import {
|
||||
commands, Event, EventEmitter, ExtensionContext,
|
||||
Selection, Task,
|
||||
TaskGroup, tasks, TextDocument, ThemeIcon, TreeDataProvider, TreeItem, TreeItemCollapsibleState, Uri,
|
||||
window, workspace, WorkspaceFolder
|
||||
} from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
import {
|
||||
createTask, getTaskName, isAutoDetectionEnabled, isWorkspaceFolder, NpmTaskDefinition,
|
||||
startDebugging
|
||||
} from './tasks';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
class Folder extends TreeItem {
|
||||
packages: PackageJSON[] = [];
|
||||
workspaceFolder: WorkspaceFolder;
|
||||
|
||||
constructor(folder: WorkspaceFolder) {
|
||||
super(folder.name, TreeItemCollapsibleState.Expanded);
|
||||
this.contextValue = 'folder';
|
||||
this.resourceUri = folder.uri;
|
||||
this.workspaceFolder = folder;
|
||||
this.iconPath = ThemeIcon.Folder;
|
||||
}
|
||||
|
||||
addPackage(packageJson: PackageJSON) {
|
||||
this.packages.push(packageJson);
|
||||
}
|
||||
}
|
||||
|
||||
const packageName = 'package.json';
|
||||
|
||||
class PackageJSON extends TreeItem {
|
||||
path: string;
|
||||
folder: Folder;
|
||||
scripts: NpmScript[] = [];
|
||||
|
||||
static getLabel(_folderName: string, relativePath: string): string {
|
||||
if (relativePath.length > 0) {
|
||||
return path.join(relativePath, packageName);
|
||||
}
|
||||
return packageName;
|
||||
}
|
||||
|
||||
constructor(folder: Folder, relativePath: string) {
|
||||
super(PackageJSON.getLabel(folder.label!, relativePath), TreeItemCollapsibleState.Expanded);
|
||||
this.folder = folder;
|
||||
this.path = relativePath;
|
||||
this.contextValue = 'packageJSON';
|
||||
if (relativePath) {
|
||||
this.resourceUri = Uri.file(path.join(folder!.resourceUri!.fsPath, relativePath, packageName));
|
||||
} else {
|
||||
this.resourceUri = Uri.file(path.join(folder!.resourceUri!.fsPath, packageName));
|
||||
}
|
||||
this.iconPath = ThemeIcon.File;
|
||||
}
|
||||
|
||||
addScript(script: NpmScript) {
|
||||
this.scripts.push(script);
|
||||
}
|
||||
}
|
||||
|
||||
type ExplorerCommands = 'open' | 'run';
|
||||
|
||||
class NpmScript extends TreeItem {
|
||||
task: Task;
|
||||
package: PackageJSON;
|
||||
|
||||
constructor(_context: ExtensionContext, packageJson: PackageJSON, task: Task) {
|
||||
super(task.name, TreeItemCollapsibleState.None);
|
||||
const command: ExplorerCommands = workspace.getConfiguration('npm').get<ExplorerCommands>('scriptExplorerAction') || 'open';
|
||||
|
||||
const commandList = {
|
||||
'open': {
|
||||
title: 'Edit Script',
|
||||
command: 'npm.openScript',
|
||||
arguments: [this]
|
||||
},
|
||||
'run': {
|
||||
title: 'Run Script',
|
||||
command: 'npm.runScript',
|
||||
arguments: [this]
|
||||
}
|
||||
};
|
||||
this.contextValue = 'script';
|
||||
this.package = packageJson;
|
||||
this.task = task;
|
||||
this.command = commandList[command];
|
||||
|
||||
if (task.group && task.group === TaskGroup.Clean) {
|
||||
this.iconPath = new ThemeIcon('wrench-subaction');
|
||||
} else {
|
||||
this.iconPath = new ThemeIcon('wrench');
|
||||
}
|
||||
if (task.detail) {
|
||||
this.tooltip = task.detail;
|
||||
}
|
||||
}
|
||||
|
||||
getFolder(): WorkspaceFolder {
|
||||
return this.package.folder.workspaceFolder;
|
||||
}
|
||||
}
|
||||
|
||||
class NoScripts extends TreeItem {
|
||||
constructor(message: string) {
|
||||
super(message, TreeItemCollapsibleState.None);
|
||||
this.contextValue = 'noscripts';
|
||||
}
|
||||
}
|
||||
|
||||
export class NpmScriptsTreeDataProvider implements TreeDataProvider<TreeItem> {
|
||||
private taskTree: Folder[] | PackageJSON[] | NoScripts[] | null = null;
|
||||
private extensionContext: ExtensionContext;
|
||||
private _onDidChangeTreeData: EventEmitter<TreeItem | null> = new EventEmitter<TreeItem | null>();
|
||||
readonly onDidChangeTreeData: Event<TreeItem | null> = this._onDidChangeTreeData.event;
|
||||
|
||||
constructor(context: ExtensionContext) {
|
||||
const subscriptions = context.subscriptions;
|
||||
this.extensionContext = context;
|
||||
subscriptions.push(commands.registerCommand('npm.runScript', this.runScript, this));
|
||||
subscriptions.push(commands.registerCommand('npm.debugScript', this.debugScript, this));
|
||||
subscriptions.push(commands.registerCommand('npm.openScript', this.openScript, this));
|
||||
subscriptions.push(commands.registerCommand('npm.runInstall', this.runInstall, this));
|
||||
}
|
||||
|
||||
private async runScript(script: NpmScript) {
|
||||
tasks.executeTask(script.task);
|
||||
}
|
||||
|
||||
private async debugScript(script: NpmScript) {
|
||||
startDebugging(script.task.definition.script, path.dirname(script.package.resourceUri!.fsPath), script.getFolder());
|
||||
}
|
||||
|
||||
private findScript(document: TextDocument, script?: NpmScript): number {
|
||||
let scriptOffset = 0;
|
||||
let inScripts = false;
|
||||
|
||||
let visitor: JSONVisitor = {
|
||||
onError() {
|
||||
return scriptOffset;
|
||||
},
|
||||
onObjectEnd() {
|
||||
if (inScripts) {
|
||||
inScripts = false;
|
||||
}
|
||||
},
|
||||
onObjectProperty(property: string, offset: number, _length: number) {
|
||||
if (property === 'scripts') {
|
||||
inScripts = true;
|
||||
if (!script) { // select the script section
|
||||
scriptOffset = offset;
|
||||
}
|
||||
}
|
||||
else if (inScripts && script) {
|
||||
let label = getTaskName(property, script.task.definition.path);
|
||||
if (script.task.name === label) {
|
||||
scriptOffset = offset;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
visit(document.getText(), visitor);
|
||||
return scriptOffset;
|
||||
|
||||
}
|
||||
|
||||
private async runInstall(selection: PackageJSON) {
|
||||
let uri: Uri | undefined = undefined;
|
||||
if (selection instanceof PackageJSON) {
|
||||
uri = selection.resourceUri;
|
||||
}
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
let task = createTask('install', 'install', selection.folder.workspaceFolder, uri, undefined, []);
|
||||
tasks.executeTask(task);
|
||||
}
|
||||
|
||||
private async openScript(selection: PackageJSON | NpmScript) {
|
||||
let uri: Uri | undefined = undefined;
|
||||
if (selection instanceof PackageJSON) {
|
||||
uri = selection.resourceUri!;
|
||||
} else if (selection instanceof NpmScript) {
|
||||
uri = selection.package.resourceUri;
|
||||
}
|
||||
if (!uri) {
|
||||
return;
|
||||
}
|
||||
let document: TextDocument = await workspace.openTextDocument(uri);
|
||||
let offset = this.findScript(document, selection instanceof NpmScript ? selection : undefined);
|
||||
let position = document.positionAt(offset);
|
||||
await window.showTextDocument(document, { preserveFocus: true, selection: new Selection(position, position) });
|
||||
}
|
||||
|
||||
public refresh() {
|
||||
this.taskTree = null;
|
||||
this._onDidChangeTreeData.fire(null);
|
||||
}
|
||||
|
||||
getTreeItem(element: TreeItem): TreeItem {
|
||||
return element;
|
||||
}
|
||||
|
||||
getParent(element: TreeItem): TreeItem | null {
|
||||
if (element instanceof Folder) {
|
||||
return null;
|
||||
}
|
||||
if (element instanceof PackageJSON) {
|
||||
return element.folder;
|
||||
}
|
||||
if (element instanceof NpmScript) {
|
||||
return element.package;
|
||||
}
|
||||
if (element instanceof NoScripts) {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async getChildren(element?: TreeItem): Promise<TreeItem[]> {
|
||||
if (!this.taskTree) {
|
||||
let taskItems = await tasks.fetchTasks({ type: 'npm' });
|
||||
if (taskItems) {
|
||||
this.taskTree = this.buildTaskTree(taskItems);
|
||||
if (this.taskTree.length === 0) {
|
||||
let message = localize('noScripts', 'No scripts found.');
|
||||
if (!isAutoDetectionEnabled()) {
|
||||
message = localize('autoDetectIsOff', 'The setting "npm.autoDetect" is "off".');
|
||||
}
|
||||
this.taskTree = [new NoScripts(message)];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (element instanceof Folder) {
|
||||
return element.packages;
|
||||
}
|
||||
if (element instanceof PackageJSON) {
|
||||
return element.scripts;
|
||||
}
|
||||
if (element instanceof NpmScript) {
|
||||
return [];
|
||||
}
|
||||
if (element instanceof NoScripts) {
|
||||
return [];
|
||||
}
|
||||
if (!element) {
|
||||
if (this.taskTree) {
|
||||
return this.taskTree;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
private isInstallTask(task: Task): boolean {
|
||||
let fullName = getTaskName('install', task.definition.path);
|
||||
return fullName === task.name;
|
||||
}
|
||||
|
||||
private buildTaskTree(tasks: Task[]): Folder[] | PackageJSON[] | NoScripts[] {
|
||||
let folders: Map<String, Folder> = new Map();
|
||||
let packages: Map<String, PackageJSON> = new Map();
|
||||
|
||||
let folder = null;
|
||||
let packageJson = null;
|
||||
|
||||
tasks.forEach(each => {
|
||||
if (isWorkspaceFolder(each.scope) && !this.isInstallTask(each)) {
|
||||
folder = folders.get(each.scope.name);
|
||||
if (!folder) {
|
||||
folder = new Folder(each.scope);
|
||||
folders.set(each.scope.name, folder);
|
||||
}
|
||||
let definition: NpmTaskDefinition = <NpmTaskDefinition>each.definition;
|
||||
let relativePath = definition.path ? definition.path : '';
|
||||
let fullPath = path.join(each.scope.name, relativePath);
|
||||
packageJson = packages.get(fullPath);
|
||||
if (!packageJson) {
|
||||
packageJson = new PackageJSON(folder, relativePath);
|
||||
folder.addPackage(packageJson);
|
||||
packages.set(fullPath, packageJson);
|
||||
}
|
||||
let script = new NpmScript(this.extensionContext, packageJson, each);
|
||||
packageJson.addScript(script);
|
||||
}
|
||||
});
|
||||
if (folders.size === 1) {
|
||||
return [...packages.values()];
|
||||
}
|
||||
return [...folders.values()];
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user