mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Merge from vscode 3a6dcb42008d509900b3a3b2d695564eeb4dbdac (#5098)
This commit is contained in:
@@ -2,5 +2,5 @@
|
||||
setlocal
|
||||
set VSCODE_DEV=
|
||||
set ELECTRON_RUN_AS_NODE=1
|
||||
call "%~dp0..\@@NAME@@.exe" "%~dp0..\resources\app\out\cli.js" %*
|
||||
"%~dp0..\@@NAME@@.exe" "%~dp0..\resources\app\out\cli.js" %*
|
||||
endlocal
|
||||
@@ -18,15 +18,20 @@ export const UTF16be_BOM = [0xFE, 0xFF];
|
||||
export const UTF16le_BOM = [0xFF, 0xFE];
|
||||
export const UTF8_BOM = [0xEF, 0xBB, 0xBF];
|
||||
|
||||
const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to decide about a file being binary or not
|
||||
const NO_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, small number of bytes are enough
|
||||
const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing
|
||||
|
||||
export interface IDecodeStreamOptions {
|
||||
guessEncoding?: boolean;
|
||||
guessEncoding: boolean;
|
||||
minBytesRequiredForDetection?: number;
|
||||
overwriteEncoding?(detectedEncoding: string | null): string;
|
||||
|
||||
overwriteEncoding(detectedEncoding: string | null): string;
|
||||
}
|
||||
|
||||
export interface IDecodeStreamResult {
|
||||
detected: IDetectedEncodingResult;
|
||||
stream: NodeJS.ReadableStream;
|
||||
detected: IDetectedEncodingResult;
|
||||
}
|
||||
|
||||
export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions): Promise<IDecodeStreamResult> {
|
||||
@@ -34,78 +39,82 @@ export function toDecodeStream(readable: Readable, options: IDecodeStreamOptions
|
||||
options.minBytesRequiredForDetection = options.guessEncoding ? AUTO_GUESS_BUFFER_MAX_LEN : NO_GUESS_BUFFER_MAX_LEN;
|
||||
}
|
||||
|
||||
if (!options.overwriteEncoding) {
|
||||
options.overwriteEncoding = detected => detected || UTF8;
|
||||
}
|
||||
|
||||
return new Promise<IDecodeStreamResult>((resolve, reject) => {
|
||||
const writer = new class extends Writable {
|
||||
private decodeStream: NodeJS.ReadWriteStream;
|
||||
private decodeStreamConstruction: Promise<void>;
|
||||
private buffer: Buffer[] = [];
|
||||
private decodeStreamPromise: Promise<void>;
|
||||
|
||||
private bufferedChunks: Buffer[] = [];
|
||||
private bytesBuffered = 0;
|
||||
|
||||
_write(chunk: any, encoding: string, callback: Function): void {
|
||||
_write(chunk: Buffer, encoding: string, callback: (error: Error | null) => void): void {
|
||||
if (!Buffer.isBuffer(chunk)) {
|
||||
callback(new Error('data must be a buffer'));
|
||||
return callback(new Error('toDecodeStream(): data must be a buffer'));
|
||||
}
|
||||
|
||||
// if the decode stream is ready, we just write directly
|
||||
if (this.decodeStream) {
|
||||
this.decodeStream.write(chunk, callback); // just a forwarder now
|
||||
this.decodeStream.write(chunk, callback);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
this.buffer.push(chunk);
|
||||
this.bytesBuffered += chunk.length;
|
||||
// otherwise we need to buffer the data until the stream is ready
|
||||
this.bufferedChunks.push(chunk);
|
||||
this.bytesBuffered += chunk.byteLength;
|
||||
|
||||
// waiting for the decoder to be ready
|
||||
if (this.decodeStreamConstruction) {
|
||||
this.decodeStreamConstruction.then(() => callback(), err => callback(err));
|
||||
if (this.decodeStreamPromise) {
|
||||
this.decodeStreamPromise.then(() => callback(null), error => callback(error));
|
||||
}
|
||||
|
||||
// buffered enough data, create stream and forward data
|
||||
// buffered enough data for encoding detection, create stream and forward data
|
||||
else if (typeof options.minBytesRequiredForDetection === 'number' && this.bytesBuffered >= options.minBytesRequiredForDetection) {
|
||||
this._startDecodeStream(callback);
|
||||
}
|
||||
|
||||
// only buffering
|
||||
// only buffering until enough data for encoding detection is there
|
||||
else {
|
||||
callback();
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
|
||||
_startDecodeStream(callback: Function): void {
|
||||
this.decodeStreamConstruction = Promise.resolve(detectEncodingFromBuffer({
|
||||
buffer: Buffer.concat(this.buffer),
|
||||
_startDecodeStream(callback: (error: Error | null) => void): void {
|
||||
|
||||
// detect encoding from buffer
|
||||
this.decodeStreamPromise = Promise.resolve(detectEncodingFromBuffer({
|
||||
buffer: Buffer.concat(this.bufferedChunks),
|
||||
bytesRead: this.bytesBuffered
|
||||
}, options.guessEncoding)).then(detected => {
|
||||
if (options.overwriteEncoding) {
|
||||
detected.encoding = options.overwriteEncoding(detected.encoding);
|
||||
}
|
||||
|
||||
// ensure to respect overwrite of encoding
|
||||
detected.encoding = options.overwriteEncoding(detected.encoding);
|
||||
|
||||
// decode and write buffer
|
||||
this.decodeStream = decodeStream(detected.encoding);
|
||||
this.decodeStream.write(Buffer.concat(this.bufferedChunks), callback);
|
||||
this.bufferedChunks.length = 0;
|
||||
|
||||
for (const buffer of this.buffer) {
|
||||
this.decodeStream.write(buffer);
|
||||
}
|
||||
|
||||
callback();
|
||||
// signal to the outside our detected encoding
|
||||
// and final decoder stream
|
||||
resolve({ detected, stream: this.decodeStream });
|
||||
}, err => {
|
||||
this.emit('error', err);
|
||||
callback(err);
|
||||
}, error => {
|
||||
this.emit('error', error);
|
||||
|
||||
callback(error);
|
||||
});
|
||||
}
|
||||
|
||||
_final(callback: (err?: any) => any) {
|
||||
_final(callback: (error: Error | null) => void) {
|
||||
|
||||
// normal finish
|
||||
if (this.decodeStream) {
|
||||
this.decodeStream.end(callback);
|
||||
}
|
||||
|
||||
// we were still waiting for data...
|
||||
// we were still waiting for data to do the encoding
|
||||
// detection. thus, wrap up starting the stream even
|
||||
// without all the data to get things going
|
||||
else {
|
||||
this._startDecodeStream(() => this.decodeStream.end(callback));
|
||||
}
|
||||
@@ -149,7 +158,7 @@ function toNodeEncoding(enc: string | null): string {
|
||||
}
|
||||
|
||||
export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null, bytesRead: number): string | null {
|
||||
if (!buffer || bytesRead < 2) {
|
||||
if (!buffer || bytesRead < UTF16be_BOM.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -166,7 +175,7 @@ export function detectEncodingByBOMFromBuffer(buffer: Buffer | VSBuffer | null,
|
||||
return UTF16le;
|
||||
}
|
||||
|
||||
if (bytesRead < 3) {
|
||||
if (bytesRead < UTF8_BOM.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -256,10 +265,6 @@ export function toCanonicalName(enc: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to decide about a file being binary or not
|
||||
const NO_GUESS_BUFFER_MAX_LEN = 512; // when not auto guessing the encoding, small number of bytes are enough
|
||||
const AUTO_GUESS_BUFFER_MAX_LEN = 512 * 8; // with auto guessing we want a lot more content to be read for guessing
|
||||
|
||||
export interface IDetectedEncodingResult {
|
||||
encoding: string | null;
|
||||
seemsBinary: boolean;
|
||||
|
||||
@@ -16,7 +16,9 @@ export function registerContextMenuListener(): void {
|
||||
y: options ? options.y : undefined,
|
||||
positioningItem: options ? options.positioningItem : undefined,
|
||||
callback: () => {
|
||||
event.sender.send(CONTEXT_MENU_CLOSE_CHANNEL, contextMenuId);
|
||||
if (menu) {
|
||||
event.sender.send(CONTEXT_MENU_CLOSE_CHANNEL, contextMenuId);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -126,7 +126,8 @@ const enum ProtocolMessageType {
|
||||
Regular = 1,
|
||||
Control = 2,
|
||||
Ack = 3,
|
||||
KeepAlive = 4
|
||||
KeepAlive = 4,
|
||||
Disconnect = 5
|
||||
}
|
||||
|
||||
export const enum ProtocolConstants {
|
||||
@@ -373,6 +374,10 @@ export class Protocol extends Disposable implements IMessagePassingProtocol {
|
||||
return this._socket;
|
||||
}
|
||||
|
||||
sendDisconnect(): void {
|
||||
// Nothing to do...
|
||||
}
|
||||
|
||||
send(buffer: VSBuffer): void {
|
||||
this._socketWriter.write(new ProtocolMessage(ProtocolMessageType.Regular, 0, 0, buffer));
|
||||
}
|
||||
@@ -393,6 +398,7 @@ export class Client<TContext = string> extends IPCClient<TContext> {
|
||||
dispose(): void {
|
||||
super.dispose();
|
||||
const socket = this.protocol.getSocket();
|
||||
this.protocol.sendDisconnect();
|
||||
this.protocol.dispose();
|
||||
socket.end();
|
||||
}
|
||||
@@ -572,7 +578,6 @@ export class PersistentProtocol {
|
||||
this._socketDisposables.push(this._socketReader);
|
||||
this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg)));
|
||||
this._socketDisposables.push(this._socket.onClose(() => this._onSocketClose.fire()));
|
||||
this._socketDisposables.push(this._socket.onEnd(() => this._onClose.fire()));
|
||||
if (initialChunk) {
|
||||
this._socketReader.acceptChunk(initialChunk);
|
||||
}
|
||||
@@ -601,6 +606,12 @@ export class PersistentProtocol {
|
||||
this._socketDisposables = dispose(this._socketDisposables);
|
||||
}
|
||||
|
||||
sendDisconnect(): void {
|
||||
const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer());
|
||||
this._socketWriter.write(msg);
|
||||
this._socketWriter.flush();
|
||||
}
|
||||
|
||||
private _sendKeepAliveCheck(): void {
|
||||
if (this._outgoingKeepAliveTimeout) {
|
||||
// there will be a check in the near future
|
||||
@@ -659,7 +670,6 @@ export class PersistentProtocol {
|
||||
this._socketDisposables.push(this._socketReader);
|
||||
this._socketDisposables.push(this._socketReader.onMessage(msg => this._receiveMessage(msg)));
|
||||
this._socketDisposables.push(this._socket.onClose(() => this._onSocketClose.fire()));
|
||||
this._socketDisposables.push(this._socket.onEnd(() => this._onClose.fire()));
|
||||
this._socketReader.acceptChunk(initialDataChunk);
|
||||
}
|
||||
|
||||
@@ -703,6 +713,8 @@ export class PersistentProtocol {
|
||||
}
|
||||
} else if (msg.type === ProtocolMessageType.Control) {
|
||||
this._onControlMessage.fire(msg.data);
|
||||
} else if (msg.type === ProtocolMessageType.Disconnect) {
|
||||
this._onClose.fire();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +240,7 @@ suite('Encoding', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4 });
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 });
|
||||
|
||||
assert.ok(detected);
|
||||
assert.ok(stream);
|
||||
@@ -260,7 +260,7 @@ suite('Encoding', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64 });
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 });
|
||||
|
||||
assert.ok(detected);
|
||||
assert.ok(stream);
|
||||
@@ -277,7 +277,7 @@ suite('Encoding', () => {
|
||||
}
|
||||
});
|
||||
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 512 });
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 512, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 });
|
||||
|
||||
assert.ok(detected);
|
||||
assert.ok(stream);
|
||||
@@ -292,7 +292,7 @@ suite('Encoding', () => {
|
||||
let path = getPathFromAmdModule(require, './fixtures/some_utf16be.css');
|
||||
let source = fs.createReadStream(path);
|
||||
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64 });
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 });
|
||||
|
||||
assert.equal(detected.encoding, 'utf16be');
|
||||
assert.equal(detected.seemsBinary, false);
|
||||
@@ -307,7 +307,7 @@ suite('Encoding', () => {
|
||||
|
||||
let path = getPathFromAmdModule(require, './fixtures/empty.txt');
|
||||
let source = fs.createReadStream(path);
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, {});
|
||||
let { detected, stream } = await encoding.toDecodeStream(source, { guessEncoding: false, overwriteEncoding: detected => detected || encoding.UTF8 });
|
||||
|
||||
let expected = await readAndDecodeFromDisk(path, detected.encoding);
|
||||
let actual = await readAllAsString(stream);
|
||||
|
||||
@@ -40,7 +40,7 @@ import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel';
|
||||
import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil';
|
||||
import { Button } from 'vs/base/browser/ui/button/button';
|
||||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
|
||||
const MAX_URL_LENGTH = platform.isWindows ? 2081 : 5400;
|
||||
|
||||
@@ -940,15 +940,24 @@ export class IssueReporter extends Disposable {
|
||||
</table>`;
|
||||
|
||||
systemInfo.remoteData.forEach(remote => {
|
||||
renderedData += `
|
||||
<hr>
|
||||
<table>
|
||||
<tr><td>Remote</td><td>${remote.hostName}</td></tr>
|
||||
<tr><td>OS</td><td>${remote.machineInfo.os}</td></tr>
|
||||
<tr><td>CPUs</td><td>${remote.machineInfo.cpus}</td></tr>
|
||||
<tr><td>Memory (System)</td><td>${remote.machineInfo.memory}</td></tr>
|
||||
<tr><td>VM</td><td>${remote.machineInfo.vmHint}</td></tr>
|
||||
</table>`;
|
||||
if (isRemoteDiagnosticError(remote)) {
|
||||
renderedData += `
|
||||
<hr>
|
||||
<table>
|
||||
<tr><td>Remote</td><td>${remote.hostName}</td></tr>
|
||||
<tr><td></td><td>${remote.errorMessage}</td></tr>
|
||||
</table>`;
|
||||
} else {
|
||||
renderedData += `
|
||||
<hr>
|
||||
<table>
|
||||
<tr><td>Remote</td><td>${remote.hostName}</td></tr>
|
||||
<tr><td>OS</td><td>${remote.machineInfo.os}</td></tr>
|
||||
<tr><td>CPUs</td><td>${remote.machineInfo.cpus}</td></tr>
|
||||
<tr><td>Memory (System)</td><td>${remote.machineInfo.memory}</td></tr>
|
||||
<tr><td>VM</td><td>${remote.machineInfo.vmHint}</td></tr>
|
||||
</table>`;
|
||||
}
|
||||
});
|
||||
|
||||
target.innerHTML = renderedData;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IssueType, ISettingSearchResult, IssueReporterExtensionData } from 'vs/platform/issue/common/issue';
|
||||
import { SystemInfo } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnosticsService';
|
||||
|
||||
export interface IssueReporterData {
|
||||
issueType: IssueType;
|
||||
@@ -76,7 +76,8 @@ ${this.getInfos()}
|
||||
|
||||
private getRemoteOSes(): string {
|
||||
if (this._data.systemInfo && this._data.systemInfo.remoteData.length) {
|
||||
return this._data.systemInfo.remoteData.map(remote => `Remote OS version: ${remote.machineInfo.os}`).join('\n') + '\n';
|
||||
return this._data.systemInfo.remoteData
|
||||
.map(remote => isRemoteDiagnosticError(remote) ? remote.errorMessage : `Remote OS version: ${remote.machineInfo.os}`).join('\n') + '\n';
|
||||
}
|
||||
|
||||
return '';
|
||||
@@ -169,7 +170,10 @@ ${this.getInfos()}
|
||||
|VM|${this._data.systemInfo.vmHint}|`;
|
||||
|
||||
this._data.systemInfo.remoteData.forEach(remote => {
|
||||
md += `
|
||||
if (isRemoteDiagnosticError(remote)) {
|
||||
md += `\n\n${remote.errorMessage}`;
|
||||
} else {
|
||||
md += `
|
||||
|
||||
|Item|Value|
|
||||
|---|---|
|
||||
@@ -178,6 +182,7 @@ ${this.getInfos()}
|
||||
|CPUs|${remote.machineInfo.cpus}|
|
||||
|Memory (System)|${remote.machineInfo.memory}|
|
||||
|VM|${remote.machineInfo.vmHint}|`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -3,9 +3,7 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<!-- // {{SQL CARBON EDIT}}
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: vscode-remote:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https:;">
|
||||
-->
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src 'self' https: data: vscode-remote:; media-src 'none'; child-src 'self'; object-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' https:; font-src 'self' https: vscode-remote:;">
|
||||
</head>
|
||||
<body class="vs-dark" aria-label="">
|
||||
</body>
|
||||
|
||||
@@ -1190,46 +1190,62 @@ export class WindowsManager implements IWindowsMainService {
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we are not asked to open a workspace or folder that is already opened
|
||||
if (cliArgs.length && cliArgs.some(path => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, URI.file(path)))) {
|
||||
cliArgs = [];
|
||||
if (!Array.isArray(extensionDevelopmentPath)) {
|
||||
extensionDevelopmentPath = [extensionDevelopmentPath];
|
||||
}
|
||||
|
||||
if (folderUris.length && folderUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) {
|
||||
folderUris = [];
|
||||
let authority = '';
|
||||
for (let p of extensionDevelopmentPath) {
|
||||
if (p.match(/^[a-zA-Z][a-zA-Z0-9\+\-\.]+:/)) {
|
||||
const url = URI.parse(p);
|
||||
if (url.scheme === Schemas.vscodeRemote) {
|
||||
if (authority) {
|
||||
if (url.authority !== authority) {
|
||||
this.logService.error('more than one extension development path authority');
|
||||
}
|
||||
} else {
|
||||
authority = url.authority;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fileUris.length && fileUris.some(uri => !!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, this.argToUri(uri)))) {
|
||||
fileUris = [];
|
||||
}
|
||||
// Make sure that we do not try to open:
|
||||
// - a workspace or folder that is already opened
|
||||
// - a workspace or file that has a different authority as the extension development.
|
||||
|
||||
cliArgs = cliArgs.filter(path => {
|
||||
const uri = URI.file(path);
|
||||
if (!!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, uri)) {
|
||||
return false;
|
||||
}
|
||||
return uri.authority === authority;
|
||||
});
|
||||
|
||||
folderUris = folderUris.filter(uri => {
|
||||
const u = this.argToUri(uri);
|
||||
if (!!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, u)) {
|
||||
return false;
|
||||
}
|
||||
return u ? u.authority === authority : false;
|
||||
});
|
||||
|
||||
fileUris = fileUris.filter(uri => {
|
||||
const u = this.argToUri(uri);
|
||||
if (!!findWindowOnWorkspaceOrFolderUri(WindowsManager.WINDOWS, u)) {
|
||||
return false;
|
||||
}
|
||||
return u ? u.authority === authority : false;
|
||||
});
|
||||
|
||||
openConfig.cli._ = cliArgs;
|
||||
openConfig.cli['folder-uri'] = folderUris;
|
||||
openConfig.cli['file-uri'] = fileUris;
|
||||
|
||||
if (Array.isArray(extensionDevelopmentPath)) {
|
||||
let authority: string | undefined = undefined;
|
||||
for (let p of extensionDevelopmentPath) {
|
||||
const match = p.match(/^vscode-remote:\/\/([^\/]+)/);
|
||||
if (match) {
|
||||
const auth = URI.parse(p).authority;
|
||||
if (authority) {
|
||||
if (auth !== authority) {
|
||||
console.log('more than one authority');
|
||||
}
|
||||
} else {
|
||||
authority = auth;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if there are no files or folders cli args left, use the "remote" cli argument
|
||||
if (!cliArgs.length && !folderUris.length && !fileUris.length) {
|
||||
if (authority) {
|
||||
openConfig.cli['remote'] = authority;
|
||||
}
|
||||
|
||||
} else {
|
||||
const match = extensionDevelopmentPath.match(/^vscode-remote:\/\/([^\/]+)/);
|
||||
if (match) {
|
||||
openConfig.cli['remote'] = URI.parse(extensionDevelopmentPath).authority;
|
||||
openConfig.cli.remote = authority;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1581,7 +1597,8 @@ export class WindowsManager implements IWindowsMainService {
|
||||
cli = { ...cli, remote };
|
||||
}
|
||||
const forceReuseWindow = options && options.reuseWindow;
|
||||
return this.open({ context, cli, forceEmpty: true, forceReuseWindow });
|
||||
const forceNewWindow = !forceReuseWindow;
|
||||
return this.open({ context, cli, forceEmpty: true, forceNewWindow, forceReuseWindow });
|
||||
}
|
||||
|
||||
openNewTabbedWindow(context: OpenContext): ICodeWindow[] {
|
||||
|
||||
@@ -55,7 +55,7 @@ export class OpenerService implements IOpenerService {
|
||||
|
||||
if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https) || equalsIgnoreCase(scheme, Schemas.mailto)) {
|
||||
// open http or default mail application
|
||||
dom.windowOpenNoOpener(resource.toString(true));
|
||||
dom.windowOpenNoOpener(encodeURI(resource.toString(true)));
|
||||
return Promise.resolve(true);
|
||||
|
||||
} else if (equalsIgnoreCase(scheme, Schemas.command)) {
|
||||
|
||||
@@ -1288,6 +1288,7 @@ export interface CommentThread2 {
|
||||
onDidChangeRange: Event<IRange>;
|
||||
onDidChangeLabel: Event<string>;
|
||||
onDidChangeCollasibleState: Event<CommentThreadCollapsibleState | undefined>;
|
||||
isDisposed: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1312,6 +1313,7 @@ export interface CommentThread {
|
||||
comments: Comment[] | undefined;
|
||||
collapsibleState?: CommentThreadCollapsibleState;
|
||||
reply?: Command;
|
||||
isDisposed?: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -399,7 +399,7 @@ export abstract class BaseEditorSimpleWorker {
|
||||
|
||||
// ---- BEGIN minimal edits ---------------------------------------------------------------
|
||||
|
||||
private static readonly _diffLimit = 10000;
|
||||
private static readonly _diffLimit = 100000;
|
||||
|
||||
public computeMoreMinimalEdits(modelUrl: string, edits: TextEdit[]): Promise<TextEdit[]> {
|
||||
const model = this._getModel(modelUrl);
|
||||
@@ -432,7 +432,7 @@ export abstract class BaseEditorSimpleWorker {
|
||||
}
|
||||
|
||||
const original = model.getValueInRange(range);
|
||||
text = text!.replace(/\r\n|\n|\r/g, model.eol);
|
||||
text = text.replace(/\r\n|\n|\r/g, model.eol);
|
||||
|
||||
if (original === text) {
|
||||
// noop
|
||||
|
||||
@@ -21,6 +21,8 @@ import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { regExpFlags } from 'vs/base/common/strings';
|
||||
import { isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { StopWatch } from 'vs/base/common/stopwatch';
|
||||
|
||||
/**
|
||||
* Stop syncing a model to the worker if it was not needed for 1 min.
|
||||
@@ -48,14 +50,16 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
|
||||
|
||||
private readonly _modelService: IModelService;
|
||||
private readonly _workerManager: WorkerManager;
|
||||
|
||||
private readonly _logService: ILogService;
|
||||
constructor(
|
||||
@IModelService modelService: IModelService,
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService
|
||||
@ITextResourceConfigurationService configurationService: ITextResourceConfigurationService,
|
||||
@ILogService logService: ILogService
|
||||
) {
|
||||
super();
|
||||
this._modelService = modelService;
|
||||
this._workerManager = this._register(new WorkerManager(this._modelService));
|
||||
this._logService = logService;
|
||||
|
||||
// todo@joh make sure this happens only once
|
||||
this._register(modes.LinkProviderRegistry.register('*', {
|
||||
@@ -96,7 +100,10 @@ export class EditorWorkerServiceImpl extends Disposable implements IEditorWorker
|
||||
if (!canSyncModel(this._modelService, resource)) {
|
||||
return Promise.resolve(edits); // File too large
|
||||
}
|
||||
return this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits));
|
||||
const sw = StopWatch.create(true);
|
||||
const result = this._workerManager.withWorker().then(client => client.computeMoreMinimalEdits(resource, edits));
|
||||
result.finally(() => this._logService.trace('FORMAT#computeMoreMinimalEdits', resource.toString(true), sw.elapsed()));
|
||||
return result;
|
||||
|
||||
} else {
|
||||
return Promise.resolve(undefined);
|
||||
|
||||
@@ -145,8 +145,6 @@ export module StaticServices {
|
||||
|
||||
export const markerDecorationsService = define(IMarkerDecorationsService, (o) => new MarkerDecorationsService(modelService.get(o), markerService.get(o)));
|
||||
|
||||
export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o)));
|
||||
|
||||
export const standaloneThemeService = define(IStandaloneThemeService, () => new StandaloneThemeServiceImpl());
|
||||
|
||||
export const codeEditorService = define(ICodeEditorService, (o) => new StandaloneCodeEditorServiceImpl(standaloneThemeService.get(o)));
|
||||
@@ -157,6 +155,8 @@ export module StaticServices {
|
||||
|
||||
export const logService = define(ILogService, () => new NullLogService());
|
||||
|
||||
export const editorWorkerService = define(IEditorWorkerService, (o) => new EditorWorkerServiceImpl(modelService.get(o), resourceConfigurationService.get(o), logService.get(o)));
|
||||
|
||||
export const suggestMemoryService = define(ISuggestMemoryService, (o) => new SuggestMemoryService(storageService.get(o), configurationService.get(o)));
|
||||
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface SystemInfo extends IMachineInfo {
|
||||
processArgs: string;
|
||||
gpuStatus: any;
|
||||
screenReader: string;
|
||||
remoteData: IRemoteDiagnosticInfo[];
|
||||
remoteData: (IRemoteDiagnosticInfo | IRemoteDiagnosticError)[];
|
||||
load?: string;
|
||||
}
|
||||
|
||||
|
||||
@@ -11,14 +11,8 @@ import product from 'vs/platform/product/node/product';
|
||||
|
||||
export function isUIExtension(manifest: IExtensionManifest, uiContributions: string[], configurationService: IConfigurationService): boolean {
|
||||
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
|
||||
const { ui, workspace } = configurationService.getValue<{ ui: string[], workspace: string[] }>('extensions.extensionKind') || { ui: [], workspace: [] };
|
||||
if (isNonEmptyArray(workspace) && workspace.some(id => areSameExtensions({ id }, { id: extensionId }))) {
|
||||
return false;
|
||||
}
|
||||
if (isNonEmptyArray(ui) && ui.some(id => areSameExtensions({ id }, { id: extensionId }))) {
|
||||
return true;
|
||||
}
|
||||
switch (manifest.extensionKind) {
|
||||
const extensionKind = getExtensionKind(manifest, configurationService);
|
||||
switch (extensionKind) {
|
||||
case 'ui': return true;
|
||||
case 'workspace': return false;
|
||||
default: {
|
||||
@@ -38,3 +32,14 @@ export function isUIExtension(manifest: IExtensionManifest, uiContributions: str
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getExtensionKind(manifest: IExtensionManifest, configurationService: IConfigurationService): string | undefined {
|
||||
const extensionId = getGalleryExtensionId(manifest.publisher, manifest.name);
|
||||
const configuredExtensionKinds = configurationService.getValue<{ [key: string]: string }>('remote.extensionKind') || {};
|
||||
for (const id of Object.keys(configuredExtensionKinds)) {
|
||||
if (areSameExtensions({ id: extensionId }, { id })) {
|
||||
return configuredExtensionKinds[id];
|
||||
}
|
||||
}
|
||||
return manifest.extensionKind;
|
||||
}
|
||||
|
||||
@@ -34,8 +34,9 @@ export function resolveCommonProperties(commit: string | undefined, version: str
|
||||
result['common.nodePlatform'] = process.platform;
|
||||
// __GDPR__COMMON__ "common.nodeArch" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
|
||||
result['common.nodeArch'] = process.arch;
|
||||
|
||||
// __GDPR__COMMON__ "common.product" : { "classification": "SystemMetaData", "purpose": "PerformanceAndHealth" }
|
||||
// {{SQL CARBON EDIT}}
|
||||
result['common.product'] = product.nameShort || 'desktop';
|
||||
result['common.application.name'] = product.nameLong;
|
||||
|
||||
// dynamic properties which value differs on each call
|
||||
|
||||
39
src/vs/vscode.proposed.d.ts
vendored
39
src/vs/vscode.proposed.d.ts
vendored
@@ -1371,4 +1371,43 @@ declare module 'vscode' {
|
||||
group?: string;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Workspace URI Ben
|
||||
|
||||
export namespace workspace {
|
||||
|
||||
/**
|
||||
* The location of the workspace file, for example:
|
||||
*
|
||||
* `file:///Users/name/Development/myProject.code-workspace`
|
||||
*
|
||||
* or
|
||||
*
|
||||
* `untitled:1555503116870`
|
||||
*
|
||||
* for a workspace that is untitled and not yet saved.
|
||||
*
|
||||
* Depending on the workspace that is opened, the value will be:
|
||||
* * `undefined` when no workspace or a single folder is opened
|
||||
* * the path of the workspace file as `Uri` otherwise. if the workspace
|
||||
* is untitled, the returned URI will use the `untitled:` scheme
|
||||
*
|
||||
* The location can e.g. be used with the `vscode.openFolder` command to
|
||||
* open the workspace again after it has been closed.
|
||||
*
|
||||
* **Example:**
|
||||
* ```typescript
|
||||
* vscode.commands.executeCommand('vscode.openFolder', uriOfWorkspace);
|
||||
* ```
|
||||
*
|
||||
* **Note:** it is not advised to use `workspace.workspaceFile` to write
|
||||
* configuration data into the file. You can use `workspace.getConfiguration().update()`
|
||||
* for that purpose which will work both when a single folder is opened as
|
||||
* well as an untitled or saved workspace.
|
||||
*/
|
||||
export const workspaceFile: Uri | undefined;
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
}
|
||||
|
||||
@@ -184,6 +184,12 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
|
||||
private _onDidChangeCollasibleState = new Emitter<modes.CommentThreadCollapsibleState | undefined>();
|
||||
public onDidChangeCollasibleState = this._onDidChangeCollasibleState.event;
|
||||
|
||||
private _isDisposed: boolean;
|
||||
|
||||
get isDisposed(): boolean {
|
||||
return this._isDisposed;
|
||||
}
|
||||
|
||||
constructor(
|
||||
public commentThreadHandle: number,
|
||||
public controller: MainThreadCommentController,
|
||||
@@ -191,7 +197,9 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
|
||||
public threadId: string,
|
||||
public resource: string,
|
||||
private _range: IRange
|
||||
) { }
|
||||
) {
|
||||
this._isDisposed = false;
|
||||
}
|
||||
|
||||
batchUpdate(
|
||||
range: IRange,
|
||||
@@ -210,7 +218,16 @@ export class MainThreadCommentThread implements modes.CommentThread2 {
|
||||
this._collapsibleState = collapsibleState;
|
||||
}
|
||||
|
||||
dispose() { }
|
||||
dispose() {
|
||||
this._isDisposed = true;
|
||||
this._onDidChangeAcceptInputCommand.dispose();
|
||||
this._onDidChangeAdditionalCommands.dispose();
|
||||
this._onDidChangeCollasibleState.dispose();
|
||||
this._onDidChangeComments.dispose();
|
||||
this._onDidChangeInput.dispose();
|
||||
this._onDidChangeLabel.dispose();
|
||||
this._onDidChangeRange.dispose();
|
||||
}
|
||||
|
||||
toJSON(): any {
|
||||
return {
|
||||
@@ -493,6 +510,8 @@ export class MainThreadComments extends Disposable implements MainThreadComments
|
||||
return undefined;
|
||||
}
|
||||
|
||||
console.log('createCommentThread', commentThreadHandle);
|
||||
|
||||
return provider.createCommentThread(commentThreadHandle, threadId, resource, range);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,23 +46,20 @@ export class MainThreadWindow implements MainThreadWindowShape {
|
||||
}
|
||||
|
||||
async $openUri(uriComponent: UriComponents, options: IOpenUriOptions): Promise<boolean> {
|
||||
const uri = URI.revive(uriComponent);
|
||||
let uri = URI.revive(uriComponent);
|
||||
if (options.allowTunneling && !!this.environmentService.configuration.remoteAuthority) {
|
||||
if (uri.scheme === 'http' || uri.scheme === 'https') {
|
||||
const port = this.getLocalhostPort(uri);
|
||||
if (typeof port === 'number') {
|
||||
const tunnel = await this.getOrCreateTunnel(port);
|
||||
if (tunnel) {
|
||||
const tunneledUrl = uri.toString().replace(
|
||||
new RegExp(`^${uri.scheme}://localhost:${port}/`),
|
||||
`${uri.scheme}://localhost:${tunnel.tunnelLocalPort}/`);
|
||||
return this.windowsService.openExternal(tunneledUrl);
|
||||
uri = uri.with({ authority: `localhost:${tunnel.tunnelLocalPort}` });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return this.windowsService.openExternal(uri.toString(true));
|
||||
return this.windowsService.openExternal(encodeURI(uri.toString(true)));
|
||||
}
|
||||
|
||||
private getLocalhostPort(uri: URI): number | undefined {
|
||||
|
||||
@@ -22,6 +22,8 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { isEqualOrParent } from 'vs/base/common/resources';
|
||||
|
||||
@extHostNamedCustomer(MainContext.MainThreadWorkspace)
|
||||
export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@@ -40,7 +42,8 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
@IStatusbarService private readonly _statusbarService: IStatusbarService,
|
||||
@IWindowService private readonly _windowService: IWindowService,
|
||||
@IInstantiationService private readonly _instantiationService: IInstantiationService,
|
||||
@ILabelService private readonly _labelService: ILabelService
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IEnvironmentService private readonly _environmentService: IEnvironmentService
|
||||
) {
|
||||
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace);
|
||||
this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)));
|
||||
@@ -110,6 +113,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape {
|
||||
}
|
||||
return {
|
||||
configuration: workspace.configuration || undefined,
|
||||
isUntitled: workspace.configuration ? isEqualOrParent(workspace.configuration, this._environmentService.untitledWorkspacesHome) : false,
|
||||
folders: workspace.folders,
|
||||
id: workspace.id,
|
||||
name: this._labelService.getWorkspaceLabel(workspace)
|
||||
|
||||
@@ -15,6 +15,7 @@ import { IWindowsService, IOpenSettings, IURIToOpen } from 'vs/platform/windows/
|
||||
import { IDownloadService } from 'vs/platform/download/common/download';
|
||||
import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IRecent } from 'vs/platform/history/common/history';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
// -----------------------------------------------------------------
|
||||
// The following commands are registered on both sides separately.
|
||||
@@ -51,7 +52,7 @@ export class OpenFolderAPICommand {
|
||||
}
|
||||
const options: IOpenSettings = { forceNewWindow: arg.forceNewWindow, noRecentEntry: arg.noRecentEntry };
|
||||
uri = URI.revive(uri);
|
||||
const uriToOpen: IURIToOpen = hasWorkspaceFileExtension(uri.path) ? { workspaceUri: uri } : { folderUri: uri };
|
||||
const uriToOpen: IURIToOpen = (hasWorkspaceFileExtension(uri.path) || uri.scheme === Schemas.untitled) ? { workspaceUri: uri } : { folderUri: uri };
|
||||
return executor.executeCommand('_files.windowOpen', [uriToOpen], options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ export interface IStaticWorkspaceData {
|
||||
id: string;
|
||||
name: string;
|
||||
configuration?: UriComponents | null;
|
||||
isUntitled?: boolean | null;
|
||||
}
|
||||
|
||||
export interface IWorkspaceData extends IStaticWorkspaceData {
|
||||
|
||||
@@ -10,7 +10,7 @@ import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { TernarySearchTree } from 'vs/base/common/map';
|
||||
import { Counter } from 'vs/base/common/numbers';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { basenameOrAuthority, dirname, isEqual, relativePath } from 'vs/base/common/resources';
|
||||
import { basenameOrAuthority, dirname, isEqual, relativePath, basename } from 'vs/base/common/resources';
|
||||
import { compare } from 'vs/base/common/strings';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -24,6 +24,7 @@ import * as vscode from 'vscode';
|
||||
import { ExtHostWorkspaceShape, IWorkspaceData, MainThreadMessageServiceShape, MainThreadWorkspaceShape, IMainContext, MainContext, IStaticWorkspaceData } from './extHost.protocol';
|
||||
import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { Barrier } from 'vs/base/common/async';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export interface IExtHostWorkspaceProvider {
|
||||
getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise<vscode.WorkspaceFolder | undefined>;
|
||||
@@ -67,7 +68,7 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
return { workspace: null, added: [], removed: [] };
|
||||
}
|
||||
|
||||
const { id, name, folders } = data;
|
||||
const { id, name, folders, configuration, isUntitled } = data;
|
||||
const newWorkspaceFolders: vscode.WorkspaceFolder[] = [];
|
||||
|
||||
// If we have an existing workspace, we try to find the folders that match our
|
||||
@@ -95,7 +96,7 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
// make sure to restore sort order based on index
|
||||
newWorkspaceFolders.sort((f1, f2) => f1.index < f2.index ? -1 : 1);
|
||||
|
||||
const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders);
|
||||
const workspace = new ExtHostWorkspaceImpl(id, name, newWorkspaceFolders, configuration ? URI.revive(configuration) : null, !!isUntitled);
|
||||
const { added, removed } = delta(oldWorkspace ? oldWorkspace.workspaceFolders : [], workspace.workspaceFolders, compareWorkspaceFolderByUri);
|
||||
|
||||
return { workspace, added, removed };
|
||||
@@ -115,8 +116,8 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
private readonly _workspaceFolders: vscode.WorkspaceFolder[] = [];
|
||||
private readonly _structure = TernarySearchTree.forPaths<vscode.WorkspaceFolder>();
|
||||
|
||||
constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[]) {
|
||||
super(id, folders.map(f => new WorkspaceFolder(f)));
|
||||
constructor(id: string, private _name: string, folders: vscode.WorkspaceFolder[], configuration: URI | null, private _isUntitled: boolean) {
|
||||
super(id, folders.map(f => new WorkspaceFolder(f)), configuration);
|
||||
|
||||
// setup the workspace folder data structure
|
||||
folders.forEach(folder => {
|
||||
@@ -129,6 +130,10 @@ class ExtHostWorkspaceImpl extends Workspace {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get isUntitled(): boolean {
|
||||
return this._isUntitled;
|
||||
}
|
||||
|
||||
get workspaceFolders(): vscode.WorkspaceFolder[] {
|
||||
return this._workspaceFolders.slice(0);
|
||||
}
|
||||
@@ -175,7 +180,7 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadWorkspace);
|
||||
this._messageService = mainContext.getProxy(MainContext.MainThreadMessageService);
|
||||
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, []) : undefined;
|
||||
this._confirmedWorkspace = data ? new ExtHostWorkspaceImpl(data.id, data.name, [], data.configuration ? URI.revive(data.configuration) : null, !!data.isUntitled) : undefined;
|
||||
}
|
||||
|
||||
$initializeWorkspace(data: IWorkspaceData): void {
|
||||
@@ -197,6 +202,20 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
return this._actualWorkspace ? this._actualWorkspace.name : undefined;
|
||||
}
|
||||
|
||||
get workspaceFile(): vscode.Uri | undefined {
|
||||
if (this._actualWorkspace) {
|
||||
if (this._actualWorkspace.configuration) {
|
||||
if (this._actualWorkspace.isUntitled) {
|
||||
return URI.from({ scheme: Schemas.untitled, path: basename(dirname(this._actualWorkspace.configuration)) }); // Untitled Worspace: return untitled URI
|
||||
}
|
||||
|
||||
return this._actualWorkspace.configuration; // Workspace: return the configuration location
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
private get _actualWorkspace(): ExtHostWorkspaceImpl | undefined {
|
||||
return this._unconfirmedWorkspace || this._confirmedWorkspace;
|
||||
}
|
||||
@@ -365,7 +384,8 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac
|
||||
id: this._actualWorkspace.id,
|
||||
name: this._actualWorkspace.name,
|
||||
configuration: this._actualWorkspace.configuration,
|
||||
folders
|
||||
folders,
|
||||
isUntitled: this._actualWorkspace.isUntitled
|
||||
} as IWorkspaceData, this._actualWorkspace).workspace || undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,7 +116,7 @@ export function createApiFactory(
|
||||
const extHostFileSystem = rpcProtocol.set(ExtHostContext.ExtHostFileSystem, new ExtHostFileSystem(rpcProtocol, extHostLanguageFeatures));
|
||||
const extHostFileSystemEvent = rpcProtocol.set(ExtHostContext.ExtHostFileSystemEventService, new ExtHostFileSystemEventService(rpcProtocol, extHostDocumentsAndEditors));
|
||||
const extHostQuickOpen = rpcProtocol.set(ExtHostContext.ExtHostQuickOpen, new ExtHostQuickOpen(rpcProtocol, extHostWorkspace, extHostCommands));
|
||||
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostLogService));
|
||||
const extHostTerminalService = rpcProtocol.set(ExtHostContext.ExtHostTerminalService, new ExtHostTerminalService(rpcProtocol, extHostConfiguration, extHostWorkspace, extHostDocumentsAndEditors, extHostLogService));
|
||||
// {{SQL CARBON EDIT}}
|
||||
// const extHostDebugService = rpcProtocol.set(ExtHostContext.ExtHostDebugService, new ExtHostDebugService(rpcProtocol, extHostWorkspace, extensionService, extHostDocumentsAndEditors, extHostConfiguration, extHostTerminalService, extHostCommands));
|
||||
const extHostSCM = rpcProtocol.set(ExtHostContext.ExtHostSCM, new ExtHostSCM(rpcProtocol, extHostCommands, extHostLogService));
|
||||
@@ -539,6 +539,12 @@ export function createApiFactory(
|
||||
set name(value) {
|
||||
throw errors.readonly();
|
||||
},
|
||||
get workspaceFile() {
|
||||
return extHostWorkspace.workspaceFile;
|
||||
},
|
||||
set workspaceFile(value) {
|
||||
throw errors.readonly();
|
||||
},
|
||||
updateWorkspaceFolders: (index, deleteCount, ...workspaceFoldersToAdd) => {
|
||||
return extHostWorkspace.updateWorkspaceFolders(extension, index, deleteCount || 0, ...workspaceFoldersToAdd);
|
||||
},
|
||||
|
||||
@@ -65,6 +65,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
private readonly _mainThreadExtensionsProxy: MainThreadExtensionServiceShape;
|
||||
|
||||
private readonly _almostReadyToRunExtensions: Barrier;
|
||||
private readonly _readyToStartExtensionHost: Barrier;
|
||||
private readonly _readyToRunExtensions: Barrier;
|
||||
private readonly _registry: ExtensionDescriptionRegistry;
|
||||
private readonly _storage: ExtHostStorage;
|
||||
@@ -101,6 +102,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
this._mainThreadExtensionsProxy = this._extHostContext.getProxy(MainContext.MainThreadExtensionService);
|
||||
|
||||
this._almostReadyToRunExtensions = new Barrier();
|
||||
this._readyToStartExtensionHost = new Barrier();
|
||||
this._readyToRunExtensions = new Barrier();
|
||||
this._registry = new ExtensionDescriptionRegistry(initData.extensions);
|
||||
this._storage = new ExtHostStorage(this._extHostContext);
|
||||
@@ -171,7 +173,7 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
this._almostReadyToRunExtensions.open();
|
||||
|
||||
await this._extHostWorkspace.waitForInitializeCall();
|
||||
this._readyToRunExtensions.open();
|
||||
this._readyToStartExtensionHost.open();
|
||||
} catch (err) {
|
||||
errors.onUnexpectedError(err);
|
||||
}
|
||||
@@ -581,7 +583,8 @@ export class ExtHostExtensionService implements ExtHostExtensionServiceShape {
|
||||
}
|
||||
this._started = true;
|
||||
|
||||
return this._readyToRunExtensions.wait()
|
||||
return this._readyToStartExtensionHost.wait()
|
||||
.then(() => this._readyToRunExtensions.open())
|
||||
.then(() => this._handleEagerExtensions())
|
||||
.then(() => this._handleExtensionTests())
|
||||
.then(() => {
|
||||
|
||||
@@ -13,10 +13,14 @@ import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ExtHostTerminalServiceShape, MainContext, MainThreadTerminalServiceShape, IMainContext, ShellLaunchConfigDto } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { EXT_HOST_CREATION_DELAY, IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { sanitizeProcessEnvironment } from 'vs/base/common/processes';
|
||||
import { ExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
// {{SQL CARBON EDIT}}
|
||||
// import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
|
||||
const RENDERER_NO_PROCESS_ID = -1;
|
||||
|
||||
@@ -288,6 +292,8 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
||||
constructor(
|
||||
mainContext: IMainContext,
|
||||
private _extHostConfiguration: ExtHostConfiguration,
|
||||
private _extHostWorkspace: ExtHostWorkspace,
|
||||
private _extHostDocumentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
private _logService: ILogService,
|
||||
) {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
|
||||
@@ -436,6 +442,16 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
||||
}
|
||||
}
|
||||
|
||||
private _apiInspectConfigToPlain<T>(
|
||||
config: { key: string; defaultValue?: T; globalValue?: T; workspaceValue?: T, workspaceFolderValue?: T } | undefined
|
||||
): { user: T | undefined, value: T | undefined, default: T | undefined } {
|
||||
return {
|
||||
user: config ? config.globalValue : undefined,
|
||||
value: config ? config.workspaceValue : undefined,
|
||||
default: config ? config.defaultValue : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
public async $createProcess(id: number, shellLaunchConfigDto: ShellLaunchConfigDto, activeWorkspaceRootUriComponents: UriComponents, cols: number, rows: number, isWorkspaceShellAllowed: boolean): Promise<void> {
|
||||
const shellLaunchConfig: IShellLaunchConfig = {
|
||||
name: shellLaunchConfigDto.name,
|
||||
@@ -447,57 +463,54 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
||||
|
||||
// Merge in shell and args from settings
|
||||
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
if (!shellLaunchConfig.executable) {
|
||||
const fetchSetting = (key: string) => {
|
||||
const setting = configProvider
|
||||
.getConfiguration(key.substr(0, key.lastIndexOf('.')))
|
||||
.inspect<string | string[]>(key.substr(key.lastIndexOf('.') + 1));
|
||||
return {
|
||||
user: setting ? setting.globalValue : undefined,
|
||||
value: setting ? setting.workspaceValue : undefined,
|
||||
default: setting ? setting.defaultValue : undefined,
|
||||
};
|
||||
return this._apiInspectConfigToPlain<string | string[]>(setting);
|
||||
};
|
||||
terminalEnvironment.mergeDefaultShellPathAndArgs(shellLaunchConfig, fetchSetting, isWorkspaceShellAllowed || false);
|
||||
}
|
||||
|
||||
// Get the initial cwd
|
||||
const configProvider = await this._extHostConfiguration.getConfigProvider();
|
||||
const terminalConfig = configProvider.getConfiguration('terminal.integrated');
|
||||
const activeWorkspaceRootUri = URI.revive(activeWorkspaceRootUriComponents);
|
||||
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, os.homedir(), activeWorkspaceRootUri, terminalConfig.cwd);
|
||||
|
||||
// TODO: Pull in and resolve config settings
|
||||
// // Resolve env vars from config and shell
|
||||
// const lastActiveWorkspaceRoot = this._workspaceContextService.getWorkspaceFolder(lastActiveWorkspaceRootUri);
|
||||
// const envFromConfig = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...terminalConfig.env[platformKey] }, lastActiveWorkspaceRoot);
|
||||
const envFromConfig = { ...terminalConfig.env[platformKey] };
|
||||
// const envFromShell = terminalEnvironment.resolveConfigurationVariables(this._configurationResolverService, { ...shellLaunchConfig.env }, lastActiveWorkspaceRoot);
|
||||
|
||||
// Merge process env with the env from config
|
||||
const env = { ...process.env };
|
||||
Object.keys(env).filter(k => env[k] === undefined).forEach(k => {
|
||||
delete env[k];
|
||||
});
|
||||
const castedEnv = env as platform.IProcessEnvironment;
|
||||
terminalEnvironment.mergeEnvironments(castedEnv, envFromConfig);
|
||||
terminalEnvironment.mergeEnvironments(castedEnv, shellLaunchConfig.env);
|
||||
|
||||
// Sanitize the environment, removing any undesirable VS Code and Electron environment
|
||||
// variables
|
||||
sanitizeProcessEnvironment(castedEnv, 'VSCODE_IPC_HOOK_CLI');
|
||||
|
||||
// Continue env initialization, merging in the env from the launch
|
||||
// config and adding keys that are needed to create the process
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(castedEnv, pkg.version, platform.locale, terminalConfig.get('setLocaleVariables') as boolean);
|
||||
// Get the environment
|
||||
const apiLastActiveWorkspace = await this._extHostWorkspace.getWorkspaceFolder(activeWorkspaceRootUri);
|
||||
const lastActiveWorkspace = apiLastActiveWorkspace ? {
|
||||
uri: apiLastActiveWorkspace.uri,
|
||||
name: apiLastActiveWorkspace.name,
|
||||
index: apiLastActiveWorkspace.index,
|
||||
toResource: () => {
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
} as IWorkspaceFolder : null;
|
||||
const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect<ITerminalEnvironment>(`env.${platformKey}`));
|
||||
const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2();
|
||||
// {{SQL CARBON EDIT}}
|
||||
// const variableResolver = workspaceFolders ? new ExtHostVariableResolverService(workspaceFolders, this._extHostDocumentsAndEditors, configProvider) : undefined;
|
||||
const variableResolver = undefined;
|
||||
const env = terminalEnvironment.createTerminalEnvironment(
|
||||
shellLaunchConfig,
|
||||
lastActiveWorkspace,
|
||||
envFromConfig,
|
||||
variableResolver,
|
||||
isWorkspaceShellAllowed,
|
||||
pkg.version,
|
||||
terminalConfig.get<boolean>('setLocaleVariables', false)
|
||||
);
|
||||
|
||||
// Fork the process and listen for messages
|
||||
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, castedEnv);
|
||||
const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, castedEnv, terminalConfig.get('windowsEnableConpty') as boolean);
|
||||
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
|
||||
const p = new TerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, terminalConfig.get('windowsEnableConpty') as boolean);
|
||||
p.onProcessIdReady(pid => this._proxy.$sendProcessPid(id, pid));
|
||||
p.onProcessTitleChanged(title => this._proxy.$sendProcessTitle(id, title));
|
||||
p.onProcessData(data => this._proxy.$sendProcessData(id, data));
|
||||
p.onProcessExit((exitCode) => this._onProcessExit(id, exitCode));
|
||||
p.onProcessExit(exitCode => this._onProcessExit(id, exitCode));
|
||||
this._terminalProcesses[id] = p;
|
||||
}
|
||||
|
||||
@@ -541,7 +554,6 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
||||
|
||||
// Send exit event to main side
|
||||
this._proxy.$sendProcessExit(id, exitCode);
|
||||
|
||||
}
|
||||
|
||||
private _getTerminalByIdEventually(id: number, retries: number = 5): Promise<ExtHostTerminal> {
|
||||
|
||||
@@ -145,12 +145,13 @@ export class NoTabsTitleControl extends TitleControl {
|
||||
this.redraw();
|
||||
}
|
||||
|
||||
updateEditorLabel(editor?: IEditorInput): void {
|
||||
if (!editor) {
|
||||
editor = withNullAsUndefined(this.group.activeEditor);
|
||||
}
|
||||
if (editor) {
|
||||
this.ifEditorIsActive(editor, () => this.redraw());
|
||||
updateEditorLabel(editor: IEditorInput): void {
|
||||
this.ifEditorIsActive(editor, () => this.redraw());
|
||||
}
|
||||
|
||||
updateEditorLabels(): void {
|
||||
if (this.group.activeEditor) {
|
||||
this.updateEditorLabel(this.group.activeEditor); // we only have the active one to update
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -371,6 +371,12 @@ export class TabsTitleControl extends TitleControl {
|
||||
|
||||
updateEditorLabel(editor: IEditorInput): void {
|
||||
|
||||
// Update all labels to account for changes to tab labels
|
||||
this.updateEditorLabels();
|
||||
}
|
||||
|
||||
updateEditorLabels(): void {
|
||||
|
||||
// A change to a label requires to recompute all labels
|
||||
this.computeTabLabels();
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ export abstract class TitleControl extends Themable {
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.extensionService.onDidRegisterExtensions(() => this.updateEditorActionsToolbar()));
|
||||
this._register(this.labelService.onDidChangeFormatters(() => this.updateEditorLabel()));
|
||||
this._register(this.labelService.onDidChangeFormatters(() => this.updateEditorLabels()));
|
||||
}
|
||||
|
||||
protected abstract create(parent: HTMLElement): void;
|
||||
@@ -343,7 +343,9 @@ export abstract class TitleControl extends Themable {
|
||||
|
||||
abstract setActive(isActive: boolean): void;
|
||||
|
||||
abstract updateEditorLabel(editor?: IEditorInput): void;
|
||||
abstract updateEditorLabel(editor: IEditorInput): void;
|
||||
|
||||
abstract updateEditorLabels(): void;
|
||||
|
||||
abstract updateEditorDirty(editor: IEditorInput): void;
|
||||
|
||||
|
||||
@@ -153,7 +153,7 @@ class NotificationMessageRenderer {
|
||||
|
||||
const anchor = document.createElement('a');
|
||||
anchor.textContent = link.name;
|
||||
anchor.title = link.href;
|
||||
anchor.title = link.title;
|
||||
anchor.href = link.href;
|
||||
|
||||
if (actionHandler) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { isPromiseCanceledError } from 'vs/base/common/errors';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { isErrorWithActions } from 'vs/base/common/errorsWithActions';
|
||||
import { startsWith } from 'vs/base/common/strings';
|
||||
import { localize } from 'vs/nls';
|
||||
|
||||
export interface INotificationsModel {
|
||||
|
||||
@@ -306,8 +308,9 @@ export class NotificationViewItemProgress extends Disposable implements INotific
|
||||
}
|
||||
|
||||
export interface IMessageLink {
|
||||
name: string;
|
||||
href: string;
|
||||
name: string;
|
||||
title: string;
|
||||
offset: number;
|
||||
length: number;
|
||||
}
|
||||
@@ -325,7 +328,7 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
|
||||
// Example link: "Some message with [link text](http://link.href)."
|
||||
// RegEx: [, anything not ], ], (, http://|https://|command:, no whitespace)
|
||||
private static LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)\)/gi;
|
||||
private static LINK_REGEX = /\[([^\]]+)\]\(((?:https?:\/\/|command:)[^\)\s]+)(?: "([^"]+)")?\)/gi;
|
||||
|
||||
private _expanded: boolean;
|
||||
|
||||
@@ -392,8 +395,17 @@ export class NotificationViewItem extends Disposable implements INotificationVie
|
||||
|
||||
// Parse Links
|
||||
const links: IMessageLink[] = [];
|
||||
message.replace(NotificationViewItem.LINK_REGEX, (matchString: string, name: string, href: string, offset: number) => {
|
||||
links.push({ name, href, offset, length: matchString.length });
|
||||
message.replace(NotificationViewItem.LINK_REGEX, (matchString: string, name: string, href: string, title: string, offset: number) => {
|
||||
let massagedTitle: string;
|
||||
if (title && title.length > 0) {
|
||||
massagedTitle = title;
|
||||
} else if (startsWith(href, 'command:')) {
|
||||
massagedTitle = localize('executeCommand', "Click to execute command '{0}'", href.substr('command:'.length));
|
||||
} else {
|
||||
massagedTitle = href;
|
||||
}
|
||||
|
||||
links.push({ name, href, title: massagedTitle, offset, length: matchString.length });
|
||||
|
||||
return matchString;
|
||||
});
|
||||
|
||||
@@ -738,6 +738,7 @@ export class ReviewController implements IEditorContribution {
|
||||
|
||||
this._commentInfos.forEach(info => {
|
||||
let providerCacheStore = this._pendingCommentCache[info.owner];
|
||||
info.threads = info.threads.filter(thread => !thread.isDisposed);
|
||||
info.threads.forEach(thread => {
|
||||
let pendingComment: string | null = null;
|
||||
if (providerCacheStore) {
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
ITreeElement, IExpression, IExpressionContainer, IDebugSession, IStackFrame, IExceptionBreakpoint, IBreakpoint, IFunctionBreakpoint, IDebugModel, IReplElementSource,
|
||||
IThread, IRawModelUpdate, IScope, IRawStoppedDetails, IEnablement, IBreakpointData, IExceptionInfo, IReplElement, IBreakpointsChangeEvent, IBreakpointUpdateData, IBaseBreakpoint, State
|
||||
} from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { Source } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { Source, UNKNOWN_SOURCE_LABEL } from 'vs/workbench/contrib/debug/common/debugSource';
|
||||
import { commonSuffixLength } from 'vs/base/common/strings';
|
||||
import { posix } from 'vs/base/common/path';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
@@ -381,7 +381,10 @@ export class StackFrame implements IStackFrame {
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return `${this.name} (${this.source.inMemory ? this.source.name : this.source.uri.fsPath}:${this.range.startLineNumber})`;
|
||||
const lineNumberToString = typeof this.range.startLineNumber === 'number' ? `:${this.range.startLineNumber}` : '';
|
||||
const sourceToString = `${this.source.inMemory ? this.source.name : this.source.uri.fsPath}${lineNumberToString}`;
|
||||
|
||||
return sourceToString === UNKNOWN_SOURCE_LABEL ? this.name : `${this.name} (${sourceToString})`;
|
||||
}
|
||||
|
||||
openInEditor(editorService: IEditorService, preserveFocus?: boolean, sideBySide?: boolean, pinned?: boolean): Promise<any> {
|
||||
|
||||
@@ -13,7 +13,7 @@ import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { isUri } from 'vs/workbench/contrib/debug/common/debugUtils';
|
||||
|
||||
const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source");
|
||||
export const UNKNOWN_SOURCE_LABEL = nls.localize('unknownSource', "Unknown Source");
|
||||
|
||||
/**
|
||||
* Debug URI format
|
||||
|
||||
@@ -394,6 +394,22 @@ suite('Debug - Model', () => {
|
||||
assert.equal(secondStackFrame.getSpecificSourceName(), '.../x/c/d/internalModule.js');
|
||||
});
|
||||
|
||||
test('stack frame toString()', () => {
|
||||
const session = createMockSession(model);
|
||||
const thread = new Thread(session, 'mockthread', 1);
|
||||
const firstSource = new Source({
|
||||
name: 'internalModule.js',
|
||||
path: 'a/b/c/d/internalModule.js',
|
||||
sourceReference: 10,
|
||||
}, 'aDebugSessionId');
|
||||
const stackFrame = new StackFrame(thread, 1, firstSource, 'app', 'normal', { startLineNumber: 1, startColumn: 1, endLineNumber: 1, endColumn: 10 }, 1);
|
||||
assert.equal(stackFrame.toString(), 'app (internalModule.js:1)');
|
||||
|
||||
const secondSource = new Source(undefined, 'aDebugSessionId');
|
||||
const stackFrame2 = new StackFrame(thread, 2, secondSource, 'module', 'normal', { startLineNumber: undefined!, startColumn: undefined!, endLineNumber: undefined!, endColumn: undefined! }, 2);
|
||||
assert.equal(stackFrame2.toString(), 'module');
|
||||
});
|
||||
|
||||
test('debug child sessions are added in correct order', () => {
|
||||
const session = createMockSession(model);
|
||||
model.addSession(session);
|
||||
|
||||
@@ -252,35 +252,6 @@ Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration)
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
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': {
|
||||
type: 'boolean',
|
||||
description: localize('extensions.showInstalledExtensionsByDefault', "When enabled, extensions view shows installed extensions view by default."),
|
||||
default: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IE
|
||||
import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate';
|
||||
import { IExtensionEnablementService, IExtensionTipsService, EnablementState, ExtensionsLabel, IExtensionRecommendation, IGalleryExtension, IExtensionsConfigContent, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, IExtensionManagementServerService, IExtensionManagementServer } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil';
|
||||
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ShowViewletAction } from 'vs/workbench/browser/viewlet';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
@@ -176,14 +176,17 @@ export class InstallAction extends ExtensionAction {
|
||||
}
|
||||
|
||||
update(): void {
|
||||
if (!this.extension || this.extension.type === ExtensionType.System) {
|
||||
if (!this.extension || this.extension.type === ExtensionType.System || this.extension.state === ExtensionState.Installed) {
|
||||
this.enabled = false;
|
||||
this.class = InstallAction.Class;
|
||||
this.label = InstallAction.INSTALL_LABEL;
|
||||
return;
|
||||
}
|
||||
|
||||
this.enabled = this.extensionsWorkbenchService.canInstall(this.extension) && !this.extensionsWorkbenchService.local.some(e => areSameExtensions(e.identifier, this.extension.identifier));
|
||||
this.enabled = false;
|
||||
if (this.extensionsWorkbenchService.canInstall(this.extension)) {
|
||||
const local = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, this.extension.identifier))[0];
|
||||
this.enabled = !local || (!!local.local && isLanguagePackExtension(local.local.manifest));
|
||||
}
|
||||
this.class = this.extension.state === ExtensionState.Installing ? InstallAction.InstallingClass : InstallAction.Class;
|
||||
this.updateLabel();
|
||||
}
|
||||
@@ -2590,6 +2593,9 @@ export class DisabledLabelAction extends ExtensionAction {
|
||||
this.class = `${DisabledLabelAction.Class} hide`;
|
||||
this.label = '';
|
||||
this.enabled = false;
|
||||
if (this.extension && this.extension.local && isLanguagePackExtension(this.extension.local.manifest)) {
|
||||
return;
|
||||
}
|
||||
if (this.warningAction.enabled) {
|
||||
this.enabled = true;
|
||||
this.class = DisabledLabelAction.Class;
|
||||
@@ -2649,6 +2655,9 @@ export class SystemDisabledWarningAction extends ExtensionAction {
|
||||
this.enabled = false;
|
||||
this.class = `${SystemDisabledWarningAction.Class} hide`;
|
||||
this.tooltip = '';
|
||||
if (this.extension && this.extension.local && isLanguagePackExtension(this.extension.local.manifest)) {
|
||||
return;
|
||||
}
|
||||
if (this.extension && this.extension.local && this.extension.server && this._runningExtensions && this.workbenchEnvironmentService.configuration.remoteAuthority && this.extensionManagementServerService.remoteExtensionManagementServer) {
|
||||
const runningExtension = this._runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, this.extension.identifier))[0];
|
||||
const runningExtensionServer = runningExtension ? this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation) : null;
|
||||
|
||||
@@ -19,6 +19,7 @@ import { Label, RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteB
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IExtensionManagementServerService } from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
export interface IExtensionsViewState {
|
||||
onFocus: Event<IExtension>;
|
||||
@@ -154,7 +155,7 @@ export class Renderer implements IPagedRenderer<IExtension, ITemplateData> {
|
||||
|
||||
const updateEnablement = async () => {
|
||||
const runningExtensions = await this.extensionService.getExtensions();
|
||||
if (extension.local) {
|
||||
if (extension.local && !isLanguagePackExtension(extension.local.manifest)) {
|
||||
const runningExtension = runningExtensions.filter(e => areSameExtensions({ id: e.identifier.value }, extension.identifier))[0];
|
||||
const isSameExtensionRunning = runningExtension && extension.server === this.extensionManagementServerService.getExtensionManagementServer(runningExtension.extensionLocation);
|
||||
toggleClass(data.root, 'disabled', !isSameExtensionRunning);
|
||||
|
||||
@@ -54,6 +54,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment'
|
||||
import { ExtensionType } from 'vs/platform/extensions/common/extensions';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
|
||||
import { RemoteAuthorityContext } from 'vs/workbench/common/contextkeys';
|
||||
|
||||
interface SearchInputEvent extends Event {
|
||||
target: HTMLInputElement;
|
||||
@@ -139,7 +140,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
|
||||
id,
|
||||
name: viewIdNameMappings[id],
|
||||
ctorDescriptor: { ctor: EnabledExtensionsView },
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.not('config.extensions.showInstalledExtensionsByDefault')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
|
||||
weight: 40,
|
||||
canToggleVisibility: true,
|
||||
order: 1
|
||||
@@ -154,7 +155,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
|
||||
id,
|
||||
name: viewIdNameMappings[id],
|
||||
ctorDescriptor: { ctor: DisabledExtensionsView },
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.not('config.extensions.showInstalledExtensionsByDefault')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.isEqualTo('')),
|
||||
weight: 10,
|
||||
canToggleVisibility: true,
|
||||
order: 3,
|
||||
@@ -195,7 +196,7 @@ export class ExtensionsViewletViewsContribution implements IWorkbenchContributio
|
||||
id: `extensions.${server.authority}.default`,
|
||||
name: localize('installed', "Installed"),
|
||||
ctorDescriptor: { ctor: ServerExtensionsView, arguments: [server] },
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), ContextKeyExpr.has('config.extensions.showInstalledExtensionsByDefault')),
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has('defaultExtensionViews'), ContextKeyExpr.has('hasInstalledExtensions'), RemoteAuthorityContext.notEqualsTo('')),
|
||||
weight: 40,
|
||||
order: 1
|
||||
}];
|
||||
@@ -512,7 +513,6 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
|
||||
|
||||
private doSearch(): Promise<void> {
|
||||
const value = this.normalizedQuery();
|
||||
this.defaultViewsContextKey.set(!value);
|
||||
const isRecommendedExtensionsQuery = ExtensionsListView.isRecommendedExtensionsQuery(value);
|
||||
this.searchInstalledExtensionsContextKey.set(ExtensionsListView.isInstalledExtensionsQuery(value));
|
||||
this.searchOutdatedExtensionsContextKey.set(ExtensionsListView.isOutdatedExtensionsQuery(value));
|
||||
@@ -522,6 +522,7 @@ export class ExtensionsViewlet extends ViewContainerViewlet implements IExtensio
|
||||
this.recommendedExtensionsContextKey.set(isRecommendedExtensionsQuery);
|
||||
this.searchMarketplaceExtensionsContextKey.set(!!value && !ExtensionsListView.isLocalExtensionsQuery(value) && !isRecommendedExtensionsQuery);
|
||||
this.nonEmptyWorkspaceContextKey.set(this.contextService.getWorkbenchState() !== WorkbenchState.EMPTY);
|
||||
this.defaultViewsContextKey.set(!value);
|
||||
|
||||
return this.progress(Promise.all(this.panels.map(view =>
|
||||
(<ExtensionsListView>view).show(this.normalizedQuery())
|
||||
|
||||
@@ -41,7 +41,7 @@ import { IListContextMenuEvent } from 'vs/base/browser/ui/list/list';
|
||||
import { createErrorWithActions } from 'vs/base/common/errorsWithActions';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { ExtensionType, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ExtensionType, ExtensionIdentifier, IExtensionDescription, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async';
|
||||
@@ -343,6 +343,11 @@ export class ExtensionsListView extends ViewletPanel {
|
||||
if ((isE1Running && isE2Running) || (!isE1Running && !isE2Running)) {
|
||||
return e1.displayName.localeCompare(e2.displayName);
|
||||
}
|
||||
const isE1LanguagePackExtension = e1.local && isLanguagePackExtension(e1.local.manifest);
|
||||
const isE2LanguagePackExtension = e2.local && isLanguagePackExtension(e2.local.manifest);
|
||||
if ((isE1Running && isE2LanguagePackExtension) || (isE2Running && isE1LanguagePackExtension)) {
|
||||
return e1.displayName.localeCompare(e2.displayName);
|
||||
}
|
||||
return isE1Running ? -1 : 1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { VIEWLET_ID, IExplorerService } from 'vs/workbench/contrib/files/common/files';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { IFileService, AutoSaveConfiguration } from 'vs/platform/files/common/files';
|
||||
import { toResource, ITextEditor, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { ExplorerViewlet } from 'vs/workbench/contrib/files/browser/explorerViewlet';
|
||||
import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/untitledEditorService';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
@@ -356,64 +356,6 @@ function containsBothDirectoryAndFile(distinctElements: ExplorerItem[]): boolean
|
||||
return directories.length > 0 && files.length > 0;
|
||||
}
|
||||
|
||||
let pasteShouldMove = false;
|
||||
// Paste File/Folder
|
||||
class PasteFileAction extends Action {
|
||||
|
||||
public static readonly ID = 'filesExplorer.paste';
|
||||
|
||||
constructor(
|
||||
private element: ExplorerItem,
|
||||
@IFileService private fileService: IFileService,
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IExplorerService private readonly explorerService: IExplorerService
|
||||
) {
|
||||
super(PasteFileAction.ID, PASTE_FILE_LABEL);
|
||||
|
||||
if (!this.element) {
|
||||
this.element = this.explorerService.roots[0];
|
||||
}
|
||||
}
|
||||
|
||||
public run(fileToPaste: URI): Promise<any> {
|
||||
|
||||
// Check if target is ancestor of pasted folder
|
||||
if (this.element.resource.toString() !== fileToPaste.toString() && resources.isEqualOrParent(this.element.resource, fileToPaste, !isLinux /* ignorecase */)) {
|
||||
throw new Error(nls.localize('fileIsAncestor', "File to paste is an ancestor of the destination folder"));
|
||||
}
|
||||
|
||||
return this.fileService.resolve(fileToPaste).then(fileToPasteStat => {
|
||||
|
||||
// Find target
|
||||
let target: ExplorerItem;
|
||||
if (this.element.resource.toString() === fileToPaste.toString()) {
|
||||
target = this.element.parent!;
|
||||
} else {
|
||||
target = this.element.isDirectory ? this.element : this.element.parent!;
|
||||
}
|
||||
|
||||
const targetFile = findValidPasteFileTarget(target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory, allowOverwirte: pasteShouldMove });
|
||||
|
||||
// Copy File
|
||||
const promise = pasteShouldMove ? this.fileService.move(fileToPaste, targetFile) : this.fileService.copy(fileToPaste, targetFile);
|
||||
return promise.then<ITextEditor | undefined>(stat => {
|
||||
if (pasteShouldMove) {
|
||||
// Cut is done. Make sure to clear cut state.
|
||||
this.explorerService.setToCopy([], false);
|
||||
}
|
||||
if (!stat.isDirectory) {
|
||||
return this.editorService.openEditor({ resource: stat.resource, options: { pinned: true, preserveFocus: true } })
|
||||
.then(types.withNullAsUndefined);
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}, e => onError(this.notificationService, e));
|
||||
}, error => {
|
||||
onError(this.notificationService, new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile")));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export function findValidPasteFileTarget(targetFolder: ExplorerItem, fileToPaste: { resource: URI, isDirectory?: boolean, allowOverwirte: boolean }): URI {
|
||||
let name = resources.basenameOrAuthority(fileToPaste.resource);
|
||||
@@ -1083,6 +1025,7 @@ export const deleteFileHandler = (accessor: ServicesAccessor) => {
|
||||
return deleteFiles(accessor, stats, false);
|
||||
};
|
||||
|
||||
let pasteShouldMove = false;
|
||||
export const copyFileHandler = (accessor: ServicesAccessor) => {
|
||||
const listService = accessor.get(IListService);
|
||||
if (!listService.lastFocusedList) {
|
||||
@@ -1112,16 +1055,50 @@ export const cutFileHandler = (accessor: ServicesAccessor) => {
|
||||
};
|
||||
|
||||
export const pasteFileHandler = (accessor: ServicesAccessor) => {
|
||||
const instantiationService = accessor.get(IInstantiationService);
|
||||
const listService = accessor.get(IListService);
|
||||
const clipboardService = accessor.get(IClipboardService);
|
||||
if (!listService.lastFocusedList) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
const explorerContext = getContext(listService.lastFocusedList);
|
||||
const explorerService = accessor.get(IExplorerService);
|
||||
const fileService = accessor.get(IFileService);
|
||||
const notificationService = accessor.get(INotificationService);
|
||||
const editorService = accessor.get(IEditorService);
|
||||
|
||||
return sequence(resources.distinctParents(clipboardService.readResources(), r => r).map(toCopy => {
|
||||
const pasteFileAction = instantiationService.createInstance(PasteFileAction, explorerContext.stat);
|
||||
return () => pasteFileAction.run(toCopy);
|
||||
}));
|
||||
if (listService.lastFocusedList) {
|
||||
const explorerContext = getContext(listService.lastFocusedList);
|
||||
const toPaste = resources.distinctParents(clipboardService.readResources(), r => r);
|
||||
const element = explorerContext.stat || explorerService.roots[0];
|
||||
|
||||
// Check if target is ancestor of pasted folder
|
||||
sequence(toPaste.map(fileToPaste => () => {
|
||||
|
||||
if (element.resource.toString() !== fileToPaste.toString() && resources.isEqualOrParent(element.resource, fileToPaste, !isLinux /* ignorecase */)) {
|
||||
throw new Error(nls.localize('fileIsAncestor', "File to paste is an ancestor of the destination folder"));
|
||||
}
|
||||
|
||||
return fileService.resolve(fileToPaste).then(fileToPasteStat => {
|
||||
|
||||
// Find target
|
||||
let target: ExplorerItem;
|
||||
if (element.resource.toString() === fileToPaste.toString()) {
|
||||
target = element.parent!;
|
||||
} else {
|
||||
target = element.isDirectory ? element : element.parent!;
|
||||
}
|
||||
|
||||
const targetFile = findValidPasteFileTarget(target, { resource: fileToPaste, isDirectory: fileToPasteStat.isDirectory, allowOverwirte: pasteShouldMove });
|
||||
|
||||
// Copy File
|
||||
return pasteShouldMove ? fileService.move(fileToPaste, targetFile) : fileService.copy(fileToPaste, targetFile);
|
||||
}, error => {
|
||||
onError(notificationService, new Error(nls.localize('fileDeleted', "File to paste was deleted or moved meanwhile")));
|
||||
});
|
||||
})).then((stat) => {
|
||||
if (pasteShouldMove) {
|
||||
// Cut is done. Make sure to clear cut state.
|
||||
explorerService.setToCopy([], false);
|
||||
}
|
||||
if (stat.length === 1 && !stat[0].isDirectory) {
|
||||
editorService.openEditor({ resource: stat[0].resource, options: { pinned: true, preserveFocus: true } }).then(undefined, onUnexpectedError);
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
// {{SQL CARBON EDIT}} - Import EditorInput
|
||||
import { toResource, IEditorCommandsContext, EditorInput, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { IWindowsService, IWindowService, IURIToOpen, IOpenSettings, INewWindowOptions } from 'vs/platform/windows/common/windows';
|
||||
// {{SQL CARBON EDIT}} import EditorInput
|
||||
import { toResource, IEditorCommandsContext, SideBySideEditor, EditorInput } from 'vs/workbench/common/editor';
|
||||
import { IWindowsService, IWindowService, IURIToOpen, IOpenSettings, INewWindowOptions, isWorkspaceToOpen } from 'vs/platform/windows/common/windows';
|
||||
import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
@@ -35,14 +35,15 @@ import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/edito
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
|
||||
// {{SQL CARBON EDIT}} - Import EditorInput
|
||||
import { IEditorService, SIDE_GROUP, IResourceEditorReplacement } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { ILabelService } from 'vs/platform/label/common/label';
|
||||
import { onUnexpectedError } from 'vs/base/common/errors';
|
||||
import { basename, toLocalResource } from 'vs/base/common/resources';
|
||||
import { basename, toLocalResource, joinPath } from 'vs/base/common/resources';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { UNTITLED_WORKSPACE_NAME } from 'vs/platform/workspaces/common/workspaces';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { IQueryEditorService } from 'sql/workbench/services/queryEditor/common/queryEditorService';
|
||||
@@ -86,6 +87,19 @@ export const REMOVE_ROOT_FOLDER_LABEL = nls.localize('removeFolderFromWorkspace'
|
||||
export const openWindowCommand = (accessor: ServicesAccessor, urisToOpen: IURIToOpen[], options?: IOpenSettings) => {
|
||||
if (Array.isArray(urisToOpen)) {
|
||||
const windowService = accessor.get(IWindowService);
|
||||
const environmentService = accessor.get(IEnvironmentService);
|
||||
|
||||
// rewrite untitled: workspace URIs to the absolute path on disk
|
||||
urisToOpen = urisToOpen.map(uriToOpen => {
|
||||
if (isWorkspaceToOpen(uriToOpen) && uriToOpen.workspaceUri.scheme === Schemas.untitled) {
|
||||
return {
|
||||
workspaceUri: joinPath(environmentService.untitledWorkspacesHome, uriToOpen.workspaceUri.path, UNTITLED_WORKSPACE_NAME)
|
||||
};
|
||||
}
|
||||
|
||||
return uriToOpen;
|
||||
});
|
||||
|
||||
windowService.openWindow(urisToOpen, options);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -228,6 +228,10 @@ export class SettingsTreeSettingElement extends SettingsTreeElement {
|
||||
return this.setting.scope === ConfigurationScope.WINDOW || this.setting.scope === ConfigurationScope.RESOURCE;
|
||||
}
|
||||
|
||||
if (configTarget === ConfigurationTarget.USER_REMOTE) {
|
||||
return this.setting.scope === ConfigurationScope.MACHINE || this.setting.scope === ConfigurationScope.WINDOW || this.setting.scope === ConfigurationScope.RESOURCE;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,13 +16,11 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { REMOTE_HOST_SCHEME, getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { sanitizeProcessEnvironment } from 'vs/base/common/processes';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IProductService } from 'vs/platform/product/common/product';
|
||||
import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
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 */
|
||||
const LAUNCHING_DURATION = 500;
|
||||
@@ -173,53 +171,20 @@ export class TerminalProcessManager implements ITerminalProcessManager {
|
||||
if (!shellLaunchConfig.executable) {
|
||||
this._configHelper.mergeDefaultShellPathAndArgs(shellLaunchConfig);
|
||||
}
|
||||
|
||||
const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file);
|
||||
const initialCwd = terminalEnvironment.getCwd(shellLaunchConfig, this._environmentService.userHome, activeWorkspaceRootUri, this._configHelper.config.cwd);
|
||||
const env = this._createEnvironment(shellLaunchConfig, activeWorkspaceRootUri);
|
||||
|
||||
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
const lastActiveWorkspace = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null;
|
||||
const envFromConfigValue = this._workspaceConfigurationService.inspect<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
|
||||
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
|
||||
const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables);
|
||||
|
||||
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 = {};
|
||||
if (shellLaunchConfig.strictEnv) {
|
||||
// strictEnv is true, only use the requested env (ignoring null entries)
|
||||
terminalEnvironment.mergeNonNullKeys(env, shellLaunchConfig.env);
|
||||
} else {
|
||||
// Merge process env with the env from config and from shellLaunchConfig
|
||||
terminalEnvironment.mergeNonNullKeys(env, process.env);
|
||||
|
||||
// Determine config env based on workspace shell permissions
|
||||
const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null;
|
||||
const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions();
|
||||
const envFromConfigValue = this._workspaceConfigurationService.inspect<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
|
||||
const allowedEnvFromConfig = { ...(isWorkspaceShellAllowed ? envFromConfigValue.value : envFromConfigValue.user) };
|
||||
|
||||
// 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);
|
||||
|
||||
// Sanitize the environment, removing any undesirable VS Code and Electron environment
|
||||
// variables
|
||||
sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
|
||||
|
||||
// Adding other env keys necessary to create the process
|
||||
terminalEnvironment.addTerminalEnvironmentKeys(env, this._productService.version, platform.locale, this._configHelper.config.setLocaleVariables);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
public setDimensions(cols: number, rows: number): void {
|
||||
if (!this._process) {
|
||||
return;
|
||||
|
||||
@@ -9,6 +9,7 @@ import { URI as Uri } from 'vs/base/common/uri';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IShellLaunchConfig, ITerminalEnvironment } from 'vs/workbench/contrib/terminal/common/terminal';
|
||||
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
|
||||
import { sanitizeProcessEnvironment } from 'vs/base/common/processes';
|
||||
|
||||
/**
|
||||
* This module contains utility functions related to the environment, cwd and paths.
|
||||
@@ -59,7 +60,7 @@ export function addTerminalEnvironmentKeys(env: platform.IProcessEnvironment, ve
|
||||
}
|
||||
}
|
||||
|
||||
export function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | NodeJS.ProcessEnv | undefined) {
|
||||
function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerminalEnvironment | NodeJS.ProcessEnv | undefined) {
|
||||
if (!other) {
|
||||
return;
|
||||
}
|
||||
@@ -71,7 +72,7 @@ export function mergeNonNullKeys(env: platform.IProcessEnvironment, other: ITerm
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: ITerminalEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder | null): ITerminalEnvironment {
|
||||
function resolveConfigurationVariables(configurationResolverService: IConfigurationResolverService, env: ITerminalEnvironment, lastActiveWorkspaceRoot: IWorkspaceFolder | null): ITerminalEnvironment {
|
||||
Object.keys(env).forEach((key) => {
|
||||
const value = env[key];
|
||||
if (typeof value === 'string' && lastActiveWorkspaceRoot !== null) {
|
||||
@@ -189,3 +190,49 @@ export function mergeDefaultShellPathAndArgs(
|
||||
shell.executable = shell.executable.replace(/\//g, '\\');
|
||||
}
|
||||
}
|
||||
|
||||
export function createTerminalEnvironment(
|
||||
shellLaunchConfig: IShellLaunchConfig,
|
||||
lastActiveWorkspace: IWorkspaceFolder | null,
|
||||
envFromConfig: { user: ITerminalEnvironment | undefined, value: ITerminalEnvironment | undefined, default: ITerminalEnvironment | undefined },
|
||||
configurationResolverService: IConfigurationResolverService | undefined,
|
||||
isWorkspaceShellAllowed: boolean,
|
||||
version: string | undefined,
|
||||
setLocaleVariables: boolean
|
||||
): platform.IProcessEnvironment {
|
||||
// Create a terminal environment based on settings, launch config and permissions
|
||||
let env: platform.IProcessEnvironment = {};
|
||||
if (shellLaunchConfig.strictEnv) {
|
||||
// strictEnv is true, only use the requested env (ignoring null entries)
|
||||
mergeNonNullKeys(env, shellLaunchConfig.env);
|
||||
} else {
|
||||
// Merge process env with the env from config and from shellLaunchConfig
|
||||
mergeNonNullKeys(env, process.env);
|
||||
|
||||
// const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux');
|
||||
// const envFromConfigValue = this._workspaceConfigurationService.inspect<ITerminalEnvironment | undefined>(`terminal.integrated.env.${platformKey}`);
|
||||
const allowedEnvFromConfig = { ...(isWorkspaceShellAllowed ? envFromConfig.value : envFromConfig.user) };
|
||||
|
||||
// Resolve env vars from config and shell
|
||||
if (configurationResolverService) {
|
||||
if (allowedEnvFromConfig) {
|
||||
resolveConfigurationVariables(configurationResolverService, allowedEnvFromConfig, lastActiveWorkspace);
|
||||
}
|
||||
if (shellLaunchConfig.env) {
|
||||
resolveConfigurationVariables(configurationResolverService, shellLaunchConfig.env, lastActiveWorkspace);
|
||||
}
|
||||
}
|
||||
|
||||
// Merge config (settings) and ShellLaunchConfig environments
|
||||
mergeEnvironments(env, allowedEnvFromConfig);
|
||||
mergeEnvironments(env, shellLaunchConfig.env);
|
||||
|
||||
// Sanitize the environment, removing any undesirable VS Code and Electron environment
|
||||
// variables
|
||||
sanitizeProcessEnvironment(env, 'VSCODE_IPC_HOOK_CLI');
|
||||
|
||||
// Adding other env keys necessary to create the process
|
||||
addTerminalEnvironmentKeys(env, version, platform.locale, setLocaleVariables);
|
||||
}
|
||||
return env;
|
||||
}
|
||||
@@ -38,6 +38,7 @@ import { parseExtensionDevOptions } from '../common/extensionDevOptions';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IExtensionHostDebugService } from 'vs/workbench/services/extensions/common/extensionHostDebug';
|
||||
import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { isEqualOrParent } from 'vs/base/common/resources';
|
||||
|
||||
export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
|
||||
@@ -400,7 +401,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter {
|
||||
workspace: this._contextService.getWorkbenchState() === WorkbenchState.EMPTY ? undefined : {
|
||||
configuration: withNullAsUndefined(workspace.configuration),
|
||||
id: workspace.id,
|
||||
name: this._labelService.getWorkspaceLabel(workspace)
|
||||
name: this._labelService.getWorkspaceLabel(workspace),
|
||||
isUntitled: workspace.configuration ? isEqualOrParent(workspace.configuration, this._environmentService.untitledWorkspacesHome) : false
|
||||
},
|
||||
resolvedExtensions: [],
|
||||
hostExtensions: [],
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
IExtensionManagementService, ILocalExtension, IGalleryExtension, InstallExtensionEvent, DidInstallExtensionEvent, IExtensionIdentifier, DidUninstallExtensionEvent, IReportedExtension, IGalleryMetadata,
|
||||
IExtensionManagementServerService, IExtensionManagementServer, IExtensionGalleryService
|
||||
} from 'vs/platform/extensionManagement/common/extensionManagement';
|
||||
import { flatten } from 'vs/base/common/arrays';
|
||||
import { ExtensionType, IExtensionManifest, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -46,8 +45,10 @@ export class MultiExtensionManagementService extends Disposable implements IExte
|
||||
}
|
||||
|
||||
getInstalled(type?: ExtensionType): Promise<ILocalExtension[]> {
|
||||
return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type)))
|
||||
.then(result => flatten(result));
|
||||
const installedExtensions: ILocalExtension[] = [];
|
||||
return Promise.all(this.servers.map(({ extensionManagementService }) => extensionManagementService.getInstalled(type).then(extensions => installedExtensions.push(...extensions))))
|
||||
.then(_ => installedExtensions)
|
||||
.catch(e => installedExtensions);
|
||||
}
|
||||
|
||||
async uninstall(extension: ILocalExtension, force?: boolean): Promise<void> {
|
||||
|
||||
@@ -17,6 +17,7 @@ import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { VSBuffer, VSBufferReadable, readableToBuffer, bufferToReadable, streamToBuffer, bufferToStream, VSBufferReadableStream, writeableBufferStream, VSBufferWriteableStream } from 'vs/base/common/buffer';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export class FileService extends Disposable implements IFileService {
|
||||
|
||||
@@ -101,7 +102,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// Assert path is absolute
|
||||
if (!isAbsolutePath(resource)) {
|
||||
throw new FileOperationError(localize('invalidPath', "The path of resource '{0}' must be absolute", resource.toString(true)), FileOperationResult.FILE_INVALID_PATH);
|
||||
throw new FileOperationError(localize('invalidPath', "The path of resource '{0}' must be absolute", this.resourceForError(resource)), FileOperationResult.FILE_INVALID_PATH);
|
||||
}
|
||||
|
||||
// Activate provider
|
||||
@@ -110,11 +111,11 @@ export class FileService extends Disposable implements IFileService {
|
||||
// Assert provider
|
||||
const provider = this.provider.get(resource.scheme);
|
||||
if (!provider) {
|
||||
const err = new Error();
|
||||
err.name = 'ENOPRO';
|
||||
err.message = `No provider found for ${resource.toString()}`;
|
||||
const error = new Error();
|
||||
error.name = 'ENOPRO';
|
||||
error.message = localize('noProviderFound', "No file system provider found for {0}", resource.toString());
|
||||
|
||||
throw err;
|
||||
throw error;
|
||||
}
|
||||
|
||||
return provider;
|
||||
@@ -150,7 +151,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
// Specially handle file not found case as file operation result
|
||||
if (toFileSystemProviderErrorCode(error) === FileSystemProviderErrorCode.FileNotFound) {
|
||||
throw new FileOperationError(
|
||||
localize('fileNotFoundError', "File not found ({0})", resource.toString(true)),
|
||||
localize('fileNotFoundError', "File not found ({0})", this.resourceForError(resource)),
|
||||
FileOperationResult.FILE_NOT_FOUND
|
||||
);
|
||||
}
|
||||
@@ -270,7 +271,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
// validate overwrite
|
||||
const overwrite = !!(options && options.overwrite);
|
||||
if (!overwrite && await this.exists(resource)) {
|
||||
throw new FileOperationError(localize('fileExists', "File to create already exists ({0})", resource.toString(true)), FileOperationResult.FILE_MODIFIED_SINCE, options);
|
||||
throw new FileOperationError(localize('fileExists', "File to create already exists ({0})", this.resourceForError(resource)), FileOperationResult.FILE_MODIFIED_SINCE, options);
|
||||
}
|
||||
|
||||
// do write into file (this will create it too)
|
||||
@@ -305,7 +306,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
await this.doWriteUnbuffered(provider, resource, bufferOrReadable);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new FileOperationError(localize('err.write', "Failed to write file {0}", resource.toString(false)), toFileOperationResult(error), options);
|
||||
throw new FileOperationError(localize('err.write', "Unable to write file ({0})", error.toString()), toFileOperationResult(error), options);
|
||||
}
|
||||
|
||||
return this.resolve(resource, { resolveMetadata: true });
|
||||
@@ -321,7 +322,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// file cannot be directory
|
||||
if ((stat.type & FileType.Directory) !== 0) {
|
||||
throw new FileOperationError(localize('fileIsDirectoryError', "Expected file {0} is actually a directory", resource.toString()), FileOperationResult.FILE_IS_DIRECTORY, options);
|
||||
throw new FileOperationError(localize('fileIsDirectoryError', "Expected file {0} is actually a directory", this.resourceForError(resource)), FileOperationResult.FILE_IS_DIRECTORY, options);
|
||||
}
|
||||
|
||||
// Dirty write prevention: if the file on disk has been changed and does not match our expected
|
||||
@@ -397,7 +398,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
value: fileStream
|
||||
};
|
||||
} catch (error) {
|
||||
throw new FileOperationError(localize('err.read', "Failed to read file {0}", resource.toString(false)), toFileOperationResult(error), options);
|
||||
throw new FileOperationError(localize('err.read', "Unable to read file ({0})", error.toString()), toFileOperationResult(error), options);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -488,7 +489,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// Return early if resource is a directory
|
||||
if (stat.isDirectory) {
|
||||
throw new FileOperationError(localize('fileIsDirectoryError', "Expected file {0} is actually a directory", resource.toString()), FileOperationResult.FILE_IS_DIRECTORY, options);
|
||||
throw new FileOperationError(localize('fileIsDirectoryError', "Expected file {0} is actually a directory", this.resourceForError(resource)), FileOperationResult.FILE_IS_DIRECTORY, options);
|
||||
}
|
||||
|
||||
// Return early if file not modified since
|
||||
@@ -692,7 +693,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
try {
|
||||
const stat = await provider.stat(directory);
|
||||
if ((stat.type & FileType.Directory) === 0) {
|
||||
throw new Error(localize('mkdirExistsError', "{0} exists, but is not a directory", directory.toString()));
|
||||
throw new Error(localize('mkdirExistsError', "{0} exists, but is not a directory", this.resourceForError(directory)));
|
||||
}
|
||||
|
||||
break; // we have hit a directory that exists -> good
|
||||
@@ -732,7 +733,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
if (!recursive && await this.exists(resource)) {
|
||||
const stat = await this.resolve(resource);
|
||||
if (stat.isDirectory && Array.isArray(stat.children) && stat.children.length > 0) {
|
||||
throw new Error(localize('deleteFailed', "Failed to delete non-empty folder '{0}'.", resource.toString()));
|
||||
throw new Error(localize('deleteFailed', "Unable to delete non-empty folder '{0}'.", this.resourceForError(resource)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1006,5 +1007,13 @@ export class FileService extends Disposable implements IFileService {
|
||||
return true;
|
||||
}
|
||||
|
||||
private resourceForError(resource: URI): string {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
return resource.fsPath;
|
||||
}
|
||||
|
||||
return resource.toString(true);
|
||||
}
|
||||
|
||||
//#endregion
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import { FileChangeType } from 'vs/platform/files/common/files';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { normalizeNFC } from 'vs/base/common/normalization';
|
||||
import { realcaseSync } from 'vs/base/node/extpath';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { isMacintosh, isLinux } from 'vs/base/common/platform';
|
||||
import { IDiskFileChange, normalizeFileChanges } from 'vs/workbench/services/files/node/watcher/watcher';
|
||||
import { IWatcherRequest, IWatcherService, IWatcherOptions, IWatchError } from 'vs/workbench/services/files/node/watcher/unix/watcher';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
@@ -114,12 +114,21 @@ export class ChokidarWatcherService implements IWatcherService {
|
||||
disableGlobbing: true // fix https://github.com/Microsoft/vscode/issues/4586
|
||||
};
|
||||
|
||||
const excludes: string[] = [];
|
||||
// if there's only one request, use the built-in ignore-filterering
|
||||
const isSingleFolder = requests.length === 1;
|
||||
if (isSingleFolder) {
|
||||
watcherOpts.ignored = requests[0].excludes;
|
||||
excludes.push(...requests[0].excludes);
|
||||
}
|
||||
|
||||
if ((isMacintosh || isLinux) && (basePath.length === 0 || basePath === '/')) {
|
||||
excludes.push('/dev/**');
|
||||
if (isLinux) {
|
||||
excludes.push('/proc/**', '/sys/**');
|
||||
}
|
||||
}
|
||||
watcherOpts.ignored = excludes;
|
||||
|
||||
// Chokidar fails when the basePath does not match case-identical to the path on disk
|
||||
// so we have to find the real casing of the path and do some path massaging to fix this
|
||||
// see https://github.com/paulmillr/chokidar/issues/418
|
||||
|
||||
@@ -1382,6 +1382,10 @@ suite('Disk File Service', () => {
|
||||
});
|
||||
|
||||
test('watch - file - multiple writes', done => {
|
||||
if (isWindows) {
|
||||
return done(); // not happy
|
||||
}
|
||||
|
||||
const toWatch = URI.file(join(testDir, 'index-watch1.html'));
|
||||
writeFileSync(toWatch.fsPath, 'Init');
|
||||
|
||||
@@ -1487,7 +1491,7 @@ suite('Disk File Service', () => {
|
||||
setTimeout(() => mkdirSync(folder.fsPath), 50);
|
||||
});
|
||||
|
||||
test('watch - folder (non recursive) - delete folder', done => {
|
||||
test.skip('watch - folder (non recursive) - delete folder', done => {
|
||||
const watchDir = URI.file(join(testDir, 'watch7'));
|
||||
mkdirSync(watchDir.fsPath);
|
||||
|
||||
|
||||
@@ -160,7 +160,7 @@ export class LabelService implements ILabelService {
|
||||
}
|
||||
|
||||
getWorkspaceLabel(workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | IWorkspace), options?: { verbose: boolean }): string {
|
||||
if (!isWorkspaceIdentifier(workspace) && !isSingleFolderWorkspaceIdentifier(workspace)) {
|
||||
if (IWorkspace.isIWorkspace(workspace)) {
|
||||
const identifier = toWorkspaceIdentifier(workspace);
|
||||
if (!identifier) {
|
||||
return '';
|
||||
@@ -176,23 +176,27 @@ export class LabelService implements ILabelService {
|
||||
return this.appendWorkspaceSuffix(label, workspace);
|
||||
}
|
||||
|
||||
// Workspace: Untitled
|
||||
if (isEqualOrParent(workspace.configPath, this.environmentService.untitledWorkspacesHome)) {
|
||||
return localize('untitledWorkspace', "Untitled (Workspace)");
|
||||
}
|
||||
if (isWorkspaceIdentifier(workspace)) {
|
||||
// Workspace: Untitled
|
||||
if (isEqualOrParent(workspace.configPath, this.environmentService.untitledWorkspacesHome)) {
|
||||
return localize('untitledWorkspace', "Untitled (Workspace)");
|
||||
}
|
||||
|
||||
// Workspace: Saved
|
||||
let filename = basename(workspace.configPath);
|
||||
if (endsWith(filename, WORKSPACE_EXTENSION)) {
|
||||
filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1);
|
||||
// Workspace: Saved
|
||||
let filename = basename(workspace.configPath);
|
||||
if (endsWith(filename, WORKSPACE_EXTENSION)) {
|
||||
filename = filename.substr(0, filename.length - WORKSPACE_EXTENSION.length - 1);
|
||||
}
|
||||
let label;
|
||||
if (options && options.verbose) {
|
||||
label = localize('workspaceNameVerbose', "{0} (Workspace)", this.getUriLabel(joinPath(dirname(workspace.configPath), filename)));
|
||||
} else {
|
||||
label = localize('workspaceName', "{0} (Workspace)", filename);
|
||||
}
|
||||
return this.appendWorkspaceSuffix(label, workspace.configPath);
|
||||
}
|
||||
let label;
|
||||
if (options && options.verbose) {
|
||||
label = localize('workspaceNameVerbose', "{0} (Workspace)", this.getUriLabel(joinPath(dirname(workspace.configPath), filename)));
|
||||
} else {
|
||||
label = localize('workspaceName', "{0} (Workspace)", filename);
|
||||
}
|
||||
return this.appendWorkspaceSuffix(label, workspace.configPath);
|
||||
return '';
|
||||
|
||||
}
|
||||
|
||||
getSeparator(scheme: string, authority?: string): '/' | '\\' {
|
||||
|
||||
@@ -154,7 +154,6 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
|
||||
// Model does not exist
|
||||
else {
|
||||
const newModel = model = this.instantiationService.createInstance(TextFileEditorModel, resource, options ? options.encoding : undefined);
|
||||
model = newModel;
|
||||
modelPromise = model.load(options);
|
||||
|
||||
// Install state change listener
|
||||
@@ -192,24 +191,24 @@ export class TextFileEditorModelManager extends Disposable implements ITextFileE
|
||||
this.mapResourceToPendingModelLoaders.set(resource, modelPromise);
|
||||
|
||||
try {
|
||||
const model = await modelPromise;
|
||||
const resolvedModel = await modelPromise;
|
||||
|
||||
// Make known to manager (if not already known)
|
||||
this.add(resource, model);
|
||||
this.add(resource, resolvedModel);
|
||||
|
||||
// Model can be dirty if a backup was restored, so we make sure to have this event delivered
|
||||
if (model.isDirty()) {
|
||||
this._onModelDirty.fire(new TextFileModelChangeEvent(model, StateChange.DIRTY));
|
||||
if (resolvedModel.isDirty()) {
|
||||
this._onModelDirty.fire(new TextFileModelChangeEvent(resolvedModel, StateChange.DIRTY));
|
||||
}
|
||||
|
||||
// Remove from pending loads
|
||||
this.mapResourceToPendingModelLoaders.delete(resource);
|
||||
|
||||
return model;
|
||||
return resolvedModel;
|
||||
} catch (error) {
|
||||
|
||||
// Free resources of this invalid model
|
||||
if (model && typeof model.dispose === 'function') { // workaround for https://github.com/Microsoft/vscode/issues/72404
|
||||
if (model) {
|
||||
model.dispose();
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import { coalesce } from 'vs/base/common/arrays';
|
||||
import { trim } from 'vs/base/common/strings';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { ITextSnapshot } from 'vs/editor/common/model';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
|
||||
/**
|
||||
* The workbench file service implementation implements the raw file service spec and adds additional methods on top.
|
||||
@@ -85,7 +86,8 @@ export abstract class TextFileService extends Disposable implements ITextFileSer
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
@IFileDialogService private readonly fileDialogService: IFileDialogService,
|
||||
@IEditorService private readonly editorService: IEditorService
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@ITextResourceConfigurationService protected readonly textResourceConfigurationService: ITextResourceConfigurationService
|
||||
) {
|
||||
super();
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ import { isMacintosh, isLinux } from 'vs/base/common/platform';
|
||||
import product from 'vs/platform/product/node/product';
|
||||
import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { UTF8, UTF8_with_bom, UTF16be, UTF16le, encodingExists, IDetectedEncodingResult, encodeStream, UTF8_BOM, UTF16be_BOM, UTF16le_BOM, toDecodeStream, IDecodeStreamResult, detectEncodingByBOMFromBuffer } from 'vs/base/node/encoding';
|
||||
import { UTF8, UTF8_with_bom, UTF16be, UTF16le, encodingExists, encodeStream, UTF8_BOM, UTF16be_BOM, UTF16le_BOM, toDecodeStream, IDecodeStreamResult, detectEncodingByBOMFromBuffer } from 'vs/base/node/encoding';
|
||||
import { WORKSPACE_EXTENSION } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { joinPath, extname, isEqualOrParent } from 'vs/base/common/resources';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -70,8 +70,8 @@ export class NodeTextFileService extends TextFileService {
|
||||
|
||||
// read through encoding library
|
||||
const decoder = await toDecodeStream(this.streamToNodeReadable(bufferStream.value), {
|
||||
guessEncoding: options && options.autoGuessEncoding,
|
||||
overwriteEncoding: detected => this.encoding.getReadEncoding(resource, options, { encoding: detected, seemsBinary: false })
|
||||
guessEncoding: (options && options.autoGuessEncoding) || this.textResourceConfigurationService.getValue(resource, 'files.autoGuessEncoding'),
|
||||
overwriteEncoding: detectedEncoding => this.encoding.getReadEncoding(resource, options, detectedEncoding)
|
||||
});
|
||||
|
||||
// validate binary
|
||||
@@ -417,7 +417,7 @@ export class EncodingOracle extends Disposable implements IResourceEncodings {
|
||||
const overwriteEncoding = options && options.overwriteEncoding;
|
||||
if (!overwriteEncoding && encoding === UTF8) {
|
||||
try {
|
||||
const buffer = (await this.fileService.readFile(resource, { length: 3 })).value;
|
||||
const buffer = (await this.fileService.readFile(resource, { length: UTF8_BOM.length })).value;
|
||||
if (detectEncodingByBOMFromBuffer(buffer, buffer.byteLength) === UTF8) {
|
||||
return { encoding, addBOM: true };
|
||||
}
|
||||
@@ -438,12 +438,12 @@ export class EncodingOracle extends Disposable implements IResourceEncodings {
|
||||
};
|
||||
}
|
||||
|
||||
getReadEncoding(resource: URI, options: IReadTextFileOptions | undefined, detected: IDetectedEncodingResult): string {
|
||||
getReadEncoding(resource: URI, options: IReadTextFileOptions | undefined, detectedEncoding: string | null): string {
|
||||
let preferredEncoding: string | undefined;
|
||||
|
||||
// Encoding passed in as option
|
||||
if (options && options.encoding) {
|
||||
if (detected.encoding === UTF8 && options.encoding === UTF8) {
|
||||
if (detectedEncoding === UTF8 && options.encoding === UTF8) {
|
||||
preferredEncoding = UTF8_with_bom; // indicate the file has BOM if we are to resolve with UTF 8
|
||||
} else {
|
||||
preferredEncoding = options.encoding; // give passed in encoding highest priority
|
||||
@@ -451,11 +451,11 @@ export class EncodingOracle extends Disposable implements IResourceEncodings {
|
||||
}
|
||||
|
||||
// Encoding detected
|
||||
else if (detected.encoding) {
|
||||
if (detected.encoding === UTF8) {
|
||||
else if (detectedEncoding) {
|
||||
if (detectedEncoding === UTF8) {
|
||||
preferredEncoding = UTF8_with_bom; // if we detected UTF-8, it can only be because of a BOM
|
||||
} else {
|
||||
preferredEncoding = detected.encoding;
|
||||
preferredEncoding = detectedEncoding;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -570,6 +570,13 @@ suite('Files - TextFileService i/o', () => {
|
||||
assert.equal(result.encoding, 'utf16be');
|
||||
});
|
||||
|
||||
test('readStream - autoguessEncoding', async () => {
|
||||
const resource = URI.file(join(testDir, 'some_cp1252.txt'));
|
||||
|
||||
const result = await service.readStream(resource, { autoGuessEncoding: true });
|
||||
assert.equal(result.encoding, 'windows1252');
|
||||
});
|
||||
|
||||
test('readStream - FILE_IS_BINARY', async () => {
|
||||
const resource = URI.file(join(testDir, 'binary.txt'));
|
||||
|
||||
@@ -586,4 +593,21 @@ suite('Files - TextFileService i/o', () => {
|
||||
const result = await service.readStream(URI.file(join(testDir, 'small.txt')), { acceptTextOnly: true });
|
||||
assert.equal(result.name, 'small.txt');
|
||||
});
|
||||
|
||||
test('read - FILE_IS_BINARY', async () => {
|
||||
const resource = URI.file(join(testDir, 'binary.txt'));
|
||||
|
||||
let error: TextFileOperationError | undefined = undefined;
|
||||
try {
|
||||
await service.read(resource, { acceptTextOnly: true });
|
||||
} catch (err) {
|
||||
error = err;
|
||||
}
|
||||
|
||||
assert.ok(error);
|
||||
assert.equal(error!.textFileOperationResult, TextFileOperationResult.FILE_IS_BINARY);
|
||||
|
||||
const result = await service.read(URI.file(join(testDir, 'small.txt')), { acceptTextOnly: true });
|
||||
assert.equal(result.name, 'small.txt');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,19 +6,19 @@
|
||||
import * as nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IWorkbenchThemeService, IColorTheme, ITokenColorCustomizations, IFileIconTheme, ExtensionData, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME, COLOR_THEME_SETTING, ICON_THEME_SETTING, CUSTOM_WORKBENCH_COLORS_SETTING, CUSTOM_EDITOR_COLORS_SETTING, DETECT_HC_SETTING, HC_THEME_ID, IColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationPropertySchema, IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { ColorThemeData } from './colorThemeData';
|
||||
import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData';
|
||||
import { ITheme, Extensions as ThemingExtensions, IThemingRegistry } from 'vs/platform/theme/common/themeService';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ColorThemeStore } from 'vs/workbench/services/themes/browser/colorThemeStore';
|
||||
import { ColorThemeStore } from 'vs/workbench/services/themes/common/colorThemeStore';
|
||||
import { FileIconThemeStore } from 'vs/workbench/services/themes/common/fileIconThemeStore';
|
||||
import { FileIconThemeData } from 'vs/workbench/services/themes/common/fileIconThemeData';
|
||||
import { removeClasses, addClasses } from 'vs/base/browser/dom';
|
||||
@@ -64,10 +64,6 @@ function validateThemeId(theme: string): string {
|
||||
return theme;
|
||||
}
|
||||
|
||||
export interface IColorCustomizations {
|
||||
[colorIdOrThemeSettingsId: string]: string | IColorCustomizations;
|
||||
}
|
||||
|
||||
export class WorkbenchThemeService implements IWorkbenchThemeService {
|
||||
_serviceBrand: any;
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import * as Json from 'vs/base/common/json';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ExtensionData, ITokenColorCustomizations, ITokenColorizationRule, IColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { convertSettings } from 'vs/workbench/services/themes/browser/themeCompatibility';
|
||||
import { ExtensionData, ITokenColorCustomizations, ITokenColorizationRule, IColorTheme, IColorMap, IThemeExtensionPoint, VS_LIGHT_THEME, VS_HC_THEME, IColorCustomizations } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { convertSettings } from 'vs/workbench/services/themes/common/themeCompatibility';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as types from 'vs/base/common/types';
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
@@ -15,7 +15,6 @@ import * as resources from 'vs/base/common/resources';
|
||||
import { Extensions, IColorRegistry, ColorIdentifier, editorBackground, editorForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { ThemeType } from 'vs/platform/theme/common/themeService';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IColorCustomizations } from 'vs/workbench/services/themes/browser/workbenchThemeService';
|
||||
import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
@@ -9,7 +9,7 @@ import * as types from 'vs/base/common/types';
|
||||
import * as resources from 'vs/base/common/resources';
|
||||
import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry';
|
||||
import { ExtensionData, IThemeExtensionPoint, VS_LIGHT_THEME, VS_DARK_THEME, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { ColorThemeData } from 'vs/workbench/services/themes/browser/colorThemeData';
|
||||
import { ColorThemeData } from 'vs/workbench/services/themes/common/colorThemeData';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
@@ -64,6 +64,10 @@ export interface IWorkbenchThemeService extends IThemeService {
|
||||
onDidFileIconThemeChange: Event<IFileIconTheme>;
|
||||
}
|
||||
|
||||
export interface IColorCustomizations {
|
||||
[colorIdOrThemeSettingsId: string]: string | IColorCustomizations;
|
||||
}
|
||||
|
||||
export interface ITokenColorCustomizations {
|
||||
comments?: string | ITokenColorizationSetting;
|
||||
strings?: string | ITokenColorizationSetting;
|
||||
|
||||
@@ -106,19 +106,27 @@ suite('Notifications', () => {
|
||||
assert.equal(item6.actions.primary!.length, 1);
|
||||
|
||||
// Links
|
||||
let item7 = NotificationViewItem.create({ severity: Severity.Info, message: 'Unable to [Link 1](http://link1.com) open [Link 2](https://link2.com) and [Invalid Link3](ftp://link3.com)' })!;
|
||||
let item7 = NotificationViewItem.create({ severity: Severity.Info, message: 'Unable to [Link 1](http://link1.com) open [Link 2](command:open.me "Open This") and [Link 3](command:without.title) and [Invalid Link4](ftp://link4.com)' })!;
|
||||
|
||||
const links = item7.message.links;
|
||||
assert.equal(links.length, 2);
|
||||
assert.equal(links.length, 3);
|
||||
assert.equal(links[0].name, 'Link 1');
|
||||
assert.equal(links[0].href, 'http://link1.com');
|
||||
assert.equal(links[0].title, 'http://link1.com');
|
||||
assert.equal(links[0].length, '[Link 1](http://link1.com)'.length);
|
||||
assert.equal(links[0].offset, 'Unable to '.length);
|
||||
|
||||
assert.equal(links[1].name, 'Link 2');
|
||||
assert.equal(links[1].href, 'https://link2.com');
|
||||
assert.equal(links[1].length, '[Link 2](https://link2.com)'.length);
|
||||
assert.equal(links[1].href, 'command:open.me');
|
||||
assert.equal(links[1].title, 'Open This');
|
||||
assert.equal(links[1].length, '[Link 2](command:open.me "Open This")'.length);
|
||||
assert.equal(links[1].offset, 'Unable to [Link 1](http://link1.com) open '.length);
|
||||
|
||||
assert.equal(links[2].name, 'Link 3');
|
||||
assert.equal(links[2].href, 'command:without.title');
|
||||
assert.equal(links[2].title, 'Click to execute command \'without.title\'');
|
||||
assert.equal(links[2].length, '[Link 3](command:without.title)'.length);
|
||||
assert.equal(links[2].offset, 'Unable to [Link 1](http://link1.com) open [Link 2](command:open.me "Open This") and '.length);
|
||||
});
|
||||
|
||||
test('Model', () => {
|
||||
|
||||
@@ -200,7 +200,8 @@ export class TestTextFileService extends BrowserTextFileService {
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IDialogService dialogService: IDialogService,
|
||||
@IFileDialogService fileDialogService: IFileDialogService,
|
||||
@IEditorService editorService: IEditorService
|
||||
@IEditorService editorService: IEditorService,
|
||||
@ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService
|
||||
) {
|
||||
super(
|
||||
contextService,
|
||||
@@ -219,7 +220,8 @@ export class TestTextFileService extends BrowserTextFileService {
|
||||
contextKeyService,
|
||||
dialogService,
|
||||
fileDialogService,
|
||||
editorService
|
||||
editorService,
|
||||
textResourceConfigurationService
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user