mirror of
https://github.com/Microsoft/vscode
synced 2024-10-12 22:37:41 +00:00
Merge remote-tracking branch 'origin/master' into joao/explorer-compressed-tree
This commit is contained in:
commit
8667883738
11
.github/commands.yml
vendored
11
.github/commands.yml
vendored
|
@ -118,10 +118,10 @@
|
|||
},
|
||||
{
|
||||
type: 'label',
|
||||
name: '*needs more info',
|
||||
name: '~needs more info',
|
||||
action: 'updateLabels',
|
||||
addLabel: 'needs more info',
|
||||
removeLabel: '*needs more info',
|
||||
removeLabel: '~needs more info',
|
||||
comment: "Thanks for creating this issue! We figured it's missing some basic information or in some other way doesn't follow our [issue reporting](https://aka.ms/vscodeissuereporting) guidelines. Please take the time to review these and update the issue.\n\nHappy Coding!"
|
||||
},
|
||||
{
|
||||
|
@ -131,12 +131,5 @@
|
|||
action: 'updateLabels',
|
||||
addLabel: 'a11ymas'
|
||||
},
|
||||
{
|
||||
type: 'label',
|
||||
name: '*needs more info',
|
||||
action: 'updateLabels',
|
||||
addLabel: 'needs more info',
|
||||
removeLabel: '*needs more info'
|
||||
},
|
||||
]
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
[
|
||||
{
|
||||
"name": "ms-vscode.node-debug",
|
||||
"version": "1.40.1",
|
||||
"version": "1.41.0",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug",
|
||||
"metadata": {
|
||||
"id": "b6ded8fb-a0a0-4c1c-acbd-ab2a3bc995a6",
|
||||
|
@ -16,7 +16,7 @@
|
|||
},
|
||||
{
|
||||
"name": "ms-vscode.node-debug2",
|
||||
"version": "1.39.3",
|
||||
"version": "1.41.0",
|
||||
"repo": "https://github.com/Microsoft/vscode-node-debug2",
|
||||
"metadata": {
|
||||
"id": "36d19e17-7569-4841-a001-947eb18602b2",
|
||||
|
@ -31,7 +31,7 @@
|
|||
},
|
||||
{
|
||||
"name": "ms-vscode.references-view",
|
||||
"version": "0.0.35",
|
||||
"version": "0.0.36",
|
||||
"repo": "https://github.com/Microsoft/vscode-reference-view",
|
||||
"metadata": {
|
||||
"id": "dc489f46-520d-4556-ae85-1f9eab3c412d",
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
"config.npm.exclude": "Configure glob patterns for folders that should be excluded from automatic script detection.",
|
||||
"config.npm.enableScriptExplorer": "Enable an explorer view for npm scripts when there is no top-level 'package.json' file.",
|
||||
"config.npm.scriptExplorerAction": "The default click action used in the npm scripts explorer: `open` or `run`, the default is `open`.",
|
||||
"config.npm.enableRunFromFolder": "Enable running NPM scripts contained in a folder from the Explorer context menu.",
|
||||
"config.npm.enableRunFromFolder": "Enable running npm scripts contained in a folder from the Explorer context menu.",
|
||||
"config.npm.fetchOnlinePackageInfo": "Fetch data from https://registry.npmjs.org and https://registry.bower.io to provide auto-completion and information on hover features on npm dependencies.",
|
||||
"npm.parseError": "Npm task detection: failed to parse the file {0}",
|
||||
"taskdef.script": "The npm script to customize.",
|
||||
|
|
|
@ -82,7 +82,8 @@ namespace ServerState {
|
|||
export default class TypeScriptServiceClient extends Disposable implements ITypeScriptServiceClient {
|
||||
private static readonly WALK_THROUGH_SNIPPET_SCHEME_COLON = `${fileSchemes.walkThroughSnippet}:`;
|
||||
|
||||
private pathSeparator: string;
|
||||
private readonly pathSeparator: string;
|
||||
private readonly inMemoryResourcePrefix = '^';
|
||||
|
||||
private _onReady?: { promise: Promise<void>; resolve: () => void; reject: () => void; };
|
||||
private _configuration: TypeScriptServiceConfiguration;
|
||||
|
@ -591,23 +592,18 @@ export default class TypeScriptServiceClient extends Disposable implements IType
|
|||
return this.toPath(document.uri) || undefined;
|
||||
}
|
||||
|
||||
private get inMemoryResourcePrefix(): string {
|
||||
return this.apiVersion.gte(API.v270) ? '^' : '';
|
||||
}
|
||||
|
||||
public toResource(filepath: string): vscode.Uri {
|
||||
if (filepath.startsWith(TypeScriptServiceClient.WALK_THROUGH_SNIPPET_SCHEME_COLON) || (filepath.startsWith(fileSchemes.untitled + ':'))
|
||||
) {
|
||||
let resource = vscode.Uri.parse(filepath);
|
||||
if (this.inMemoryResourcePrefix) {
|
||||
const dirName = path.dirname(resource.path);
|
||||
const fileName = path.basename(resource.path);
|
||||
if (fileName.startsWith(this.inMemoryResourcePrefix)) {
|
||||
resource = resource.with({
|
||||
path: path.posix.join(dirName, fileName.slice(this.inMemoryResourcePrefix.length))
|
||||
});
|
||||
}
|
||||
const dirName = path.dirname(resource.path);
|
||||
const fileName = path.basename(resource.path);
|
||||
if (fileName.startsWith(this.inMemoryResourcePrefix)) {
|
||||
resource = resource.with({
|
||||
path: path.posix.join(dirName, fileName.slice(this.inMemoryResourcePrefix.length))
|
||||
});
|
||||
}
|
||||
|
||||
return this.bufferSyncSupport.toVsCodeResource(resource);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "code-oss-dev",
|
||||
"version": "1.41.0",
|
||||
"distro": "be6ad88ea0214dfdb03b943bef28e7a5c9fc2e4b",
|
||||
"distro": "403ab44be562c63a0cde1969fd8f5b45ff51709c",
|
||||
"author": {
|
||||
"name": "Microsoft Corporation"
|
||||
},
|
||||
|
@ -42,10 +42,10 @@
|
|||
"native-keymap": "2.0.0",
|
||||
"native-watchdog": "1.2.0",
|
||||
"node-pty": "^0.10.0-beta2",
|
||||
"onigasm-umd": "^2.2.2",
|
||||
"onigasm-umd": "^2.2.4",
|
||||
"semver-umd": "^5.5.3",
|
||||
"spdlog": "^0.11.1",
|
||||
"sudo-prompt": "9.0.0",
|
||||
"sudo-prompt": "9.1.0",
|
||||
"v8-inspect-profiler": "^0.0.20",
|
||||
"vscode-minimist": "^1.2.1",
|
||||
"vscode-nsfw": "1.2.8",
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"jschardet": "2.1.1",
|
||||
"native-watchdog": "1.2.0",
|
||||
"node-pty": "^0.10.0-beta2",
|
||||
"onigasm-umd": "^2.2.2",
|
||||
"onigasm-umd": "^2.2.4",
|
||||
"semver-umd": "^5.5.3",
|
||||
"spdlog": "^0.11.1",
|
||||
"vscode-minimist": "^1.2.1",
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
"name": "vscode-web",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"onigasm-umd": "^2.2.2",
|
||||
"onigasm-umd": "^2.2.4",
|
||||
"semver-umd": "^5.5.3",
|
||||
"vscode-textmate": "^4.3.0",
|
||||
"xterm": "4.3.0-beta17",
|
||||
|
|
|
@ -7,10 +7,10 @@ nan@^2.14.0:
|
|||
resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c"
|
||||
integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg==
|
||||
|
||||
onigasm-umd@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257"
|
||||
integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw==
|
||||
onigasm-umd@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.4.tgz#27ee87f7496c66ad40cebfbc0d418c19bb7db5ec"
|
||||
integrity sha512-N9VqCUhl9KBuzm47vcK8T/xUnbYylIhMN45Rwltlo1sZc3QUDda6SxIlyVB8r0SJQwURv8JOHjyXjjCriGvzRg==
|
||||
|
||||
oniguruma@^7.2.0:
|
||||
version "7.2.0"
|
||||
|
|
|
@ -283,10 +283,10 @@ normalize-path@^3.0.0, normalize-path@~3.0.0:
|
|||
resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
|
||||
integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
|
||||
|
||||
onigasm-umd@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257"
|
||||
integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw==
|
||||
onigasm-umd@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.4.tgz#27ee87f7496c66ad40cebfbc0d418c19bb7db5ec"
|
||||
integrity sha512-N9VqCUhl9KBuzm47vcK8T/xUnbYylIhMN45Rwltlo1sZc3QUDda6SxIlyVB8r0SJQwURv8JOHjyXjjCriGvzRg==
|
||||
|
||||
oniguruma@^7.2.0:
|
||||
version "7.2.0"
|
||||
|
|
|
@ -13,9 +13,8 @@ set NAMESHORT=%NAMESHORT: "=%
|
|||
set NAMESHORT=%NAMESHORT:"=%.exe
|
||||
set CODE=".build\electron\%NAMESHORT%"
|
||||
|
||||
:: Download Electron if needed
|
||||
node build\lib\electron.js
|
||||
if %errorlevel% neq 0 node .\node_modules\gulp\bin\gulp.js electron
|
||||
:: Get electron
|
||||
call yarn electron
|
||||
|
||||
:: Manage built-in extensions
|
||||
if "%1"=="--builtin" goto builtin
|
||||
|
|
9
src/typings/sudo-prompt.d.ts
vendored
9
src/typings/sudo-prompt.d.ts
vendored
|
@ -1,9 +0,0 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
declare module 'sudo-prompt' {
|
||||
|
||||
export function exec(cmd: string, options: { name?: string, icns?: string }, callback: (error: string, stdout: string, stderr: string) => void): void;
|
||||
}
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
@font-face {
|
||||
font-family: "codicon";
|
||||
src: url("./codicon.ttf?3443fb4013ab5a04c23a30f912dd6627") format("truetype");
|
||||
src: url("./codicon.ttf?72bd9e6bbf1e48287bcb9a9e4babeb28") format("truetype");
|
||||
}
|
||||
|
||||
.codicon[class*='codicon-'] {
|
||||
|
@ -385,4 +385,5 @@
|
|||
.codicon-list-filter:before { content: "\eb83" }
|
||||
.codicon-list-flat:before { content: "\eb84" }
|
||||
.codicon-list-selection:before { content: "\eb85" }
|
||||
.codicon-selection:before { content: "\eb85" }
|
||||
.codicon-list-tree:before { content: "\eb86" }
|
||||
|
|
Binary file not shown.
|
@ -267,10 +267,10 @@ export class URI implements UriComponents {
|
|||
}
|
||||
return new _URI(
|
||||
match[2] || _empty,
|
||||
decodeURIComponent(match[4] || _empty),
|
||||
decodeURIComponent(match[5] || _empty),
|
||||
decodeURIComponent(match[7] || _empty),
|
||||
decodeURIComponent(match[9] || _empty),
|
||||
percentDecode(match[4] || _empty),
|
||||
percentDecode(match[5] || _empty),
|
||||
percentDecode(match[7] || _empty),
|
||||
percentDecode(match[9] || _empty),
|
||||
_strict
|
||||
);
|
||||
}
|
||||
|
@ -648,3 +648,26 @@ function _asFormatted(uri: URI, skipEncoding: boolean): string {
|
|||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// --- decode
|
||||
|
||||
function decodeURIComponentGraceful(str: string): string {
|
||||
try {
|
||||
return decodeURIComponent(str);
|
||||
} catch {
|
||||
if (str.length > 3) {
|
||||
return str.substr(0, 3) + decodeURIComponentGraceful(str.substr(3));
|
||||
} else {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const _rEncodedAsHex = /(%[0-9A-Za-z][0-9A-Za-z])+/g;
|
||||
|
||||
function percentDecode(str: string): string {
|
||||
if (!str.match(_rEncodedAsHex)) {
|
||||
return str;
|
||||
}
|
||||
return str.replace(_rEncodedAsHex, (match) => decodeURIComponentGraceful(match));
|
||||
}
|
||||
|
|
|
@ -439,6 +439,10 @@ suite('URI', () => {
|
|||
assert.equal(uri.path, uri2.path);
|
||||
});
|
||||
|
||||
test('Unable to open \'%A0.txt\': URI malformed #76506', function () {
|
||||
assert.equal(URI.parse('file://some/%.txt'), 'file://some/%25.txt');
|
||||
assert.equal(URI.parse('file://some/%A0.txt'), 'file://some/%25A0.txt');
|
||||
});
|
||||
|
||||
test('Links in markdown are broken if url contains encoded parameters #79474', function () {
|
||||
this.skip();
|
||||
|
|
|
@ -16,7 +16,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService';
|
|||
import { MenuId, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IConstructorSignature1, ServicesAccessor as InstantiationServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConstructorSignature1, ServicesAccessor as InstantiationServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
|
@ -303,7 +303,7 @@ export function registerInstantiatedEditorAction(editorAction: EditorAction): vo
|
|||
EditorContributionRegistry.INSTANCE.registerEditorAction(editorAction);
|
||||
}
|
||||
|
||||
export function registerEditorContribution(id: string, ctor: IEditorContributionCtor): void {
|
||||
export function registerEditorContribution<Services extends BrandedService[]>(id: string, ctor: { new(editor: ICodeEditor, ...services: Services): IEditorContribution }): void {
|
||||
EditorContributionRegistry.INSTANCE.registerEditorContribution(id, ctor);
|
||||
}
|
||||
|
||||
|
@ -355,7 +355,7 @@ class EditorContributionRegistry {
|
|||
this.editorCommands = Object.create(null);
|
||||
}
|
||||
|
||||
public registerEditorContribution(id: string, ctor: IEditorContributionCtor): void {
|
||||
public registerEditorContribution<Services extends BrandedService[]>(id: string, ctor: { new(editor: ICodeEditor, ...services: Services): IEditorContribution }): void {
|
||||
this.editorContributions.push({ id, ctor });
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
import 'vs/css!./codelensWidget';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { coalesce, isFalsyOrEmpty } from 'vs/base/common/arrays';
|
||||
import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel';
|
||||
import * as editorBrowser from 'vs/editor/browser/editorBrowser';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
|
@ -66,7 +65,7 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
|||
constructor(
|
||||
editor: editorBrowser.ICodeEditor,
|
||||
symbolRange: Range,
|
||||
data: CodeLensItem[]
|
||||
lenses: Array<CodeLens | undefined | null>
|
||||
) {
|
||||
this._id = 'codeLensWidget' + (++CodeLensContentWidget._idPool);
|
||||
this._editor = editor;
|
||||
|
@ -74,10 +73,9 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
|||
this.setSymbolRange(symbolRange);
|
||||
|
||||
this._domNode = document.createElement('span');
|
||||
this._domNode.innerHTML = ' ';
|
||||
dom.addClass(this._domNode, 'codelens-decoration');
|
||||
this._domNode.className = 'codelens-decoration';
|
||||
this.updateHeight();
|
||||
this.withCommands(data.map(data => data.symbol), false);
|
||||
this.withCommands(lenses, false);
|
||||
}
|
||||
|
||||
updateHeight(): void {
|
||||
|
@ -88,39 +86,45 @@ class CodeLensContentWidget implements editorBrowser.IContentWidget {
|
|||
this._domNode.style.lineHeight = `${lineHeight}px`;
|
||||
this._domNode.style.fontSize = `${Math.round(fontInfo.fontSize * 0.9)}px`;
|
||||
this._domNode.style.paddingRight = `${Math.round(fontInfo.fontSize * 0.45)}px`;
|
||||
this._domNode.innerHTML = ' ';
|
||||
}
|
||||
|
||||
withCommands(inSymbols: Array<CodeLens | undefined | null>, animate: boolean): void {
|
||||
withCommands(lenses: Array<CodeLens | undefined | null>, animate: boolean): void {
|
||||
this._commands.clear();
|
||||
|
||||
const symbols = coalesce(inSymbols);
|
||||
if (isFalsyOrEmpty(symbols)) {
|
||||
this._domNode.innerHTML = '<span>no commands</span>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html: string[] = [];
|
||||
for (let i = 0; i < symbols.length; i++) {
|
||||
const command = symbols[i].command;
|
||||
if (command) {
|
||||
const title = renderCodicons(command.title);
|
||||
let part: string;
|
||||
if (command.id) {
|
||||
part = `<a id=${i}>${title}</a>`;
|
||||
this._commands.set(String(i), command);
|
||||
let innerHtml = '';
|
||||
let hasSymbol = false;
|
||||
for (let i = 0; i < lenses.length; i++) {
|
||||
const lens = lenses[i];
|
||||
if (!lens) {
|
||||
continue;
|
||||
}
|
||||
hasSymbol = true;
|
||||
if (lens.command) {
|
||||
const title = renderCodicons(lens.command.title);
|
||||
if (lens.command.id) {
|
||||
innerHtml += `<a id=${i}>${title}</a>`;
|
||||
this._commands.set(String(i), lens.command);
|
||||
} else {
|
||||
part = `<span>${title}</span>`;
|
||||
innerHtml += `<span>${title}</span>`;
|
||||
}
|
||||
if (i + 1 < lenses.length) {
|
||||
innerHtml += '<span> | </span>';
|
||||
}
|
||||
html.push(part);
|
||||
}
|
||||
}
|
||||
|
||||
const wasEmpty = this._domNode.innerHTML === '' || this._domNode.innerHTML === ' ';
|
||||
this._domNode.innerHTML = html.join('<span> | </span>');
|
||||
this._editor.layoutContentWidget(this);
|
||||
if (wasEmpty && animate) {
|
||||
dom.addClass(this._domNode, 'fadein');
|
||||
if (!hasSymbol) {
|
||||
// symbols but no commands
|
||||
this._domNode.innerHTML = '<span>no commands</span>';
|
||||
|
||||
} else {
|
||||
// symbols and commands
|
||||
const wasEmpty = this._domNode.innerHTML === '' || this._domNode.innerHTML === ' ';
|
||||
this._domNode.innerHTML = innerHtml || ' ';
|
||||
this._editor.layoutContentWidget(this);
|
||||
if (wasEmpty && animate) {
|
||||
dom.addClass(this._domNode, 'fadein');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,8 +217,11 @@ export class CodeLensWidget {
|
|||
this._decorationIds = new Array<string>(this._data.length);
|
||||
|
||||
let range: Range | undefined;
|
||||
let lenses: CodeLens[] = [];
|
||||
this._data.forEach((codeLensData, i) => {
|
||||
|
||||
lenses.push(codeLensData.symbol);
|
||||
|
||||
helper.addDecoration({
|
||||
range: codeLensData.symbol.range,
|
||||
options: ModelDecorationOptions.EMPTY
|
||||
|
@ -229,7 +236,7 @@ export class CodeLensWidget {
|
|||
});
|
||||
|
||||
if (range) {
|
||||
this._contentWidget = new CodeLensContentWidget(editor, range, this._data);
|
||||
this._contentWidget = new CodeLensContentWidget(editor, range, lenses);
|
||||
this._viewZone = new CodeLensViewZone(range.startLineNumber - 1, updateCallback);
|
||||
|
||||
this._viewZoneId = viewZoneChangeAccessor.addZone(this._viewZone);
|
||||
|
|
|
@ -13,12 +13,12 @@
|
|||
.monaco-editor .parameter-hints-widget > .wrapper {
|
||||
max-width: 440px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget.multiple {
|
||||
min-height: 3.3em;
|
||||
padding: 0 0 0 1.9em;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget.visible {
|
||||
|
@ -62,20 +62,19 @@
|
|||
padding: 0 0.4em;
|
||||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget .buttons {
|
||||
position: absolute;
|
||||
.monaco-editor .parameter-hints-widget .controls {
|
||||
display: none;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 22px;
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget.multiple .buttons {
|
||||
display: block;
|
||||
.monaco-editor .parameter-hints-widget.multiple .controls {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget.multiple .button {
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
background-repeat: no-repeat;
|
||||
|
@ -88,26 +87,16 @@
|
|||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget .button.next {
|
||||
bottom: 0;
|
||||
background-image: url('arrow-down.svg');
|
||||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget .overloads {
|
||||
position: absolute;
|
||||
display: none;
|
||||
text-align: center;
|
||||
bottom: 14px;
|
||||
left: 0;
|
||||
width: 22px;
|
||||
height: 12px;
|
||||
line-height: 12px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget.multiple .overloads {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.monaco-editor .parameter-hints-widget .signature .parameter.active {
|
||||
font-weight: bold;
|
||||
text-decoration: underline;
|
||||
|
|
|
@ -22,6 +22,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener';
|
|||
import { editorHoverBackground, editorHoverBorder, textCodeBlockBackground, textLinkForeground, editorHoverForeground } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { HIGH_CONTRAST, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
|
||||
import { ParameterHintsModel, TriggerContext } from 'vs/editor/contrib/parameterHints/parameterHintsModel';
|
||||
import { pad } from 'vs/base/common/strings';
|
||||
|
||||
const $ = dom.$;
|
||||
|
||||
|
@ -76,9 +77,10 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
|
|||
const wrapper = dom.append(element, $('.wrapper'));
|
||||
wrapper.tabIndex = -1;
|
||||
|
||||
const buttons = dom.append(wrapper, $('.buttons'));
|
||||
const previous = dom.append(buttons, $('.button.previous'));
|
||||
const next = dom.append(buttons, $('.button.next'));
|
||||
const controls = dom.append(wrapper, $('.controls'));
|
||||
const previous = dom.append(controls, $('.button.previous'));
|
||||
const overloads = dom.append(controls, $('.overloads'));
|
||||
const next = dom.append(controls, $('.button.next'));
|
||||
|
||||
const onPreviousClick = stop(domEvent(previous, 'click'));
|
||||
this._register(onPreviousClick(this.previous, this));
|
||||
|
@ -86,8 +88,6 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
|
|||
const onNextClick = stop(domEvent(next, 'click'));
|
||||
this._register(onNextClick(this.next, this));
|
||||
|
||||
const overloads = dom.append(wrapper, $('.overloads'));
|
||||
|
||||
const body = $('.body');
|
||||
const scrollbar = new DomScrollableElement(body, {});
|
||||
this._register(scrollbar);
|
||||
|
@ -239,12 +239,8 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget {
|
|||
dom.toggleClass(this.domNodes.signature, 'has-docs', hasDocs);
|
||||
dom.toggleClass(this.domNodes.docs, 'empty', !hasDocs);
|
||||
|
||||
let currentOverload = String(hints.activeSignature + 1);
|
||||
if (hints.signatures.length < 10) {
|
||||
currentOverload += `/${hints.signatures.length}`;
|
||||
}
|
||||
|
||||
this.domNodes.overloads.textContent = currentOverload;
|
||||
this.domNodes.overloads.textContent =
|
||||
pad(hints.activeSignature + 1, hints.signatures.length.toString().length) + '/' + hints.signatures.length;
|
||||
|
||||
if (activeParameter) {
|
||||
const labelToAnnounce = this.getParameterLabel(signature, hints.activeParameter);
|
||||
|
|
|
@ -141,7 +141,6 @@ export interface IOpenDialogOptions {
|
|||
availableFileSystems?: readonly string[];
|
||||
}
|
||||
|
||||
|
||||
export const IDialogService = createDecorator<IDialogService>('dialogService');
|
||||
|
||||
export interface IDialogOptions {
|
||||
|
@ -240,12 +239,23 @@ export interface IFileDialogService {
|
|||
*/
|
||||
showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined>;
|
||||
|
||||
/**
|
||||
* Shows a confirm dialog for saving 1-N files.
|
||||
*/
|
||||
showSaveConfirm(fileNameOrResources: string | URI[]): Promise<ConfirmResult>;
|
||||
|
||||
/**
|
||||
* Shows a open file dialog and returns the chosen file URI.
|
||||
*/
|
||||
showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined>;
|
||||
}
|
||||
|
||||
export const enum ConfirmResult {
|
||||
SAVE,
|
||||
DONT_SAVE,
|
||||
CANCEL
|
||||
}
|
||||
|
||||
const MAX_CONFIRM_FILES = 10;
|
||||
export function getConfirmMessage(start: string, resourcesToConfirm: readonly URI[]): string {
|
||||
const message = [start];
|
||||
|
|
|
@ -85,6 +85,7 @@ export interface ParsedArgs {
|
|||
'js-flags'?: string;
|
||||
'disable-gpu'?: boolean;
|
||||
'nolazy'?: boolean;
|
||||
'force-device-scale-factor'?: string;
|
||||
}
|
||||
|
||||
export const IEnvironmentService = createDecorator<IEnvironmentService>('environmentService');
|
||||
|
|
|
@ -117,6 +117,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
|
|||
'inspect': { type: 'string' },
|
||||
'inspect-brk': { type: 'string' },
|
||||
'nolazy': { type: 'boolean' }, // node inspect
|
||||
'force-device-scale-factor': { type: 'string' },
|
||||
'_urls': { type: 'string[]' },
|
||||
|
||||
_: { type: 'string[]' } // main arguments
|
||||
|
|
|
@ -362,7 +362,7 @@ export class FileService extends Disposable implements IFileService {
|
|||
// mtime and etag, we bail out to prevent dirty writing.
|
||||
//
|
||||
// First, we check for a mtime that is in the future before we do more checks. The assumption is
|
||||
// that only the mtime is an indicator for a file that has changd on disk.
|
||||
// that only the mtime is an indicator for a file that has changed on disk.
|
||||
//
|
||||
// Second, if the mtime has advanced, we compare the size of the file on disk with our previous
|
||||
// one using the etag() function. Relying only on the mtime check has prooven to produce false
|
||||
|
|
|
@ -134,7 +134,7 @@ suite('Disk File Service', function () {
|
|||
// we see random test failures when accessing the native file system. To
|
||||
// diagnose further, we retry node.js file access tests up to 3 times to
|
||||
// rule out any random disk issue.
|
||||
// this.retries(3);
|
||||
this.retries(3);
|
||||
|
||||
setup(async () => {
|
||||
const logService = new NullLogService();
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SyncDescriptor } from './descriptors';
|
||||
import { ServiceIdentifier, IConstructorSignature0 } from './instantiation';
|
||||
import { ServiceIdentifier, BrandedService } from './instantiation';
|
||||
|
||||
const _registry: [ServiceIdentifier<any>, SyncDescriptor<any>][] = [];
|
||||
|
||||
export function registerSingleton<T>(id: ServiceIdentifier<T>, ctor: IConstructorSignature0<T>, supportsDelayedInstantiation?: boolean): void {
|
||||
export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctor: { new(...services: Services): T }, supportsDelayedInstantiation?: boolean): void {
|
||||
_registry.push([id, new SyncDescriptor<T>(ctor, [], supportsDelayedInstantiation)]);
|
||||
}
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ export namespace _util {
|
|||
|
||||
// --- interfaces ------
|
||||
|
||||
type BrandedService = { _serviceBrand: undefined };
|
||||
export type BrandedService = { _serviceBrand: undefined };
|
||||
|
||||
export interface IConstructorSignature0<T> {
|
||||
new(...services: BrandedService[]): T;
|
||||
|
@ -101,7 +101,8 @@ export interface IInstantiationService {
|
|||
createInstance<A1, A2, A3, A4, A5, A6, A7, T>(descriptor: descriptors.SyncDescriptor7<A1, A2, A3, A4, A5, A6, A7, T>, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7): T;
|
||||
createInstance<A1, A2, A3, A4, A5, A6, A7, A8, T>(descriptor: descriptors.SyncDescriptor8<A1, A2, A3, A4, A5, A6, A7, A8, T>, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8): T;
|
||||
|
||||
createInstance<Ctor extends new (...args: any) => any, R extends InstanceType<Ctor>>(t: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
|
||||
createInstance<Ctor extends new (...args: any[]) => any, R extends InstanceType<Ctor>>(t: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
|
||||
createInstance<Services extends BrandedService[], Ctor extends new (...services: Services) => any, R extends InstanceType<Ctor>>(t: Ctor): R;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -33,7 +33,7 @@ export function registerConfiguration(): IDisposable {
|
|||
'configurationSync.enable': {
|
||||
type: 'boolean',
|
||||
description: localize('configurationSync.enable', "When enabled, synchronizes configuration that includes Settings and Extensions."),
|
||||
default: true,
|
||||
default: false,
|
||||
scope: ConfigurationScope.APPLICATION
|
||||
},
|
||||
'configurationSync.enableSettings': {
|
||||
|
@ -63,12 +63,6 @@ export function registerConfiguration(): IDisposable {
|
|||
$ref: ignoredSettingsSchemaId,
|
||||
additionalProperties: true,
|
||||
uniqueItems: true
|
||||
},
|
||||
'configurationSync.enableAuth': {
|
||||
'type': 'boolean',
|
||||
description: localize('configurationSync.enableAuth', "Enables authentication and requires VS Code restart when changed"),
|
||||
'default': false,
|
||||
'scope': ConfigurationScope.APPLICATION
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -496,7 +496,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
|||
|
||||
// Remember in recent document list (unless this opens for extension development)
|
||||
// Also do not add paths when files are opened for diffing, only if opened individually
|
||||
if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !(fileInputs?.filesToDiff) && !openConfig.noRecentEntry) {
|
||||
const isDiff = fileInputs && fileInputs.filesToDiff.length > 0;
|
||||
if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !isDiff && !openConfig.noRecentEntry) {
|
||||
const recents: IRecent[] = [];
|
||||
for (let pathToOpen of pathsToOpen) {
|
||||
if (pathToOpen.workspace) {
|
||||
|
|
24
src/vs/vscode.d.ts
vendored
24
src/vs/vscode.d.ts
vendored
|
@ -2967,6 +2967,17 @@ declare module 'vscode' {
|
|||
*/
|
||||
appendPlaceholder(value: string | ((snippet: SnippetString) => any), number?: number): SnippetString;
|
||||
|
||||
/**
|
||||
* Builder-function that appends a choice (`${1|a,b,c}`) to
|
||||
* the [`value`](#SnippetString.value) of this snippet string.
|
||||
*
|
||||
* @param values The values for choices - the array of strings
|
||||
* @param number The number of this tabstop, defaults to an auto-increment
|
||||
* value starting at 1.
|
||||
* @return This snippet string.
|
||||
*/
|
||||
appendChoice(values: string[], number?: number): SnippetString;
|
||||
|
||||
/**
|
||||
* Builder-function that appends a variable (`${VAR}`) to
|
||||
* the [`value`](#SnippetString.value) of this snippet string.
|
||||
|
@ -5732,10 +5743,18 @@ declare module 'vscode' {
|
|||
ctime: number;
|
||||
/**
|
||||
* The modification timestamp in milliseconds elapsed since January 1, 1970 00:00:00 UTC.
|
||||
*
|
||||
* *Note:* If the file changed, it is important to provide an updated `mtime` that advanced
|
||||
* from the previous value. Otherwise there may be optimizations in place that will not show
|
||||
* the updated file contents in an editor for example.
|
||||
*/
|
||||
mtime: number;
|
||||
/**
|
||||
* The size in bytes.
|
||||
*
|
||||
* *Note:* If the file changed, it is important to provide an updated `size`. Otherwise there
|
||||
* may be optimizations in place that will not show the updated file contents in an editor for
|
||||
* example.
|
||||
*/
|
||||
size: number;
|
||||
}
|
||||
|
@ -5849,6 +5868,11 @@ declare module 'vscode' {
|
|||
* An event to signal that a resource has been created, changed, or deleted. This
|
||||
* event should fire for resources that are being [watched](#FileSystemProvider.watch)
|
||||
* by clients of this provider.
|
||||
*
|
||||
* *Note:* It is important that the metadata of the file that changed provides an
|
||||
* updated `mtime` that advanced from the previous value in the [stat](#FileStat) and a
|
||||
* correct `size` value. Otherwise there may be optimizations in place that will not show
|
||||
* the change in an editor for example.
|
||||
*/
|
||||
readonly onDidChangeFile: Event<FileChangeEvent[]>;
|
||||
|
||||
|
|
21
src/vs/vscode.proposed.d.ts
vendored
21
src/vs/vscode.proposed.d.ts
vendored
|
@ -523,6 +523,27 @@ declare module 'vscode' {
|
|||
|
||||
//#region André: debug
|
||||
|
||||
/**
|
||||
* A DebugSource is an opaque stand-in type for the type [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) defined in the Debug Adapter Protocol.
|
||||
*/
|
||||
export interface DebugSource {
|
||||
// opaque contents
|
||||
}
|
||||
|
||||
export namespace debug {
|
||||
|
||||
/**
|
||||
* Converts a "Source" object received via the Debug Adapter Protocol into a Uri that can be used to load its contents.
|
||||
*
|
||||
* If the "Source" object has insufficient information to create a uri, an error is thrown.
|
||||
*
|
||||
* @param source An object conforming to the [Source](https://microsoft.github.io/debug-adapter-protocol/specification#Types_Source) type defined in the Debug Adapter Protocol.
|
||||
* @param session An optional debug session that will be used to locate the Debug Adapter Protocol.
|
||||
* @return A uri that can be used to load the contents of the source.
|
||||
*/
|
||||
function asDebugSourceUri(source: DebugSource, session?: DebugSession): Uri;
|
||||
}
|
||||
|
||||
// deprecated
|
||||
|
||||
export interface DebugConfigurationProvider {
|
||||
|
|
|
@ -778,7 +778,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
if (!parentSessionOrOptions || (typeof parentSessionOrOptions === 'object' && 'configuration' in parentSessionOrOptions)) {
|
||||
return extHostDebugService.startDebugging(folder, nameOrConfig, { parentSession: parentSessionOrOptions });
|
||||
}
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostDebugService.startDebugging(folder, nameOrConfig, parentSessionOrOptions || {});
|
||||
},
|
||||
addBreakpoints(breakpoints: vscode.Breakpoint[]) {
|
||||
|
@ -786,6 +785,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
|||
},
|
||||
removeBreakpoints(breakpoints: vscode.Breakpoint[]) {
|
||||
return extHostDebugService.removeBreakpoints(breakpoints);
|
||||
},
|
||||
asDebugSourceUri(source: vscode.DebugSource, session?: vscode.DebugSession): vscode.Uri {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostDebugService.asDebugSourceUri(source, session);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IConstructorSignature1 } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConstructorSignature1, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ProxyIdentifier } from 'vs/workbench/services/extensions/common/proxyIdentifier';
|
||||
|
||||
|
@ -13,12 +13,12 @@ export type IExtHostNamedCustomer<T extends IDisposable> = [ProxyIdentifier<T>,
|
|||
export type IExtHostCustomerCtor<T extends IDisposable> = IConstructorSignature1<IExtHostContext, T>;
|
||||
|
||||
export function extHostNamedCustomer<T extends IDisposable>(id: ProxyIdentifier<T>) {
|
||||
return function (ctor: IExtHostCustomerCtor<T>): void {
|
||||
return function <Services extends BrandedService[]>(ctor: { new(context: IExtHostContext, ...services: Services): T }): void {
|
||||
ExtHostCustomersRegistryImpl.INSTANCE.registerNamedCustomer(id, ctor);
|
||||
};
|
||||
}
|
||||
|
||||
export function extHostCustomer<T extends IDisposable>(ctor: IExtHostCustomerCtor<T>): void {
|
||||
export function extHostCustomer<T extends IDisposable, Services extends BrandedService[]>(ctor: { new(context: IExtHostContext, ...services: Services): T }): void {
|
||||
ExtHostCustomersRegistryImpl.INSTANCE.registerCustomer(ctor);
|
||||
}
|
||||
|
||||
|
|
|
@ -30,5 +30,6 @@ export interface IExtHostDebugService extends ExtHostDebugServiceShape {
|
|||
registerDebugConfigurationProvider(type: string, provider: vscode.DebugConfigurationProvider): vscode.Disposable;
|
||||
registerDebugAdapterDescriptorFactory(extension: IExtensionDescription, type: string, factory: vscode.DebugAdapterDescriptorFactory): vscode.Disposable;
|
||||
registerDebugAdapterTrackerFactory(type: string, factory: vscode.DebugAdapterTrackerFactory): vscode.Disposable;
|
||||
asDebugSourceUri(source: vscode.DebugSource, session?: vscode.DebugSession): vscode.Uri;
|
||||
}
|
||||
|
||||
|
|
|
@ -740,6 +740,18 @@ export class SnippetString {
|
|||
return this;
|
||||
}
|
||||
|
||||
appendChoice(values: string[], number: number = this._tabstop++): SnippetString {
|
||||
const value = SnippetString._escape(values.toString());
|
||||
|
||||
this.value += '${';
|
||||
this.value += number;
|
||||
this.value += '|';
|
||||
this.value += value;
|
||||
this.value += '|}';
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
appendVariable(name: string, defaultValue?: string | ((snippet: SnippetString) => any)): SnippetString {
|
||||
|
||||
if (typeof defaultValue === 'function') {
|
||||
|
|
|
@ -138,6 +138,32 @@ export class ExtHostDebugService implements IExtHostDebugService, ExtHostDebugSe
|
|||
});
|
||||
}
|
||||
|
||||
public asDebugSourceUri(src: vscode.DebugSource, session?: vscode.DebugSession): URI {
|
||||
|
||||
const source = <any>src;
|
||||
|
||||
if (typeof source.sourceReference === 'number') {
|
||||
// src can be retrieved via DAP's "source" request
|
||||
|
||||
let debug = `debug:${encodeURIComponent(source.path || '')}`;
|
||||
let sep = '?';
|
||||
|
||||
if (session) {
|
||||
debug += `${sep}session=${encodeURIComponent(session.id)}`;
|
||||
sep = '&';
|
||||
}
|
||||
|
||||
debug += `${sep}ref=${source.sourceReference}`;
|
||||
|
||||
return URI.parse(debug);
|
||||
} else if (source.path) {
|
||||
// src is just a local file path
|
||||
return URI.file(source.path);
|
||||
} else {
|
||||
throw new Error(`cannot create uri from DAP 'source' object; properties 'path' and 'sourceReference' are both missing.`);
|
||||
}
|
||||
}
|
||||
|
||||
private registerAllDebugTypes(extensionRegistry: ExtensionDescriptionRegistry) {
|
||||
|
||||
const debugTypes: string[] = [];
|
||||
|
|
|
@ -7,7 +7,7 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
/**
|
||||
* The action bar contributor allows to add actions to an actionbar in a given context.
|
||||
|
@ -134,7 +134,7 @@ export interface IActionBarRegistry {
|
|||
* Registers an Actionbar contributor. It will be called to contribute actions to all the action bars
|
||||
* that are used in the Workbench in the given scope.
|
||||
*/
|
||||
registerActionBarContributor(scope: string, ctor: IConstructorSignature0<ActionBarContributor>): void;
|
||||
registerActionBarContributor<Services extends BrandedService[]>(scope: string, ctor: { new(...services: Services): ActionBarContributor }): void;
|
||||
|
||||
/**
|
||||
* Returns an array of registered action bar contributors known to the workbench for the given scope.
|
||||
|
|
|
@ -368,7 +368,7 @@ class ResourceLabelWidget extends IconLabel {
|
|||
setEditor(editor: IEditorInput, options?: IResourceLabelOptions): void {
|
||||
this.setResource({
|
||||
resource: toResource(editor, { supportSideBySide: SideBySideEditor.MASTER }),
|
||||
name: withNullAsUndefined(editor.getName()),
|
||||
name: editor.getName(),
|
||||
description: editor.getDescription(options ? options.descriptionVerbosity : undefined)
|
||||
}, options);
|
||||
}
|
||||
|
|
|
@ -95,6 +95,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
private readonly _onCenteredLayoutChange: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onCenteredLayoutChange: Event<boolean> = this._onCenteredLayoutChange.event;
|
||||
|
||||
private readonly _onMaximizeChange: Emitter<boolean> = this._register(new Emitter<boolean>());
|
||||
readonly onMaximizeChange: Event<boolean> = this._onMaximizeChange.event;
|
||||
|
||||
private readonly _onPanelPositionChange: Emitter<string> = this._register(new Emitter<string>());
|
||||
readonly onPanelPositionChange: Event<string> = this._onPanelPositionChange.event;
|
||||
|
||||
|
@ -142,6 +145,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
|
||||
protected readonly state = {
|
||||
fullscreen: false,
|
||||
maximized: false,
|
||||
hasFocus: false,
|
||||
windowBorder: false,
|
||||
|
||||
|
@ -304,6 +308,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
// Propagate to grid
|
||||
this.workbenchGrid.setViewVisible(this.titleBarPartView, this.isVisible(Parts.TITLEBAR_PART));
|
||||
|
||||
this.updateWindowBorder(true);
|
||||
|
||||
this.layout(); // handle title bar when fullscreen changes
|
||||
}
|
||||
|
||||
|
@ -399,7 +405,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
const inactiveBorder = theme.getColor(WINDOW_INACTIVE_BORDER);
|
||||
|
||||
let windowBorder = false;
|
||||
if (activeBorder || inactiveBorder) {
|
||||
if (!this.state.fullscreen && !this.state.maximized && (activeBorder || inactiveBorder)) {
|
||||
windowBorder = true;
|
||||
|
||||
// If one color is missing, just fallback to the other one
|
||||
|
@ -1250,6 +1256,21 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi
|
|||
this._onPanelPositionChange.fire(positionToString(this.state.panel.position));
|
||||
}
|
||||
|
||||
isWindowMaximized() {
|
||||
return this.state.maximized;
|
||||
}
|
||||
|
||||
updateWindowMaximizedState(maximized: boolean) {
|
||||
if (this.state.maximized === maximized) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.state.maximized = maximized;
|
||||
|
||||
this.updateWindowBorder();
|
||||
this._onMaximizeChange.fire(maximized);
|
||||
}
|
||||
|
||||
private createGridDescriptor(): ISerializedGrid {
|
||||
const workbenchDimensions = this.getClientArea();
|
||||
const width = this.storageService.getNumber(Storage.GRID_WIDTH, StorageScope.GLOBAL, workbenchDimensions.width);
|
||||
|
|
|
@ -56,9 +56,8 @@ body.web {
|
|||
width: 100vw;
|
||||
}
|
||||
|
||||
.monaco-workbench.border {
|
||||
height: calc(100vh - 2px);
|
||||
width: calc(100vw - 2px);
|
||||
.monaco-workbench.border:not(.fullscreen) {
|
||||
box-sizing: border-box;
|
||||
border: 1px solid var(--window-border-color);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,7 @@ export abstract class Part extends Component implements ISerializableView {
|
|||
private options: IPartOptions,
|
||||
themeService: IThemeService,
|
||||
storageService: IStorageService,
|
||||
layoutService: IWorkbenchLayoutService
|
||||
protected readonly layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(id, themeService, storageService);
|
||||
|
||||
|
|
|
@ -83,7 +83,7 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
|||
constructor(
|
||||
@IViewletService private readonly viewletService: IViewletService,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@IStorageService private readonly storageService: IStorageService,
|
||||
@IExtensionService private readonly extensionService: IExtensionService,
|
||||
|
|
|
@ -56,7 +56,7 @@ export abstract class BaseBinaryResourceEditor extends BaseEditor {
|
|||
this.callbacks = callbacks;
|
||||
}
|
||||
|
||||
getTitle() {
|
||||
getTitle(): string {
|
||||
return this.input ? this.input.getName() : nls.localize('binaryEditor', "Binary Viewer");
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
import { IEditorInput, EditorInput, IEditorIdentifier, ConfirmResult, IEditorCommandsContext, CloseDirection } from 'vs/workbench/common/editor';
|
||||
import { IEditorInput, EditorInput, IEditorIdentifier, IEditorCommandsContext, CloseDirection } from 'vs/workbench/common/editor';
|
||||
import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
|
||||
import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen';
|
||||
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
|
||||
|
@ -22,6 +22,8 @@ import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/
|
|||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
|
||||
export class ExecuteCommandAction extends Action {
|
||||
|
||||
|
@ -597,6 +599,8 @@ export abstract class BaseCloseAllAction extends Action {
|
|||
label: string,
|
||||
clazz: string | undefined,
|
||||
private textFileService: ITextFileService,
|
||||
private workingCopyService: IWorkingCopyService,
|
||||
private fileDialogService: IFileDialogService,
|
||||
protected editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super(id, label, clazz);
|
||||
|
@ -619,7 +623,7 @@ export abstract class BaseCloseAllAction extends Action {
|
|||
async run(): Promise<any> {
|
||||
|
||||
// Just close all if there are no dirty editors
|
||||
if (!this.textFileService.isDirty()) {
|
||||
if (!this.workingCopyService.hasDirty) {
|
||||
return this.doCloseAll();
|
||||
}
|
||||
|
||||
|
@ -636,7 +640,7 @@ export abstract class BaseCloseAllAction extends Action {
|
|||
return undefined;
|
||||
}));
|
||||
|
||||
const confirm = await this.textFileService.confirmSave();
|
||||
const confirm = await this.fileDialogService.showSaveConfirm(this.workingCopyService.getDirty().map(copy => copy.resource));
|
||||
if (confirm === ConfirmResult.CANCEL) {
|
||||
return;
|
||||
}
|
||||
|
@ -667,9 +671,11 @@ export class CloseAllEditorsAction extends BaseCloseAllAction {
|
|||
id: string,
|
||||
label: string,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IFileDialogService fileDialogService: IFileDialogService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super(id, label, 'codicon-close-all', textFileService, editorGroupService);
|
||||
super(id, label, 'codicon-close-all', textFileService, workingCopyService, fileDialogService, editorGroupService);
|
||||
}
|
||||
|
||||
protected doCloseAll(): Promise<any> {
|
||||
|
@ -686,9 +692,11 @@ export class CloseAllEditorGroupsAction extends BaseCloseAllAction {
|
|||
id: string,
|
||||
label: string,
|
||||
@ITextFileService textFileService: ITextFileService,
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IFileDialogService fileDialogService: IFileDialogService,
|
||||
@IEditorGroupsService editorGroupService: IEditorGroupsService
|
||||
) {
|
||||
super(id, label, undefined, textFileService, editorGroupService);
|
||||
super(id, label, undefined, textFileService, workingCopyService, fileDialogService, editorGroupService);
|
||||
}
|
||||
|
||||
protected async doCloseAll(): Promise<any> {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import 'vs/css!./media/editorgroupview';
|
||||
|
||||
import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup';
|
||||
import { EditorInput, EditorOptions, GroupIdentifier, ConfirmResult, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, EditorGroupActiveEditorDirtyContext, IEditor, EditorGroupEditorsCountContext, toResource, SideBySideEditor } from 'vs/workbench/common/editor';
|
||||
import { Event, Emitter, Relay } from 'vs/base/common/event';
|
||||
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { addClass, addClasses, Dimension, trackFocus, toggleClass, removeClass, addDisposableListener, EventType, EventHelper, findParentWithClass, clearNode, isAncestor } from 'vs/base/browser/dom';
|
||||
|
@ -50,7 +50,8 @@ import { guessMimeTypes } from 'vs/base/common/mime';
|
|||
import { extname } from 'vs/base/common/resources';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { EditorActivation, EditorOpenContext } from 'vs/platform/editor/common/editor';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IDialogService, IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
export class EditorGroupView extends Themable implements IEditorGroupView {
|
||||
|
||||
|
@ -131,7 +132,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
@IUntitledTextEditorService private readonly untitledTextEditorService: IUntitledTextEditorService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService,
|
||||
@IMenuService private readonly menuService: IMenuService,
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService
|
||||
@IContextMenuService private readonly contextMenuService: IContextMenuService,
|
||||
@IFileDialogService private readonly fileDialogService: IFileDialogService,
|
||||
@ILogService private readonly logService: ILogService
|
||||
) {
|
||||
super(themeService);
|
||||
|
||||
|
@ -920,64 +923,74 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
|
||||
private async doHandleOpenEditorError(error: Error, editor: EditorInput, options?: EditorOptions): Promise<void> {
|
||||
|
||||
// Report error only if this was not us restoring previous error state or
|
||||
// we are told to ignore errors that occur from opening an editor
|
||||
if (this.isRestored && !isPromiseCanceledError(error) && (!options || !options.ignoreError)) {
|
||||
// Report error only if we are not told to ignore errors that occur from opening an editor
|
||||
if (!isPromiseCanceledError(error) && (!options || !options.ignoreError)) {
|
||||
|
||||
// Extract possible error actions from the error
|
||||
let errorActions: ReadonlyArray<IAction> | undefined = undefined;
|
||||
if (isErrorWithActions(error)) {
|
||||
errorActions = (error as IErrorWithActions).actions;
|
||||
}
|
||||
// Since it is more likely that errors fail to open when restoring them e.g.
|
||||
// because files got deleted or moved meanwhile, we do not show any notifications
|
||||
// if we are still restoring editors.
|
||||
if (this.isRestored) {
|
||||
|
||||
// If the context is USER, we try to show a modal dialog instead of a background notification
|
||||
if (options?.context === EditorOpenContext.USER) {
|
||||
const buttons: string[] = [];
|
||||
if (Array.isArray(errorActions) && errorActions.length > 0) {
|
||||
errorActions.forEach(action => buttons.push(action.label));
|
||||
} else {
|
||||
buttons.push(localize('ok', 'OK'));
|
||||
// Extract possible error actions from the error
|
||||
let errorActions: ReadonlyArray<IAction> | undefined = undefined;
|
||||
if (isErrorWithActions(error)) {
|
||||
errorActions = (error as IErrorWithActions).actions;
|
||||
}
|
||||
|
||||
let cancelId: number | undefined = undefined;
|
||||
if (buttons.length === 1) {
|
||||
buttons.push(localize('cancel', "Cancel"));
|
||||
cancelId = 1;
|
||||
// If the context is USER, we try to show a modal dialog instead of a background notification
|
||||
if (options?.context === EditorOpenContext.USER) {
|
||||
const buttons: string[] = [];
|
||||
if (Array.isArray(errorActions) && errorActions.length > 0) {
|
||||
errorActions.forEach(action => buttons.push(action.label));
|
||||
} else {
|
||||
buttons.push(localize('ok', 'OK'));
|
||||
}
|
||||
|
||||
let cancelId: number | undefined = undefined;
|
||||
if (buttons.length === 1) {
|
||||
buttons.push(localize('cancel', "Cancel"));
|
||||
cancelId = 1;
|
||||
}
|
||||
|
||||
const result = await this.dialogService.show(
|
||||
Severity.Error,
|
||||
localize('editorOpenErrorDialog', "Unable to open '{0}'", editor.getName()),
|
||||
buttons,
|
||||
{
|
||||
detail: toErrorMessage(error),
|
||||
cancelId
|
||||
}
|
||||
);
|
||||
|
||||
// Make sure to run any error action if present
|
||||
if (result.choice !== cancelId && Array.isArray(errorActions)) {
|
||||
const errorAction = errorActions[result.choice];
|
||||
if (errorAction) {
|
||||
errorAction.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.dialogService.show(
|
||||
Severity.Error,
|
||||
localize('editorOpenErrorDialog', "Unable to open '{0}'", editor.getName()),
|
||||
buttons,
|
||||
{
|
||||
detail: toErrorMessage(error),
|
||||
cancelId
|
||||
// Otherwise, show a background notification.
|
||||
else {
|
||||
const actions: INotificationActions = { primary: [] };
|
||||
if (Array.isArray(errorActions)) {
|
||||
actions.primary = errorActions;
|
||||
}
|
||||
);
|
||||
|
||||
// Make sure to run any error action if present
|
||||
if (result.choice !== cancelId && Array.isArray(errorActions)) {
|
||||
const errorAction = errorActions[result.choice];
|
||||
if (errorAction) {
|
||||
errorAction.run();
|
||||
}
|
||||
const handle = this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('editorOpenError', "Unable to open '{0}': {1}.", editor.getName(), toErrorMessage(error)),
|
||||
actions
|
||||
});
|
||||
|
||||
Event.once(handle.onDidClose)(() => actions.primary && dispose(actions.primary));
|
||||
}
|
||||
}
|
||||
|
||||
// Otherwise, show a background notification.
|
||||
// Restoring: just log errors to console
|
||||
else {
|
||||
const actions: INotificationActions = { primary: [] };
|
||||
if (Array.isArray(errorActions)) {
|
||||
actions.primary = errorActions;
|
||||
}
|
||||
|
||||
const handle = this.notificationService.notify({
|
||||
severity: Severity.Error,
|
||||
message: localize('editorOpenError', "Unable to open '{0}': {1}.", editor.getName(), toErrorMessage(error)),
|
||||
actions
|
||||
});
|
||||
|
||||
Event.once(handle.onDidClose)(() => actions.primary && dispose(actions.primary));
|
||||
this.logService.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1260,7 +1273,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
|
|||
// Switch to editor that we want to handle and confirm to save/revert
|
||||
await this.openEditor(editor);
|
||||
|
||||
const res = await editor.confirmSave();
|
||||
const editorResource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
|
||||
const res = await this.fileDialogService.showSaveConfirm(editorResource ? [editorResource] : editor.getName());
|
||||
|
||||
// It could be that the editor saved meanwhile, so we check again
|
||||
// to see if anything needs to happen before closing for good.
|
||||
|
|
|
@ -139,7 +139,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro
|
|||
@IThemeService themeService: IThemeService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IStorageService storageService: IStorageService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService
|
||||
@IWorkbenchLayoutService layoutService: IWorkbenchLayoutService
|
||||
) {
|
||||
super(Parts.EDITOR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti
|
|||
import { toResource, SideBySideEditor, IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { compareItemsByScore, scoreItem, ScorerCache, prepareQuery } from 'vs/base/parts/quickopen/common/quickOpenScorer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { withNullAsUndefined } from 'vs/base/common/types';
|
||||
|
||||
export class EditorPickerEntry extends QuickOpenEntryGroup {
|
||||
|
||||
|
@ -38,8 +37,8 @@ export class EditorPickerEntry extends QuickOpenEntryGroup {
|
|||
};
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
return withNullAsUndefined(this.editor.getName());
|
||||
getLabel(): string {
|
||||
return this.editor.getName();
|
||||
}
|
||||
|
||||
getIcon(): string {
|
||||
|
|
|
@ -248,7 +248,7 @@ export class NoTabsTitleControl extends TitleControl {
|
|||
|
||||
// Editor Label
|
||||
const resource = toResource(editor, { supportSideBySide: SideBySideEditor.MASTER });
|
||||
const name = editor.getName() || '';
|
||||
const name = editor.getName();
|
||||
|
||||
const { labelFormat } = this.accessor.partOptions;
|
||||
let description: string;
|
||||
|
|
|
@ -65,7 +65,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
|||
return new EditorMemento(this.getId(), key, Object.create(null), limit, editorGroupService); // do not persist in storage as diff editors are never persisted
|
||||
}
|
||||
|
||||
getTitle(): string | undefined {
|
||||
getTitle(): string {
|
||||
if (this.input) {
|
||||
return this.input.getName();
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ export class TextDiffEditor extends BaseTextEditor implements ITextDiffEditor {
|
|||
|
||||
protected getAriaLabel(): string {
|
||||
let ariaLabel: string;
|
||||
const inputName = this.input && this.input.getName();
|
||||
const inputName = this.input?.getName();
|
||||
if (this.isReadOnly()) {
|
||||
ariaLabel = inputName ? nls.localize('readonlyEditorWithInputAriaLabel', "{0}. Readonly text compare editor.", inputName) : nls.localize('readonlyEditorAriaLabel', "Readonly text compare editor.");
|
||||
} else {
|
||||
|
|
|
@ -287,7 +287,7 @@ export abstract class TitleControl extends Themable {
|
|||
label = localize('draggedEditorGroup', "{0} (+{1})", label, this.group.count - 1);
|
||||
}
|
||||
|
||||
applyDragImage(e, withUndefinedAsNull(label), 'monaco-editor-group-drag-image');
|
||||
applyDragImage(e, label, 'monaco-editor-group-drag-image');
|
||||
}
|
||||
}));
|
||||
|
||||
|
|
|
@ -661,7 +661,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
|
|||
|
||||
// Select element when keys are pressed that signal it
|
||||
const quickNavKeys = this.quickNavigate.keybindings;
|
||||
const wasTriggerKeyPressed = keyCode === KeyCode.Enter || quickNavKeys.some(k => {
|
||||
const wasTriggerKeyPressed = quickNavKeys.some(k => {
|
||||
const [firstPart, chordPart] = k.getParts();
|
||||
if (chordPart) {
|
||||
return false;
|
||||
|
|
|
@ -724,7 +724,7 @@ export class EditorHistoryEntryGroup extends QuickOpenEntryGroup {
|
|||
export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
||||
private input: IEditorInput | IResourceInput;
|
||||
private resource: URI | undefined;
|
||||
private label: string | undefined;
|
||||
private label: string;
|
||||
private description?: string;
|
||||
private dirty: boolean;
|
||||
|
||||
|
@ -745,7 +745,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
|||
|
||||
if (input instanceof EditorInput) {
|
||||
this.resource = resourceForEditorHistory(input, fileService);
|
||||
this.label = types.withNullAsUndefined(input.getName());
|
||||
this.label = input.getName();
|
||||
this.description = input.getDescription();
|
||||
this.dirty = input.isDirty();
|
||||
} else {
|
||||
|
@ -765,7 +765,7 @@ export class EditorHistoryEntry extends EditorQuickOpenEntry {
|
|||
return this.dirty ? 'dirty' : '';
|
||||
}
|
||||
|
||||
getLabel(): string | undefined {
|
||||
getLabel(): string {
|
||||
return this.label;
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,13 @@
|
|||
display: flex;
|
||||
}
|
||||
|
||||
.monaco-workbench.windows .part.titlebar,
|
||||
.monaco-workbench.linux .part.titlebar,
|
||||
.monaco-workbench.web .part.titlebar {
|
||||
/* put on own layer due to https://github.com/microsoft/vscode/issues/84806 */
|
||||
transform: translate3d(0px, 0px, 0px);
|
||||
}
|
||||
|
||||
.monaco-workbench .part.titlebar > .titlebar-drag-region {
|
||||
top: 0;
|
||||
left: 0;
|
||||
|
@ -68,7 +75,7 @@
|
|||
position: absolute;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 20%;
|
||||
height: 4px;
|
||||
}
|
||||
|
||||
.monaco-workbench.windows.fullscreen .part.titlebar > .resizer,
|
||||
|
|
|
@ -45,8 +45,6 @@ import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts';
|
|||
// TODO@sbatten https://github.com/microsoft/vscode/issues/81360
|
||||
// tslint:disable-next-line: import-patterns layering
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
// tslint:disable-next-line: import-patterns layering
|
||||
import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService';
|
||||
|
||||
export class TitlebarPart extends Part implements ITitleService {
|
||||
|
||||
|
@ -107,8 +105,7 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@optional(IElectronService) private electronService: IElectronService,
|
||||
@optional(IElectronEnvironmentService) private readonly electronEnvironmentService: IElectronEnvironmentService
|
||||
@optional(IElectronService) private electronService: IElectronService
|
||||
) {
|
||||
super(Parts.TITLEBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
|
||||
|
@ -448,13 +445,8 @@ export class TitlebarPart extends Part implements ITitleService {
|
|||
// Resizer
|
||||
this.resizer = append(this.element, $('div.resizer'));
|
||||
|
||||
const isMaximized = this.environmentService.configuration.maximized ? true : false;
|
||||
this.onDidChangeMaximized(isMaximized);
|
||||
|
||||
this._register(Event.any(
|
||||
Event.map(Event.filter(this.electronService.onWindowMaximize, id => id === this.electronEnvironmentService.windowId), _ => true),
|
||||
Event.map(Event.filter(this.electronService.onWindowUnmaximize, id => id === this.electronEnvironmentService.windowId), _ => false)
|
||||
)(e => this.onDidChangeMaximized(e)));
|
||||
this._register(this.layoutService.onMaximizeChange(maximized => this.onDidChangeMaximized(maximized)));
|
||||
this.onDidChangeMaximized(this.layoutService.isWindowMaximized());
|
||||
}
|
||||
|
||||
// Since the title area is used to drag the window, we do not want to steal focus from the
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IInstantiationService, IConstructorSignature0, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { runWhenIdle, IdleDeadline } from 'vs/base/common/async';
|
||||
|
@ -19,7 +19,7 @@ export namespace Extensions {
|
|||
export const Workbench = 'workbench.contributions.kind';
|
||||
}
|
||||
|
||||
export type IWorkbenchContributionSignature = IConstructorSignature0<IWorkbenchContribution>;
|
||||
type IWorkbenchContributionSignature<Service extends BrandedService[]> = new (...services: Service) => IWorkbenchContribution;
|
||||
|
||||
export interface IWorkbenchContributionsRegistry {
|
||||
|
||||
|
@ -29,7 +29,7 @@ export interface IWorkbenchContributionsRegistry {
|
|||
*
|
||||
* @param phase the lifecycle phase when to instantiate the contribution.
|
||||
*/
|
||||
registerWorkbenchContribution(contribution: IWorkbenchContributionSignature, phase: LifecyclePhase): void;
|
||||
registerWorkbenchContribution<Services extends BrandedService[]>(contribution: IWorkbenchContributionSignature<Services>, phase: LifecyclePhase): void;
|
||||
|
||||
/**
|
||||
* Starts the registry by providing the required services.
|
||||
|
@ -43,8 +43,7 @@ class WorkbenchContributionsRegistry implements IWorkbenchContributionsRegistry
|
|||
|
||||
private readonly toBeInstantiated: Map<LifecyclePhase, IConstructorSignature0<IWorkbenchContribution>[]> = new Map<LifecyclePhase, IConstructorSignature0<IWorkbenchContribution>[]>();
|
||||
|
||||
registerWorkbenchContribution(ctor: IWorkbenchContributionSignature, phase: LifecyclePhase = LifecyclePhase.Starting): void {
|
||||
|
||||
registerWorkbenchContribution<Services extends BrandedService[]>(ctor: { new(...services: Services): IWorkbenchContribution }, phase: LifecyclePhase = LifecyclePhase.Starting): void {
|
||||
// Instantiate directly if we are already matching the provided phase
|
||||
if (this.instantiationService && this.lifecycleService && this.lifecycleService.phase >= phase) {
|
||||
this.instantiationService.createInstance(ctor);
|
||||
|
|
|
@ -294,7 +294,7 @@ export interface IEditorInput extends IDisposable {
|
|||
/**
|
||||
* Returns the display name of this input.
|
||||
*/
|
||||
getName(): string | undefined;
|
||||
getName(): string;
|
||||
|
||||
/**
|
||||
* Returns the display description of this input.
|
||||
|
@ -360,8 +360,8 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
|
|||
* Returns the name of this input that can be shown to the user. Examples include showing the name of the input
|
||||
* above the editor area when the input is shown.
|
||||
*/
|
||||
getName(): string | undefined {
|
||||
return undefined;
|
||||
getName(): string {
|
||||
return `Editor ${this.getTypeId()}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -376,7 +376,7 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
|
|||
* Returns the title of this input that can be shown to the user. Examples include showing the title of
|
||||
* the input above the editor area as hover over the input label.
|
||||
*/
|
||||
getTitle(verbosity?: Verbosity): string | undefined {
|
||||
getTitle(verbosity?: Verbosity): string {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
|
@ -415,13 +415,6 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Subclasses should bring up a proper dialog for the user if the editor is dirty and return the result.
|
||||
*/
|
||||
confirmSave(): Promise<ConfirmResult> {
|
||||
return Promise.resolve(ConfirmResult.DONT_SAVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves the editor if it is dirty. Subclasses return a promise with a boolean indicating the success of the operation.
|
||||
*/
|
||||
|
@ -476,12 +469,6 @@ export abstract class EditorInput extends Disposable implements IEditorInput {
|
|||
}
|
||||
}
|
||||
|
||||
export const enum ConfirmResult {
|
||||
SAVE,
|
||||
DONT_SAVE,
|
||||
CANCEL
|
||||
}
|
||||
|
||||
export const enum EncodingMode {
|
||||
|
||||
/**
|
||||
|
@ -573,10 +560,6 @@ export class SideBySideEditorInput extends EditorInput {
|
|||
return this.master.isDirty();
|
||||
}
|
||||
|
||||
confirmSave(): Promise<ConfirmResult> {
|
||||
return this.master.confirmSave();
|
||||
}
|
||||
|
||||
save(): Promise<boolean> {
|
||||
return this.master.save();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ import { EditorModel } from 'vs/workbench/common/editor';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { DataUri, basename } from 'vs/base/common/resources';
|
||||
import { DataUri } from 'vs/base/common/resources';
|
||||
import { MIME_BINARY } from 'vs/base/common/mime';
|
||||
|
||||
/**
|
||||
|
@ -20,7 +20,7 @@ export class BinaryEditorModel extends EditorModel {
|
|||
|
||||
constructor(
|
||||
public readonly resource: URI,
|
||||
private readonly name: string | undefined,
|
||||
private readonly name: string,
|
||||
@IFileService private readonly fileService: IFileService
|
||||
) {
|
||||
super();
|
||||
|
@ -46,7 +46,7 @@ export class BinaryEditorModel extends EditorModel {
|
|||
* The name of the binary resource.
|
||||
*/
|
||||
getName(): string {
|
||||
return this.name || basename(this.resource);
|
||||
return this.name;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -7,7 +7,7 @@ import { EditorInput } from 'vs/workbench/common/editor';
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
|
||||
import { DataUri } from 'vs/base/common/resources';
|
||||
import { DataUri, basename } from 'vs/base/common/resources';
|
||||
|
||||
/**
|
||||
* An editor input to present data URIs in a binary editor. Data URIs have the form of:
|
||||
|
@ -17,25 +17,31 @@ export class DataUriEditorInput extends EditorInput {
|
|||
|
||||
static readonly ID: string = 'workbench.editors.dataUriEditorInput';
|
||||
|
||||
private readonly name: string;
|
||||
private readonly description: string | undefined;
|
||||
|
||||
constructor(
|
||||
private readonly name: string | undefined,
|
||||
private readonly description: string | undefined,
|
||||
name: string | undefined,
|
||||
description: string | undefined,
|
||||
private readonly resource: URI,
|
||||
@IInstantiationService private readonly instantiationService: IInstantiationService
|
||||
) {
|
||||
super();
|
||||
|
||||
if (!this.name || !this.description) {
|
||||
if (!name || !description) {
|
||||
const metadata = DataUri.parseMetaData(this.resource);
|
||||
|
||||
if (!this.name) {
|
||||
this.name = metadata.get(DataUri.META_DATA_LABEL);
|
||||
if (!name) {
|
||||
name = metadata.get(DataUri.META_DATA_LABEL) || basename(resource);
|
||||
}
|
||||
|
||||
if (!this.description) {
|
||||
this.description = metadata.get(DataUri.META_DATA_DESCRIPTION);
|
||||
if (!description) {
|
||||
description = metadata.get(DataUri.META_DATA_DESCRIPTION);
|
||||
}
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
this.description = description;
|
||||
}
|
||||
|
||||
getResource(): URI {
|
||||
|
@ -46,7 +52,7 @@ export class DataUriEditorInput extends EditorInput {
|
|||
return DataUriEditorInput.ID;
|
||||
}
|
||||
|
||||
getName(): string | undefined {
|
||||
getName(): string {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,13 @@ export class DiffEditorInput extends SideBySideEditorInput {
|
|||
|
||||
private cachedModel: DiffEditorModel | null = null;
|
||||
|
||||
constructor(name: string, description: string | undefined, original: EditorInput, modified: EditorInput, private readonly forceOpenAsBinary?: boolean) {
|
||||
constructor(
|
||||
name: string,
|
||||
description: string | undefined,
|
||||
original: EditorInput,
|
||||
modified: EditorInput,
|
||||
private readonly forceOpenAsBinary?: boolean
|
||||
) {
|
||||
super(name, description, original, modified);
|
||||
}
|
||||
|
||||
|
|
|
@ -8,7 +8,7 @@ import { suggestFilename } from 'vs/base/common/mime';
|
|||
import { createMemoizer } from 'vs/base/common/decorators';
|
||||
import { PLAINTEXT_MODE_ID } from 'vs/editor/common/modes/modesRegistry';
|
||||
import { basenameOrAuthority, dirname } from 'vs/base/common/resources';
|
||||
import { EditorInput, IEncodingSupport, EncodingMode, ConfirmResult, Verbosity, IModeSupport } from 'vs/workbench/common/editor';
|
||||
import { EditorInput, IEncodingSupport, EncodingMode, Verbosity, IModeSupport } from 'vs/workbench/common/editor';
|
||||
import { UntitledTextEditorModel } from 'vs/workbench/common/editor/untitledTextEditorModel';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
@ -109,7 +109,7 @@ export class UntitledTextEditorInput extends EditorInput implements IEncodingSup
|
|||
return this.labelService.getUriLabel(this.resource);
|
||||
}
|
||||
|
||||
getTitle(verbosity: Verbosity): string | undefined {
|
||||
getTitle(verbosity: Verbosity): string {
|
||||
if (!this.hasAssociatedFilePath) {
|
||||
return this.getName();
|
||||
}
|
||||
|
@ -122,8 +122,6 @@ export class UntitledTextEditorInput extends EditorInput implements IEncodingSup
|
|||
case Verbosity.LONG:
|
||||
return this.longTitle;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
isDirty(): boolean {
|
||||
|
@ -148,10 +146,6 @@ export class UntitledTextEditorInput extends EditorInput implements IEncodingSup
|
|||
return false;
|
||||
}
|
||||
|
||||
confirmSave(): Promise<ConfirmResult> {
|
||||
return this.textFileService.confirmSave([this.resource]);
|
||||
}
|
||||
|
||||
save(): Promise<boolean> {
|
||||
return this.textFileService.save(this.resource);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ export class BinaryFileEditor extends BaseBinaryResourceEditor {
|
|||
}
|
||||
}
|
||||
|
||||
getTitle(): string | undefined {
|
||||
getTitle(): string {
|
||||
return this.input ? this.input.getName() : nls.localize('binaryFileEditor', "Binary File Viewer");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,7 +39,7 @@ import { IDragAndDropData, DataTransfers } from 'vs/base/browser/dnd';
|
|||
import { memoize } from 'vs/base/common/decorators';
|
||||
import { ElementsDragAndDropData, DesktopDragAndDropData } from 'vs/base/browser/ui/list/listView';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { withNullAsUndefined, withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { withUndefinedAsNull } from 'vs/base/common/types';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
|
||||
|
@ -619,13 +619,13 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop<OpenEditor | IEditorGro
|
|||
return null;
|
||||
}
|
||||
|
||||
getDragLabel?(elements: (OpenEditor | IEditorGroup)[]): string | undefined {
|
||||
getDragLabel?(elements: (OpenEditor | IEditorGroup)[]): string {
|
||||
if (elements.length > 1) {
|
||||
return String(elements.length);
|
||||
}
|
||||
const element = elements[0];
|
||||
|
||||
return element instanceof OpenEditor ? withNullAsUndefined(element.editor.getName()) : element.label;
|
||||
return element instanceof OpenEditor ? element.editor.getName() : element.label;
|
||||
}
|
||||
|
||||
onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void {
|
||||
|
|
|
@ -41,9 +41,9 @@ export class DirtyFilesIndicator extends Disposable implements IWorkbenchContrib
|
|||
this.lifecycleService.onShutdown(this.dispose, this);
|
||||
}
|
||||
|
||||
private onWorkingCopyDidChangeDirty(copy: IWorkingCopy): void {
|
||||
const gotDirty = copy.isDirty();
|
||||
if (gotDirty && !!(copy.capabilities & WorkingCopyCapabilities.AutoSave) && this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) {
|
||||
private onWorkingCopyDidChangeDirty(workingCopy: IWorkingCopy): void {
|
||||
const gotDirty = workingCopy.isDirty();
|
||||
if (gotDirty && !!(workingCopy.capabilities & WorkingCopyCapabilities.AutoSave) && this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) {
|
||||
return; // do not indicate dirty of working copies that are auto saved after short delay
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import { localize } from 'vs/nls';
|
|||
import { createMemoizer } from 'vs/base/common/decorators';
|
||||
import { dirname } from 'vs/base/common/resources';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { EncodingMode, ConfirmResult, EditorInput, IFileEditorInput, ITextEditorModel, Verbosity, IRevertOptions } from 'vs/workbench/common/editor';
|
||||
import { EncodingMode, EditorInput, IFileEditorInput, ITextEditorModel, Verbosity, IRevertOptions } from 'vs/workbench/common/editor';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel';
|
||||
import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files';
|
||||
|
@ -243,10 +243,6 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput {
|
|||
return model.isDirty();
|
||||
}
|
||||
|
||||
confirmSave(): Promise<ConfirmResult> {
|
||||
return this.textFileService.confirmSave([this.resource]);
|
||||
}
|
||||
|
||||
save(): Promise<boolean> {
|
||||
return this.textFileService.save(this.resource);
|
||||
}
|
||||
|
|
|
@ -164,7 +164,7 @@ registerEditorAction(class extends EditorAction {
|
|||
kbOpts: {
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
kbExpr: EditorContextKeys.focus,
|
||||
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.DownArrow,
|
||||
primary: undefined,
|
||||
mac: {
|
||||
primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.DownArrow,
|
||||
},
|
||||
|
@ -188,7 +188,7 @@ registerEditorAction(class extends EditorAction {
|
|||
kbOpts: {
|
||||
weight: KeybindingWeight.EditorContrib,
|
||||
kbExpr: EditorContextKeys.focus,
|
||||
primary: KeyMod.Shift | KeyMod.Alt | KeyCode.UpArrow,
|
||||
primary: undefined,
|
||||
mac: {
|
||||
primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.UpArrow,
|
||||
},
|
||||
|
|
|
@ -39,7 +39,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
|
|||
private enableCrashReporter: boolean | undefined;
|
||||
private treeHorizontalScrolling: boolean | undefined;
|
||||
private debugConsoleWordWrap: boolean | undefined;
|
||||
private enableConfigSyncAuth: boolean | undefined;
|
||||
|
||||
constructor(
|
||||
@IHostService private readonly hostService: IHostService,
|
||||
|
@ -107,12 +106,6 @@ export class SettingsChangeRelauncher extends Disposable implements IWorkbenchCo
|
|||
}
|
||||
}
|
||||
|
||||
// Configuration Sync Auth
|
||||
if (typeof config.configurationSync?.enableAuth === 'boolean' && config.configurationSync.enableAuth !== this.enableConfigSyncAuth) {
|
||||
this.enableConfigSyncAuth = config.configurationSync.enableAuth;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Notify only when changed and we are the focused window (avoids notification spam across windows)
|
||||
if (notify && changed) {
|
||||
this.doConfirm(
|
||||
|
|
|
@ -43,8 +43,8 @@ export class PatternInputWidget extends Widget {
|
|||
private domNode!: HTMLElement;
|
||||
protected inputBox!: HistoryInputBox;
|
||||
|
||||
private _onSubmit = this._register(new Emitter<void>());
|
||||
onSubmit: CommonEvent<void> = this._onSubmit.event;
|
||||
private _onSubmit = this._register(new Emitter<boolean>());
|
||||
onSubmit: CommonEvent<boolean /* triggeredOnType */> = this._onSubmit.event;
|
||||
|
||||
private _onCancel = this._register(new Emitter<void>());
|
||||
onCancel: CommonEvent<void> = this._onCancel.event;
|
||||
|
@ -152,7 +152,7 @@ export class PatternInputWidget extends Widget {
|
|||
this._register(this.inputBox.onDidChange(() => {
|
||||
if (this.searchConfig.searchOnType) {
|
||||
this._onCancel.fire();
|
||||
this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(), this.searchConfig.searchOnTypeDebouncePeriod);
|
||||
this.searchOnTypeDelayer.trigger(() => this._onSubmit.fire(false), this.searchConfig.searchOnTypeDebouncePeriod);
|
||||
}
|
||||
}));
|
||||
|
||||
|
@ -170,7 +170,7 @@ export class PatternInputWidget extends Widget {
|
|||
private onInputKeyUp(keyboardEvent: IKeyboardEvent) {
|
||||
switch (keyboardEvent.keyCode) {
|
||||
case KeyCode.Enter:
|
||||
this._onSubmit.fire();
|
||||
this._onSubmit.fire(true);
|
||||
return;
|
||||
case KeyCode.Escape:
|
||||
this._onCancel.fire();
|
||||
|
|
|
@ -41,7 +41,7 @@ import { ExplorerFolderContext, ExplorerRootContext, FilesExplorerFocusCondition
|
|||
import { OpenAnythingHandler } from 'vs/workbench/contrib/search/browser/openAnythingHandler';
|
||||
import { OpenSymbolHandler } from 'vs/workbench/contrib/search/browser/openSymbolHandler';
|
||||
import { registerContributions as replaceContributions } from 'vs/workbench/contrib/search/browser/replaceContributions';
|
||||
import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand } from 'vs/workbench/contrib/search/browser/searchActions';
|
||||
import { clearHistoryCommand, ClearSearchResultsAction, CloseReplaceAction, CollapseDeepestExpandedLevelAction, copyAllCommand, copyMatchCommand, copyPathCommand, FocusNextInputAction, FocusNextSearchResultAction, FocusPreviousInputAction, FocusPreviousSearchResultAction, focusSearchListCommand, getSearchView, openSearchView, OpenSearchViewletAction, RefreshAction, RemoveAction, ReplaceAction, ReplaceAllAction, ReplaceAllInFolderAction, ReplaceInFilesAction, toggleCaseSensitiveCommand, toggleRegexCommand, toggleWholeWordCommand, FindInFilesCommand, ToggleSearchOnTypeAction } from 'vs/workbench/contrib/search/browser/searchActions';
|
||||
import { SearchPanel } from 'vs/workbench/contrib/search/browser/searchPanel';
|
||||
import { SearchView } from 'vs/workbench/contrib/search/browser/searchView';
|
||||
import { SearchViewlet } from 'vs/workbench/contrib/search/browser/searchViewlet';
|
||||
|
@ -619,6 +619,7 @@ KeybindingsRegistry.registerCommandAndKeybindingRule(objects.assign({
|
|||
registry.registerWorkbenchAction(new SyncActionDescriptor(CollapseDeepestExpandedLevelAction, CollapseDeepestExpandedLevelAction.ID, CollapseDeepestExpandedLevelAction.LABEL), 'Search: Collapse All', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ShowAllSymbolsAction, ShowAllSymbolsAction.ID, ShowAllSymbolsAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_T }), 'Go to Symbol in Workspace...');
|
||||
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleSearchOnTypeAction, ToggleSearchOnTypeAction.ID, ToggleSearchOnTypeAction.LABEL), 'Search: Toggle Search on Type', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(RefreshAction, RefreshAction.ID, RefreshAction.LABEL), 'Search: Refresh', category);
|
||||
registry.registerWorkbenchAction(new SyncActionDescriptor(ClearSearchResultsAction, ClearSearchResultsAction.ID, ClearSearchResultsAction.LABEL), 'Search: Clear Search Results', category);
|
||||
|
||||
|
|
|
@ -251,6 +251,30 @@ export class CloseReplaceAction extends Action {
|
|||
}
|
||||
}
|
||||
|
||||
// --- Toggle Search On Type
|
||||
|
||||
export class ToggleSearchOnTypeAction extends Action {
|
||||
|
||||
static readonly ID = 'workbench.action.toggleSearchOnType';
|
||||
static readonly LABEL = nls.localize('toggleTabs', "Toggle Search on Type");
|
||||
|
||||
private static readonly searchOnTypeKey = 'search.searchOnType';
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
run(): Promise<any> {
|
||||
const searchOnType = this.configurationService.getValue<boolean>(ToggleSearchOnTypeAction.searchOnTypeKey);
|
||||
return this.configurationService.updateValue(ToggleSearchOnTypeAction.searchOnTypeKey, !searchOnType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export class RefreshAction extends Action {
|
||||
|
||||
static readonly ID: string = 'search.action.refreshSearchResults';
|
||||
|
@ -275,7 +299,7 @@ export class RefreshAction extends Action {
|
|||
run(): Promise<void> {
|
||||
const searchView = getSearchView(this.viewletService, this.panelService);
|
||||
if (searchView) {
|
||||
searchView.onQueryChanged();
|
||||
searchView.onQueryChanged(false);
|
||||
}
|
||||
|
||||
return Promise.resolve();
|
||||
|
|
|
@ -268,7 +268,7 @@ export class SearchView extends ViewletPanel {
|
|||
|
||||
this.inputPatternIncludes.setValue(patternIncludes);
|
||||
|
||||
this.inputPatternIncludes.onSubmit(() => this.onQueryChanged(true));
|
||||
this.inputPatternIncludes.onSubmit(triggeredOnType => this.onQueryChanged(true, triggeredOnType));
|
||||
this.inputPatternIncludes.onCancel(() => this.cancelSearch(false));
|
||||
this.trackInputBox(this.inputPatternIncludes.inputFocusTracker, this.inputPatternIncludesFocused);
|
||||
|
||||
|
@ -284,7 +284,7 @@ export class SearchView extends ViewletPanel {
|
|||
this.inputPatternExcludes.setValue(patternExclusions);
|
||||
this.inputPatternExcludes.setUseExcludesAndIgnoreFiles(useExcludesAndIgnoreFiles);
|
||||
|
||||
this.inputPatternExcludes.onSubmit(() => this.onQueryChanged(true));
|
||||
this.inputPatternExcludes.onSubmit(triggeredOnType => this.onQueryChanged(true, triggeredOnType));
|
||||
this.inputPatternExcludes.onCancel(() => this.cancelSearch(false));
|
||||
this.inputPatternExcludes.onChangeIgnoreBox(() => this.onQueryChanged(true));
|
||||
this.trackInputBox(this.inputPatternExcludes.inputFocusTracker, this.inputPatternExclusionsFocused);
|
||||
|
@ -386,7 +386,7 @@ export class SearchView extends ViewletPanel {
|
|||
this.searchWidget.toggleReplace(true);
|
||||
}
|
||||
|
||||
this._register(this.searchWidget.onSearchSubmit(() => this.onQueryChanged()));
|
||||
this._register(this.searchWidget.onSearchSubmit(triggeredOnType => this.onQueryChanged(false, triggeredOnType)));
|
||||
this._register(this.searchWidget.onSearchCancel(({ focus }) => this.cancelSearch(focus)));
|
||||
this._register(this.searchWidget.searchInput.onDidOptionChange(() => this.onQueryChanged(true)));
|
||||
|
||||
|
@ -800,7 +800,7 @@ export class SearchView extends ViewletPanel {
|
|||
}
|
||||
this.searchWidget.setValue(selectedText, true);
|
||||
updatedText = true;
|
||||
this.onQueryChanged();
|
||||
this.onQueryChanged(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1155,7 +1155,7 @@ export class SearchView extends ViewletPanel {
|
|||
this.searchWidget.focus(false);
|
||||
}
|
||||
|
||||
onQueryChanged(preserveFocus?: boolean): void {
|
||||
onQueryChanged(preserveFocus: boolean, triggeredOnType = false): void {
|
||||
if (!this.searchWidget.searchInput.inputBox.isInputValid()) {
|
||||
return;
|
||||
}
|
||||
|
@ -1221,7 +1221,7 @@ export class SearchView extends ViewletPanel {
|
|||
}
|
||||
|
||||
this.validateQuery(query).then(() => {
|
||||
this.onQueryTriggered(query, options, excludePatternText, includePatternText);
|
||||
this.onQueryTriggered(query, options, excludePatternText, includePatternText, triggeredOnType);
|
||||
|
||||
if (!preserveFocus) {
|
||||
this.searchWidget.focus(false); // focus back to input field
|
||||
|
@ -1251,7 +1251,7 @@ export class SearchView extends ViewletPanel {
|
|||
});
|
||||
}
|
||||
|
||||
private onQueryTriggered(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string): void {
|
||||
private onQueryTriggered(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string, triggeredOnType: boolean): void {
|
||||
this.addToSearchHistoryDelayer.trigger(() => this.searchWidget.searchInput.onSearchSubmit());
|
||||
this.inputPatternExcludes.onSearchSubmit();
|
||||
this.inputPatternIncludes.onSearchSubmit();
|
||||
|
@ -1259,13 +1259,13 @@ export class SearchView extends ViewletPanel {
|
|||
this.viewModel.cancelSearch();
|
||||
|
||||
this.currentSearchQ = this.currentSearchQ
|
||||
.then(() => this.doSearch(query, options, excludePatternText, includePatternText))
|
||||
.then(() => this.doSearch(query, options, excludePatternText, includePatternText, triggeredOnType))
|
||||
.then(() => undefined, () => undefined);
|
||||
}
|
||||
|
||||
private doSearch(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string): Thenable<void> {
|
||||
private doSearch(query: ITextQuery, options: ITextQueryBuilderOptions, excludePatternText: string, includePatternText: string, triggeredOnType: boolean): Thenable<void> {
|
||||
let progressComplete: () => void;
|
||||
this.progressService.withProgress({ location: VIEWLET_ID, delay: 300 }, _progress => {
|
||||
this.progressService.withProgress({ location: VIEWLET_ID, delay: triggeredOnType ? 300 : 0 }, _progress => {
|
||||
return new Promise(resolve => progressComplete = resolve);
|
||||
});
|
||||
|
||||
|
@ -1335,7 +1335,7 @@ export class SearchView extends ViewletPanel {
|
|||
const searchAgainLink = dom.append(p, $('a.pointer.prominent', undefined, nls.localize('rerunSearch.message', "Search again")));
|
||||
this.messageDisposables.push(dom.addDisposableListener(searchAgainLink, dom.EventType.CLICK, (e: MouseEvent) => {
|
||||
dom.EventHelper.stop(e, false);
|
||||
this.onQueryChanged();
|
||||
this.onQueryChanged(false);
|
||||
}));
|
||||
} else if (hasIncludes || hasExcludes) {
|
||||
const searchAgainLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('rerunSearchInAll.message', "Search again in all files")));
|
||||
|
@ -1345,7 +1345,7 @@ export class SearchView extends ViewletPanel {
|
|||
this.inputPatternExcludes.setValue('');
|
||||
this.inputPatternIncludes.setValue('');
|
||||
|
||||
this.onQueryChanged();
|
||||
this.onQueryChanged(false);
|
||||
}));
|
||||
} else {
|
||||
const openSettingsLink = dom.append(p, $('a.pointer.prominent', { tabindex: 0 }, nls.localize('openSettings.message', "Open Settings")));
|
||||
|
|
|
@ -121,8 +121,8 @@ export class SearchWidget extends Widget {
|
|||
private ignoreGlobalFindBufferOnNextFocus = false;
|
||||
private previousGlobalFindBufferValue: string | null = null;
|
||||
|
||||
private _onSearchSubmit = this._register(new Emitter<void>());
|
||||
readonly onSearchSubmit: Event<void> = this._onSearchSubmit.event;
|
||||
private _onSearchSubmit = this._register(new Emitter<boolean>());
|
||||
readonly onSearchSubmit: Event<boolean /* triggeredOnType */> = this._onSearchSubmit.event;
|
||||
|
||||
private _onSearchCancel = this._register(new Emitter<{ focus: boolean }>());
|
||||
readonly onSearchCancel: Event<{ focus: boolean }> = this._onSearchCancel.event;
|
||||
|
@ -454,7 +454,7 @@ export class SearchWidget extends Widget {
|
|||
this.temporarilySkipSearchOnChange = false;
|
||||
} else {
|
||||
this._onSearchCancel.fire({ focus: false });
|
||||
this._searchDelayer.trigger((() => this.submitSearch()), this.searchConfiguration.searchOnTypeDebouncePeriod);
|
||||
this._searchDelayer.trigger((() => this.submitSearch(true)), this.searchConfiguration.searchOnTypeDebouncePeriod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -563,7 +563,7 @@ export class SearchWidget extends Widget {
|
|||
}
|
||||
}
|
||||
|
||||
private submitSearch(): void {
|
||||
private submitSearch(triggeredOnType = false): void {
|
||||
this.searchInput.validate();
|
||||
if (!this.searchInput.inputBox.isInputValid()) {
|
||||
return;
|
||||
|
@ -574,7 +574,7 @@ export class SearchWidget extends Widget {
|
|||
if (value && useGlobalFindBuffer) {
|
||||
this.clipboardServce.writeFindText(value);
|
||||
}
|
||||
this._onSearchSubmit.fire();
|
||||
this._onSearchSubmit.fire(triggeredOnType);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import * as nls from 'vs/nls';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
|
||||
import { IEditorInputFactory, EditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, EditorModel, ConfirmResult, IRevertOptions, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { IEditorInputFactory, EditorInput, IEditorInputFactoryRegistry, Extensions as EditorInputExtensions, EditorModel, IRevertOptions, EditorOptions } from 'vs/workbench/common/editor';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IEditorModel } from 'vs/platform/editor/common/editor';
|
||||
import { Dimension, addDisposableListener, EventType } from 'vs/base/browser/dom';
|
||||
|
@ -160,11 +160,6 @@ class TestCustomEditorInput extends EditorInput implements IWorkingCopy {
|
|||
return this.dirty;
|
||||
}
|
||||
|
||||
confirmSave(): Promise<ConfirmResult> {
|
||||
// TODO
|
||||
return Promise.resolve(ConfirmResult.DONT_SAVE);
|
||||
}
|
||||
|
||||
save(): Promise<boolean> {
|
||||
this.setDirty(false);
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/
|
|||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { IEditorInput } from 'vs/workbench/common/editor';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
||||
const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey<string>('authTokenStatus', AuthTokenStatus.Inactive);
|
||||
const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`));
|
||||
|
@ -33,6 +33,8 @@ const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbenc
|
|||
|
||||
export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution {
|
||||
|
||||
private static readonly ENABLEMENT_SETTING = 'configurationSync.enable';
|
||||
|
||||
private readonly syncStatusContext: IContextKey<string>;
|
||||
private readonly authTokenContext: IContextKey<string>;
|
||||
private readonly badgeDisposable = this._register(new MutableDisposable());
|
||||
|
@ -50,6 +52,7 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
@ITextFileService private readonly textFileService: ITextFileService,
|
||||
@IHistoryService private readonly historyService: IHistoryService,
|
||||
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IDialogService private readonly dialogService: IDialogService,
|
||||
) {
|
||||
super();
|
||||
this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService);
|
||||
|
@ -59,14 +62,8 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
this.onDidChangeSyncStatus(this.userDataSyncService.status);
|
||||
this._register(Event.debounce(authTokenService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeAuthTokenStatus(this.authTokenService.status)));
|
||||
this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status)));
|
||||
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateBadge()));
|
||||
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING))(() => this.onDidChangeEnablement()));
|
||||
this.registerActions();
|
||||
|
||||
timeout(2000).then(() => {
|
||||
if (this.authTokenService.status === AuthTokenStatus.Inactive && this.userDataSyncService.status !== SyncStatus.Uninitialized && configurationService.getValue<boolean>('configurationSync.enable')) {
|
||||
this.showSignInNotification();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private onDidChangeAuthTokenStatus(status: AuthTokenStatus) {
|
||||
|
@ -103,14 +100,34 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
}
|
||||
}
|
||||
|
||||
private onDidChangeEnablement() {
|
||||
this.updateBadge();
|
||||
const enabled = this.configurationService.getValue<boolean>(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING);
|
||||
if (enabled) {
|
||||
if (this.authTokenService.status === AuthTokenStatus.Inactive) {
|
||||
const handle = this.notificationService.prompt(Severity.Info, localize('ask to sign in', "Please sign in with your '{0}' account to sync configuration", "{ACCOUNT_NAME}"),
|
||||
[
|
||||
{
|
||||
label: localize('Sign in', "Sign in"),
|
||||
run: () => this.signIn()
|
||||
}
|
||||
]);
|
||||
this.signInNotificationDisposable.value = toDisposable(() => handle.close());
|
||||
handle.onDidClose(() => this.signInNotificationDisposable.clear());
|
||||
}
|
||||
} else {
|
||||
this.signInNotificationDisposable.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private updateBadge(): void {
|
||||
this.badgeDisposable.clear();
|
||||
|
||||
let badge: IBadge | undefined = undefined;
|
||||
let clazz: string | undefined;
|
||||
|
||||
if (this.authTokenService.status === AuthTokenStatus.Inactive && this.userDataSyncService.status !== SyncStatus.Uninitialized && this.configurationService.getValue<boolean>('configurationSync.enable')) {
|
||||
badge = new NumberBadge(1, () => localize('sign in', "Sign in..."));
|
||||
if (this.userDataSyncService.status !== SyncStatus.Uninitialized && this.configurationService.getValue<boolean>(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING) && this.authTokenService.status === AuthTokenStatus.Inactive) {
|
||||
badge = new NumberBadge(1, () => localize('sign in', "Sync: Sign in..."));
|
||||
} else if (this.userDataSyncService.status === SyncStatus.HasConflicts) {
|
||||
badge = new NumberBadge(1, () => localize('resolve conflicts', "Resolve Conflicts"));
|
||||
} else if (this.userDataSyncService.status === SyncStatus.Syncing) {
|
||||
|
@ -123,16 +140,24 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
}
|
||||
}
|
||||
|
||||
private showSignInNotification(): void {
|
||||
const handle = this.notificationService.prompt(Severity.Info, localize('show sign in', "Please sign in to Settings Sync service to start syncing configuration."),
|
||||
[
|
||||
{
|
||||
label: localize('sign in', "Sign in..."),
|
||||
run: () => this.signIn()
|
||||
}
|
||||
]);
|
||||
this.signInNotificationDisposable.value = toDisposable(() => handle.close());
|
||||
handle.onDidClose(() => this.signInNotificationDisposable.clear());
|
||||
private async turnOn(): Promise<void> {
|
||||
if (this.authTokenService.status === AuthTokenStatus.Inactive) {
|
||||
const result = await this.dialogService.confirm({
|
||||
type: 'info',
|
||||
message: localize('sign in to account', "Sign in to {0}", "{ACCOUNT_NAME}"),
|
||||
detail: localize('ask to sign in', "Please sign in with your '{0}' account to sync configuration", "{ACCOUNT_NAME}"),
|
||||
primaryButton: localize('Sign in', "Sign in")
|
||||
});
|
||||
if (!result.confirmed) {
|
||||
return;
|
||||
}
|
||||
await this.signIn();
|
||||
}
|
||||
await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, true);
|
||||
}
|
||||
|
||||
private async turnOff(): Promise<void> {
|
||||
await this.configurationService.updateValue(UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING, false);
|
||||
}
|
||||
|
||||
private async signIn(): Promise<void> {
|
||||
|
@ -194,17 +219,93 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
|
||||
private registerActions(): void {
|
||||
|
||||
const signInMenuItem: IMenuItem = {
|
||||
const startSyncMenuItem: IMenuItem = {
|
||||
group: '5_sync',
|
||||
command: {
|
||||
id: 'workbench.userData.actions.login',
|
||||
title: localize('sign in', "Sign in...")
|
||||
id: 'workbench.userData.actions.syncStart',
|
||||
title: localize('start sync', "Sync: Turn On")
|
||||
},
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Inactive), ContextKeyExpr.has('config.configurationSync.enable')),
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`)),
|
||||
};
|
||||
CommandsRegistry.registerCommand(signInMenuItem.command.id, () => this.signIn());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, signInMenuItem);
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, signInMenuItem);
|
||||
CommandsRegistry.registerCommand(startSyncMenuItem.command.id, () => this.turnOn());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, startSyncMenuItem);
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, startSyncMenuItem);
|
||||
|
||||
const signInCommandId = 'workbench.userData.actions.login';
|
||||
const signInWhenContext = ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Inactive));
|
||||
CommandsRegistry.registerCommand(signInCommandId, () => this.signIn());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '5_sync',
|
||||
command: {
|
||||
id: signInCommandId,
|
||||
title: localize('global activity sign in', "Sync: Sign in... (1)")
|
||||
},
|
||||
when: signInWhenContext,
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: signInCommandId,
|
||||
title: localize('sign in', "Sync: Sign in...")
|
||||
},
|
||||
when: signInWhenContext,
|
||||
});
|
||||
|
||||
const stopSycCommand = {
|
||||
id: 'workbench.userData.actions.stopSync',
|
||||
title: localize('stop sync', "Sync: Turn Off")
|
||||
};
|
||||
CommandsRegistry.registerCommand(stopSycCommand.id, () => this.turnOff());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '5_sync',
|
||||
command: stopSycCommand,
|
||||
when: ContextKeyExpr.and(ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`), CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Active), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.HasConflicts))
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: stopSycCommand,
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has(`config.${UserDataSyncWorkbenchContribution.ENABLEMENT_SETTING}`)),
|
||||
});
|
||||
|
||||
const resolveConflictsCommandId = 'workbench.userData.actions.resolveConflicts';
|
||||
const resolveConflictsWhenContext = CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts);
|
||||
CommandsRegistry.registerCommand(resolveConflictsCommandId, () => this.handleConflicts());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, {
|
||||
group: '5_sync',
|
||||
command: {
|
||||
id: resolveConflictsCommandId,
|
||||
title: localize('resolveConflicts_global', "Sync: Resolve Conflicts (1)"),
|
||||
},
|
||||
when: resolveConflictsWhenContext,
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: resolveConflictsCommandId,
|
||||
title: localize('resolveConflicts', "Sync: Resolve Conflicts"),
|
||||
},
|
||||
when: resolveConflictsWhenContext,
|
||||
});
|
||||
|
||||
const continueSyncCommandId = 'workbench.userData.actions.continueSync';
|
||||
CommandsRegistry.registerCommand(continueSyncCommandId, () => this.continueSync());
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: continueSyncCommandId,
|
||||
title: localize('continue sync', "Sync: Continue")
|
||||
},
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts)),
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: continueSyncCommandId,
|
||||
title: localize('continue sync', "Sync: Continue"),
|
||||
iconLocation: {
|
||||
light: SYNC_PUSH_LIGHT_ICON_URI,
|
||||
dark: SYNC_PUSH_DARK_ICON_URI
|
||||
}
|
||||
},
|
||||
group: 'navigation',
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts), ResourceContextKey.Resource.isEqualTo(this.workbenchEnvironmentService.settingsSyncPreviewResource.toString())),
|
||||
});
|
||||
|
||||
const signOutMenuItem: IMenuItem = {
|
||||
group: '5_sync',
|
||||
|
@ -215,66 +316,6 @@ export class UserDataSyncWorkbenchContribution extends Disposable implements IWo
|
|||
when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Active)),
|
||||
};
|
||||
CommandsRegistry.registerCommand(signOutMenuItem.command.id, () => this.signOut());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, signOutMenuItem);
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, signOutMenuItem);
|
||||
|
||||
const startSyncMenuItem: IMenuItem = {
|
||||
group: '5_sync',
|
||||
command: {
|
||||
id: 'workbench.userData.actions.syncStart',
|
||||
title: localize('start sync', "Configuration Sync: Turn On")
|
||||
},
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not('config.configurationSync.enable')),
|
||||
};
|
||||
CommandsRegistry.registerCommand(startSyncMenuItem.command.id, () => this.configurationService.updateValue('configurationSync.enable', true));
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, startSyncMenuItem);
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, startSyncMenuItem);
|
||||
|
||||
const stopSyncMenuItem: IMenuItem = {
|
||||
group: '5_sync',
|
||||
command: {
|
||||
id: 'workbench.userData.actions.stopSync',
|
||||
title: localize('stop sync', "Configuration Sync: Turn Off")
|
||||
},
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has('config.configurationSync.enable')),
|
||||
};
|
||||
CommandsRegistry.registerCommand(stopSyncMenuItem.command.id, () => this.configurationService.updateValue('configurationSync.enable', false));
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, stopSyncMenuItem);
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, stopSyncMenuItem);
|
||||
|
||||
const resolveConflictsMenuItem: IMenuItem = {
|
||||
group: '5_sync',
|
||||
command: {
|
||||
id: 'sync.resolveConflicts',
|
||||
title: localize('resolveConflicts', "Configuration Sync: Resolve Conflicts"),
|
||||
},
|
||||
when: CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts),
|
||||
};
|
||||
CommandsRegistry.registerCommand(resolveConflictsMenuItem.command.id, () => this.handleConflicts());
|
||||
MenuRegistry.appendMenuItem(MenuId.GlobalActivity, resolveConflictsMenuItem);
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, resolveConflictsMenuItem);
|
||||
|
||||
const continueSyncCommandId = 'workbench.userData.actions.continueSync';
|
||||
CommandsRegistry.registerCommand(continueSyncCommandId, () => this.continueSync());
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: {
|
||||
id: continueSyncCommandId,
|
||||
title: localize('continue sync', "Configuration Sync: Continue")
|
||||
},
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts)),
|
||||
});
|
||||
MenuRegistry.appendMenuItem(MenuId.EditorTitle, {
|
||||
command: {
|
||||
id: continueSyncCommandId,
|
||||
title: localize('continue sync', "Configuration Sync: Continue"),
|
||||
iconLocation: {
|
||||
light: SYNC_PUSH_LIGHT_ICON_URI,
|
||||
dark: SYNC_PUSH_DARK_ICON_URI
|
||||
}
|
||||
},
|
||||
group: 'navigation',
|
||||
order: 1,
|
||||
when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts), ResourceContextKey.Resource.isEqualTo(this.workbenchEnvironmentService.settingsSyncPreviewResource.toString())),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ export class WebviewInput extends EditorInput {
|
|||
return this._name;
|
||||
}
|
||||
|
||||
public getTitle(_verbosity?: Verbosity) {
|
||||
public getTitle(_verbosity?: Verbosity): string {
|
||||
return this.getName();
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import * as browser from 'vs/base/browser/browser';
|
|||
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IResourceInput } from 'vs/platform/editor/common/editor';
|
||||
import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService';
|
||||
import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron';
|
||||
import { ipcRenderer as ipc, webFrame, crashReporter, Event as IpcEvent } from 'electron';
|
||||
import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing';
|
||||
import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions';
|
||||
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
@ -60,7 +60,9 @@ import { ITunnelService, extractLocalHostUriMetaDataForPortMapping } from 'vs/pl
|
|||
import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService';
|
||||
import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export class ElectronWindow extends Disposable {
|
||||
|
||||
|
@ -103,7 +105,8 @@ export class ElectronWindow extends Disposable {
|
|||
@ITunnelService private readonly tunnelService: ITunnelService,
|
||||
@IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService,
|
||||
@IElectronEnvironmentService private readonly electronEnvironmentService: IElectronEnvironmentService,
|
||||
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService
|
||||
@IWorkingCopyService private readonly workingCopyService: IWorkingCopyService,
|
||||
@IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService
|
||||
) {
|
||||
super();
|
||||
|
||||
|
@ -124,7 +127,7 @@ export class ElectronWindow extends Disposable {
|
|||
});
|
||||
|
||||
// Support runAction event
|
||||
ipc.on('vscode:runAction', async (event: Event, request: IRunActionInWindowRequest) => {
|
||||
ipc.on('vscode:runAction', async (event: IpcEvent, request: IRunActionInWindowRequest) => {
|
||||
const args: unknown[] = request.args || [];
|
||||
|
||||
// If we run an action from the touchbar, we fill in the currently active resource
|
||||
|
@ -155,27 +158,27 @@ export class ElectronWindow extends Disposable {
|
|||
});
|
||||
|
||||
// Support runKeybinding event
|
||||
ipc.on('vscode:runKeybinding', (event: Event, request: IRunKeybindingInWindowRequest) => {
|
||||
ipc.on('vscode:runKeybinding', (event: IpcEvent, request: IRunKeybindingInWindowRequest) => {
|
||||
if (document.activeElement) {
|
||||
this.keybindingService.dispatchByUserSettingsLabel(request.userSettingsLabel, document.activeElement);
|
||||
}
|
||||
});
|
||||
|
||||
// Error reporting from main
|
||||
ipc.on('vscode:reportError', (event: Event, error: string) => {
|
||||
ipc.on('vscode:reportError', (event: IpcEvent, error: string) => {
|
||||
if (error) {
|
||||
errors.onUnexpectedError(JSON.parse(error));
|
||||
}
|
||||
});
|
||||
|
||||
// Support openFiles event for existing and new files
|
||||
ipc.on('vscode:openFiles', (event: Event, request: IOpenFileRequest) => this.onOpenFiles(request));
|
||||
ipc.on('vscode:openFiles', (event: IpcEvent, request: IOpenFileRequest) => this.onOpenFiles(request));
|
||||
|
||||
// Support addFolders event if we have a workspace opened
|
||||
ipc.on('vscode:addFolders', (event: Event, request: IAddFoldersRequest) => this.onAddFoldersRequest(request));
|
||||
ipc.on('vscode:addFolders', (event: IpcEvent, request: IAddFoldersRequest) => this.onAddFoldersRequest(request));
|
||||
|
||||
// Message support
|
||||
ipc.on('vscode:showInfoMessage', (event: Event, message: string) => {
|
||||
ipc.on('vscode:showInfoMessage', (event: IpcEvent, message: string) => {
|
||||
this.notificationService.info(message);
|
||||
});
|
||||
|
||||
|
@ -213,7 +216,7 @@ export class ElectronWindow extends Disposable {
|
|||
});
|
||||
|
||||
// keyboard layout changed event
|
||||
ipc.on('vscode:accessibilitySupportChanged', (event: Event, accessibilitySupportEnabled: boolean) => {
|
||||
ipc.on('vscode:accessibilitySupportChanged', (event: IpcEvent, accessibilitySupportEnabled: boolean) => {
|
||||
this.accessibilityService.setAccessibilitySupport(accessibilitySupportEnabled ? AccessibilitySupport.Enabled : AccessibilitySupport.Disabled);
|
||||
});
|
||||
|
||||
|
@ -267,6 +270,10 @@ export class ElectronWindow extends Disposable {
|
|||
if (isMacintosh) {
|
||||
this._register(this.workingCopyService.onDidChangeDirty(workingCopy => {
|
||||
const gotDirty = workingCopy.isDirty();
|
||||
if (gotDirty && !!(workingCopy.capabilities & WorkingCopyCapabilities.AutoSave) && this.filesConfigurationService.getAutoSaveMode() === AutoSaveMode.AFTER_SHORT_DELAY) {
|
||||
return; // do not indicate dirty of working copies that are auto saved after short delay
|
||||
}
|
||||
|
||||
if ((!this.isDocumentedEdited && gotDirty) || (this.isDocumentedEdited && !gotDirty)) {
|
||||
const hasDirtyFiles = this.workingCopyService.hasDirty;
|
||||
this.isDocumentedEdited = hasDirtyFiles;
|
||||
|
@ -275,6 +282,17 @@ export class ElectronWindow extends Disposable {
|
|||
}
|
||||
}));
|
||||
}
|
||||
|
||||
this._register(Event.any(
|
||||
Event.map(Event.filter(this.electronService.onWindowMaximize, id => id === this.electronEnvironmentService.windowId), () => true),
|
||||
Event.map(Event.filter(this.electronService.onWindowUnmaximize, id => id === this.electronEnvironmentService.windowId), () => false)
|
||||
)(e => this.onDidChangeMaximized(e)));
|
||||
|
||||
this.onDidChangeMaximized(this.environmentService.configuration.maximized ?? false);
|
||||
}
|
||||
|
||||
private onDidChangeMaximized(maximized: boolean): void {
|
||||
this.layoutService.updateWindowMaximizedState(maximized);
|
||||
}
|
||||
|
||||
private onDidVisibleEditorsChange(): void {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import * as nls from 'vs/nls';
|
||||
import { IWindowOpenable } from 'vs/platform/windows/common/windows';
|
||||
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, FileFilter } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, FileFilter, IFileDialogService, IDialogService, ConfirmResult, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
@ -20,8 +20,9 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
|
|||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { IOpenerService } from 'vs/platform/opener/common/opener';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
||||
export abstract class AbstractFileDialogService {
|
||||
export abstract class AbstractFileDialogService implements IFileDialogService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
|
@ -34,6 +35,7 @@ export abstract class AbstractFileDialogService {
|
|||
@IConfigurationService protected readonly configurationService: IConfigurationService,
|
||||
@IFileService protected readonly fileService: IFileService,
|
||||
@IOpenerService protected readonly openerService: IOpenerService,
|
||||
@IDialogService private readonly dialogService: IDialogService
|
||||
) { }
|
||||
|
||||
defaultFilePath(schemeFilter = this.getSchemeFilterForWindow()): URI | undefined {
|
||||
|
@ -78,6 +80,40 @@ export abstract class AbstractFileDialogService {
|
|||
return this.defaultFilePath(schemeFilter);
|
||||
}
|
||||
|
||||
async showSaveConfirm(fileNameOrResources: string | URI[]): Promise<ConfirmResult> {
|
||||
if (this.environmentService.isExtensionDevelopment) {
|
||||
return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests)
|
||||
}
|
||||
|
||||
if (Array.isArray(fileNameOrResources) && fileNameOrResources.length === 0) {
|
||||
return ConfirmResult.DONT_SAVE;
|
||||
}
|
||||
|
||||
let message: string;
|
||||
if (typeof fileNameOrResources === 'string' || fileNameOrResources.length === 1) {
|
||||
message = nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", typeof fileNameOrResources === 'string' ? fileNameOrResources : resources.basename(fileNameOrResources[0]));
|
||||
} else {
|
||||
message = getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", fileNameOrResources.length), fileNameOrResources);
|
||||
}
|
||||
|
||||
const buttons: string[] = [
|
||||
Array.isArray(fileNameOrResources) && fileNameOrResources.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"),
|
||||
nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
|
||||
const { choice } = await this.dialogService.show(Severity.Warning, message, buttons, {
|
||||
cancelId: 2,
|
||||
detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.")
|
||||
});
|
||||
|
||||
switch (choice) {
|
||||
case 0: return ConfirmResult.SAVE;
|
||||
case 1: return ConfirmResult.DONT_SAVE;
|
||||
default: return ConfirmResult.CANCEL;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract addFileSchemaIfNeeded(schema: string): string[];
|
||||
|
||||
protected async pickFileFolderAndOpenSimplified(schema: string, options: IPickAndOpenOptions, preferNewWindow: boolean): Promise<any> {
|
||||
|
@ -179,4 +215,12 @@ export abstract class AbstractFileDialogService {
|
|||
protected getFileSystemSchema(options: { availableFileSystems?: readonly string[], defaultUri?: URI }): string {
|
||||
return options.availableFileSystems && options.availableFileSystems[0] || this.getSchemeFilterForWindow();
|
||||
}
|
||||
|
||||
abstract pickFileFolderAndOpen(options: IPickAndOpenOptions): Promise<void>;
|
||||
abstract pickFileAndOpen(options: IPickAndOpenOptions): Promise<void>;
|
||||
abstract pickFolderAndOpen(options: IPickAndOpenOptions): Promise<void>;
|
||||
abstract pickWorkspaceAndOpen(options: IPickAndOpenOptions): Promise<void>;
|
||||
abstract pickFileToSave(options: ISaveDialogOptions): Promise<URI | undefined>;
|
||||
abstract showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined>;
|
||||
abstract showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined>;
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { SaveDialogOptions, OpenDialogOptions } from 'electron';
|
||||
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace';
|
||||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
|
@ -33,8 +33,11 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
|
|||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IFileService fileService: IFileService,
|
||||
@IOpenerService openerService: IOpenerService,
|
||||
@IElectronService private readonly electronService: IElectronService
|
||||
) { super(hostService, contextService, historyService, environmentService, instantiationService, configurationService, fileService, openerService); }
|
||||
@IElectronService private readonly electronService: IElectronService,
|
||||
@IDialogService dialogService: IDialogService
|
||||
) {
|
||||
super(hostService, contextService, historyService, environmentService, instantiationService, configurationService, fileService, openerService, dialogService);
|
||||
}
|
||||
|
||||
private toNativeOpenDialogOptions(options: IPickAndOpenOptions): INativeOpenDialogOptions {
|
||||
return {
|
||||
|
@ -180,6 +183,16 @@ export class FileDialogService extends AbstractFileDialogService implements IFil
|
|||
// Don't allow untitled schema through.
|
||||
return schema === Schemas.untitled ? [Schemas.file] : (schema !== Schemas.file ? [schema, Schemas.file] : [schema]);
|
||||
}
|
||||
|
||||
async showSaveConfirm(fileNameOrResources: string | URI[]): Promise<ConfirmResult> {
|
||||
if (this.environmentService.isExtensionDevelopment) {
|
||||
if (!this.environmentService.args['extension-development-confirm-save']) {
|
||||
return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests)
|
||||
}
|
||||
}
|
||||
|
||||
return super.showSaveConfirm(fileNameOrResources);
|
||||
}
|
||||
}
|
||||
|
||||
registerSingleton(IFileDialogService, FileDialogService, true);
|
||||
|
|
|
@ -623,7 +623,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
|
|||
|
||||
// Data URI
|
||||
else if (resource.scheme === Schemas.data) {
|
||||
input = instantiationService.createInstance(DataUriEditorInput, label, description, resource);
|
||||
input = instantiationService.createInstance(DataUriEditorInput, label || basename(resource), description, resource);
|
||||
}
|
||||
|
||||
// Resource
|
||||
|
|
|
@ -484,8 +484,8 @@ export class HistoryService extends Disposable implements IHistoryService {
|
|||
private handleEditorEventInHistory(editor?: IBaseEditor): void {
|
||||
const input = editor?.input;
|
||||
|
||||
// Ensure we have at least a name to show and not configured to exclude input
|
||||
if (!input || !input.getName() || !this.include(input)) {
|
||||
// Ensure we have not configured to exclude input
|
||||
if (!input || !this.include(input)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -41,6 +41,11 @@ export interface IWorkbenchLayoutService extends ILayoutService {
|
|||
*/
|
||||
readonly onFullscreenChange: Event<boolean>;
|
||||
|
||||
/**
|
||||
* Emits when the window is maximized or unmaximized.
|
||||
*/
|
||||
readonly onMaximizeChange: Event<boolean>;
|
||||
|
||||
/**
|
||||
* Emits when centered layout is enabled or disabled.
|
||||
*/
|
||||
|
@ -188,4 +193,15 @@ export interface IWorkbenchLayoutService extends ILayoutService {
|
|||
* Register a part to participate in the layout.
|
||||
*/
|
||||
registerPart(part: Part): void;
|
||||
|
||||
|
||||
/**
|
||||
* Returns whether the window is maximized.
|
||||
*/
|
||||
isWindowMaximized(): boolean;
|
||||
|
||||
/**
|
||||
* Updates the maximized state of the window.
|
||||
*/
|
||||
updateWindowMaximizedState(maximized: boolean): void;
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ export class PreferencesEditorInput extends SideBySideEditorInput {
|
|||
return PreferencesEditorInput.ID;
|
||||
}
|
||||
|
||||
getTitle(verbosity: Verbosity): string | undefined {
|
||||
getTitle(verbosity: Verbosity): string {
|
||||
return this.master.getTitle(verbosity);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ import { Emitter, AsyncEmitter } from 'vs/base/common/event';
|
|||
import * as platform from 'vs/base/common/platform';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IResult, ITextFileOperationResult, ITextFileService, ITextFileStreamContent, SaveReason, ITextFileEditorModelManager, ITextFileEditorModel, ModelState, ISaveOptions, ITextFileContent, IResourceEncodings, IReadTextFileOptions, IWriteTextFileOptions, toBufferOrReadable, TextFileOperationError, TextFileOperationResult, FileOperationWillRunEvent, FileOperationDidRunEvent } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ConfirmResult, IRevertOptions } from 'vs/workbench/common/editor';
|
||||
import { IRevertOptions } from 'vs/workbench/common/editor';
|
||||
import { ILifecycleService, ShutdownReason, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
|
||||
import { IFileService, FileOperationError, FileOperationResult, HotExitConfiguration, IFileStatWithMetadata, ICreateFileOptions, FileOperation } from 'vs/platform/files/common/files';
|
||||
|
@ -24,9 +24,9 @@ import { Schemas } from 'vs/base/common/network';
|
|||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { createTextBufferFactoryFromSnapshot, createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel';
|
||||
import { IModelService } from 'vs/editor/common/services/modelService';
|
||||
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { isEqualOrParent, isEqual, joinPath, dirname, extname, basename, toLocalResource } from 'vs/base/common/resources';
|
||||
import { getConfirmMessage, IDialogService, IFileDialogService, ISaveDialogOptions, IConfirmation } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IDialogService, IFileDialogService, ISaveDialogOptions, IConfirmation, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IModeService } from 'vs/editor/common/services/modeService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
|
@ -232,7 +232,7 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
|||
}
|
||||
|
||||
private async confirmBeforeShutdown(): Promise<boolean> {
|
||||
const confirm = await this.confirmSave();
|
||||
const confirm = await this.fileDialogService.showSaveConfirm(this.getDirty());
|
||||
|
||||
// Save
|
||||
if (confirm === ConfirmResult.SAVE) {
|
||||
|
@ -496,30 +496,6 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
|||
return result.results.length === 1 && !!result.results[0].success;
|
||||
}
|
||||
|
||||
async confirmSave(resources?: URI[]): Promise<ConfirmResult> {
|
||||
if (this.environmentService.isExtensionDevelopment) {
|
||||
return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests)
|
||||
}
|
||||
|
||||
const resourcesToConfirm = this.getDirty(resources);
|
||||
if (resourcesToConfirm.length === 0) {
|
||||
return ConfirmResult.DONT_SAVE;
|
||||
}
|
||||
|
||||
return promptSave(this.dialogService, resourcesToConfirm);
|
||||
}
|
||||
|
||||
async confirmOverwrite(resource: URI): Promise<boolean> {
|
||||
const confirm: IConfirmation = {
|
||||
message: nls.localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)),
|
||||
detail: nls.localize('irreversible', "A file or folder with the same name already exists in the folder {0}. Replacing it will overwrite its current contents.", basename(dirname(resource))),
|
||||
primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
|
||||
type: 'warning'
|
||||
};
|
||||
|
||||
return (await this.dialogService.confirm(confirm)).confirmed;
|
||||
}
|
||||
|
||||
saveAll(includeUntitled?: boolean, options?: ISaveOptions): Promise<ITextFileOperationResult>;
|
||||
saveAll(resources: URI[], options?: ISaveOptions): Promise<ITextFileOperationResult>;
|
||||
saveAll(arg1?: boolean | URI[], options?: ISaveOptions): Promise<ITextFileOperationResult> {
|
||||
|
@ -830,6 +806,17 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
|||
}
|
||||
}
|
||||
|
||||
private async confirmOverwrite(resource: URI): Promise<boolean> {
|
||||
const confirm: IConfirmation = {
|
||||
message: nls.localize('confirmOverwrite', "'{0}' already exists. Do you want to replace it?", basename(resource)),
|
||||
detail: nls.localize('irreversible', "A file or folder with the same name already exists in the folder {0}. Replacing it will overwrite its current contents.", basename(dirname(resource))),
|
||||
primaryButton: nls.localize({ key: 'replaceButtonLabel', comment: ['&& denotes a mnemonic'] }, "&&Replace"),
|
||||
type: 'warning'
|
||||
};
|
||||
|
||||
return (await this.dialogService.confirm(confirm)).confirmed;
|
||||
}
|
||||
|
||||
private suggestFileName(untitledResource: URI): URI {
|
||||
const untitledFileName = this.untitledTextEditorService.suggestFileName(untitledResource);
|
||||
const remoteAuthority = this.environmentService.configuration.remoteAuthority;
|
||||
|
@ -939,26 +926,3 @@ export abstract class AbstractTextFileService extends Disposable implements ITex
|
|||
super.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
export async function promptSave(dialogService: IDialogService, resourcesToConfirm: readonly URI[]) {
|
||||
const message = resourcesToConfirm.length === 1
|
||||
? nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", basename(resourcesToConfirm[0]))
|
||||
: getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", resourcesToConfirm.length), resourcesToConfirm);
|
||||
|
||||
const buttons: string[] = [
|
||||
resourcesToConfirm.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"),
|
||||
nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"),
|
||||
nls.localize('cancel', "Cancel")
|
||||
];
|
||||
|
||||
const { choice } = await dialogService.show(Severity.Warning, message, buttons, {
|
||||
cancelId: 2,
|
||||
detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.")
|
||||
});
|
||||
|
||||
switch (choice) {
|
||||
case 0: return ConfirmResult.SAVE;
|
||||
case 1: return ConfirmResult.DONT_SAVE;
|
||||
default: return ConfirmResult.CANCEL;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event, IWaitUntil } from 'vs/base/common/event';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IEncodingSupport, ConfirmResult, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor';
|
||||
import { IEncodingSupport, IRevertOptions, IModeSupport } from 'vs/workbench/common/editor';
|
||||
import { IBaseStatWithMetadata, IFileStatWithMetadata, IReadFileOptions, IWriteFileOptions, FileOperationError, FileOperationResult, FileOperation } from 'vs/platform/files/common/files';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ITextEditorModel } from 'vs/editor/common/services/resolverService';
|
||||
|
@ -129,17 +129,8 @@ export interface ITextFileService extends IDisposable {
|
|||
* Move a file. If the file is dirty, its contents will be preserved and restored.
|
||||
*/
|
||||
move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata>;
|
||||
|
||||
/**
|
||||
* Brings up the confirm dialog to either save, don't save or cancel.
|
||||
*
|
||||
* @param resources the resources of the files to ask for confirmation or null if
|
||||
* confirming for all dirty resources.
|
||||
*/
|
||||
confirmSave(resources?: URI[]): Promise<ConfirmResult>;
|
||||
}
|
||||
|
||||
|
||||
export class FileOperationWillRunEvent implements IWaitUntil {
|
||||
|
||||
constructor(
|
||||
|
@ -157,7 +148,6 @@ export class FileOperationWillRunEvent implements IWaitUntil {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
export class FileOperationDidRunEvent {
|
||||
|
||||
constructor(
|
||||
|
@ -167,7 +157,6 @@ export class FileOperationDidRunEvent {
|
|||
) { }
|
||||
}
|
||||
|
||||
|
||||
export interface IReadTextFileOptions extends IReadFileOptions {
|
||||
|
||||
/**
|
||||
|
|
|
@ -39,7 +39,6 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
|||
import { IHistoryService } from 'vs/workbench/services/history/common/history';
|
||||
import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { ConfirmResult } from 'vs/workbench/common/editor';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
|
||||
|
@ -287,7 +286,8 @@ export class NativeTextFileService extends AbstractTextFileService {
|
|||
private async sudoPromptCopy(source: string, target: string, options?: IWriteTextFileOptions): Promise<void> {
|
||||
|
||||
// load sudo-prompt module lazy
|
||||
const sudoPrompt = await import('sudo-prompt');
|
||||
// @ts-ignore TODO@ben wait for update of sudo-prompt
|
||||
const sudoPrompt: { exec(cmd: string, options: { name?: string, icns?: string }, callback: (error: string, stdout: string, stderr: string) => void): void } = await import('sudo-prompt');
|
||||
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const promptOptions = {
|
||||
|
@ -315,16 +315,6 @@ export class NativeTextFileService extends AbstractTextFileService {
|
|||
protected getWindowCount(): Promise<number> {
|
||||
return this.electronService.getWindowCount();
|
||||
}
|
||||
|
||||
async confirmSave(resources?: URI[]): Promise<ConfirmResult> {
|
||||
if (this.environmentService.isExtensionDevelopment) {
|
||||
if (!this.environmentService.args['extension-development-confirm-save']) {
|
||||
return ConfirmResult.DONT_SAVE; // no veto when we are in extension dev mode because we cannot assume we run interactive (e.g. tests)
|
||||
}
|
||||
}
|
||||
|
||||
return super.confirmSave(resources);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IEncodingOverride {
|
||||
|
|
|
@ -7,12 +7,11 @@ import * as sinon from 'sinon';
|
|||
import * as platform from 'vs/base/common/platform';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ILifecycleService, BeforeShutdownEvent, ShutdownReason } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestContextService, TestFileService, TestElectronService, TestFilesConfigurationService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { workbenchInstantiationService, TestLifecycleService, TestTextFileService, TestContextService, TestFileService, TestElectronService, TestFilesConfigurationService, TestFileDialogService } from 'vs/workbench/test/workbenchTestServices';
|
||||
import { toResource } from 'vs/base/test/common/utils';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
|
||||
import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
|
||||
import { ConfirmResult } from 'vs/workbench/common/editor';
|
||||
import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService';
|
||||
import { HotExitConfiguration, IFileService } from 'vs/platform/files/common/files';
|
||||
import { TextFileEditorModelManager } from 'vs/workbench/services/textfile/common/textFileEditorModelManager';
|
||||
|
@ -22,6 +21,7 @@ import { ModelServiceImpl } from 'vs/editor/common/services/modelServiceImpl';
|
|||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService';
|
||||
import { IFileDialogService, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
|
||||
class ServiceAccessor {
|
||||
constructor(
|
||||
|
@ -32,7 +32,8 @@ class ServiceAccessor {
|
|||
@IWorkspaceContextService public contextService: TestContextService,
|
||||
@IModelService public modelService: ModelServiceImpl,
|
||||
@IFileService public fileService: TestFileService,
|
||||
@IElectronService public electronService: TestElectronService
|
||||
@IElectronService public electronService: TestElectronService,
|
||||
@IFileDialogService public fileDialogService: TestFileDialogService
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +88,7 @@ suite('Files - TextFileService', () => {
|
|||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.resource, model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
service.setConfirmResult(ConfirmResult.CANCEL);
|
||||
accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL);
|
||||
|
||||
await model.load();
|
||||
model.textEditorModel!.setValue('foo');
|
||||
|
@ -103,7 +104,7 @@ suite('Files - TextFileService', () => {
|
|||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.resource, model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
service.setConfirmResult(ConfirmResult.DONT_SAVE);
|
||||
accessor.fileDialogService.setConfirmResult(ConfirmResult.DONT_SAVE);
|
||||
accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } });
|
||||
|
||||
await model.load();
|
||||
|
@ -129,7 +130,7 @@ suite('Files - TextFileService', () => {
|
|||
(<TextFileEditorModelManager>accessor.textFileService.models).add(model.resource, model);
|
||||
|
||||
const service = accessor.textFileService;
|
||||
service.setConfirmResult(ConfirmResult.SAVE);
|
||||
accessor.fileDialogService.setConfirmResult(ConfirmResult.SAVE);
|
||||
accessor.filesConfigurationService.onFilesConfigurationChange({ files: { hotExit: 'off' } });
|
||||
|
||||
await model.load();
|
||||
|
@ -429,7 +430,7 @@ suite('Files - TextFileService', () => {
|
|||
accessor.electronService.windowCount = Promise.resolve(2);
|
||||
}
|
||||
// Set cancel to force a veto if hot exit does not trigger
|
||||
service.setConfirmResult(ConfirmResult.CANCEL);
|
||||
accessor.fileDialogService.setConfirmResult(ConfirmResult.CANCEL);
|
||||
|
||||
await model.load();
|
||||
model.textEditorModel!.setValue('foo');
|
||||
|
|
|
@ -50,6 +50,8 @@ export interface IWorkingCopyService {
|
|||
|
||||
isDirty(resource: URI): boolean;
|
||||
|
||||
getDirty(...resources: URI[]): IWorkingCopy[];
|
||||
|
||||
//#endregion
|
||||
|
||||
|
||||
|
@ -69,6 +71,34 @@ export class WorkingCopyService extends Disposable implements IWorkingCopyServic
|
|||
private readonly _onDidChangeDirty = this._register(new Emitter<IWorkingCopy>());
|
||||
readonly onDidChangeDirty = this._onDidChangeDirty.event;
|
||||
|
||||
getDirty(...resources: URI[]): IWorkingCopy[] {
|
||||
const dirtyWorkingCopies: IWorkingCopy[] = [];
|
||||
|
||||
// Specific resource(s)
|
||||
if (resources.length > 0) {
|
||||
for (const resource of resources) {
|
||||
this.fillDirty(this.mapResourceToWorkingCopy.get(resource.toString()), dirtyWorkingCopies);
|
||||
}
|
||||
}
|
||||
|
||||
// All resources
|
||||
else {
|
||||
this.fillDirty(this.workingCopies, dirtyWorkingCopies);
|
||||
}
|
||||
|
||||
return dirtyWorkingCopies;
|
||||
}
|
||||
|
||||
private fillDirty(workingCopies: Set<IWorkingCopy> | undefined, target: IWorkingCopy[]): void {
|
||||
if (workingCopies) {
|
||||
for (const workingCopy of workingCopies) {
|
||||
if (workingCopy.isDirty()) {
|
||||
target.push(workingCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isDirty(resource: URI): boolean {
|
||||
const workingCopies = this.mapResourceToWorkingCopy.get(resource.toString());
|
||||
if (workingCopies) {
|
||||
|
|
|
@ -56,6 +56,8 @@ suite('WorkingCopyService', () => {
|
|||
|
||||
assert.equal(service.hasDirty, false);
|
||||
assert.equal(service.dirtyCount, 0);
|
||||
assert.equal(service.getDirty().length, 0);
|
||||
assert.equal(service.getDirty(URI.file('/'), URI.file('/some')).length, 0);
|
||||
assert.equal(service.isDirty(URI.file('/')), false);
|
||||
|
||||
// resource 1
|
||||
|
@ -66,6 +68,8 @@ suite('WorkingCopyService', () => {
|
|||
assert.equal(service.dirtyCount, 0);
|
||||
assert.equal(service.isDirty(resource1), false);
|
||||
assert.equal(service.hasDirty, false);
|
||||
assert.equal(service.getDirty(resource1).length, 0);
|
||||
assert.equal(service.getDirty().length, 0);
|
||||
|
||||
copy1.setDirty(true);
|
||||
|
||||
|
@ -74,6 +78,8 @@ suite('WorkingCopyService', () => {
|
|||
assert.equal(service.hasDirty, true);
|
||||
assert.equal(onDidChangeDirty.length, 1);
|
||||
assert.equal(onDidChangeDirty[0], copy1);
|
||||
assert.equal(service.getDirty(resource1).length, 1);
|
||||
assert.equal(service.getDirty().length, 1);
|
||||
|
||||
copy1.setDirty(false);
|
||||
|
||||
|
@ -82,6 +88,8 @@ suite('WorkingCopyService', () => {
|
|||
assert.equal(service.hasDirty, false);
|
||||
assert.equal(onDidChangeDirty.length, 2);
|
||||
assert.equal(onDidChangeDirty[1], copy1);
|
||||
assert.equal(service.getDirty(resource1).length, 0);
|
||||
assert.equal(service.getDirty().length, 0);
|
||||
|
||||
unregister1.dispose();
|
||||
|
||||
|
@ -93,6 +101,8 @@ suite('WorkingCopyService', () => {
|
|||
assert.equal(service.dirtyCount, 1);
|
||||
assert.equal(service.isDirty(resource2), true);
|
||||
assert.equal(service.hasDirty, true);
|
||||
assert.equal(service.getDirty(resource1, resource2).length, 1);
|
||||
assert.equal(service.getDirty().length, 1);
|
||||
|
||||
assert.equal(onDidChangeDirty.length, 3);
|
||||
assert.equal(onDidChangeDirty[2], copy2);
|
||||
|
@ -102,6 +112,8 @@ suite('WorkingCopyService', () => {
|
|||
assert.equal(service.hasDirty, false);
|
||||
assert.equal(onDidChangeDirty.length, 4);
|
||||
assert.equal(onDidChangeDirty[3], copy2);
|
||||
assert.equal(service.getDirty(resource1, resource2).length, 0);
|
||||
assert.equal(service.getDirty().length, 0);
|
||||
});
|
||||
|
||||
test('registry - multiple copies on same resource', () => {
|
||||
|
@ -123,23 +135,31 @@ suite('WorkingCopyService', () => {
|
|||
assert.equal(service.dirtyCount, 1);
|
||||
assert.equal(onDidChangeDirty.length, 1);
|
||||
assert.equal(service.isDirty(resource), true);
|
||||
assert.equal(service.getDirty(resource).length, 1);
|
||||
assert.equal(service.getDirty().length, 1);
|
||||
|
||||
copy2.setDirty(true);
|
||||
|
||||
assert.equal(service.dirtyCount, 2);
|
||||
assert.equal(onDidChangeDirty.length, 2);
|
||||
assert.equal(service.isDirty(resource), true);
|
||||
assert.equal(service.getDirty(resource).length, 2);
|
||||
assert.equal(service.getDirty().length, 2);
|
||||
|
||||
unregister1.dispose();
|
||||
|
||||
assert.equal(service.dirtyCount, 1);
|
||||
assert.equal(onDidChangeDirty.length, 3);
|
||||
assert.equal(service.isDirty(resource), true);
|
||||
assert.equal(service.getDirty(resource).length, 1);
|
||||
assert.equal(service.getDirty().length, 1);
|
||||
|
||||
unregister2.dispose();
|
||||
|
||||
assert.equal(service.dirtyCount, 0);
|
||||
assert.equal(onDidChangeDirty.length, 4);
|
||||
assert.equal(service.isDirty(resource), false);
|
||||
assert.equal(service.getDirty(resource).length, 0);
|
||||
assert.equal(service.getDirty().length, 0);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,4 +31,4 @@ suite('DataUriEditorInput', () => {
|
|||
assert.equal(model.getMime(), 'image/png');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,7 +22,7 @@ suite('Workbench editor input', () => {
|
|||
assert(input.matches(input));
|
||||
assert(!input.matches(otherInput));
|
||||
assert(!input.matches(null));
|
||||
assert(!input.getName());
|
||||
assert(input.getName());
|
||||
|
||||
input.onDispose(() => {
|
||||
assert(true);
|
||||
|
@ -84,4 +84,4 @@ suite('Workbench editor input', () => {
|
|||
otherInput.dispose();
|
||||
assert.equal(counter, 2);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -527,6 +527,25 @@ suite('ExtHostTypes', function () {
|
|||
string.appendVariable('BAR', b => { });
|
||||
assert.equal(string.value, '${BAR}');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendChoice(['b', 'a', 'r']);
|
||||
assert.equal(string.value, '${1|b,a,r|}');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendChoice(['b', 'a', 'r'], 0);
|
||||
assert.equal(string.value, '${0|b,a,r|}');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendText('foo').appendChoice(['far', 'boo']).appendText('bar');
|
||||
assert.equal(string.value, 'foo${1|far,boo|}bar');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendText('foo').appendChoice(['far', '$boo']).appendText('bar');
|
||||
assert.equal(string.value, 'foo${1|far,\\$boo|}bar');
|
||||
|
||||
string = new types.SnippetString();
|
||||
string.appendText('foo').appendPlaceholder('farboo').appendChoice(['far', 'boo']).appendText('bar');
|
||||
assert.equal(string.value, 'foo${1:farboo}${2|far,boo|}bar');
|
||||
});
|
||||
|
||||
test('instanceof doesn\'t work for FileSystemError #49386', function () {
|
||||
|
|
|
@ -11,7 +11,7 @@ import * as resources from 'vs/base/common/resources';
|
|||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils';
|
||||
import { ConfirmResult, IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions } from 'vs/workbench/common/editor';
|
||||
import { IEditorInputWithOptions, CloseDirection, IEditorIdentifier, IUntitledTextResourceInput, IResourceDiffInput, IResourceSideBySideInput, IEditorInput, IEditor, IEditorCloseEvent, IEditorPartOptions } from 'vs/workbench/common/editor';
|
||||
import { IEditorOpeningEvent, EditorServiceImpl, IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import Severity from 'vs/base/common/severity';
|
||||
|
@ -49,7 +49,7 @@ import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
|||
import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService';
|
||||
import { ITextBufferFactory, DefaultEndOfLine, EndOfLinePreference, IModelDecorationOptions, ITextModel, ITextSnapshot } from 'vs/editor/common/model';
|
||||
import { Range } from 'vs/editor/common/core/range';
|
||||
import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IShowResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { IConfirmation, IConfirmationResult, IDialogService, IDialogOptions, IPickAndOpenOptions, ISaveDialogOptions, IOpenDialogOptions, IFileDialogService, IShowResult, ConfirmResult } from 'vs/platform/dialogs/common/dialogs';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { TestNotificationService } from 'vs/platform/notification/test/common/testNotificationService';
|
||||
import { IExtensionService, NullExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
|
@ -192,7 +192,6 @@ export class TestTextFileService extends NativeTextFileService {
|
|||
public cleanupBackupsBeforeShutdownCalled!: boolean;
|
||||
|
||||
private promptPath!: URI;
|
||||
private confirmResult!: ConfirmResult;
|
||||
private resolveTextContentError!: FileOperationError | null;
|
||||
|
||||
constructor(
|
||||
|
@ -241,10 +240,6 @@ export class TestTextFileService extends NativeTextFileService {
|
|||
this.promptPath = path;
|
||||
}
|
||||
|
||||
public setConfirmResult(result: ConfirmResult): void {
|
||||
this.confirmResult = result;
|
||||
}
|
||||
|
||||
public setResolveTextContentErrorOnce(error: FileOperationError): void {
|
||||
this.resolveTextContentError = error;
|
||||
}
|
||||
|
@ -275,14 +270,6 @@ export class TestTextFileService extends NativeTextFileService {
|
|||
return Promise.resolve(this.promptPath);
|
||||
}
|
||||
|
||||
public confirmSave(_resources?: URI[]): Promise<ConfirmResult> {
|
||||
return Promise.resolve(this.confirmResult);
|
||||
}
|
||||
|
||||
public confirmOverwrite(_resource: URI): Promise<boolean> {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
protected cleanupBackupsBeforeShutdown(): Promise<void> {
|
||||
this.cleanupBackupsBeforeShutdownCalled = true;
|
||||
return Promise.resolve();
|
||||
|
@ -303,6 +290,8 @@ export function workbenchInstantiationService(): IInstantiationService {
|
|||
instantiationService.stub(IUntitledTextEditorService, instantiationService.createInstance(UntitledTextEditorService));
|
||||
instantiationService.stub(IStorageService, new TestStorageService());
|
||||
instantiationService.stub(IWorkbenchLayoutService, new TestLayoutService());
|
||||
instantiationService.stub(IDialogService, new TestDialogService());
|
||||
instantiationService.stub(IFileDialogService, new TestFileDialogService());
|
||||
instantiationService.stub(IElectronService, new TestElectronService());
|
||||
instantiationService.stub(IModeService, instantiationService.createInstance(ModeServiceImpl));
|
||||
instantiationService.stub(IHistoryService, new TestHistoryService());
|
||||
|
@ -420,6 +409,8 @@ export class TestFileDialogService implements IFileDialogService {
|
|||
|
||||
public _serviceBrand: undefined;
|
||||
|
||||
private confirmResult!: ConfirmResult;
|
||||
|
||||
public defaultFilePath(_schemeFilter?: string): URI | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -450,6 +441,12 @@ export class TestFileDialogService implements IFileDialogService {
|
|||
public showOpenDialog(_options: IOpenDialogOptions): Promise<URI[] | undefined> {
|
||||
return Promise.resolve(undefined);
|
||||
}
|
||||
public setConfirmResult(result: ConfirmResult): void {
|
||||
this.confirmResult = result;
|
||||
}
|
||||
public showSaveConfirm(resources: string | URI[]): Promise<ConfirmResult> {
|
||||
return Promise.resolve(this.confirmResult);
|
||||
}
|
||||
}
|
||||
|
||||
export class TestLayoutService implements IWorkbenchLayoutService {
|
||||
|
@ -463,6 +460,7 @@ export class TestLayoutService implements IWorkbenchLayoutService {
|
|||
onZenModeChange: Event<boolean> = Event.None;
|
||||
onCenteredLayoutChange: Event<boolean> = Event.None;
|
||||
onFullscreenChange: Event<boolean> = Event.None;
|
||||
onMaximizeChange: Event<boolean> = Event.None;
|
||||
onPanelPositionChange: Event<string> = Event.None;
|
||||
onPartVisibilityChange: Event<void> = Event.None;
|
||||
onLayout = Event.None;
|
||||
|
@ -572,6 +570,12 @@ export class TestLayoutService implements IWorkbenchLayoutService {
|
|||
public resizePart(_part: Parts, _sizeChange: number): void { }
|
||||
|
||||
public registerPart(part: Part): void { }
|
||||
|
||||
isWindowMaximized() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public updateWindowMaximizedState(maximized: boolean): void { }
|
||||
}
|
||||
|
||||
let activeViewlet: Viewlet = {} as any;
|
||||
|
|
16
yarn.lock
16
yarn.lock
|
@ -6068,10 +6068,10 @@ onetime@^2.0.0:
|
|||
dependencies:
|
||||
mimic-fn "^1.0.0"
|
||||
|
||||
onigasm-umd@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257"
|
||||
integrity sha512-v2eMOJu7iE444L2iJN+U6s6s5S0y7oj/N0DAkrd6wokRtTVoq/v/yaDI1lIqFrTeJbNtqNzYvguDF5yNzW3Rvw==
|
||||
onigasm-umd@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.4.tgz#27ee87f7496c66ad40cebfbc0d418c19bb7db5ec"
|
||||
integrity sha512-N9VqCUhl9KBuzm47vcK8T/xUnbYylIhMN45Rwltlo1sZc3QUDda6SxIlyVB8r0SJQwURv8JOHjyXjjCriGvzRg==
|
||||
|
||||
oniguruma@^7.2.0:
|
||||
version "7.2.0"
|
||||
|
@ -8150,10 +8150,10 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1:
|
|||
resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a"
|
||||
integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo=
|
||||
|
||||
sudo-prompt@9.0.0:
|
||||
version "9.0.0"
|
||||
resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.0.0.tgz#eebedeee9fcd6f661324e6bb46335e3288e8dc8a"
|
||||
integrity sha512-kUn5fiOk0nhY2oKD9onIkcNCE4Zt85WTsvOfSmqCplmlEvXCcPOmp1npH5YWuf8Bmyy9wLWkIxx+D+8cThBORQ==
|
||||
sudo-prompt@9.1.0:
|
||||
version "9.1.0"
|
||||
resolved "https://registry.yarnpkg.com/sudo-prompt/-/sudo-prompt-9.1.0.tgz#9618823e748ce19e2d9e481feaf3ada7d52df52f"
|
||||
integrity sha512-bJigY3ELFd2ZA7gfyQ4wMZIp1EICPFQcMe3RgSz5OQTzrPPaeryhgaxbInO/G62vpiqJs37qlGdb9TaqHeF2yA==
|
||||
|
||||
supports-color@1.2.0:
|
||||
version "1.2.0"
|
||||
|
|
Loading…
Reference in a new issue