mirror of
https://github.com/microsoft/vscode.git
synced 2026-04-23 10:08:49 +01:00
Merge branch 'master' into fix-70323
This commit is contained in:
@@ -18,7 +18,7 @@ import { ExtHostTask } from 'vs/workbench/api/node/extHostTask';
|
||||
import { ExtHostDebugService } from 'vs/workbench/api/node/extHostDebugService';
|
||||
import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch';
|
||||
import { ExtHostSearch } from 'vs/workbench/api/node/extHostSearch';
|
||||
import { NativeExtHostSearch } from 'vs/workbench/api/node/extHostSearch';
|
||||
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { IExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
|
||||
@@ -26,6 +26,8 @@ import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionS
|
||||
import { IExtHostStorage, ExtHostStorage } from 'vs/workbench/api/common/extHostStorage';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { ExtHostLogService } from 'vs/workbench/api/node/extHostLogService';
|
||||
import { IExtHostTunnelService } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { ExtHostTunnelService } from 'vs/workbench/api/node/extHostTunnelService';
|
||||
|
||||
// register singleton services
|
||||
registerSingleton(ILogService, ExtHostLogService);
|
||||
@@ -38,7 +40,8 @@ registerSingleton(IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors);
|
||||
registerSingleton(IExtHostTerminalService, ExtHostTerminalService);
|
||||
registerSingleton(IExtHostTask, ExtHostTask);
|
||||
registerSingleton(IExtHostDebugService, ExtHostDebugService);
|
||||
registerSingleton(IExtHostSearch, ExtHostSearch);
|
||||
registerSingleton(IExtHostSearch, NativeExtHostSearch);
|
||||
registerSingleton(IExtensionStoragePaths, ExtensionStoragePaths);
|
||||
registerSingleton(IExtHostExtensionService, ExtHostExtensionService);
|
||||
registerSingleton(IExtHostStorage, ExtHostStorage);
|
||||
registerSingleton(IExtHostTunnelService, ExtHostTunnelService);
|
||||
|
||||
@@ -101,9 +101,6 @@ export class CLIServer {
|
||||
for (const s of folderURIs) {
|
||||
try {
|
||||
urisToOpen.push({ folderUri: URI.parse(s) });
|
||||
if (!addMode && !forceReuseWindow) {
|
||||
forceNewWindow = true;
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
@@ -114,9 +111,6 @@ export class CLIServer {
|
||||
try {
|
||||
if (hasWorkspaceFileExtension(s)) {
|
||||
urisToOpen.push({ workspaceUri: URI.parse(s) });
|
||||
if (!forceReuseWindow) {
|
||||
forceNewWindow = true;
|
||||
}
|
||||
} else {
|
||||
urisToOpen.push({ fileUri: URI.parse(s) });
|
||||
}
|
||||
@@ -127,7 +121,8 @@ export class CLIServer {
|
||||
}
|
||||
if (urisToOpen.length) {
|
||||
const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined;
|
||||
const windowOpenArgs: INativeOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, waitMarkerFileURI };
|
||||
const preferNewWindow = !forceReuseWindow && !waitMarkerFileURI && !addMode;
|
||||
const windowOpenArgs: INativeOpenWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, preferNewWindow, waitMarkerFileURI };
|
||||
this._commands.executeCommand('_files.windowOpen', urisToOpen, windowOpenArgs);
|
||||
}
|
||||
res.writeHead(200);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,31 +3,21 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { ILogService, DelegatedLogService, LogLevel } from 'vs/platform/log/common/log';
|
||||
import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { SpdLogService } from 'vs/platform/log/node/spdlogService';
|
||||
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
|
||||
export class ExtHostLogService extends DelegatedLogService implements ILogService, ExtHostLogServiceShape {
|
||||
|
||||
constructor(
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@IExtHostOutputService extHostOutputService: IExtHostOutputService
|
||||
) {
|
||||
if (initData.logsLocation.scheme !== Schemas.file) { throw new Error('Only file-logging supported'); }
|
||||
super(new SpdLogService(ExtensionHostLogFileName, initData.logsLocation.fsPath, initData.logLevel));
|
||||
|
||||
// Register an output channel for exthost log
|
||||
extHostOutputService.createOutputChannelFromLogFile(
|
||||
initData.remote.isRemote ? localize('remote extension host Log', "Remote Extension Host") : localize('extension host Log', "Extension Host"),
|
||||
URI.file(join(initData.logsLocation.fsPath, `${ExtensionHostLogFileName}.log`))
|
||||
);
|
||||
if (initData.logFile.scheme !== Schemas.file) { throw new Error('Only file-logging supported'); }
|
||||
super(new SpdLogService(ExtensionHostLogFileName, dirname(initData.logFile).fsPath, initData.logLevel));
|
||||
}
|
||||
|
||||
$setLevel(level: LogLevel): void {
|
||||
|
||||
@@ -14,6 +14,7 @@ import { AbstractExtHostOutputChannel, ExtHostPushOutputChannel, ExtHostOutputSe
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { MutableDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class ExtHostOutputChannelBackedByFile extends AbstractExtHostOutputChannel {
|
||||
|
||||
@@ -55,6 +56,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService {
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@ILogService private readonly logService: ILogService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
) {
|
||||
super(extHostRpc);
|
||||
@@ -90,7 +92,7 @@ export class ExtHostOutputService2 extends ExtHostOutputService {
|
||||
return new ExtHostOutputChannelBackedByFile(name, appender, this._proxy);
|
||||
} catch (error) {
|
||||
// Do not crash if logger cannot be created
|
||||
console.log(error);
|
||||
this.logService.error(error);
|
||||
return new ExtHostPushOutputChannel(name, this._proxy);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,47 +3,37 @@
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as pfs from 'vs/base/node/pfs';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IFileQuery, IFolderQuery, IRawFileQuery, IRawQuery, IRawTextQuery, ISearchCompleteStats, ITextQuery, isSerializedFileMatch, ISerializedSearchProgressItem } from 'vs/workbench/services/search/common/search';
|
||||
import { FileSearchManager } from 'vs/workbench/services/search/node/fileSearchManager';
|
||||
import { IFileQuery, IRawFileQuery, ISearchCompleteStats, isSerializedFileMatch, ISerializedSearchProgressItem, ITextQuery } from 'vs/workbench/services/search/common/search';
|
||||
import { SearchService } from 'vs/workbench/services/search/node/rawSearchService';
|
||||
import { RipgrepSearchProvider } from 'vs/workbench/services/search/node/ripgrepSearchProvider';
|
||||
import { OutputChannel } from 'vs/workbench/services/search/node/ripgrepSearchUtils';
|
||||
import { TextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
|
||||
import * as vscode from 'vscode';
|
||||
import { ExtHostSearchShape, MainContext, MainThreadSearchShape } from '../common/extHost.protocol';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostSearch, reviveQuery } from 'vs/workbench/api/common/extHostSearch';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { NativeTextSearchManager } from 'vs/workbench/services/search/node/textSearchManager';
|
||||
import { TextSearchManager } from 'vs/workbench/services/search/common/textSearchManager';
|
||||
|
||||
export class ExtHostSearch implements ExtHostSearchShape {
|
||||
export class NativeExtHostSearch extends ExtHostSearch {
|
||||
|
||||
private readonly _proxy: MainThreadSearchShape;
|
||||
private readonly _textSearchProvider = new Map<number, vscode.TextSearchProvider>();
|
||||
private readonly _textSearchUsedSchemes = new Set<string>();
|
||||
private readonly _fileSearchProvider = new Map<number, vscode.FileSearchProvider>();
|
||||
private readonly _fileSearchUsedSchemes = new Set<string>();
|
||||
private _handlePool: number = 0;
|
||||
protected _pfs: typeof pfs = pfs; // allow extending for tests
|
||||
|
||||
private _internalFileSearchHandle: number = -1;
|
||||
private _internalFileSearchProvider: SearchService | null = null;
|
||||
|
||||
private _fileSearchManager: FileSearchManager;
|
||||
|
||||
protected _pfs: typeof pfs = pfs; // allow extending for tests
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@IURITransformerService private _uriTransformer: IURITransformerService,
|
||||
@ILogService private _logService: ILogService,
|
||||
@IURITransformerService _uriTransformer: IURITransformerService,
|
||||
@ILogService _logService: ILogService,
|
||||
) {
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadSearch);
|
||||
this._fileSearchManager = new FileSearchManager();
|
||||
super(extHostRpc, _uriTransformer, _logService);
|
||||
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this._registerEHSearchProviders();
|
||||
@@ -52,47 +42,11 @@ export class ExtHostSearch implements ExtHostSearchShape {
|
||||
|
||||
private _registerEHSearchProviders(): void {
|
||||
const outputChannel = new OutputChannel(this._logService);
|
||||
this.registerTextSearchProvider('file', new RipgrepSearchProvider(outputChannel));
|
||||
this.registerInternalFileSearchProvider('file', new SearchService());
|
||||
this.registerTextSearchProvider(Schemas.file, new RipgrepSearchProvider(outputChannel));
|
||||
this.registerInternalFileSearchProvider(Schemas.file, new SearchService());
|
||||
}
|
||||
|
||||
private _transformScheme(scheme: string): string {
|
||||
return this._uriTransformer.transformOutgoingScheme(scheme);
|
||||
}
|
||||
|
||||
registerTextSearchProvider(scheme: string, provider: vscode.TextSearchProvider): IDisposable {
|
||||
if (this._textSearchUsedSchemes.has(scheme)) {
|
||||
throw new Error(`a text search provider for the scheme '${scheme}' is already registered`);
|
||||
}
|
||||
|
||||
this._textSearchUsedSchemes.add(scheme);
|
||||
const handle = this._handlePool++;
|
||||
this._textSearchProvider.set(handle, provider);
|
||||
this._proxy.$registerTextSearchProvider(handle, this._transformScheme(scheme));
|
||||
return toDisposable(() => {
|
||||
this._textSearchUsedSchemes.delete(scheme);
|
||||
this._textSearchProvider.delete(handle);
|
||||
this._proxy.$unregisterProvider(handle);
|
||||
});
|
||||
}
|
||||
|
||||
registerFileSearchProvider(scheme: string, provider: vscode.FileSearchProvider): IDisposable {
|
||||
if (this._fileSearchUsedSchemes.has(scheme)) {
|
||||
throw new Error(`a file search provider for the scheme '${scheme}' is already registered`);
|
||||
}
|
||||
|
||||
this._fileSearchUsedSchemes.add(scheme);
|
||||
const handle = this._handlePool++;
|
||||
this._fileSearchProvider.set(handle, provider);
|
||||
this._proxy.$registerFileSearchProvider(handle, this._transformScheme(scheme));
|
||||
return toDisposable(() => {
|
||||
this._fileSearchUsedSchemes.delete(scheme);
|
||||
this._fileSearchProvider.delete(handle);
|
||||
this._proxy.$unregisterProvider(handle);
|
||||
});
|
||||
}
|
||||
|
||||
registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable {
|
||||
private registerInternalFileSearchProvider(scheme: string, provider: SearchService): IDisposable {
|
||||
const handle = this._handlePool++;
|
||||
this._internalFileSearchProvider = provider;
|
||||
this._internalFileSearchHandle = handle;
|
||||
@@ -103,23 +57,16 @@ export class ExtHostSearch implements ExtHostSearchShape {
|
||||
});
|
||||
}
|
||||
|
||||
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
|
||||
$provideFileSearchResults(handle: number, session: number, rawQuery: IRawFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
|
||||
const query = reviveQuery(rawQuery);
|
||||
if (handle === this._internalFileSearchHandle) {
|
||||
return this.doInternalFileSearch(handle, session, query, token);
|
||||
} else {
|
||||
const provider = this._fileSearchProvider.get(handle);
|
||||
if (provider) {
|
||||
return this._fileSearchManager.fileSearch(query, provider, batch => {
|
||||
this._proxy.$handleFileMatch(handle, session, batch.map(p => p.resource));
|
||||
}, token);
|
||||
} else {
|
||||
throw new Error('unknown provider: ' + handle);
|
||||
}
|
||||
}
|
||||
|
||||
return super.$provideFileSearchResults(handle, session, rawQuery, token);
|
||||
}
|
||||
|
||||
private doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
|
||||
private doInternalFileSearch(handle: number, session: number, rawQuery: IFileQuery, token: vscode.CancellationToken): Promise<ISearchCompleteStats> {
|
||||
const onResult = (ev: ISerializedSearchProgressItem) => {
|
||||
if (isSerializedFileMatch(ev)) {
|
||||
ev = [ev];
|
||||
@@ -147,37 +94,11 @@ export class ExtHostSearch implements ExtHostSearchShape {
|
||||
this._internalFileSearchProvider.clearCache(cacheKey);
|
||||
}
|
||||
|
||||
this._fileSearchManager.clearCache(cacheKey);
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
return super.$clearCache(cacheKey);
|
||||
}
|
||||
|
||||
$provideTextSearchResults(handle: number, session: number, rawQuery: IRawTextQuery, token: CancellationToken): Promise<ISearchCompleteStats> {
|
||||
const provider = this._textSearchProvider.get(handle);
|
||||
if (!provider || !provider.provideTextSearchResults) {
|
||||
throw new Error(`Unknown provider ${handle}`);
|
||||
}
|
||||
|
||||
const query = reviveQuery(rawQuery);
|
||||
const engine = new TextSearchManager(query, provider, this._pfs);
|
||||
return engine.search(progress => this._proxy.$handleTextMatch(handle, session, progress), token);
|
||||
protected createTextSearchManager(query: ITextQuery, provider: vscode.TextSearchProvider): TextSearchManager {
|
||||
return new NativeTextSearchManager(query, provider);
|
||||
}
|
||||
}
|
||||
|
||||
function reviveQuery<U extends IRawQuery>(rawQuery: U): U extends IRawTextQuery ? ITextQuery : IFileQuery {
|
||||
return {
|
||||
...<any>rawQuery, // TODO
|
||||
...{
|
||||
folderQueries: rawQuery.folderQueries && rawQuery.folderQueries.map(reviveFolderQuery),
|
||||
extraFileResources: rawQuery.extraFileResources && rawQuery.extraFileResources.map(components => URI.revive(components))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function reviveFolderQuery(rawFolderQuery: IFolderQuery<UriComponents>): IFolderQuery<URI> {
|
||||
return {
|
||||
...rawFolderQuery,
|
||||
folder: URI.revive(rawFolderQuery.folder)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'
|
||||
import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class ExtensionStoragePaths implements IExtensionStoragePaths {
|
||||
|
||||
@@ -22,7 +23,10 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
|
||||
readonly whenReady: Promise<string | undefined>;
|
||||
private _value?: string;
|
||||
|
||||
constructor(@IExtHostInitDataService initData: IExtHostInitDataService) {
|
||||
constructor(
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService,
|
||||
@ILogService private readonly _logService: ILogService,
|
||||
) {
|
||||
this._workspace = withNullAsUndefined(initData.workspace);
|
||||
this._environment = initData.environment;
|
||||
this.whenReady = this._getOrCreateWorkspaceStoragePath().then(value => this._value = value);
|
||||
@@ -69,7 +73,7 @@ export class ExtensionStoragePaths implements IExtensionStoragePaths {
|
||||
return storagePath;
|
||||
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
this._logService.error(e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import * as vscode from 'vscode';
|
||||
import * as tasks from '../common/shared/tasks';
|
||||
import * as Objects from 'vs/base/common/objects';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
@@ -22,6 +22,8 @@ import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecutionDTO, HandlerData } from 'vs/workbench/api/common/extHostTask';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
|
||||
export class ExtHostTask extends ExtHostTaskBase {
|
||||
private _variableResolver: ExtHostVariableResolverService | undefined;
|
||||
@@ -32,9 +34,10 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
@IExtHostWorkspace workspaceService: IExtHostWorkspace,
|
||||
@IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors,
|
||||
@IExtHostConfiguration configurationService: IExtHostConfiguration,
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService
|
||||
@IExtHostTerminalService extHostTerminalService: IExtHostTerminalService,
|
||||
@ILogService logService: ILogService
|
||||
) {
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService);
|
||||
super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService, logService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerTaskSystem(Schemas.vscodeRemote, {
|
||||
scheme: Schemas.vscodeRemote,
|
||||
@@ -71,7 +74,7 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
if (value) {
|
||||
for (let task of value) {
|
||||
if (!task.definition || !validTypes[task.definition.type]) {
|
||||
console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
this._logService.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`);
|
||||
}
|
||||
|
||||
const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension);
|
||||
@@ -100,7 +103,7 @@ export class ExtHostTask extends ExtHostTaskBase {
|
||||
private async getVariableResolver(workspaceFolders: vscode.WorkspaceFolder[]): Promise<ExtHostVariableResolverService> {
|
||||
if (this._variableResolver === undefined) {
|
||||
const configProvider = await this._configurationService.getConfigProvider();
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider);
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders, this._editorService, configProvider, process.env as IProcessEnvironment);
|
||||
}
|
||||
return this._variableResolver;
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/t
|
||||
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
|
||||
import { ExtHostWorkspace, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
|
||||
import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostDebugService';
|
||||
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { getSystemShell, detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal';
|
||||
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
|
||||
@@ -120,7 +120,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
private async _updateVariableResolver(): Promise<void> {
|
||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2();
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider);
|
||||
this._variableResolver = new ExtHostVariableResolverService(workspaceFolders || [], this._extHostDocumentsAndEditors, configProvider, process.env as platform.IProcessEnvironment);
|
||||
}
|
||||
|
||||
public async $spawnExtHostProcess(id: number, shellLaunchConfigDto: IShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents | undefined, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
|
||||
|
||||
198
src/vs/workbench/api/node/extHostTunnelService.ts
Normal file
198
src/vs/workbench/api/node/extHostTunnelService.ts
Normal file
@@ -0,0 +1,198 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { MainThreadTunnelServiceShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import * as vscode from 'vscode';
|
||||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { exec } from 'child_process';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import * as fs from 'fs';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { IExtHostTunnelService, TunnelOptions, TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { asPromise } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
class ExtensionTunnel implements vscode.Tunnel {
|
||||
private _onDispose: Emitter<void> = new Emitter();
|
||||
onDispose: Event<void> = this._onDispose.event;
|
||||
|
||||
constructor(
|
||||
public readonly remote: { port: number; host: string; },
|
||||
public readonly localAddress: string,
|
||||
private readonly _dispose: () => void) { }
|
||||
|
||||
dispose(): void {
|
||||
this._onDispose.fire();
|
||||
this._dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostTunnelService extends Disposable implements IExtHostTunnelService {
|
||||
readonly _serviceBrand: undefined;
|
||||
private readonly _proxy: MainThreadTunnelServiceShape;
|
||||
private _forwardPortProvider: ((tunnelOptions: TunnelOptions) => Thenable<vscode.Tunnel> | undefined) | undefined;
|
||||
private _extensionTunnels: Map<string, Map<number, vscode.Tunnel>> = new Map();
|
||||
|
||||
constructor(
|
||||
@IExtHostRpcService extHostRpc: IExtHostRpcService,
|
||||
@IExtHostInitDataService initData: IExtHostInitDataService
|
||||
) {
|
||||
super();
|
||||
this._proxy = extHostRpc.getProxy(MainContext.MainThreadTunnelService);
|
||||
if (initData.remote.isRemote && initData.remote.authority) {
|
||||
this.registerCandidateFinder();
|
||||
}
|
||||
}
|
||||
async makeTunnel(forward: TunnelOptions): Promise<vscode.Tunnel | undefined> {
|
||||
const tunnel = await this._proxy.$openTunnel(forward);
|
||||
if (tunnel) {
|
||||
const disposableTunnel: vscode.Tunnel = new ExtensionTunnel(tunnel.remote, tunnel.localAddress, () => {
|
||||
return this._proxy.$closeTunnel(tunnel.remote.port);
|
||||
});
|
||||
this._register(disposableTunnel);
|
||||
return disposableTunnel;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
registerCandidateFinder(): Promise<void> {
|
||||
return this._proxy.$registerCandidateFinder();
|
||||
}
|
||||
|
||||
async setForwardPortProvider(provider: vscode.RemoteAuthorityResolver | undefined): Promise<IDisposable> {
|
||||
if (provider && provider.forwardPort) {
|
||||
this._forwardPortProvider = provider.forwardPort;
|
||||
await this._proxy.$setTunnelProvider();
|
||||
} else {
|
||||
this._forwardPortProvider = undefined;
|
||||
}
|
||||
return toDisposable(() => {
|
||||
this._forwardPortProvider = undefined;
|
||||
});
|
||||
}
|
||||
|
||||
async $closeTunnel(remote: { host: string, port: number }): Promise<void> {
|
||||
if (this._extensionTunnels.has(remote.host)) {
|
||||
const hostMap = this._extensionTunnels.get(remote.host)!;
|
||||
if (hostMap.has(remote.port)) {
|
||||
hostMap.get(remote.port)!.dispose();
|
||||
hostMap.delete(remote.port);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$forwardPort(tunnelOptions: TunnelOptions): Promise<TunnelDto> | undefined {
|
||||
if (this._forwardPortProvider) {
|
||||
const providedPort = this._forwardPortProvider!(tunnelOptions);
|
||||
if (providedPort !== undefined) {
|
||||
return asPromise(() => providedPort).then(tunnel => {
|
||||
if (!this._extensionTunnels.has(tunnelOptions.remote.host)) {
|
||||
this._extensionTunnels.set(tunnelOptions.remote.host, new Map());
|
||||
}
|
||||
this._extensionTunnels.get(tunnelOptions.remote.host)!.set(tunnelOptions.remote.port, tunnel);
|
||||
this._register(tunnel.onDispose(() => this._proxy.$closeTunnel(tunnel.remote.port)));
|
||||
return Promise.resolve(TunnelDto.fromApiTunnel(tunnel));
|
||||
});
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
async $findCandidatePorts(): Promise<{ port: number, detail: string }[]> {
|
||||
if (!isLinux) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const ports: { port: number, detail: string }[] = [];
|
||||
const tcp: string = fs.readFileSync('/proc/net/tcp', 'utf8');
|
||||
const tcp6: string = fs.readFileSync('/proc/net/tcp6', 'utf8');
|
||||
const procSockets: string = await (new Promise(resolve => {
|
||||
exec('ls -l /proc/[0-9]*/fd/[0-9]* | grep socket:', (error, stdout, stderr) => {
|
||||
resolve(stdout);
|
||||
});
|
||||
}));
|
||||
|
||||
const procChildren = fs.readdirSync('/proc');
|
||||
const processes: { pid: number, cwd: string, cmd: string }[] = [];
|
||||
for (let childName of procChildren) {
|
||||
try {
|
||||
const pid: number = Number(childName);
|
||||
const childUri = resources.joinPath(URI.file('/proc'), childName);
|
||||
const childStat = fs.statSync(childUri.fsPath);
|
||||
if (childStat.isDirectory() && !isNaN(pid)) {
|
||||
const cwd = fs.readlinkSync(resources.joinPath(childUri, 'cwd').fsPath);
|
||||
const cmd = fs.readFileSync(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8').replace(/\0/g, ' ');
|
||||
processes.push({ pid, cwd, cmd });
|
||||
}
|
||||
} catch (e) {
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
const connections: { socket: number, ip: string, port: number }[] = this.loadListeningPorts(tcp, tcp6);
|
||||
const sockets = this.getSockets(procSockets);
|
||||
|
||||
const socketMap = sockets.reduce((m, socket) => {
|
||||
m[socket.socket] = socket;
|
||||
return m;
|
||||
}, {} as Record<string, typeof sockets[0]>);
|
||||
const processMap = processes.reduce((m, process) => {
|
||||
m[process.pid] = process;
|
||||
return m;
|
||||
}, {} as Record<string, typeof processes[0]>);
|
||||
|
||||
connections.filter((connection => socketMap[connection.socket])).forEach(({ socket, ip, port }) => {
|
||||
const command = processMap[socketMap[socket].pid].cmd;
|
||||
if (!command.match('.*\.vscode\-server\-[a-zA-Z]+\/bin.*') && (command.indexOf('out/vs/server/main.js') === -1)) {
|
||||
ports.push({ port, detail: processMap[socketMap[socket].pid].cmd });
|
||||
}
|
||||
});
|
||||
|
||||
return ports;
|
||||
}
|
||||
|
||||
private getSockets(stdout: string) {
|
||||
const lines = stdout.trim().split('\n');
|
||||
return lines.map(line => {
|
||||
const match = /\/proc\/(\d+)\/fd\/\d+ -> socket:\[(\d+)\]/.exec(line)!;
|
||||
return {
|
||||
pid: parseInt(match[1], 10),
|
||||
socket: parseInt(match[2], 10)
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
private loadListeningPorts(...stdouts: string[]): { socket: number, ip: string, port: number }[] {
|
||||
const table = ([] as Record<string, string>[]).concat(...stdouts.map(this.loadConnectionTable));
|
||||
return [
|
||||
...new Map(
|
||||
table.filter(row => row.st === '0A')
|
||||
.map(row => {
|
||||
const address = row.local_address.split(':');
|
||||
return {
|
||||
socket: parseInt(row.inode, 10),
|
||||
ip: address[0],
|
||||
port: parseInt(address[1], 16)
|
||||
};
|
||||
}).map(port => [port.port, port])
|
||||
).values()
|
||||
];
|
||||
}
|
||||
|
||||
private loadConnectionTable(stdout: string): Record<string, string>[] {
|
||||
const lines = stdout.trim().split('\n');
|
||||
const names = lines.shift()!.trim().split(/\s+/)
|
||||
.filter(name => name !== 'rx_queue' && name !== 'tm->when');
|
||||
const table = lines.map(line => line.trim().split(/\s+/).reduce((obj, value, i) => {
|
||||
obj[names[i] || i] = value;
|
||||
return obj;
|
||||
}, {} as Record<string, string>));
|
||||
return table;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user