Merge from vscode 05fc61ffb1aee9fd19173c32113daed079f9b7bd (#5074)

* Merge from vscode 05fc61ffb1aee9fd19173c32113daed079f9b7bd

* fix tests
This commit is contained in:
Anthony Dresser
2019-04-16 22:11:30 -07:00
committed by GitHub
parent 2f8519cb6b
commit 8956b591f7
217 changed files with 5120 additions and 3926 deletions

View File

@@ -90,8 +90,6 @@ const vscodeResources = [
'out-build/vs/workbench/contrib/welcome/walkThrough/**/*.md', 'out-build/vs/workbench/contrib/welcome/walkThrough/**/*.md',
'out-build/vs/workbench/services/files/**/*.exe', 'out-build/vs/workbench/services/files/**/*.exe',
'out-build/vs/workbench/services/files/**/*.md', 'out-build/vs/workbench/services/files/**/*.md',
'out-build/vs/workbench/services/files2/**/*.exe',
'out-build/vs/workbench/services/files2/**/*.md',
'out-build/vs/code/electron-browser/workbench/**', 'out-build/vs/code/electron-browser/workbench/**',
'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js', 'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js',
'out-build/vs/code/electron-browser/issue/issueReporter.js', 'out-build/vs/code/electron-browser/issue/issueReporter.js',

View File

@@ -222,10 +222,6 @@
"name": "vs/workbench/services/files", "name": "vs/workbench/services/files",
"project": "vscode-workbench" "project": "vscode-workbench"
}, },
{
"name": "vs/workbench/services/files2",
"project": "vscode-workbench"
},
{ {
"name": "vs/workbench/services/integrity", "name": "vs/workbench/services/integrity",
"project": "vscode-workbench" "project": "vscode-workbench"

View File

@@ -71,8 +71,9 @@
"scope": [ "scope": [
"keyword.control", "keyword.control",
"keyword.operator.new.cpp", "keyword.operator.new.cpp",
"keyword.operator.delete.cpp", "keyword.operator.delete",
"keyword.other.using" "keyword.other.using",
"keyword.other.operator"
], ],
"settings": { "settings": {
"foreground": "#C586C0" "foreground": "#C586C0"

View File

@@ -69,7 +69,8 @@
"keyword.control", "keyword.control",
"keyword.operator.new.cpp", "keyword.operator.new.cpp",
"keyword.operator.delete.cpp", "keyword.operator.delete.cpp",
"keyword.other.using" "keyword.other.using",
"keyword.other.operator"
], ],
"settings": { "settings": {
"foreground": "#C586C0" "foreground": "#C586C0"

View File

@@ -72,7 +72,8 @@
"keyword.control", "keyword.control",
"keyword.operator.new.cpp", "keyword.operator.new.cpp",
"keyword.operator.delete.cpp", "keyword.operator.delete.cpp",
"keyword.other.using" "keyword.other.using",
"keyword.other.operator"
], ],
"settings": { "settings": {
"foreground": "#AF00DB" "foreground": "#AF00DB"

View File

@@ -116,7 +116,8 @@
"keyword.control", "keyword.control",
"keyword.operator.new.cpp", "keyword.operator.new.cpp",
"keyword.operator.delete.cpp", "keyword.operator.delete.cpp",
"keyword.other.using" "keyword.other.using",
"keyword.other.operator"
], ],
"settings": { "settings": {
"foreground": "#98676a" "foreground": "#98676a"

View File

@@ -259,7 +259,8 @@
"keyword.control", "keyword.control",
"keyword.operator.new.cpp", "keyword.operator.new.cpp",
"keyword.operator.delete.cpp", "keyword.operator.delete.cpp",
"keyword.other.using" "keyword.other.using",
"keyword.other.operator"
], ],
"settings": { "settings": {
"fontStyle": "", "fontStyle": "",

View File

@@ -7,19 +7,18 @@ APP_NAME="@@APPNAME@@"
QUALITY="@@QUALITY@@" QUALITY="@@QUALITY@@"
NAME="@@NAME@@" NAME="@@NAME@@"
set -e
if grep -qi Microsoft /proc/version; then if grep -qi Microsoft /proc/version; then
# in a wsl shell # in a wsl shell
WIN_CODE_CMD=$(wslpath -w "$(dirname "$(realpath "$0")")/$APP_NAME.cmd") WIN_CODE_CMD=$(wslpath -w "$(dirname "$(realpath "$0")")/$APP_NAME.cmd")
if ! [ -z "$WIN_CODE_CMD" ]; then
WSL_EXT_ID="ms-vscode.remote-wsl" WSL_EXT_ID="ms-vscode.remote-wsl"
WSL_EXT_WLOC=$(cmd.exe /c "$WIN_CODE_CMD" --locate-extension $WSL_EXT_ID) WSL_EXT_WLOC=$(cmd.exe /c "$WIN_CODE_CMD" --locate-extension $WSL_EXT_ID)
if ! [ -z "$WSL_EXT_WLOC" ]; then if ! [ -z "$WSL_EXT_WLOC" ]; then
# replace \r\n with \n in WSL_EXT_WLOC, get linux path for # replace \r\n with \n in WSL_EXT_WLOC, get linux path for
WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh WSL_CODE=$(wslpath -u "${WSL_EXT_WLOC%%[[:cntrl:]]}")/scripts/wslCode.sh
$WSL_CODE $COMMIT $QUALITY "$WIN_CODE_CMD" "$APP_NAME" "$@" $WSL_CODE $COMMIT $QUALITY "$WIN_CODE_CMD" "$APP_NAME" "$@"
exit $? exit $?
fi
fi fi
fi fi

View File

@@ -12,7 +12,7 @@ import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs'; import * as pfs from 'vs/base/node/pfs';
import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { Workspace, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; import { ConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService';
import { TestContextService } from 'vs/workbench/test/workbenchTestServices'; import { TestContextService } from 'vs/workbench/test/workbenchTestServices';
import { IExtensionHostDebugParams, IDebugParams, ParsedArgs } from 'vs/platform/environment/common/environment'; import { IExtensionHostDebugParams, IDebugParams, ParsedArgs } from 'vs/platform/environment/common/environment';
@@ -113,7 +113,7 @@ suite('Insights Utils tests', function () {
const contextService = new TestContextService( const contextService = new TestContextService(
new Workspace( new Workspace(
'TestWorkspace', 'TestWorkspace',
toWorkspaceFolders([{ path: queryFileDir }]) [toWorkspaceFolder(URI.file(queryFileDir))]
)); ));
const configurationResolverService = new ConfigurationResolverService( const configurationResolverService = new ConfigurationResolverService(
undefined, undefined,
@@ -133,8 +133,8 @@ suite('Insights Utils tests', function () {
const contextService = new TestContextService( const contextService = new TestContextService(
new Workspace( new Workspace(
'TestWorkspace', 'TestWorkspace',
toWorkspaceFolders([{ path: os.tmpdir() }]) [toWorkspaceFolder(URI.file(os.tmpdir()))])
)); );
const configurationResolverService = new ConfigurationResolverService( const configurationResolverService = new ConfigurationResolverService(
undefined, undefined,
new TestEnvironmentService({}), new TestEnvironmentService({}),
@@ -194,7 +194,7 @@ suite('Insights Utils tests', function () {
test('resolveQueryFilePath resolves path correctly with env var and non-empty workspace', async () => { test('resolveQueryFilePath resolves path correctly with env var and non-empty workspace', async () => {
const contextService = new TestContextService( const contextService = new TestContextService(
new Workspace('TestWorkspace', toWorkspaceFolders([{ path: os.tmpdir() }]))); new Workspace('TestWorkspace', [toWorkspaceFolder(URI.file(os.tmpdir()))]));
// Create mock window service with env variable containing test folder for resolution // Create mock window service with env variable containing test folder for resolution
const configurationResolverService = new ConfigurationResolverService( const configurationResolverService = new ConfigurationResolverService(

View File

@@ -397,7 +397,7 @@ export class InputBox extends Widget {
return errorMsg ? errorMsg.type !== MessageType.ERROR : true; return errorMsg ? errorMsg.type !== MessageType.ERROR : true;
} }
private stylesForType(type: MessageType | undefined): { border: Color | undefined; background: Color | undefined; foreground: Color | undefined } { public stylesForType(type: MessageType | undefined): { border: Color | undefined; background: Color | undefined; foreground: Color | undefined } {
switch (type) { switch (type) {
case MessageType.INFO: return { border: this.inputValidationInfoBorder, background: this.inputValidationInfoBackground, foreground: this.inputValidationInfoForeground }; case MessageType.INFO: return { border: this.inputValidationInfoBorder, background: this.inputValidationInfoBackground, foreground: this.inputValidationInfoForeground };
case MessageType.WARNING: return { border: this.inputValidationWarningBorder, background: this.inputValidationWarningBackground, foreground: this.inputValidationWarningForeground }; case MessageType.WARNING: return { border: this.inputValidationWarningBorder, background: this.inputValidationWarningBackground, foreground: this.inputValidationWarningForeground };

View File

@@ -11,7 +11,7 @@ let textDecoder: TextDecoder | null;
export class VSBuffer { export class VSBuffer {
public static alloc(byteLength: number): VSBuffer { static alloc(byteLength: number): VSBuffer {
if (hasBuffer) { if (hasBuffer) {
return new VSBuffer(Buffer.allocUnsafe(byteLength)); return new VSBuffer(Buffer.allocUnsafe(byteLength));
} else { } else {
@@ -19,7 +19,7 @@ export class VSBuffer {
} }
} }
public static wrap(actual: Uint8Array): VSBuffer { static wrap(actual: Uint8Array): VSBuffer {
if (hasBuffer && !(Buffer.isBuffer(actual))) { if (hasBuffer && !(Buffer.isBuffer(actual))) {
// https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length // https://nodejs.org/dist/latest-v10.x/docs/api/buffer.html#buffer_class_method_buffer_from_arraybuffer_byteoffset_length
// Create a zero-copy Buffer wrapper around the ArrayBuffer pointed to by the Uint8Array // Create a zero-copy Buffer wrapper around the ArrayBuffer pointed to by the Uint8Array
@@ -28,7 +28,7 @@ export class VSBuffer {
return new VSBuffer(actual); return new VSBuffer(actual);
} }
public static fromString(source: string): VSBuffer { static fromString(source: string): VSBuffer {
if (hasBuffer) { if (hasBuffer) {
return new VSBuffer(Buffer.from(source)); return new VSBuffer(Buffer.from(source));
} else { } else {
@@ -39,7 +39,7 @@ export class VSBuffer {
} }
} }
public static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer { static concat(buffers: VSBuffer[], totalLength?: number): VSBuffer {
if (typeof totalLength === 'undefined') { if (typeof totalLength === 'undefined') {
totalLength = 0; totalLength = 0;
for (let i = 0, len = buffers.length; i < len; i++) { for (let i = 0, len = buffers.length; i < len; i++) {
@@ -58,15 +58,15 @@ export class VSBuffer {
return ret; return ret;
} }
public readonly buffer: Uint8Array; readonly buffer: Uint8Array;
public readonly byteLength: number; readonly byteLength: number;
private constructor(buffer: Uint8Array) { private constructor(buffer: Uint8Array) {
this.buffer = buffer; this.buffer = buffer;
this.byteLength = this.buffer.byteLength; this.byteLength = this.buffer.byteLength;
} }
public toString(): string { toString(): string {
if (hasBuffer) { if (hasBuffer) {
return this.buffer.toString(); return this.buffer.toString();
} else { } else {
@@ -77,33 +77,32 @@ export class VSBuffer {
} }
} }
public slice(start?: number, end?: number): VSBuffer { slice(start?: number, end?: number): VSBuffer {
return new VSBuffer(this.buffer.slice(start, end)); return new VSBuffer(this.buffer.slice(start, end));
} }
public set(array: VSBuffer, offset?: number): void { set(array: VSBuffer, offset?: number): void {
this.buffer.set(array.buffer, offset); this.buffer.set(array.buffer, offset);
} }
public readUint32BE(offset: number): number { readUInt32BE(offset: number): number {
return readUint32BE(this.buffer, offset); return readUInt32BE(this.buffer, offset);
} }
public writeUint32BE(value: number, offset: number): void { writeUInt32BE(value: number, offset: number): void {
writeUint32BE(this.buffer, value, offset); writeUInt32BE(this.buffer, value, offset);
} }
public readUint8(offset: number): number { readUInt8(offset: number): number {
return readUint8(this.buffer, offset); return readUInt8(this.buffer, offset);
} }
public writeUint8(value: number, offset: number): void { writeUInt8(value: number, offset: number): void {
writeUint8(this.buffer, value, offset); writeUInt8(this.buffer, value, offset);
} }
} }
function readUint32BE(source: Uint8Array, offset: number): number { function readUInt32BE(source: Uint8Array, offset: number): number {
return ( return (
source[offset] * 2 ** 24 source[offset] * 2 ** 24
+ source[offset + 1] * 2 ** 16 + source[offset + 1] * 2 ** 16
@@ -112,7 +111,7 @@ function readUint32BE(source: Uint8Array, offset: number): number {
); );
} }
function writeUint32BE(destination: Uint8Array, value: number, offset: number): void { function writeUInt32BE(destination: Uint8Array, value: number, offset: number): void {
destination[offset + 3] = value; destination[offset + 3] = value;
value = value >>> 8; value = value >>> 8;
destination[offset + 2] = value; destination[offset + 2] = value;
@@ -122,11 +121,11 @@ function writeUint32BE(destination: Uint8Array, value: number, offset: number):
destination[offset] = value; destination[offset] = value;
} }
function readUint8(source: Uint8Array, offset: number): number { function readUInt8(source: Uint8Array, offset: number): number {
return source[offset]; return source[offset];
} }
function writeUint8(destination: Uint8Array, value: number, offset: number): void { function writeUInt8(destination: Uint8Array, value: number, offset: number): void {
destination[offset] = value; destination[offset] = value;
} }
@@ -139,6 +138,47 @@ export interface VSBufferReadable {
read(): VSBuffer | null; read(): VSBuffer | null;
} }
/**
* A buffer readable stream emits data to listeners. The stream
* will only start emitting when the first data listener has
* been added or the resume() method has been called.
*/
export interface VSBufferReadableStream {
/**
* The 'data' event is emitted whenever the stream is
* relinquishing ownership of a chunk of data to a consumer.
*/
on(event: 'data', callback: (chunk: VSBuffer) => void): void;
/**
* Emitted when any error occurs.
*/
on(event: 'error', callback: (err: any) => void): void;
/**
* The 'end' event is emitted when there is no more data
* to be consumed from the stream. The 'end' event will
* not be emitted unless the data is completely consumed.
*/
on(event: 'end', callback: () => void): void;
/**
* Stops emitting any events until resume() is called.
*/
pause(): void;
/**
* Starts emitting events again after pause() was called.
*/
resume(): void;
/**
* Destroys the stream and stops emitting any event.
*/
destroy(): void;
}
/** /**
* Helper to fully read a VSBuffer readable into a single buffer. * Helper to fully read a VSBuffer readable into a single buffer.
*/ */
@@ -158,6 +198,7 @@ export function readableToBuffer(readable: VSBufferReadable): VSBuffer {
*/ */
export function bufferToReadable(buffer: VSBuffer): VSBufferReadable { export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {
let done = false; let done = false;
return { return {
read: () => { read: () => {
if (done) { if (done) {
@@ -170,3 +211,230 @@ export function bufferToReadable(buffer: VSBuffer): VSBufferReadable {
} }
}; };
} }
/**
* Helper to fully read a VSBuffer stream into a single buffer.
*/
export function streamToBuffer(stream: VSBufferReadableStream): Promise<VSBuffer> {
return new Promise((resolve, reject) => {
const chunks: VSBuffer[] = [];
stream.on('data', chunk => chunks.push(chunk));
stream.on('error', error => reject(error));
stream.on('end', () => resolve(VSBuffer.concat(chunks)));
});
}
/**
* Helper to create a VSBufferStream from an existing VSBuffer.
*/
export function bufferToStream(buffer: VSBuffer): VSBufferReadableStream {
const stream = writeableBufferStream();
stream.end(buffer);
return stream;
}
/**
* Helper to create a VSBufferStream that can be pushed
* buffers to. Will only start to emit data when a listener
* is added.
*/
export function writeableBufferStream(): VSBufferWriteableStream {
return new VSBufferWriteableStreamImpl();
}
export interface VSBufferWriteableStream extends VSBufferReadableStream {
write(chunk: VSBuffer): void;
error(error: Error): void;
end(result?: VSBuffer | Error): void;
}
class VSBufferWriteableStreamImpl implements VSBufferWriteableStream {
private readonly state = {
flowing: false,
ended: false,
destroyed: false
};
private readonly buffer = {
data: [] as VSBuffer[],
error: [] as Error[]
};
private readonly listeners = {
data: [] as { (chunk: VSBuffer): void }[],
error: [] as { (error: Error): void }[],
end: [] as { (): void }[]
};
pause(): void {
if (this.state.destroyed) {
return;
}
this.state.flowing = false;
}
resume(): void {
if (this.state.destroyed) {
return;
}
if (!this.state.flowing) {
this.state.flowing = true;
// emit buffered events
this.flowData();
this.flowErrors();
this.flowEnd();
}
}
write(chunk: VSBuffer): void {
if (this.state.destroyed) {
return;
}
// flowing: directly send the data to listeners
if (this.state.flowing) {
this.listeners.data.forEach(listener => listener(chunk));
}
// not yet flowing: buffer data until flowing
else {
this.buffer.data.push(chunk);
}
}
error(error: Error): void {
if (this.state.destroyed) {
return;
}
// flowing: directly send the error to listeners
if (this.state.flowing) {
this.listeners.error.forEach(listener => listener(error));
}
// not yet flowing: buffer errors until flowing
else {
this.buffer.error.push(error);
}
}
end(result?: VSBuffer | Error): void {
if (this.state.destroyed) {
return;
}
// end with data or error if provided
if (result instanceof Error) {
this.error(result);
} else if (result) {
this.write(result);
}
// flowing: send end event to listeners
if (this.state.flowing) {
this.listeners.end.forEach(listener => listener());
this.destroy();
}
// not yet flowing: remember state
else {
this.state.ended = true;
}
}
on(event: 'data', callback: (chunk: VSBuffer) => void): void;
on(event: 'error', callback: (err: any) => void): void;
on(event: 'end', callback: () => void): void;
on(event: 'data' | 'error' | 'end', callback: (arg0?: any) => void): void {
if (this.state.destroyed) {
return;
}
switch (event) {
case 'data':
this.listeners.data.push(callback);
// switch into flowing mode as soon as the first 'data'
// listener is added and we are not yet in flowing mode
this.resume();
break;
case 'end':
this.listeners.end.push(callback);
// emit 'end' event directly if we are flowing
// and the end has already been reached
//
// finish() when it went through
if (this.state.flowing && this.flowEnd()) {
this.destroy();
}
break;
case 'error':
this.listeners.error.push(callback);
// emit buffered 'error' events unless done already
// now that we know that we have at least one listener
if (this.state.flowing) {
this.flowErrors();
}
break;
}
}
private flowData(): void {
if (this.buffer.data.length > 0) {
const fullDataBuffer = VSBuffer.concat(this.buffer.data);
this.listeners.data.forEach(listener => listener(fullDataBuffer));
this.buffer.data.length = 0;
}
}
private flowErrors(): void {
if (this.listeners.error.length > 0) {
for (const error of this.buffer.error) {
this.listeners.error.forEach(listener => listener(error));
}
this.buffer.error.length = 0;
}
}
private flowEnd(): boolean {
if (this.state.ended) {
this.listeners.end.forEach(listener => listener());
return this.listeners.end.length > 0;
}
return false;
}
destroy(): void {
if (!this.state.destroyed) {
this.state.destroyed = true;
this.state.ended = true;
this.buffer.data.length = 0;
this.buffer.error.length = 0;
this.listeners.data.length = 0;
this.listeners.error.length = 0;
this.listeners.end.length = 0;
}
}
}

View File

@@ -3,11 +3,11 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as stream from 'vs/base/node/stream';
import * as iconv from 'iconv-lite'; import * as iconv from 'iconv-lite';
import { isLinux, isMacintosh } from 'vs/base/common/platform'; import { isLinux, isMacintosh } from 'vs/base/common/platform';
import { exec } from 'child_process'; import { exec } from 'child_process';
import { Readable, Writable } from 'stream'; import { Readable, Writable } from 'stream';
import { VSBuffer } from 'vs/base/common/buffer';
export const UTF8 = 'utf8'; export const UTF8 = 'utf8';
export const UTF8_with_bom = 'utf8bom'; export const UTF8_with_bom = 'utf8bom';
@@ -24,7 +24,12 @@ export interface IDecodeStreamOptions {
overwriteEncoding?(detectedEncoding: string | null): string; overwriteEncoding?(detectedEncoding: string | null): string;
} }
export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions): Promise<{ detected: IDetectedEncodingResult, stream: NodeJS.ReadableStream }> { export interface IDecodeStreamResult {
detected: IDetectedEncodingResult;
stream: NodeJS.ReadableStream;
}
export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions): Promise<IDecodeStreamResult> {
if (!options.minBytesRequiredForDetection) { if (!options.minBytesRequiredForDetection) {
options.minBytesRequiredForDetection = options.guessEncoding ? AUTO_GUESS_BUFFER_MAX_LEN : NO_GUESS_BUFFER_MAX_LEN; options.minBytesRequiredForDetection = options.guessEncoding ? AUTO_GUESS_BUFFER_MAX_LEN : NO_GUESS_BUFFER_MAX_LEN;
} }
@@ -33,90 +38,88 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions
options.overwriteEncoding = detected => detected || UTF8; options.overwriteEncoding = detected => detected || UTF8;
} }
return new Promise<{ detected: IDetectedEncodingResult, stream: NodeJS.ReadableStream }>((resolve, reject) => { return new Promise<IDecodeStreamResult>((resolve, reject) => {
const writer = new class extends Writable {
readable.on('error', reject); private decodeStream: NodeJS.ReadWriteStream;
private decodeStreamConstruction: Promise<void>;
readable.pipe(new class extends Writable { private buffer: Buffer[] = [];
private bytesBuffered = 0;
private _decodeStream: NodeJS.ReadWriteStream;
private _decodeStreamConstruction: Promise<void>;
private _buffer: Buffer[] = [];
private _bytesBuffered = 0;
_write(chunk: any, encoding: string, callback: Function): void { _write(chunk: any, encoding: string, callback: Function): void {
if (!Buffer.isBuffer(chunk)) { if (!Buffer.isBuffer(chunk)) {
callback(new Error('data must be a buffer')); callback(new Error('data must be a buffer'));
} }
if (this._decodeStream) { if (this.decodeStream) {
// just a forwarder now this.decodeStream.write(chunk, callback); // just a forwarder now
this._decodeStream.write(chunk, callback);
return; return;
} }
this._buffer.push(chunk); this.buffer.push(chunk);
this._bytesBuffered += chunk.length; this.bytesBuffered += chunk.length;
if (this._decodeStreamConstruction) { // waiting for the decoder to be ready
// waiting for the decoder to be ready if (this.decodeStreamConstruction) {
this._decodeStreamConstruction.then(_ => callback(), err => callback(err)); this.decodeStreamConstruction.then(() => callback(), err => callback(err));
}
} else if (typeof options.minBytesRequiredForDetection === 'number' && this._bytesBuffered >= options.minBytesRequiredForDetection) { // buffered enough data, create stream and forward data
// buffered enough data, create stream and forward data else if (typeof options.minBytesRequiredForDetection === 'number' && this.bytesBuffered >= options.minBytesRequiredForDetection) {
this._startDecodeStream(callback); this._startDecodeStream(callback);
}
} else { // only buffering
// only buffering else {
callback(); callback();
} }
} }
_startDecodeStream(callback: Function): void { _startDecodeStream(callback: Function): void {
this.decodeStreamConstruction = Promise.resolve(detectEncodingFromBuffer({
this._decodeStreamConstruction = Promise.resolve(detectEncodingFromBuffer({ buffer: Buffer.concat(this.buffer),
buffer: Buffer.concat(this._buffer), bytesRead: this._bytesBuffered bytesRead: this.bytesBuffered
}, options.guessEncoding)).then(detected => { }, options.guessEncoding)).then(detected => {
if (options.overwriteEncoding) { if (options.overwriteEncoding) {
detected.encoding = options.overwriteEncoding(detected.encoding); detected.encoding = options.overwriteEncoding(detected.encoding);
} }
this._decodeStream = decodeStream(detected.encoding);
for (const buffer of this._buffer) {
this._decodeStream.write(buffer);
}
callback();
resolve({ detected, stream: this._decodeStream });
this.decodeStream = decodeStream(detected.encoding);
for (const buffer of this.buffer) {
this.decodeStream.write(buffer);
}
callback();
resolve({ detected, stream: this.decodeStream });
}, err => { }, err => {
this.emit('error', err); this.emit('error', err);
callback(err); callback(err);
}); });
} }
_final(callback: (err?: any) => any) { _final(callback: (err?: any) => any) {
if (this._decodeStream) {
// normal finish // normal finish
this._decodeStream.end(callback); if (this.decodeStream) {
} else { this.decodeStream.end(callback);
// we were still waiting for data... }
this._startDecodeStream(() => this._decodeStream.end(callback));
// we were still waiting for data...
else {
this._startDecodeStream(() => this.decodeStream.end(callback));
} }
} }
}); };
// errors
readable.on('error', reject);
// pipe through
readable.pipe(writer);
}); });
} }
export function bomLength(encoding: string): number {
switch (encoding) {
case UTF8:
return 3;
case UTF16be:
case UTF16le:
return 2;
}
return 0;
}
export function decode(buffer: Buffer, encoding: string): string { export function decode(buffer: Buffer, encoding: string): string {
return iconv.decode(buffer, toNodeEncoding(encoding)); return iconv.decode(buffer, toNodeEncoding(encoding));
} }
@@ -129,7 +132,7 @@ export function encodingExists(encoding: string): boolean {
return iconv.encodingExists(toNodeEncoding(encoding)); return iconv.encodingExists(toNodeEncoding(encoding));
} }
export function decodeStream(encoding: string | null): NodeJS.ReadWriteStream { function decodeStream(encoding: string | null): NodeJS.ReadWriteStream {
return iconv.decodeStream(toNodeEncoding(encoding)); return iconv.decodeStream(toNodeEncoding(encoding));
} }
@@ -145,7 +148,7 @@ function toNodeEncoding(enc: string | null): string {
return enc; return enc;
} }
export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead: number): string | null { export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, bytesRead: number): string | null {
if (!buffer || bytesRead < 2) { if (!buffer || bytesRead < 2) {
return null; return null;
} }
@@ -177,39 +180,31 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer | null, bytesRead:
return null; return null;
} }
/**
* Detects the Byte Order Mark in a given file.
* If no BOM is detected, null will be passed to callback.
*/
export function detectEncodingByBOM(file: string): Promise<string | null> {
return stream.readExactlyByFile(file, 3).then(({ buffer, bytesRead }) => detectEncodingByBOMFromBuffer(buffer, bytesRead), error => null);
}
const MINIMUM_THRESHOLD = 0.2; const MINIMUM_THRESHOLD = 0.2;
const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32']; const IGNORE_ENCODINGS = ['ascii', 'utf-8', 'utf-16', 'utf-32'];
/** /**
* Guesses the encoding from buffer. * Guesses the encoding from buffer.
*/ */
export function guessEncodingByBuffer(buffer: Buffer): Promise<string | null> { async function guessEncodingByBuffer(buffer: Buffer): Promise<string | null> {
return import('jschardet').then(jschardet => { const jschardet = await import('jschardet');
jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD;
const guessed = jschardet.detect(buffer); jschardet.Constants.MINIMUM_THRESHOLD = MINIMUM_THRESHOLD;
if (!guessed || !guessed.encoding) {
return null;
}
const enc = guessed.encoding.toLowerCase(); const guessed = jschardet.detect(buffer);
if (!guessed || !guessed.encoding) {
return null;
}
// Ignore encodings that cannot guess correctly const enc = guessed.encoding.toLowerCase();
// (http://chardet.readthedocs.io/en/latest/supported-encodings.html)
if (0 <= IGNORE_ENCODINGS.indexOf(enc)) {
return null;
}
return toIconvLiteEncoding(guessed.encoding); // Ignore encodings that cannot guess correctly
}); // (http://chardet.readthedocs.io/en/latest/supported-encodings.html)
if (0 <= IGNORE_ENCODINGS.indexOf(enc)) {
return null;
}
return toIconvLiteEncoding(guessed.encoding);
} }
const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = {
@@ -270,9 +265,14 @@ export interface IDetectedEncodingResult {
seemsBinary: boolean; seemsBinary: boolean;
} }
export function detectEncodingFromBuffer(readResult: stream.ReadResult, autoGuessEncoding?: false): IDetectedEncodingResult; export interface IReadResult {
export function detectEncodingFromBuffer(readResult: stream.ReadResult, autoGuessEncoding?: boolean): Promise<IDetectedEncodingResult>; buffer: Buffer | null;
export function detectEncodingFromBuffer({ buffer, bytesRead }: stream.ReadResult, autoGuessEncoding?: boolean): Promise<IDetectedEncodingResult> | IDetectedEncodingResult { bytesRead: number;
}
export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: false): IDetectedEncodingResult;
export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: boolean): Promise<IDetectedEncodingResult>;
export function detectEncodingFromBuffer({ buffer, bytesRead }: IReadResult, autoGuessEncoding?: boolean): Promise<IDetectedEncodingResult> | IDetectedEncodingResult {
// Always first check for BOM to find out about encoding // Always first check for BOM to find out about encoding
let encoding = detectEncodingByBOMFromBuffer(buffer, bytesRead); let encoding = detectEncodingByBOMFromBuffer(buffer, bytesRead);
@@ -357,7 +357,7 @@ const windowsTerminalEncodings = {
'1252': 'cp1252' // West European Latin '1252': 'cp1252' // West European Latin
}; };
export function resolveTerminalEncoding(verbose?: boolean): Promise<string> { export async function resolveTerminalEncoding(verbose?: boolean): Promise<string> {
let rawEncodingPromise: Promise<string>; let rawEncodingPromise: Promise<string>;
// Support a global environment variable to win over other mechanics // Support a global environment variable to win over other mechanics
@@ -403,24 +403,23 @@ export function resolveTerminalEncoding(verbose?: boolean): Promise<string> {
}); });
} }
return rawEncodingPromise.then(rawEncoding => { const rawEncoding = await rawEncodingPromise;
if (verbose) { if (verbose) {
console.log(`Detected raw terminal encoding: ${rawEncoding}`); console.log(`Detected raw terminal encoding: ${rawEncoding}`);
} }
if (!rawEncoding || rawEncoding.toLowerCase() === 'utf-8' || rawEncoding.toLowerCase() === UTF8) {
return UTF8;
}
const iconvEncoding = toIconvLiteEncoding(rawEncoding);
if (iconv.encodingExists(iconvEncoding)) {
return iconvEncoding;
}
if (verbose) {
console.log('Unsupported terminal encoding, falling back to UTF-8.');
}
if (!rawEncoding || rawEncoding.toLowerCase() === 'utf-8' || rawEncoding.toLowerCase() === UTF8) {
return UTF8; return UTF8;
}); }
const iconvEncoding = toIconvLiteEncoding(rawEncoding);
if (iconv.encodingExists(iconvEncoding)) {
return iconvEncoding;
}
if (verbose) {
console.log('Unsupported terminal encoding, falling back to UTF-8.');
}
return UTF8;
} }

View File

@@ -5,63 +5,6 @@
import * as fs from 'fs'; import * as fs from 'fs';
export interface ReadResult {
buffer: Buffer | null;
bytesRead: number;
}
/**
* Reads totalBytes from the provided file.
*/
export function readExactlyByFile(file: string, totalBytes: number): Promise<ReadResult> {
return new Promise<ReadResult>((resolve, reject) => {
fs.open(file, 'r', null, (err, fd) => {
if (err) {
return reject(err);
}
function end(err: Error | null, resultBuffer: Buffer | null, bytesRead: number): void {
fs.close(fd, closeError => {
if (closeError) {
return reject(closeError);
}
if (err && (<any>err).code === 'EISDIR') {
return reject(err); // we want to bubble this error up (file is actually a folder)
}
return resolve({ buffer: resultBuffer, bytesRead });
});
}
const buffer = Buffer.allocUnsafe(totalBytes);
let offset = 0;
function readChunk(): void {
fs.read(fd, buffer, offset, totalBytes - offset, null, (err, bytesRead) => {
if (err) {
return end(err, null, 0);
}
if (bytesRead === 0) {
return end(null, buffer, offset);
}
offset += bytesRead;
if (offset === totalBytes) {
return end(null, buffer, offset);
}
return readChunk();
});
}
readChunk();
});
});
}
/** /**
* Reads a file until a matching string is found. * Reads a file until a matching string is found.
* *

View File

@@ -216,10 +216,10 @@ class ProtocolReader extends Disposable {
// save new state => next time will read the body // save new state => next time will read the body
this._state.readHead = false; this._state.readHead = false;
this._state.readLen = buff.readUint32BE(9); this._state.readLen = buff.readUInt32BE(9);
this._state.messageType = <ProtocolMessageType>buff.readUint8(0); this._state.messageType = <ProtocolMessageType>buff.readUInt8(0);
this._state.id = buff.readUint32BE(1); this._state.id = buff.readUInt32BE(1);
this._state.ack = buff.readUint32BE(5); this._state.ack = buff.readUInt32BE(5);
} else { } else {
// buff is the body // buff is the body
const messageType = this._state.messageType; const messageType = this._state.messageType;
@@ -288,10 +288,10 @@ class ProtocolWriter {
msg.writtenTime = Date.now(); msg.writtenTime = Date.now();
this.lastWriteTime = Date.now(); this.lastWriteTime = Date.now();
const header = VSBuffer.alloc(ProtocolConstants.HeaderLength); const header = VSBuffer.alloc(ProtocolConstants.HeaderLength);
header.writeUint8(msg.type, 0); header.writeUInt8(msg.type, 0);
header.writeUint32BE(msg.id, 1); header.writeUInt32BE(msg.id, 1);
header.writeUint32BE(msg.ack, 5); header.writeUInt32BE(msg.ack, 5);
header.writeUint32BE(msg.data.byteLength, 9); header.writeUInt32BE(msg.data.byteLength, 9);
this._writeSoon(header, msg.data); this._writeSoon(header, msg.data);
} }

View File

@@ -166,17 +166,17 @@ enum DataType {
function createSizeBuffer(size: number): VSBuffer { function createSizeBuffer(size: number): VSBuffer {
const result = VSBuffer.alloc(4); const result = VSBuffer.alloc(4);
result.writeUint32BE(size, 0); result.writeUInt32BE(size, 0);
return result; return result;
} }
function readSizeBuffer(reader: IReader): number { function readSizeBuffer(reader: IReader): number {
return reader.read(4).readUint32BE(0); return reader.read(4).readUInt32BE(0);
} }
function createOneByteBuffer(value: number): VSBuffer { function createOneByteBuffer(value: number): VSBuffer {
const result = VSBuffer.alloc(1); const result = VSBuffer.alloc(1);
result.writeUint8(value, 0); result.writeUInt8(value, 0);
return result; return result;
} }
@@ -225,7 +225,7 @@ function serialize(writer: IWriter, data: any): void {
} }
function deserialize(reader: IReader): any { function deserialize(reader: IReader): any {
const type = reader.read(1).readUint8(0); const type = reader.read(1).readUInt8(0);
switch (type) { switch (type) {
case DataType.Undefined: return undefined; case DataType.Undefined: return undefined;

View File

@@ -136,10 +136,10 @@ suite('IPC, Socket Protocol', () => {
assert.equal(msg1.toString(), 'foobarfarboo'); assert.equal(msg1.toString(), 'foobarfarboo');
const buffer = VSBuffer.alloc(1); const buffer = VSBuffer.alloc(1);
buffer.writeUint8(123, 0); buffer.writeUInt8(123, 0);
a.send(buffer); a.send(buffer);
const msg2 = await bMessages.waitForOne(); const msg2 = await bMessages.waitForOne();
assert.equal(msg2.readUint8(0), 123); assert.equal(msg2.readUInt8(0), 123);
}); });

View File

@@ -3,18 +3,365 @@
* Licensed under the MIT License. See License.txt in the project root for license information. * Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as assert from 'assert'; import * as assert from 'assert';
import { hasBuffer, VSBuffer } from 'vs/base/common/buffer'; import { VSBuffer, bufferToReadable, readableToBuffer, bufferToStream, streamToBuffer, writeableBufferStream } from 'vs/base/common/buffer';
import { timeout } from 'vs/base/common/async';
suite('Buffer', () => { suite('Buffer', () => {
if (hasBuffer) { test('issue #71993 - VSBuffer#toString returns numbers', () => {
test('issue #71993 - VSBuffer#toString returns numbers', () => { const data = new Uint8Array([1, 2, 3, 'h'.charCodeAt(0), 'i'.charCodeAt(0), 4, 5]).buffer;
const data = new Uint8Array([1, 2, 3, 'h'.charCodeAt(0), 'i'.charCodeAt(0), 4, 5]).buffer; const buffer = VSBuffer.wrap(new Uint8Array(data, 3, 2));
const buffer = VSBuffer.wrap(new Uint8Array(data, 3, 2)); assert.deepEqual(buffer.toString(), 'hi');
assert.deepEqual(buffer.toString(), 'hi'); });
});
}
test('bufferToReadable / readableToBuffer', () => {
const content = 'Hello World';
const readable = bufferToReadable(VSBuffer.fromString(content));
assert.equal(readableToBuffer(readable).toString(), content);
});
test('bufferToStream / streamToBuffer', async () => {
const content = 'Hello World';
const stream = bufferToStream(VSBuffer.fromString(content));
assert.equal((await streamToBuffer(stream)).toString(), content);
});
test('bufferWriteableStream - basics (no error)', async () => {
const stream = writeableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(chunks.length, 2);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(chunks[1].toString(), 'World');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - basics (error)', async () => {
const stream = writeableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(new Error());
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(ended, true);
assert.equal(errors.length, 1);
});
test('bufferWriteableStream - buffers data when no listener', async () => {
const stream = writeableBufferStream();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'HelloWorld');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - buffers errors when no listener', async () => {
const stream = writeableBufferStream();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.error(new Error());
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
stream.end();
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(ended, true);
assert.equal(errors.length, 1);
});
test('bufferWriteableStream - buffers end when no listener', async () => {
const stream = writeableBufferStream();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
let ended = false;
stream.on('end', () => {
ended = true;
});
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'HelloWorld');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - nothing happens after end()', async () => {
const stream = writeableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
let dataCalledAfterEnd = false;
stream.on('data', data => {
dataCalledAfterEnd = true;
});
let errorCalledAfterEnd = false;
stream.on('error', error => {
errorCalledAfterEnd = true;
});
let endCalledAfterEnd = false;
stream.on('end', () => {
endCalledAfterEnd = true;
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.error(new Error());
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(dataCalledAfterEnd, false);
assert.equal(errorCalledAfterEnd, false);
assert.equal(endCalledAfterEnd, false);
assert.equal(chunks.length, 2);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(chunks[1].toString(), 'World');
});
test('bufferWriteableStream - pause/resume (simple)', async () => {
const stream = writeableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
stream.pause();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(chunks.length, 0);
assert.equal(errors.length, 0);
assert.equal(ended, false);
stream.resume();
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'HelloWorld');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - pause/resume (pause after first write)', async () => {
const stream = writeableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
stream.pause();
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(errors.length, 0);
assert.equal(ended, false);
stream.resume();
assert.equal(chunks.length, 2);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(chunks[1].toString(), 'World');
assert.equal(ended, true);
assert.equal(errors.length, 0);
});
test('bufferWriteableStream - pause/resume (error)', async () => {
const stream = writeableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
stream.pause();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(new Error());
assert.equal(chunks.length, 0);
assert.equal(ended, false);
assert.equal(errors.length, 0);
stream.resume();
assert.equal(chunks.length, 1);
assert.equal(chunks[0].toString(), 'Hello');
assert.equal(ended, true);
assert.equal(errors.length, 1);
});
test('bufferWriteableStream - destroy', async () => {
const stream = writeableBufferStream();
let chunks: VSBuffer[] = [];
stream.on('data', data => {
chunks.push(data);
});
let ended = false;
stream.on('end', () => {
ended = true;
});
let errors: Error[] = [];
stream.on('error', error => {
errors.push(error);
});
stream.destroy();
await timeout(0);
stream.write(VSBuffer.fromString('Hello'));
await timeout(0);
stream.end(VSBuffer.fromString('World'));
assert.equal(chunks.length, 0);
assert.equal(ended, false);
assert.equal(errors.length, 0);
});
}); });

View File

@@ -6,51 +6,114 @@
import * as assert from 'assert'; import * as assert from 'assert';
import * as fs from 'fs'; import * as fs from 'fs';
import * as encoding from 'vs/base/node/encoding'; import * as encoding from 'vs/base/node/encoding';
import { readExactlyByFile } from 'vs/base/node/stream';
import { Readable } from 'stream'; import { Readable } from 'stream';
import { getPathFromAmdModule } from 'vs/base/common/amd'; import { getPathFromAmdModule } from 'vs/base/common/amd';
export async function detectEncodingByBOM(file: string): Promise<string | null> {
try {
const { buffer, bytesRead } = await readExactlyByFile(file, 3);
return encoding.detectEncodingByBOMFromBuffer(buffer, bytesRead);
} catch (error) {
return null; // ignore errors (like file not found)
}
}
interface ReadResult {
buffer: Buffer | null;
bytesRead: number;
}
function readExactlyByFile(file: string, totalBytes: number): Promise<ReadResult> {
return new Promise<ReadResult>((resolve, reject) => {
fs.open(file, 'r', null, (err, fd) => {
if (err) {
return reject(err);
}
function end(err: Error | null, resultBuffer: Buffer | null, bytesRead: number): void {
fs.close(fd, closeError => {
if (closeError) {
return reject(closeError);
}
if (err && (<any>err).code === 'EISDIR') {
return reject(err); // we want to bubble this error up (file is actually a folder)
}
return resolve({ buffer: resultBuffer, bytesRead });
});
}
const buffer = Buffer.allocUnsafe(totalBytes);
let offset = 0;
function readChunk(): void {
fs.read(fd, buffer, offset, totalBytes - offset, null, (err, bytesRead) => {
if (err) {
return end(err, null, 0);
}
if (bytesRead === 0) {
return end(null, buffer, offset);
}
offset += bytesRead;
if (offset === totalBytes) {
return end(null, buffer, offset);
}
return readChunk();
});
}
readChunk();
});
});
}
suite('Encoding', () => { suite('Encoding', () => {
test('detectBOM does not return error for non existing file', async () => { test('detectBOM does not return error for non existing file', async () => {
const file = getPathFromAmdModule(require, './fixtures/not-exist.css'); const file = getPathFromAmdModule(require, './fixtures/not-exist.css');
const detectedEncoding = await encoding.detectEncodingByBOM(file); const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, null); assert.equal(detectedEncoding, null);
}); });
test('detectBOM UTF-8', async () => { test('detectBOM UTF-8', async () => {
const file = getPathFromAmdModule(require, './fixtures/some_utf8.css'); const file = getPathFromAmdModule(require, './fixtures/some_utf8.css');
const detectedEncoding = await encoding.detectEncodingByBOM(file); const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, 'utf8'); assert.equal(detectedEncoding, 'utf8');
}); });
test('detectBOM UTF-16 LE', async () => { test('detectBOM UTF-16 LE', async () => {
const file = getPathFromAmdModule(require, './fixtures/some_utf16le.css'); const file = getPathFromAmdModule(require, './fixtures/some_utf16le.css');
const detectedEncoding = await encoding.detectEncodingByBOM(file); const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, 'utf16le'); assert.equal(detectedEncoding, 'utf16le');
}); });
test('detectBOM UTF-16 BE', async () => { test('detectBOM UTF-16 BE', async () => {
const file = getPathFromAmdModule(require, './fixtures/some_utf16be.css'); const file = getPathFromAmdModule(require, './fixtures/some_utf16be.css');
const detectedEncoding = await encoding.detectEncodingByBOM(file); const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, 'utf16be'); assert.equal(detectedEncoding, 'utf16be');
}); });
test('detectBOM ANSI', async function () { test('detectBOM ANSI', async function () {
const file = getPathFromAmdModule(require, './fixtures/some_ansi.css'); const file = getPathFromAmdModule(require, './fixtures/some_ansi.css');
const detectedEncoding = await encoding.detectEncodingByBOM(file); const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, null); assert.equal(detectedEncoding, null);
}); });
test('detectBOM ANSI', async function () { test('detectBOM ANSI', async function () {
const file = getPathFromAmdModule(require, './fixtures/empty.txt'); const file = getPathFromAmdModule(require, './fixtures/empty.txt');
const detectedEncoding = await encoding.detectEncodingByBOM(file); const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, null); assert.equal(detectedEncoding, null);
}); });

View File

@@ -9,23 +9,6 @@ import * as stream from 'vs/base/node/stream';
import { getPathFromAmdModule } from 'vs/base/common/amd'; import { getPathFromAmdModule } from 'vs/base/common/amd';
suite('Stream', () => { suite('Stream', () => {
test('readExactlyByFile - ANSI', function () {
const file = getPathFromAmdModule(require, './fixtures/file.css');
return stream.readExactlyByFile(file, 10).then(({ buffer, bytesRead }) => {
assert.equal(bytesRead, 10);
assert.equal(buffer!.toString(), '/*--------');
});
});
test('readExactlyByFile - empty', function () {
const file = getPathFromAmdModule(require, './fixtures/empty.txt');
return stream.readExactlyByFile(file, 10).then(({ bytesRead }) => {
assert.equal(bytesRead, 0);
});
});
test('readToMatchingString - ANSI', function () { test('readToMatchingString - ANSI', function () {
const file = getPathFromAmdModule(require, './fixtures/file.css'); const file = getPathFromAmdModule(require, './fixtures/file.css');

View File

@@ -1580,8 +1580,8 @@ export class WindowsManager implements IWindowsMainService {
if (cli && (cli.remote !== remote)) { if (cli && (cli.remote !== remote)) {
cli = { ...cli, remote }; cli = { ...cli, remote };
} }
const forceNewWindow = !(options && options.reuseWindow); const forceReuseWindow = options && options.reuseWindow;
return this.open({ context, cli, forceNewWindow, forceEmpty: true }); return this.open({ context, cli, forceEmpty: true, forceReuseWindow });
} }
openNewTabbedWindow(context: OpenContext): ICodeWindow[] { openNewTabbedWindow(context: OpenContext): ICodeWindow[] {

View File

@@ -18,13 +18,15 @@ const testWorkspace: IWorkspaceIdentifier = {
configPath: URI.file(path.join(fixturesFolder, 'workspaces.json')) configPath: URI.file(path.join(fixturesFolder, 'workspaces.json'))
}; };
const testWorkspaceFolders = toWorkspaceFolders([{ path: path.join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: path.join(fixturesFolder, 'vscode_workspace_2_folder') }], testWorkspace.configPath);
function options(custom?: Partial<IBestWindowOrFolderOptions<ISimpleWindow>>): IBestWindowOrFolderOptions<ISimpleWindow> { function options(custom?: Partial<IBestWindowOrFolderOptions<ISimpleWindow>>): IBestWindowOrFolderOptions<ISimpleWindow> {
return { return {
windows: [], windows: [],
newWindow: false, newWindow: false,
context: OpenContext.CLI, context: OpenContext.CLI,
codeSettingsFolder: '_vscode', codeSettingsFolder: '_vscode',
localWorkspaceResolver: workspace => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: toWorkspaceFolders([{ path: path.join(fixturesFolder, 'vscode_workspace_1_folder') }, { path: path.join(fixturesFolder, 'vscode_workspace_2_folder') }]) } : null!; }, localWorkspaceResolver: workspace => { return workspace === testWorkspace ? { id: testWorkspace.id, configPath: workspace.configPath, folders: testWorkspaceFolders } : null; },
...custom ...custom
}; };
} }

View File

@@ -13,7 +13,6 @@ import { Selection } from 'vs/editor/common/core/selection';
import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents'; import { IModelContentChange, IModelContentChangedEvent, IModelDecorationsChangedEvent, IModelLanguageChangedEvent, IModelLanguageConfigurationChangedEvent, IModelOptionsChangedEvent, IModelTokensChangedEvent, ModelRawContentChangedEvent } from 'vs/editor/common/model/textModelEvents';
import { SearchData } from 'vs/editor/common/model/textModelSearch'; import { SearchData } from 'vs/editor/common/model/textModelSearch';
import { LanguageId, LanguageIdentifier, FormattingOptions } from 'vs/editor/common/modes'; import { LanguageId, LanguageIdentifier, FormattingOptions } from 'vs/editor/common/modes';
import { ITextSnapshot } from 'vs/platform/files/common/files';
import { ThemeColor } from 'vs/platform/theme/common/themeService'; import { ThemeColor } from 'vs/platform/theme/common/themeService';
/** /**
@@ -460,6 +459,17 @@ export interface IActiveIndentGuideInfo {
indent: number; indent: number;
} }
/**
* Text snapshot that works like an iterator.
* Will try to return chunks of roughly ~64KB size.
* Will return null when finished.
*
* @internal
*/
export interface ITextSnapshot {
read(): string | null;
}
/** /**
* A model. * A model.
*/ */

View File

@@ -6,10 +6,9 @@
import { CharCode } from 'vs/base/common/charCode'; import { CharCode } from 'vs/base/common/charCode';
import { Position } from 'vs/editor/common/core/position'; import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import { FindMatch } from 'vs/editor/common/model'; import { FindMatch, ITextSnapshot } from 'vs/editor/common/model';
import { NodeColor, SENTINEL, TreeNode, fixInsert, leftest, rbDelete, righttest, updateTreeMetadata } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; import { NodeColor, SENTINEL, TreeNode, fixInsert, leftest, rbDelete, righttest, updateTreeMetadata } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase';
import { SearchData, Searcher, createFindMatch, isValidMatch } from 'vs/editor/common/model/textModelSearch'; import { SearchData, Searcher, createFindMatch, isValidMatch } from 'vs/editor/common/model/textModelSearch';
import { ITextSnapshot } from 'vs/platform/files/common/files';
// const lfRegex = new RegExp(/\r\n|\r|\n/g); // const lfRegex = new RegExp(/\r\n|\r|\n/g);
export const AverageBufferSize = 65535; export const AverageBufferSize = 65535;

View File

@@ -6,10 +6,9 @@
import * as strings from 'vs/base/common/strings'; import * as strings from 'vs/base/common/strings';
import { Position } from 'vs/editor/common/core/position'; import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import { ApplyEditsResult, EndOfLinePreference, FindMatch, IIdentifiedSingleEditOperation, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer } from 'vs/editor/common/model'; import { ApplyEditsResult, EndOfLinePreference, FindMatch, IIdentifiedSingleEditOperation, IInternalModelContentChange, ISingleEditOperationIdentifier, ITextBuffer, ITextSnapshot } from 'vs/editor/common/model';
import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; import { PieceTreeBase, StringBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase';
import { SearchData } from 'vs/editor/common/model/textModelSearch'; import { SearchData } from 'vs/editor/common/model/textModelSearch';
import { ITextSnapshot } from 'vs/platform/files/common/files';
export interface IValidatedEditOperation { export interface IValidatedEditOperation {
sortIndex: number; sortIndex: number;

View File

@@ -30,9 +30,9 @@ import { LanguageConfigurationRegistry } from 'vs/editor/common/modes/languageCo
import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode'; import { NULL_LANGUAGE_IDENTIFIER } from 'vs/editor/common/modes/nullMode';
import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports'; import { ignoreBracketsInToken } from 'vs/editor/common/modes/supports';
import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets'; import { BracketsUtils, RichEditBracket, RichEditBrackets } from 'vs/editor/common/modes/supports/richEditBrackets';
import { IStringStream, ITextSnapshot } from 'vs/platform/files/common/files';
import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService'; import { ITheme, ThemeColor } from 'vs/platform/theme/common/themeService';
import { withUndefinedAsNull } from 'vs/base/common/types'; import { withUndefinedAsNull } from 'vs/base/common/types';
import { VSBufferReadableStream, VSBuffer } from 'vs/base/common/buffer';
const CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048; const CHEAP_TOKENIZATION_LENGTH_LIMIT = 2048;
@@ -46,36 +46,54 @@ export function createTextBufferFactory(text: string): model.ITextBufferFactory
return builder.finish(); return builder.finish();
} }
export function createTextBufferFactoryFromStream(stream: IStringStream, filter?: (chunk: string) => string): Promise<model.ITextBufferFactory> { interface ITextStream {
return new Promise<model.ITextBufferFactory>((c, e) => { on(event: 'data', callback: (data: string) => void): void;
let done = false; on(event: 'error', callback: (err: Error) => void): void;
let builder = createTextBufferBuilder(); on(event: 'end', callback: () => void): void;
on(event: string, callback: any): void;
}
export function createTextBufferFactoryFromStream(stream: ITextStream, filter?: (chunk: string) => string, validator?: (chunk: string) => Error | undefined): Promise<model.ITextBufferFactory>;
export function createTextBufferFactoryFromStream(stream: VSBufferReadableStream, filter?: (chunk: VSBuffer) => VSBuffer, validator?: (chunk: VSBuffer) => Error | undefined): Promise<model.ITextBufferFactory>;
export function createTextBufferFactoryFromStream(stream: ITextStream | VSBufferReadableStream, filter?: (chunk: any) => string | VSBuffer, validator?: (chunk: any) => Error | undefined): Promise<model.ITextBufferFactory> {
return new Promise<model.ITextBufferFactory>((resolve, reject) => {
const builder = createTextBufferBuilder();
let done = false;
stream.on('data', (chunk: string | VSBuffer) => {
if (validator) {
const error = validator(chunk);
if (error) {
done = true;
reject(error);
}
}
stream.on('data', (chunk) => {
if (filter) { if (filter) {
chunk = filter(chunk); chunk = filter(chunk);
} }
builder.acceptChunk(chunk); builder.acceptChunk((typeof chunk === 'string') ? chunk : chunk.toString());
}); });
stream.on('error', (error) => { stream.on('error', (error) => {
if (!done) { if (!done) {
done = true; done = true;
e(error); reject(error);
} }
}); });
stream.on('end', () => { stream.on('end', () => {
if (!done) { if (!done) {
done = true; done = true;
c(builder.finish()); resolve(builder.finish());
} }
}); });
}); });
} }
export function createTextBufferFactoryFromSnapshot(snapshot: ITextSnapshot): model.ITextBufferFactory { export function createTextBufferFactoryFromSnapshot(snapshot: model.ITextSnapshot): model.ITextBufferFactory {
let builder = createTextBufferBuilder(); let builder = createTextBufferBuilder();
let chunk: string | null; let chunk: string | null;
@@ -111,12 +129,12 @@ function singleLetter(result: number): string {
const LIMIT_FIND_COUNT = 999; const LIMIT_FIND_COUNT = 999;
export const LONG_LINE_BOUNDARY = 10000; export const LONG_LINE_BOUNDARY = 10000;
class TextModelSnapshot implements ITextSnapshot { class TextModelSnapshot implements model.ITextSnapshot {
private readonly _source: ITextSnapshot; private readonly _source: model.ITextSnapshot;
private _eos: boolean; private _eos: boolean;
constructor(source: ITextSnapshot) { constructor(source: model.ITextSnapshot) {
this._source = source; this._source = source;
this._eos = false; this._eos = false;
} }
@@ -731,7 +749,7 @@ export class TextModel extends Disposable implements model.ITextModel {
return fullModelValue; return fullModelValue;
} }
public createSnapshot(preserveBOM: boolean = false): ITextSnapshot { public createSnapshot(preserveBOM: boolean = false): model.ITextSnapshot {
return new TextModelSnapshot(this._buffer.createSnapshot(preserveBOM)); return new TextModelSnapshot(this._buffer.createSnapshot(preserveBOM));
} }

View File

@@ -10,7 +10,7 @@ import { SelectionBasedVariableResolver, CompositeSnippetVariableResolver, Model
import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/snippet/snippetParser'; import { SnippetParser, Variable, VariableResolver } from 'vs/editor/contrib/snippet/snippetParser';
import { TextModel } from 'vs/editor/common/model/textModel'; import { TextModel } from 'vs/editor/common/model/textModel';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { Workspace, toWorkspaceFolders, IWorkspace, IWorkspaceContextService, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
suite('Snippet Variables Resolver', function () { suite('Snippet Variables Resolver', function () {
@@ -328,11 +328,12 @@ suite('Snippet Variables Resolver', function () {
assertVariableResolve(resolver, 'WORKSPACE_NAME', undefined); assertVariableResolve(resolver, 'WORKSPACE_NAME', undefined);
// single folder workspace without config // single folder workspace without config
workspace = new Workspace('', toWorkspaceFolders([{ path: '/folderName' }])); workspace = new Workspace('', [toWorkspaceFolder(URI.file('/folderName'))]);
assertVariableResolve(resolver, 'WORKSPACE_NAME', 'folderName'); assertVariableResolve(resolver, 'WORKSPACE_NAME', 'folderName');
// workspace with config // workspace with config
workspace = new Workspace('', toWorkspaceFolders([{ path: 'folderName' }]), URI.file('testWorkspace.code-workspace')); const workspaceConfigPath = URI.file('testWorkspace.code-workspace');
workspace = new Workspace('', toWorkspaceFolders([{ path: 'folderName' }], workspaceConfigPath), workspaceConfigPath);
assertVariableResolve(resolver, 'WORKSPACE_NAME', 'testWorkspace'); assertVariableResolve(resolver, 'WORKSPACE_NAME', 'testWorkspace');
}); });
}); });

View File

@@ -7,14 +7,13 @@ import * as assert from 'assert';
import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier'; import { WordCharacterClassifier } from 'vs/editor/common/controller/wordCharacterClassifier';
import { Position } from 'vs/editor/common/core/position'; import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range'; import { Range } from 'vs/editor/common/core/range';
import { DefaultEndOfLine } from 'vs/editor/common/model'; import { DefaultEndOfLine, ITextSnapshot } from 'vs/editor/common/model';
import { PieceTreeBase } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase'; import { PieceTreeBase } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeBase';
import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer'; import { PieceTreeTextBuffer } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBuffer';
import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder'; import { PieceTreeTextBufferBuilder } from 'vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';
import { NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase'; import { NodeColor, SENTINEL, TreeNode } from 'vs/editor/common/model/pieceTreeTextBuffer/rbTreeBase';
import { TextModel } from 'vs/editor/common/model/textModel'; import { TextModel } from 'vs/editor/common/model/textModel';
import { SearchData } from 'vs/editor/common/model/textModelSearch'; import { SearchData } from 'vs/editor/common/model/textModelSearch';
import { ITextSnapshot } from 'vs/platform/files/common/files';
const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n'; const alphabet = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ\r\n';

View File

@@ -362,13 +362,6 @@ export class ExtensionManagementService extends Disposable implements IExtension
return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE)); return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install because, the extension '{0}' compatible with current version '{1}' of VS Code is not found.", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE));
} }
if (this.remote) {
const manifest = await this.galleryService.getManifest(extension, CancellationToken.None);
if (manifest && isUIExtension(manifest, [], this.configurationService) && !isLanguagePackExtension(manifest)) {
return Promise.reject(new Error(nls.localize('notSupportedUIExtension', "Can't install extension {0} since UI Extensions are not supported", extension.identifier.id)));
}
}
return compatibleExtension; return compatibleExtension;
} }

View File

@@ -11,13 +11,13 @@ import product from 'vs/platform/product/node/product';
export function isUIExtension(manifest: IExtensionManifest, uiContributions: string[], configurationService: IConfigurationService): boolean { export function isUIExtension(manifest: IExtensionManifest, uiContributions: string[], configurationService: IConfigurationService): boolean {
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name); const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
const configuredUIExtensions = configurationService.getValue<string[]>('_workbench.uiExtensions') || []; const { ui, workspace } = configurationService.getValue<{ ui: string[], workspace: string[] }>('extensions.extensionKind') || { ui: [], workspace: [] };
if (configuredUIExtensions.some(id => areSameExtensions({ id }, { id: extensionId }))) { if (isNonEmptyArray(workspace) && workspace.some(id => areSameExtensions({ id }, { id: extensionId }))) {
return true;
}
if (configuredUIExtensions.some(id => areSameExtensions({ id }, { id: `-${extensionId}` }))) {
return false; return false;
} }
if (isNonEmptyArray(ui) && ui.some(id => areSameExtensions({ id }, { id: extensionId }))) {
return true;
}
switch (manifest.extensionKind) { switch (manifest.extensionKind) {
case 'ui': return true; case 'ui': return true;
case 'workspace': return false; case 'workspace': return false;

View File

@@ -13,25 +13,14 @@ import { startsWithIgnoreCase } from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { isEqualOrParent, isEqual } from 'vs/base/common/resources'; import { isEqualOrParent, isEqual } from 'vs/base/common/resources';
import { isUndefinedOrNull } from 'vs/base/common/types'; import { isUndefinedOrNull } from 'vs/base/common/types';
import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer';
export const IFileService = createDecorator<IFileService>('fileService'); export const IFileService = createDecorator<IFileService>('fileService');
export interface IResourceEncodings {
getWriteEncoding(resource: URI, preferredEncoding?: string): IResourceEncoding;
}
export interface IResourceEncoding {
encoding: string;
hasBOM: boolean;
}
export interface IFileService { export interface IFileService {
_serviceBrand: ServiceIdentifier<any>; _serviceBrand: ServiceIdentifier<any>;
//#region File System Provider
/** /**
* An event that is fired when a file system provider is added or removed * An event that is fired when a file system provider is added or removed
*/ */
@@ -63,13 +52,6 @@ export interface IFileService {
*/ */
hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean; hasCapability(resource: URI, capability: FileSystemProviderCapabilities): boolean;
//#endregion
/**
* Helper to determine read/write encoding for resources.
*/
encoding: IResourceEncodings;
/** /**
* Allows to listen for file changes. The event will fire for every file within the opened workspace * Allows to listen for file changes. The event will fire for every file within the opened workspace
* (if any) as well as all files that have been watched explicitly using the #watch() API. * (if any) as well as all files that have been watched explicitly using the #watch() API.
@@ -111,18 +93,14 @@ export interface IFileService {
exists(resource: URI): Promise<boolean>; exists(resource: URI): Promise<boolean>;
/** /**
* Resolve the contents of a file identified by the resource. * Read the contents of the provided resource unbuffered.
*
* The returned object contains properties of the file and the full value as string.
*/ */
resolveContent(resource: URI, options?: IResolveContentOptions): Promise<IContent>; readFile(resource: URI, options?: IReadFileOptions): Promise<IFileContent>;
/** /**
* Resolve the contents of a file identified by the resource. * Read the contents of the provided resource buffered as stream.
*
* The returned object contains properties of the file and the value as a readable stream.
*/ */
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>; readFileStream(resource: URI, options?: IReadFileOptions): Promise<IFileStreamContent>;
/** /**
* Updates the content replacing its previous value. * Updates the content replacing its previous value.
@@ -228,11 +206,11 @@ export const enum FileSystemProviderCapabilities {
export interface IFileSystemProvider { export interface IFileSystemProvider {
readonly capabilities: FileSystemProviderCapabilities; readonly capabilities: FileSystemProviderCapabilities;
onDidChangeCapabilities: Event<void>; readonly onDidChangeCapabilities: Event<void>;
onDidErrorOccur?: Event<Error>; // TODO@ben remove once file watchers are solid readonly onDidErrorOccur?: Event<Error>; // TODO@ben remove once file watchers are solid
onDidChangeFile: Event<IFileChange[]>; readonly onDidChangeFile: Event<IFileChange[]>;
watch(resource: URI, opts: IWatchOptions): IDisposable; watch(resource: URI, opts: IWatchOptions): IDisposable;
stat(resource: URI): Promise<IStat>; stat(resource: URI): Promise<IStat>;
@@ -337,6 +315,13 @@ export function toFileSystemProviderErrorCode(error: Error): FileSystemProviderE
} }
export function toFileOperationResult(error: Error): FileOperationResult { export function toFileOperationResult(error: Error): FileOperationResult {
// FileSystemProviderError comes with the result already
if (error instanceof FileOperationError) {
return error.fileOperationResult;
}
// Otherwise try to find from code
switch (toFileSystemProviderErrorCode(error)) { switch (toFileSystemProviderErrorCode(error)) {
case FileSystemProviderErrorCode.FileNotFound: case FileSystemProviderErrorCode.FileNotFound:
return FileOperationResult.FILE_NOT_FOUND; return FileOperationResult.FILE_NOT_FOUND;
@@ -576,8 +561,7 @@ export interface IBaseStatWithMetadata extends IBaseStat {
export interface IFileStat extends IBaseStat { export interface IFileStat extends IBaseStat {
/** /**
* The resource is a directory. if {{true}} * The resource is a directory
* {{encoding}} has no meaning.
*/ */
isDirectory: boolean; isDirectory: boolean;
@@ -608,147 +592,23 @@ export interface IResolveFileResultWithMetadata extends IResolveFileResult {
stat?: IFileStatWithMetadata; stat?: IFileStatWithMetadata;
} }
/** export interface IFileContent extends IBaseStatWithMetadata {
* Content and meta information of a file.
*/
export interface IContent extends IBaseStatWithMetadata {
/** /**
* The content of a text file. * The content of a file as buffer.
*/ */
value: string; value: VSBuffer;
}
export interface IFileStreamContent extends IBaseStatWithMetadata {
/** /**
* The encoding of the content if known. * The content of a file as stream.
*/ */
encoding: string; value: VSBufferReadableStream;
} }
// this should eventually replace IContent such export interface IReadFileOptions {
// that we have a clear separation between content
// and metadata (TODO@Joh, TODO@Ben)
export interface IContentData {
encoding: string;
stream: IStringStream;
}
/**
* A Stream emitting strings.
*/
export interface IStringStream {
on(event: 'data', callback: (chunk: string) => void): void;
on(event: 'error', callback: (err: any) => void): void;
on(event: 'end', callback: () => void): void;
on(event: string, callback: any): void;
}
/**
* Text snapshot that works like an iterator.
* Will try to return chunks of roughly ~64KB size.
* Will return null when finished.
*/
export interface ITextSnapshot {
read(): string | null;
}
/**
* Helper method to convert a snapshot into its full string form.
*/
export function snapshotToString(snapshot: ITextSnapshot): string {
const chunks: string[] = [];
let chunk: string | null;
while (typeof (chunk = snapshot.read()) === 'string') {
chunks.push(chunk);
}
return chunks.join('');
}
export function stringToSnapshot(value: string): ITextSnapshot {
let done = false;
return {
read(): string | null {
if (!done) {
done = true;
return value;
}
return null;
}
};
}
export class TextSnapshotReadable implements VSBufferReadable {
private preambleHandled: boolean;
constructor(private snapshot: ITextSnapshot, private preamble?: string) { }
read(): VSBuffer | null {
let value = this.snapshot.read();
// Handle preamble if provided
if (!this.preambleHandled) {
this.preambleHandled = true;
if (typeof this.preamble === 'string') {
if (typeof value === 'string') {
value = this.preamble + value;
} else {
value = this.preamble;
}
}
}
if (typeof value === 'string') {
return VSBuffer.fromString(value);
}
return null;
}
}
export function toBufferOrReadable(value: string): VSBuffer;
export function toBufferOrReadable(value: ITextSnapshot): VSBufferReadable;
export function toBufferOrReadable(value: string | ITextSnapshot): VSBuffer | VSBufferReadable;
export function toBufferOrReadable(value: string | ITextSnapshot | undefined): VSBuffer | VSBufferReadable | undefined;
export function toBufferOrReadable(value: string | ITextSnapshot | undefined): VSBuffer | VSBufferReadable | undefined {
if (typeof value === 'undefined') {
return undefined;
}
if (typeof value === 'string') {
return VSBuffer.fromString(value);
}
return new TextSnapshotReadable(value);
}
/**
* Streamable content and meta information of a file.
*/
export interface IStreamContent extends IBaseStatWithMetadata {
/**
* The streamable content of a text file.
*/
value: IStringStream;
/**
* The encoding of the content if known.
*/
encoding: string;
}
export interface IResolveContentOptions {
/**
* The optional acceptTextOnly parameter allows to fail this request early if the file
* contents are not textual.
*/
acceptTextOnly?: boolean;
/** /**
* The optional etag parameter allows to return early from resolving the resource if * The optional etag parameter allows to return early from resolving the resource if
@@ -758,22 +618,25 @@ export interface IResolveContentOptions {
*/ */
etag?: string; etag?: string;
/**
* The optional encoding parameter allows to specify the desired encoding when resolving
* the contents of the file.
*/
encoding?: string;
/**
* The optional guessEncoding parameter allows to guess encoding from content of the file.
*/
autoGuessEncoding?: boolean;
/** /**
* Is an integer specifying where to begin reading from in the file. If position is null, * Is an integer specifying where to begin reading from in the file. If position is null,
* data will be read from the current file position. * data will be read from the current file position.
*/ */
position?: number; position?: number;
/**
* Is an integer specifying how many bytes to read from the file. By default, all bytes
* will be read.
*/
length?: number;
/**
* If provided, the size of the file will be checked against the limits.
*/
limits?: {
size?: number;
memory?: number;
};
} }
export interface IWriteFileOptions { export interface IWriteFileOptions {
@@ -789,30 +652,6 @@ export interface IWriteFileOptions {
etag?: string; etag?: string;
} }
export interface IWriteTextFileOptions extends IWriteFileOptions {
/**
* The encoding to use when updating a file.
*/
encoding?: string;
/**
* If set to true, will enforce the selected encoding and not perform any detection using BOMs.
*/
overwriteEncoding?: boolean;
/**
* Whether to overwrite a file even if it is readonly.
*/
overwriteReadonly?: boolean;
/**
* Wether to write to the file as elevated (admin) user. When setting this option a prompt will
* ask the user to authenticate as super user.
*/
writeElevated?: boolean;
}
export interface IResolveFileOptions { export interface IResolveFileOptions {
/** /**
@@ -847,7 +686,7 @@ export interface ICreateFileOptions {
} }
export class FileOperationError extends Error { export class FileOperationError extends Error {
constructor(message: string, public fileOperationResult: FileOperationResult, public options?: IResolveContentOptions & IWriteTextFileOptions & ICreateFileOptions) { constructor(message: string, public fileOperationResult: FileOperationResult, public options?: IReadFileOptions & IWriteFileOptions & ICreateFileOptions) {
super(message); super(message);
} }
@@ -857,7 +696,6 @@ export class FileOperationError extends Error {
} }
export const enum FileOperationResult { export const enum FileOperationResult {
FILE_IS_BINARY,
FILE_IS_DIRECTORY, FILE_IS_DIRECTORY,
FILE_NOT_FOUND, FILE_NOT_FOUND,
FILE_NOT_MODIFIED_SINCE, FILE_NOT_MODIFIED_SINCE,
@@ -906,247 +744,6 @@ export interface IFilesConfiguration {
}; };
} }
export const SUPPORTED_ENCODINGS: { [encoding: string]: { labelLong: string; labelShort: string; order: number; encodeOnly?: boolean; alias?: string } } = {
utf8: {
labelLong: 'UTF-8',
labelShort: 'UTF-8',
order: 1,
alias: 'utf8bom'
},
utf8bom: {
labelLong: 'UTF-8 with BOM',
labelShort: 'UTF-8 with BOM',
encodeOnly: true,
order: 2,
alias: 'utf8'
},
utf16le: {
labelLong: 'UTF-16 LE',
labelShort: 'UTF-16 LE',
order: 3
},
utf16be: {
labelLong: 'UTF-16 BE',
labelShort: 'UTF-16 BE',
order: 4
},
windows1252: {
labelLong: 'Western (Windows 1252)',
labelShort: 'Windows 1252',
order: 5
},
iso88591: {
labelLong: 'Western (ISO 8859-1)',
labelShort: 'ISO 8859-1',
order: 6
},
iso88593: {
labelLong: 'Western (ISO 8859-3)',
labelShort: 'ISO 8859-3',
order: 7
},
iso885915: {
labelLong: 'Western (ISO 8859-15)',
labelShort: 'ISO 8859-15',
order: 8
},
macroman: {
labelLong: 'Western (Mac Roman)',
labelShort: 'Mac Roman',
order: 9
},
cp437: {
labelLong: 'DOS (CP 437)',
labelShort: 'CP437',
order: 10
},
windows1256: {
labelLong: 'Arabic (Windows 1256)',
labelShort: 'Windows 1256',
order: 11
},
iso88596: {
labelLong: 'Arabic (ISO 8859-6)',
labelShort: 'ISO 8859-6',
order: 12
},
windows1257: {
labelLong: 'Baltic (Windows 1257)',
labelShort: 'Windows 1257',
order: 13
},
iso88594: {
labelLong: 'Baltic (ISO 8859-4)',
labelShort: 'ISO 8859-4',
order: 14
},
iso885914: {
labelLong: 'Celtic (ISO 8859-14)',
labelShort: 'ISO 8859-14',
order: 15
},
windows1250: {
labelLong: 'Central European (Windows 1250)',
labelShort: 'Windows 1250',
order: 16
},
iso88592: {
labelLong: 'Central European (ISO 8859-2)',
labelShort: 'ISO 8859-2',
order: 17
},
cp852: {
labelLong: 'Central European (CP 852)',
labelShort: 'CP 852',
order: 18
},
windows1251: {
labelLong: 'Cyrillic (Windows 1251)',
labelShort: 'Windows 1251',
order: 19
},
cp866: {
labelLong: 'Cyrillic (CP 866)',
labelShort: 'CP 866',
order: 20
},
iso88595: {
labelLong: 'Cyrillic (ISO 8859-5)',
labelShort: 'ISO 8859-5',
order: 21
},
koi8r: {
labelLong: 'Cyrillic (KOI8-R)',
labelShort: 'KOI8-R',
order: 22
},
koi8u: {
labelLong: 'Cyrillic (KOI8-U)',
labelShort: 'KOI8-U',
order: 23
},
iso885913: {
labelLong: 'Estonian (ISO 8859-13)',
labelShort: 'ISO 8859-13',
order: 24
},
windows1253: {
labelLong: 'Greek (Windows 1253)',
labelShort: 'Windows 1253',
order: 25
},
iso88597: {
labelLong: 'Greek (ISO 8859-7)',
labelShort: 'ISO 8859-7',
order: 26
},
windows1255: {
labelLong: 'Hebrew (Windows 1255)',
labelShort: 'Windows 1255',
order: 27
},
iso88598: {
labelLong: 'Hebrew (ISO 8859-8)',
labelShort: 'ISO 8859-8',
order: 28
},
iso885910: {
labelLong: 'Nordic (ISO 8859-10)',
labelShort: 'ISO 8859-10',
order: 29
},
iso885916: {
labelLong: 'Romanian (ISO 8859-16)',
labelShort: 'ISO 8859-16',
order: 30
},
windows1254: {
labelLong: 'Turkish (Windows 1254)',
labelShort: 'Windows 1254',
order: 31
},
iso88599: {
labelLong: 'Turkish (ISO 8859-9)',
labelShort: 'ISO 8859-9',
order: 32
},
windows1258: {
labelLong: 'Vietnamese (Windows 1258)',
labelShort: 'Windows 1258',
order: 33
},
gbk: {
labelLong: 'Simplified Chinese (GBK)',
labelShort: 'GBK',
order: 34
},
gb18030: {
labelLong: 'Simplified Chinese (GB18030)',
labelShort: 'GB18030',
order: 35
},
cp950: {
labelLong: 'Traditional Chinese (Big5)',
labelShort: 'Big5',
order: 36
},
big5hkscs: {
labelLong: 'Traditional Chinese (Big5-HKSCS)',
labelShort: 'Big5-HKSCS',
order: 37
},
shiftjis: {
labelLong: 'Japanese (Shift JIS)',
labelShort: 'Shift JIS',
order: 38
},
eucjp: {
labelLong: 'Japanese (EUC-JP)',
labelShort: 'EUC-JP',
order: 39
},
euckr: {
labelLong: 'Korean (EUC-KR)',
labelShort: 'EUC-KR',
order: 40
},
windows874: {
labelLong: 'Thai (Windows 874)',
labelShort: 'Windows 874',
order: 41
},
iso885911: {
labelLong: 'Latin/Thai (ISO 8859-11)',
labelShort: 'ISO 8859-11',
order: 42
},
koi8ru: {
labelLong: 'Cyrillic (KOI8-RU)',
labelShort: 'KOI8-RU',
order: 43
},
koi8t: {
labelLong: 'Tajik (KOI8-T)',
labelShort: 'KOI8-T',
order: 44
},
gb2312: {
labelLong: 'Simplified Chinese (GB 2312)',
labelShort: 'GB 2312',
order: 45
},
cp865: {
labelLong: 'Nordic DOS (CP 865)',
labelShort: 'CP 865',
order: 46
},
cp850: {
labelLong: 'Western European DOS (CP 850)',
labelShort: 'CP 850',
order: 47
}
};
export enum FileKind { export enum FileKind {
FILE, FILE,
FOLDER, FOLDER,
@@ -1165,20 +762,3 @@ export function etag(mtime: number | undefined, size: number | undefined): strin
return mtime.toString(29) + size.toString(31); return mtime.toString(29) + size.toString(31);
} }
// TODO@ben remove traces of legacy file service
export const ILegacyFileService = createDecorator<ILegacyFileService>('legacyFileService');
export interface ILegacyFileService extends IDisposable {
_serviceBrand: any;
encoding: IResourceEncodings;
onAfterOperation: Event<FileOperationEvent>;
registerProvider(scheme: string, provider: IFileSystemProvider): IDisposable;
resolveContent(resource: URI, options?: IResolveContentOptions): Promise<IContent>;
resolveStreamContent(resource: URI, options?: IResolveContentOptions): Promise<IStreamContent>;
}

View File

@@ -13,6 +13,49 @@ export interface ResolvedAuthority {
readonly port: number; readonly port: number;
} }
export enum RemoteAuthorityResolverErrorCode {
Unknown = 'Unknown',
NotAvailable = 'NotAvailable',
TemporarilyNotAvailable = 'TemporarilyNotAvailable',
}
export class RemoteAuthorityResolverError extends Error {
public static isHandledNotAvailable(err: any): boolean {
if (err instanceof RemoteAuthorityResolverError) {
if (err._code === RemoteAuthorityResolverErrorCode.NotAvailable && err._detail === true) {
return true;
}
}
return false;
}
public static isTemporarilyNotAvailable(err: any): boolean {
if (err instanceof RemoteAuthorityResolverError) {
return err._code === RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable;
}
return false;
}
public readonly _message: string | undefined;
public readonly _code: RemoteAuthorityResolverErrorCode;
public readonly _detail: any;
constructor(message?: string, code: RemoteAuthorityResolverErrorCode = RemoteAuthorityResolverErrorCode.Unknown, detail?: any) {
super(message);
this._message = message;
this._code = code;
this._detail = detail;
// workaround when extending builtin objects and when compiling to ES5, see:
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
if (typeof (<any>Object).setPrototypeOf === 'function') {
(<any>Object).setPrototypeOf(this, RemoteAuthorityResolverError.prototype);
}
}
}
export interface IRemoteAuthorityResolverService { export interface IRemoteAuthorityResolverService {
_serviceBrand: any; _serviceBrand: any;

View File

@@ -4,14 +4,11 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { isAbsolute } from 'vs/base/common/path';
import * as resources from 'vs/base/common/resources'; import * as resources from 'vs/base/common/resources';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { TernarySearchTree } from 'vs/base/common/map'; import { TernarySearchTree } from 'vs/base/common/map';
import { Event } from 'vs/base/common/event'; import { Event } from 'vs/base/common/event';
import { IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isRawUriWorkspaceFolder, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isRawUriWorkspaceFolder, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
import { coalesce, distinct } from 'vs/base/common/arrays';
import { isLinux } from 'vs/base/common/platform';
export const IWorkspaceContextService = createDecorator<IWorkspaceContextService>('contextService'); export const IWorkspaceContextService = createDecorator<IWorkspaceContextService>('contextService');
@@ -225,17 +222,21 @@ export class WorkspaceFolder implements IWorkspaceFolder {
} }
} }
export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], relativeTo?: URI): WorkspaceFolder[] { export function toWorkspaceFolder(resource: URI): WorkspaceFolder {
let workspaceFolders = parseWorkspaceFolders(configuredFolders, relativeTo); return new WorkspaceFolder({ uri: resource, index: 0, name: resources.basenameOrAuthority(resource) }, { uri: resource.toString() });
return ensureUnique(coalesce(workspaceFolders))
.map(({ uri, raw, name }, index) => new WorkspaceFolder({ uri, name: name || resources.basenameOrAuthority(uri), index }, raw));
} }
function parseWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], relativeTo: URI | undefined): Array<WorkspaceFolder | undefined> { export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], workspaceConfigFile: URI): WorkspaceFolder[] {
return configuredFolders.map((configuredFolder, index) => { let result: WorkspaceFolder[] = [];
let seen: { [uri: string]: boolean } = Object.create(null);
const relativeTo = resources.dirname(workspaceConfigFile);
for (let configuredFolder of configuredFolders) {
let uri: URI | null = null; let uri: URI | null = null;
if (isRawFileWorkspaceFolder(configuredFolder)) { if (isRawFileWorkspaceFolder(configuredFolder)) {
uri = toUri(configuredFolder.path, relativeTo); if (configuredFolder.path) {
uri = resources.resolvePath(relativeTo, configuredFolder.path);
}
} else if (isRawUriWorkspaceFolder(configuredFolder)) { } else if (isRawUriWorkspaceFolder(configuredFolder)) {
try { try {
uri = URI.parse(configuredFolder.uri); uri = URI.parse(configuredFolder.uri);
@@ -248,25 +249,16 @@ function parseWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], rela
// ignore // ignore
} }
} }
if (!uri) { if (uri) {
return undefined; // remove duplicates
} let comparisonKey = resources.getComparisonKey(uri);
return new WorkspaceFolder({ uri, name: configuredFolder.name! /*is ensured in caller*/, index }, configuredFolder); if (!seen[comparisonKey]) {
}); seen[comparisonKey] = true;
}
function toUri(path: string, relativeTo: URI | undefined): URI | null { const name = configuredFolder.name || resources.basenameOrAuthority(uri);
if (path) { result.push(new WorkspaceFolder({ uri, name, index: result.length }, configuredFolder));
if (isAbsolute(path)) { }
return URI.file(path);
}
if (relativeTo) {
return resources.joinPath(relativeTo, path);
} }
} }
return null; return result;
}
function ensureUnique(folders: WorkspaceFolder[]): WorkspaceFolder[] {
return distinct(folders, folder => isLinux ? folder.uri.toString() : folder.uri.toString().toLowerCase());
} }

View File

@@ -4,15 +4,12 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { Workspace, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { isWindows } from 'vs/base/common/platform'; import { isWindows } from 'vs/base/common/platform';
const wsUri = URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace'); const wsUri = URI.file(isWindows ? 'C:\\testWorkspace' : '/testWorkspace');
export const TestWorkspace = testWorkspace(wsUri); export const TestWorkspace = testWorkspace(wsUri);
export function testWorkspace(resource: URI): Workspace { export function testWorkspace(resource: URI): Workspace {
return new Workspace( return new Workspace(resource.toString(), [toWorkspaceFolder(resource)]);
resource.toString(),
toWorkspaceFolders([{ path: resource.fsPath }])
);
} }

View File

@@ -4,15 +4,30 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as assert from 'assert'; import * as assert from 'assert';
import * as path from 'vs/base/common/path';
import { Workspace, toWorkspaceFolders, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { Workspace, toWorkspaceFolders, WorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { IRawFileWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces';
import { isWindows } from 'vs/base/common/platform';
suite('Workspace', () => { suite('Workspace', () => {
const fileFolder = isWindows ? 'c:\\src' : '/src';
const abcFolder = isWindows ? 'c:\\abc' : '/abc';
const testFolderUri = URI.file(path.join(fileFolder, 'test'));
const mainFolderUri = URI.file(path.join(fileFolder, 'main'));
const test1FolderUri = URI.file(path.join(fileFolder, 'test1'));
const test2FolderUri = URI.file(path.join(fileFolder, 'test2'));
const test3FolderUri = URI.file(path.join(fileFolder, 'test3'));
const abcTest1FolderUri = URI.file(path.join(abcFolder, 'test1'));
const abcTest3FolderUri = URI.file(path.join(abcFolder, 'test3'));
const workspaceConfigUri = URI.file(path.join(fileFolder, 'test.code-workspace'));
test('getFolder returns the folder with given uri', () => { test('getFolder returns the folder with given uri', () => {
const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 2 }); const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 2 });
let testObject = new Workspace('', [new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), expected, new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]);
const actual = testObject.getFolder(expected.uri); const actual = testObject.getFolder(expected.uri);
@@ -20,172 +35,172 @@ suite('Workspace', () => {
}); });
test('getFolder returns the folder if the uri is sub', () => { test('getFolder returns the folder if the uri is sub', () => {
const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 0 }); const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 0 });
let testObject = new Workspace('', [expected, new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]); let testObject = new Workspace('', [expected, new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 1 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 2 })]);
const actual = testObject.getFolder(URI.file('/src/test/a')); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'test/a')));
assert.equal(actual, expected); assert.equal(actual, expected);
}); });
test('getFolder returns the closest folder if the uri is sub', () => { test('getFolder returns the closest folder if the uri is sub', () => {
const expected = new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 2 }); const expected = new WorkspaceFolder({ uri: testFolderUri, name: '', index: 2 });
let testObject = new Workspace('', [new WorkspaceFolder({ uri: URI.file('/src/main'), name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]); let testObject = new Workspace('', [new WorkspaceFolder({ uri: mainFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 }), expected]);
const actual = testObject.getFolder(URI.file('/src/test/a')); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'test/a')));
assert.equal(actual, expected); assert.equal(actual, expected);
}); });
test('getFolder returns null if the uri is not sub', () => { test('getFolder returns null if the uri is not sub', () => {
let testObject = new Workspace('', [new WorkspaceFolder({ uri: URI.file('/src/test'), name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })]); let testObject = new Workspace('', [new WorkspaceFolder({ uri: testFolderUri, name: '', index: 0 }), new WorkspaceFolder({ uri: URI.file('/src/code'), name: '', index: 1 })]);
const actual = testObject.getFolder(URI.file('/src/main/a')); const actual = testObject.getFolder(URI.file(path.join(fileFolder, 'main/a')));
assert.equal(actual, undefined); assert.equal(actual, undefined);
}); });
test('toWorkspaceFolders with single absolute folder', () => { test('toWorkspaceFolders with single absolute folder', () => {
const actual = toWorkspaceFolders([{ path: '/src/test' }]); const actual = toWorkspaceFolders([{ path: '/src/test' }], workspaceConfigUri);
assert.equal(actual.length, 1); assert.equal(actual.length, 1);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test').fsPath); assert.equal(actual[0].uri.fsPath, testFolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'test'); assert.equal(actual[0].name, 'test');
}); });
test('toWorkspaceFolders with single relative folder', () => { test('toWorkspaceFolders with single relative folder', () => {
const actual = toWorkspaceFolders([{ path: './test' }], URI.file('src')); const actual = toWorkspaceFolders([{ path: './test' }], workspaceConfigUri);
assert.equal(actual.length, 1); assert.equal(actual.length, 1);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test').fsPath); assert.equal(actual[0].uri.fsPath, testFolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, './test'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, './test');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'test'); assert.equal(actual[0].name, 'test');
}); });
test('toWorkspaceFolders with single absolute folder with name', () => { test('toWorkspaceFolders with single absolute folder with name', () => {
const actual = toWorkspaceFolders([{ path: '/src/test', name: 'hello' }]); const actual = toWorkspaceFolders([{ path: '/src/test', name: 'hello' }], workspaceConfigUri);
assert.equal(actual.length, 1); assert.equal(actual.length, 1);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test').fsPath); assert.equal(actual[0].uri.fsPath, testFolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'hello'); assert.equal(actual[0].name, 'hello');
}); });
test('toWorkspaceFolders with multiple unique absolute folders', () => { test('toWorkspaceFolders with multiple unique absolute folders', () => {
const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3' }, { path: '/src/test1' }]); const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3' }, { path: '/src/test1' }], workspaceConfigUri);
assert.equal(actual.length, 3); assert.equal(actual.length, 3);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); assert.equal(actual[0].uri.fsPath, test2FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'test2'); assert.equal(actual[0].name, 'test2');
assert.equal(actual[1].uri.fsPath, URI.file('/src/test3').fsPath); assert.equal(actual[1].uri.fsPath, test3FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/src/test3'); assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/src/test3');
assert.equal(actual[1].index, 1); assert.equal(actual[1].index, 1);
assert.equal(actual[1].name, 'test3'); assert.equal(actual[1].name, 'test3');
assert.equal(actual[2].uri.fsPath, URI.file('/src/test1').fsPath); assert.equal(actual[2].uri.fsPath, test1FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, '/src/test1'); assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, '/src/test1');
assert.equal(actual[2].index, 2); assert.equal(actual[2].index, 2);
assert.equal(actual[2].name, 'test1'); assert.equal(actual[2].name, 'test1');
}); });
test('toWorkspaceFolders with multiple unique absolute folders with names', () => { test('toWorkspaceFolders with multiple unique absolute folders with names', () => {
const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3', name: 'noName' }, { path: '/src/test1' }]); const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3', name: 'noName' }, { path: '/src/test1' }], workspaceConfigUri);
assert.equal(actual.length, 3); assert.equal(actual.length, 3);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); assert.equal(actual[0].uri.fsPath, test2FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'test2'); assert.equal(actual[0].name, 'test2');
assert.equal(actual[1].uri.fsPath, URI.file('/src/test3').fsPath); assert.equal(actual[1].uri.fsPath, test3FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/src/test3'); assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/src/test3');
assert.equal(actual[1].index, 1); assert.equal(actual[1].index, 1);
assert.equal(actual[1].name, 'noName'); assert.equal(actual[1].name, 'noName');
assert.equal(actual[2].uri.fsPath, URI.file('/src/test1').fsPath); assert.equal(actual[2].uri.fsPath, test1FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, '/src/test1'); assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, '/src/test1');
assert.equal(actual[2].index, 2); assert.equal(actual[2].index, 2);
assert.equal(actual[2].name, 'test1'); assert.equal(actual[2].name, 'test1');
}); });
test('toWorkspaceFolders with multiple unique absolute and relative folders', () => { test('toWorkspaceFolders with multiple unique absolute and relative folders', () => {
const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/abc/test3', name: 'noName' }, { path: './test1' }], URI.file('src')); const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/abc/test3', name: 'noName' }, { path: './test1' }], workspaceConfigUri);
assert.equal(actual.length, 3); assert.equal(actual.length, 3);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); assert.equal(actual[0].uri.fsPath, test2FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'test2'); assert.equal(actual[0].name, 'test2');
assert.equal(actual[1].uri.fsPath, URI.file('/abc/test3').fsPath); assert.equal(actual[1].uri.fsPath, abcTest3FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/abc/test3'); assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/abc/test3');
assert.equal(actual[1].index, 1); assert.equal(actual[1].index, 1);
assert.equal(actual[1].name, 'noName'); assert.equal(actual[1].name, 'noName');
assert.equal(actual[2].uri.fsPath, URI.file('/src/test1').fsPath); assert.equal(actual[2].uri.fsPath, test1FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, './test1'); assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, './test1');
assert.equal(actual[2].index, 2); assert.equal(actual[2].index, 2);
assert.equal(actual[2].name, 'test1'); assert.equal(actual[2].name, 'test1');
}); });
test('toWorkspaceFolders with multiple absolute folders with duplicates', () => { test('toWorkspaceFolders with multiple absolute folders with duplicates', () => {
const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test2', name: 'noName' }, { path: '/src/test1' }]); const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test2', name: 'noName' }, { path: '/src/test1' }], workspaceConfigUri);
assert.equal(actual.length, 2); assert.equal(actual.length, 2);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); assert.equal(actual[0].uri.fsPath, test2FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'test2'); assert.equal(actual[0].name, 'test2');
assert.equal(actual[1].uri.fsPath, URI.file('/src/test1').fsPath); assert.equal(actual[1].uri.fsPath, test1FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/src/test1'); assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/src/test1');
assert.equal(actual[1].index, 1); assert.equal(actual[1].index, 1);
assert.equal(actual[1].name, 'test1'); assert.equal(actual[1].name, 'test1');
}); });
test('toWorkspaceFolders with multiple absolute and relative folders with duplicates', () => { test('toWorkspaceFolders with multiple absolute and relative folders with duplicates', () => {
const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3', name: 'noName' }, { path: './test3' }, { path: '/abc/test1' }], URI.file('src')); const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '/src/test3', name: 'noName' }, { path: './test3' }, { path: '/abc/test1' }], workspaceConfigUri);
assert.equal(actual.length, 3); assert.equal(actual.length, 3);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); assert.equal(actual[0].uri.fsPath, test2FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'test2'); assert.equal(actual[0].name, 'test2');
assert.equal(actual[1].uri.fsPath, URI.file('/src/test3').fsPath); assert.equal(actual[1].uri.fsPath, test3FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/src/test3'); assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, '/src/test3');
assert.equal(actual[1].index, 1); assert.equal(actual[1].index, 1);
assert.equal(actual[1].name, 'noName'); assert.equal(actual[1].name, 'noName');
assert.equal(actual[2].uri.fsPath, URI.file('/abc/test1').fsPath); assert.equal(actual[2].uri.fsPath, abcTest1FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, '/abc/test1'); assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, '/abc/test1');
assert.equal(actual[2].index, 2); assert.equal(actual[2].index, 2);
assert.equal(actual[2].name, 'test1'); assert.equal(actual[2].name, 'test1');
}); });
test('toWorkspaceFolders with multiple absolute and relative folders with invalid paths', () => { test('toWorkspaceFolders with multiple absolute and relative folders with invalid paths', () => {
const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '', name: 'noName' }, { path: './test3' }, { path: '/abc/test1' }], URI.file('src')); const actual = toWorkspaceFolders([{ path: '/src/test2' }, { path: '', name: 'noName' }, { path: './test3' }, { path: '/abc/test1' }], workspaceConfigUri);
assert.equal(actual.length, 3); assert.equal(actual.length, 3);
assert.equal(actual[0].uri.fsPath, URI.file('/src/test2').fsPath); assert.equal(actual[0].uri.fsPath, test2FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2'); assert.equal((<IRawFileWorkspaceFolder>actual[0].raw).path, '/src/test2');
assert.equal(actual[0].index, 0); assert.equal(actual[0].index, 0);
assert.equal(actual[0].name, 'test2'); assert.equal(actual[0].name, 'test2');
assert.equal(actual[1].uri.fsPath, URI.file('/src/test3').fsPath); assert.equal(actual[1].uri.fsPath, test3FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, './test3'); assert.equal((<IRawFileWorkspaceFolder>actual[1].raw).path, './test3');
assert.equal(actual[1].index, 1); assert.equal(actual[1].index, 1);
assert.equal(actual[1].name, 'test3'); assert.equal(actual[1].name, 'test3');
assert.equal(actual[2].uri.fsPath, URI.file('/abc/test1').fsPath); assert.equal(actual[2].uri.fsPath, abcTest1FolderUri.fsPath);
assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, '/abc/test1'); assert.equal((<IRawFileWorkspaceFolder>actual[2].raw).path, '/abc/test1');
assert.equal(actual[2].index, 2); assert.equal(actual[2].index, 2);
assert.equal(actual[2].name, 'test1'); assert.equal(actual[2].name, 'test1');

View File

@@ -17,7 +17,7 @@ import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { originalFSPath, dirname as resourcesDirname, isEqualOrParent, joinPath } from 'vs/base/common/resources'; import { originalFSPath, isEqualOrParent, joinPath } from 'vs/base/common/resources';
export interface IStoredWorkspace { export interface IStoredWorkspace {
folders: IStoredWorkspaceFolder[]; folders: IStoredWorkspaceFolder[];
@@ -71,7 +71,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
return { return {
id: workspaceIdentifier.id, id: workspaceIdentifier.id,
configPath: workspaceIdentifier.configPath, configPath: workspaceIdentifier.configPath,
folders: toWorkspaceFolders(workspace.folders, resourcesDirname(path)), folders: toWorkspaceFolders(workspace.folders, workspaceIdentifier.configPath),
remoteAuthority: workspace.remoteAuthority remoteAuthority: workspace.remoteAuthority
}; };
} catch (error) { } catch (error) {

View File

@@ -72,6 +72,10 @@ declare module 'vscode' {
//#region Alex - resolvers //#region Alex - resolvers
export interface RemoteAuthorityResolverContext {
resolveAttempt: number;
}
export class ResolvedAuthority { export class ResolvedAuthority {
readonly host: string; readonly host: string;
readonly port: number; readonly port: number;
@@ -79,8 +83,15 @@ declare module 'vscode' {
constructor(host: string, port: number); constructor(host: string, port: number);
} }
export class RemoteAuthorityResolverError extends Error {
static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError;
static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError;
constructor(message?: string);
}
export interface RemoteAuthorityResolver { export interface RemoteAuthorityResolver {
resolve(authority: string): ResolvedAuthority | Thenable<ResolvedAuthority>; resolve(authority: string, context: RemoteAuthorityResolverContext): ResolvedAuthority | Thenable<ResolvedAuthority>;
} }
export interface ResourceLabelFormatter { export interface ResourceLabelFormatter {

View File

@@ -224,7 +224,7 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
cwd: request.shellLaunchConfig.cwd, cwd: request.shellLaunchConfig.cwd,
env: request.shellLaunchConfig.env env: request.shellLaunchConfig.env
}; };
this._proxy.$createProcess(request.proxy.terminalId, shellLaunchConfigDto, request.activeWorkspaceRootUri, request.cols, request.rows); this._proxy.$createProcess(request.proxy.terminalId, shellLaunchConfigDto, request.activeWorkspaceRootUri, request.cols, request.rows, request.isWorkspaceShellAllowed);
request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data)); request.proxy.onInput(data => this._proxy.$acceptProcessInput(request.proxy.terminalId, data));
request.proxy.onResize(dimensions => this._proxy.$acceptProcessResize(request.proxy.terminalId, dimensions.cols, dimensions.rows)); request.proxy.onResize(dimensions => this._proxy.$acceptProcessResize(request.proxy.terminalId, dimensions.cols, dimensions.rows));
request.proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(request.proxy.terminalId, immediate)); request.proxy.onShutdown(immediate => this._proxy.$acceptProcessShutdown(request.proxy.terminalId, immediate));

View File

@@ -40,7 +40,7 @@ import { IRPCProtocol, createExtHostContextProxyIdentifier as createExtId, creat
import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress'; import { IProgressOptions, IProgressStep } from 'vs/platform/progress/common/progress';
import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
import { IMarkdownString } from 'vs/base/common/htmlContent'; import { IMarkdownString } from 'vs/base/common/htmlContent';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ResolvedAuthority, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset'; import * as codeInset from 'vs/workbench/contrib/codeinset/common/codeInset';
import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy'; import * as callHierarchy from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
@@ -812,8 +812,24 @@ export interface ExtHostSearchShape {
$clearCache(cacheKey: string): Promise<void>; $clearCache(cacheKey: string): Promise<void>;
} }
export interface IResolveAuthorityErrorResult {
type: 'error';
error: {
message: string | undefined;
code: RemoteAuthorityResolverErrorCode;
detail: any;
};
}
export interface IResolveAuthorityOKResult {
type: 'ok';
value: ResolvedAuthority;
}
export type IResolveAuthorityResult = IResolveAuthorityErrorResult | IResolveAuthorityOKResult;
export interface ExtHostExtensionServiceShape { export interface ExtHostExtensionServiceShape {
$resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority>; $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult>;
$startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>; $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void>;
$activateByEvent(activationEvent: string): Promise<void>; $activateByEvent(activationEvent: string): Promise<void>;
$activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean>; $activate(extensionId: ExtensionIdentifier, activationEvent: string): Promise<boolean>;
@@ -1062,7 +1078,7 @@ export interface ExtHostTerminalServiceShape {
$acceptTerminalRendererInput(id: number, data: string): void; $acceptTerminalRendererInput(id: number, data: string): void;
$acceptTerminalTitleChange(id: number, name: string): void; $acceptTerminalTitleChange(id: number, name: string): void;
$acceptTerminalDimensions(id: number, cols: number, rows: number): void; $acceptTerminalDimensions(id: number, cols: number, rows: number): void;
$createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents, cols: number, rows: number): void; $createProcess(id: number, shellLaunchConfig: ShellLaunchConfigDto, activeWorkspaceRootUri: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void;
$acceptProcessInput(id: number, data: string): void; $acceptProcessInput(id: number, data: string): void;
$acceptProcessResize(id: number, cols: number, rows: number): void; $acceptProcessResize(id: number, cols: number, rows: number): void;
$acceptProcessShutdown(id: number, immediate: boolean): void; $acceptProcessShutdown(id: number, immediate: boolean): void;

View File

@@ -13,6 +13,7 @@ import { URI } from 'vs/base/common/uri';
import { generateUuid } from 'vs/base/common/uuid'; import { generateUuid } from 'vs/base/common/uuid';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files';
import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver';
function es5ClassCompat(target: Function): any { function es5ClassCompat(target: Function): any {
///@ts-ignore ///@ts-ignore
@@ -445,6 +446,35 @@ export class ResolvedAuthority {
} }
} }
export class RemoteAuthorityResolverError extends Error {
static NotAvailable(message?: string, handled?: boolean): RemoteAuthorityResolverError {
return new RemoteAuthorityResolverError(message, RemoteAuthorityResolverErrorCode.NotAvailable, handled);
}
static TemporarilyNotAvailable(message?: string): RemoteAuthorityResolverError {
return new RemoteAuthorityResolverError(message, RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable);
}
public readonly _message: string | undefined;
public readonly _code: RemoteAuthorityResolverErrorCode;
public readonly _detail: any;
constructor(message?: string, code: RemoteAuthorityResolverErrorCode = RemoteAuthorityResolverErrorCode.Unknown, detail?: any) {
super(message);
this._message = message;
this._code = code;
this._detail = detail;
// workaround when extending builtin objects and when compiling to ES5, see:
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
if (typeof (<any>Object).setPrototypeOf === 'function') {
(<any>Object).setPrototypeOf(this, RemoteAuthorityResolverError.prototype);
}
}
}
export enum EndOfLine { export enum EndOfLine {
LF = 1, LF = 1,
CRLF = 2 CRLF = 2

View File

@@ -814,6 +814,7 @@ export function createApiFactory(
Range: extHostTypes.Range, Range: extHostTypes.Range,
RelativePattern: extHostTypes.RelativePattern, RelativePattern: extHostTypes.RelativePattern,
ResolvedAuthority: extHostTypes.ResolvedAuthority, ResolvedAuthority: extHostTypes.ResolvedAuthority,
RemoteAuthorityResolverError: extHostTypes.RemoteAuthorityResolverError,
Selection: extHostTypes.Selection, Selection: extHostTypes.Selection,
SelectionRange: extHostTypes.SelectionRange, SelectionRange: extHostTypes.SelectionRange,
ShellExecution: extHostTypes.ShellExecution, ShellExecution: extHostTypes.ShellExecution,

View File

@@ -29,7 +29,7 @@ export interface StatusPipeArgs {
export interface RunCommandPipeArgs { export interface RunCommandPipeArgs {
type: 'command'; type: 'command';
command: string; command: string;
args: string[]; args: any[];
} }
export class CLIServer { export class CLIServer {
@@ -99,7 +99,9 @@ export class CLIServer {
for (const s of folderURIs) { for (const s of folderURIs) {
try { try {
urisToOpen.push({ folderUri: URI.parse(s) }); urisToOpen.push({ folderUri: URI.parse(s) });
forceNewWindow = true; if (!addMode && !forceReuseWindow) {
forceNewWindow = true;
}
} catch (e) { } catch (e) {
// ignore // ignore
} }
@@ -110,7 +112,9 @@ export class CLIServer {
try { try {
if (hasWorkspaceFileExtension(s)) { if (hasWorkspaceFileExtension(s)) {
urisToOpen.push({ workspaceUri: URI.parse(s) }); urisToOpen.push({ workspaceUri: URI.parse(s) });
forceNewWindow = true; if (!forceReuseWindow) {
forceNewWindow = true;
}
} else { } else {
urisToOpen.push({ fileUri: URI.parse(s) }); urisToOpen.push({ fileUri: URI.parse(s) });
} }

View File

@@ -16,7 +16,7 @@ import { ILogService } from 'vs/platform/log/common/log';
// {{SQL CARBON EDIT}} - Remove createApiFactory initializeExtensionApi, and IExtensionApiFactory imports // {{SQL CARBON EDIT}} - Remove createApiFactory initializeExtensionApi, and IExtensionApiFactory imports
// import { createApiFactory, IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl'; // import { createApiFactory, IExtensionApiFactory } from 'vs/workbench/api/node/extHost.api.impl';
import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor'; import { NodeModuleRequireInterceptor, VSCodeNodeModuleFactory, KeytarNodeModuleFactory, OpenNodeModuleFactory } from 'vs/workbench/api/node/extHostRequireInterceptor';
import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostExtensionServiceShape, IEnvironment, IInitData, IMainContext, MainContext, MainThreadExtensionServiceShape, MainThreadTelemetryShape, MainThreadWorkspaceShape, IResolveAuthorityResult } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension } from 'vs/workbench/api/common/extHostExtensionActivator'; import { ActivatedExtension, EmptyExtension, ExtensionActivatedByAPI, ExtensionActivatedByEvent, ExtensionActivationReason, ExtensionActivationTimes, ExtensionActivationTimesBuilder, ExtensionsActivator, IExtensionAPI, IExtensionContext, IExtensionModule, HostExtension } from 'vs/workbench/api/common/extHostExtensionActivator';
import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService'; import { ExtHostLogService } from 'vs/workbench/api/common/extHostLogService';
@@ -27,7 +27,6 @@ import { ExtensionDescriptionRegistry } from 'vs/workbench/services/extensions/c
import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver'; import { connectProxyResolver } from 'vs/workbench/services/extensions/node/proxyResolver';
import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { CancellationTokenSource } from 'vs/base/common/cancellation';
import * as errors from 'vs/base/common/errors'; import * as errors from 'vs/base/common/errors';
import { ResolvedAuthority } from 'vs/platform/remote/common/remoteAuthorityResolver';
import * as vscode from 'vscode'; import * as vscode from 'vscode';
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
import { IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkspace } from 'vs/platform/workspace/common/workspace';
@@ -37,6 +36,7 @@ import { VSBuffer } from 'vs/base/common/buffer';
import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures'; import { ISchemeTransformer } from 'vs/workbench/api/common/extHostLanguageFeatures';
import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento'; import { ExtensionMemento } from 'vs/workbench/api/common/extHostMemento';
import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths'; import { ExtensionStoragePaths } from 'vs/workbench/api/node/extHostStoragePaths';
import { RemoteAuthorityResolverError } from 'vs/workbench/api/common/extHostTypes';
interface ITestRunner { interface ITestRunner {
run(testsRoot: string, clb: (error: Error, failures?: number) => void): void; run(testsRoot: string, clb: (error: Error, failures?: number) => void): void;
@@ -600,7 +600,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
// -- called by main thread // -- called by main thread
public async $resolveAuthority(remoteAuthority: string): Promise<ResolvedAuthority> { public async $resolveAuthority(remoteAuthority: string, resolveAttempt: number): Promise<IResolveAuthorityResult> {
const authorityPlusIndex = remoteAuthority.indexOf('+'); const authorityPlusIndex = remoteAuthority.indexOf('+');
if (authorityPlusIndex === -1) { if (authorityPlusIndex === -1) {
throw new Error(`Not an authority that can be resolved!`); throw new Error(`Not an authority that can be resolved!`);
@@ -615,12 +615,29 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
throw new Error(`No resolver available for ${authorityPrefix}`); throw new Error(`No resolver available for ${authorityPrefix}`);
} }
const result = await resolver.resolve(remoteAuthority); try {
return { const result = await resolver.resolve(remoteAuthority, { resolveAttempt });
authority: remoteAuthority, return {
host: result.host, type: 'ok',
port: result.port, value: {
}; authority: remoteAuthority,
host: result.host,
port: result.port,
}
};
} catch (err) {
if (err instanceof RemoteAuthorityResolverError) {
return {
type: 'error',
error: {
code: err._code,
message: err._message,
detail: err._detail
}
};
}
throw err;
}
} }
public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> { public $startExtensionHost(enabledExtensionIds: ExtensionIdentifier[]): Promise<void> {
@@ -680,7 +697,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
let buff = VSBuffer.alloc(size); let buff = VSBuffer.alloc(size);
let value = Math.random() % 256; let value = Math.random() % 256;
for (let i = 0; i < size; i++) { for (let i = 0; i < size; i++) {
buff.writeUint8(value, i); buff.writeUInt8(value, i);
} }
return buff; return buff;
} }

View File

@@ -436,7 +436,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
} }
} }
public async $createProcess(id: number, shellLaunchConfigDto: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number): Promise<void> { public async $createProcess(id: number, shellLaunchConfigDto: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
const shellLaunchConfig: IShellLaunchConfig = { const shellLaunchConfig: IShellLaunchConfig = {
name: shellLaunchConfigDto.name, name: shellLaunchConfigDto.name,
executable: shellLaunchConfigDto.executable, executable: shellLaunchConfigDto.executable,
@@ -445,31 +445,31 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
env: shellLaunchConfigDto.env env: shellLaunchConfigDto.env
}; };
// TODO: This function duplicates a lot of TerminalProcessManager.createProcess, ideally // Merge in shell and args from settings
// they would be merged into a single implementation. const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
const configProvider = await this._extHostConfiguration.getConfigProvider();
const terminalConfig = configProvider.getConfiguration('terminal.integrated');
if (!shellLaunchConfig.executable) { if (!shellLaunchConfig.executable) {
// TODO: This duplicates some of TerminalConfigHelper.mergeDefaultShellPathAndArgs and should be merged const fetchSetting = (key: string) => {
// this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig); const setting = configProvider
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
const platformKey = platform.isWindows ? 'windows' : platform.isMacintosh ? 'osx' : 'linux'; .inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
const shellConfigValue: string | undefined = terminalConfig.get(`shell.${platformKey}`); return {
const shellArgsConfigValue: string | undefined = terminalConfig.get(`shellArgs.${platformKey}`); user: setting ? setting.globalValue : undefined,
value: setting ? setting.workspaceValue : undefined,
shellLaunchConfig.executable = shellConfigValue; default: setting ? setting.defaultValue : undefined,
shellLaunchConfig.args = shellArgsConfigValue; };
};
terminalEnvironment.mergeDefaultShellPathAndArgs(shellLaunchConfig, fetchSetting, isWorkspaceShellAllowed || false);
} }
// TODO: @daniel // Get the initial cwd
const configProvider = await this._extHostConfiguration.getConfigProvider();
const terminalConfig = configProvider.getConfiguration('terminal.integrated');
const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents); const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents);
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), activeWorkspaceRootUri, terminalConfig.cwd); const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), activeWorkspaceRootUri, terminalConfig.cwd);
// TODO: Pull in and resolve config settings // TODO: Pull in and resolve config settings
// // Resolve env vars from config and shell // // Resolve env vars from config and shell
// const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri); // const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri);
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
// const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...terminalConfig.env[platformKey] }, lastActiveWorkspaceRoot); // const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...terminalConfig.env[platformKey] }, lastActiveWorkspaceRoot);
const envFromConfig = { ...terminalConfig.env[platformKey] }; const envFromConfig = { ...terminalConfig.env[platformKey] };
// const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot); // const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
@@ -501,7 +501,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
this._terminalProcesses[id] = p; this._terminalProcesses[id] = p;
} }
public $acceptProcessInput(id: number, data: string): void { public $acceptProcessInput(id: number, data: string): void {
this._terminalProcesses[id].input(data); this._terminalProcesses[id].input(data);
} }

View File

@@ -19,7 +19,7 @@ import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remot
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { FileService3 } from 'vs/workbench/services/files2/browser/fileService2'; import { FileService } from 'vs/workbench/services/files/common/fileService';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
class CodeRendererMain extends Disposable { class CodeRendererMain extends Disposable {
@@ -78,7 +78,7 @@ class CodeRendererMain extends Disposable {
serviceCollection.set(IRemoteAgentService, remoteAgentService); serviceCollection.set(IRemoteAgentService, remoteAgentService);
// Files // Files
const fileService = this._register(new FileService3(logService)); const fileService = this._register(new FileService(logService));
serviceCollection.set(IFileService, fileService); serviceCollection.set(IFileService, fileService);
const connection = remoteAgentService.getConnection(); const connection = remoteAgentService.getConnection();

View File

@@ -5,8 +5,7 @@
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { ITextSnapshot } from 'vs/platform/files/common/files'; import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model';
import { ITextBufferFactory } from 'vs/editor/common/model';
import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
import { keys, ResourceMap } from 'vs/base/common/map'; import { keys, ResourceMap } from 'vs/base/common/map';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
@@ -52,7 +51,7 @@ import { ExportData } from 'vs/base/common/performance';
import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history';
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
import { IWorkspaceContextService, Workspace, toWorkspaceFolders, IWorkspaceFolder, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService, Workspace, toWorkspaceFolder, IWorkspaceFolder, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace';
import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration'; import { ITextResourcePropertiesService } from 'vs/editor/common/services/resourceConfiguration';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -1366,10 +1365,7 @@ export class SimpleWorkspaceService implements IWorkspaceContextService {
readonly onDidChangeWorkbenchState = Event.None; readonly onDidChangeWorkbenchState = Event.None;
constructor() { constructor() {
this.workspace = new Workspace( this.workspace = new Workspace(workspaceResource.toString(), [toWorkspaceFolder(workspaceResource)]);
workspaceResource.toString(),
toWorkspaceFolders([{ uri: workspaceResource.toString() }])
);
} }
getFolders(): IWorkspaceFolder[] { getFolders(): IWorkspaceFolder[] {

View File

@@ -15,7 +15,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer'; import { ResourceViewerContext, ResourceViewer } from 'vs/workbench/browser/parts/editor/resourceViewer';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { Dimension, size, clearNode } from 'vs/base/browser/dom'; import { Dimension, size, clearNode } from 'vs/base/browser/dom';
import { IFileService } from 'vs/platform/files/common/files'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { dispose } from 'vs/base/common/lifecycle'; import { dispose } from 'vs/base/common/lifecycle';
import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageService } from 'vs/platform/storage/common/storage';
@@ -47,7 +47,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
callbacks: IOpenCallbacks, callbacks: IOpenCallbacks,
telemetryService: ITelemetryService, telemetryService: ITelemetryService,
themeService: IThemeService, themeService: IThemeService,
@IFileService private readonly _fileService: IFileService, @ITextFileService private readonly textFileService: ITextFileService,
@IStorageService storageService: IStorageService @IStorageService storageService: IStorageService
) { ) {
super(id, telemetryService, themeService, storageService); super(id, telemetryService, themeService, storageService);
@@ -89,7 +89,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
// Render Input // Render Input
this.resourceViewerContext = ResourceViewer.show( this.resourceViewerContext = ResourceViewer.show(
{ name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() }, { name: model.getName(), resource: model.getResource(), size: model.getSize(), etag: model.getETag(), mime: model.getMime() },
this._fileService, this.textFileService,
this.binaryContainer, this.binaryContainer,
this.scrollbar, this.scrollbar,
resource => this.handleOpenInternalCallback(input, options), resource => this.handleOpenInternalCallback(input, options),

View File

@@ -26,7 +26,7 @@ import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/bina
import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor'; import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { SUPPORTED_ENCODINGS, IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files'; import { IFileService, FILES_ASSOCIATIONS_CONFIG } from 'vs/platform/files/common/files';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService'; import { IModelService } from 'vs/editor/common/services/modelService';
@@ -35,7 +35,7 @@ import { Selection } from 'vs/editor/common/core/selection';
import { TabFocus } from 'vs/editor/common/config/commonEditorConfig'; import { TabFocus } from 'vs/editor/common/config/commonEditorConfig';
import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICommandService } from 'vs/platform/commands/common/commands';
import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService, SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles';
import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents'; import { ICursorPositionChangedEvent } from 'vs/editor/common/controller/cursorEvents';
import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IConfigurationChangedEvent, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
@@ -1152,7 +1152,8 @@ export class ChangeEncodingAction extends Action {
@IEditorService private readonly editorService: IEditorService, @IEditorService private readonly editorService: IEditorService,
@IQuickInputService private readonly quickInputService: IQuickInputService, @IQuickInputService private readonly quickInputService: IQuickInputService,
@ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService, @ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService,
@IFileService private readonly fileService: IFileService @IFileService private readonly fileService: IFileService,
@ITextFileService private readonly textFileService: ITextFileService
) { ) {
super(actionId, actionLabel); super(actionId, actionLabel);
} }
@@ -1203,7 +1204,7 @@ export class ChangeEncodingAction extends Action {
return Promise.resolve(null); // encoding detection only possible for resources the file service can handle return Promise.resolve(null); // encoding detection only possible for resources the file service can handle
} }
return this.fileService.resolveContent(resource, { autoGuessEncoding: true, acceptTextOnly: true }).then(content => content.encoding, err => null); return this.textFileService.read(resource, { autoGuessEncoding: true, acceptTextOnly: true }).then(content => content.encoding, err => null);
}) })
.then((guessedEncoding: string) => { .then((guessedEncoding: string) => {
const isReopenWithEncoding = (action === reopenWithEncodingPick); const isReopenWithEncoding = (action === reopenWithEncodingPick);

View File

@@ -145,8 +145,13 @@ export class NoTabsTitleControl extends TitleControl {
this.redraw(); this.redraw();
} }
updateEditorLabel(editor: IEditorInput): void { updateEditorLabel(editor?: IEditorInput): void {
this.ifEditorIsActive(editor, () => this.redraw()); if (!editor) {
editor = withNullAsUndefined(this.group.activeEditor);
}
if (editor) {
this.ifEditorIsActive(editor, () => this.redraw());
}
} }
updateEditorDirty(editor: IEditorInput): void { updateEditorDirty(editor: IEditorInput): void {

View File

@@ -21,7 +21,7 @@ import { Action } from 'vs/base/common/actions';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { memoize } from 'vs/base/common/decorators'; import { memoize } from 'vs/base/common/decorators';
import * as platform from 'vs/base/common/platform'; import * as platform from 'vs/base/common/platform';
import { IFileService } from 'vs/platform/files/common/files'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
export interface IResourceDescriptor { export interface IResourceDescriptor {
readonly resource: URI; readonly resource: URI;
@@ -72,7 +72,7 @@ export class ResourceViewer {
static show( static show(
descriptor: IResourceDescriptor, descriptor: IResourceDescriptor,
fileService: IFileService, textFileService: ITextFileService,
container: HTMLElement, container: HTMLElement,
scrollbar: DomScrollableElement, scrollbar: DomScrollableElement,
openInternalClb: (uri: URI) => void, openInternalClb: (uri: URI) => void,
@@ -85,7 +85,7 @@ export class ResourceViewer {
// Images // Images
if (ResourceViewer.isImageResource(descriptor)) { if (ResourceViewer.isImageResource(descriptor)) {
return ImageView.create(container, descriptor, fileService, scrollbar, openExternalClb, metadataClb); return ImageView.create(container, descriptor, textFileService, scrollbar, openExternalClb, metadataClb);
} }
// Large Files // Large Files
@@ -114,13 +114,13 @@ class ImageView {
static create( static create(
container: HTMLElement, container: HTMLElement,
descriptor: IResourceDescriptor, descriptor: IResourceDescriptor,
fileService: IFileService, textFileService: ITextFileService,
scrollbar: DomScrollableElement, scrollbar: DomScrollableElement,
openExternalClb: (uri: URI) => void, openExternalClb: (uri: URI) => void,
metadataClb: (meta: string) => void metadataClb: (meta: string) => void
): ResourceViewerContext { ): ResourceViewerContext {
if (ImageView.shouldShowImageInline(descriptor)) { if (ImageView.shouldShowImageInline(descriptor)) {
return InlineImageView.create(container, descriptor, fileService, scrollbar, metadataClb); return InlineImageView.create(container, descriptor, textFileService, scrollbar, metadataClb);
} }
return LargeImageView.create(container, descriptor, openExternalClb, metadataClb); return LargeImageView.create(container, descriptor, openExternalClb, metadataClb);
@@ -357,7 +357,7 @@ class InlineImageView {
static create( static create(
container: HTMLElement, container: HTMLElement,
descriptor: IResourceDescriptor, descriptor: IResourceDescriptor,
fileService: IFileService, textFileService: ITextFileService,
scrollbar: DomScrollableElement, scrollbar: DomScrollableElement,
metadataClb: (meta: string) => void metadataClb: (meta: string) => void
) { ) {
@@ -559,7 +559,7 @@ class InlineImageView {
} }
})); }));
InlineImageView.imageSrc(descriptor, fileService).then(dataUri => { InlineImageView.imageSrc(descriptor, textFileService).then(dataUri => {
const imgs = container.getElementsByTagName('img'); const imgs = container.getElementsByTagName('img');
if (imgs.length) { if (imgs.length) {
imgs[0].src = dataUri; imgs[0].src = dataUri;
@@ -569,12 +569,12 @@ class InlineImageView {
return context; return context;
} }
private static imageSrc(descriptor: IResourceDescriptor, fileService: IFileService): Promise<string> { private static imageSrc(descriptor: IResourceDescriptor, textFileService: ITextFileService): Promise<string> {
if (descriptor.resource.scheme === Schemas.data) { if (descriptor.resource.scheme === Schemas.data) {
return Promise.resolve(descriptor.resource.toString(true /* skip encoding */)); return Promise.resolve(descriptor.resource.toString(true /* skip encoding */));
} }
return fileService.resolveContent(descriptor.resource, { encoding: 'base64' }).then(data => { return textFileService.read(descriptor.resource, { encoding: 'base64' }).then(data => {
const mime = getMime(descriptor); const mime = getMime(descriptor);
return `data:${mime};base64,${data.value}`; return `data:${mime};base64,${data.value}`;

View File

@@ -42,6 +42,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl'; import { BreadcrumbsControl } from 'vs/workbench/browser/parts/editor/breadcrumbsControl';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { withNullAsUndefined } from 'vs/base/common/types'; import { withNullAsUndefined } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label';
// {{SQL CARBON EDIT}} -- Display the editor's tab color // {{SQL CARBON EDIT}} -- Display the editor's tab color
import { ICommandService } from 'vs/platform/commands/common/commands'; import { ICommandService } from 'vs/platform/commands/common/commands';
@@ -94,8 +95,9 @@ export class TabsTitleControl extends TitleControl {
// {{SQL CARBON EDIT}} -- Display the editor's tab color // {{SQL CARBON EDIT}} -- Display the editor's tab color
@ICommandService private commandService: ICommandService, @ICommandService private commandService: ICommandService,
// {{SQL CARBON EDIT}} -- End // {{SQL CARBON EDIT}} -- End
@ILabelService labelService: ILabelService
) { ) {
super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService, fileService); super(parent, accessor, group, contextMenuService, instantiationService, contextKeyService, keybindingService, telemetryService, notificationService, menuService, quickOpenService, themeService, extensionService, configurationService, fileService, labelService);
} }
protected create(parent: HTMLElement): void { protected create(parent: HTMLElement): void {

View File

@@ -15,13 +15,12 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput';
import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator';
import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget';
import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel';
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IThemeService } from 'vs/platform/theme/common/themeService';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { ScrollType, IDiffEditorViewState, IDiffEditorModel } from 'vs/editor/common/editorCommon';
import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Registry } from 'vs/platform/registry/common/platform'; import { Registry } from 'vs/platform/registry/common/platform';
@@ -242,10 +241,11 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
private isFileBinaryError(error: Error | Error[]): boolean { private isFileBinaryError(error: Error | Error[]): boolean {
if (types.isArray(error)) { if (types.isArray(error)) {
const errors = <Error[]>error; const errors = <Error[]>error;
return errors.some(e => this.isFileBinaryError(e)); return errors.some(e => this.isFileBinaryError(e));
} }
return (<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY; return (<TextFileOperationError>error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY;
} }
clearInput(): void { clearInput(): void {

View File

@@ -40,6 +40,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { withNullAsUndefined } from 'vs/base/common/types'; import { withNullAsUndefined } from 'vs/base/common/types';
import { ILabelService } from 'vs/platform/label/common/label';
export interface IToolbarActions { export interface IToolbarActions {
primary: IAction[]; primary: IAction[];
@@ -79,6 +80,7 @@ export abstract class TitleControl extends Themable {
@IExtensionService private readonly extensionService: IExtensionService, @IExtensionService private readonly extensionService: IExtensionService,
@IConfigurationService protected configurationService: IConfigurationService, @IConfigurationService protected configurationService: IConfigurationService,
@IFileService private readonly fileService: IFileService, @IFileService private readonly fileService: IFileService,
@ILabelService private readonly labelService: ILabelService
) { ) {
super(themeService); super(themeService);
@@ -91,6 +93,7 @@ export abstract class TitleControl extends Themable {
private registerListeners(): void { private registerListeners(): void {
this._register(this.extensionService.onDidRegisterExtensions(() => this.updateEditorActionsToolbar())); this._register(this.extensionService.onDidRegisterExtensions(() => this.updateEditorActionsToolbar()));
this._register(this.labelService.onDidChangeFormatters(() => this.updateEditorLabel()));
} }
protected abstract create(parent: HTMLElement): void; protected abstract create(parent: HTMLElement): void;
@@ -340,7 +343,7 @@ export abstract class TitleControl extends Themable {
abstract setActive(isActive: boolean): void; abstract setActive(isActive: boolean): void;
abstract updateEditorLabel(editor: IEditorInput): void; abstract updateEditorLabel(editor?: IEditorInput): void;
abstract updateEditorDirty(editor: IEditorInput): void; abstract updateEditorDirty(editor: IEditorInput): void;

View File

@@ -50,6 +50,13 @@
padding: 6px 6px 4px 6px; padding: 6px 6px 4px 6px;
} }
.quick-input-and-message {
display: flex;
flex-direction: column;
flex-grow: 1;
position: relative;
}
.quick-input-check-all { .quick-input-check-all {
align-self: center; align-self: center;
margin: 0; margin: 0;
@@ -65,7 +72,8 @@
flex-grow: 1; flex-grow: 1;
} }
.quick-input-widget.show-checkboxes .quick-input-box { .quick-input-widget.show-checkboxes .quick-input-box,
.quick-input-widget.show-checkboxes .quick-input-message {
margin-left: 5px; margin-left: 5px;
} }
@@ -95,7 +103,8 @@
} }
.quick-input-message { .quick-input-message {
margin: 0px 11px; margin-top: -1px;
padding: 6px 5px 2px 5px;
} }
.quick-input-progress.monaco-progress-container { .quick-input-progress.monaco-progress-container {

View File

@@ -301,6 +301,18 @@ class QuickInput implements IQuickInput {
return ''; return '';
} }
protected showMessageDecoration(severity: Severity) {
this.ui.inputBox.showDecoration(severity);
if (severity === Severity.Error) {
const styles = this.ui.inputBox.stylesForType(severity);
this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : null;
this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : null;
} else {
this.ui.message.style.backgroundColor = '';
this.ui.message.style.border = '';
}
}
public dispose(): void { public dispose(): void {
this.hide(); this.hide();
this.disposables = dispose(this.disposables); this.disposables = dispose(this.disposables);
@@ -742,10 +754,10 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
} }
if (this.validationMessage) { if (this.validationMessage) {
this.ui.message.textContent = this.validationMessage; this.ui.message.textContent = this.validationMessage;
this.ui.inputBox.showDecoration(Severity.Error); this.showMessageDecoration(Severity.Error);
} else { } else {
this.ui.message.textContent = null; this.ui.message.textContent = null;
this.ui.inputBox.showDecoration(Severity.Ignore); this.showMessageDecoration(Severity.Info);
} }
this.ui.customButton.label = this.customLabel; this.ui.customButton.label = this.customLabel;
this.ui.customButton.element.title = this.customHover; this.ui.customButton.element.title = this.customHover;
@@ -876,11 +888,11 @@ class InputBox extends QuickInput implements IInputBox {
} }
if (!this.validationMessage && this.ui.message.textContent !== this.noValidationMessage) { if (!this.validationMessage && this.ui.message.textContent !== this.noValidationMessage) {
this.ui.message.textContent = this.noValidationMessage; this.ui.message.textContent = this.noValidationMessage;
this.ui.inputBox.showDecoration(Severity.Ignore); this.showMessageDecoration(Severity.Info);
} }
if (this.validationMessage && this.ui.message.textContent !== this.validationMessage) { if (this.validationMessage && this.ui.message.textContent !== this.validationMessage) {
this.ui.message.textContent = this.validationMessage; this.ui.message.textContent = this.validationMessage;
this.ui.inputBox.showDecoration(Severity.Error); this.showMessageDecoration(Severity.Error);
} }
this.ui.setVisibilities({ title: !!this.title || !!this.step, inputBox: true, message: true }); this.ui.setVisibilities({ title: !!this.title || !!this.step, inputBox: true, message: true });
} }
@@ -1044,7 +1056,8 @@ export class QuickInputService extends Component implements IQuickInputService {
} }
})); }));
this.filterContainer = dom.append(headerContainer, $('.quick-input-filter')); const extraContainer = dom.append(headerContainer, $('.quick-input-and-message'));
this.filterContainer = dom.append(extraContainer, $('.quick-input-filter'));
const inputBox = this._register(new QuickInputBox(this.filterContainer)); const inputBox = this._register(new QuickInputBox(this.filterContainer));
inputBox.setAttribute('aria-describedby', `${this.idPrefix}message`); inputBox.setAttribute('aria-describedby', `${this.idPrefix}message`);
@@ -1075,7 +1088,7 @@ export class QuickInputService extends Component implements IQuickInputService {
this.onDidCustomEmitter.fire(); this.onDidCustomEmitter.fire();
})); }));
const message = dom.append(container, $(`#${this.idPrefix}message.quick-input-message`)); const message = dom.append(extraContainer, $(`#${this.idPrefix}message.quick-input-message`));
const progressBar = new ProgressBar(container); const progressBar = new ProgressBar(container);
dom.addClass(progressBar.getContainer(), 'quick-input-progress'); dom.addClass(progressBar.getContainer(), 'quick-input-progress');

View File

@@ -101,6 +101,10 @@ export class QuickInputBox {
} }
} }
stylesForType(decoration: Severity) {
return this.inputBox.stylesForType(decoration === Severity.Info ? MessageType.INFO : decoration === Severity.Warning ? MessageType.WARNING : MessageType.ERROR);
}
setFocus(): void { setFocus(): void {
this.inputBox.focus(); this.inputBox.focus();
} }

View File

@@ -23,7 +23,6 @@ import { Position, Parts, IWorkbenchLayoutService } from 'vs/workbench/services/
import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageService } from 'vs/platform/storage/common/storage';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { IFileService, ILegacyFileService } from 'vs/platform/files/common/files';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
@@ -197,20 +196,12 @@ export class Workbench extends Layout {
instantiationService.invokeFunction(accessor => { instantiationService.invokeFunction(accessor => {
const lifecycleService = accessor.get(ILifecycleService); const lifecycleService = accessor.get(ILifecycleService);
// TODO@Ben legacy file service
const fileService = accessor.get(IFileService) as any;
if (typeof fileService.setLegacyService === 'function') {
try {
fileService.setLegacyService(accessor.get(ILegacyFileService));
} catch (error) {
//ignore, legacy file service might not be registered
}
}
// TODO@Sandeep debt around cyclic dependencies // TODO@Sandeep debt around cyclic dependencies
const configurationService = accessor.get(IConfigurationService) as any; const configurationService = accessor.get(IConfigurationService) as any;
if (typeof configurationService.acquireInstantiationService === 'function') { if (typeof configurationService.acquireInstantiationService === 'function') {
configurationService.acquireInstantiationService(instantiationService); setTimeout(() => {
configurationService.acquireInstantiationService(instantiationService);
}, 0);
} }
// Signal to lifecycle that services are set // Signal to lifecycle that services are set

View File

@@ -25,8 +25,8 @@ exports.collectModules = function () {
createModuleDescription('vs/workbench/services/search/node/searchApp', []), createModuleDescription('vs/workbench/services/search/node/searchApp', []),
createModuleDescription('vs/workbench/services/files2/node/watcher/unix/watcherApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/unix/watcherApp', []),
createModuleDescription('vs/workbench/services/files2/node/watcher/nsfw/watcherApp', []), createModuleDescription('vs/workbench/services/files/node/watcher/nsfw/watcherApp', []),
createModuleDescription('vs/workbench/services/extensions/node/extensionHostProcess', []), createModuleDescription('vs/workbench/services/extensions/node/extensionHostProcess', []),
]; ];

View File

@@ -3,14 +3,13 @@
* Licensed under the Source EULA. See License.txt in the project root for license information. * Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { ITextModel, ITextBufferFactory } from 'vs/editor/common/model'; import { ITextModel, ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model';
import { EditorModel } from 'vs/workbench/common/editor'; import { EditorModel } from 'vs/workbench/common/editor';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService'; import { ITextEditorModel, IResolvedTextEditorModel } from 'vs/editor/common/services/resolverService';
import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService'; import { IModeService, ILanguageSelection } from 'vs/editor/common/services/modeService';
import { IModelService } from 'vs/editor/common/services/modelService'; import { IModelService } from 'vs/editor/common/services/modelService';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { ITextSnapshot } from 'vs/platform/files/common/files';
/** /**
* The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated. * The base text editor model leverages the code editor model. This class is only intended to be subclassed and not instantiated.

View File

@@ -313,7 +313,13 @@ export const STATUS_BAR_HOST_NAME_BACKGROUND = registerColor('statusBarItem.host
dark: STATUS_BAR_PROMINENT_ITEM_BACKGROUND, dark: STATUS_BAR_PROMINENT_ITEM_BACKGROUND,
light: STATUS_BAR_PROMINENT_ITEM_BACKGROUND, light: STATUS_BAR_PROMINENT_ITEM_BACKGROUND,
hc: STATUS_BAR_PROMINENT_ITEM_BACKGROUND hc: STATUS_BAR_PROMINENT_ITEM_BACKGROUND
}, nls.localize('statusBarItemHostBackground', "Background color for the remote host name on the status bar.")); }, nls.localize('statusBarItemHostBackground', "Background color for the host indicator on the status bar."));
export const STATUS_BAR_HOST_NAME_FOREGROUND = registerColor('statusBarItem.hostForeground', {
dark: STATUS_BAR_PROMINENT_ITEM_FOREGROUND,
light: STATUS_BAR_PROMINENT_ITEM_FOREGROUND,
hc: STATUS_BAR_PROMINENT_ITEM_FOREGROUND
}, nls.localize('statusBarItemHostForeground', "Foreground color for the host indicator on the status bar."));
// < --- Activity Bar --- > // < --- Activity Bar --- >

View File

@@ -97,7 +97,7 @@ export class LanguageConfigurationFileHandler {
} }
private _handleConfigFile(languageIdentifier: LanguageIdentifier, configFileLocation: URI): void { private _handleConfigFile(languageIdentifier: LanguageIdentifier, configFileLocation: URI): void {
this._fileService.resolveContent(configFileLocation, { encoding: 'utf8' }).then((contents) => { this._fileService.readFile(configFileLocation).then((contents) => {
const errors: ParseError[] = []; const errors: ParseError[] = [];
const configuration = <ILanguageConfiguration>parse(contents.value.toString(), errors); const configuration = <ILanguageConfiguration>parse(contents.value.toString(), errors);
if (errors.length) { if (errors.length) {

View File

@@ -559,7 +559,7 @@ class Launch extends AbstractLaunch implements ILaunch {
const resource = this.uri; const resource = this.uri;
let created = false; let created = false;
return this.fileService.resolveContent(resource).then(content => content.value, err => { return this.fileService.readFile(resource).then(content => content.value, err => {
// launch.json not found: create one by collecting launch configs from debugConfigProviders // launch.json not found: create one by collecting launch configs from debugConfigProviders
return this.configurationManager.guessDebugger(type).then(adapter => { return this.configurationManager.guessDebugger(type).then(adapter => {
if (adapter) { if (adapter) {
@@ -576,19 +576,17 @@ class Launch extends AbstractLaunch implements ILaunch {
} }
created = true; // pin only if config file is created #8727 created = true; // pin only if config file is created #8727
return this.textFileService.write(resource, content).then(() => { return this.textFileService.write(resource, content).then(() => content);
// convert string into IContent; see #32135
return content;
});
}); });
}).then(content => { }).then(content => {
if (!content) { if (!content) {
return { editor: null, created: false }; return { editor: null, created: false };
} }
const index = content.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`); const contentValue = content.toString();
const index = contentValue.indexOf(`"${this.configurationManager.selectedConfiguration.name}"`);
let startLineNumber = 1; let startLineNumber = 1;
for (let i = 0; i < index; i++) { for (let i = 0; i < index; i++) {
if (content.charAt(i) === '\n') { if (contentValue.charAt(i) === '\n') {
startLineNumber++; startLineNumber++;
} }
} }

View File

@@ -29,35 +29,37 @@ export function getTerminalLauncher() {
} }
let _DEFAULT_TERMINAL_LINUX_READY: Promise<string> | null = null; let _DEFAULT_TERMINAL_LINUX_READY: Promise<string> | null = null;
export function getDefaultTerminalLinuxReady(): Promise<string> { export function getDefaultTerminalLinuxReady(): Promise<string> {
if (!_DEFAULT_TERMINAL_LINUX_READY) { if (!_DEFAULT_TERMINAL_LINUX_READY) {
_DEFAULT_TERMINAL_LINUX_READY = new Promise<string>(c => { _DEFAULT_TERMINAL_LINUX_READY = new Promise<string>(resolve => {
if (env.isLinux) { if (env.isLinux) {
Promise.all<any>([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => { Promise.all<any>([pfs.exists('/etc/debian_version'), process.lazyEnv]).then(([isDebian]) => {
if (isDebian) { if (isDebian) {
c('x-terminal-emulator'); resolve('x-terminal-emulator');
} else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') { } else if (process.env.DESKTOP_SESSION === 'gnome' || process.env.DESKTOP_SESSION === 'gnome-classic') {
c('gnome-terminal'); resolve('gnome-terminal');
} else if (process.env.DESKTOP_SESSION === 'kde-plasma') { } else if (process.env.DESKTOP_SESSION === 'kde-plasma') {
c('konsole'); resolve('konsole');
} else if (process.env.COLORTERM) { } else if (process.env.COLORTERM) {
c(process.env.COLORTERM); resolve(process.env.COLORTERM);
} else if (process.env.TERM) { } else if (process.env.TERM) {
c(process.env.TERM); resolve(process.env.TERM);
} else { } else {
c('xterm'); resolve('xterm');
} }
}); });
return; return;
} }
c('xterm'); resolve('xterm');
}); });
} }
return _DEFAULT_TERMINAL_LINUX_READY; return _DEFAULT_TERMINAL_LINUX_READY;
} }
let _DEFAULT_TERMINAL_WINDOWS: string | null = null; let _DEFAULT_TERMINAL_WINDOWS: string | null = null;
export function getDefaultTerminalWindows(): string { export function getDefaultTerminalWindows(): string {
if (!_DEFAULT_TERMINAL_WINDOWS) { if (!_DEFAULT_TERMINAL_WINDOWS) {
const isWoW64 = !!process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432'); const isWoW64 = !!process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432');
@@ -82,7 +84,7 @@ class WinTerminalService extends TerminalLauncher {
const exec = configuration.external.windowsExec || getDefaultTerminalWindows(); const exec = configuration.external.windowsExec || getDefaultTerminalWindows();
return new Promise<number | undefined>((c, e) => { return new Promise<number | undefined>((resolve, reject) => {
const title = `"${dir} - ${TERMINAL_TITLE}"`; const title = `"${dir} - ${TERMINAL_TITLE}"`;
const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code const command = `""${args.join('" "')}" & pause"`; // use '|' to only pause on non-zero exit code
@@ -104,9 +106,11 @@ class WinTerminalService extends TerminalLauncher {
}; };
const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options); const cmd = cp.spawn(WinTerminalService.CMD, cmdArgs, options);
cmd.on('error', e); cmd.on('error', err => {
reject(improveError(err));
});
c(undefined); resolve(undefined);
}); });
} }
} }
@@ -120,7 +124,7 @@ class MacTerminalService extends TerminalLauncher {
const terminalApp = configuration.external.osxExec || MacTerminalService.DEFAULT_TERMINAL_OSX; const terminalApp = configuration.external.osxExec || MacTerminalService.DEFAULT_TERMINAL_OSX;
return new Promise<number | undefined>((c, e) => { return new Promise<number | undefined>((resolve, reject) => {
if (terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') { if (terminalApp === MacTerminalService.DEFAULT_TERMINAL_OSX || terminalApp === 'iTerm.app') {
@@ -156,24 +160,26 @@ class MacTerminalService extends TerminalLauncher {
let stderr = ''; let stderr = '';
const osa = cp.spawn(MacTerminalService.OSASCRIPT, osaArgs); const osa = cp.spawn(MacTerminalService.OSASCRIPT, osaArgs);
osa.on('error', e); osa.on('error', err => {
reject(improveError(err));
});
osa.stderr.on('data', (data) => { osa.stderr.on('data', (data) => {
stderr += data.toString(); stderr += data.toString();
}); });
osa.on('exit', (code: number) => { osa.on('exit', (code: number) => {
if (code === 0) { // OK if (code === 0) { // OK
c(undefined); resolve(undefined);
} else { } else {
if (stderr) { if (stderr) {
const lines = stderr.split('\n', 1); const lines = stderr.split('\n', 1);
e(new Error(lines[0])); reject(new Error(lines[0]));
} else { } else {
e(new Error(nls.localize('mac.terminal.script.failed', "Script '{0}' failed with exit code {1}", script, code))); reject(new Error(nls.localize('mac.terminal.script.failed', "Script '{0}' failed with exit code {1}", script, code)));
} }
} }
}); });
} else { } else {
e(new Error(nls.localize('mac.terminal.type.not.supported', "'{0}' not supported", terminalApp))); reject(new Error(nls.localize('mac.terminal.type.not.supported', "'{0}' not supported", terminalApp)));
} }
}); });
} }
@@ -188,7 +194,7 @@ class LinuxTerminalService extends TerminalLauncher {
const terminalConfig = configuration.external; const terminalConfig = configuration.external;
const execThenable: Promise<string> = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady(); const execThenable: Promise<string> = terminalConfig.linuxExec ? Promise.resolve(terminalConfig.linuxExec) : getDefaultTerminalLinuxReady();
return new Promise<number | undefined>((c, e) => { return new Promise<number | undefined>((resolve, reject) => {
let termArgs: string[] = []; let termArgs: string[] = [];
//termArgs.push('--title'); //termArgs.push('--title');
@@ -218,19 +224,21 @@ class LinuxTerminalService extends TerminalLauncher {
let stderr = ''; let stderr = '';
const cmd = cp.spawn(exec, termArgs, options); const cmd = cp.spawn(exec, termArgs, options);
cmd.on('error', e); cmd.on('error', err => {
reject(improveError(err));
});
cmd.stderr.on('data', (data) => { cmd.stderr.on('data', (data) => {
stderr += data.toString(); stderr += data.toString();
}); });
cmd.on('exit', (code: number) => { cmd.on('exit', (code: number) => {
if (code === 0) { // OK if (code === 0) { // OK
c(undefined); resolve(undefined);
} else { } else {
if (stderr) { if (stderr) {
const lines = stderr.split('\n', 1); const lines = stderr.split('\n', 1);
e(new Error(lines[0])); reject(new Error(lines[0]));
} else { } else {
e(new Error(nls.localize('linux.term.failed', "'{0}' failed with exit code {1}", exec, code))); reject(new Error(nls.localize('linux.term.failed', "'{0}' failed with exit code {1}", exec, code)));
} }
} }
}); });
@@ -239,6 +247,16 @@ class LinuxTerminalService extends TerminalLauncher {
} }
} }
/**
* tries to turn OS errors into more meaningful error messages
*/
function improveError(err: Error): Error {
if (err['errno'] === 'ENOENT' && err['path']) {
return new Error(nls.localize('ext.term.app.not.found', "can't find terminal application '{0}'", err['path']));
}
return err;
}
/** /**
* Quote args if necessary and combine into a space separated string. * Quote args if necessary and combine into a space separated string.
*/ */

View File

@@ -46,6 +46,7 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo
import { CancellationToken } from 'vs/base/common/cancellation'; import { CancellationToken } from 'vs/base/common/cancellation';
import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { extname } from 'vs/base/common/resources'; import { extname } from 'vs/base/common/resources';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
const milliSecondsInADay = 1000 * 60 * 60 * 24; const milliSecondsInADay = 1000 * 60 * 60 * 24;
const choiceNever = localize('neverShowAgain', "Don't Show Again"); const choiceNever = localize('neverShowAgain', "Don't Show Again");
@@ -111,6 +112,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
@IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService,
@IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService,
@IExperimentService private readonly experimentService: IExperimentService, @IExperimentService private readonly experimentService: IExperimentService,
@ITextFileService private readonly textFileService: ITextFileService
) { ) {
super(); super();
@@ -339,8 +341,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
return Promise.resolve(null); return Promise.resolve(null);
} }
return Promise.resolve(this.fileService.resolveContent(workspace.configuration) return Promise.resolve(this.fileService.readFile(workspace.configuration)
.then(content => <IExtensionsConfigContent>(json.parse(content.value)['extensions']), err => null)); .then(content => <IExtensionsConfigContent>(json.parse(content.value.toString())['extensions']), err => null));
} }
/** /**
@@ -350,8 +352,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
const extensionsJsonUri = workspaceFolder.toResource(EXTENSIONS_CONFIG); const extensionsJsonUri = workspaceFolder.toResource(EXTENSIONS_CONFIG);
return Promise.resolve(this.fileService.resolve(extensionsJsonUri) return Promise.resolve(this.fileService.resolve(extensionsJsonUri)
.then(() => this.fileService.resolveContent(extensionsJsonUri)) .then(() => this.fileService.readFile(extensionsJsonUri))
.then(content => <IExtensionsConfigContent>json.parse(content.value), err => null)); .then(content => <IExtensionsConfigContent>json.parse(content.value.toString()), err => null));
} }
/** /**
@@ -968,7 +970,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe
const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations'; const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations';
const workspaceUri = this.contextService.getWorkspace().folders[0].uri; const workspaceUri = this.contextService.getWorkspace().folders[0].uri;
return Promise.all([getHashedRemotesFromUri(workspaceUri, this.fileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => { return Promise.all([getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, false), getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true)]).then(([hashedRemotes1, hashedRemotes2]) => {
const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []); const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []);
if (!hashedRemotes.length) { if (!hashedRemotes.length) {
return undefined; return undefined;

View File

@@ -252,6 +252,30 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
scope: ConfigurationScope.APPLICATION, scope: ConfigurationScope.APPLICATION,
default: ExtensionsPolicy.allowAll default: ExtensionsPolicy.allowAll
}, },
'extensions.extensionKind': {
type: 'object',
description: localize('extensions.extensionKind', "Configure ui or workspace extensions and allow them to run locally or remotely in a remote window."),
properties: {
'ui': {
type: 'array',
items: {
type: 'string',
pattern: '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$',
}
},
'workspace': {
type: 'array',
items: {
type: 'string',
pattern: '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$',
}
}
},
default: {
ui: [],
workspace: []
}
},
'extensions.showInstalledExtensionsByDefault': { 'extensions.showInstalledExtensionsByDefault': {
type: 'boolean', type: 'boolean',
description: localize('extensions.showInstalledExtensionsByDefault', "When enabled, extensions view shows installed extensions view by default."), description: localize('extensions.showInstalledExtensionsByDefault', "When enabled, extensions view shows installed extensions view by default."),

View File

@@ -23,7 +23,7 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati
import { ShowViewletAction } from 'vs/workbench/browser/viewlet'; import { ShowViewletAction } from 'vs/workbench/browser/viewlet';
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery';
import { IFileService, IContent } from 'vs/platform/files/common/files'; import { IFileService, IFileContent } from 'vs/platform/files/common/files';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
@@ -168,8 +168,7 @@ export class InstallAction extends ExtensionAction {
@IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService,
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IConfigurationService private readonly configurationService: IConfigurationService, @IConfigurationService private readonly configurationService: IConfigurationService,
@ILabelService private readonly labelService: ILabelService, @ILabelService private readonly labelService: ILabelService
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
) { ) {
super(`extensions.install`, InstallAction.INSTALL_LABEL, InstallAction.Class, false); super(`extensions.install`, InstallAction.INSTALL_LABEL, InstallAction.Class, false);
this.update(); this.update();
@@ -196,12 +195,12 @@ export class InstallAction extends ExtensionAction {
} else { } else {
if (this._manifest && this.workbenchEnvironmentService.configuration.remoteAuthority) { if (this._manifest && this.workbenchEnvironmentService.configuration.remoteAuthority) {
if (isUIExtension(this._manifest, this.configurationService)) { if (isUIExtension(this._manifest, this.configurationService)) {
this.label = `${InstallAction.INSTALL_LABEL} (${this.extensionManagementServerService.localExtensionManagementServer.label})`; this.label = `${InstallAction.INSTALL_LABEL} ${localize('locally', "Locally")}`;
this.tooltip = `${InstallAction.INSTALL_LABEL} (${this.extensionManagementServerService.localExtensionManagementServer.label})`; this.tooltip = `${InstallAction.INSTALL_LABEL} ${localize('locally', "Locally")}`;
} else { } else {
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority) || localize('remote', "Remote"); const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.workbenchEnvironmentService.configuration.remoteAuthority) || localize('remote', "Remote");
this.label = `${InstallAction.INSTALL_LABEL} (${host})`; this.label = `${InstallAction.INSTALL_LABEL} on ${host}`;
this.tooltip = `${InstallAction.INSTALL_LABEL} (${host})`; this.tooltip = `${InstallAction.INSTALL_LABEL} on ${host}`;
} }
} else { } else {
this.label = InstallAction.INSTALL_LABEL; this.label = InstallAction.INSTALL_LABEL;
@@ -319,7 +318,7 @@ export class RemoteInstallAction extends ExtensionAction {
const remoteAuthority = this.environmentService.configuration.remoteAuthority; const remoteAuthority = this.environmentService.configuration.remoteAuthority;
if (remoteAuthority) { if (remoteAuthority) {
const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority) || localize('remote', "Remote"); const host = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority) || localize('remote', "Remote");
this.label = `${RemoteInstallAction.INSTALL_LABEL} (${host})`; this.label = `${RemoteInstallAction.INSTALL_LABEL} on ${host}`;
return; return;
} }
} }
@@ -1233,13 +1232,21 @@ export class ReloadAction extends ExtensionAction {
const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local); const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
if (runningExtension) { if (runningExtension) {
// Extension is running // Extension is running
const isSameVersionRunning = isSameExtensionRunning && this.extension.version === runningExtension.version;
if (isEnabled) { if (isEnabled) {
if (!isSameVersionRunning && !this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) { if (!this.extensionService.canAddExtension(toExtensionDescription(this.extension.local))) {
this.enabled = true; if (isSameExtensionRunning) {
this.label = localize('reloadRequired', "Reload Required"); if (this.extension.version !== runningExtension.version) {
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio this.enabled = true;
this.tooltip = localize('postUpdateTooltip', "Please reload Azure Data Studio to enable the updated extension."); this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postUpdateTooltip', "Please reload Azure Data Studio to enable the updated extension.");
}
} else {
this.enabled = true;
this.label = localize('reloadRequired', "Reload Required");
// {{SQL CARBON EDIT}} - replace Visual Studio Code with Azure Data Studio
this.tooltip = localize('postEnableTooltip', "Please reload Azure Data Studio to enable this extension.");
}
} }
} else { } else {
if (isSameExtensionRunning) { if (isSameExtensionRunning) {
@@ -2057,7 +2064,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<any> { protected openWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<any> {
return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile) return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
.then(content => this.getSelectionPosition(content.value, content.resource, ['extensions', 'recommendations'])) .then(content => this.getSelectionPosition(content.value.toString(), content.resource, ['extensions', 'recommendations']))
.then(selection => this.editorService.openEditor({ .then(selection => this.editorService.openEditor({
resource: workspaceConfigurationFile, resource: workspaceConfigurationFile,
options: { options: {
@@ -2071,7 +2078,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile) return this.getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile)
.then(content => { .then(content => {
const extensionIdLowerCase = extensionId.toLowerCase(); const extensionIdLowerCase = extensionId.toLowerCase();
const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value) || {})['extensions'] || {}; const workspaceExtensionsConfigContent: IExtensionsConfigContent = (json.parse(content.value.toString()) || {})['extensions'] || {};
let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || []; let insertInto = shouldRecommend ? workspaceExtensionsConfigContent.recommendations || [] : workspaceExtensionsConfigContent.unwantedRecommendations || [];
let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || []; let removeFrom = shouldRecommend ? workspaceExtensionsConfigContent.unwantedRecommendations || [] : workspaceExtensionsConfigContent.recommendations || [];
@@ -2131,26 +2138,26 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
} }
protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> { protected getWorkspaceExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
return Promise.resolve(this.fileService.resolveContent(extensionsFileResource)) return Promise.resolve(this.fileService.readFile(extensionsFileResource))
.then(content => { .then(content => {
return (json.parse(content.value) || {})['extensions'] || {}; return (json.parse(content.value.toString()) || {})['extensions'] || {};
}, err => ({ recommendations: [], unwantedRecommendations: [] })); }, err => ({ recommendations: [], unwantedRecommendations: [] }));
} }
protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> { protected getWorkspaceFolderExtensionsConfigContent(extensionsFileResource: URI): Promise<IExtensionsConfigContent> {
return Promise.resolve(this.fileService.resolveContent(extensionsFileResource)) return Promise.resolve(this.fileService.readFile(extensionsFileResource))
.then(content => { .then(content => {
return (<IExtensionsConfigContent>json.parse(content.value)); return (<IExtensionsConfigContent>json.parse(content.value.toString()));
}, err => ({ recommendations: [], unwantedRecommendations: [] })); }, err => ({ recommendations: [], unwantedRecommendations: [] }));
} }
private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<IContent> { private getOrUpdateWorkspaceConfigurationFile(workspaceConfigurationFile: URI): Promise<IFileContent> {
return Promise.resolve(this.fileService.resolveContent(workspaceConfigurationFile)) return Promise.resolve(this.fileService.readFile(workspaceConfigurationFile))
.then(content => { .then(content => {
const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value)['extensions']; const workspaceRecommendations = <IExtensionsConfigContent>json.parse(content.value.toString())['extensions'];
if (!workspaceRecommendations || !workspaceRecommendations.recommendations) { if (!workspaceRecommendations || !workspaceRecommendations.recommendations) {
return this.jsonEditingService.write(workspaceConfigurationFile, { key: 'extensions', value: { recommendations: [] } }, true) return this.jsonEditingService.write(workspaceConfigurationFile, { key: 'extensions', value: { recommendations: [] } }, true)
.then(() => this.fileService.resolveContent(workspaceConfigurationFile)); .then(() => this.fileService.readFile(workspaceConfigurationFile));
} }
return content; return content;
}); });
@@ -2179,8 +2186,8 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio
} }
private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean, extensionsFileResource: URI, content: string }> { private getOrCreateExtensionsFile(extensionsFileResource: URI): Promise<{ created: boolean, extensionsFileResource: URI, content: string }> {
return Promise.resolve(this.fileService.resolveContent(extensionsFileResource)).then(content => { return Promise.resolve(this.fileService.readFile(extensionsFileResource)).then(content => {
return { created: false, extensionsFileResource, content: content.value }; return { created: false, extensionsFileResource, content: content.value.toString() };
}, err => { }, err => {
return this.textFileService.write(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => { return this.textFileService.write(extensionsFileResource, ExtensionsConfigurationInitialContent).then(() => {
return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent }; return { created: true, extensionsFileResource, content: ExtensionsConfigurationInitialContent };
@@ -2562,29 +2569,42 @@ export class DisabledLabelAction extends ExtensionAction {
updateWhenCounterExtensionChanges: boolean = true; updateWhenCounterExtensionChanges: boolean = true;
private disposables: IDisposable[] = []; private disposables: IDisposable[] = [];
private _runningExtensions: IExtensionDescription[] | null = null;
constructor( constructor(
private readonly warningAction: SystemDisabledWarningAction, private readonly warningAction: SystemDisabledWarningAction,
@IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService, @IExtensionEnablementService private readonly extensionEnablementService: IExtensionEnablementService,
@IExtensionService private readonly extensionService: IExtensionService,
) { ) {
super('extensions.disabledLabel', warningAction.tooltip, `${DisabledLabelAction.Class} hide`, false); super('extensions.disabledLabel', warningAction.tooltip, `${DisabledLabelAction.Class} hide`, false);
warningAction.onDidChange(() => this.update(), this, this.disposables); warningAction.onDidChange(() => this.update(), this, this.disposables);
this.extensionService.onDidChangeExtensions(this.updateRunningExtensions, this, this.disposables);
this.updateRunningExtensions();
}
private updateRunningExtensions(): void {
this.extensionService.getExtensions().then(runningExtensions => { this._runningExtensions = runningExtensions; this.update(); });
} }
update(): void { update(): void {
this.class = `${DisabledLabelAction.Class} hide`; this.class = `${DisabledLabelAction.Class} hide`;
this.label = ''; this.label = '';
this.enabled = false;
if (this.warningAction.enabled) { if (this.warningAction.enabled) {
this.enabled = true; this.enabled = true;
this.class = DisabledLabelAction.Class; this.class = DisabledLabelAction.Class;
this.label = this.warningAction.tooltip; this.label = this.warningAction.tooltip;
return; return;
} }
if (this.extension && this.extension.local && !this.extensionEnablementService.isEnabled(this.extension.local)) { if (this.extension && this.extension.local && this._runningExtensions) {
this.enabled = true; const isEnabled = this.extensionEnablementService.isEnabled(this.extension.local);
this.class = DisabledLabelAction.Class; const isExtensionRunning = this._runningExtensions.some(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier));
this.label = localize('disabled by user', "This extension is disabled by the user."); if (!isExtensionRunning && !isEnabled) {
return; this.enabled = true;
this.class = DisabledLabelAction.Class;
this.label = localize('disabled by user', "This extension is disabled by the user.");
return;
}
} }
} }
@@ -2630,31 +2650,37 @@ export class SystemDisabledWarningAction extends ExtensionAction {
this.class = `${SystemDisabledWarningAction.Class} hide`; this.class = `${SystemDisabledWarningAction.Class} hide`;
this.tooltip = ''; this.tooltip = '';
if (this.extension && this.extension.local && this.extension.server && this._runningExtensions && this.workbenchEnvironmentService.configuration.remoteAuthority && this.extensionManagementServerService.remoteExtensionManagementServer) { if (this.extension && this.extension.local && this.extension.server && this._runningExtensions && this.workbenchEnvironmentService.configuration.remoteAuthority && this.extensionManagementServerService.remoteExtensionManagementServer) {
if (
// Local Workspace Extension
this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)
) {
this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`;
this.tooltip = localize('disabled workspace Extension', "This extension from {0} server is disabled because it cannot run in a window connected to the remote server.", this.getServerLabel(this.extensionManagementServerService.localExtensionManagementServer));
if (!this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier) && e.server === this.extensionManagementServerService.remoteExtensionManagementServer)
&& this.extensionsWorkbenchService.canInstall(this.extension)
) {
// Extension does not exist in remote
this.tooltip = `${this.tooltip} ${localize('Install in remote server', "Install it in {0} server to enable.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer))}`;
}
return;
}
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0]; const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null; const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null;
if ( const localExtension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
// Not same as running extension const localExtensionServer = localExtension ? localExtension.server : null;
runningExtensionServer && this.extension.server !== runningExtensionServer if (this.extension.server === this.extensionManagementServerService.localExtensionManagementServer && !isUIExtension(this.extension.local.manifest, this.configurationService)) {
) { if (runningExtensionServer === this.extensionManagementServerService.remoteExtensionManagementServer) {
this.enabled = true; this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`; this.class = `${SystemDisabledWarningAction.Class}`;
this.tooltip = localize('disabled because running in another server', "This extension from {0} server is disabled because another instance of same extension from {1} server is enabled.", this.getServerLabel(this.extension.server), this.getServerLabel(runningExtensionServer)); this.tooltip = localize('disabled locally', "Extension is enabled on '{0}' and disabled locally.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
return; return;
}
if (localExtensionServer !== this.extensionManagementServerService.remoteExtensionManagementServer) {
this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`;
this.tooltip = localize('Install in remote server', "Install the extension on '{0}' to enable.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
return;
}
}
if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer && isUIExtension(this.extension.local.manifest, this.configurationService)) {
if (runningExtensionServer === this.extensionManagementServerService.localExtensionManagementServer) {
this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`;
this.tooltip = localize('disabled remotely', "Extension is enabled locally and disabled on '{0}'.", this.getServerLabel(this.extensionManagementServerService.remoteExtensionManagementServer));
return;
}
if (localExtensionServer !== this.extensionManagementServerService.localExtensionManagementServer) {
this.enabled = true;
this.class = `${SystemDisabledWarningAction.Class}`;
this.tooltip = localize('Install in local server', "Install the extension locally to enable.");
return;
}
} }
} }
} }
@@ -2878,7 +2904,7 @@ export class InstallVSIXAction extends Action {
this.extensionsWorkbenchService.install(vsix).then(extension => { this.extensionsWorkbenchService.install(vsix).then(extension => {
const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local))); const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Azure Data Studio to complete installing the extension {0}.", extension.identifier.id) const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Azure Data Studio to complete installing the extension {0}.", extension.identifier.id)
: localize('InstallVSIXAction.success', "Installing the extension {0} is completed.", extension.identifier.id); : localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.identifier.id);
const actions = requireReload ? [{ const actions = requireReload ? [{
label: localize('InstallVSIXAction.reloadNow', "Reload Now"), label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
run: () => this.windowService.reloadWindow() run: () => this.windowService.reloadWindow()
@@ -2911,7 +2937,7 @@ export class InstallVSIXAction extends Action {
this.extensionsWorkbenchService.install(vsix).then(extension => { this.extensionsWorkbenchService.install(vsix).then(extension => {
const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local))); const requireReload = !(extension.local && this.extensionService.canAddExtension(toExtensionDescription(extension.local)));
const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Azure Data Studio to complete installing the extension {0}.", extension.identifier.id) const message = requireReload ? localize('InstallVSIXAction.successReload', "Please reload Azure Data Studio to complete installing the extension {0}.", extension.identifier.id)
: localize('InstallVSIXAction.success', "Installing the extension {0} is completed.", extension.identifier.id); : localize('InstallVSIXAction.success', "Completed installing the extension {0}.", extension.identifier.id);
const actions = requireReload ? [{ const actions = requireReload ? [{
label: localize('InstallVSIXAction.reloadNow', "Reload Now"), label: localize('InstallVSIXAction.reloadNow', "Reload Now"),
run: () => this.windowService.reloadWindow() run: () => this.windowService.reloadWindow()

View File

@@ -66,7 +66,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
const element = append(root, $('.extension')); const element = append(root, $('.extension'));
const iconContainer = append(element, $('.icon-container')); const iconContainer = append(element, $('.icon-container'));
const icon = append(iconContainer, $<HTMLImageElement>('img.icon')); const icon = append(iconContainer, $<HTMLImageElement>('img.icon'));
const badgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer); const iconRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, iconContainer);
const details = append(element, $('.details')); const details = append(element, $('.details'));
const headerContainer = append(details, $('.header-container')); const headerContainer = append(details, $('.header-container'));
const header = append(headerContainer, $('.header')); const header = append(headerContainer, $('.header'));
@@ -74,6 +74,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
const version = append(header, $('span.version')); const version = append(header, $('span.version'));
const installCount = append(header, $('span.install-count')); const installCount = append(header, $('span.install-count'));
const ratings = append(header, $('span.ratings')); const ratings = append(header, $('span.ratings'));
const headerRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, header);
const description = append(details, $('.description.ellipsis')); const description = append(details, $('.description.ellipsis'));
const footer = append(details, $('.footer')); const footer = append(details, $('.footer'));
const author = append(footer, $('.author.ellipsis')); const author = append(footer, $('.author.ellipsis'));
@@ -89,10 +90,11 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
actionbar.onDidRun(({ error }) => error && this.notificationService.error(error)); actionbar.onDidRun(({ error }) => error && this.notificationService.error(error));
const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction); const systemDisabledWarningAction = this.instantiationService.createInstance(SystemDisabledWarningAction);
const reloadAction = this.instantiationService.createInstance(ReloadAction);
const actions = [ const actions = [
this.instantiationService.createInstance(StatusLabelAction), this.instantiationService.createInstance(StatusLabelAction),
this.instantiationService.createInstance(UpdateAction), this.instantiationService.createInstance(UpdateAction),
this.instantiationService.createInstance(ReloadAction), reloadAction,
this.instantiationService.createInstance(InstallAction), this.instantiationService.createInstance(InstallAction),
this.instantiationService.createInstance(RemoteInstallAction), this.instantiationService.createInstance(RemoteInstallAction),
this.instantiationService.createInstance(MaliciousStatusLabelAction, false), this.instantiationService.createInstance(MaliciousStatusLabelAction, false),
@@ -100,10 +102,11 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
this.instantiationService.createInstance(ManageExtensionAction) this.instantiationService.createInstance(ManageExtensionAction)
]; ];
const disabledLabelAction = this.instantiationService.createInstance(DisabledLabelAction, systemDisabledWarningAction); const disabledLabelAction = this.instantiationService.createInstance(DisabledLabelAction, systemDisabledWarningAction);
const tooltipWidget = this.instantiationService.createInstance(TooltipWidget, root, disabledLabelAction, recommendationWidget); const tooltipWidget = this.instantiationService.createInstance(TooltipWidget, root, disabledLabelAction, recommendationWidget, reloadAction);
const widgets = [ const widgets = [
recommendationWidget, recommendationWidget,
badgeWidget, iconRemoteBadgeWidget,
headerRemoteBadgeWidget,
tooltipWidget, tooltipWidget,
this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version), this.instantiationService.createInstance(Label, version, (e: IExtension) => e.version),
this.instantiationService.createInstance(InstallCountWidget, installCount, true), this.instantiationService.createInstance(InstallCountWidget, installCount, true),

View File

@@ -11,13 +11,14 @@ import * as platform from 'vs/base/common/platform';
import { localize } from 'vs/nls'; import { localize } from 'vs/nls';
import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement';
import { ILabelService } from 'vs/platform/label/common/label'; import { ILabelService } from 'vs/platform/label/common/label';
import { extensionButtonProminentBackground, extensionButtonProminentForeground, DisabledLabelAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; import { extensionButtonProminentBackground, extensionButtonProminentForeground, DisabledLabelAction, ReloadAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions';
import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService'; import { IThemeService, ITheme } from 'vs/platform/theme/common/themeService';
import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme'; import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_FOREGROUND } from 'vs/workbench/common/theme';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { export abstract class ExtensionWidget extends Disposable implements IExtensionContainer {
private _extension: IExtension; private _extension: IExtension;
@@ -148,11 +149,15 @@ export class TooltipWidget extends ExtensionWidget {
constructor( constructor(
private readonly parent: HTMLElement, private readonly parent: HTMLElement,
private readonly extensionLabelAction: DisabledLabelAction, private readonly extensionLabelAction: DisabledLabelAction,
private readonly recommendationWidget: RecommendationWidget private readonly recommendationWidget: RecommendationWidget,
private readonly reloadAction: ReloadAction
) { ) {
super(); super();
this._register(this.extensionLabelAction.onDidChange(() => this.render())); this._register(Event.any<any>(
this._register(this.recommendationWidget.onDidChangeTooltip(() => this.render())); this.extensionLabelAction.onDidChange,
this.reloadAction.onDidChange,
this.recommendationWidget.onDidChangeTooltip
)(() => this.render()));
} }
render(): void { render(): void {
@@ -166,6 +171,9 @@ export class TooltipWidget extends ExtensionWidget {
} }
private getTitle(): string { private getTitle(): string {
if (this.reloadAction.enabled) {
return this.reloadAction.tooltip;
}
if (this.extensionLabelAction.enabled) { if (this.extensionLabelAction.enabled) {
return this.extensionLabelAction.label; return this.extensionLabelAction.label;
} }
@@ -235,30 +243,30 @@ export class RecommendationWidget extends ExtensionWidget {
} }
export class RemoteBadgeWidget extends ExtensionWidget { export class RemoteBadgeWidget extends ExtensionWidget {
private element: HTMLElement | null; private remoteBadge: RemoteBadge | null;
private disposables: IDisposable[] = []; private disposables: IDisposable[] = [];
private element: HTMLElement;
constructor( constructor(
private parent: HTMLElement, parent: HTMLElement,
@ILabelService private readonly labelService: ILabelService,
@IThemeService private readonly themeService: IThemeService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IInstantiationService private readonly instantiationService: IInstantiationService
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) { ) {
super(); super();
this.element = append(parent, $('.extension-remote-badge-container'));
this.render(); this.render();
this._register(toDisposable(() => this.clear())); this._register(toDisposable(() => this.clear()));
} }
private clear(): void { private clear(): void {
if (this.element) { if (this.remoteBadge) {
this.parent.removeChild(this.element); this.element.removeChild(this.remoteBadge.element);
this.remoteBadge.dispose();
} }
this.element = null; this.remoteBadge = null;
this.disposables = dispose(this.disposables); this.disposables = dispose(this.disposables);
} }
@@ -268,30 +276,56 @@ export class RemoteBadgeWidget extends ExtensionWidget {
return; return;
} }
if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) { if (this.extension.server === this.extensionManagementServerService.remoteExtensionManagementServer) {
this.element = append(this.parent, $('div.extension-remote-badge')); this.remoteBadge = this.instantiationService.createInstance(RemoteBadge);
append(this.element, $('span.octicon.octicon-remote')); append(this.element, this.remoteBadge.element);
const applyBadgeStyle = () => {
if (!this.element) {
return;
}
const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND);
const fgColor = this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY ? this.themeService.getTheme().getColor(STATUS_BAR_NO_FOLDER_FOREGROUND) : this.themeService.getTheme().getColor(STATUS_BAR_FOREGROUND);
this.element.style.backgroundColor = bgColor ? bgColor.toString() : '';
this.element.style.color = fgColor ? fgColor.toString() : '';
};
applyBadgeStyle();
this.themeService.onThemeChange(applyBadgeStyle, this, this.disposables);
this.workspaceContextService.onDidChangeWorkbenchState(applyBadgeStyle, this, this.disposables);
const updateTitle = () => {
if (this.element) {
this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority));
}
};
this.labelService.onDidChangeFormatters(() => updateTitle(), this, this.disposables);
updateTitle();
} }
} }
dispose(): void {
if (this.remoteBadge) {
this.remoteBadge.dispose();
}
super.dispose();
}
}
class RemoteBadge extends Disposable {
readonly element: HTMLElement;
constructor(
@ILabelService private readonly labelService: ILabelService,
@IThemeService private readonly themeService: IThemeService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService
) {
super();
this.element = $('div.extension-remote-badge');
this.render();
}
private render(): void {
append(this.element, $('span.octicon.octicon-remote'));
const applyBadgeStyle = () => {
if (!this.element) {
return;
}
const bgColor = this.themeService.getTheme().getColor(STATUS_BAR_HOST_NAME_BACKGROUND);
const fgColor = this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY ? this.themeService.getTheme().getColor(STATUS_BAR_NO_FOLDER_FOREGROUND) : this.themeService.getTheme().getColor(STATUS_BAR_FOREGROUND);
this.element.style.backgroundColor = bgColor ? bgColor.toString() : '';
this.element.style.color = fgColor ? fgColor.toString() : '';
};
applyBadgeStyle();
this._register(this.themeService.onThemeChange(() => applyBadgeStyle()));
this._register(this.workspaceContextService.onDidChangeWorkbenchState(() => applyBadgeStyle()));
const updateTitle = () => {
if (this.element) {
this.element.title = localize('remote extension title', "Extension in {0}", this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority));
}
};
this._register(this.labelService.onDidChangeFormatters(() => updateTitle()));
updateTitle();
}
} }

View File

@@ -35,7 +35,17 @@
.extension-editor > .header > .icon-container .extension-remote-badge { .extension-editor > .header > .icon-container .extension-remote-badge {
position: absolute; position: absolute;
right: 0px; right: 0px;
top: 94px; top: 88px;
width: 38px;
height: 38px;
line-height: 38px;
border-radius: 20px;
text-align: center;
}
.extension-editor > .header > .icon-container .extension-remote-badge .octicon {
font-size: 32px;
vertical-align: middle;
} }
.extension-editor > .header > .details { .extension-editor > .header > .details {

View File

@@ -102,10 +102,32 @@
object-fit: contain; object-fit: contain;
} }
.extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container > .extension-remote-badge { .extensions-viewlet > .extensions .monaco-list-row > .extension > .icon-container .extension-remote-badge {
position: absolute; position: absolute;
right: 5px; right: 5px;
bottom: 5px; bottom: 5px;
width: 22px;
height: 22px;
line-height: 22px;
border-radius: 20px;
text-align: center;
}
.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header > .extension-remote-badge-container {
margin-left: 6px;
}
.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header .extension-remote-badge {
width: 14px;
height: 14px;
line-height: 14px;
border-radius: 20px;
text-align: center;
}
.extensions-viewlet > .extensions .monaco-list-row > .extension > .details > .header-container > .header .extension-remote-badge > .octicon {
font-size: 13px;
vertical-align: middle;
} }
.extensions-viewlet.narrow > .extensions .extension > .icon-container, .extensions-viewlet.narrow > .extensions .extension > .icon-container,
@@ -146,10 +168,13 @@
opacity: 0.85; opacity: 0.85;
font-size: 80%; font-size: 80%;
padding-left: 6px; padding-left: 6px;
flex: 1;
min-width: fit-content; min-width: fit-content;
} }
.extensions-viewlet:not(.narrow) > .extensions .extension > .details > .header-container > .header > .version {
flex: 1;
}
.extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count:not(:empty) { .extensions-viewlet > .extensions .extension > .details > .header-container > .header > .install-count:not(:empty) {
font-size: 80%; font-size: 80%;
margin: 0 6px; margin: 0 6px;
@@ -164,6 +189,7 @@
text-align: right; text-align: right;
} }
.extensions-viewlet:not(.narrow) > .extensions .extension > .details > .header-container > .header > .extension-remote-badge-container,
.extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .ratings, .extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .ratings,
.extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .install-count { .extensions-viewlet.narrow > .extensions .extension > .details > .header-container > .header > .install-count {
display: none; display: none;

View File

@@ -47,11 +47,3 @@
.extension-ratings.small > .count { .extension-ratings.small > .count {
margin-left: 2px; margin-left: 2px;
} }
.extension-remote-badge {
width: 22px;
height: 22px;
line-height: 22px;
border-radius: 20px;
text-align: center;
}

View File

@@ -254,7 +254,7 @@ class Extension implements IExtension {
} }
if (this.local && this.local.readmeUrl) { if (this.local && this.local.readmeUrl) {
return this.fileService.resolveContent(this.local.readmeUrl, { encoding: 'utf8' }).then(content => content.value); return this.fileService.readFile(this.local.readmeUrl).then(content => content.value.toString());
} }
if (this.type === ExtensionType.System) { if (this.type === ExtensionType.System) {
@@ -297,7 +297,7 @@ ${this.description}
return Promise.reject(new Error('not available')); return Promise.reject(new Error('not available'));
} }
return this.fileService.resolveContent(changelogUrl, { encoding: 'utf8' }).then(content => content.value); return this.fileService.readFile(changelogUrl).then(content => content.value.toString());
} }
get dependencies(): string[] { get dependencies(): string[] {

View File

@@ -21,12 +21,11 @@ import { Emitter } from 'vs/base/common/event';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
import { TestTextResourceConfigurationService, TestContextService, TestLifecycleService, TestEnvironmentService, TestStorageService, TestSharedProcessService } from 'vs/workbench/test/workbenchTestServices'; import { TestContextService, TestLifecycleService, TestSharedProcessService } from 'vs/workbench/test/workbenchTestServices';
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService'; import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { testWorkspace } from 'vs/platform/workspace/test/common/testWorkspace';
import { LegacyFileService } from 'vs/workbench/services/files/node/fileService';
import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService';
import { IPager } from 'vs/base/common/paging'; import { IPager } from 'vs/base/common/paging';
import { assign } from 'vs/base/common/objects'; import { assign } from 'vs/base/common/objects';
@@ -47,6 +46,11 @@ import { TestExperimentService } from 'vs/workbench/contrib/experiments/test/ele
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ExtensionType } from 'vs/platform/extensions/common/extensions';
import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService';
import { FileService } from 'vs/workbench/services/files/common/fileService';
import { NullLogService } from 'vs/platform/log/common/log';
import { Schemas } from 'vs/base/common/network';
import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider';
import { IFileService } from 'vs/platform/files/common/files';
const mockExtensionGallery: IGalleryExtension[] = [ const mockExtensionGallery: IGalleryExtension[] = [
aGalleryExtension('MockExtension1', { aGalleryExtension('MockExtension1', {

View File

@@ -12,9 +12,9 @@ import { EditorInput, EditorOptions } from 'vs/workbench/common/editor';
import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files'; import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/contrib/files/common/files';
import { IFileService } from 'vs/platform/files/common/files';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IStorageService } from 'vs/platform/storage/common/storage'; import { IStorageService } from 'vs/platform/storage/common/storage';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
/** /**
* An implementation of editor for binary files like images. * An implementation of editor for binary files like images.
@@ -26,10 +26,10 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
constructor( constructor(
@ITelemetryService telemetryService: ITelemetryService, @ITelemetryService telemetryService: ITelemetryService,
@IThemeService themeService: IThemeService, @IThemeService themeService: IThemeService,
@IFileService fileService: IFileService,
@IWindowsService private readonly windowsService: IWindowsService, @IWindowsService private readonly windowsService: IWindowsService,
@IEditorService private readonly editorService: IEditorService, @IEditorService private readonly editorService: IEditorService,
@IStorageService storageService: IStorageService @IStorageService storageService: IStorageService,
@ITextFileService textFileService: ITextFileService
) { ) {
super( super(
BinaryFileEditor.ID, BinaryFileEditor.ID,
@@ -39,7 +39,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
}, },
telemetryService, telemetryService,
themeService, themeService,
fileService, textFileService,
storageService storageService
); );
} }

View File

@@ -10,7 +10,7 @@ import { isValidBasename } from 'vs/base/common/extpath';
import { basename } from 'vs/base/common/resources'; import { basename } from 'vs/base/common/resources';
import { Action } from 'vs/base/common/actions'; import { Action } from 'vs/base/common/actions';
import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { VIEWLET_ID, TEXT_FILE_EDITOR_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileEditorModel, ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor'; import { BaseTextEditor, IEditorConfiguration } from 'vs/workbench/browser/parts/editor/textEditor';
import { EditorOptions, TextEditorOptions, IEditorCloseEvent } from 'vs/workbench/common/editor'; import { EditorOptions, TextEditorOptions, IEditorCloseEvent } from 'vs/workbench/common/editor';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
@@ -170,7 +170,7 @@ export class TextFileEditor extends BaseTextEditor {
// In case we tried to open a file inside the text editor and the response // In case we tried to open a file inside the text editor and the response
// indicates that this is not a text file, reopen the file through the binary // indicates that this is not a text file, reopen the file through the binary
// editor. // editor.
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY) { if ((<TextFileOperationError>error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY) {
return this.openAsBinary(input, options); return this.openAsBinary(input, options);
} }

View File

@@ -13,7 +13,7 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IEditorInputFactory, EditorInput, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor'; import { IEditorInputFactory, EditorInput, IFileEditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions } from 'vs/workbench/common/editor';
import { AutoSaveConfiguration, HotExitConfiguration, SUPPORTED_ENCODINGS } from 'vs/platform/files/common/files'; import { AutoSaveConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files';
import { VIEWLET_ID, SortOrderConfiguration, FILE_EDITOR_INPUT_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files'; import { VIEWLET_ID, SortOrderConfiguration, FILE_EDITOR_INPUT_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { FileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/fileEditorTracker'; import { FileEditorTracker } from 'vs/workbench/contrib/files/browser/editors/fileEditorTracker';
import { SaveErrorHandler } from 'vs/workbench/contrib/files/browser/saveErrorHandler'; import { SaveErrorHandler } from 'vs/workbench/contrib/files/browser/saveErrorHandler';
@@ -37,6 +37,7 @@ import { ILabelService } from 'vs/platform/label/common/label';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { ExplorerService } from 'vs/workbench/contrib/files/common/explorerService'; import { ExplorerService } from 'vs/workbench/contrib/files/common/explorerService';
import { SUPPORTED_ENCODINGS } from 'vs/workbench/services/textfile/common/textfiles';
// Viewlet Action // Viewlet Action
export class OpenExplorerViewletAction extends ShowViewletAction { export class OpenExplorerViewletAction extends ShowViewletAction {

View File

@@ -9,7 +9,7 @@ import { basename } from 'vs/base/common/resources';
import { Action } from 'vs/base/common/actions'; import { Action } from 'vs/base/common/actions';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService, ISaveErrorHandler, ITextFileEditorModel, IResolvedTextFileEditorModel, IWriteTextFileOptions } from 'vs/workbench/services/textfile/common/textfiles';
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle'; import { IDisposable, dispose, Disposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions';
@@ -134,7 +134,7 @@ export class SaveErrorHandler extends Disposable implements ISaveErrorHandler, I
// Any other save error // Any other save error
else { else {
const isReadonly = fileOperationError.fileOperationResult === FileOperationResult.FILE_READ_ONLY; const isReadonly = fileOperationError.fileOperationResult === FileOperationResult.FILE_READ_ONLY;
const triedToMakeWriteable = isReadonly && fileOperationError.options && fileOperationError.options.overwriteReadonly; const triedToMakeWriteable = isReadonly && fileOperationError.options && (fileOperationError.options as IWriteTextFileOptions).overwriteReadonly;
const isPermissionDenied = fileOperationError.fileOperationResult === FileOperationResult.FILE_PERMISSION_DENIED; const isPermissionDenied = fileOperationError.fileOperationResult === FileOperationResult.FILE_PERMISSION_DENIED;
// Save Elevated (TODO@remote cannot write elevated https://github.com/Microsoft/vscode/issues/48659) // Save Elevated (TODO@remote cannot write elevated https://github.com/Microsoft/vscode/issues/48659)

View File

@@ -12,7 +12,7 @@ import { EncodingMode, ConfirmResult, EditorInput, IFileEditorInput, ITextEditor
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files';
import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent, LoadReason } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService, AutoSaveMode, ModelState, TextFileModelChangeEvent, LoadReason, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IReference } from 'vs/base/common/lifecycle'; import { IReference } from 'vs/base/common/lifecycle';
import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ITextModelService } from 'vs/editor/common/services/resolverService';
@@ -269,7 +269,10 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
}, error => { }, error => {
// In case of an error that indicates that the file is binary or too large, just return with the binary editor model // In case of an error that indicates that the file is binary or too large, just return with the binary editor model
if ((<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_IS_BINARY || (<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE) { if (
(<TextFileOperationError>error).textFileOperationResult === TextFileOperationResult.FILE_IS_BINARY ||
(<FileOperationError>error).fileOperationResult === FileOperationResult.FILE_TOO_LARGE
) {
return this.doResolveAsBinary(); return this.doResolveAsBinary();
} }

View File

@@ -176,7 +176,7 @@ export class FileOnDiskContentProvider implements ITextModelContentProvider {
private resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> { private resolveEditorModel(resource: URI, createAsNeeded: boolean = true): Promise<ITextModel | null> {
const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority); const savedFileResource = toLocalResource(resource, this.environmentService.configuration.remoteAuthority);
return this.textFileService.resolve(savedFileResource).then(content => { return this.textFileService.readStream(savedFileResource).then(content => {
let codeEditorModel = this.modelService.getModel(resource); let codeEditorModel = this.modelService.getModel(resource);
if (codeEditorModel) { if (codeEditorModel) {
this.modelService.updateModel(codeEditorModel, content.value); this.modelService.updateModel(codeEditorModel, content.value);

View File

@@ -11,7 +11,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices'; import { workbenchInstantiationService, TestTextFileService } from 'vs/workbench/test/workbenchTestServices';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { EncodingMode, Verbosity } from 'vs/workbench/common/editor'; import { EncodingMode, Verbosity } from 'vs/workbench/common/editor';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService, TextFileOperationError, TextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles';
import { FileOperationResult, FileOperationError } from 'vs/platform/files/common/files'; import { FileOperationResult, FileOperationError } from 'vs/platform/files/common/files';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel'; import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { IModelService } from 'vs/editor/common/services/modelService'; import { IModelService } from 'vs/editor/common/services/modelService';
@@ -155,7 +155,7 @@ suite('Files - FileEditorInput', () => {
test('resolve handles binary files', function () { test('resolve handles binary files', function () {
const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined); const input = instantiationService.createInstance(FileEditorInput, toResource.call(this, '/foo/bar/updatefile.js'), undefined);
accessor.textFileService.setResolveTextContentErrorOnce(new FileOperationError('error', FileOperationResult.FILE_IS_BINARY)); accessor.textFileService.setResolveTextContentErrorOnce(new TextFileOperationError('error', TextFileOperationResult.FILE_IS_BINARY));
return input.resolve().then(resolved => { return input.resolve().then(resolved => {
assert.ok(resolved); assert.ok(resolved);

View File

@@ -9,8 +9,8 @@ import { toResource } from 'vs/base/test/common/utils';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { workbenchInstantiationService, TestTextFileService, TestFileService } from 'vs/workbench/test/workbenchTestServices'; import { workbenchInstantiationService, TestTextFileService, TestFileService } from 'vs/workbench/test/workbenchTestServices';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ITextFileService, IResolvedTextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles'; import { ITextFileService, IResolvedTextFileEditorModel, snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
import { FileChangesEvent, FileChangeType, IFileService, snapshotToString } from 'vs/platform/files/common/files'; import { FileChangesEvent, FileChangeType, IFileService } from 'vs/platform/files/common/files';
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
import { timeout } from 'vs/base/common/async'; import { timeout } from 'vs/base/common/async';

View File

@@ -511,20 +511,20 @@ export class SettingsTargetsWidget extends Widget {
actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : undefined actionItemProvider: (action: Action) => action.id === 'folderSettings' ? this.folderSettings : undefined
})); }));
this.userLocalSettings = new Action('userSettings', localize('userSettings', "User Settings"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL)); this.userLocalSettings = new Action('userSettings', localize('userSettings', "User"), '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_LOCAL));
this.userLocalSettings.tooltip = this.userLocalSettings.label; this.userLocalSettings.tooltip = this.userLocalSettings.label;
const remoteAuthority = this.environmentService.configuration.remoteAuthority; const remoteAuthority = this.environmentService.configuration.remoteAuthority;
const hostLabel = remoteAuthority && this.labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAuthority); const hostLabel = remoteAuthority && this.labelService.getHostLabel(REMOTE_HOST_SCHEME, remoteAuthority);
const remoteSettingsLabel = localize('userSettingsRemote', "Remote Settings") + const remoteSettingsLabel = localize('userSettingsRemote', "Remote") +
(hostLabel ? ` (${hostLabel})` : ''); (hostLabel ? ` (${hostLabel})` : '');
this.userRemoteSettings = new Action('userSettingsRemote', remoteSettingsLabel, '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_REMOTE)); this.userRemoteSettings = new Action('userSettingsRemote', remoteSettingsLabel, '.settings-tab', true, () => this.updateTarget(ConfigurationTarget.USER_REMOTE));
this.userRemoteSettings.tooltip = this.userRemoteSettings.label; this.userRemoteSettings.tooltip = this.userRemoteSettings.label;
this.workspaceSettings = new Action('workspaceSettings', localize('workspaceSettings', "Workspace Settings"), '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE)); this.workspaceSettings = new Action('workspaceSettings', localize('workspaceSettings', "Workspace"), '.settings-tab', false, () => this.updateTarget(ConfigurationTarget.WORKSPACE));
this.workspaceSettings.tooltip = this.workspaceSettings.label; this.workspaceSettings.tooltip = this.workspaceSettings.label;
const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder Settings"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder.uri)); const folderSettingsAction = new Action('folderSettings', localize('folderSettings', "Folder"), '.settings-tab', false, (folder: IWorkspaceFolder) => this.updateTarget(folder.uri));
this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionItem, folderSettingsAction); this.folderSettings = this.instantiationService.createInstance(FolderSettingsActionItem, folderSettingsAction);
this.update(); this.update();
@@ -551,14 +551,14 @@ export class SettingsTargetsWidget extends Widget {
setResultCount(settingsTarget: SettingsTarget, count: number): void { setResultCount(settingsTarget: SettingsTarget, count: number): void {
if (settingsTarget === ConfigurationTarget.WORKSPACE) { if (settingsTarget === ConfigurationTarget.WORKSPACE) {
let label = localize('workspaceSettings', "Workspace Settings"); let label = localize('workspaceSettings', "Workspace");
if (count) { if (count) {
label += ` (${count})`; label += ` (${count})`;
} }
this.workspaceSettings.label = label; this.workspaceSettings.label = label;
} else if (settingsTarget === ConfigurationTarget.USER_LOCAL) { } else if (settingsTarget === ConfigurationTarget.USER_LOCAL) {
let label = localize('userSettings', "User Settings"); let label = localize('userSettings', "User");
if (count) { if (count) {
label += ` (${count})`; label += ` (${count})`;
} }

View File

@@ -11,7 +11,7 @@ import { TestConfigurationService } from 'vs/platform/configuration/test/common/
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock';
import { IFolderQuery, IPatternInfo, QueryType, ITextQuery, IFileQuery } from 'vs/workbench/services/search/common/search'; import { IFolderQuery, IPatternInfo, QueryType, ITextQuery, IFileQuery } from 'vs/workbench/services/search/common/search';
import { IWorkspaceContextService, toWorkspaceFolders, Workspace } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService, toWorkspaceFolder, Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { ISearchPathsInfo, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { ISearchPathsInfo, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder';
import { TestContextService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices'; import { TestContextService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
@@ -24,6 +24,7 @@ suite('QueryBuilder', () => {
const PATTERN_INFO: IPatternInfo = { pattern: 'a' }; const PATTERN_INFO: IPatternInfo = { pattern: 'a' };
const ROOT_1 = fixPath('/foo/root1'); const ROOT_1 = fixPath('/foo/root1');
const ROOT_1_URI = getUri(ROOT_1); const ROOT_1_URI = getUri(ROOT_1);
const WS_CONFIG_PATH = getUri('/bar/test.code-workspace'); // location of the workspace file (not important except that it is a file URI)
let instantiationService: TestInstantiationService; let instantiationService: TestInstantiationService;
let queryBuilder: QueryBuilder; let queryBuilder: QueryBuilder;
@@ -40,7 +41,7 @@ suite('QueryBuilder', () => {
instantiationService.stub(IConfigurationService, mockConfigService); instantiationService.stub(IConfigurationService, mockConfigService);
mockContextService = new TestContextService(); mockContextService = new TestContextService();
mockWorkspace = new Workspace('workspace', toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }])); mockWorkspace = new Workspace('workspace', [toWorkspaceFolder(ROOT_1_URI)]);
mockContextService.setWorkspace(mockWorkspace); mockContextService.setWorkspace(mockWorkspace);
instantiationService.stub(IWorkspaceContextService, mockContextService); instantiationService.stub(IWorkspaceContextService, mockContextService);
@@ -277,7 +278,7 @@ suite('QueryBuilder', () => {
const ROOT_2_URI = getUri(ROOT_2); const ROOT_2_URI = getUri(ROOT_2);
const ROOT_3 = fixPath('/project/root3'); const ROOT_3 = fixPath('/project/root3');
const ROOT_3_URI = getUri(ROOT_3); const ROOT_3_URI = getUri(ROOT_3);
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: ROOT_2_URI.fsPath }, { path: ROOT_3_URI.fsPath }]); mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: ROOT_2_URI.fsPath }, { path: ROOT_3_URI.fsPath }], WS_CONFIG_PATH);
mockWorkspace.configuration = uri.file(fixPath('/config')); mockWorkspace.configuration = uri.file(fixPath('/config'));
mockConfigService.setUserConfiguration('search', { mockConfigService.setUserConfiguration('search', {
@@ -689,7 +690,7 @@ suite('QueryBuilder', () => {
test('relative includes w/two root folders', () => { test('relative includes w/two root folders', () => {
const ROOT_2 = '/project/root2'; const ROOT_2 = '/project/root2';
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }]); mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }], WS_CONFIG_PATH);
mockWorkspace.configuration = uri.file(fixPath('config')); mockWorkspace.configuration = uri.file(fixPath('config'));
const cases: [string, ISearchPathsInfo][] = [ const cases: [string, ISearchPathsInfo][] = [
@@ -730,7 +731,7 @@ suite('QueryBuilder', () => {
test('include ./foldername', () => { test('include ./foldername', () => {
const ROOT_2 = '/project/root2'; const ROOT_2 = '/project/root2';
const ROOT_1_FOLDERNAME = 'foldername'; const ROOT_1_FOLDERNAME = 'foldername';
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath, name: ROOT_1_FOLDERNAME }, { path: getUri(ROOT_2).fsPath }]); mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath, name: ROOT_1_FOLDERNAME }, { path: getUri(ROOT_2).fsPath }], WS_CONFIG_PATH);
mockWorkspace.configuration = uri.file(fixPath('config')); mockWorkspace.configuration = uri.file(fixPath('config'));
const cases: [string, ISearchPathsInfo][] = [ const cases: [string, ISearchPathsInfo][] = [
@@ -758,7 +759,7 @@ suite('QueryBuilder', () => {
test('relative includes w/multiple ambiguous root folders', () => { test('relative includes w/multiple ambiguous root folders', () => {
const ROOT_2 = '/project/rootB'; const ROOT_2 = '/project/rootB';
const ROOT_3 = '/otherproject/rootB'; const ROOT_3 = '/otherproject/rootB';
mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }, { path: getUri(ROOT_3).fsPath }]); mockWorkspace.folders = toWorkspaceFolders([{ path: ROOT_1_URI.fsPath }, { path: getUri(ROOT_2).fsPath }, { path: getUri(ROOT_3).fsPath }], WS_CONFIG_PATH);
mockWorkspace.configuration = uri.file(fixPath('/config')); mockWorkspace.configuration = uri.file(fixPath('/config'));
const cases: [string, ISearchPathsInfo][] = [ const cases: [string, ISearchPathsInfo][] = [

View File

@@ -198,7 +198,7 @@ export class SnippetFile {
load(): Promise<this> { load(): Promise<this> {
if (!this._loadPromise) { if (!this._loadPromise) {
this._loadPromise = Promise.resolve(this._fileService.resolveContent(this.location, { encoding: 'utf8' })).then(content => { this._loadPromise = Promise.resolve(this._fileService.readFile(this.location)).then(content => {
const data = <JsonSerializedSnippets>jsonParse(content.value.toString()); const data = <JsonSerializedSnippets>jsonParse(content.value.toString());
if (typeof data === 'object') { if (typeof data === 'object') {
forEach(data, entry => { forEach(data, entry => {

View File

@@ -7,7 +7,7 @@ import { localize } from 'vs/nls';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
import { onUnexpectedError } from 'vs/base/common/errors'; import { onUnexpectedError } from 'vs/base/common/errors';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IFileService, IFileStat, IResolveFileResult, IContent } from 'vs/platform/files/common/files'; import { IFileService, IFileStat, IResolveFileResult } from 'vs/platform/files/common/files';
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
@@ -20,6 +20,7 @@ import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspa
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { joinPath } from 'vs/base/common/resources'; import { joinPath } from 'vs/base/common/resources';
import { ITextFileService, ITextFileContent } from 'vs/workbench/services/textfile/common/textfiles';
const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/; const SshProtocolMatcher = /^([^@:]+@)?([^:]+):/;
const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/; const SshUrlMatcher = /^([^@:]+@)?([^:]+):(.+)$/;
@@ -193,14 +194,14 @@ export function getHashedRemotesFromConfig(text: string, stripEndingDotGit: bool
}); });
} }
export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, stripEndingDotGit: boolean = false): Promise<string[]> { export function getHashedRemotesFromUri(workspaceUri: URI, fileService: IFileService, textFileService: ITextFileService, stripEndingDotGit: boolean = false): Promise<string[]> {
const path = workspaceUri.path; const path = workspaceUri.path;
const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` }); const uri = workspaceUri.with({ path: `${path !== '/' ? path : ''}/.git/config` });
return fileService.exists(uri).then(exists => { return fileService.exists(uri).then(exists => {
if (!exists) { if (!exists) {
return []; return [];
} }
return fileService.resolveContent(uri, { acceptTextOnly: true }).then( return textFileService.read(uri, { acceptTextOnly: true }).then(
content => getHashedRemotesFromConfig(content.value, stripEndingDotGit), content => getHashedRemotesFromConfig(content.value, stripEndingDotGit),
err => [] // ignore missing or binary file err => [] // ignore missing or binary file
); );
@@ -221,7 +222,8 @@ export class WorkspaceStats implements IWorkbenchContribution {
@IWindowService private readonly windowService: IWindowService, @IWindowService private readonly windowService: IWindowService,
@INotificationService private readonly notificationService: INotificationService, @INotificationService private readonly notificationService: INotificationService,
@IQuickInputService private readonly quickInputService: IQuickInputService, @IQuickInputService private readonly quickInputService: IQuickInputService,
@IStorageService private readonly storageService: IStorageService @IStorageService private readonly storageService: IStorageService,
@ITextFileService private readonly textFileService: ITextFileService
) { ) {
this.report(); this.report();
} }
@@ -434,7 +436,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
tags['workspace.android.cpp'] = true; tags['workspace.android.cpp'] = true;
} }
function getFilePromises(filename: string, fileService: IFileService, contentHandler: (content: IContent) => void): Promise<void>[] { function getFilePromises(filename: string, fileService: IFileService, textFileService: ITextFileService, contentHandler: (content: ITextFileContent) => void): Promise<void>[] {
return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => { return !nameSet.has(filename) ? [] : (folders as URI[]).map(workspaceUri => {
const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` }); const uri = workspaceUri.with({ path: `${workspaceUri.path !== '/' ? workspaceUri.path : ''}/${filename}` });
return fileService.exists(uri).then(exists => { return fileService.exists(uri).then(exists => {
@@ -442,7 +444,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
return undefined; return undefined;
} }
return fileService.resolveContent(uri, { acceptTextOnly: true }).then(contentHandler); return textFileService.read(uri, { acceptTextOnly: true }).then(contentHandler);
}, err => { }, err => {
// Ignore missing file // Ignore missing file
}); });
@@ -468,7 +470,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
} }
} }
const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, content => { const requirementsTxtPromises = getFilePromises('requirements.txt', this.fileService, this.textFileService, content => {
const dependencies: string[] = content.value.split(/\r\n|\r|\n/); const dependencies: string[] = content.value.split(/\r\n|\r|\n/);
for (let dependency of dependencies) { for (let dependency of dependencies) {
// Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo` // Dependencies in requirements.txt can have 3 formats: `foo==3.1, foo>=3.1, foo`
@@ -479,7 +481,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
} }
}); });
const pipfilePromises = getFilePromises('pipfile', this.fileService, content => { const pipfilePromises = getFilePromises('pipfile', this.fileService, this.textFileService, content => {
let dependencies: string[] = content.value.split(/\r\n|\r|\n/); let dependencies: string[] = content.value.split(/\r\n|\r|\n/);
// We're only interested in the '[packages]' section of the Pipfile // We're only interested in the '[packages]' section of the Pipfile
@@ -499,7 +501,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
}); });
const packageJsonPromises = getFilePromises('package.json', this.fileService, content => { const packageJsonPromises = getFilePromises('package.json', this.fileService, this.textFileService, content => {
try { try {
const packageJsonContents = JSON.parse(content.value); const packageJsonContents = JSON.parse(content.value);
if (packageJsonContents['dependencies']) { if (packageJsonContents['dependencies']) {
@@ -624,7 +626,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
if (!exists) { if (!exists) {
return []; return [];
} }
return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then( return this.textFileService.read(uri, { acceptTextOnly: true }).then(
content => getDomainsOfRemotes(content.value, SecondLevelDomainWhitelist), content => getDomainsOfRemotes(content.value, SecondLevelDomainWhitelist),
err => [] // ignore missing or binary file err => [] // ignore missing or binary file
); );
@@ -644,7 +646,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
private reportRemotes(workspaceUris: URI[]): void { private reportRemotes(workspaceUris: URI[]): void {
Promise.all<string[]>(workspaceUris.map(workspaceUri => { Promise.all<string[]>(workspaceUris.map(workspaceUri => {
return getHashedRemotesFromUri(workspaceUri, this.fileService, true); return getHashedRemotesFromUri(workspaceUri, this.fileService, this.textFileService, true);
})).then(hashedRemotes => { })).then(hashedRemotes => {
/* __GDPR__ /* __GDPR__
"workspace.hashedRemotes" : { "workspace.hashedRemotes" : {
@@ -693,7 +695,7 @@ export class WorkspaceStats implements IWorkbenchContribution {
if (!exists) { if (!exists) {
return false; return false;
} }
return this.fileService.resolveContent(uri, { acceptTextOnly: true }).then( return this.textFileService.read(uri, { acceptTextOnly: true }).then(
content => !!content.value.match(/azure/i), content => !!content.value.match(/azure/i),
err => false err => false
); );

View File

@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls'; import * as nls from 'vs/nls';
import * as path from 'vs/base/common/path';
import * as platform from 'vs/base/common/platform'; import * as platform from 'vs/base/common/platform';
import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -14,6 +13,7 @@ import Severity from 'vs/base/common/severity';
import { Terminal as XTermTerminal } from 'vscode-xterm'; import { Terminal as XTermTerminal } from 'vscode-xterm';
import { INotificationService } from 'vs/platform/notification/common/notification'; import { INotificationService } from 'vs/platform/notification/common/notification';
import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IBrowserTerminalConfigHelper } from 'vs/workbench/contrib/terminal/browser/terminal';
import { mergeDefaultShellPathAndArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
const MINIMUM_FONT_SIZE = 6; const MINIMUM_FONT_SIZE = 6;
const MAXIMUM_FONT_SIZE = 25; const MAXIMUM_FONT_SIZE = 25;
@@ -167,9 +167,9 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
return this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, defaultValue); return this._storageService.getBoolean(IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY, StorageScope.WORKSPACE, defaultValue);
} }
public checkWorkspaceShellPermissions(platformOverride: platform.Platform = platform.platform): boolean { public checkWorkspaceShellPermissions(osOverride: platform.OperatingSystem = platform.OS): boolean {
// Check whether there is a workspace setting // Check whether there is a workspace setting
const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; const platformKey = osOverride === platform.OperatingSystem.Windows ? 'windows' : osOverride === platform.OperatingSystem.Macintosh ? 'osx' : 'linux';
const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`); const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`);
const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`); const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`);
const envConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.env.${platformKey}`); const envConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.env.${platformKey}`);
@@ -228,28 +228,8 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper {
} }
public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride: platform.Platform = platform.platform): void { public mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride: platform.Platform = platform.platform): void {
const isWorkspaceShellAllowed = this.checkWorkspaceShellPermissions(platformOverride); const isWorkspaceShellAllowed = this.checkWorkspaceShellPermissions(platformOverride === platform.Platform.Windows ? platform.OperatingSystem.Windows : (platformOverride === platform.Platform.Mac ? platform.OperatingSystem.Macintosh : platform.OperatingSystem.Linux));
const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux'; mergeDefaultShellPathAndArgs(shell, (key) => this._workspaceConfigurationService.inspect(key), isWorkspaceShellAllowed);
const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`);
const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`);
shell.executable = (isWorkspaceShellAllowed ? shellConfigValue.value : shellConfigValue.user) || shellConfigValue.default;
shell.args = (isWorkspaceShellAllowed ? shellArgsConfigValue.value : shellArgsConfigValue.user) || shellArgsConfigValue.default;
// Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's
// safe to assume that this was used by accident as Sysnative does not
// exist and will break the terminal in non-WoW64 environments.
if ((platformOverride === platform.Platform.Windows) && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') && process.env.windir) {
const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase();
if (shell.executable.toLowerCase().indexOf(sysnativePath) === 0) {
shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length));
}
}
// Convert / to \ on Windows for convenience
if (platformOverride === platform.Platform.Windows) {
shell.executable = shell.executable.replace(/\//g, '\\');
}
} }
private _toInteger(source: any, minimum: number, maximum: number, fallback: number): number { private _toInteger(source: any, minimum: number, maximum: number, fallback: number): number {

View File

@@ -6,7 +6,7 @@
import * as platform from 'vs/base/common/platform'; import * as platform from 'vs/base/common/platform';
import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import * as terminalEnvironment from 'vs/workbench/contrib/terminal/common/terminalEnvironment';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent } from 'vs/workbench/contrib/terminal/common/terminal'; import { ProcessState, ITerminalProcessManager, IShellLaunchConfig, ITerminalConfigHelper, ITerminalChildProcess, IBeforeProcessDataEvent, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal';
import { ILogService } from 'vs/platform/log/common/log'; import { ILogService } from 'vs/platform/log/common/log';
import { Emitter, Event } from 'vs/base/common/event'; import { Emitter, Event } from 'vs/base/common/event';
import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IHistoryService } from 'vs/workbench/services/history/common/history';
@@ -22,6 +22,7 @@ import { IProductService } from 'vs/platform/product/common/product';
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { URI } from 'vs/base/common/uri';
/** The amount of time to consider terminal errors to be related to the launch */ /** The amount of time to consider terminal errors to be related to the launch */
const LAUNCHING_DURATION = 500; const LAUNCHING_DURATION = 500;
@@ -133,16 +134,13 @@ export class TerminalProcessManager implements ITerminalProcessManager {
} }
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(hasRemoteAuthority ? REMOTE_HOST_SCHEME : undefined); const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(hasRemoteAuthority ? REMOTE_HOST_SCHEME : undefined);
this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows); this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper);
} else { } else {
this._process = this._launchProcess(shellLaunchConfig, cols, rows); this._process = this._launchProcess(shellLaunchConfig, cols, rows);
} }
this.processState = ProcessState.LAUNCHING; this.processState = ProcessState.LAUNCHING;
// The process is non-null, but TS isn't clever enough to know this._process.onProcessData(data => {
const p = this._process!;
p.onProcessData(data => {
const beforeProcessDataEvent: IBeforeProcessDataEvent = { data }; const beforeProcessDataEvent: IBeforeProcessDataEvent = { data };
this._onBeforeProcessData.fire(beforeProcessDataEvent); this._onBeforeProcessData.fire(beforeProcessDataEvent);
if (beforeProcessDataEvent.data && beforeProcessDataEvent.data.length > 0) { if (beforeProcessDataEvent.data && beforeProcessDataEvent.data.length > 0) {
@@ -150,19 +148,19 @@ export class TerminalProcessManager implements ITerminalProcessManager {
} }
}); });
p.onProcessIdReady(pid => { this._process.onProcessIdReady(pid => {
this.shellProcessId = pid; this.shellProcessId = pid;
this._onProcessReady.fire(); this._onProcessReady.fire();
// Send any queued data that's waiting // Send any queued data that's waiting
if (this._preLaunchInputQueue.length > 0) { if (this._preLaunchInputQueue.length > 0 && this._process) {
p.input(this._preLaunchInputQueue.join('')); this._process.input(this._preLaunchInputQueue.join(''));
this._preLaunchInputQueue.length = 0; this._preLaunchInputQueue.length = 0;
} }
}); });
p.onProcessTitleChanged(title => this._onProcessTitle.fire(title)); this._process.onProcessTitleChanged(title => this._onProcessTitle.fire(title));
p.onProcessExit(exitCode => this._onExit(exitCode)); this._process.onProcessExit(exitCode => this._onExit(exitCode));
setTimeout(() => { setTimeout(() => {
if (this.processState === ProcessState.LAUNCHING) { if (this.processState === ProcessState.LAUNCHING) {
@@ -175,32 +173,41 @@ export class TerminalProcessManager implements ITerminalProcessManager {
if (!shellLaunchConfig.executable) { if (!shellLaunchConfig.executable) {
this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig); this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
} }
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd); const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd);
const env = this._createEnvironment(shellLaunchConfig, activeWorkspaceRootUri);
// Compel type system as process.env should not have any undefined entries this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env);
return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty);
}
private _createEnvironment(shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI | undefined): platform.IProcessEnvironment {
// Create a terminal environment based on settings, launch config and permissions
let env: platform.IProcessEnvironment = {}; let env: platform.IProcessEnvironment = {};
if (shellLaunchConfig.strictEnv) { if (shellLaunchConfig.strictEnv) {
// Only base the terminal process environment on this environment and add the // strictEnv is true, only use the requested env (ignoring null entries)
// various mixins when strictEnv is false terminalEnvironment.mergeNonNullKeys(env, shellLaunchConfig.env);
env = { ...shellLaunchConfig.env } as any;
} else { } else {
// Merge process env with the env from config and from shellLaunchConfig // Merge process env with the env from config and from shellLaunchConfig
env = { ...process.env } as any; terminalEnvironment.mergeNonNullKeys(env, process.env);
// Resolve env vars from config and shell // Determine config env based on workspace shell permissions
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null;
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
const envFromConfigValue = this._workspaceConfigurationService.inspect<{ [key: string]: string }>(`terminal.integrated.env.${platformKey}`); const envFromConfigValue = this._workspaceConfigurationService.inspect<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
const allowedEnvFromConfig = (isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user); const allowedEnvFromConfig = { ...(isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user) };
const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...allowedEnvFromConfig }, lastActiveWorkspaceRoot);
const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
shellLaunchConfig.env = envFromShell;
terminalEnvironment.mergeEnvironments(env, envFromConfig); // Resolve env vars from config and shell
if (allowedEnvFromConfig) {
terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, allowedEnvFromConfig, lastActiveWorkspaceRoot);
}
if (shellLaunchConfig.env) {
terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, shellLaunchConfig.env, lastActiveWorkspaceRoot);
}
// Merge config (settings) and ShellLaunchConfig environments
terminalEnvironment.mergeEnvironments(env, allowedEnvFromConfig);
terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env); terminalEnvironment.mergeEnvironments(env, shellLaunchConfig.env);
// Sanitize the environment, removing any undesirable VS Code and Electron environment // Sanitize the environment, removing any undesirable VS Code and Electron environment
@@ -210,9 +217,7 @@ export class TerminalProcessManager implements ITerminalProcessManager {
// Adding other env keys necessary to create the process // Adding other env keys necessary to create the process
terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables); terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables);
} }
return env;
this._logService.debug(`Terminal process launching`, shellLaunchConfig, initialCwd, cols, rows, env);
return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, this._configHelper.config.windowsEnableConpty);
} }
public setDimensions(cols: number, rows: number): void { public setDimensions(cols: number, rows: number): void {

View File

@@ -115,7 +115,7 @@ export interface ITerminalConfigHelper {
mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride?: platform.Platform): void; mergeDefaultShellPathAndArgs(shell: IShellLaunchConfig, platformOverride?: platform.Platform): void;
/** Sets whether a workspace shell configuration is allowed or not */ /** Sets whether a workspace shell configuration is allowed or not */
setWorkspaceShellAllowed(isAllowed: boolean): void; setWorkspaceShellAllowed(isAllowed: boolean): void;
checkWorkspaceShellPermissions(platformOverride?: platform.Platform): boolean; checkWorkspaceShellPermissions(osOverride?: platform.OperatingSystem): boolean;
} }
export interface ITerminalFont { export interface ITerminalFont {
@@ -268,7 +268,7 @@ export interface ITerminalService {
preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise<string>; preparePathForTerminalAsync(path: string, executable: string | undefined, title: string): Promise<string>;
extHostReady(remoteAuthority: string): void; extHostReady(remoteAuthority: string): void;
requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void; requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void;
} }
export const enum Direction { export const enum Direction {
@@ -714,6 +714,7 @@ export interface ITerminalProcessExtHostRequest {
activeWorkspaceRootUri: URI; activeWorkspaceRootUri: URI;
cols: number; cols: number;
rows: number; rows: number;
isWorkspaceShellAllowed: boolean;
} }
export enum LinuxDistro { export enum LinuxDistro {

View File

@@ -14,7 +14,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati
* This module contains utility functions related to the environment, cwd and paths. * This module contains utility functions related to the environment, cwd and paths.
*/ */
export function mergeEnvironments(parent: platform.IProcessEnvironment, other?: ITerminalEnvironment): void { export function mergeEnvironments(parent: platform.IProcessEnvironment, other: ITerminalEnvironment | undefined): void {
if (!other) { if (!other) {
return; return;
} }
@@ -49,14 +49,28 @@ function _mergeEnvironmentValue(env: ITerminalEnvironment, key: string, value: s
} }
} }
export function addTerminalEnvironmentKeys(env: ITerminalEnvironment, version: string | undefined, locale: string | undefined, setLocaleVariables: boolean): void { export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, version: string | undefined, locale: string | undefined, setLocaleVariables: boolean): void {
env['TERM_PROGRAM'] = 'vscode'; env['TERM_PROGRAM'] = 'vscode';
env['TERM_PROGRAM_VERSION'] = version ? version : null; if (version) {
env['TERM_PROGRAM_VERSION'] = version;
}
if (setLocaleVariables) { if (setLocaleVariables) {
env['LANG'] = _getLangEnvVariable(locale); env['LANG'] = _getLangEnvVariable(locale);
} }
} }
export function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | NodeJS.ProcessEnv | undefined) {
if (!other) {
return;
}
for (const key of Object.keys(other)) {
const value = other[key];
if (value) {
env[key] = value;
}
}
}
export function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: ITerminalEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder | null): ITerminalEnvironment { export function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: ITerminalEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder | null): ITerminalEnvironment {
Object.keys(env).forEach((key) => { Object.keys(env).forEach((key) => {
const value = env[key]; const value = env[key];
@@ -144,3 +158,34 @@ export function escapeNonWindowsPath(path: string): string {
} }
return newPath; return newPath;
} }
export function mergeDefaultShellPathAndArgs(
shell: IShellLaunchConfig,
fetchSetting: (key: string) => { user: string | string[] | undefined, value: string | string[] | undefined, default: string | string[] | undefined },
isWorkspaceShellAllowed: boolean,
platformOverride: platform.Platform = platform.platform
): void {
const platformKey = platformOverride === platform.Platform.Windows ? 'windows' : platformOverride === platform.Platform.Mac ? 'osx' : 'linux';
const shellConfigValue = fetchSetting(`terminal.integrated.shell.${platformKey}`);
// const shellConfigValue = this._workspaceConfigurationService.inspect<string>(`terminal.integrated.shell.${platformKey}`);
const shellArgsConfigValue = fetchSetting(`terminal.integrated.shellArgs.${platformKey}`);
// const shellArgsConfigValue = this._workspaceConfigurationService.inspect<string[]>(`terminal.integrated.shellArgs.${platformKey}`);
shell.executable = (isWorkspaceShellAllowed ? <string>shellConfigValue.value : <string>shellConfigValue.user) || <string>shellConfigValue.default;
shell.args = (isWorkspaceShellAllowed ? <string[]>shellArgsConfigValue.value : <string[]>shellArgsConfigValue.user) || <string[]>shellArgsConfigValue.default;
// Change Sysnative to System32 if the OS is Windows but NOT WoW64. It's
// safe to assume that this was used by accident as Sysnative does not
// exist and will break the terminal in non-WoW64 environments.
if ((platformOverride === platform.Platform.Windows) && !process.env.hasOwnProperty('PROCESSOR_ARCHITEW6432') && process.env.windir) {
const sysnativePath = path.join(process.env.windir, 'Sysnative').toLowerCase();
if (shell.executable && shell.executable.toLowerCase().indexOf(sysnativePath) === 0) {
shell.executable = path.join(process.env.windir, 'System32', shell.executable.substr(sysnativePath.length));
}
}
// Convert / to \ on Windows for convenience
if (shell.executable && platformOverride === platform.Platform.Windows) {
shell.executable = shell.executable.replace(/\//g, '\\');
}
}

View File

@@ -4,9 +4,13 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess } from 'vs/workbench/contrib/terminal/common/terminal'; import { ITerminalService, ITerminalProcessExtHostProxy, IShellLaunchConfig, ITerminalChildProcess, ITerminalConfigHelper } from 'vs/workbench/contrib/terminal/common/terminal';
import { IDisposable } from 'vs/base/common/lifecycle'; import { IDisposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import * as nls from 'vs/nls';
let hasReceivedResponse: boolean = false;
export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerminalProcessExtHostProxy { export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerminalProcessExtHostProxy {
private _disposables: IDisposable[] = []; private _disposables: IDisposable[] = [];
@@ -43,10 +47,19 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm
activeWorkspaceRootUri: URI, activeWorkspaceRootUri: URI,
cols: number, cols: number,
rows: number, rows: number,
@ITerminalService private readonly _terminalService: ITerminalService configHelper: ITerminalConfigHelper,
@ITerminalService private readonly _terminalService: ITerminalService,
@IRemoteAgentService readonly remoteAgentService: IRemoteAgentService
) { ) {
this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows); remoteAgentService.getEnvironment().then(env => {
setTimeout(() => this._onProcessTitleChanged.fire('Starting...'), 0); if (!env) {
throw new Error('Could not fetch environment');
}
this._terminalService.requestExtHostProcess(this, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, configHelper.checkWorkspaceShellPermissions(env.os));
});
if (!hasReceivedResponse) {
setTimeout(() => this._onProcessTitleChanged.fire(nls.localize('terminal.integrated.starting', "Starting...")), 0);
}
} }
public dispose(): void { public dispose(): void {
@@ -59,6 +72,7 @@ export class TerminalProcessExtHostProxy implements ITerminalChildProcess, ITerm
} }
public emitTitle(title: string): void { public emitTitle(title: string): void {
// hasReceivedResponse = true;
this._onProcessTitleChanged.fire(title); this._onProcessTitleChanged.fire(title);
} }

View File

@@ -118,7 +118,7 @@ export abstract class TerminalService implements ITerminalService {
return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction); return activeInstance ? activeInstance : this.createTerminal(undefined, wasNewTerminalAction);
} }
public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number): void { public requestExtHostProcess(proxy: ITerminalProcessExtHostProxy, shellLaunchConfig: IShellLaunchConfig, activeWorkspaceRootUri: URI, cols: number, rows: number, isWorkspaceShellAllowed: boolean): void {
this._extensionService.whenInstalledExtensionsRegistered().then(async () => { this._extensionService.whenInstalledExtensionsRegistered().then(async () => {
// Wait for the remoteAuthority to be ready (and listening for events) before proceeding // Wait for the remoteAuthority to be ready (and listening for events) before proceeding
const conn = this._remoteAgentService.getConnection(); const conn = this._remoteAgentService.getConnection();
@@ -127,7 +127,7 @@ export abstract class TerminalService implements ITerminalService {
while (!this._extHostsReady[remoteAuthority] && ++retries < 50) { while (!this._extHostsReady[remoteAuthority] && ++retries < 50) {
await timeout(100); await timeout(100);
} }
this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows }); this._onInstanceRequestExtHostProcess.fire({ proxy, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, isWorkspaceShellAllowed });
}); });
} }

