mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-01-13 17:22:15 -05:00
Merge from vscode 3bd60b2ba753e7fe39b42f99184bc6c5881d3551 (#4712)
This commit is contained in:
@@ -16,6 +16,9 @@ steps:
|
||||
- script: |
|
||||
set -e
|
||||
|
||||
# Get snapcraft version
|
||||
snapcraft --version
|
||||
|
||||
# Make sure we get latest packages
|
||||
sudo apt-get update
|
||||
sudo apt-get upgrade -y
|
||||
@@ -38,7 +41,7 @@ steps:
|
||||
PACKAGEJSON="$(ls $SNAP_ROOT/code*/usr/share/code*/resources/app/package.json)"
|
||||
VERSION=$(node -p "require(\"$PACKAGEJSON\").version")
|
||||
SNAP_PATH="$SNAP_ROOT/$SNAP_FILENAME"
|
||||
(cd $SNAP_ROOT/code-* && snapcraft snap --output "$SNAP_PATH")
|
||||
(cd $SNAP_ROOT/code-* && sudo snapcraft snap --output "$SNAP_PATH")
|
||||
|
||||
# Publish snap package
|
||||
AZURE_DOCUMENTDB_MASTERKEY="$(AZURE_DOCUMENTDB_MASTERKEY)" \
|
||||
|
||||
@@ -203,11 +203,17 @@ function prepareSnapPackage(arch) {
|
||||
|
||||
return function () {
|
||||
const desktop = gulp.src('resources/linux/code.desktop', { base: '.' })
|
||||
.pipe(rename(`usr/share/applications/${product.applicationName}.desktop`));
|
||||
|
||||
const desktopUrlHandler = gulp.src('resources/linux/code-url-handler.desktop', { base: '.' })
|
||||
.pipe(rename(`usr/share/applications/${product.applicationName}-url-handler.desktop`));
|
||||
|
||||
const desktops = es.merge(desktop, desktopUrlHandler)
|
||||
.pipe(replace('@@NAME_LONG@@', product.nameLong))
|
||||
.pipe(replace('@@NAME_SHORT@@', product.nameShort))
|
||||
.pipe(replace('@@NAME@@', product.applicationName))
|
||||
.pipe(replace('@@ICON@@', `/usr/share/pixmaps/${product.linuxIconName}.png`))
|
||||
.pipe(rename(`usr/share/applications/${product.applicationName}.desktop`));
|
||||
.pipe(replace('@@URLPROTOCOL@@', product.urlProtocol));
|
||||
|
||||
const icon = gulp.src('resources/linux/code.png', { base: '.' })
|
||||
.pipe(rename(`usr/share/pixmaps/${product.linuxIconName}.png`));
|
||||
@@ -223,7 +229,7 @@ function prepareSnapPackage(arch) {
|
||||
const electronLaunch = gulp.src('resources/linux/snap/electron-launch', { base: '.' })
|
||||
.pipe(rename('electron-launch'));
|
||||
|
||||
const all = es.merge(desktop, icon, code, snapcraft, electronLaunch);
|
||||
const all = es.merge(desktops, icon, code, snapcraft, electronLaunch);
|
||||
|
||||
return all.pipe(vfs.dest(destination));
|
||||
};
|
||||
|
||||
@@ -45,6 +45,7 @@ export interface IPickAndOpenOptions {
|
||||
forceNewWindow?: boolean;
|
||||
defaultUri?: URI;
|
||||
telemetryExtraData?: ITelemetryData;
|
||||
availableFileSystems?: string[];
|
||||
}
|
||||
|
||||
export interface ISaveDialogOptions {
|
||||
|
||||
@@ -285,30 +285,31 @@ export enum FileSystemProviderErrorCode {
|
||||
FileNotADirectory = 'EntryNotADirectory',
|
||||
FileIsADirectory = 'EntryIsADirectory',
|
||||
NoPermissions = 'NoPermissions',
|
||||
Unavailable = 'Unavailable'
|
||||
Unavailable = 'Unavailable',
|
||||
Unknown = 'Unknown'
|
||||
}
|
||||
|
||||
export class FileSystemProviderError extends Error {
|
||||
|
||||
constructor(message: string, public readonly code?: FileSystemProviderErrorCode) {
|
||||
constructor(message: string, public readonly code: FileSystemProviderErrorCode) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
export function createFileSystemProviderError(error: Error, code?: FileSystemProviderErrorCode): FileSystemProviderError {
|
||||
export function createFileSystemProviderError(error: Error, code: FileSystemProviderErrorCode): FileSystemProviderError {
|
||||
const providerError = new FileSystemProviderError(error.toString(), code);
|
||||
markAsFileSystemProviderError(providerError);
|
||||
markAsFileSystemProviderError(providerError, code);
|
||||
|
||||
return providerError;
|
||||
}
|
||||
|
||||
export function markAsFileSystemProviderError(error: Error, code?: FileSystemProviderErrorCode): Error {
|
||||
export function markAsFileSystemProviderError(error: Error, code: FileSystemProviderErrorCode): Error {
|
||||
error.name = code ? `${code} (FileSystemError)` : `FileSystemError`;
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
export function toFileSystemProviderErrorCode(error: Error): FileSystemProviderErrorCode | undefined {
|
||||
export function toFileSystemProviderErrorCode(error: Error): FileSystemProviderErrorCode {
|
||||
|
||||
// FileSystemProviderError comes with the code
|
||||
if (error instanceof FileSystemProviderError) {
|
||||
@@ -319,7 +320,7 @@ export function toFileSystemProviderErrorCode(error: Error): FileSystemProviderE
|
||||
// went through the markAsFileSystemProviderError() method
|
||||
const match = /^(.+) \(FileSystemError\)$/.exec(error.name);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
return FileSystemProviderErrorCode.Unknown;
|
||||
}
|
||||
|
||||
switch (match[1]) {
|
||||
@@ -331,7 +332,7 @@ export function toFileSystemProviderErrorCode(error: Error): FileSystemProviderE
|
||||
case FileSystemProviderErrorCode.Unavailable: return FileSystemProviderErrorCode.Unavailable;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
return FileSystemProviderErrorCode.Unknown;
|
||||
}
|
||||
|
||||
export function toFileOperationResult(error: Error): FileOperationResult {
|
||||
|
||||
5
src/vs/vscode.d.ts
vendored
5
src/vs/vscode.d.ts
vendored
@@ -6599,10 +6599,11 @@ declare module 'vscode' {
|
||||
*
|
||||
* @param name Optional human-readable string which will be used to represent the terminal in the UI.
|
||||
* @param shellPath Optional path to a custom shell executable to be used in the terminal.
|
||||
* @param shellArgs Optional args for the custom shell executable, this does not work on Windows (see #8429)
|
||||
* @param shellArgs Optional args for the custom shell executable. A string can be used on Windows only which
|
||||
* allows specifying shell args in [command-line format](https://msdn.microsoft.com/en-au/08dfcab2-eb6e-49a4-80eb-87d4076c98c6).
|
||||
* @return A new Terminal.
|
||||
*/
|
||||
export function createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): Terminal;
|
||||
export function createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): Terminal;
|
||||
|
||||
/**
|
||||
* Creates a [Terminal](#Terminal). The cwd of the terminal will be the workspace directory
|
||||
|
||||
@@ -294,6 +294,9 @@ jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', {
|
||||
default: {},
|
||||
description: nls.localize('workspaceConfig.extensions.description', "Workspace extensions"),
|
||||
$ref: 'vscode://schemas/extensions'
|
||||
},
|
||||
'remoteAuthority': {
|
||||
type: 'string'
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
|
||||
@@ -479,7 +479,7 @@ export function createApiFactory(
|
||||
createWebviewPanel(viewType: string, title: string, showOptions: vscode.ViewColumn | { viewColumn: vscode.ViewColumn, preserveFocus?: boolean }, options: vscode.WebviewPanelOptions & vscode.WebviewOptions): vscode.WebviewPanel {
|
||||
return extHostWebviews.createWebviewPanel(extension, viewType, title, showOptions, options);
|
||||
},
|
||||
createTerminal(nameOrOptions?: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
|
||||
createTerminal(nameOrOptions?: vscode.TerminalOptions | string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
|
||||
if (typeof nameOrOptions === 'object') {
|
||||
return extHostTerminalService.createTerminalFromOptions(<vscode.TerminalOptions>nameOrOptions);
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
||||
try {
|
||||
validateConstraint(args[i], description.args[i].constraint);
|
||||
} catch (err) {
|
||||
return Promise.reject(new Error(`Running the contributed command:'${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`));
|
||||
return Promise.reject(new Error(`Running the contributed command: '${id}' failed. Illegal argument '${description.args[i].name}' - ${description.args[i].description}`));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -158,7 +158,7 @@ export class ExtHostCommands implements ExtHostCommandsShape {
|
||||
return Promise.resolve(result);
|
||||
} catch (err) {
|
||||
this._logService.error(err, id);
|
||||
return Promise.reject(new Error(`Running the contributed command:'${id}' failed.`));
|
||||
return Promise.reject(new Error(`Running the contributed command: '${id}' failed.`));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -651,7 +651,7 @@ function convertToModeComment(commentController: ExtHostCommentController, vscod
|
||||
isDraft: vscodeComment.isDraft,
|
||||
selectCommand: vscodeComment.selectCommand ? commandsConverter.toInternal(vscodeComment.selectCommand) : undefined,
|
||||
editCommand: vscodeComment.editCommand ? commandsConverter.toInternal(vscodeComment.editCommand) : undefined,
|
||||
deleteCommand: vscodeComment.editCommand ? commandsConverter.toInternal(vscodeComment.deleteCommand) : undefined,
|
||||
deleteCommand: vscodeComment.deleteCommand ? commandsConverter.toInternal(vscodeComment.deleteCommand) : undefined,
|
||||
label: vscodeComment.label,
|
||||
commentReactions: vscodeComment.commentReactions ? vscodeComment.commentReactions.map(reaction => convertToReaction2(commentController.reactionProvider, reaction)) : undefined
|
||||
};
|
||||
|
||||
@@ -293,7 +293,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape {
|
||||
this._proxy = mainContext.getProxy(MainContext.MainThreadTerminalService);
|
||||
}
|
||||
|
||||
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[]): vscode.Terminal {
|
||||
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, name);
|
||||
terminal.create(shellPath, shellArgs);
|
||||
this._terminals.push(terminal);
|
||||
|
||||
@@ -2207,12 +2207,12 @@ export class FileSystemError extends Error {
|
||||
return new FileSystemError(messageOrUri, FileSystemProviderErrorCode.Unavailable, FileSystemError.Unavailable);
|
||||
}
|
||||
|
||||
constructor(uriOrMessage?: string | URI, code?: string, terminator?: Function) {
|
||||
constructor(uriOrMessage?: string | URI, code: FileSystemProviderErrorCode = FileSystemProviderErrorCode.Unknown, terminator?: Function) {
|
||||
super(URI.isUri(uriOrMessage) ? uriOrMessage.toString(true) : uriOrMessage);
|
||||
|
||||
// mark the error as file system provider error so that
|
||||
// we can extract the error code on the receiving side
|
||||
markAsFileSystemProviderError(this);
|
||||
markAsFileSystemProviderError(this, code);
|
||||
|
||||
// workaround when extending builtin objects and when compiling to ES5, see:
|
||||
// https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export class OpenFileAction extends Action {
|
||||
|
||||
@@ -34,6 +35,24 @@ export class OpenFileAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenLocalFileAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openLocalFile';
|
||||
static LABEL = nls.localize('openLocalFile', "Open Local File...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IFileDialogService private readonly dialogService: IFileDialogService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: any, data?: ITelemetryData): Promise<any> {
|
||||
return this.dialogService.pickFileAndOpen({ forceNewWindow: false, telemetryExtraData: data, availableFileSystems: [Schemas.file] });
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFolder';
|
||||
@@ -52,6 +71,25 @@ export class OpenFolderAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenLocalFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openLocalFolder';
|
||||
static LABEL = nls.localize('openLocalFolder', "Open Local Folder...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IFileDialogService private readonly dialogService: IFileDialogService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: any, data?: ITelemetryData): Promise<any> {
|
||||
return this.dialogService.pickFolderAndOpen({ forceNewWindow: false, telemetryExtraData: data, availableFileSystems: [Schemas.file] });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class OpenFileFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openFileFolder';
|
||||
@@ -70,6 +108,24 @@ export class OpenFileFolderAction extends Action {
|
||||
}
|
||||
}
|
||||
|
||||
export class OpenLocalFileFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.files.openLocalFileFolder';
|
||||
static LABEL = nls.localize('openLocalFileFolder', "Open Local...");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IFileDialogService private readonly dialogService: IFileDialogService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(event?: any, data?: ITelemetryData): Promise<any> {
|
||||
return this.dialogService.pickFileFolderAndOpen({ forceNewWindow: false, telemetryExtraData: data, availableFileSystems: [Schemas.file] });
|
||||
}
|
||||
}
|
||||
|
||||
export class AddRootFolderAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.addRootFolder';
|
||||
|
||||
@@ -70,7 +70,7 @@ CommandsRegistry.registerCommand({
|
||||
}
|
||||
|
||||
// Add and show Files Explorer viewlet
|
||||
return workspaceEditingService.addFolders(folders.map(folder => ({ uri: folder })))
|
||||
return workspaceEditingService.addFolders(folders.map(folder => ({ uri: resources.removeTrailingPathSeparator(folder) })))
|
||||
.then(() => viewletService.openViewlet(viewletService.getDefaultViewletId(), true))
|
||||
.then(() => undefined);
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { hasWorkspaceFileExtension, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { normalize } from 'vs/base/common/path';
|
||||
import { basename } from 'vs/base/common/resources';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
@@ -29,6 +29,7 @@ import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IRecentFile } from 'vs/platform/history/common/history';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing';
|
||||
|
||||
export interface IDraggedResource {
|
||||
resource: URI;
|
||||
@@ -154,12 +155,12 @@ export class ResourcesDropHandler {
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IWindowsService private readonly windowsService: IWindowsService,
|
||||
@IWindowService private readonly windowService: IWindowService,
|
||||
@IWorkspacesService private readonly workspacesService: IWorkspacesService,
|
||||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IBackupFileService private readonly backupFileService: IBackupFileService,
|
||||
@IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService,
|
||||
@IEditorService private readonly editorService: IEditorService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -284,26 +285,13 @@ export class ResourcesDropHandler {
|
||||
// Pass focus to window
|
||||
this.windowService.focusWindow();
|
||||
|
||||
let workspacesToOpen: Promise<IURIToOpen[]> | undefined;
|
||||
|
||||
// Open in separate windows if we drop workspaces or just one folder
|
||||
if (workspaces.length > 0 || folders.length === 1) {
|
||||
workspacesToOpen = Promise.resolve([...workspaces, ...folders]);
|
||||
return this.windowService.openWindow([...workspaces, ...folders], { forceReuseWindow: true }).then(_ => true);
|
||||
}
|
||||
|
||||
// Multiple folders: Create new workspace with folders and open
|
||||
else if (folders.length > 1) {
|
||||
workspacesToOpen = this.workspacesService.createUntitledWorkspace(folders).then(workspace => [<IURIToOpen>{ uri: workspace.configPath, typeHint: 'file' }]);
|
||||
}
|
||||
|
||||
// Open
|
||||
if (workspacesToOpen) {
|
||||
workspacesToOpen.then(workspaces => {
|
||||
this.windowService.openWindow(workspaces, { forceReuseWindow: true });
|
||||
});
|
||||
}
|
||||
|
||||
return true;
|
||||
// folders.length > 1: Multiple folders: Create new workspace with folders and open
|
||||
return this.workspaceEditingService.createAndEnterWorkspace(folders).then(_ => true);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1810,7 +1810,7 @@ export class SimpleWorkspacesService implements IWorkspacesService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[]): Promise<IWorkspaceIdentifier> {
|
||||
createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
|
||||
// @ts-ignore
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
@@ -21,3 +21,5 @@ export const IsDevelopmentContext = new RawContextKey<boolean>('isDevelopment',
|
||||
export const WorkbenchStateContext = new RawContextKey<string>('workbenchState', undefined);
|
||||
|
||||
export const WorkspaceFolderCountContext = new RawContextKey<number>('workspaceFolderCount', 0);
|
||||
|
||||
export const RemoteFileDialogContext = new RawContextKey<boolean>('remoteFileDialogVisible', false);
|
||||
|
||||
@@ -119,8 +119,8 @@ registerEditorAction(class extends EditorAction {
|
||||
constructor() {
|
||||
super({
|
||||
id: 'editor.showCallHierarchy',
|
||||
label: localize('title', "Call Hierarchy"),
|
||||
alias: 'Call Hierarchy',
|
||||
label: localize('title', "Peek Call Hierarchy"),
|
||||
alias: 'Peek Call Hierarchy',
|
||||
menuOpts: {
|
||||
group: 'navigation',
|
||||
order: 1.48
|
||||
|
||||
@@ -144,7 +144,7 @@ export class CommentNode extends Disposable {
|
||||
let reactionGroup = this.commentService.getReactionGroup(this.owner);
|
||||
if (reactionGroup && reactionGroup.length) {
|
||||
let commentThread = this.commentThread as modes.CommentThread2;
|
||||
if (commentThread.commentThreadHandle) {
|
||||
if (commentThread.commentThreadHandle !== undefined) {
|
||||
let toggleReactionAction = this.createReactionPicker2();
|
||||
actions.push(toggleReactionAction);
|
||||
} else {
|
||||
@@ -327,7 +327,7 @@ export class CommentNode extends Disposable {
|
||||
let action = new ReactionAction(`reaction.${reaction.label}`, `${reaction.label}`, reaction.hasReacted && reaction.canEdit ? 'active' : '', reaction.canEdit, async () => {
|
||||
try {
|
||||
let commentThread = this.commentThread as modes.CommentThread2;
|
||||
if (commentThread.commentThreadHandle) {
|
||||
if (commentThread.commentThreadHandle !== undefined) {
|
||||
await this.commentService.toggleReaction(this.owner, this.resource, this.commentThread as modes.CommentThread2, this.comment, reaction);
|
||||
} else {
|
||||
if (reaction.hasReacted) {
|
||||
@@ -360,7 +360,7 @@ export class CommentNode extends Disposable {
|
||||
let reactionGroup = this.commentService.getReactionGroup(this.owner);
|
||||
if (reactionGroup && reactionGroup.length) {
|
||||
let commentThread = this.commentThread as modes.CommentThread2;
|
||||
if (commentThread.commentThreadHandle) {
|
||||
if (commentThread.commentThreadHandle !== undefined) {
|
||||
let toggleReactionAction = this.createReactionPicker2();
|
||||
this._reactionsActionBar.push(toggleReactionAction, { label: false, icon: true });
|
||||
} else {
|
||||
@@ -386,7 +386,7 @@ export class CommentNode extends Disposable {
|
||||
this._commentEditor.setSelection(new Selection(lastLine, lastColumn, lastLine, lastColumn));
|
||||
|
||||
let commentThread = this.commentThread as modes.CommentThread2;
|
||||
if (commentThread.commentThreadHandle) {
|
||||
if (commentThread.commentThreadHandle !== undefined) {
|
||||
commentThread.input = {
|
||||
uri: this._commentEditor.getModel()!.uri,
|
||||
value: this.comment.body.value
|
||||
|
||||
@@ -689,7 +689,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget
|
||||
async submitComment(): Promise<void> {
|
||||
const activeComment = this.getActiveComment();
|
||||
if (activeComment instanceof ReviewZoneWidget) {
|
||||
if ((this._commentThread as modes.CommentThread2).commentThreadHandle) {
|
||||
if ((this._commentThread as modes.CommentThread2).commentThreadHandle !== undefined) {
|
||||
let commentThread = this._commentThread as modes.CommentThread2;
|
||||
|
||||
if (commentThread.acceptInputCommand) {
|
||||
|
||||
@@ -21,7 +21,7 @@ export class DebugStatus extends Themable implements IStatusbarItem {
|
||||
private statusBarItem: HTMLElement;
|
||||
private label: HTMLElement;
|
||||
private icon: HTMLElement;
|
||||
private showInStatusBar: string;
|
||||
private showInStatusBar: 'never' | 'always' | 'onFirstSessionStart';
|
||||
|
||||
constructor(
|
||||
@IQuickOpenService private readonly quickOpenService: IQuickOpenService,
|
||||
@@ -36,6 +36,10 @@ export class DebugStatus extends Themable implements IStatusbarItem {
|
||||
this._register(this.debugService.onDidChangeState(state => {
|
||||
if (state !== State.Inactive && this.showInStatusBar === 'onFirstSessionStart') {
|
||||
this.doRender();
|
||||
} else {
|
||||
if (this.showInStatusBar !== 'never') {
|
||||
this.updateStyles();
|
||||
}
|
||||
}
|
||||
}));
|
||||
this.showInStatusBar = configurationService.getValue<IDebugConfiguration>('debug').showInStatusBar;
|
||||
@@ -53,7 +57,6 @@ export class DebugStatus extends Themable implements IStatusbarItem {
|
||||
}
|
||||
|
||||
protected updateStyles(): void {
|
||||
super.updateStyles();
|
||||
if (this.icon) {
|
||||
if (isStatusbarInDebugMode(this.debugService)) {
|
||||
this.icon.style.backgroundColor = this.getColor(STATUS_BAR_DEBUGGING_FOREGROUND);
|
||||
|
||||
@@ -11,7 +11,7 @@ import { isObject } from 'vs/base/common/types';
|
||||
import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc';
|
||||
import { IJSONSchema, IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
|
||||
import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
|
||||
import { IConfig, IDebuggerContribution, IDebugAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, ITerminalSettings, IDebugger, IDebugSession, IAdapterDescriptor, IDebugAdapterServer } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IConfig, IDebuggerContribution, IDebugAdapterExecutable, INTERNAL_CONSOLE_OPTIONS_SCHEMA, IConfigurationManager, IDebugAdapter, ITerminalSettings, IDebugger, IDebugSession, IAdapterDescriptor, IDebugAdapterServer, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
|
||||
@@ -187,8 +187,11 @@ export class Debugger implements IDebugger {
|
||||
}
|
||||
|
||||
private inExtHost(): boolean {
|
||||
/*
|
||||
const debugConfigs = this.configurationService.getValue<IDebugConfiguration>('debug');
|
||||
if (typeof debugConfigs.extensionHostDebugAdapter === 'boolean') {
|
||||
return debugConfigs.extensionHostDebugAdapter;
|
||||
}
|
||||
/*
|
||||
return !!debugConfigs.extensionHostDebugAdapter
|
||||
|| this.configurationManager.needsToRunInExtHost(this.type)
|
||||
|| (!!this.mainExtensionDescription && this.mainExtensionDescription.extensionLocation.scheme !== 'file');
|
||||
|
||||
@@ -429,6 +429,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
|
||||
|
||||
private toDispose: IDisposable[];
|
||||
private dropEnabled: boolean;
|
||||
private isCopy: boolean;
|
||||
|
||||
constructor(
|
||||
@INotificationService private notificationService: INotificationService,
|
||||
@@ -549,7 +550,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
|
||||
}
|
||||
|
||||
getDragURI(element: ExplorerItem): string | null {
|
||||
if (this.explorerService.isEditable(element)) {
|
||||
if (this.explorerService.isEditable(element) || (!this.isCopy && element.isReadonly)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -565,6 +566,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
|
||||
}
|
||||
|
||||
onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void {
|
||||
this.isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh);
|
||||
const items = (data as ElementsDragAndDropData<ExplorerItem>).elements;
|
||||
if (items && items.length && originalEvent.dataTransfer) {
|
||||
// Apply some datatransfer types to allow for dragging the element outside of the application
|
||||
@@ -597,7 +599,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
|
||||
}
|
||||
// In-Explorer DND (Move/Copy file)
|
||||
else {
|
||||
this.handleExplorerDrop(data, target, originalEvent);
|
||||
this.handleExplorerDrop(data, target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -711,15 +713,14 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
|
||||
private handleExplorerDrop(data: IDragAndDropData, target: ExplorerItem, originalEvent: DragEvent): Promise<void> {
|
||||
private handleExplorerDrop(data: IDragAndDropData, target: ExplorerItem): Promise<void> {
|
||||
const elementsData = (data as ElementsDragAndDropData<ExplorerItem>).elements;
|
||||
const items = distinctParents(elementsData, s => s.resource);
|
||||
const isCopy = (originalEvent.ctrlKey && !isMacintosh) || (originalEvent.altKey && isMacintosh);
|
||||
|
||||
let confirmPromise: Promise<IConfirmationResult>;
|
||||
|
||||
// Handle confirm setting
|
||||
const confirmDragAndDrop = !isCopy && this.configurationService.getValue<boolean>(FileDragAndDrop.CONFIRM_DND_SETTING_KEY);
|
||||
const confirmDragAndDrop = !this.isCopy && this.configurationService.getValue<boolean>(FileDragAndDrop.CONFIRM_DND_SETTING_KEY);
|
||||
if (confirmDragAndDrop) {
|
||||
confirmPromise = this.dialogService.confirm({
|
||||
message: items.length > 1 && items.every(s => s.isRoot) ? localize('confirmRootsMove', "Are you sure you want to change the order of multiple root folders in your workspace?")
|
||||
@@ -747,7 +748,7 @@ export class FileDragAndDrop implements ITreeDragAndDrop<ExplorerItem> {
|
||||
return updateConfirmSettingsPromise.then(() => {
|
||||
if (res.confirmed) {
|
||||
const rootDropPromise = this.doHandleRootDrop(items.filter(s => s.isRoot), target);
|
||||
return Promise.all(items.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(source, target, isCopy)).concat(rootDropPromise)).then(() => undefined);
|
||||
return Promise.all(items.filter(s => !s.isRoot).map(source => this.doHandleExplorerDrop(source, target, this.isCopy)).concat(rootDropPromise)).then(() => undefined);
|
||||
}
|
||||
|
||||
return Promise.resolve(undefined);
|
||||
|
||||
@@ -40,6 +40,7 @@ export class ExplorerService implements IExplorerService {
|
||||
private editable: { stat: ExplorerItem, data: IEditableData } | undefined;
|
||||
private _sortOrder: SortOrder;
|
||||
private cutItems: ExplorerItem[] | undefined;
|
||||
private fileSystemProviderSchemes = new Set<string>();
|
||||
|
||||
constructor(
|
||||
@IFileService private fileService: IFileService,
|
||||
@@ -98,7 +99,14 @@ export class ExplorerService implements IExplorerService {
|
||||
this.disposables.push(this.fileService.onAfterOperation(e => this.onFileOperation(e)));
|
||||
this.disposables.push(this.fileService.onFileChanges(e => this.onFileChanges(e)));
|
||||
this.disposables.push(this.configurationService.onDidChangeConfiguration(e => this.onConfigurationUpdated(this.configurationService.getValue<IFilesConfiguration>())));
|
||||
this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(() => this._onDidChangeItem.fire(undefined)));
|
||||
this.disposables.push(this.fileService.onDidChangeFileSystemProviderRegistrations(e => {
|
||||
if (e.added && this.fileSystemProviderSchemes.has(e.scheme)) {
|
||||
// A file system provider got re-registered, we should update all file stats since they might change (got read-only)
|
||||
this._onDidChangeItem.fire(undefined);
|
||||
} else {
|
||||
this.fileSystemProviderSchemes.add(e.scheme);
|
||||
}
|
||||
}));
|
||||
this.disposables.push(model.onDidChangeRoots(() => this._onDidChangeRoots.fire()));
|
||||
|
||||
return model;
|
||||
|
||||
@@ -61,15 +61,16 @@ export class ActivityUpdater extends Disposable implements IWorkbenchContributio
|
||||
|
||||
constructor(
|
||||
@IActivityService private readonly activityService: IActivityService,
|
||||
@IMarkersWorkbenchService private readonly markersWorkbenchService: IMarkersWorkbenchService
|
||||
@IMarkerService private readonly markerService: IMarkerService
|
||||
) {
|
||||
super();
|
||||
this._register(this.markersWorkbenchService.markersModel.onDidChange(() => this.updateBadge()));
|
||||
this._register(this.markerService.onMarkerChanged(() => this.updateBadge()));
|
||||
this.updateBadge();
|
||||
}
|
||||
|
||||
private updateBadge(): void {
|
||||
const total = this.markersWorkbenchService.markersModel.resourceMarkers.reduce((r, rm) => r + rm.markers.length, 0);
|
||||
const { errors, warnings, infos, unknowns } = this.markerService.getStatistics();
|
||||
const total = errors + warnings + infos + unknowns;
|
||||
const message = localize('totalProblems', 'Total {0} Problems', total);
|
||||
this.activityService.showActivity(Constants.MARKERS_PANEL_ID, new NumberBadge(total, () => message));
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@
|
||||
|
||||
.markers-panel .monaco-tl-contents .marker-icon {
|
||||
height: 22px;
|
||||
flex: 0 0 16px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
.markers-panel .marker-icon.warning {
|
||||
|
||||
@@ -943,7 +943,9 @@ export namespace KeyedTaskIdentifier {
|
||||
}
|
||||
export function create(value: TaskIdentifier): KeyedTaskIdentifier {
|
||||
const resultKey = sortedStringify(value);
|
||||
return { _key: resultKey, type: value.taskType };
|
||||
let result = { _key: resultKey, type: value.taskType };
|
||||
Objects.assign(result, value);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -890,7 +890,8 @@ export class TerminalTaskSystem implements ITaskSystem {
|
||||
this.currentTask.shellLaunchConfig = {
|
||||
isRendererOnly: true,
|
||||
waitOnExit,
|
||||
name: this.createTerminalName(task)
|
||||
name: this.createTerminalName(task),
|
||||
initialText: task.command.presentation && task.command.presentation.echo ? `\x1b[1m> Executing task: ${task._label} <\x1b[0m\n` : undefined
|
||||
};
|
||||
} else {
|
||||
let resolvedResult: { command: CommandString, args: CommandString[] } = this.resolveCommandAndArgs(resolver, task.command);
|
||||
|
||||
@@ -955,7 +955,7 @@ export class TerminalInstance implements ITerminalInstance {
|
||||
if (typeof this._shellLaunchConfig.waitOnExit === 'string') {
|
||||
let message = this._shellLaunchConfig.waitOnExit;
|
||||
// Bold the message and add an extra new line to make it stand out from the rest of the output
|
||||
message = `\n\x1b[1m${message}\x1b[0m`;
|
||||
message = `\r\n\x1b[1m${message}\x1b[0m`;
|
||||
this._xterm.writeln(message);
|
||||
}
|
||||
// Disable all input if the terminal is exiting and listen for next keypress
|
||||
|
||||
@@ -14,14 +14,14 @@ import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform';
|
||||
import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction } from 'vs/workbench/electron-browser/actions/helpActions';
|
||||
import { ToggleSharedProcessAction, InspectContextKeysAction, ToggleScreencastModeAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions';
|
||||
import { ShowAboutDialogAction, ZoomResetAction, ZoomOutAction, ZoomInAction, ToggleFullScreenAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, QuickOpenRecentAction, inRecentFilesPickerContextKey, OpenRecentAction, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ReloadWindowAction, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions';
|
||||
import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { AddRootFolderAction, GlobalRemoveRootFolderAction, OpenWorkspaceAction, SaveWorkspaceAsAction, OpenWorkspaceConfigFileAction, DuplicateWorkspaceInNewWindowAction, OpenFileFolderAction, OpenFileAction, OpenFolderAction, CloseWorkspaceAction, OpenLocalFileAction, OpenLocalFolderAction, OpenLocalFileFolderAction } from 'vs/workbench/browser/actions/workspaceActions';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands';
|
||||
import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/common/contextkeys';
|
||||
import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext, RemoteFileDialogContext } from 'vs/workbench/common/contextkeys';
|
||||
import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor';
|
||||
import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows';
|
||||
import { LogStorageAction } from 'vs/platform/storage/node/storageService';
|
||||
@@ -40,9 +40,12 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/electron-brow
|
||||
|
||||
if (isMacintosh) {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFileFolderAction, OpenLocalFileFolderAction.ID, OpenLocalFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, RemoteFileDialogContext), 'File: Open Local...', fileCategory, RemoteFileDialogContext);
|
||||
} else {
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFileAction, OpenLocalFileAction.ID, OpenLocalFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }, RemoteFileDialogContext), 'File: Open Local File...', fileCategory, RemoteFileDialogContext);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLocalFolderAction, OpenLocalFolderAction.ID, OpenLocalFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }, RemoteFileDialogContext), 'File: Open Local Folder...', fileCategory, RemoteFileDialogContext);
|
||||
}
|
||||
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory);
|
||||
|
||||
@@ -181,7 +181,7 @@ class CodeRendererMain extends Disposable {
|
||||
const fileService = new FileService2(logService);
|
||||
serviceCollection.set(IFileService, fileService);
|
||||
|
||||
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider());
|
||||
fileService.registerProvider(Schemas.file, new DiskFileSystemProvider(logService));
|
||||
|
||||
// Remote
|
||||
const remoteAuthorityResolverService = new RemoteAuthorityResolverService();
|
||||
|
||||
@@ -22,6 +22,8 @@ import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
|
||||
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { RemoteFileDialogContext } from 'vs/workbench/common/contextkeys';
|
||||
|
||||
interface FileQuickPickItem extends IQuickPickItem {
|
||||
uri: URI;
|
||||
@@ -48,6 +50,7 @@ export class RemoteFileDialog {
|
||||
private scheme: string = REMOTE_HOST_SCHEME;
|
||||
private shouldOverwriteFile: boolean = false;
|
||||
private autoComplete: string;
|
||||
private contextKey: IContextKey<boolean>;
|
||||
|
||||
constructor(
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@@ -61,9 +64,11 @@ export class RemoteFileDialog {
|
||||
@IModeService private readonly modeService: IModeService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService,
|
||||
@IContextKeyService contextKeyService: IContextKeyService
|
||||
|
||||
) {
|
||||
this.remoteAuthority = this.windowService.getConfiguration().remoteAuthority;
|
||||
this.contextKey = RemoteFileDialogContext.bindTo(contextKeyService);
|
||||
}
|
||||
|
||||
public async showOpenDialog(options: IOpenDialogOptions = {}): Promise<IURIToOpen[] | undefined> {
|
||||
@@ -245,10 +250,12 @@ export class RemoteFileDialog {
|
||||
if (!isResolving) {
|
||||
resolve(undefined);
|
||||
}
|
||||
this.contextKey.set(false);
|
||||
this.filePickBox.dispose();
|
||||
});
|
||||
|
||||
this.filePickBox.show();
|
||||
this.contextKey.set(true);
|
||||
this.updateItems(homedir, trailing);
|
||||
if (trailing) {
|
||||
this.filePickBox.valueSelection = [this.filePickBox.value.length - trailing.length, this.filePickBox.value.length - ext.length];
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, IDisposable, toDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileService, IResolveFileOptions, IResourceEncodings, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, IResolveContentOptions, IContent, IStreamContent, ITextSnapshot, IUpdateContentOptions, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability } from 'vs/platform/files/common/files';
|
||||
import { IFileService, IResolveFileOptions, IResourceEncodings, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, IResolveContentOptions, IContent, IStreamContent, ITextSnapshot, IUpdateContentOptions, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
|
||||
@@ -252,7 +252,7 @@ export class FileService2 extends Disposable implements IFileService {
|
||||
}
|
||||
|
||||
async resolveFiles(toResolve: { resource: URI, options?: IResolveFileOptions }[]): Promise<IResolveFileResult[]>;
|
||||
async resolveFiles(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResult[]>;
|
||||
async resolveFiles(toResolve: { resource: URI, options: IResolveMetadataFileOptions }[]): Promise<IResolveFileResultWithMetadata[]>;
|
||||
async resolveFiles(toResolve: { resource: URI; options?: IResolveFileOptions; }[]): Promise<IResolveFileResult[]> {
|
||||
return Promise.all(toResolve.map(async entry => {
|
||||
try {
|
||||
@@ -600,14 +600,17 @@ export class FileService2 extends Disposable implements IFileService {
|
||||
|
||||
private async doWriteBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, buffer: Uint8Array): Promise<void> {
|
||||
|
||||
// Open handle
|
||||
// open handle
|
||||
const handle = await provider.open(resource, { create: true });
|
||||
|
||||
// write into handle until all bytes from buffer have been written
|
||||
await this.doWriteBuffer(provider, handle, buffer, buffer.byteLength, 0, 0);
|
||||
|
||||
// Close handle
|
||||
return provider.close(handle);
|
||||
try {
|
||||
await this.doWriteBuffer(provider, handle, buffer, buffer.byteLength, 0, 0);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
await provider.close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
private async doWriteBuffer(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, handle: number, buffer: Uint8Array, length: number, posInFile: number, posInBuffer: number): Promise<void> {
|
||||
@@ -623,39 +626,45 @@ export class FileService2 extends Disposable implements IFileService {
|
||||
}
|
||||
|
||||
private async doPipeBuffered(sourceProvider: IFileSystemProviderWithOpenReadWriteCloseCapability, source: URI, targetProvider: IFileSystemProviderWithOpenReadWriteCloseCapability, target: URI): Promise<void> {
|
||||
let sourceHandle: number | undefined = undefined;
|
||||
let targetHandle: number | undefined = undefined;
|
||||
|
||||
// Open handles
|
||||
const sourceHandle = await sourceProvider.open(source, { create: false });
|
||||
const targetHandle = await targetProvider.open(target, { create: true });
|
||||
try {
|
||||
|
||||
const buffer = new Uint8Array(8 * 1024);
|
||||
// Open handles
|
||||
sourceHandle = await sourceProvider.open(source, { create: false });
|
||||
targetHandle = await targetProvider.open(target, { create: true });
|
||||
|
||||
let posInFile = 0;
|
||||
let posInBuffer = 0;
|
||||
let bytesRead = 0;
|
||||
do {
|
||||
// read from source (sourceHandle) at current position (posInFile) into buffer (buffer) at
|
||||
// buffer position (posInBuffer) up to the size of the buffer (buffer.byteLength).
|
||||
bytesRead = await sourceProvider.read(sourceHandle, posInFile, buffer, posInBuffer, buffer.byteLength - posInBuffer);
|
||||
const buffer = new Uint8Array(16 * 1024);
|
||||
|
||||
// write into target (targetHandle) at current position (posInFile) from buffer (buffer) at
|
||||
// buffer position (posInBuffer) all bytes we read (bytesRead).
|
||||
await this.doWriteBuffer(targetProvider, targetHandle, buffer, bytesRead, posInFile, posInBuffer);
|
||||
let posInFile = 0;
|
||||
let posInBuffer = 0;
|
||||
let bytesRead = 0;
|
||||
do {
|
||||
// read from source (sourceHandle) at current position (posInFile) into buffer (buffer) at
|
||||
// buffer position (posInBuffer) up to the size of the buffer (buffer.byteLength).
|
||||
bytesRead = await sourceProvider.read(sourceHandle, posInFile, buffer, posInBuffer, buffer.byteLength - posInBuffer);
|
||||
|
||||
posInFile += bytesRead;
|
||||
posInBuffer += bytesRead;
|
||||
// write into target (targetHandle) at current position (posInFile) from buffer (buffer) at
|
||||
// buffer position (posInBuffer) all bytes we read (bytesRead).
|
||||
await this.doWriteBuffer(targetProvider, targetHandle, buffer, bytesRead, posInFile, posInBuffer);
|
||||
|
||||
// when buffer full, fill it again from the beginning
|
||||
if (posInBuffer === buffer.length) {
|
||||
posInBuffer = 0;
|
||||
}
|
||||
} while (bytesRead > 0);
|
||||
posInFile += bytesRead;
|
||||
posInBuffer += bytesRead;
|
||||
|
||||
// Close handles
|
||||
return Promise.all([
|
||||
sourceProvider.close(sourceHandle),
|
||||
targetProvider.close(targetHandle)
|
||||
]).then(() => undefined);
|
||||
// when buffer full, fill it again from the beginning
|
||||
if (posInBuffer === buffer.length) {
|
||||
posInBuffer = 0;
|
||||
}
|
||||
} while (bytesRead > 0);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
await Promise.all([
|
||||
typeof sourceHandle === 'number' ? sourceProvider.close(sourceHandle) : Promise.resolve(),
|
||||
typeof targetHandle === 'number' ? targetProvider.close(targetHandle) : Promise.resolve(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
private async doPipeUnbuffered(sourceProvider: IFileSystemProviderWithFileReadWriteCapability, source: URI, targetProvider: IFileSystemProviderWithFileReadWriteCapability, target: URI, overwrite: boolean): Promise<void> {
|
||||
@@ -668,11 +677,14 @@ export class FileService2 extends Disposable implements IFileService {
|
||||
const targetHandle = await targetProvider.open(target, { create: true });
|
||||
|
||||
// Read entire buffer from source and write buffered
|
||||
const buffer = await sourceProvider.readFile(source);
|
||||
await this.doWriteBuffer(targetProvider, targetHandle, buffer, buffer.byteLength, 0, 0);
|
||||
|
||||
// Close handle
|
||||
return targetProvider.close(targetHandle);
|
||||
try {
|
||||
const buffer = await sourceProvider.readFile(source);
|
||||
await this.doWriteBuffer(targetProvider, targetHandle, buffer, buffer.byteLength, 0, 0);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
await targetProvider.close(targetHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private async doPipeBufferedToUnbuffered(sourceProvider: IFileSystemProviderWithOpenReadWriteCloseCapability, source: URI, targetProvider: IFileSystemProviderWithFileReadWriteCapability, target: URI, overwrite: boolean): Promise<void> {
|
||||
@@ -683,23 +695,26 @@ export class FileService2 extends Disposable implements IFileService {
|
||||
// Open handle
|
||||
const sourceHandle = await sourceProvider.open(source, { create: false });
|
||||
|
||||
const buffer = new Uint8Array(size);
|
||||
try {
|
||||
const buffer = new Uint8Array(size);
|
||||
|
||||
let pos = 0;
|
||||
let bytesRead = 0;
|
||||
do {
|
||||
// read from source (sourceHandle) at current position (posInFile) into buffer (buffer) at
|
||||
// buffer position (posInBuffer) up to the size of the buffer (buffer.byteLength).
|
||||
bytesRead = await sourceProvider.read(sourceHandle, pos, buffer, pos, buffer.byteLength - pos);
|
||||
let pos = 0;
|
||||
let bytesRead = 0;
|
||||
do {
|
||||
// read from source (sourceHandle) at current position (posInFile) into buffer (buffer) at
|
||||
// buffer position (posInBuffer) up to the size of the buffer (buffer.byteLength).
|
||||
bytesRead = await sourceProvider.read(sourceHandle, pos, buffer, pos, buffer.byteLength - pos);
|
||||
|
||||
pos += bytesRead;
|
||||
} while (bytesRead > 0);
|
||||
pos += bytesRead;
|
||||
} while (bytesRead > 0 && pos < size);
|
||||
|
||||
// Write buffer into target at once
|
||||
await this.doWriteUnbuffered(targetProvider, target, buffer, overwrite);
|
||||
|
||||
// Close handle
|
||||
return sourceProvider.close(sourceHandle);
|
||||
// Write buffer into target at once
|
||||
await this.doWriteUnbuffered(targetProvider, target, buffer, overwrite);
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
await sourceProvider.close(sourceHandle);
|
||||
}
|
||||
}
|
||||
|
||||
private throwIfFileSystemIsReadonly(provider: IFileSystemProvider): IFileSystemProvider {
|
||||
|
||||
@@ -9,9 +9,14 @@ import { FileDeleteOptions, FileSystemProviderCapabilities } from 'vs/platform/f
|
||||
import { isWindows } from 'vs/base/common/platform';
|
||||
import { localize } from 'vs/nls';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class DiskFileSystemProvider extends NodeDiskFileSystemProvider {
|
||||
|
||||
constructor(logService: ILogService) {
|
||||
super(logService);
|
||||
}
|
||||
|
||||
get capabilities(): FileSystemProviderCapabilities {
|
||||
if (!this._capabilities) {
|
||||
this._capabilities = super.capabilities | FileSystemProviderCapabilities.Trash;
|
||||
|
||||
@@ -16,9 +16,14 @@ import { normalize } from 'vs/base/common/path';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { isEqual } from 'vs/base/common/extpath';
|
||||
import { retry } from 'vs/base/common/async';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class DiskFileSystemProvider extends Disposable implements IFileSystemProvider {
|
||||
|
||||
constructor(private logService: ILogService) {
|
||||
super();
|
||||
}
|
||||
|
||||
//#region File Capabilities
|
||||
|
||||
onDidChangeCapabilities: Event<void> = Event.None;
|
||||
@@ -73,8 +78,12 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
const child = children[i];
|
||||
|
||||
const stat = await this.stat(joinPath(resource, child));
|
||||
result.push([child, stat.type]);
|
||||
try {
|
||||
const stat = await this.stat(joinPath(resource, child));
|
||||
result.push([child, stat.type]);
|
||||
} catch (error) {
|
||||
this.logService.trace(error); // ignore errors for individual entries that can arise from permission denied
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -112,7 +121,7 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
if (exists && isWindows) {
|
||||
try {
|
||||
// On Windows and if the file exists, we use a different strategy of saving the file
|
||||
// by first truncating the file and then writing with r+ mode. This helps to save hidden files on Windows
|
||||
// by first truncating the file and then writing with r+ flag. This helps to save hidden files on Windows
|
||||
// (see https://github.com/Microsoft/vscode/issues/931) and prevent removing alternate data streams
|
||||
// (see https://github.com/Microsoft/vscode/issues/6363)
|
||||
await truncate(filePath, 0);
|
||||
@@ -123,6 +132,8 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
// short timeout, assuming that the file is free to write then.
|
||||
await retry(() => writeFile(filePath, content, { flag: 'r+' }), 100 /* ms delay */, 3 /* retries */);
|
||||
} catch (error) {
|
||||
this.logService.trace(error);
|
||||
|
||||
// we heard from users that fs.truncate() fails (https://github.com/Microsoft/vscode/issues/59561)
|
||||
// in that case we simply save the file without truncating first (same as macOS and Linux)
|
||||
await writeFile(filePath, content);
|
||||
@@ -142,20 +153,20 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
try {
|
||||
const filePath = this.toFilePath(resource);
|
||||
|
||||
let mode: string;
|
||||
let flags: string;
|
||||
if (opts.create) {
|
||||
// we take this as a hint that the file is opened for writing
|
||||
// as such we use 'w' to truncate an existing or create the
|
||||
// file otherwise. we do not allow reading.
|
||||
mode = 'w';
|
||||
flags = 'w';
|
||||
} else {
|
||||
// otherwise we assume the file is opened for reading
|
||||
// as such we use 'r' to neither truncate, nor create
|
||||
// the file.
|
||||
mode = 'r';
|
||||
flags = 'r';
|
||||
}
|
||||
|
||||
return await promisify(open)(filePath, mode);
|
||||
return await promisify(open)(filePath, flags);
|
||||
} catch (error) {
|
||||
throw this.toFileSystemProviderError(error);
|
||||
}
|
||||
@@ -300,7 +311,7 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
return error; // avoid double conversion
|
||||
}
|
||||
|
||||
let code: FileSystemProviderErrorCode | undefined = undefined;
|
||||
let code: FileSystemProviderErrorCode;
|
||||
switch (error.code) {
|
||||
case 'ENOENT':
|
||||
code = FileSystemProviderErrorCode.FileNotFound;
|
||||
@@ -315,6 +326,8 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
case 'EACCESS':
|
||||
code = FileSystemProviderErrorCode.NoPermissions;
|
||||
break;
|
||||
default:
|
||||
code = FileSystemProviderErrorCode.Unknown;
|
||||
}
|
||||
|
||||
return createFileSystemProviderError(error, code);
|
||||
|
||||
@@ -17,8 +17,10 @@ import { URI } from 'vs/base/common/uri';
|
||||
import { existsSync, statSync, readdirSync, readFileSync } from 'fs';
|
||||
import { FileOperation, FileOperationEvent, IFileStat, FileOperationResult, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
|
||||
import { NullLogService } from 'vs/platform/log/common/log';
|
||||
import { isLinux } from 'vs/base/common/platform';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { promisify } from 'util';
|
||||
import { exec } from 'child_process';
|
||||
|
||||
function getByName(root: IFileStat, name: string): IFileStat | null {
|
||||
if (root.children === undefined) {
|
||||
@@ -70,13 +72,15 @@ suite('Disk File Service', () => {
|
||||
let disposables: IDisposable[] = [];
|
||||
|
||||
setup(async () => {
|
||||
service = new FileService2(new NullLogService());
|
||||
const logService = new NullLogService();
|
||||
|
||||
service = new FileService2(logService);
|
||||
disposables.push(service);
|
||||
|
||||
fileProvider = new TestDiskFileSystemProvider();
|
||||
fileProvider = new TestDiskFileSystemProvider(logService);
|
||||
service.registerProvider(Schemas.file, fileProvider);
|
||||
|
||||
testProvider = new TestDiskFileSystemProvider();
|
||||
testProvider = new TestDiskFileSystemProvider(logService);
|
||||
service.registerProvider(testSchema, testProvider);
|
||||
|
||||
const id = generateUuid();
|
||||
@@ -307,6 +311,45 @@ suite('Disk File Service', () => {
|
||||
assert.equal(r2.name, 'deep');
|
||||
});
|
||||
|
||||
test('resolveFile - folder symbolic link', async () => {
|
||||
if (isWindows) {
|
||||
return; // only for unix systems
|
||||
}
|
||||
|
||||
const link = URI.file(join(testDir, 'deep-link'));
|
||||
await promisify(exec)(`ln -s deep ${basename(link.fsPath)}`, { cwd: testDir });
|
||||
|
||||
const resolved = await service.resolveFile(link);
|
||||
assert.equal(resolved.children!.length, 4);
|
||||
assert.equal(resolved.isDirectory, true);
|
||||
assert.equal(resolved.isSymbolicLink, true);
|
||||
});
|
||||
|
||||
test('resolveFile - file symbolic link', async () => {
|
||||
if (isWindows) {
|
||||
return; // only for unix systems
|
||||
}
|
||||
|
||||
const link = URI.file(join(testDir, 'lorem.txt-linked'));
|
||||
await promisify(exec)(`ln -s lorem.txt ${basename(link.fsPath)}`, { cwd: testDir });
|
||||
|
||||
const resolved = await service.resolveFile(link);
|
||||
assert.equal(resolved.isDirectory, false);
|
||||
assert.equal(resolved.isSymbolicLink, true);
|
||||
});
|
||||
|
||||
test('resolveFile - invalid symbolic link does not break', async () => {
|
||||
if (isWindows) {
|
||||
return; // only for unix systems
|
||||
}
|
||||
|
||||
await promisify(exec)('ln -s foo bar', { cwd: testDir });
|
||||
|
||||
const resolved = await service.resolveFile(URI.file(testDir));
|
||||
assert.equal(resolved.isDirectory, true);
|
||||
assert.equal(resolved.children!.length, 8);
|
||||
});
|
||||
|
||||
test('deleteFile', async () => {
|
||||
let event: FileOperationEvent;
|
||||
disposables.push(service.onAfterOperation(e => event = e));
|
||||
|
||||
@@ -21,7 +21,7 @@ import { BackupFileService } from 'vs/workbench/services/backup/node/backupFileS
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { distinct } from 'vs/base/common/arrays';
|
||||
import { isLinux, isWindows, isMacintosh } from 'vs/base/common/platform';
|
||||
import { isEqual, basename, isEqualOrParent } from 'vs/base/common/resources';
|
||||
import { isEqual, basename, isEqualOrParent, getComparisonKey } from 'vs/base/common/resources';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
@@ -205,7 +205,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
if (state !== WorkbenchState.WORKSPACE) {
|
||||
let newWorkspaceFolders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri } as IWorkspaceFolderCreationData));
|
||||
newWorkspaceFolders.splice(typeof index === 'number' ? index : newWorkspaceFolders.length, 0, ...foldersToAdd);
|
||||
newWorkspaceFolders = distinct(newWorkspaceFolders, folder => isLinux ? folder.uri.toString() : folder.uri.toString().toLowerCase());
|
||||
newWorkspaceFolders = distinct(newWorkspaceFolders, folder => getComparisonKey(folder.uri));
|
||||
|
||||
if (state === WorkbenchState.EMPTY && newWorkspaceFolders.length === 0 || state === WorkbenchState.FOLDER && newWorkspaceFolders.length === 1) {
|
||||
return Promise.resolve(); // return if the operation is a no-op for the current state
|
||||
@@ -245,8 +245,8 @@ export class WorkspaceEditingService implements IWorkspaceEditingService {
|
||||
if (path && !this.isValidTargetWorkspacePath(path)) {
|
||||
return Promise.reject(null);
|
||||
}
|
||||
|
||||
const untitledWorkspace = await this.workspaceService.createUntitledWorkspace(folders);
|
||||
const remoteAuthority = this.windowService.getConfiguration().remoteAuthority;
|
||||
const untitledWorkspace = await this.workspaceService.createUntitledWorkspace(folders, remoteAuthority);
|
||||
if (path) {
|
||||
await this.saveWorkspaceAs(untitledWorkspace, path);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user