View File

@@ -234,8 +234,8 @@ CommandsRegistry.registerCommand('_workbench.captureSyntaxTokens', function (acc
let fileName = basename(resource); let fileName = basename(resource);
let snapper = accessor.get(IInstantiationService).createInstance(Snapper); let snapper = accessor.get(IInstantiationService).createInstance(Snapper);
return fileService.resolveContent(resource).then(content => { return fileService.readFile(resource).then(content => {
return snapper.captureSyntaxTokens(fileName, content.value); return snapper.captureSyntaxTokens(fileName, content.value.toString());
}); });
}; };

View File

@@ -16,7 +16,7 @@ import * as modes from 'vs/editor/common/modes';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IFileService } from 'vs/platform/files/common/files'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
@@ -118,7 +118,7 @@ class WebviewProtocolProvider extends Disposable {
private readonly _extensionLocation: URI | undefined, private readonly _extensionLocation: URI | undefined,
private readonly _getLocalResourceRoots: () => ReadonlyArray<URI>, private readonly _getLocalResourceRoots: () => ReadonlyArray<URI>,
private readonly _environmentService: IEnvironmentService, private readonly _environmentService: IEnvironmentService,
private readonly _fileService: IFileService, private readonly _textFileService: ITextFileService,
) { ) {
super(); super();
@@ -137,11 +137,11 @@ class WebviewProtocolProvider extends Disposable {
const appRootUri = URI.file(this._environmentService.appRoot); const appRootUri = URI.file(this._environmentService.appRoot);
registerFileProtocol(contents, WebviewProtocol.CoreResource, this._fileService, undefined, () => [ registerFileProtocol(contents, WebviewProtocol.CoreResource, this._textFileService, undefined, () => [
appRootUri appRootUri
]); ]);
registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._fileService, this._extensionLocation, () => registerFileProtocol(contents, WebviewProtocol.VsCodeResource, this._textFileService, this._extensionLocation, () =>
this._getLocalResourceRoots() this._getLocalResourceRoots()
); );
} }
@@ -374,7 +374,7 @@ export class WebviewElement extends Disposable implements Webview {
@IInstantiationService instantiationService: IInstantiationService, @IInstantiationService instantiationService: IInstantiationService,
@IThemeService themeService: IThemeService, @IThemeService themeService: IThemeService,
@IEnvironmentService environmentService: IEnvironmentService, @IEnvironmentService environmentService: IEnvironmentService,
@IFileService fileService: IFileService, @ITextFileService textFileService: ITextFileService,
@ITunnelService tunnelService: ITunnelService, @ITunnelService tunnelService: ITunnelService,
@ITelemetryService telemetryService: ITelemetryService, @ITelemetryService telemetryService: ITelemetryService,
@IConfigurationService private readonly _configurationService: IConfigurationService, @IConfigurationService private readonly _configurationService: IConfigurationService,
@@ -412,7 +412,7 @@ export class WebviewElement extends Disposable implements Webview {
this._options.extension ? this._options.extension.location : undefined, this._options.extension ? this._options.extension.location : undefined,
() => (this._contentOptions.localResourceRoots || []), () => (this._contentOptions.localResourceRoots || []),
environmentService, environmentService,
fileService)); textFileService));
this._register(new WebviewPortMappingProvider( this._register(new WebviewPortMappingProvider(
session, session,

View File

@@ -6,16 +6,16 @@ import { getMediaMime, MIME_UNKNOWN } from 'vs/base/common/mime';
import { extname, sep } from 'vs/base/common/path'; import { extname, sep } from 'vs/base/common/path';
import { startsWith } from 'vs/base/common/strings'; import { startsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
export const enum WebviewProtocol { export const enum WebviewProtocol {
CoreResource = 'vscode-core-resource', CoreResource = 'vscode-core-resource',
VsCodeResource = 'vscode-resource', VsCodeResource = 'vscode-resource',
} }
function resolveContent(fileService: IFileService, resource: URI, mime: string, callback: any): void { function resolveContent(textFileService: ITextFileService, resource: URI, mime: string, callback: any): void {
fileService.resolveContent(resource, { encoding: 'binary' }).then(contents => { textFileService.read(resource, { encoding: 'binary' }).then(contents => {
callback({ callback({
data: Buffer.from(contents.value, contents.encoding), data: Buffer.from(contents.value, contents.encoding),
mimeType: mime mimeType: mime
@@ -29,7 +29,7 @@ function resolveContent(fileService: IFileService, resource: URI, mime: string,
export function registerFileProtocol( export function registerFileProtocol(
contents: Electron.WebContents, contents: Electron.WebContents,
protocol: WebviewProtocol, protocol: WebviewProtocol,
fileService: IFileService, textFileService: ITextFileService,
extensionLocation: URI | undefined, extensionLocation: URI | undefined,
getRoots: () => ReadonlyArray<URI> getRoots: () => ReadonlyArray<URI>
) { ) {
@@ -44,7 +44,7 @@ export function registerFileProtocol(
requestResourcePath: requestUri.path requestResourcePath: requestUri.path
}) })
}); });
resolveContent(fileService, redirectedUri, getMimeType(requestUri), callback); resolveContent(textFileService, redirectedUri, getMimeType(requestUri), callback);
return; return;
} }
@@ -52,7 +52,7 @@ export function registerFileProtocol(
const normalizedPath = URI.file(requestPath); const normalizedPath = URI.file(requestPath);
for (const root of getRoots()) { for (const root of getRoots()) {
if (startsWith(normalizedPath.fsPath, root.fsPath + sep)) { if (startsWith(normalizedPath.fsPath, root.fsPath + sep)) {
resolveContent(fileService, normalizedPath, getMimeType(normalizedPath), callback); resolveContent(textFileService, normalizedPath, getMimeType(normalizedPath), callback);
return; return;
} }
} }

View File

@@ -35,7 +35,7 @@ export class WalkThroughContentProvider implements ITextModelContentProvider, IW
reject(err); reject(err);
} }
}); });
}) : this.textFileService.resolve(URI.file(resource.fsPath)).then(content => content.value)); }) : this.textFileService.readStream(URI.file(resource.fsPath)).then(content => content.value));
return content.then(content => { return content.then(content => {
let codeEditorModel = this.modelService.getModel(resource); let codeEditorModel = this.modelService.getModel(resource);
if (!codeEditorModel) { if (!codeEditorModel) {
@@ -61,7 +61,7 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi
} }
public provideTextContent(resource: URI): Promise<ITextModel> { public provideTextContent(resource: URI): Promise<ITextModel> {
return this.textFileService.resolve(URI.file(resource.fsPath)).then(content => { return this.textFileService.readStream(URI.file(resource.fsPath)).then(content => {
let codeEditorModel = this.modelService.getModel(resource); let codeEditorModel = this.modelService.getModel(resource);
if (!codeEditorModel) { if (!codeEditorModel) {
const j = parseInt(resource.fragment); const j = parseInt(resource.fragment);

View File

@@ -41,9 +41,9 @@ import { RemoteAuthorityResolverService } from 'vs/platform/remote/electron-brow
import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl';
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { FileService2 } from 'vs/workbench/services/files2/common/fileService2'; import { FileService } from 'vs/workbench/services/files/common/fileService';
import { IFileService } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { DiskFileSystemProvider } from 'vs/workbench/services/files2/electron-browser/diskFileSystemProvider'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/electron-browser/diskFileSystemProvider';
import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IChannel } from 'vs/base/parts/ipc/common/ipc';
import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel'; import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel';
import { DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationExportHelper'; import { DefaultConfigurationExportHelper } from 'vs/workbench/services/configuration/node/configurationExportHelper';
@@ -190,7 +190,7 @@ class CodeRendererMain extends Disposable {
serviceCollection.set(IRemoteAgentService, remoteAgentService); serviceCollection.set(IRemoteAgentService, remoteAgentService);
// Files // Files
const fileService = this._register(new FileService2(logService)); const fileService = this._register(new FileService(logService));
serviceCollection.set(IFileService, fileService); serviceCollection.set(IFileService, fileService);
const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService)); const diskFileSystemProvider = this._register(new DiskFileSystemProvider(logService));
@@ -295,9 +295,9 @@ class CodeRendererMain extends Disposable {
}, error => onUnexpectedError(error)); }, error => onUnexpectedError(error));
} }
private createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService2, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise<WorkspaceService> { private createWorkspaceService(payload: IWorkspaceInitializationPayload, environmentService: IWorkbenchEnvironmentService, fileService: FileService, remoteAgentService: IRemoteAgentService, logService: ILogService): Promise<WorkspaceService> {
const configurationFileService = new ConfigurationFileService(); const configurationFileService = new ConfigurationFileService();
fileService.whenReady.then(() => configurationFileService.fileService = fileService); configurationFileService.fileService = fileService;
const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService); const workspaceService = new WorkspaceService({ userSettingsResource: URI.file(environmentService.appSettingsPath), remoteAuthority: this.configuration.remoteAuthority, configurationCache: new ConfigurationCache(environmentService) }, configurationFileService, remoteAgentService);

View File

@@ -5,13 +5,10 @@
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IResolveContentOptions, ITextSnapshot } from 'vs/platform/files/common/files'; import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model';
import { ITextBufferFactory } from 'vs/editor/common/model';
export const IBackupFileService = createDecorator<IBackupFileService>('backupFileService'); export const IBackupFileService = createDecorator<IBackupFileService>('backupFileService');
export const BACKUP_FILE_RESOLVE_OPTIONS: IResolveContentOptions = { acceptTextOnly: true, encoding: 'utf8' };
/** /**
* A service that handles any I/O and state associated with the backup system. * A service that handles any I/O and state associated with the backup system.
*/ */

View File

@@ -8,15 +8,17 @@ import * as crypto from 'crypto';
import * as pfs from 'vs/base/node/pfs'; import * as pfs from 'vs/base/node/pfs';
import { URI as Uri } from 'vs/base/common/uri'; import { URI as Uri } from 'vs/base/common/uri';
import { ResourceQueue } from 'vs/base/common/async'; import { ResourceQueue } from 'vs/base/common/async';
import { IBackupFileService, BACKUP_FILE_RESOLVE_OPTIONS } from 'vs/workbench/services/backup/common/backup'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
import { IFileService, ITextSnapshot, TextSnapshotReadable } from 'vs/platform/files/common/files'; import { IFileService } from 'vs/platform/files/common/files';
import { readToMatchingString } from 'vs/base/node/stream'; import { readToMatchingString } from 'vs/base/node/stream';
import { ITextBufferFactory } from 'vs/editor/common/model'; import { ITextBufferFactory, ITextSnapshot } from 'vs/editor/common/model';
import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { createTextBufferFactoryFromStream, createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel';
import { keys } from 'vs/base/common/map'; import { keys } from 'vs/base/common/map';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { VSBuffer } from 'vs/base/common/buffer';
import { TextSnapshotReadable } from 'vs/workbench/services/textfile/common/textfiles';
export interface IBackupFilesModel { export interface IBackupFilesModel {
resolve(backupRoot: string): Promise<IBackupFilesModel>; resolve(backupRoot: string): Promise<IBackupFilesModel>;
@@ -249,19 +251,21 @@ class BackupFileServiceImpl implements IBackupFileService {
} }
resolveBackupContent(backup: Uri): Promise<ITextBufferFactory> { resolveBackupContent(backup: Uri): Promise<ITextBufferFactory> {
return this.fileService.resolveStreamContent(backup, BACKUP_FILE_RESOLVE_OPTIONS).then(content => { return this.fileService.readFileStream(backup).then(content => {
// Add a filter method to filter out everything until the meta marker // Add a filter method to filter out everything until the meta marker
let metaFound = false; let metaFound = false;
const metaPreambleFilter = (chunk: string) => { const metaPreambleFilter = (chunk: VSBuffer) => {
const chunkString = chunk.toString();
if (!metaFound && chunk) { if (!metaFound && chunk) {
const metaIndex = chunk.indexOf(BackupFileServiceImpl.META_MARKER); const metaIndex = chunkString.indexOf(BackupFileServiceImpl.META_MARKER);
if (metaIndex === -1) { if (metaIndex === -1) {
return ''; // meta not yet found, return empty string return VSBuffer.fromString(''); // meta not yet found, return empty string
} }
metaFound = true; metaFound = true;
return chunk.substr(metaIndex + 1); // meta found, return everything after return VSBuffer.fromString(chunkString.substr(metaIndex + 1)); // meta found, return everything after
} }
return chunk; return chunk;

View File

@@ -12,20 +12,17 @@ import * as path from 'vs/base/common/path';
import * as pfs from 'vs/base/node/pfs'; import * as pfs from 'vs/base/node/pfs';
import { URI as Uri } from 'vs/base/common/uri'; import { URI as Uri } from 'vs/base/common/uri';
import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService'; import { BackupFileService, BackupFilesModel, hashPath } from 'vs/workbench/services/backup/node/backupFileService';
import { LegacyFileService } from 'vs/workbench/services/files/node/fileService';
import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel'; import { TextModel, createTextBufferFactory } from 'vs/editor/common/model/textModel';
import { TestContextService, TestTextResourceConfigurationService, TestEnvironmentService } from 'vs/workbench/test/workbenchTestServices';
import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { Workspace, toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
import { DefaultEndOfLine } from 'vs/editor/common/model'; import { DefaultEndOfLine } from 'vs/editor/common/model';
import { snapshotToString } from 'vs/platform/files/common/files';
import { Schemas } from 'vs/base/common/network'; import { Schemas } from 'vs/base/common/network';
import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { FileService2 } from 'vs/workbench/services/files2/common/fileService2'; import { FileService } from 'vs/workbench/services/files/common/fileService';
import { NullLogService } from 'vs/platform/log/common/log'; import { NullLogService } from 'vs/platform/log/common/log';
import { DiskFileSystemProvider } from 'vs/workbench/services/files2/node/diskFileSystemProvider'; import { DiskFileSystemProvider } from 'vs/workbench/services/files/node/diskFileSystemProvider';
import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService'; import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/node/environmentService';
import { parseArgs } from 'vs/platform/environment/node/argv'; import { parseArgs } from 'vs/platform/environment/node/argv';
import { snapshotToString } from 'vs/workbench/services/textfile/common/textfiles';
const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice'); const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'backupfileservice');
const backupHome = path.join(parentDir, 'Backups'); const backupHome = path.join(parentDir, 'Backups');
@@ -58,14 +55,8 @@ class TestBackupEnvironmentService extends WorkbenchEnvironmentService {
class TestBackupFileService extends BackupFileService { class TestBackupFileService extends BackupFileService {
constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) { constructor(workspace: Uri, backupHome: string, workspacesJsonPath: string) {
const fileService = new FileService2(new NullLogService()); const fileService = new FileService(new NullLogService());
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService())); fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(new NullLogService()));
fileService.setLegacyService(new LegacyFileService(
fileService,
new TestContextService(new Workspace(workspace.fsPath, toWorkspaceFolders([{ path: workspace.fsPath }]))),
TestEnvironmentService,
new TestTextResourceConfigurationService(),
));
const environmentService = new TestBackupEnvironmentService(workspaceBackupPath); const environmentService = new TestBackupEnvironmentService(workspaceBackupPath);
super(environmentService, fileService); super(environmentService, fileService);

View File

@@ -133,7 +133,7 @@ export class UserConfiguration extends Disposable {
async reload(): Promise<ConfigurationModel> { async reload(): Promise<ConfigurationModel> {
try { try {
const content = await this.configurationFileService.resolveContent(this.configurationResource); const content = await this.configurationFileService.readFile(this.configurationResource);
this.parser.parseContent(content); this.parser.parseContent(content);
return this.parser.configurationModel; return this.parser.configurationModel;
} catch (e) { } catch (e) {
@@ -379,7 +379,7 @@ class FileServiceBasedWorkspaceConfiguration extends Disposable implements IWork
} }
let contents = ''; let contents = '';
try { try {
contents = await this.configurationFileService.resolveContent(this._workspaceIdentifier.configPath); contents = await this.configurationFileService.readFile(this._workspaceIdentifier.configPath);
} catch (error) { } catch (error) {
const exists = await this.configurationFileService.exists(this._workspaceIdentifier.configPath); const exists = await this.configurationFileService.exists(this._workspaceIdentifier.configPath);
if (exists) { if (exists) {
@@ -547,7 +547,7 @@ class FileServiceBasedFolderConfiguration extends Disposable implements IFolderC
async loadConfiguration(): Promise<ConfigurationModel> { async loadConfiguration(): Promise<ConfigurationModel> {
const configurationContents = await Promise.all(this.configurationResources.map(async resource => { const configurationContents = await Promise.all(this.configurationResources.map(async resource => {
try { try {
return await this.configurationFileService.resolveContent(resource); return await this.configurationFileService.readFile(resource);
} catch (error) { } catch (error) {
const exists = await this.configurationFileService.exists(resource); const exists = await this.configurationFileService.exists(resource);
if (exists) { if (exists) {

View File

@@ -4,14 +4,13 @@
*--------------------------------------------------------------------------------------------*/ *--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri'; import { URI } from 'vs/base/common/uri';
import * as assert from 'vs/base/common/assert';
import { Event, Emitter } from 'vs/base/common/event'; import { Event, Emitter } from 'vs/base/common/event';
import { ResourceMap } from 'vs/base/common/map'; import { ResourceMap } from 'vs/base/common/map';
import { equals, deepClone } from 'vs/base/common/objects'; import { equals, deepClone } from 'vs/base/common/objects';
import { Disposable } from 'vs/base/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle';
import { Queue, Barrier } from 'vs/base/common/async'; import { Queue, Barrier } from 'vs/base/common/async';
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceContextService, Workspace, WorkbenchState, IWorkspaceFolder, toWorkspaceFolders, IWorkspaceFoldersChangeEvent, WorkspaceFolder, toWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { isLinux } from 'vs/base/common/platform'; import { isLinux } from 'vs/base/common/platform';
import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels'; import { ConfigurationChangeEvent, ConfigurationModel, DefaultConfigurationModel } from 'vs/platform/configuration/common/configurationModels';
import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationChangeEvent, ConfigurationTarget, IConfigurationOverrides, keyFromOverrideIdentifier, isConfigurationOverrides, IConfigurationData, IConfigurationService } from 'vs/platform/configuration/common/configuration';
@@ -61,6 +60,9 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
private configurationEditingService: ConfigurationEditingService; private configurationEditingService: ConfigurationEditingService;
private jsonEditingService: JSONEditingService; private jsonEditingService: JSONEditingService;
private cyclicDependencyReady: Function;
private cyclicDependency = new Promise<void>(resolve => this.cyclicDependencyReady = resolve);
constructor( constructor(
{ userSettingsResource, remoteAuthority, configurationCache }: { userSettingsResource?: URI, remoteAuthority?: string, configurationCache: IConfigurationCache }, { userSettingsResource, remoteAuthority, configurationCache }: { userSettingsResource?: URI, remoteAuthority?: string, configurationCache: IConfigurationCache },
private readonly configurationFileService: IConfigurationFileService, private readonly configurationFileService: IConfigurationFileService,
@@ -131,8 +133,9 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
} }
public updateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> { public updateFolders(foldersToAdd: IWorkspaceFolderCreationData[], foldersToRemove: URI[], index?: number): Promise<void> {
assert.ok(this.jsonEditingService, 'Workbench is not initialized yet'); return this.cyclicDependency.then(() => {
return Promise.resolve(this.workspaceEditingQueue.queue(() => this.doUpdateFolders(foldersToAdd, foldersToRemove, index))); return this.workspaceEditingQueue.queue(() => this.doUpdateFolders(foldersToAdd, foldersToRemove, index));
});
} }
public isInsideWorkspace(resource: URI): boolean { public isInsideWorkspace(resource: URI): boolean {
@@ -178,8 +181,9 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
if (foldersToAdd.length) { if (foldersToAdd.length) {
// Recompute current workspace folders if we have folders to add // Recompute current workspace folders if we have folders to add
const workspaceConfigFolder = dirname(this.getWorkspace().configuration!); const workspaceConfigPath = this.getWorkspace().configuration!;
currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, workspaceConfigFolder); const workspaceConfigFolder = dirname(workspaceConfigPath);
currentWorkspaceFolders = toWorkspaceFolders(newStoredFolders, workspaceConfigPath);
const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri); const currentWorkspaceFolderUris = currentWorkspaceFolders.map(folder => folder.uri);
const storedFoldersToAdd: IStoredWorkspaceFolder[] = []; const storedFoldersToAdd: IStoredWorkspaceFolder[] = [];
@@ -214,8 +218,10 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
} }
private setFolders(folders: IStoredWorkspaceFolder[]): Promise<void> { private setFolders(folders: IStoredWorkspaceFolder[]): Promise<void> {
return this.workspaceConfiguration.setFolders(folders, this.jsonEditingService) return this.cyclicDependency.then(() => {
.then(() => this.onWorkspaceConfigurationChanged()); return this.workspaceConfiguration.setFolders(folders, this.jsonEditingService)
.then(() => this.onWorkspaceConfigurationChanged());
});
} }
private contains(resources: URI[], toCheck: URI): boolean { private contains(resources: URI[], toCheck: URI): boolean {
@@ -250,11 +256,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): Promise<void>; updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget): Promise<void>;
updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError: boolean): Promise<void>; updateValue(key: string, value: any, overrides: IConfigurationOverrides, target: ConfigurationTarget, donotNotifyError: boolean): Promise<void>;
updateValue(key: string, value: any, arg3?: any, arg4?: any, donotNotifyError?: any): Promise<void> { updateValue(key: string, value: any, arg3?: any, arg4?: any, donotNotifyError?: any): Promise<void> {
assert.ok(this.configurationEditingService, 'Workbench is not initialized yet'); return this.cyclicDependency.then(() => {
const overrides = isConfigurationOverrides(arg3) ? arg3 : undefined; const overrides = isConfigurationOverrides(arg3) ? arg3 : undefined;
const target = this.deriveConfigurationTarget(key, value, overrides, overrides ? arg4 : arg3); const target = this.deriveConfigurationTarget(key, value, overrides, overrides ? arg4 : arg3);
return target ? this.writeConfigurationValue(key, value, target, overrides, donotNotifyError) return target ? this.writeConfigurationValue(key, value, target, overrides, donotNotifyError)
: Promise.resolve(); : Promise.resolve();
});
} }
reloadConfiguration(folder?: IWorkspaceFolder, key?: string): Promise<void> { reloadConfiguration(folder?: IWorkspaceFolder, key?: string): Promise<void> {
@@ -299,6 +306,12 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
acquireInstantiationService(instantiationService: IInstantiationService): void { acquireInstantiationService(instantiationService: IInstantiationService): void {
this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService); this.configurationEditingService = instantiationService.createInstance(ConfigurationEditingService);
this.jsonEditingService = instantiationService.createInstance(JSONEditingService); this.jsonEditingService = instantiationService.createInstance(JSONEditingService);
if (this.cyclicDependencyReady) {
this.cyclicDependencyReady();
} else {
this.cyclicDependency = Promise.resolve(undefined);
}
} }
private createWorkspace(arg: IWorkspaceInitializationPayload): Promise<Workspace> { private createWorkspace(arg: IWorkspaceInitializationPayload): Promise<Workspace> {
@@ -317,7 +330,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
return this.workspaceConfiguration.load({ id: workspaceIdentifier.id, configPath: workspaceIdentifier.configPath }) return this.workspaceConfiguration.load({ id: workspaceIdentifier.id, configPath: workspaceIdentifier.configPath })
.then(() => { .then(() => {
const workspaceConfigPath = workspaceIdentifier.configPath; const workspaceConfigPath = workspaceIdentifier.configPath;
const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(workspaceConfigPath)); const workspaceFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), workspaceConfigPath);
const workspaceId = workspaceIdentifier.id; const workspaceId = workspaceIdentifier.id;
const workspace = new Workspace(workspaceId, workspaceFolders, workspaceConfigPath); const workspace = new Workspace(workspaceId, workspaceFolders, workspaceConfigPath);
if (this.workspaceConfiguration.loaded) { if (this.workspaceConfiguration.loaded) {
@@ -328,16 +341,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
} }
private createSingleFolderWorkspace(singleFolder: ISingleFolderWorkspaceInitializationPayload): Promise<Workspace> { private createSingleFolderWorkspace(singleFolder: ISingleFolderWorkspaceInitializationPayload): Promise<Workspace> {
const folder = singleFolder.folder; const workspace = new Workspace(singleFolder.id, [toWorkspaceFolder(singleFolder.folder)]);
let configuredFolders: IStoredWorkspaceFolder[];
if (folder.scheme === 'file') {
configuredFolders = [{ path: folder.fsPath }];
} else {
configuredFolders = [{ uri: folder.toString() }];
}
const workspace = new Workspace(singleFolder.id, toWorkspaceFolders(configuredFolders));
this.releaseWorkspaceBarrier(); // Release barrier as workspace is complete because it is single folder. this.releaseWorkspaceBarrier(); // Release barrier as workspace is complete because it is single folder.
return Promise.resolve(workspace); return Promise.resolve(workspace);
} }
@@ -546,7 +550,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic
private onWorkspaceConfigurationChanged(): Promise<void> { private onWorkspaceConfigurationChanged(): Promise<void> {
if (this.workspace && this.workspace.configuration && this._configuration) { if (this.workspace && this.workspace.configuration && this._configuration) {
const workspaceConfigurationChangeEvent = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration()); const workspaceConfigurationChangeEvent = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration());
let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), dirname(this.workspace.configuration)); let configuredFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration);
const changes = this.compareFolders(this.workspace.folders, configuredFolders); const changes = this.compareFolders(this.workspace.folders, configuredFolders);
if (changes.added.length || changes.removed.length || changes.changed.length) { if (changes.added.length || changes.removed.length || changes.changed.length) {
this.workspace.folders = configuredFolders; this.workspace.folders = configuredFolders;

Some files were not shown because too many files have changed in this diff Show More