Merge from vscode e558dc6ea73a75bd69d7a0b485f0e7e4194c66bf (#6864)

This commit is contained in:
Anthony Dresser
2019-08-21 20:44:59 -07:00
committed by GitHub
parent d2ae0f0154
commit 985bfae8a0
107 changed files with 2260 additions and 814 deletions

View File

@@ -0,0 +1,37 @@
trigger:
branches:
include: ['master']
pr:
branches:
include: ['master']
steps:
- task: NodeTool@0
inputs:
versionSpec: "10.15.1"
- task: AzureKeyVault@1
displayName: 'Azure Key Vault: Get Secrets'
inputs:
azureSubscription: 'vscode-builds-subscription'
KeyVaultName: vscode
- script: |
set -e
cat << EOF > ~/.netrc
machine github.com
login vscode
password $(github-distro-mixin-password)
EOF
git config user.email "vscode@microsoft.com"
git config user.name "VSCode"
git checkout origin/ben/electron-test
git merge origin/master
# Push master branch into exploration branch
git push origin HEAD:ben/electron-test
displayName: Sync & Merge Exploration

View File

@@ -19,18 +19,23 @@ class AbstractGlobalsRuleWalker extends Lint.RuleWalker {
const checker = this.program.getTypeChecker();
const symbol = checker.getSymbolAtLocation(node);
if (symbol) {
const valueDeclaration = symbol.valueDeclaration;
if (valueDeclaration) {
const parent = valueDeclaration.parent;
if (parent) {
const sourceFile = parent.getSourceFile();
if (sourceFile) {
const fileName = sourceFile.fileName;
if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) {
this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`);
const declarations = symbol.declarations;
if (Array.isArray(declarations) && symbol.declarations.some(declaration => {
if (declaration) {
const parent = declaration.parent;
if (parent) {
const sourceFile = parent.getSourceFile();
if (sourceFile) {
const fileName = sourceFile.fileName;
if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) {
return true;
}
}
}
}
return false;
})) {
this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`);
}
}
}

View File

@@ -30,18 +30,24 @@ export abstract class AbstractGlobalsRuleWalker extends Lint.RuleWalker {
const checker = this.program.getTypeChecker();
const symbol = checker.getSymbolAtLocation(node);
if (symbol) {
const valueDeclaration = symbol.valueDeclaration;
if (valueDeclaration) {
const parent = valueDeclaration.parent;
if (parent) {
const sourceFile = parent.getSourceFile();
if (sourceFile) {
const fileName = sourceFile.fileName;
if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) {
this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`);
const declarations = symbol.declarations;
if (Array.isArray(declarations) && symbol.declarations.some(declaration => {
if (declaration) {
const parent = declaration.parent;
if (parent) {
const sourceFile = parent.getSourceFile();
if (sourceFile) {
const fileName = sourceFile.fileName;
if (fileName && fileName.indexOf(this.getDefinitionPattern()) >= 0) {
return true;
}
}
}
}
return false;
})) {
this.addFailureAtNode(node, `Cannot use global '${node.text}' in '${this._config.target}'`);
}
}
}

View File

@@ -18,7 +18,7 @@
"watch": "gulp watch-extension:configuration-editing"
},
"dependencies": {
"jsonc-parser": "2.0.2",
"jsonc-parser": "^2.1.1",
"vscode-nls": "^4.0.0"
},
"contributes": {

View File

@@ -7,10 +7,10 @@
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.14.8.tgz#fe444203ecef1162348cd6deb76c62477b2cc6e9"
integrity sha512-I4+DbJEhLEg4/vIy/2gkWDvXBOOtPKV9EnLhYjMoqxcRW+TTZtUftkHktz/a8suoD5mUL7m6ReLrkPvSsCQQmw==
jsonc-parser@2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d"
integrity sha512-TSU435K5tEKh3g7bam1AFf+uZrISheoDsLlpmAo6wWZYqjsnd09lHYK1Qo+moK4Ikifev1Gdpa69g4NELKnCrQ==
jsonc-parser@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e"
integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g==
vscode-nls@^4.0.0:
version "4.0.0"

View File

@@ -19,7 +19,7 @@
"watch": "gulp watch-extension:extension-editing"
},
"dependencies": {
"jsonc-parser": "^2.0.2",
"jsonc-parser": "^2.1.1",
"markdown-it": "^8.3.1",
"parse5": "^3.0.2",
"vscode-nls": "^4.0.0"

View File

@@ -29,10 +29,10 @@ entities@~1.1.1:
resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0"
integrity sha1-blwtClYhtdra7O+AuQ7ftc13cvA=
jsonc-parser@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.0.2.tgz#42fcf56d70852a043fadafde51ddb4a85649978d"
integrity sha512-TSU435K5tEKh3g7bam1AFf+uZrISheoDsLlpmAo6wWZYqjsnd09lHYK1Qo+moK4Ikifev1Gdpa69g4NELKnCrQ==
jsonc-parser@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e"
integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g==
linkify-it@^2.0.0:
version "2.0.3"

View File

@@ -145,11 +145,14 @@ export function activate(context: ExtensionContext) {
}
});
const schemaDocuments: { [uri: string]: boolean } = {};
// handle content request
client.onRequest(VSCodeContentRequest.type, (uriPath: string) => {
let uri = Uri.parse(uriPath);
if (uri.scheme !== 'http' && uri.scheme !== 'https') {
return workspace.openTextDocument(uri).then(doc => {
schemaDocuments[uri.toString()] = true;
return doc.getText();
}, error => {
return Promise.reject(error);
@@ -164,10 +167,12 @@ export function activate(context: ExtensionContext) {
}
});
let handleContentChange = (uri: Uri) => {
if (uri.scheme === 'vscode' && uri.authority === 'schemas') {
client.sendNotification(SchemaContentChangeNotification.type, uri.toString());
let handleContentChange = (uriString: string) => {
if (schemaDocuments[uriString]) {
client.sendNotification(SchemaContentChangeNotification.type, uriString);
return true;
}
return false;
};
let handleActiveEditorChange = (activeEditor?: TextEditor) => {
@@ -184,10 +189,13 @@ export function activate(context: ExtensionContext) {
}
};
toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri)));
toDispose.push(workspace.onDidChangeTextDocument(e => handleContentChange(e.document.uri.toString())));
toDispose.push(workspace.onDidCloseTextDocument(d => {
handleContentChange(d.uri);
fileSchemaErrors.delete(d.uri.toString());
const uriString = d.uri.toString();
if (handleContentChange(uriString)) {
delete schemaDocuments[uriString];
}
fileSchemaErrors.delete(uriString);
}));
toDispose.push(window.onDidChangeActiveTextEditor(handleActiveEditorChange));

View File

@@ -12,7 +12,7 @@
},
"main": "./out/jsonServerMain",
"dependencies": {
"jsonc-parser": "^2.1.0",
"jsonc-parser": "^2.1.1",
"request-light": "^0.2.4",
"vscode-json-languageservice": "^3.3.1",
"vscode-languageserver": "^5.3.0-next.8",

View File

@@ -59,6 +59,11 @@ jsonc-parser@^2.1.0:
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.0.tgz#eb0d0c7a3c33048524ce3574c57c7278fb2f1bf3"
integrity sha512-n9GrT8rrr2fhvBbANa1g+xFmgGK5X91KFeDwlKQ3+SJfmH5+tKv/M/kahx/TXOMflfWHKGKqKyfHQaLKTNzJ6w==
jsonc-parser@^2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-2.1.1.tgz#83dc3d7a6e7186346b889b1280eefa04446c6d3e"
integrity sha512-VC0CjnWJylKB1iov4u76/W/5Ef0ydDkjtYWxoZ9t3HdWlSnZQwZL5MgFikaB/EtQ4RmMEw3tmQzuYnZA2/Ja1g==
ms@2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"

View File

@@ -320,7 +320,7 @@
},
"dependencies": {
"highlight.js": "9.15.8",
"markdown-it": "^8.4.2",
"markdown-it": "^9.1.0",
"markdown-it-front-matter": "^0.1.2",
"vscode-extension-telemetry": "0.1.1",
"vscode-nls": "^4.0.0"

View File

@@ -3900,10 +3900,10 @@ markdown-it-front-matter@^0.1.2:
resolved "https://registry.yarnpkg.com/markdown-it-front-matter/-/markdown-it-front-matter-0.1.2.tgz#e50bf56e77e6a4f5ac4ffa894d4d45ccd9896b20"
integrity sha1-5Qv1bnfmpPWsT/qJTU1FzNmJayA=
markdown-it@^8.4.2:
version "8.4.2"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-8.4.2.tgz#386f98998dc15a37722aa7722084f4020bdd9b54"
integrity sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ==
markdown-it@^9.1.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/markdown-it/-/markdown-it-9.1.0.tgz#df9601c168568704d554b1fff9af0c5b561168d9"
integrity sha512-xHKG4C8iPriyfu/jc2hsCC045fKrMQ0VexX2F1FGYiRxDxqMB2aAhF8WauJ3fltn2kb90moGBkiiEdooGIg55w==
dependencies:
argparse "^1.0.7"
entities "~1.1.1"

View File

@@ -33,6 +33,7 @@ set VSCODE_CLI=1
set ELECTRON_DEFAULT_ERROR_MODE=1
set ELECTRON_ENABLE_LOGGING=1
set ELECTRON_ENABLE_STACK_DUMPING=1
set VSCODE_LOGS=
:: Launch Code

View File

@@ -22,8 +22,12 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati
import { IFileService } from 'vs/platform/files/common/files';
import * as pfs from 'vs/base/node/pfs';
import { getRandomTestPath } from 'vs/base/test/node/testUtils';
import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
class TestEnvironmentService implements IWorkbenchEnvironmentService {
logFile: URI;
options?: IWorkbenchConstructionOptions;
galleryMachineIdResource?: URI;
webviewCspSource: string;
webviewCspRule: string;
localeResource: URI;

View File

@@ -1186,22 +1186,14 @@ export function animate(fn: () => void): IDisposable {
return toDisposable(() => stepDisposable.dispose());
}
const _location = URI.parse(window.location.href);
RemoteAuthorities.setPreferredWebSchema(/^https:/.test(window.location.href) ? 'https' : 'http');
export function asDomUri(uri: URI): URI {
if (!uri) {
return uri;
}
if (Schemas.vscodeRemote === uri.scheme) {
if (platform.isWeb) {
// rewrite vscode-remote-uris to uris of the window location
// so that they can be intercepted by the service worker
return _location.with({ path: '/vscode-remote', query: JSON.stringify(uri) });
} else {
return RemoteAuthorities.rewrite(uri.authority, uri.path);
}
return RemoteAuthorities.rewrite(uri.authority, uri.path);
}
return uri;
}

View File

@@ -197,9 +197,11 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
allowedAttributes: {
'a': ['href', 'name', 'target', 'data-href'],
'iframe': ['allowfullscreen', 'frameborder', 'src'],
'img': ['src', 'title', 'alt', 'width', 'height']
'img': ['src', 'title', 'alt', 'width', 'height'],
'div': ['class', 'data-code']
}
});
signalInnerHTML!();
return element;

View File

@@ -24,6 +24,8 @@ export interface IFindInputOptions extends IFindInputStyles {
readonly validation?: IInputValidator;
readonly label: string;
readonly flexibleHeight?: boolean;
readonly flexibleWidth?: boolean;
readonly flexibleMaxHeight?: number;
readonly appendCaseSensitiveLabel?: string;
readonly appendWholeWordsLabel?: string;
@@ -119,6 +121,8 @@ export class FindInput extends Widget {
const appendRegexLabel = options.appendRegexLabel || '';
const history = options.history || [];
const flexibleHeight = !!options.flexibleHeight;
const flexibleWidth = !!options.flexibleWidth;
const flexibleMaxHeight = options.flexibleMaxHeight;
this.domNode = document.createElement('div');
dom.addClass(this.domNode, 'monaco-findInput');
@@ -142,7 +146,9 @@ export class FindInput extends Widget {
inputValidationErrorForeground: this.inputValidationErrorForeground,
inputValidationErrorBorder: this.inputValidationErrorBorder,
history,
flexibleHeight
flexibleHeight,
flexibleWidth,
flexibleMaxHeight
}));
this.regex = this._register(new RegexCheckbox({
@@ -194,11 +200,7 @@ export class FindInput extends Widget {
}));
if (this._showOptionButtons) {
const paddingRight = (this.caseSensitive.width() + this.wholeWords.width() + this.regex.width()) + 'px';
this.inputBox.inputElement.style.paddingRight = paddingRight;
if (this.inputBox.mirrorElement) {
this.inputBox.mirrorElement.style.paddingRight = paddingRight;
}
this.inputBox.paddingRight = this.caseSensitive.width() + this.wholeWords.width() + this.regex.width();
}
// Arrow-Key support to navigate between options

View File

@@ -0,0 +1,373 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import 'vs/css!./findInput';
import * as nls from 'vs/nls';
import * as dom from 'vs/base/browser/dom';
import { IMessage as InputBoxMessage, IInputValidator, IInputBoxStyles, HistoryInputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { Widget } from 'vs/base/browser/ui/widget';
import { Event, Emitter } from 'vs/base/common/event';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { KeyCode } from 'vs/base/common/keyCodes';
import { Color } from 'vs/base/common/color';
import { ICheckboxStyles, Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { IFindInputCheckboxOpts } from 'vs/base/browser/ui/findinput/findInputCheckboxes';
export interface IReplaceInputOptions extends IReplaceInputStyles {
readonly placeholder?: string;
readonly width?: number;
readonly validation?: IInputValidator;
readonly label: string;
readonly flexibleHeight?: boolean;
readonly flexibleWidth?: boolean;
readonly flexibleMaxHeight?: number;
readonly history?: string[];
}
export interface IReplaceInputStyles extends IInputBoxStyles {
inputActiveOptionBorder?: Color;
}
const NLS_DEFAULT_LABEL = nls.localize('defaultLabel', "input");
const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseCheckbox', "Preserve Case");
export class PreserveCaseCheckbox extends Checkbox {
constructor(opts: IFindInputCheckboxOpts) {
super({
// TODO: does this need its own icon?
actionClassName: 'monaco-case-sensitive',
title: NLS_PRESERVE_CASE_LABEL + opts.appendTitle,
isChecked: opts.isChecked,
inputActiveOptionBorder: opts.inputActiveOptionBorder
});
}
}
export class ReplaceInput extends Widget {
static readonly OPTION_CHANGE: string = 'optionChange';
private contextViewProvider: IContextViewProvider | undefined;
private placeholder: string;
private validation?: IInputValidator;
private label: string;
private fixFocusOnOptionClickEnabled = true;
private inputActiveOptionBorder?: Color;
private inputBackground?: Color;
private inputForeground?: Color;
private inputBorder?: Color;
private inputValidationInfoBorder?: Color;
private inputValidationInfoBackground?: Color;
private inputValidationInfoForeground?: Color;
private inputValidationWarningBorder?: Color;
private inputValidationWarningBackground?: Color;
private inputValidationWarningForeground?: Color;
private inputValidationErrorBorder?: Color;
private inputValidationErrorBackground?: Color;
private inputValidationErrorForeground?: Color;
private preserveCase: PreserveCaseCheckbox;
private cachedOptionsWidth: number = 0;
public domNode: HTMLElement;
public inputBox: HistoryInputBox;
private readonly _onDidOptionChange = this._register(new Emitter<boolean>());
public readonly onDidOptionChange: Event<boolean /* via keyboard */> = this._onDidOptionChange.event;
private readonly _onKeyDown = this._register(new Emitter<IKeyboardEvent>());
public readonly onKeyDown: Event<IKeyboardEvent> = this._onKeyDown.event;
private readonly _onMouseDown = this._register(new Emitter<IMouseEvent>());
public readonly onMouseDown: Event<IMouseEvent> = this._onMouseDown.event;
private readonly _onInput = this._register(new Emitter<void>());
public readonly onInput: Event<void> = this._onInput.event;
private readonly _onKeyUp = this._register(new Emitter<IKeyboardEvent>());
public readonly onKeyUp: Event<IKeyboardEvent> = this._onKeyUp.event;
private _onPreserveCaseKeyDown = this._register(new Emitter<IKeyboardEvent>());
public readonly onPreserveCaseKeyDown: Event<IKeyboardEvent> = this._onPreserveCaseKeyDown.event;
constructor(parent: HTMLElement | null, contextViewProvider: IContextViewProvider | undefined, private readonly _showOptionButtons: boolean, options: IReplaceInputOptions) {
super();
this.contextViewProvider = contextViewProvider;
this.placeholder = options.placeholder || '';
this.validation = options.validation;
this.label = options.label || NLS_DEFAULT_LABEL;
this.inputActiveOptionBorder = options.inputActiveOptionBorder;
this.inputBackground = options.inputBackground;
this.inputForeground = options.inputForeground;
this.inputBorder = options.inputBorder;
this.inputValidationInfoBorder = options.inputValidationInfoBorder;
this.inputValidationInfoBackground = options.inputValidationInfoBackground;
this.inputValidationInfoForeground = options.inputValidationInfoForeground;
this.inputValidationWarningBorder = options.inputValidationWarningBorder;
this.inputValidationWarningBackground = options.inputValidationWarningBackground;
this.inputValidationWarningForeground = options.inputValidationWarningForeground;
this.inputValidationErrorBorder = options.inputValidationErrorBorder;
this.inputValidationErrorBackground = options.inputValidationErrorBackground;
this.inputValidationErrorForeground = options.inputValidationErrorForeground;
const flexibleHeight = !!options.flexibleHeight;
const flexibleWidth = !!options.flexibleWidth;
const flexibleMaxHeight = options.flexibleMaxHeight;
this.buildDomNode(options.history || [], flexibleHeight, flexibleWidth, flexibleMaxHeight);
if (parent) {
parent.appendChild(this.domNode);
}
this.onkeydown(this.inputBox.inputElement, (e) => this._onKeyDown.fire(e));
this.onkeyup(this.inputBox.inputElement, (e) => this._onKeyUp.fire(e));
this.oninput(this.inputBox.inputElement, (e) => this._onInput.fire());
this.onmousedown(this.inputBox.inputElement, (e) => this._onMouseDown.fire(e));
}
public enable(): void {
dom.removeClass(this.domNode, 'disabled');
this.inputBox.enable();
this.preserveCase.enable();
}
public disable(): void {
dom.addClass(this.domNode, 'disabled');
this.inputBox.disable();
this.preserveCase.disable();
}
public setFocusInputOnOptionClick(value: boolean): void {
this.fixFocusOnOptionClickEnabled = value;
}
public setEnabled(enabled: boolean): void {
if (enabled) {
this.enable();
} else {
this.disable();
}
}
public clear(): void {
this.clearValidation();
this.setValue('');
this.focus();
}
public getValue(): string {
return this.inputBox.value;
}
public setValue(value: string): void {
if (this.inputBox.value !== value) {
this.inputBox.value = value;
}
}
public onSearchSubmit(): void {
this.inputBox.addToHistory();
}
public style(styles: IReplaceInputStyles): void {
this.inputActiveOptionBorder = styles.inputActiveOptionBorder;
this.inputBackground = styles.inputBackground;
this.inputForeground = styles.inputForeground;
this.inputBorder = styles.inputBorder;
this.inputValidationInfoBackground = styles.inputValidationInfoBackground;
this.inputValidationInfoForeground = styles.inputValidationInfoForeground;
this.inputValidationInfoBorder = styles.inputValidationInfoBorder;
this.inputValidationWarningBackground = styles.inputValidationWarningBackground;
this.inputValidationWarningForeground = styles.inputValidationWarningForeground;
this.inputValidationWarningBorder = styles.inputValidationWarningBorder;
this.inputValidationErrorBackground = styles.inputValidationErrorBackground;
this.inputValidationErrorForeground = styles.inputValidationErrorForeground;
this.inputValidationErrorBorder = styles.inputValidationErrorBorder;
this.applyStyles();
}
protected applyStyles(): void {
if (this.domNode) {
const checkBoxStyles: ICheckboxStyles = {
inputActiveOptionBorder: this.inputActiveOptionBorder,
};
this.preserveCase.style(checkBoxStyles);
const inputBoxStyles: IInputBoxStyles = {
inputBackground: this.inputBackground,
inputForeground: this.inputForeground,
inputBorder: this.inputBorder,
inputValidationInfoBackground: this.inputValidationInfoBackground,
inputValidationInfoForeground: this.inputValidationInfoForeground,
inputValidationInfoBorder: this.inputValidationInfoBorder,
inputValidationWarningBackground: this.inputValidationWarningBackground,
inputValidationWarningForeground: this.inputValidationWarningForeground,
inputValidationWarningBorder: this.inputValidationWarningBorder,
inputValidationErrorBackground: this.inputValidationErrorBackground,
inputValidationErrorForeground: this.inputValidationErrorForeground,
inputValidationErrorBorder: this.inputValidationErrorBorder
};
this.inputBox.style(inputBoxStyles);
}
}
public select(): void {
this.inputBox.select();
}
public focus(): void {
this.inputBox.focus();
}
public getPreserveCase(): boolean {
return this.preserveCase.checked;
}
public setPreserveCase(value: boolean): void {
this.preserveCase.checked = value;
}
public focusOnPreserve(): void {
this.preserveCase.focus();
}
private _lastHighlightFindOptions: number = 0;
public highlightFindOptions(): void {
dom.removeClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions));
this._lastHighlightFindOptions = 1 - this._lastHighlightFindOptions;
dom.addClass(this.domNode, 'highlight-' + (this._lastHighlightFindOptions));
}
private buildDomNode(history: string[], flexibleHeight: boolean, flexibleWidth: boolean, flexibleMaxHeight: number | undefined): void {
this.domNode = document.createElement('div');
dom.addClass(this.domNode, 'monaco-findInput');
this.inputBox = this._register(new HistoryInputBox(this.domNode, this.contextViewProvider, {
ariaLabel: this.label || '',
placeholder: this.placeholder || '',
validationOptions: {
validation: this.validation
},
inputBackground: this.inputBackground,
inputForeground: this.inputForeground,
inputBorder: this.inputBorder,
inputValidationInfoBackground: this.inputValidationInfoBackground,
inputValidationInfoForeground: this.inputValidationInfoForeground,
inputValidationInfoBorder: this.inputValidationInfoBorder,
inputValidationWarningBackground: this.inputValidationWarningBackground,
inputValidationWarningForeground: this.inputValidationWarningForeground,
inputValidationWarningBorder: this.inputValidationWarningBorder,
inputValidationErrorBackground: this.inputValidationErrorBackground,
inputValidationErrorForeground: this.inputValidationErrorForeground,
inputValidationErrorBorder: this.inputValidationErrorBorder,
history,
flexibleHeight,
flexibleWidth,
flexibleMaxHeight
}));
this.preserveCase = this._register(new PreserveCaseCheckbox({
appendTitle: '',
isChecked: false,
inputActiveOptionBorder: this.inputActiveOptionBorder
}));
this._register(this.preserveCase.onChange(viaKeyboard => {
this._onDidOptionChange.fire(viaKeyboard);
if (!viaKeyboard && this.fixFocusOnOptionClickEnabled) {
this.inputBox.focus();
}
this.validate();
}));
this._register(this.preserveCase.onKeyDown(e => {
this._onPreserveCaseKeyDown.fire(e);
}));
if (this._showOptionButtons) {
this.cachedOptionsWidth = this.preserveCase.width();
} else {
this.cachedOptionsWidth = 0;
}
// Arrow-Key support to navigate between options
let indexes = [this.preserveCase.domNode];
this.onkeydown(this.domNode, (event: IKeyboardEvent) => {
if (event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) || event.equals(KeyCode.Escape)) {
let index = indexes.indexOf(<HTMLElement>document.activeElement);
if (index >= 0) {
let newIndex: number = -1;
if (event.equals(KeyCode.RightArrow)) {
newIndex = (index + 1) % indexes.length;
} else if (event.equals(KeyCode.LeftArrow)) {
if (index === 0) {
newIndex = indexes.length - 1;
} else {
newIndex = index - 1;
}
}
if (event.equals(KeyCode.Escape)) {
indexes[index].blur();
} else if (newIndex >= 0) {
indexes[newIndex].focus();
}
dom.EventHelper.stop(event, true);
}
}
});
let controls = document.createElement('div');
controls.className = 'controls';
controls.style.display = this._showOptionButtons ? 'block' : 'none';
controls.appendChild(this.preserveCase.domNode);
this.domNode.appendChild(controls);
}
public validate(): void {
if (this.inputBox) {
this.inputBox.validate();
}
}
public showMessage(message: InputBoxMessage): void {
if (this.inputBox) {
this.inputBox.showMessage(message);
}
}
public clearMessage(): void {
if (this.inputBox) {
this.inputBox.hideMessage();
}
}
private clearValidation(): void {
if (this.inputBox) {
this.inputBox.hideMessage();
}
}
public set width(newWidth: number) {
this.inputBox.paddingRight = this.cachedOptionsWidth;
this.inputBox.width = newWidth;
this.domNode.style.width = newWidth + 'px';
}
public dispose(): void {
super.dispose();
}
}

View File

@@ -60,6 +60,8 @@
display: block;
-ms-overflow-style: none; /* IE 10+ */
overflow: -moz-scrollbars-none; /* Firefox */
scrollbar-width: none; /* Firefox ^64 */
outline: none;
}
.monaco-inputbox > .wrapper > textarea.input::-webkit-scrollbar {

View File

@@ -32,6 +32,7 @@ export interface IInputOptions extends IInputBoxStyles {
readonly type?: string;
readonly validationOptions?: IInputValidationOptions;
readonly flexibleHeight?: boolean;
readonly flexibleWidth?: boolean;
readonly flexibleMaxHeight?: number;
readonly actions?: ReadonlyArray<IAction>;
@@ -185,6 +186,13 @@ export class InputBox extends Widget {
this.mirror.innerHTML = '&nbsp;';
this.scrollableElement = new ScrollableElement(this.element, { vertical: ScrollbarVisibility.Auto });
if (this.options.flexibleWidth) {
this.input.setAttribute('wrap', 'off');
this.mirror.style.whiteSpace = 'pre';
this.mirror.style.wordWrap = 'initial';
}
dom.append(container, this.scrollableElement.getDomNode());
this._register(this.scrollableElement);
@@ -345,12 +353,36 @@ export class InputBox extends Widget {
}
public set width(width: number) {
this.input.style.width = width + 'px';
if (this.options.flexibleHeight && this.options.flexibleWidth) {
// textarea with horizontal scrolling
let horizontalPadding = 0;
if (this.mirror) {
const paddingLeft = parseFloat(this.mirror.style.paddingLeft || '') || 0;
const paddingRight = parseFloat(this.mirror.style.paddingRight || '') || 0;
horizontalPadding = paddingLeft + paddingRight;
}
this.input.style.width = (width - horizontalPadding) + 'px';
} else {
this.input.style.width = width + 'px';
}
if (this.mirror) {
this.mirror.style.width = width + 'px';
}
}
public set paddingRight(paddingRight: number) {
if (this.options.flexibleHeight && this.options.flexibleWidth) {
this.input.style.width = `calc(100% - ${paddingRight}px)`;
} else {
this.input.style.paddingRight = paddingRight + 'px';
}
if (this.mirror) {
this.mirror.style.paddingRight = paddingRight + 'px';
}
}
private updateScrollDimensions(): void {
if (typeof this.cachedContentHeight !== 'number' || typeof this.cachedHeight !== 'number') {
return;

View File

@@ -683,6 +683,22 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
this.scrollableElement.setScrollPosition({ scrollTop });
}
getScrollLeft(): number {
const scrollPosition = this.scrollableElement.getScrollPosition();
return scrollPosition.scrollLeft;
}
setScrollLeftt(scrollLeft: number): void {
if (this.scrollableElementUpdateDisposable) {
this.scrollableElementUpdateDisposable.dispose();
this.scrollableElementUpdateDisposable = null;
this.scrollableElement.setScrollDimensions({ scrollWidth: this.scrollWidth });
}
this.scrollableElement.setScrollPosition({ scrollLeft });
}
get scrollTop(): number {
return this.getScrollTop();
}

View File

@@ -1307,6 +1307,14 @@ export class List<T> implements ISpliceable<T>, IDisposable {
this.view.setScrollTop(scrollTop);
}
get scrollLeft(): number {
return this.view.getScrollLeft();
}
set scrollLeft(scrollLeft: number) {
this.view.setScrollLeftt(scrollLeft);
}
get scrollHeight(): number {
return this.view.scrollHeight;
}

View File

@@ -1319,6 +1319,14 @@ export abstract class AbstractTree<T, TFilterData, TRef> implements IDisposable
this.view.scrollTop = scrollTop;
}
get scrollLeft(): number {
return this.view.scrollTop;
}
set scrollLeft(scrollLeft: number) {
this.view.scrollLeft = scrollLeft;
}
get scrollHeight(): number {
return this.view.scrollHeight;
}

View File

@@ -400,6 +400,14 @@ export class AsyncDataTree<TInput, T, TFilterData = void> implements IDisposable
this.tree.scrollTop = scrollTop;
}
get scrollLeft(): number {
return this.tree.scrollLeft;
}
set scrollLeft(scrollLeft: number) {
this.tree.scrollLeft = scrollLeft;
}
get scrollHeight(): number {
return this.tree.scrollHeight;
}

View File

@@ -219,7 +219,7 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
const result = this._setListNodeCollapsed(node, listIndex, revealed, collapsed!, recursive || false);
if (this.autoExpandSingleChildren && !collapsed! && !recursive) {
if (node !== this.root && this.autoExpandSingleChildren && !collapsed! && !recursive) {
let onlyVisibleChildIndex = -1;
for (let i = 0; i < node.children.length; i++) {

View File

@@ -549,7 +549,7 @@ export function fuzzyScore(pattern: string, patternLow: string, patternPos: numb
const patternStartPos = patternPos;
const wordStartPos = wordPos;
// There will be a mach, fill in tables
// There will be a match, fill in tables
for (patternPos = patternStartPos + 1; patternPos <= patternLen; patternPos++) {
for (wordPos = 1; wordPos <= wordLen; wordPos++) {
@@ -573,6 +573,11 @@ export function fuzzyScore(pattern: string, patternLow: string, patternPos: numb
} else {
score = 5;
}
} else if (isSeparatorAtPos(wordLow, wordPos - 1) && (wordPos === 1 || !isSeparatorAtPos(wordLow, wordPos - 2))) {
// hitting a separator: `. <-> foo.bar`
// ^
score = 5;
} else if (isSeparatorAtPos(wordLow, wordPos - 2) || isWhitespaceAtPos(wordLow, wordPos - 2)) {
// post separator: `foo <-> bar_foo`
// ^^^

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { URI } from 'vs/base/common/uri';
import * as platform from 'vs/base/common/platform';
export namespace Schemas {
@@ -58,11 +59,17 @@ class RemoteAuthoritiesImpl {
private readonly _hosts: { [authority: string]: string; };
private readonly _ports: { [authority: string]: number; };
private readonly _connectionTokens: { [authority: string]: string; };
private _preferredWebSchema: 'http' | 'https';
constructor() {
this._hosts = Object.create(null);
this._ports = Object.create(null);
this._connectionTokens = Object.create(null);
this._preferredWebSchema = 'http';
}
public setPreferredWebSchema(schema: 'http' | 'https') {
this._preferredWebSchema = schema;
}
public set(authority: string, host: string, port: number): void {
@@ -79,9 +86,9 @@ class RemoteAuthoritiesImpl {
const port = this._ports[authority];
const connectionToken = this._connectionTokens[authority];
return URI.from({
scheme: Schemas.vscodeRemoteResource,
scheme: platform.isWeb ? this._preferredWebSchema : Schemas.vscodeRemoteResource,
authority: `${host}:${port}`,
path: `/vscode-remote2`,
path: `/vscode-remote-resource`,
query: `path=${encodeURIComponent(path)}&tkn=${encodeURIComponent(connectionToken)}`
});
}

View File

@@ -366,6 +366,10 @@ suite('Filters', () => {
assertMatches('f', ':foo', ':^foo', fuzzyScore);
});
test('Separator only match should not be weak #79558', function () {
assertMatches('.', 'foo.bar', 'foo^.bar', fuzzyScore);
});
test('Cannot set property \'1\' of undefined, #26511', function () {
let word = new Array<void>(123).join('a');
let pattern = new Array<void>(120).join('a');

View File

@@ -25,8 +25,8 @@
</body>
<!-- Require our AMD loader -->
<script src="./out/vs/loader.js"></script>
<script src="./static/out/vs/loader.js"></script>
<!-- Startup via workbench.js -->
<script src="./out/vs/code/browser/workbench/workbench.js"></script>
<script src="./static/out/vs/code/browser/workbench/workbench.js"></script>
</html>

View File

@@ -8,15 +8,15 @@
(function () {
require.config({
baseUrl: `${window.location.origin}/out`,
baseUrl: `${window.location.origin}/static/out`,
paths: {
'vscode-textmate': `${window.location.origin}/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${window.location.origin}/node_modules/onigasm-umd/release/main`,
'xterm': `${window.location.origin}/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-web-links': `${window.location.origin}/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'semver-umd': `${window.location.origin}/node_modules/semver-umd/lib/semver-umd.js`,
'@microsoft/applicationinsights-web': `${window.location.origin}/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`,
'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`,
'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`,
'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`,
'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`,
'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`,
'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`,
'@microsoft/applicationinsights-web': `${window.location.origin}/static/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`,
}
});

View File

@@ -4,41 +4,39 @@
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import { parse } from 'vs/base/common/marshalling';
import { Schemas } from 'vs/base/common/network';
import * as resources from 'vs/base/common/resources';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { IDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { localize } from 'vs/nls';
import { IProductService } from 'vs/platform/product/common/product';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import Severity from 'vs/base/common/severity';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { IOpener, IOpenerService, IValidator } from 'vs/platform/opener/common/opener';
export class OpenerService implements IOpenerService {
export class OpenerService extends Disposable implements IOpenerService {
_serviceBrand!: ServiceIdentifier<any>;
private readonly _opener = new LinkedList<IOpener>();
private readonly _openers = new LinkedList<IOpener>();
private readonly _validators = new LinkedList<IValidator>();
constructor(
@ICodeEditorService private readonly _editorService: ICodeEditorService,
@ICommandService private readonly _commandService: ICommandService,
@IStorageService private readonly _storageService: IStorageService,
@IDialogService private readonly _dialogService: IDialogService,
@IProductService private readonly _productService: IProductService
) {
//
super();
}
registerOpener(opener: IOpener): IDisposable {
const remove = this._opener.push(opener);
const remove = this._openers.push(opener);
return { dispose: remove };
}
registerValidator(validator: IValidator): IDisposable {
const remove = this._validators.push(validator);
return { dispose: remove };
}
@@ -47,8 +45,16 @@ export class OpenerService implements IOpenerService {
if (!resource.scheme) {
return Promise.resolve(false);
}
// check with contributed validators
for (const validator of this._validators.toArray()) {
if (!(await validator.shouldOpen(resource))) {
return false;
}
}
// check with contributed openers
for (const opener of this._opener.toArray()) {
for (const opener of this._openers.toArray()) {
const handled = await opener.open(resource, options);
if (handled) {
return true;
@@ -60,7 +66,7 @@ export class OpenerService implements IOpenerService {
private _doOpen(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise<boolean> {
const { scheme, authority, path, query, fragment } = resource;
const { scheme, path, query, fragment } = resource;
if (equalsIgnoreCase(scheme, Schemas.mailto) || (options && options.openExternal)) {
// open default mail application
@@ -68,48 +74,8 @@ export class OpenerService implements IOpenerService {
}
if (equalsIgnoreCase(scheme, Schemas.http) || equalsIgnoreCase(scheme, Schemas.https)) {
let trustedDomains: string[] = ['https://code.visualstudio.com'];
try {
const trustedDomainsSrc = this._storageService.get('http.trustedDomains', StorageScope.GLOBAL);
if (trustedDomainsSrc) {
trustedDomains = JSON.parse(trustedDomainsSrc);
}
} catch (err) { }
const domainToOpen = `${scheme}://${authority}`;
if (isDomainTrusted(domainToOpen, trustedDomains)) {
return this._doOpenExternal(resource);
} else {
return this._dialogService.show(
Severity.Info,
localize(
'openExternalLinkAt',
'Do you want {0} to open the external website?\n{1}',
this._productService.nameShort,
resource.toString(true)
),
[
localize('openLink', 'Open Link'),
localize('cancel', 'Cancel'),
localize('configureTrustedDomains', 'Configure Trusted Domains')
],
{
cancelId: 1
}).then((choice) => {
if (choice === 0) {
return this._doOpenExternal(resource);
} else if (choice === 2) {
return this._commandService.executeCommand('workbench.action.configureTrustedDomains', domainToOpen).then((pickedDomains: string[]) => {
if (pickedDomains.indexOf(domainToOpen) !== -1) {
return this._doOpenExternal(resource);
}
return Promise.resolve(false);
});
}
return Promise.resolve(false);
});
}
// open link in default browser
return this._doOpenExternal(resource);
} else if (equalsIgnoreCase(scheme, Schemas.command)) {
// run command or bail out if command isn't known
if (!CommandsRegistry.getCommand(path)) {
@@ -158,22 +124,8 @@ export class OpenerService implements IOpenerService {
return Promise.resolve(true);
}
}
/**
* Check whether a domain like https://www.microsoft.com matches
* the list of trusted domains.
*/
function isDomainTrusted(domain: string, trustedDomains: string[]) {
for (let i = 0; i < trustedDomains.length; i++) {
if (trustedDomains[i] === '*') {
return true;
}
if (trustedDomains[i] === domain) {
return true;
}
dispose() {
this._validators.clear();
}
return false;
}

View File

@@ -367,6 +367,10 @@ export let completionKindFromString: {
};
})();
export const enum CompletionItemKindModifier {
Deprecated = 1
}
export const enum CompletionItemInsertTextRule {
/**
* Adjust whitespace/indentation of multiline insert texts to
@@ -396,6 +400,11 @@ export interface CompletionItem {
* an icon is chosen by the editor.
*/
kind: CompletionItemKind;
/**
* A modifier to the `kind` which affect how the item
* is rendered, e.g. Deprecated is rendered with a strikeout
*/
kindModifier?: CompletionItemKindModifier;
/**
* A human-readable string with additional information
* about this item, like type or symbol information.
@@ -464,7 +473,7 @@ export interface CompletionItem {
/**
* @internal
*/
[key: string]: any;
_id?: [number, number];
}
export interface CompletionList {

View File

@@ -581,6 +581,10 @@ export enum CompletionItemKind {
Snippet = 25
}
export enum CompletionItemKindModifier {
Deprecated = 1
}
export enum CompletionItemInsertTextRule {
/**
* Adjust whitespace/indentation of multiline insert texts to

View File

@@ -12,19 +12,20 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, EditorCommand, ServicesAccessor, registerEditorAction, registerEditorCommand, registerEditorContribution } from 'vs/editor/browser/editorExtensions';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding } from 'vs/editor/contrib/find/findModel';
import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_FIND_WIDGET_VISIBLE, FIND_IDS, FindModelBoundToEditorModel, ToggleCaseSensitiveKeybinding, ToggleRegexKeybinding, ToggleSearchScopeKeybinding, ToggleWholeWordKeybinding, CONTEXT_REPLACE_INPUT_FOCUSED } from 'vs/editor/contrib/find/findModel';
import { FindOptionsWidget } from 'vs/editor/contrib/find/findOptionsWidget';
import { FindReplaceState, FindReplaceStateChangedEvent, INewFindReplaceState } from 'vs/editor/contrib/find/findState';
import { FindWidget, IFindController } from 'vs/editor/contrib/find/findWidget';
import { MenuId } from 'vs/platform/actions/common/actions';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKey, IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { optional } from 'vs/platform/instantiation/common/instantiation';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { INotificationService } from 'vs/platform/notification/common/notification';
const SEARCH_STRING_MAX_LENGTH = 524288;
@@ -75,7 +76,7 @@ export class CommonFindController extends Disposable implements editorCommon.IEd
protected _state: FindReplaceState;
protected _updateHistoryDelayer: Delayer<void>;
private _model: FindModelBoundToEditorModel | null;
private readonly _storageService: IStorageService;
protected readonly _storageService: IStorageService;
private readonly _clipboardService: IClipboardService;
protected readonly _contextKeyService: IContextKeyService;
@@ -383,10 +384,11 @@ export class FindController extends CommonFindController implements IFindControl
@IContextKeyService _contextKeyService: IContextKeyService,
@IKeybindingService private readonly _keybindingService: IKeybindingService,
@IThemeService private readonly _themeService: IThemeService,
@IStorageService storageService: IStorageService,
@optional(IClipboardService) clipboardService: IClipboardService
@INotificationService private readonly _notificationService: INotificationService,
@IStorageService _storageService: IStorageService,
@optional(IClipboardService) clipboardService: IClipboardService,
) {
super(editor, _contextKeyService, storageService, clipboardService);
super(editor, _contextKeyService, _storageService, clipboardService);
this._widget = null;
this._findOptionsWidget = null;
}
@@ -422,7 +424,7 @@ export class FindController extends CommonFindController implements IFindControl
}
private _createFindWidget() {
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService));
this._widget = this._register(new FindWidget(this._editor, this, this._state, this._contextViewService, this._keybindingService, this._contextKeyService, this._themeService, this._storageService, this._notificationService));
this._findOptionsWidget = this._register(new FindOptionsWidget(this._editor, this._state, this._keybindingService, this._themeService));
}
}
@@ -540,6 +542,27 @@ export class NextMatchFindAction extends MatchFindAction {
}
}
export class NextMatchFindAction2 extends MatchFindAction {
constructor() {
super({
id: FIND_IDS.NextMatchFindAction,
label: nls.localize('findNextMatchAction', "Find Next"),
alias: 'Find Next',
precondition: undefined,
kbOpts: {
kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_FIND_INPUT_FOCUSED),
primary: KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
});
}
protected _run(controller: CommonFindController): boolean {
return controller.moveToNextMatch();
}
}
export class PreviousMatchFindAction extends MatchFindAction {
constructor() {
@@ -562,6 +585,27 @@ export class PreviousMatchFindAction extends MatchFindAction {
}
}
export class PreviousMatchFindAction2 extends MatchFindAction {
constructor() {
super({
id: FIND_IDS.PreviousMatchFindAction,
label: nls.localize('findPreviousMatchAction', "Find Previous"),
alias: 'Find Previous',
precondition: undefined,
kbOpts: {
kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_FIND_INPUT_FOCUSED),
primary: KeyMod.Shift | KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
});
}
protected _run(controller: CommonFindController): boolean {
return controller.moveToPrevMatch();
}
}
export abstract class SelectionMatchFindAction extends EditorAction {
public run(accessor: ServicesAccessor | null, editor: ICodeEditor): void {
let controller = CommonFindController.get(editor);
@@ -695,7 +739,9 @@ registerEditorContribution(FindController);
registerEditorAction(StartFindAction);
registerEditorAction(StartFindWithSelectionAction);
registerEditorAction(NextMatchFindAction);
registerEditorAction(NextMatchFindAction2);
registerEditorAction(PreviousMatchFindAction);
registerEditorAction(PreviousMatchFindAction2);
registerEditorAction(NextSelectionMatchFindAction);
registerEditorAction(PreviousSelectionMatchFindAction);
registerEditorAction(StartFindReplaceAction);
@@ -781,6 +827,17 @@ registerEditorCommand(new FindCommand({
}
}));
registerEditorCommand(new FindCommand({
id: FIND_IDS.ReplaceOneAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.replace(),
kbOpts: {
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_REPLACE_INPUT_FOCUSED),
primary: KeyCode.Enter
}
}));
registerEditorCommand(new FindCommand({
id: FIND_IDS.ReplaceAllAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
@@ -792,6 +849,20 @@ registerEditorCommand(new FindCommand({
}
}));
registerEditorCommand(new FindCommand({
id: FIND_IDS.ReplaceAllAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,
handler: x => x.replaceAll(),
kbOpts: {
weight: KeybindingWeight.EditorContrib + 5,
kbExpr: ContextKeyExpr.and(EditorContextKeys.focus, CONTEXT_REPLACE_INPUT_FOCUSED),
primary: undefined,
mac: {
primary: KeyMod.CtrlCmd | KeyCode.Enter,
}
}
}));
registerEditorCommand(new FindCommand({
id: FIND_IDS.SelectAllMatchesAction,
precondition: CONTEXT_FIND_WIDGET_VISIBLE,

View File

@@ -32,8 +32,8 @@
.monaco-editor .find-widget {
position: absolute;
z-index: 10;
top: -44px; /* find input height + shadow (10px) */
height: 34px; /* find input height */
top: -44px;
height: 33px;
overflow: hidden;
line-height: 19px;
transition: top 200ms linear;
@@ -47,12 +47,10 @@
/* Find widget when replace is toggled on */
.monaco-editor .find-widget.replaceToggled {
top: -74px; /* find input height + replace input height + shadow (10px) */
height: 64px; /* find input height + replace input height */
}
.monaco-editor .find-widget.replaceToggled > .replace-part {
display: flex;
display: -webkit-flex;
align-items: center;
}
.monaco-editor .find-widget.visible,
@@ -60,13 +58,31 @@
top: 0;
}
/* Multiple line find widget */
.monaco-editor .find-widget.multipleline {
top: unset;
bottom: 10px;
}
.monaco-editor .find-widget.multipleline.visible,
.monaco-editor .find-widget.multipleline.replaceToggled.visible {
top: 0px;
bottom: unset;
}
.monaco-editor .find-widget .monaco-inputbox.synthetic-focus {
outline: 1px solid -webkit-focus-ring-color;
outline-offset: -1px;
}
.monaco-editor .find-widget .monaco-inputbox .input {
background-color: transparent;
/* Style to compensate for //winjs */
min-height: 0;
}
.monaco-editor .find-widget .replace-input .input {
.monaco-editor .find-widget .monaco-findInput .input {
font-size: 13px;
}
@@ -76,29 +92,38 @@
font-size: 12px;
display: flex;
display: -webkit-flex;
align-items: center;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox,
.monaco-editor .find-widget > .replace-part .monaco-inputbox {
height: 25px;
min-height: 25px;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input {
width: 100% !important;
padding-right: 66px;
}
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input {
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .mirror {
padding-right: 22px;
}
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .input,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input {
.monaco-editor .find-widget > .find-part .monaco-inputbox > .wrapper > .mirror,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .input,
.monaco-editor .find-widget > .replace-part .monaco-inputbox > .wrapper > .mirror {
padding-top: 2px;
padding-bottom: 2px;
}
.monaco-editor .find-widget > .find-part .find-actions {
height: 25px;
display: flex;
align-items: center;
}
.monaco-editor .find-widget > .replace-part .replace-actions {
height: 25px;
display: flex;
align-items: center;
}
.monaco-editor .find-widget .monaco-findInput {
vertical-align: middle;
display: flex;
@@ -106,6 +131,16 @@
flex:1;
}
.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element {
/* Make sure textarea inherits the width correctly */
width: 100%;
}
.monaco-editor .find-widget .monaco-findInput .monaco-scrollable-element .scrollbar.vertical {
/* Hide vertical scrollbar */
opacity: 0;
}
.monaco-editor .find-widget .matchesCount {
display: flex;
display: -webkit-flex;
@@ -237,15 +272,17 @@
display: none;
}
.monaco-editor .find-widget > .replace-part > .replace-input {
.monaco-editor .find-widget > .replace-part > .monaco-findInput {
position: relative;
display: flex;
display: -webkit-flex;
vertical-align: middle;
width: auto !important;
flex: auto;
flex-grow: 0;
flex-shrink: 0;
}
.monaco-editor .find-widget > .replace-part > .replace-input > .controls {
.monaco-editor .find-widget > .replace-part > .monaco-findInput > .controls {
position: absolute;
top: 3px;
right: 2px;

View File

@@ -9,11 +9,12 @@ import * as dom from 'vs/base/browser/dom';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { IMouseEvent } from 'vs/base/browser/mouseEvent';
import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
import { alert as alertFn } from 'vs/base/browser/ui/aria/aria';
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
import { HistoryInputBox, IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput';
import { IHorizontalSashLayoutProvider, ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash';
import { Widget } from 'vs/base/browser/ui/widget';
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
import { Delayer } from 'vs/base/common/async';
import { Color } from 'vs/base/common/color';
import { onUnexpectedError } from 'vs/base/common/errors';
@@ -28,11 +29,12 @@ import { CONTEXT_FIND_INPUT_FOCUSED, CONTEXT_REPLACE_INPUT_FOCUSED, FIND_IDS, MA
import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState';
import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground } from 'vs/platform/theme/common/colorRegistry';
import { contrastBorder, editorFindMatch, editorFindMatchBorder, editorFindMatchHighlight, editorFindMatchHighlightBorder, editorFindRangeHighlight, editorFindRangeHighlightBorder, editorWidgetBackground, editorWidgetBorder, editorWidgetResizeBorder, errorForeground, inputActiveOptionBorder, inputActiveOptionBackground, inputBackground, inputBorder, inputForeground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground, inputValidationInfoBackground, inputValidationInfoBorder, inputValidationInfoForeground, inputValidationWarningBackground, inputValidationWarningBorder, inputValidationWarningForeground, widgetShadow, editorWidgetForeground, focusBorder } from 'vs/platform/theme/common/colorRegistry';
import { ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService';
import { ContextScopedFindInput, ContextScopedHistoryInputBox } from 'vs/platform/browser/contextScopedHistoryWidget';
import { ContextScopedFindInput, ContextScopedReplaceInput } from 'vs/platform/browser/contextScopedHistoryWidget';
import { AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
import { alert as alertFn } from 'vs/base/browser/ui/aria/aria';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { INotificationService } from 'vs/platform/notification/common/notification';
export interface IFindController {
replace(): void;
@@ -48,7 +50,6 @@ const NLS_TOGGLE_SELECTION_FIND_TITLE = nls.localize('label.toggleSelectionFind'
const NLS_CLOSE_BTN_LABEL = nls.localize('label.closeButton', "Close");
const NLS_REPLACE_INPUT_LABEL = nls.localize('label.replace', "Replace");
const NLS_REPLACE_INPUT_PLACEHOLDER = nls.localize('placeholder.replace', "Replace");
const NLS_PRESERVE_CASE_LABEL = nls.localize('label.preserveCaseCheckbox', "Preserve Case");
const NLS_REPLACE_BTN_LABEL = nls.localize('label.replaceButton', "Replace");
const NLS_REPLACE_ALL_BTN_LABEL = nls.localize('label.replaceAllButton', "Replace All");
const NLS_TOGGLE_REPLACE_MODE_BTN_LABEL = nls.localize('label.toggleReplaceButton', "Toggle Replace mode");
@@ -59,14 +60,12 @@ const NLS_NO_RESULTS = nls.localize('label.noResults', "No Results");
const FIND_WIDGET_INITIAL_WIDTH = 411;
const PART_WIDTH = 275;
const FIND_INPUT_AREA_WIDTH = PART_WIDTH - 54;
const REPLACE_INPUT_AREA_WIDTH = FIND_INPUT_AREA_WIDTH;
let MAX_MATCHES_COUNT_WIDTH = 69;
let FIND_ALL_CONTROLS_WIDTH = 17/** Find Input margin-left */ + (MAX_MATCHES_COUNT_WIDTH + 3 + 1) /** Match Results */ + 23 /** Button */ * 4 + 2/** sash */;
const FIND_INPUT_AREA_HEIGHT = 34; // The height of Find Widget when Replace Input is not visible.
const FIND_REPLACE_AREA_HEIGHT = 64; // The height of Find Widget when Replace Input is visible.
const FIND_INPUT_AREA_HEIGHT = 33; // The height of Find Widget when Replace Input is not visible.
const ctrlEnterReplaceAllWarningPromptedKey = 'ctrlEnterReplaceAll.windows.donotask';
export class FindWidgetViewZone implements IViewZone {
public readonly afterLineNumber: number;
@@ -84,6 +83,22 @@ export class FindWidgetViewZone implements IViewZone {
}
}
function stopPropagationForMultiLineUpwards(event: IKeyboardEvent, value: string, textarea: HTMLTextAreaElement | null) {
const isMultiline = !!value.match(/\n/);
if (textarea && isMultiline && textarea.selectionStart > 0) {
event.stopPropagation();
return;
}
}
function stopPropagationForMultiLineDownwards(event: IKeyboardEvent, value: string, textarea: HTMLTextAreaElement | null) {
const isMultiline = !!value.match(/\n/);
if (textarea && isMultiline && textarea.selectionEnd < textarea.value.length) {
event.stopPropagation();
return;
}
}
export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSashLayoutProvider {
private static readonly ID = 'editor.contrib.findWidget';
private readonly _codeEditor: ICodeEditor;
@@ -92,10 +107,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private readonly _contextViewProvider: IContextViewProvider;
private readonly _keybindingService: IKeybindingService;
private readonly _contextKeyService: IContextKeyService;
private readonly _storageService: IStorageService;
private readonly _notificationService: INotificationService;
private _domNode!: HTMLElement;
private _cachedHeight: number | null;
private _findInput!: FindInput;
private _replaceInputBox!: HistoryInputBox;
private _replaceInput!: ReplaceInput;
private _toggleReplaceBtn!: SimpleButton;
private _matchesCount!: HTMLElement;
@@ -103,13 +121,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _nextBtn!: SimpleButton;
private _toggleSelectionFind!: SimpleCheckbox;
private _closeBtn!: SimpleButton;
private _preserveCase!: Checkbox;
private _replaceBtn!: SimpleButton;
private _replaceAllBtn!: SimpleButton;
private _isVisible: boolean;
private _isReplaceVisible: boolean;
private _ignoreChangeEvent: boolean;
private _ctrlEnterReplaceAllWarningPrompted: boolean;
private readonly _findFocusTracker: dom.IFocusTracker;
private readonly _findInputFocused: IContextKey<boolean>;
@@ -129,7 +147,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
contextViewProvider: IContextViewProvider,
keybindingService: IKeybindingService,
contextKeyService: IContextKeyService,
themeService: IThemeService
themeService: IThemeService,
storageService: IStorageService,
notificationService: INotificationService,
) {
super();
this._codeEditor = codeEditor;
@@ -138,6 +158,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._contextViewProvider = contextViewProvider;
this._keybindingService = keybindingService;
this._contextKeyService = contextKeyService;
this._storageService = storageService;
this._notificationService = notificationService;
this._ctrlEnterReplaceAllWarningPrompted = !!storageService.getBoolean(ctrlEnterReplaceAllWarningPromptedKey, StorageScope.GLOBAL);
this._isVisible = false;
this._isReplaceVisible = false;
@@ -149,6 +173,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._buildDomNode();
this._updateButtons();
this._tryUpdateWidgetWidth();
this._findInput.inputBox.layout();
this._register(this._codeEditor.onDidChangeConfiguration((e: IConfigurationChangedEvent) => {
if (e.readOnly) {
@@ -203,7 +228,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}));
this._replaceInputFocused = CONTEXT_REPLACE_INPUT_FOCUSED.bindTo(contextKeyService);
this._replaceFocusTracker = this._register(dom.trackFocus(this._replaceInputBox.inputElement));
this._replaceFocusTracker = this._register(dom.trackFocus(this._replaceInput.inputBox.inputElement));
this._register(this._replaceFocusTracker.onDidFocus(() => {
this._replaceInputFocused.set(true);
this._updateSearchScope();
@@ -264,6 +289,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _onStateChanged(e: FindReplaceStateChangedEvent): void {
if (e.searchString) {
if (this._state.searchString.indexOf('\n') >= 0) {
dom.addClass(this._domNode, 'multipleline');
} else {
dom.removeClass(this._domNode, 'multipleline');
}
try {
this._ignoreChangeEvent = true;
this._findInput.setValue(this._state.searchString);
@@ -273,7 +304,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._updateButtons();
}
if (e.replaceString) {
this._replaceInputBox.value = this._state.replaceString;
this._replaceInput.inputBox.value = this._state.replaceString;
}
if (e.isRevealed) {
if (this._state.isRevealed) {
@@ -286,8 +317,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (this._state.isReplaceRevealed) {
if (!this._codeEditor.getConfiguration().readOnly && !this._isReplaceVisible) {
this._isReplaceVisible = true;
this._replaceInputBox.width = this._findInput.inputBox.width;
this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode);
this._updateButtons();
this._replaceInput.inputBox.layout();
}
} else {
if (this._isReplaceVisible) {
@@ -296,6 +328,12 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}
}
if ((e.isRevealed || e.isReplaceRevealed) && (this._state.isRevealed || this._state.isReplaceRevealed)) {
if (this._tryUpdateHeight()) {
this._showViewZone();
}
}
if (e.isRegex) {
this._findInput.setRegex(this._state.isRegex);
}
@@ -337,7 +375,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._findInput.inputBox.addToHistory();
}
if (this._state.replaceString) {
this._replaceInputBox.addToHistory();
this._replaceInput.inputBox.addToHistory();
}
}
@@ -402,7 +440,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
private _updateButtons(): void {
this._findInput.setEnabled(this._isVisible);
this._replaceInputBox.setEnabled(this._isVisible && this._isReplaceVisible);
this._replaceInput.setEnabled(this._isVisible && this._isReplaceVisible);
this._updateToggleSelectionFindButton();
this._closeBtn.setEnabled(this._isVisible);
@@ -512,12 +550,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
this._codeEditor.changeViewZones((accessor) => {
if (this._state.isReplaceRevealed) {
viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT;
} else {
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
}
viewZone.heightInPx = this._getHeight();
this._viewZoneId = accessor.addZone(viewZone);
// scroll top adjust to make sure the editor doesn't scroll when adding viewzone at the beginning.
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + viewZone.heightInPx);
@@ -525,30 +558,47 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
private _showViewZone(adjustScroll: boolean = true) {
const viewZone = this._viewZone;
if (!this._isVisible || !viewZone) {
if (!this._isVisible) {
return;
}
const addExtraSpaceOnTop = this._codeEditor.getConfiguration().contribInfo.find.addExtraSpaceOnTop;
if (!addExtraSpaceOnTop) {
return;
}
if (this._viewZone === undefined) {
this._viewZone = new FindWidgetViewZone(0);
}
const viewZone = this._viewZone;
this._codeEditor.changeViewZones((accessor) => {
let scrollAdjustment = FIND_INPUT_AREA_HEIGHT;
if (this._viewZoneId !== undefined) {
if (this._state.isReplaceRevealed) {
viewZone.heightInPx = FIND_REPLACE_AREA_HEIGHT;
scrollAdjustment = FIND_REPLACE_AREA_HEIGHT - FIND_INPUT_AREA_HEIGHT;
} else {
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
scrollAdjustment = FIND_INPUT_AREA_HEIGHT - FIND_REPLACE_AREA_HEIGHT;
// the view zone already exists, we need to update the height
const newHeight = this._getHeight();
if (newHeight === viewZone.heightInPx) {
return;
}
accessor.removeZone(this._viewZoneId);
} else {
viewZone.heightInPx = FIND_INPUT_AREA_HEIGHT;
}
this._viewZoneId = accessor.addZone(viewZone);
if (adjustScroll) {
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment);
let scrollAdjustment = newHeight - viewZone.heightInPx;
viewZone.heightInPx = newHeight;
accessor.layoutZone(this._viewZoneId);
if (adjustScroll) {
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment);
}
return;
} else {
const scrollAdjustment = this._getHeight();
viewZone.heightInPx = scrollAdjustment;
this._viewZoneId = accessor.addZone(viewZone);
if (adjustScroll) {
this._codeEditor.setScrollTop(this._codeEditor.getScrollTop() + scrollAdjustment);
}
}
});
}
@@ -584,8 +634,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
inputValidationErrorBorder: theme.getColor(inputValidationErrorBorder),
};
this._findInput.style(inputStyles);
this._replaceInputBox.style(inputStyles);
this._preserveCase.style(inputStyles);
this._replaceInput.style(inputStyles);
}
private _tryUpdateWidgetWidth() {
@@ -615,7 +664,7 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
if (widgetWidth > FIND_WIDGET_INITIAL_WIDTH) {
// as the widget is resized by users, we may need to change the max width of the widget as the editor width changes.
this._domNode.style.maxWidth = `${editorWidth - 28 - minimapWidth - 15}px`;
this._replaceInputBox.inputElement.style.width = `${dom.getTotalWidth(this._findInput.inputBox.inputElement)}px`;
this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode);
return;
}
}
@@ -639,13 +688,47 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
if (this._resized) {
let findInputWidth = dom.getTotalWidth(this._findInput.inputBox.inputElement);
this._findInput.inputBox.layout();
let findInputWidth = this._findInput.inputBox.width;
if (findInputWidth > 0) {
this._replaceInputBox.inputElement.style.width = `${findInputWidth}px`;
this._replaceInput.width = findInputWidth;
}
}
}
private _getHeight(): number {
let totalheight = 0;
// find input margin top
totalheight += 4;
// find input height
totalheight += this._findInput.inputBox.height + 2 /** input box border */;
if (this._isReplaceVisible) {
// replace input margin
totalheight += 4;
totalheight += this._replaceInput.inputBox.height + 2 /** input box border */;
}
// margin bottom
totalheight += 4;
return totalheight;
}
private _tryUpdateHeight(): boolean {
const totalHeight = this._getHeight();
if (this._cachedHeight !== null && this._cachedHeight === totalHeight) {
return false;
}
this._cachedHeight = totalHeight;
this._domNode.style.height = `${totalHeight}px`;
return true;
}
// ----- Public
public focusFindInput(): void {
@@ -655,9 +738,9 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
public focusReplaceInput(): void {
this._replaceInputBox.select();
this._replaceInput.select();
// Edge browser requires focus() in addition to select()
this._replaceInputBox.focus();
this._replaceInput.focus();
}
public highlightFindOptions(): void {
@@ -692,22 +775,25 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
private _onFindInputKeyDown(e: IKeyboardEvent): void {
if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) {
const inputElement = this._findInput.inputBox.inputElement;
const start = inputElement.selectionStart;
const end = inputElement.selectionEnd;
const content = inputElement.value;
if (e.equals(KeyCode.Enter)) {
this._codeEditor.getAction(FIND_IDS.NextMatchFindAction).run().then(undefined, onUnexpectedError);
e.preventDefault();
return;
}
if (e.equals(KeyMod.Shift | KeyCode.Enter)) {
this._codeEditor.getAction(FIND_IDS.PreviousMatchFindAction).run().then(undefined, onUnexpectedError);
e.preventDefault();
return;
if (start && end) {
const value = content.substr(0, start) + '\n' + content.substr(end);
this._findInput.inputBox.value = value;
inputElement.setSelectionRange(start + 1, start + 1);
this._findInput.inputBox.layout();
e.preventDefault();
return;
}
}
if (e.equals(KeyCode.Tab)) {
if (this._isReplaceVisible) {
this._replaceInputBox.focus();
this._replaceInput.focus();
} else {
this._findInput.focusOnCaseSensitive();
}
@@ -720,20 +806,43 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
e.preventDefault();
return;
}
if (e.equals(KeyCode.UpArrow)) {
return stopPropagationForMultiLineUpwards(e, this._findInput.getValue(), this._findInput.domNode.querySelector('textarea'));
}
if (e.equals(KeyCode.DownArrow)) {
return stopPropagationForMultiLineDownwards(e, this._findInput.getValue(), this._findInput.domNode.querySelector('textarea'));
}
}
private _onReplaceInputKeyDown(e: IKeyboardEvent): void {
if (e.equals(KeyMod.WinCtrl | KeyCode.Enter)) {
if (platform.isWindows && platform.isNative && !this._ctrlEnterReplaceAllWarningPrompted) {
// this is the first time when users press Ctrl + Enter to replace all
this._notificationService.info(
nls.localize('ctrlEnter.keybindingChanged',
'Ctrl+Enter now inserts line break instead of replacing all. You can modify the keybinding for editor.action.replaceAll to override this behavior.')
);
if (e.equals(KeyCode.Enter)) {
this._controller.replace();
e.preventDefault();
return;
}
this._ctrlEnterReplaceAllWarningPrompted = true;
this._storageService.store(ctrlEnterReplaceAllWarningPromptedKey, true, StorageScope.GLOBAL);
if (e.equals(KeyMod.CtrlCmd | KeyCode.Enter)) {
this._controller.replaceAll();
e.preventDefault();
return;
}
const inputElement = this._replaceInput.inputBox.inputElement;
const start = inputElement.selectionStart;
const end = inputElement.selectionEnd;
const content = inputElement.value;
if (start && end) {
const value = content.substr(0, start) + '\n' + content.substr(end);
this._replaceInput.inputBox.value = value;
inputElement.setSelectionRange(start + 1, start + 1);
this._replaceInput.inputBox.layout();
e.preventDefault();
return;
}
}
if (e.equals(KeyCode.Tab)) {
@@ -753,6 +862,14 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
e.preventDefault();
return;
}
if (e.equals(KeyCode.UpArrow)) {
return stopPropagationForMultiLineUpwards(e, this._replaceInput.inputBox.value, this._replaceInput.inputBox.element.querySelector('textarea'));
}
if (e.equals(KeyCode.DownArrow)) {
return stopPropagationForMultiLineDownwards(e, this._replaceInput.inputBox.value, this._replaceInput.inputBox.element.querySelector('textarea'));
}
}
// ----- sash
@@ -777,6 +894,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
private _buildDomNode(): void {
const flexibleHeight = true;
const flexibleWidth = true;
// Find input
this._findInput = this._register(new ContextScopedFindInput(null, this._contextViewProvider, {
width: FIND_INPUT_AREA_WIDTH,
@@ -796,7 +915,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
} catch (e) {
return { content: e.message };
}
}
},
flexibleHeight,
flexibleWidth,
flexibleMaxHeight: 118
}, this._contextKeyService, true));
this._findInput.setRegex(!!this._state.isRegex);
this._findInput.setCaseSensitive(!!this._state.matchCase);
@@ -818,11 +940,24 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
this._register(this._findInput.onCaseSensitiveKeyDown((e) => {
if (e.equals(KeyMod.Shift | KeyCode.Tab)) {
if (this._isReplaceVisible) {
this._replaceInputBox.focus();
this._replaceInput.focus();
e.preventDefault();
}
}
}));
this._register(this._findInput.onRegexKeyDown((e) => {
if (e.equals(KeyCode.Tab)) {
if (this._isReplaceVisible) {
this._replaceInput.focusOnPreserve();
e.preventDefault();
}
}
}));
this._register(this._findInput.inputBox.onDidHeightChange((e) => {
if (this._tryUpdateHeight()) {
this._showViewZone();
}
}));
if (platform.isLinux) {
this._register(this._findInput.onMouseDown((e) => this._onFindInputMouseDown(e)));
}
@@ -852,13 +987,16 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
let findPart = document.createElement('div');
findPart.className = 'find-part';
findPart.appendChild(this._findInput.domNode);
findPart.appendChild(this._matchesCount);
findPart.appendChild(this._prevBtn.domNode);
findPart.appendChild(this._nextBtn.domNode);
const actionsContainer = document.createElement('div');
actionsContainer.className = 'find-actions';
findPart.appendChild(actionsContainer);
actionsContainer.appendChild(this._matchesCount);
actionsContainer.appendChild(this._prevBtn.domNode);
actionsContainer.appendChild(this._nextBtn.domNode);
// Toggle selection button
this._toggleSelectionFind = this._register(new SimpleCheckbox({
parent: findPart,
parent: actionsContainer,
title: NLS_TOGGLE_SELECTION_FIND_TITLE + this._keybindingLabelFor(FIND_IDS.ToggleSearchScopeCommand),
onChange: () => {
if (this._toggleSelectionFind.checked) {
@@ -898,34 +1036,45 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}));
findPart.appendChild(this._closeBtn.domNode);
actionsContainer.appendChild(this._closeBtn.domNode);
// Replace input
let replaceInput = document.createElement('div');
replaceInput.className = 'replace-input';
replaceInput.style.width = REPLACE_INPUT_AREA_WIDTH + 'px';
this._replaceInputBox = this._register(new ContextScopedHistoryInputBox(replaceInput, undefined, {
ariaLabel: NLS_REPLACE_INPUT_LABEL,
this._replaceInput = this._register(new ContextScopedReplaceInput(null, undefined, {
label: NLS_REPLACE_INPUT_LABEL,
placeholder: NLS_REPLACE_INPUT_PLACEHOLDER,
history: []
}, this._contextKeyService));
this._register(dom.addStandardDisposableListener(this._replaceInputBox.inputElement, 'keydown', (e) => this._onReplaceInputKeyDown(e)));
this._register(this._replaceInputBox.onDidChange(() => {
this._state.change({ replaceString: this._replaceInputBox.value }, false);
history: [],
flexibleHeight,
flexibleWidth,
flexibleMaxHeight: 118
}, this._contextKeyService, true));
this._replaceInput.setPreserveCase(!!this._state.preserveCase);
this._register(this._replaceInput.onKeyDown((e) => this._onReplaceInputKeyDown(e)));
this._register(this._replaceInput.inputBox.onDidChange(() => {
this._state.change({ replaceString: this._replaceInput.inputBox.value }, false);
}));
this._preserveCase = this._register(new Checkbox({
actionClassName: 'monaco-preserve-case',
title: NLS_PRESERVE_CASE_LABEL,
isChecked: false,
this._register(this._replaceInput.inputBox.onDidHeightChange((e) => {
if (this._isReplaceVisible && this._tryUpdateHeight()) {
this._showViewZone();
}
}));
this._preserveCase.checked = !!this._state.preserveCase;
this._register(this._preserveCase.onChange(viaKeyboard => {
if (!viaKeyboard) {
this._state.change({ preserveCase: !this._state.preserveCase }, false);
this._replaceInputBox.focus();
this._register(this._replaceInput.onDidOptionChange(() => {
this._state.change({
preserveCase: this._replaceInput.getPreserveCase()
}, true);
}));
this._register(this._replaceInput.onPreserveCaseKeyDown((e) => {
if (e.equals(KeyCode.Tab)) {
if (this._prevBtn.isEnabled()) {
this._prevBtn.focus();
} else if (this._nextBtn.isEnabled()) {
this._nextBtn.focus();
} else if (this._toggleSelectionFind.isEnabled()) {
this._toggleSelectionFind.focus();
} else if (this._closeBtn.isEnabled()) {
this._closeBtn.focus();
}
e.preventDefault();
}
}));
@@ -953,17 +1102,16 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
}
}));
let controls = document.createElement('div');
controls.className = 'controls';
controls.style.display = 'block';
controls.appendChild(this._preserveCase.domNode);
replaceInput.appendChild(controls);
let replacePart = document.createElement('div');
replacePart.className = 'replace-part';
replacePart.appendChild(replaceInput);
replacePart.appendChild(this._replaceBtn.domNode);
replacePart.appendChild(this._replaceAllBtn.domNode);
replacePart.appendChild(this._replaceInput.domNode);
const replaceActionsContainer = document.createElement('div');
replaceActionsContainer.className = 'replace-actions';
replacePart.appendChild(replaceActionsContainer);
replaceActionsContainer.appendChild(this._replaceBtn.domNode);
replaceActionsContainer.appendChild(this._replaceAllBtn.domNode);
// Toggle replace button
this._toggleReplaceBtn = this._register(new SimpleButton({
@@ -972,7 +1120,8 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
onTrigger: () => {
this._state.change({ isReplaceRevealed: !this._isReplaceVisible }, false);
if (this._isReplaceVisible) {
this._replaceInputBox.width = this._findInput.inputBox.width;
this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode);
this._replaceInput.inputBox.layout();
}
this._showViewZone();
}
@@ -1015,9 +1164,13 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas
return;
}
this._domNode.style.width = `${width}px`;
this._findInput.inputBox.width = inputBoxWidth;
if (this._isReplaceVisible) {
this._replaceInputBox.width = inputBoxWidth;
this._replaceInput.width = dom.getTotalWidth(this._findInput.domNode);
}
this._findInput.inputBox.layout();
this._tryUpdateHeight();
}));
}
@@ -1077,6 +1230,10 @@ class SimpleCheckbox extends Widget {
return this._domNode;
}
public isEnabled(): boolean {
return (this._domNode.tabIndex >= 0);
}
public get checked(): boolean {
return this._checkbox.checked;
}
@@ -1086,7 +1243,7 @@ class SimpleCheckbox extends Widget {
}
public focus(): void {
this._checkbox.focus();
this._domNode.focus();
}
private enable(): void {
@@ -1245,4 +1402,11 @@ registerThemingParticipant((theme, collector) => {
if (inputActiveBackground) {
collector.addRule(`.monaco-editor .find-widget .monaco-checkbox .checkbox:checked + .label { background-color: ${inputActiveBackground.toString()}; }`);
}
// This rule is used to override the outline color for synthetic-focus find input.
const focusOutline = theme.getColor(focusBorder);
if (focusOutline) {
collector.addRule(`.monaco-workbench .monaco-editor .find-widget .monaco-inputbox.synthetic-focus { outline-color: ${focusOutline}; }`);
}
});

View File

@@ -52,7 +52,7 @@ export class ParameterHintsModel extends Disposable {
public readonly onChangedHints = this._onChangedHints.event;
private readonly editor: ICodeEditor;
private enabled: boolean;
private triggerOnType = false;
private _state: ParameterHintState.State = ParameterHintState.Default;
private readonly _lastSignatureHelpResult = this._register(new MutableDisposable<modes.SignatureHelpResult>());
private triggerChars = new CharacterSet();
@@ -68,7 +68,6 @@ export class ParameterHintsModel extends Disposable {
super();
this.editor = editor;
this.enabled = false;
this.throttledDelayer = new Delayer(delay);
@@ -242,7 +241,7 @@ export class ParameterHintsModel extends Disposable {
}
private onDidType(text: string) {
if (!this.enabled) {
if (!this.triggerOnType) {
return;
}
@@ -272,9 +271,9 @@ export class ParameterHintsModel extends Disposable {
}
private onEditorConfigurationChange(): void {
this.enabled = this.editor.getConfiguration().contribInfo.parameterHints.enabled;
this.triggerOnType = this.editor.getConfiguration().contribInfo.parameterHints.enabled;
if (!this.enabled) {
if (!this.triggerOnType) {
this.cancel();
}
}
@@ -283,4 +282,4 @@ export class ParameterHintsModel extends Disposable {
this.cancel(true);
super.dispose();
}
}
}

View File

@@ -97,6 +97,10 @@
font-weight: bold;
}
.monaco-editor .suggest-widget-deprecated span {
text-decoration: line-through;
}
/** Icon styles **/
.monaco-editor .suggest-widget .details > .monaco-scrollable-element > .body > .header > .close,

View File

@@ -30,7 +30,7 @@ import { MarkdownRenderer } from 'vs/editor/contrib/markdown/markdownRenderer';
import { IModeService } from 'vs/editor/common/services/modeService';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { TimeoutTimer, CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async';
import { CompletionItemKind, completionKindToCssClass } from 'vs/editor/common/modes';
import { CompletionItemKind, completionKindToCssClass, CompletionItemKindModifier } from 'vs/editor/common/modes';
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { getIconClasses } from 'vs/editor/common/services/getIconClasses';
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -38,6 +38,7 @@ import { URI } from 'vs/base/common/uri';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { FileKind } from 'vs/platform/files/common/files';
import { MarkdownString } from 'vs/base/common/htmlContent';
import { flatten } from 'vs/base/common/arrays';
const expandSuggestionDocsByDefault = false;
@@ -60,7 +61,6 @@ export const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.
export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: listFocusBackground, light: listFocusBackground, hc: listFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.'));
export const editorSuggestWidgetHighlightForeground = registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hc: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.'));
const colorRegExp = /^(#([\da-f]{3}){1,2}|(rgb|hsl)a\(\s*(\d{1,3}%?\s*,\s*){3}(1|0?\.\d+)\)|(rgb|hsl)\(\s*\d{1,3}%?(\s*,\s*\d{1,3}%?){2}\s*\))$/i;
function extractColor(item: CompletionItem, out: string[]): boolean {
if (item.completion.label.match(colorRegExp)) {
@@ -173,18 +173,18 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
} else if (suggestion.kind === CompletionItemKind.File && this._themeService.getIconTheme().hasFileIcons) {
// special logic for 'file' completion items
data.icon.className = 'icon hide';
labelOptions.extraClasses = ([] as string[]).concat(
labelOptions.extraClasses = flatten([
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FILE),
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FILE)
);
]);
} else if (suggestion.kind === CompletionItemKind.Folder && this._themeService.getIconTheme().hasFolderIcons) {
// special logic for 'folder' completion items
data.icon.className = 'icon hide';
labelOptions.extraClasses = ([] as string[]).concat(
labelOptions.extraClasses = flatten([
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.label }), FileKind.FOLDER),
getIconClasses(this._modelService, this._modeService, URI.from({ scheme: 'fake', path: suggestion.detail }), FileKind.FOLDER)
);
]);
} else {
// normal icon
data.icon.className = 'icon hide';
@@ -193,6 +193,10 @@ class Renderer implements IListRenderer<CompletionItem, ISuggestionTemplateData>
];
}
if (suggestion.kindModifier && suggestion.kindModifier & CompletionItemKindModifier.Deprecated) {
labelOptions.extraClasses = (labelOptions.extraClasses || []).concat(['suggest-widget-deprecated']);
}
data.iconLabel.setLabel(suggestion.label, undefined, labelOptions);
data.typeLabel.textContent = (suggestion.detail || '').replace(/\n.*$/m, '');

View File

@@ -38,9 +38,6 @@ import { INotificationService } from 'vs/platform/notification/common/notificati
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility';
import { clearAllFontInfos } from 'vs/editor/browser/config/configuration';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProductService } from 'vs/platform/product/common/product';
import { IStorageService } from 'vs/platform/storage/common/storage';
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
@@ -54,13 +51,7 @@ function withAllStandaloneServices<T extends editorCommon.IEditor>(domElement: H
}
if (!services.has(IOpenerService)) {
services.set(IOpenerService, new OpenerService(
services.get(ICodeEditorService),
services.get(ICommandService),
services.get(IStorageService),
services.get(IDialogService),
services.get(IProductService)
));
services.set(IOpenerService, new OpenerService(services.get(ICodeEditorService), services.get(ICommandService)));
}
let result = callback(services);

View File

@@ -562,6 +562,7 @@ export function createMonacoLanguagesAPI(): typeof monaco.languages {
// enums
DocumentHighlightKind: standaloneEnums.DocumentHighlightKind,
CompletionItemKind: standaloneEnums.CompletionItemKind,
CompletionItemKindModifier: standaloneEnums.CompletionItemKindModifier,
CompletionItemInsertTextRule: standaloneEnums.CompletionItemInsertTextRule,
SymbolKind: standaloneEnums.SymbolKind,
IndentAction: standaloneEnums.IndentAction,

View File

@@ -7,18 +7,13 @@ import { URI } from 'vs/base/common/uri';
import { OpenerService } from 'vs/editor/browser/services/openerService';
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands';
import { deepClone } from 'vs/base/common/objects';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProductService } from 'vs/platform/product/common/product';
import { IStorageService } from 'vs/platform/storage/common/storage';
suite('OpenerService', function () {
const editorService = new TestCodeEditorService();
let lastCommand: { id: string, args: any[] } | undefined;
let lastCommand: { id: string; args: any[] } | undefined;
const commandService = new class implements ICommandService {
const commandService = new (class implements ICommandService {
_serviceBrand: any;
onWillExecuteCommand = () => ({ dispose: () => { } });
onDidExecuteCommand = () => ({ dispose: () => { } });
@@ -26,79 +21,20 @@ suite('OpenerService', function () {
lastCommand = { id, args };
return Promise.resolve(undefined);
}
};
function getStorageService(trustedDomainsSetting: string[]) {
let _settings = deepClone(trustedDomainsSetting);
return new class implements IStorageService {
get = () => JSON.stringify(_settings);
store = (key: string, val: string) => _settings = JSON.parse(val);
// Don't care
_serviceBrand: any;
onDidChangeStorage = () => ({ dispose: () => { } });
onWillSaveState = () => ({ dispose: () => { } });
getBoolean = () => true;
getNumber = () => 0;
remove = () => { };
logStorage = () => { };
};
}
function getDialogService() {
return new class implements IDialogService {
_showInvoked = 0;
show = () => {
this._showInvoked++;
return Promise.resolve({} as any);
}
get confirmInvoked() { return this._showInvoked; }
// Don't care
_serviceBrand: any;
confirm = () => {
return Promise.resolve({} as any);
}
};
}
function getProductService(): IProductService {
return new class {
nameShort: 'VS Code';
_serviceBrand: any;
} as IProductService;
}
})();
setup(function () {
lastCommand = undefined;
});
test('delegate to editorService, scheme:///fff', function () {
const openerService = new OpenerService(
editorService,
NullCommandService,
getStorageService([]),
getDialogService(),
getProductService()
);
const openerService = new OpenerService(editorService, NullCommandService);
openerService.open(URI.parse('another:///somepath'));
assert.equal(editorService.lastInput!.options!.selection, undefined);
});
test('delegate to editorService, scheme:///fff#L123', function () {
const openerService = new OpenerService(
editorService,
NullCommandService,
getStorageService([]),
getDialogService(),
getProductService()
);
const openerService = new OpenerService(editorService, NullCommandService);
openerService.open(URI.parse('file:///somepath#L23'));
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
@@ -120,14 +56,7 @@ suite('OpenerService', function () {
});
test('delegate to editorService, scheme:///fff#123,123', function () {
const openerService = new OpenerService(
editorService,
NullCommandService,
getStorageService([]),
getDialogService(),
getProductService()
);
const openerService = new OpenerService(editorService, NullCommandService);
openerService.open(URI.parse('file:///somepath#23'));
assert.equal(editorService.lastInput!.options!.selection!.startLineNumber, 23);
@@ -145,14 +74,7 @@ suite('OpenerService', function () {
});
test('delegate to commandsService, command:someid', function () {
const openerService = new OpenerService(
editorService,
commandService,
getStorageService([]),
getDialogService(),
getProductService()
);
const openerService = new OpenerService(editorService, commandService);
const id = `aCommand${Math.random()}`;
CommandsRegistry.registerCommand(id, function () { });
@@ -173,69 +95,107 @@ suite('OpenerService', function () {
assert.equal(lastCommand!.args[1], true);
});
test('links are protected by dialog.show', function () {
const dialogService = getDialogService();
const openerService = new OpenerService(
editorService,
commandService,
getStorageService([]),
dialogService,
getProductService()
);
test('links are protected by validators', async function () {
const openerService = new OpenerService(editorService, commandService);
openerService.open(URI.parse('https://www.microsoft.com'));
assert.equal(dialogService.confirmInvoked, 1);
openerService.registerValidator({ shouldOpen: () => Promise.resolve(false) });
const httpResult = await openerService.open(URI.parse('https://www.microsoft.com'));
const httpsResult = await openerService.open(URI.parse('https://www.microsoft.com'));
assert.equal(httpResult, false);
assert.equal(httpsResult, false);
});
test('links on the whitelisted domains can be opened without dialog.show', function () {
const dialogService = getDialogService();
const openerService = new OpenerService(
editorService,
commandService,
getStorageService(['https://microsoft.com']),
dialogService,
getProductService()
);
test('links validated by validators go to openers', async function () {
const openerService = new OpenerService(editorService, commandService);
openerService.open(URI.parse('https://microsoft.com'));
openerService.open(URI.parse('https://microsoft.com/'));
openerService.open(URI.parse('https://microsoft.com/en-us/'));
openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar'));
openerService.open(URI.parse('https://microsoft.com/en-us/?foo=bar#baz'));
openerService.registerValidator({ shouldOpen: () => Promise.resolve(true) });
assert.equal(dialogService.confirmInvoked, 0);
let openCount = 0;
openerService.registerOpener({
open: (resource: URI) => {
openCount++;
return Promise.resolve(true);
}
});
await openerService.open(URI.parse('http://microsoft.com'));
assert.equal(openCount, 1);
await openerService.open(URI.parse('https://microsoft.com'));
assert.equal(openCount, 2);
});
test('variations of links are protected by dialog confirmation', function () {
const dialogService = getDialogService();
const openerService = new OpenerService(
editorService,
commandService,
getStorageService(['https://microsoft.com']),
dialogService,
getProductService()
);
test('links validated by multiple validators', async function () {
const openerService = new OpenerService(editorService, commandService);
openerService.open(URI.parse('http://microsoft.com'));
openerService.open(URI.parse('https://www.microsoft.com'));
let v1 = 0;
openerService.registerValidator({
shouldOpen: () => {
v1++;
return Promise.resolve(true);
}
});
assert.equal(dialogService.confirmInvoked, 2);
let v2 = 0;
openerService.registerValidator({
shouldOpen: () => {
v2++;
return Promise.resolve(true);
}
});
let openCount = 0;
openerService.registerOpener({
open: (resource: URI) => {
openCount++;
return Promise.resolve(true);
}
});
await openerService.open(URI.parse('http://microsoft.com'));
assert.equal(openCount, 1);
assert.equal(v1, 1);
assert.equal(v2, 1);
await openerService.open(URI.parse('https://microsoft.com'));
assert.equal(openCount, 2);
assert.equal(v1, 2);
assert.equal(v2, 2);
});
test('* removes all link protection', function () {
const dialogService = getDialogService();
const openerService = new OpenerService(
editorService,
commandService,
getStorageService(['*']),
dialogService,
getProductService()
);
test('links invalidated by first validator do not continue validating', async function () {
const openerService = new OpenerService(editorService, commandService);
openerService.open(URI.parse('https://code.visualstudio.com/'));
openerService.open(URI.parse('https://www.microsoft.com'));
openerService.open(URI.parse('https://www.github.com'));
let v1 = 0;
openerService.registerValidator({
shouldOpen: () => {
v1++;
return Promise.resolve(false);
}
});
assert.equal(dialogService.confirmInvoked, 0);
let v2 = 0;
openerService.registerValidator({
shouldOpen: () => {
v2++;
return Promise.resolve(true);
}
});
let openCount = 0;
openerService.registerOpener({
open: (resource: URI) => {
openCount++;
return Promise.resolve(true);
}
});
await openerService.open(URI.parse('http://microsoft.com'));
assert.equal(openCount, 0);
assert.equal(v1, 1);
assert.equal(v2, 0);
await openerService.open(URI.parse('https://microsoft.com'));
assert.equal(openCount, 0);
assert.equal(v1, 2);
assert.equal(v2, 0);
});
});

9
src/vs/monaco.d.ts vendored
View File

@@ -4790,6 +4790,10 @@ declare namespace monaco.languages {
Snippet = 25
}
export enum CompletionItemKindModifier {
Deprecated = 1
}
export enum CompletionItemInsertTextRule {
/**
* Adjust whitespace/indentation of multiline insert texts to
@@ -4818,6 +4822,11 @@ declare namespace monaco.languages {
* an icon is chosen by the editor.
*/
kind: CompletionItemKind;
/**
* A modifier to the `kind` which affect how the item
* is rendered, e.g. Deprecated is rendered with a strikeout
*/
kindModifier?: CompletionItemKindModifier;
/**
* A human-readable string with additional information
* about this item, like type or symbol information.

View File

@@ -10,6 +10,7 @@ import { IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview
import { IHistoryNavigationWidget } from 'vs/base/browser/history';
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { ReplaceInput, IReplaceInputOptions } from 'vs/base/browser/ui/findinput/replaceInput';
export const HistoryNavigationWidgetContext = 'historyNavigationWidget';
export const HistoryNavigationEnablementContext = 'historyNavigationEnabled';
@@ -60,6 +61,16 @@ export class ContextScopedFindInput extends FindInput {
super(container, contextViewProvider, showFindOptions, options);
this._register(createAndBindHistoryNavigationWidgetScopedContextKeyService(contextKeyService, <IContextScopedHistoryNavigationWidget>{ target: this.inputBox.element, historyNavigator: this.inputBox }).scopedContextKeyService);
}
}
export class ContextScopedReplaceInput extends ReplaceInput {
constructor(container: HTMLElement | null, contextViewProvider: IContextViewProvider | undefined, options: IReplaceInputOptions,
@IContextKeyService contextKeyService: IContextKeyService, showReplaceOptions: boolean = false
) {
super(container, contextViewProvider, showReplaceOptions, options);
this._register(createAndBindHistoryNavigationWidgetScopedContextKeyService(contextKeyService, <IContextScopedHistoryNavigationWidget>{ target: this.inputBox.element, historyNavigator: this.inputBox }).scopedContextKeyService);
}
}

View File

@@ -76,16 +76,27 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise
return done(results);
}
if (token.count > MAX_FILES) {
token.count += files.length;
token.maxReached = true;
return done(results);
}
let pending = files.length;
if (pending === 0) {
return done(results);
}
for (const file of files) {
if (token.maxReached) {
return done(results);
}
let filesToRead = files;
if (token.count + files.length > MAX_FILES) {
token.maxReached = true;
pending = MAX_FILES - token.count;
filesToRead = files.slice(0, pending);
}
token.count += files.length;
for (const file of filesToRead) {
stat(join(dir, file), (err, stats) => {
// Ignore files that can't be read
if (err) {
@@ -108,11 +119,6 @@ export function collectWorkspaceStats(folder: string, filter: string[]): Promise
}
}
} else {
if (token.count >= MAX_FILES) {
token.maxReached = true;
}
token.count++;
results.push(file);
if (--pending === 0) {

View File

@@ -0,0 +1,134 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { Queue } from 'vs/base/common/async';
import { VSBuffer } from 'vs/base/common/buffer';
export class FileLogService extends AbstractLogService implements ILogService {
_serviceBrand: any;
private readonly queue: Queue<void>;
constructor(
private readonly name: string,
private readonly resource: URI,
level: LogLevel,
@IFileService private readonly fileService: IFileService
) {
super();
this.setLevel(level);
this.queue = this._register(new Queue<void>());
}
trace(): void {
if (this.getLevel() <= LogLevel.Trace) {
this._log(LogLevel.Trace, this.format(arguments));
}
}
debug(): void {
if (this.getLevel() <= LogLevel.Debug) {
this._log(LogLevel.Debug, this.format(arguments));
}
}
info(): void {
if (this.getLevel() <= LogLevel.Info) {
this._log(LogLevel.Info, this.format(arguments));
}
}
warn(): void {
if (this.getLevel() <= LogLevel.Warning) {
this._log(LogLevel.Warning, this.format(arguments));
}
}
error(): void {
if (this.getLevel() <= LogLevel.Error) {
const arg = arguments[0];
if (arg instanceof Error) {
const array = Array.prototype.slice.call(arguments) as any[];
array[0] = arg.stack;
this._log(LogLevel.Error, this.format(array));
} else {
this._log(LogLevel.Error, this.format(arguments));
}
}
}
critical(): void {
if (this.getLevel() <= LogLevel.Critical) {
this._log(LogLevel.Critical, this.format(arguments));
}
}
flush(): Promise<void> {
return this.queue.queue(() => Promise.resolve());
}
log(level: LogLevel, args: any[]): void {
this._log(level, this.format(args));
}
private _log(level: LogLevel, message: string): void {
this.queue.queue(async () => {
let content = await this.loadContent();
content += `[${this.getCurrentTimestamp()}] [${this.name}] [${this.stringifyLogLevel(level)}] ${message}\n`;
await this.fileService.writeFile(this.resource, VSBuffer.fromString(content));
});
}
private getCurrentTimestamp(): string {
const toTwoDigits = (v: number) => v < 10 ? `0${v}` : v;
const toThreeDigits = (v: number) => v < 10 ? `00${v}` : v < 100 ? `0${v}` : v;
const currentTime = new Date();
return `${currentTime.getFullYear()}-${toTwoDigits(currentTime.getMonth() + 1)}-${toTwoDigits(currentTime.getDate())} ${toTwoDigits(currentTime.getHours())}:${toTwoDigits(currentTime.getMinutes())}:${toTwoDigits(currentTime.getSeconds())}.${toThreeDigits(currentTime.getMilliseconds())}`;
}
private async loadContent(): Promise<string> {
try {
const content = await this.fileService.readFile(this.resource);
return content.value.toString();
} catch (e) {
return '';
}
}
private stringifyLogLevel(level: LogLevel): string {
switch (level) {
case LogLevel.Critical: return 'critical';
case LogLevel.Debug: return 'debug';
case LogLevel.Error: return 'error';
case LogLevel.Info: return 'info';
case LogLevel.Trace: return 'trace';
case LogLevel.Warning: return 'warning';
}
return '';
}
private format(args: any): string {
let result = '';
for (let i = 0; i < args.length; i++) {
let a = args[i];
if (typeof a === 'object') {
try {
a = JSON.stringify(a);
} catch (e) { }
}
result += (i > 0 ? ' ' : '') + a;
}
return result;
}
}

View File

@@ -14,6 +14,10 @@ export interface IOpener {
open(resource: URI, options?: { openExternal?: boolean }): Promise<boolean>;
}
export interface IValidator {
shouldOpen(resource: URI): Promise<boolean>;
}
export interface IOpenerService {
_serviceBrand: any;
@@ -23,6 +27,12 @@ export interface IOpenerService {
*/
registerOpener(opener: IOpener): IDisposable;
/**
* Register a participant that can validate if the URI resource be opened.
* validators are run before openers.
*/
registerValidator(validator: IValidator): IDisposable;
/**
* Opens a resource, like a webaddress, a document uri, or executes command.
*
@@ -36,5 +46,6 @@ export interface IOpenerService {
export const NullOpenerService: IOpenerService = Object.freeze({
_serviceBrand: undefined,
registerOpener() { return { dispose() { } }; },
registerValidator() { return { dispose() { } }; },
open() { return Promise.resolve(false); },
});

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver';
import { RemoteAuthorities } from 'vs/base/common/network';
export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverService {
@@ -15,13 +16,14 @@ export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverS
resolveAuthority(authority: string): Promise<ResolverResult> {
if (authority.indexOf(':') >= 0) {
const pieces = authority.split(':');
return Promise.resolve({
authority: { authority, host: pieces[0], port: parseInt(pieces[1], 10) }
});
return Promise.resolve(this._createResolvedAuthority(authority, pieces[0], parseInt(pieces[1], 10)));
}
return Promise.resolve({
authority: { authority, host: authority, port: 80 }
});
return Promise.resolve(this._createResolvedAuthority(authority, authority, 80));
}
private _createResolvedAuthority(authority: string, host: string, port: number): ResolverResult {
RemoteAuthorities.set(authority, host, port);
return { authority: { authority, host, port } };
}
clearResolvedAuthority(authority: string): void {

View File

@@ -1141,4 +1141,15 @@ declare module 'vscode' {
}
//#endregion
//#region Deprecated support
export interface CompletionItem {
/**
* Indicates if this item is deprecated.
*/
deprecated?: boolean;
}
//#endregion
}

View File

@@ -330,6 +330,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
return {
label: data.a,
kind: data.b,
kindModifier: data.n ? modes.CompletionItemKindModifier.Deprecated : undefined,
detail: data.c,
documentation: data.d,
sortText: data.e,
@@ -366,7 +367,7 @@ export class MainThreadLanguageFeatures implements MainThreadLanguageFeaturesSha
};
if (supportsResolveDetails) {
provider.resolveCompletionItem = (model, position, suggestion, token) => {
return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id, token).then(result => {
return this._proxy.$resolveCompletionItem(handle, model.uri, position, suggestion._id!, token).then(result => {
if (!result) {
return suggestion;
}

View File

@@ -3,20 +3,46 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { IExtHostContext, ExtHostContext } from 'vs/workbench/api/common/extHost.protocol';
import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IExtHostContext, ExtHostContext, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { UriComponents, URI } from 'vs/base/common/uri';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { basename } from 'vs/base/common/path';
@extHostCustomer
export class MainThreadLogService extends Disposable {
@extHostNamedCustomer(MainContext.MainThreadLog)
export class MainThreadLogService implements MainThreadLogShape {
private readonly _loggers = new Map<string, FileLogService>();
private readonly _logListener: IDisposable;
constructor(
extHostContext: IExtHostContext,
@ILogService logService: ILogService,
@ILogService private readonly _logService: ILogService,
@IInstantiationService private readonly _instaService: IInstantiationService,
) {
super();
this._register(logService.onDidChangeLogLevel(level => extHostContext.getProxy(ExtHostContext.ExtHostLogService).$setLevel(level)));
const proxy = extHostContext.getProxy(ExtHostContext.ExtHostLogService);
this._logListener = _logService.onDidChangeLogLevel(level => {
proxy.$setLevel(level);
this._loggers.forEach(value => value.setLevel(level));
});
}
}
dispose(): void {
this._logListener.dispose();
this._loggers.forEach(value => value.dispose());
this._loggers.clear();
}
$log(file: UriComponents, level: LogLevel, message: any[]): void {
const uri = URI.revive(file);
let logger = this._loggers.get(uri.toString());
if (!logger) {
logger = this._instaService.createInstance(FileLogService, basename(file.path), URI.revive(file), this._logService.getLevel());
this._loggers.set(uri.toString(), logger);
}
logger.log(level, message);
}
}

View File

@@ -292,7 +292,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
viewStates[handle] = {
visible: input === group.activeEditor,
active: input === activeInput,
position: editorGroupToViewColumn(this._editorGroupService, group.id || 0),
position: editorGroupToViewColumn(this._editorGroupService, group.id),
};
}
}
@@ -304,10 +304,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews
}
private onDidClickLink(handle: WebviewPanelHandle, link: URI): void {
if (!link) {
return;
}
const webview = this.getWebviewEditorInput(handle);
if (this.isSupportedLink(webview, link)) {
this._openerService.open(link);

View File

@@ -939,6 +939,7 @@ export interface ISuggestDataDto {
k/* commitCharacters */?: string[];
l/* additionalTextEdits */?: ISingleEditOperation[];
m/* command */?: modes.Command;
n/* deprecated */?: boolean;
// not-standard
x?: ChainedCacheId;
}
@@ -1287,6 +1288,10 @@ export interface ExtHostLogServiceShape {
$setLevel(level: LogLevel): void;
}
export interface MainThreadLogShape {
$log(file: UriComponents, level: LogLevel, args: any[]): void;
}
export interface ExtHostOutputServiceShape {
$setVisibleChannel(channelId: string | null): void;
}
@@ -1329,6 +1334,7 @@ export const MainContext = {
MainThreadKeytar: createMainId<MainThreadKeytarShape>('MainThreadKeytar'),
MainThreadLanguageFeatures: createMainId<MainThreadLanguageFeaturesShape>('MainThreadLanguageFeatures'),
MainThreadLanguages: createMainId<MainThreadLanguagesShape>('MainThreadLanguages'),
MainThreadLog: createMainId<MainThreadLogShape>('MainThread'),
MainThreadMessageService: createMainId<MainThreadMessageServiceShape>('MainThreadMessageService'),
MainThreadOutputService: createMainId<MainThreadOutputServiceShape>('MainThreadOutputService'),
MainThreadProgress: createMainId<MainThreadProgressShape>('MainThreadProgress'),

View File

@@ -736,6 +736,7 @@ class SuggestAdapter {
k: item.commitCharacters,
l: item.additionalTextEdits && item.additionalTextEdits.map(typeConvert.TextEdit.from),
m: this._commands.toInternal(item.command, disposables),
n: item.deprecated
};
// 'insertText'-logic

View File

@@ -8,7 +8,6 @@ import { ExtensionActivationTimesBuilder } from 'vs/workbench/api/common/extHost
import { AbstractExtHostExtensionService } from 'vs/workbench/api/common/extHostExtensionService';
import { endsWith, startsWith } from 'vs/base/common/strings';
import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { joinPath } from 'vs/base/common/resources';
import { RequireInterceptor } from 'vs/workbench/api/common/extHostRequireInterceptor';
@@ -128,7 +127,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
const next = joinPath(parent, '..', ensureSuffix(mod, '.js'));
moduleStack.push(next);
const trap = ExportsTrap.Instance.add(next.toString());
importScripts(asDomUri(next).toString(true));
importScripts(next.toString(true));
moduleStack.pop();
return trap.claim();
@@ -139,7 +138,7 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
module = module.with({ path: ensureSuffix(module.path, '.js') });
moduleStack.push(module);
const trap = ExportsTrap.Instance.add(module.toString());
importScripts(asDomUri(module).toString(true));
importScripts(module.toString(true));
moduleStack.pop();
return Promise.resolve<T>(trap.claim());
@@ -153,16 +152,6 @@ export class ExtHostExtensionService extends AbstractExtHostExtensionService {
}
}
// todo@joh this is a copy of `dom.ts#asDomUri`
function asDomUri(uri: URI): URI {
if (Schemas.vscodeRemote === uri.scheme) {
// rewrite vscode-remote-uris to uris of the window location
// so that they can be intercepted by the service worker
return URI.parse(window.location.href).with({ path: '/vscode-remote', query: JSON.stringify(uri) });
}
return uri;
}
function ensureSuffix(path: string, suffix: string): string {
return endsWith(path, suffix) ? path : path + suffix;
}

View File

@@ -4,24 +4,33 @@
*--------------------------------------------------------------------------------------------*/
import { ILogService, LogLevel, AbstractLogService } from 'vs/platform/log/common/log';
import { ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol';
import { ExtHostLogServiceShape, MainThreadLogShape, MainContext } from 'vs/workbench/api/common/extHost.protocol';
import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService';
import { IExtHostOutputService } from 'vs/workbench/api/common/extHostOutput';
import * as vscode from 'vscode';
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
import { joinPath } from 'vs/base/common/resources';
import { ExtensionHostLogFileName } from 'vs/workbench/services/extensions/common/extensions';
import { UriComponents } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
export class ExtHostLogService extends AbstractLogService implements ILogService, ExtHostLogServiceShape {
_serviceBrand: any;
private readonly _logChannel: vscode.OutputChannel;
private readonly _proxy: MainThreadLogShape;
private readonly _logFile: UriComponents;
constructor(
@IExtHostRpcService rpc: IExtHostRpcService,
@IExtHostInitDataService initData: IExtHostInitDataService,
@IExtHostOutputService extHostOutputService: IExtHostOutputService
) {
super();
const logFile = joinPath(initData.logsLocation, `${ExtensionHostLogFileName}.log`);
this._proxy = rpc.getProxy(MainContext.MainThreadLog);
this._logFile = logFile.toJSON();
this.setLevel(initData.logLevel);
this._logChannel = extHostOutputService.createOutputChannel('Log (Worker Extension Host)');
extHostOutputService.createOutputChannelFromLogFile(localize('name', "Worker Extension Host"), logFile);
}
$setLevel(level: LogLevel): void {
@@ -30,55 +39,37 @@ export class ExtHostLogService extends AbstractLogService implements ILogService
trace(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Trace) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Trace, Array.from(arguments));
}
}
debug(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Debug) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Debug, Array.from(arguments));
}
}
info(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Info) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Info, Array.from(arguments));
}
}
warn(_message: string, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Warning) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Warning, Array.from(arguments));
}
}
error(_message: string | Error, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Error) {
this._logChannel.appendLine(this._format(arguments));
this._proxy.$log(this._logFile, LogLevel.Error, Array.from(arguments));
}
}
critical(_message: string | Error, ..._args: any[]): void {
if (this.getLevel() <= LogLevel.Critical) {
this._logChannel.appendLine(String(arguments));
this._proxy.$log(this._logFile, LogLevel.Critical, Array.from(arguments));
}
}
private _format(args: any): string {
let result = '';
for (let i = 0; i < args.length; i++) {
let a = args[i];
if (typeof a === 'object') {
try {
a = JSON.stringify(a);
} catch (e) { }
}
result += (i > 0 ? ' ' : '') + a;
}
return result;
}
}

View File

@@ -8,7 +8,6 @@ import { domContentLoaded, addDisposableListener, EventType, addClass } from 'vs
import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable } from 'vs/base/common/lifecycle';
import { SimpleLogService } from 'vs/workbench/browser/web.simpleservices';
import { BrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService';
import { Workbench } from 'vs/workbench/browser/workbench';
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
@@ -42,6 +41,10 @@ import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/th
import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider';
import { registerWindowDriver } from 'vs/platform/driver/browser/driver';
import { StaticExtensionsService, IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions';
import { BufferLogService } from 'vs/platform/log/common/bufferLog';
import { FileLogService } from 'vs/platform/log/common/fileLogService';
import { toLocalISOString } from 'vs/base/common/date';
import { INDEXEDDB_LOG_SCHEME, IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider';
class CodeRendererMain extends Disposable {
@@ -117,13 +120,14 @@ class CodeRendererMain extends Disposable {
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// Log
const logService = new SimpleLogService();
const logsPath = URI.file(toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, '')).with({ scheme: INDEXEDDB_LOG_SCHEME });
const logService = new BufferLogService();
serviceCollection.set(ILogService, logService);
const payload = await this.resolveWorkspaceInitializationPayload();
// Environment
const environmentService = new BrowserWorkbenchEnvironmentService(payload.id, this.configuration);
const environmentService = new BrowserWorkbenchEnvironmentService({ workspaceId: payload.id, logsPath, ...this.configuration });
serviceCollection.set(IWorkbenchEnvironmentService, environmentService);
// Product
@@ -146,6 +150,10 @@ class CodeRendererMain extends Disposable {
const fileService = this._register(new FileService(logService));
serviceCollection.set(IFileService, fileService);
// Logger
fileService.registerProvider(INDEXEDDB_LOG_SCHEME, new IndexedDBLogProvider());
logService.logger = new FileLogService('window', environmentService.logFile, logService.getLevel(), fileService);
// Static Extensions
const staticExtensions = new StaticExtensionsService(this.configuration.staticExtensions || []);
serviceCollection.set(IStaticExtensionsService, staticExtensions);

View File

@@ -8,10 +8,9 @@ import * as browser from 'vs/base/browser/browser';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { Event } from 'vs/base/common/event';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionRecommendation } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
import { IURLHandler, IURLService } from 'vs/platform/url/common/url';
import { ConsoleLogService, ILogService } from 'vs/platform/log/common/log';
import { ILogService } from 'vs/platform/log/common/log';
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { IUpdateService, State } from 'vs/platform/update/common/update';
@@ -31,7 +30,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { ParsedArgs } from 'vs/platform/environment/common/environment';
import { IProcessEnvironment } from 'vs/base/common/platform';
import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProductService } from 'vs/platform/product/common/product';
import Severity from 'vs/base/common/severity';
@@ -40,55 +38,6 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService
// tslint:disable-next-line: import-patterns
import { IWorkspaceStatsService, Tags } from 'vs/workbench/contrib/stats/common/workspaceStats';
//#region Extension Tips
export class SimpleExtensionTipsService implements IExtensionTipsService {
_serviceBrand: any;
onRecommendationChange = Event.None;
getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason; reasonText: string; }; } {
return Object.create(null);
}
getFileBasedRecommendations(): IExtensionRecommendation[] {
return [];
}
getOtherRecommendations(): Promise<IExtensionRecommendation[]> {
return Promise.resolve([]);
}
getWorkspaceRecommendations(): Promise<IExtensionRecommendation[]> {
return Promise.resolve([]);
}
getKeymapRecommendations(): IExtensionRecommendation[] {
return [];
}
toggleIgnoredRecommendation(extensionId: string, shouldIgnore: boolean): void {
}
getAllIgnoredRecommendations(): { global: string[]; workspace: string[]; } {
return { global: [], workspace: [] };
}
// {{SQL CARBON EDIT}}
getRecommendedExtensionsByScenario(scenarioType: string): Promise<IExtensionRecommendation[]> {
return Promise.resolve([]);
}
promptRecommendedExtensionsByScenario(scenarioType: string): void {
return;
}
// {{SQL CARBON EDIT}} - End
}
registerSingleton(IExtensionTipsService, SimpleExtensionTipsService, true);
//#endregion
//#region Extension URL Handler
export const IExtensionUrlHandler = createDecorator<IExtensionUrlHandler>('inactiveExtensionUrlHandler');
@@ -112,12 +61,6 @@ registerSingleton(IExtensionUrlHandler, SimpleExtensionURLHandler, true);
//#endregion
//#region Log
export class SimpleLogService extends ConsoleLogService { }
//#endregion
//#region Update
export class SimpleUpdateService implements IUpdateService {
@@ -192,7 +135,6 @@ export class SimpleWindowService extends Disposable implements IWindowService {
@IStorageService private readonly storageService: IStorageService,
@IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService,
@ILogService private readonly logService: ILogService,
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService
) {
super();
@@ -388,7 +330,7 @@ export class SimpleWindowService extends Disposable implements IWindowService {
for (let i = 0; i < _uris.length; i++) {
const uri = _uris[i];
if ('folderUri' in uri) {
const newAddress = `${document.location.origin}/?folder=${uri.folderUri.path}${this.workbenchEnvironmentService.configuration.connectionToken ? `&tkn=${this.workbenchEnvironmentService.configuration.connectionToken}` : ''}`;
const newAddress = `${document.location.origin}/?folder=${uri.folderUri.path}`;
if (openFolderInNewWindow) {
window.open(newAddress);
} else {
@@ -475,7 +417,6 @@ export class SimpleWindowsService implements IWindowsService {
readonly onRecentlyOpenedChange: Event<void> = Event.None;
constructor(
@IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService,
@IDialogService private readonly dialogService: IDialogService,
@IProductService private readonly productService: IProductService,
@IClipboardService private readonly clipboardService: IClipboardService
@@ -667,11 +608,6 @@ export class SimpleWindowsService implements IWindowsService {
addQueryParameter('ibe', ibe);
}
// add connection token
if (this.workbenchEnvironmentService.configuration.connectionToken) {
addQueryParameter('tkn', this.workbenchEnvironmentService.configuration.connectionToken);
}
window.open(newAddress);
return Promise.resolve();

View File

@@ -9,7 +9,7 @@ import * as dom from 'vs/base/browser/dom';
import { FindInput, IFindInputStyles } from 'vs/base/browser/ui/findinput/findInput';
import { Widget } from 'vs/base/browser/ui/widget';
import { Delayer } from 'vs/base/common/async';
import { KeyCode, KeyMod } from 'vs/base/common/keyCodes';
import { KeyCode } from 'vs/base/common/keyCodes';
import { FindReplaceState } from 'vs/editor/contrib/find/findState';
import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { SimpleButton } from 'vs/editor/contrib/find/findWidget';
@@ -42,8 +42,7 @@ export abstract class SimpleFindWidget extends Widget {
@IContextViewService private readonly _contextViewService: IContextViewService,
@IContextKeyService contextKeyService: IContextKeyService,
private readonly _state: FindReplaceState = new FindReplaceState(),
showOptionButtons?: boolean,
private readonly _invertDefaultDirection: boolean = false
showOptionButtons?: boolean
) {
super();
@@ -94,20 +93,6 @@ export abstract class SimpleFindWidget extends Widget {
this.findFirst();
}));
this._register(this._findInput.onKeyDown((e) => {
if (e.equals(KeyCode.Enter)) {
this.find(this._invertDefaultDirection);
e.preventDefault();
return;
}
if (e.equals(KeyMod.Shift | KeyCode.Enter)) {
this.find(!this._invertDefaultDirection);
e.preventDefault();
return;
}
}));
this.prevBtn = this._register(new SimpleButton({
label: NLS_PREVIOUS_MATCH_BTN_LABEL,
className: 'previous',

View File

@@ -593,13 +593,13 @@ class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem
return isDebugModel(element) || isDebugSession(element) || (element instanceof Thread && element.stopped);
}
getChildren(element: IDebugModel | CallStackItem): Promise<CallStackItem[]> {
async getChildren(element: IDebugModel | CallStackItem): Promise<CallStackItem[]> {
if (isDebugModel(element)) {
const sessions = element.getSessions();
if (sessions.length === 0) {
return Promise.resolve([]);
}
if (sessions.length > 1 || this.debugService.getViewModel().isMultiSessionView()) {
if (sessions.length > 1) {
return Promise.resolve(sessions.filter(s => !s.parentSession));
}
@@ -609,9 +609,10 @@ class CallStackDataSource implements IAsyncDataSource<IDebugModel, CallStackItem
} else if (isDebugSession(element)) {
const childSessions = this.debugService.getModel().getSessions().filter(s => s.parentSession === element);
const threads: CallStackItem[] = element.getAllThreads();
if (threads.length === 1 && childSessions.length === 0) {
if (threads.length === 1) {
// Do not show thread when there is only one to be compact.
return this.getThreadChildren(<Thread>threads[0]);
const children = await this.getThreadChildren(<Thread>threads[0]);
return children.concat(childSessions);
}
return Promise.resolve(threads.concat(childSessions));

View File

@@ -245,6 +245,9 @@ export class DebugHoverWidget implements IContentWidget {
this.layoutTreeAndContainer();
this.editor.layoutContentWidget(this);
this.scrollbar.scanDomNode();
this.tree.scrollTop = 0;
this.tree.scrollLeft = 0;
if (focus) {
this.editor.render();
this.tree.domFocus();

View File

@@ -479,11 +479,11 @@ export class DebugService implements IDebugService {
});
}
private launchOrAttachToSession(session: IDebugSession, focus = true): Promise<void> {
private launchOrAttachToSession(session: IDebugSession, forceFocus = false): Promise<void> {
const dbgr = this.configurationManager.getDebugger(session.configuration.type);
return session.initialize(dbgr!).then(() => {
return session.launchOrAttach(session.configuration).then(() => {
if (focus) {
if (forceFocus || !this.viewModel.focusedSession) {
this.focusStackFrame(undefined, undefined, session);
}
});
@@ -572,7 +572,7 @@ export class DebugService implements IDebugService {
return runTasks().then(taskResult => taskResult === TaskRunResult.Success ? this.extensionHostDebugService.reload(session.getId()) : undefined);
}
const shouldFocus = this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();
const shouldFocus = !!this.viewModel.focusedSession && session.getId() === this.viewModel.focusedSession.getId();
// If the restart is automatic -> disconnect, otherwise -> terminate #55064
return (isAutoRestart ? session.disconnect(true) : session.terminate(true)).then(() => {

View File

@@ -131,6 +131,7 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution {
private registerListeners(): void {
this._register(this.debugService.onDidChangeState(() => this.updateScheduler.schedule()));
this._register(this.debugService.getViewModel().onDidFocusSession(() => this.updateScheduler.schedule()));
this._register(this.debugService.onDidNewSession(() => this.updateScheduler.schedule()));
this._register(this.configurationService.onDidChangeConfiguration(e => this.onDidConfigurationChange(e)));
this._register(this.actionBar.actionRunner.onDidRun((e: IRunEvent) => {
// check for error

View File

@@ -50,7 +50,7 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten
import { getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry';
import { isUndefined } from 'vs/base/common/types';
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
import { IWebviewService, Webview } from 'vs/workbench/contrib/webview/browser/webview';
import { IWebviewService, Webview, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { renderDashboardContributions } from 'sql/workbench/parts/extensions/browser/contributionRenders'; // {{SQL CARBON EDIT}}
import { generateUuid } from 'vs/base/common/uuid';
@@ -536,6 +536,12 @@ export class ExtensionEditor extends BaseEditor {
}
}
runFindAction(previous: boolean): void {
if (this.activeElement && (<Webview>this.activeElement).runFindAction) {
(<Webview>this.activeElement).runFindAction(previous);
}
}
private onNavbarChange(extension: IExtension, { id, focus }: { id: string | null, focus: boolean }, template: IExtensionEditorTemplate): void {
if (this.editorLoadComplete) {
/* __GDPR__
@@ -1326,28 +1332,66 @@ export class ExtensionEditor extends BaseEditor {
}
}
const contextKeyExpr = ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', ExtensionEditor.ID), ContextKeyExpr.not('editorFocus'));
class ShowExtensionEditorFindCommand extends Command {
public runCommand(accessor: ServicesAccessor, args: any): void {
const extensionEditor = this.getExtensionEditor(accessor);
const extensionEditor = getExtensionEditor(accessor);
if (extensionEditor) {
extensionEditor.showFind();
}
}
private getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor | null {
const activeControl = accessor.get(IEditorService).activeControl as ExtensionEditor;
if (activeControl instanceof ExtensionEditor) {
return activeControl;
}
return null;
}
}
const showCommand = new ShowExtensionEditorFindCommand({
(new ShowExtensionEditorFindCommand({
id: 'editor.action.extensioneditor.showfind',
precondition: ContextKeyExpr.and(ContextKeyExpr.equals('activeEditor', ExtensionEditor.ID), ContextKeyExpr.not('editorFocus')),
precondition: contextKeyExpr,
kbOpts: {
primary: KeyMod.CtrlCmd | KeyCode.KEY_F,
weight: KeybindingWeight.EditorContrib
}
});
showCommand.register();
})).register();
class StartExtensionEditorFindNextCommand extends Command {
public runCommand(accessor: ServicesAccessor, args: any): void {
const extensionEditor = getExtensionEditor(accessor);
if (extensionEditor) {
extensionEditor.runFindAction(false);
}
}
}
(new StartExtensionEditorFindNextCommand({
id: 'editor.action.extensioneditor.findNext',
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED),
kbOpts: {
primary: KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
})).register();
class StartExtensionEditorFindPreviousCommand extends Command {
public runCommand(accessor: ServicesAccessor, args: any): void {
const extensionEditor = getExtensionEditor(accessor);
if (extensionEditor) {
extensionEditor.runFindAction(true);
}
}
}
(new StartExtensionEditorFindPreviousCommand({
id: 'editor.action.extensioneditor.findPrevious',
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED),
kbOpts: {
primary: KeyMod.Shift | KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
})).register();
function getExtensionEditor(accessor: ServicesAccessor): ExtensionEditor | null {
const activeControl = accessor.get(IEditorService).activeControl as ExtensionEditor;
if (activeControl instanceof ExtensionEditor) {
return activeControl;
}
return null;
}

View File

@@ -1,11 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement';
import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService';
// Singletons
registerSingleton(IExtensionTipsService, ExtensionTipsService);

View File

@@ -3100,7 +3100,6 @@ export class InstallLocalExtensionsInRemoteAction extends Action {
private extensions: IExtension[] | undefined = undefined;
constructor(
private readonly selectAndInstall: boolean,
@IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService,
@IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService,
@IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService,
@@ -3122,9 +3121,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action {
get label(): string {
if (this.extensionManagementServerService.remoteExtensionManagementServer) {
return this.selectAndInstall ?
localize('select and install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label)
: localize('install local extensions', "Install Local Extensions in {0}", this.extensionManagementServerService.remoteExtensionManagementServer.label);
return localize('select and install local extensions', "Install Local Extensions in {0}...", this.extensionManagementServerService.remoteExtensionManagementServer.label);
}
return '';
}
@@ -3140,12 +3137,7 @@ export class InstallLocalExtensionsInRemoteAction extends Action {
}
async run(): Promise<void> {
if (this.selectAndInstall) {
return this.selectAndInstallLocalExtensions();
} else {
const extensionsToInstall = await this.queryExtensionsToInstall();
return this.installLocalExtensions(extensionsToInstall);
}
return this.selectAndInstallLocalExtensions();
}
private async queryExtensionsToInstall(): Promise<IExtension[]> {

View File

@@ -961,7 +961,7 @@ export class ServerExtensionsView extends ExtensionsListView {
getActions(): IAction[] {
if (this.extensionManagementServerService.remoteExtensionManagementServer && this.extensionManagementServerService.localExtensionManagementServer === this.server) {
const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, false));
const installLocalExtensionsInRemoteAction = this._register(this.instantiationService.createInstance(InstallLocalExtensionsInRemoteAction));
installLocalExtensionsInRemoteAction.class = 'octicon octicon-cloud-download';
return [installLocalExtensionsInRemoteAction];
}

View File

@@ -22,7 +22,7 @@ export class RemoteExtensionsInstaller extends Disposable implements IWorkbenchC
) {
super();
if (this.extensionManagementServerService.localExtensionManagementServer && this.extensionManagementServerService.remoteExtensionManagementServer) {
const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction, true);
const installLocalExtensionsInRemoteAction = instantiationService.createInstance(InstallLocalExtensionsInRemoteAction);
CommandsRegistry.registerCommand('workbench.extensions.installLocalExtensions', () => installLocalExtensionsInRemoteAction.run());
let disposable = Disposable.None;
const appendMenuItem = () => {

View File

@@ -11,6 +11,7 @@ import { IDecorationsProvider, IDecorationData } from 'vs/workbench/services/dec
import { listInvalidItemForeground } from 'vs/platform/theme/common/colorRegistry';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IExplorerService } from 'vs/workbench/contrib/files/common/files';
import { explorerRootErrorEmitter } from 'vs/workbench/contrib/files/browser/views/explorerViewer';
export class ExplorerDecorationsProvider implements IDecorationsProvider {
readonly label: string = localize('label', "Explorer");
@@ -30,6 +31,9 @@ export class ExplorerDecorationsProvider implements IDecorationsProvider {
this._onDidChange.fire([change.item.resource]);
}
}));
this.toDispose.add(explorerRootErrorEmitter.event((resource => {
this._onDidChange.fire([resource]);
})));
}
get onDidChange(): Event<URI[]> {

View File

@@ -50,6 +50,7 @@ import { first } from 'vs/base/common/arrays';
import { withNullAsUndefined } from 'vs/base/common/types';
import { IFileService, FileSystemProviderCapabilities } from 'vs/platform/files/common/files';
import { dispose } from 'vs/base/common/lifecycle';
import { timeout } from 'vs/base/common/async';
export class ExplorerView extends ViewletPanel {
static readonly ID: string = 'workbench.explorer.fileView';
@@ -522,6 +523,8 @@ export class ExplorerView extends ViewletPanel {
while (item && item.resource.toString() !== resource.toString()) {
await this.tree.expand(item);
// Tree returns too early from the expand, need to wait for next tick #77106
await timeout(0);
item = first(values(item.children), i => isEqualOrParent(resource, i.resource));
}

View File

@@ -46,6 +46,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic
import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces';
import { findValidPasteFileTarget } from 'vs/workbench/contrib/files/browser/fileActions';
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
import { Emitter } from 'vs/base/common/event';
export class ExplorerDelegate implements IListVirtualDelegate<ExplorerItem> {
@@ -60,6 +61,7 @@ export class ExplorerDelegate implements IListVirtualDelegate<ExplorerItem> {
}
}
export const explorerRootErrorEmitter = new Emitter<URI>();
export class ExplorerDataSource implements IAsyncDataSource<ExplorerItem | ExplorerItem[], ExplorerItem> {
constructor(
@@ -87,8 +89,9 @@ export class ExplorerDataSource implements IAsyncDataSource<ExplorerItem | Explo
// Single folder create a dummy explorer item to show error
const placeholder = new ExplorerItem(element.resource, undefined, false);
placeholder.isError = true;
return [placeholder];
} else {
explorerRootErrorEmitter.fire(element.resource);
}
} else {
// Do not show error for roots since we already use an explorer decoration to notify user

View File

@@ -247,9 +247,14 @@ export class ExplorerItem {
// Resolve metadata only when the mtime is needed since this can be expensive
// Mtime is only used when the sort order is 'modified'
const resolveMetadata = explorerService.sortOrder === 'modified';
const stat = await fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata });
const resolved = ExplorerItem.create(stat, this);
ExplorerItem.mergeLocalWithDisk(resolved, this);
try {
const stat = await fileService.resolve(this.resource, { resolveSingleChildDescendants: true, resolveMetadata });
const resolved = ExplorerItem.create(stat, this);
ExplorerItem.mergeLocalWithDisk(resolved, this);
} catch (e) {
this.isError = true;
throw e;
}
this._isDirectoryResolved = true;
}

View File

@@ -4,11 +4,90 @@
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { join } from 'vs/base/common/path';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { SetLogLevelAction } from 'vs/workbench/contrib/logs/common/logsActions';
import { SetLogLevelAction, OpenLogsFolderAction, OpenSessionLogFileAction } from 'vs/workbench/contrib/logs/common/logsActions';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';
import { IOutputChannelRegistry, Extensions as OutputExt } from 'vs/workbench/contrib/output/common/output';
import { Disposable } from 'vs/base/common/lifecycle';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { dirname } from 'vs/base/common/resources';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { isWeb } from 'vs/base/common/platform';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { LogsDataCleaner } from 'vs/workbench/contrib/logs/common/logsDataCleaner';
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory);
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(SetLogLevelAction, SetLogLevelAction.ID, SetLogLevelAction.LABEL), 'Developer: Set Log Level...', devCategory);
class LogOutputChannels extends Disposable implements IWorkbenchContribution {
constructor(
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@ILogService private readonly logService: ILogService,
@IFileService private readonly fileService: IFileService,
@IInstantiationService private readonly instantiationService: IInstantiationService
) {
super();
if (isWeb) {
this.registerWebContributions();
} else {
this.registerNativeContributions();
}
}
private registerWebContributions(): void {
Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels).registerChannel({ id: Constants.rendererLogChannelId, label: nls.localize('rendererLog', "Window"), file: this.environmentService.logFile, log: true });
this.instantiationService.createInstance(LogsDataCleaner);
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenSessionLogFileAction, OpenSessionLogFileAction.ID, OpenSessionLogFileAction.LABEL), 'Developer: Open Log File (Session)...', devCategory);
}
private registerNativeContributions(): void {
this.registerLogChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Main"), URI.file(join(this.environmentService.logsPath, `main.log`)));
this.registerLogChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Shared"), URI.file(join(this.environmentService.logsPath, `sharedprocess.log`)));
this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), this.environmentService.logFile);
const registerTelemetryChannel = (level: LogLevel) => {
if (level === LogLevel.Trace && !Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels).getChannel(Constants.telemetryLogChannelId)) {
this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), URI.file(join(this.environmentService.logsPath, `telemetry.log`)));
}
};
registerTelemetryChannel(this.logService.getLevel());
this.logService.onDidChangeLogLevel(registerTelemetryChannel);
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory);
}
private async registerLogChannel(id: string, label: string, file: URI): Promise<void> {
const outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
const exists = await this.fileService.exists(file);
if (exists) {
outputChannelRegistry.registerChannel({ id, label, file, log: true });
return;
}
const watcher = this.fileService.watch(dirname(file));
const disposable = this.fileService.onFileChanges(e => {
if (e.contains(file, FileChangeType.ADDED)) {
watcher.dispose();
disposable.dispose();
outputChannelRegistry.registerChannel({ id, label, file, log: true });
}
});
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored);

View File

@@ -9,8 +9,12 @@ import { join } from 'vs/base/common/path';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWindowsService } from 'vs/platform/windows/common/windows';
import { ILogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput';
import { URI } from 'vs/base/common/uri';
import { IFileService } from 'vs/platform/files/common/files';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { dirname, basename, isEqual } from 'vs/base/common/resources';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
export class OpenLogsFolderAction extends Action {
@@ -73,3 +77,68 @@ export class SetLogLevelAction extends Action {
return undefined;
}
}
export class OpenSessionLogFileAction extends Action {
static ID = 'workbench.action.openSessionLogFile';
static LABEL = nls.localize('openSessionLogFile', "Open Log File (Session)...");
constructor(id: string, label: string,
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IFileService private readonly fileService: IFileService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IEditorService private readonly editorService: IEditorService,
) {
super(id, label);
}
async run(): Promise<void> {
const sessionResult = await this.quickInputService.pick(
this.getSessions().then(sessions => sessions.map((s, index) => (<IQuickPickItem>{
id: s.toString(),
label: basename(s),
description: index === 0 ? nls.localize('current', "Current") : undefined
}))),
{
canPickMany: false,
placeHolder: nls.localize('sessions placeholder', "Select Session")
});
if (sessionResult) {
const logFileResult = await this.quickInputService.pick(
this.getLogFiles(URI.parse(sessionResult.id!)).then(logFiles => logFiles.map(s => (<IQuickPickItem>{
id: s.toString(),
label: basename(s)
}))),
{
canPickMany: false,
placeHolder: nls.localize('log placeholder', "Select Log file")
});
if (logFileResult) {
return this.editorService.openEditor({ resource: URI.parse(logFileResult.id!) }).then(() => undefined);
}
}
}
private async getSessions(): Promise<URI[]> {
const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme });
const result: URI[] = [logsPath];
const stat = await this.fileService.resolve(dirname(logsPath));
if (stat.children) {
result.push(...stat.children
.filter(stat => !isEqual(stat.resource, logsPath) && stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name))
.sort()
.reverse()
.map(d => d.resource));
}
return result;
}
private async getLogFiles(session: URI): Promise<URI[]> {
const stat = await this.fileService.resolve(session);
if (stat.children) {
return stat.children.filter(stat => !stat.isDirectory).map(stat => stat.resource);
}
return [];
}
}

View File

@@ -0,0 +1,44 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { Disposable } from 'vs/base/common/lifecycle';
import { IFileService } from 'vs/platform/files/common/files';
import { basename, dirname } from 'vs/base/common/resources';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { URI } from 'vs/base/common/uri';
import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle';
export class LogsDataCleaner extends Disposable {
constructor(
@IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService,
@IFileService private readonly fileService: IFileService,
@ILifecycleService private readonly lifecycleService: ILifecycleService,
) {
super();
this.cleanUpOldLogsSoon();
}
private cleanUpOldLogsSoon(): void {
let handle: NodeJS.Timeout | undefined = setTimeout(async () => {
handle = undefined;
const logsPath = URI.file(this.environmentService.logsPath).with({ scheme: this.environmentService.logFile.scheme });
const stat = await this.fileService.resolve(dirname(logsPath));
if (stat.children) {
const currentLog = basename(logsPath);
const allSessions = stat.children.filter(stat => stat.isDirectory && /^\d{8}T\d{6}$/.test(stat.name));
const oldSessions = allSessions.sort().filter((d, i) => d.name !== currentLog);
const toDelete = oldSessions.slice(0, Math.max(0, oldSessions.length - 49));
Promise.all(toDelete.map(stat => this.fileService.del(stat.resource, { recursive: true })));
}
}, 10 * 1000);
this.lifecycleService.onWillShutdown(() => {
if (handle) {
clearTimeout(handle);
handle = undefined;
}
});
}
}

View File

@@ -1,68 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as nls from 'vs/nls';
import { join } from 'vs/base/common/path';
import { Registry } from 'vs/platform/registry/common/platform';
import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions';
import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
import { Disposable } from 'vs/base/common/lifecycle';
import { URI } from 'vs/base/common/uri';
import * as Constants from 'vs/workbench/contrib/logs/common/logConstants';
import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions';
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
import { OpenLogsFolderAction } from 'vs/workbench/contrib/logs/common/logsActions';
import { ILogService, LogLevel } from 'vs/platform/log/common/log';
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
import { dirname } from 'vs/base/common/resources';
class LogOutputChannels extends Disposable implements IWorkbenchContribution {
constructor(
@IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService,
@ILogService logService: ILogService,
@IFileService private readonly fileService: IFileService
) {
super();
this.registerLogChannel(Constants.mainLogChannelId, nls.localize('mainLog', "Main"), URI.file(join(environmentService.logsPath, `main.log`)));
this.registerLogChannel(Constants.sharedLogChannelId, nls.localize('sharedLog', "Shared"), URI.file(join(environmentService.logsPath, `sharedprocess.log`)));
this.registerLogChannel(Constants.rendererLogChannelId, nls.localize('rendererLog', "Window"), URI.file(join(environmentService.logsPath, `renderer${environmentService.configuration.windowId}.log`)));
const registerTelemetryChannel = (level: LogLevel) => {
if (level === LogLevel.Trace && !Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels).getChannel(Constants.telemetryLogChannelId)) {
this.registerLogChannel(Constants.telemetryLogChannelId, nls.localize('telemetryLog', "Telemetry"), URI.file(join(environmentService.logsPath, `telemetry.log`)));
}
};
registerTelemetryChannel(logService.getLevel());
logService.onDidChangeLogLevel(registerTelemetryChannel);
const workbenchActionsRegistry = Registry.as<IWorkbenchActionRegistry>(WorkbenchActionExtensions.WorkbenchActions);
const devCategory = nls.localize('developer', "Developer");
workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenLogsFolderAction, OpenLogsFolderAction.ID, OpenLogsFolderAction.LABEL), 'Developer: Open Logs Folder', devCategory);
}
private async registerLogChannel(id: string, label: string, file: URI): Promise<void> {
const outputChannelRegistry = Registry.as<IOutputChannelRegistry>(OutputExt.OutputChannels);
const exists = await this.fileService.exists(file);
if (exists) {
outputChannelRegistry.registerChannel({ id, label, file, log: true });
return;
}
const watcher = this.fileService.watch(dirname(file));
const disposable = this.fileService.onFileChanges(e => {
if (e.contains(file, FileChangeType.ADDED)) {
watcher.dispose();
disposable.dispose();
outputChannelRegistry.registerChannel({ id, label, file, log: true });
}
});
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(LogOutputChannels, LifecyclePhase.Restored);

View File

@@ -109,7 +109,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
) {
super(KeybindingsEditor.ID, telemetryService, themeService, storageService);
this.delayedFiltering = new Delayer<void>(300);
this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(true)));
this._register(keybindingsService.onDidUpdateKeybindings(() => this.render(!!this.keybindingFocusContextKey.get())));
this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService);
this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
@@ -537,7 +537,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
}
this.unAssignedKeybindingItemToRevealAndFocus = null;
} else if (currentSelectedIndex !== -1 && currentSelectedIndex < this.listEntries.length) {
this.selectEntry(currentSelectedIndex);
this.selectEntry(currentSelectedIndex, preserveFocus);
} else if (this.editorService.activeControl === this && !preserveFocus) {
this.focus();
}
@@ -597,11 +597,13 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor
return -1;
}
private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number): void {
private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number, focus: boolean = true): void {
const index = typeof keybindingItemEntry === 'number' ? keybindingItemEntry : this.getIndexOf(keybindingItemEntry);
if (index !== -1) {
this.keybindingsList.getHTMLElement().focus();
this.keybindingsList.setFocus([index]);
if (focus) {
this.keybindingsList.getHTMLElement().focus();
this.keybindingsList.setFocus([index]);
}
this.keybindingsList.setSelection([index]);
}
}

View File

@@ -115,6 +115,7 @@ export class TOCRenderer implements ITreeRenderer<SettingsTreeGroupElement, neve
const label = element.label;
template.labelElement.textContent = label;
template.labelElement.title = label;
if (count) {
template.countElement.textContent = ` (${count})`;

View File

@@ -34,7 +34,7 @@ self.addEventListener('activate', event => {
//#region --- fetching/caching
const _cacheName = 'vscode-extension-resources';
const _resourcePrefix = '/vscode-remote';
const _resourcePrefix = '/vscode-remote-resource';
const _pendingFetch = new Map<string, Function>();
self.addEventListener('message', event => {

View File

@@ -10,7 +10,7 @@
// statement.
// trigger service worker updates
const _tag = '52278406-3ca9-48af-a8fb-8495add5bb4e';
const _tag = '23549971-9b8d-41bb-92ae-d7f6a68c9702';
// loader world
const baseUrl = '../../../../../';

View File

@@ -530,7 +530,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNe
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find next', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindNext, FindNext.ID, FindNext.LABEL, {
primary: KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3] }
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_G, secondary: [KeyCode.F3, KeyMod.Shift | KeyCode.Enter] }
}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find next');
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID_TERMINAL_FOCUS, FindPrevious.LABEL, {
primary: KeyMod.Shift | KeyCode.F3,
@@ -538,7 +538,7 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi
}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Find previous', category);
actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, FindPrevious.ID, FindPrevious.LABEL, {
primary: KeyMod.Shift | KeyCode.F3,
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3] },
mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3, KeyCode.Enter] },
}, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous');

View File

@@ -19,7 +19,7 @@ export class TerminalFindWidget extends SimpleFindWidget {
@IContextKeyService private readonly _contextKeyService: IContextKeyService,
@ITerminalService private readonly _terminalService: ITerminalService
) {
super(_contextViewService, _contextKeyService, findState, true, true);
super(_contextViewService, _contextKeyService, findState, true);
this._register(findState.onFindReplaceStateChange(() => {
this.show();
}));

View File

@@ -26,6 +26,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
export class TerminalService extends CommonTerminalService implements ITerminalService {
private _configHelper: IBrowserTerminalConfigHelper;
private _terminalContainer: HTMLElement | undefined;
public get configHelper(): ITerminalConfigHelper { return this._configHelper; }

View File

@@ -35,7 +35,6 @@ export abstract class TerminalService implements ITerminalService {
protected _isShuttingDown: boolean;
protected _terminalFocusContextKey: IContextKey<boolean>;
protected _findWidgetVisible: IContextKey<boolean>;
protected _terminalContainer: HTMLElement | undefined;
protected _terminalTabs: ITerminalTab[] = [];
protected _backgroundedTerminalInstances: ITerminalInstance[] = [];
protected get _terminalInstances(): ITerminalInstance[] {

View File

@@ -14,17 +14,24 @@ import { Action } from 'vs/base/common/actions';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IProductService } from 'vs/platform/product/common/product';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { equalsIgnoreCase } from 'vs/base/common/strings';
import { Schemas } from 'vs/base/common/network';
import Severity from 'vs/base/common/severity';
export class OpenUrlAction extends Action {
static readonly ID = 'workbench.action.url.openUrl';
static readonly LABEL = localize('openUrl', "Open URL");
static readonly LABEL = localize('openUrl', 'Open URL');
constructor(
id: string,
label: string,
@IURLService private readonly urlService: IURLService,
@IQuickInputService private readonly quickInputService: IQuickInputService,
@IQuickInputService private readonly quickInputService: IQuickInputService
) {
super(id, label);
}
@@ -45,7 +52,7 @@ Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions).registe
const VSCODE_DOMAIN = 'https://code.visualstudio.com';
const configureTrustedDomainsHandler = (
const configureTrustedDomainsHandler = async (
quickInputService: IQuickInputService,
storageService: IStorageService,
domainToConfigure?: string
@@ -66,7 +73,7 @@ const configureTrustedDomainsHandler = (
type: 'item',
label: d,
id: d,
picked: true,
picked: true
};
});
@@ -91,23 +98,24 @@ const configureTrustedDomainsHandler = (
specialQuickPickItems.push(<IQuickPickItem>domainToConfigureItem);
}
const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = domainQuickPickItems.length === 0
? specialQuickPickItems
: [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems];
const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] =
domainQuickPickItems.length === 0
? specialQuickPickItems
: [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems];
return quickInputService.pick(quickPickItems, {
const pickedResult = await quickInputService.pick(quickPickItems, {
canPickMany: true,
activeItem: domainToConfigureItem
}).then(result => {
if (result) {
const pickedDomains = result.map(r => r.id);
storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL);
return pickedDomains;
}
return [];
});
if (pickedResult) {
const pickedDomains: string[] = pickedResult.map(r => r.id!);
storageService.store('http.trustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL);
return pickedDomains;
}
return [];
};
const configureTrustedDomainCommand = {
@@ -131,3 +139,93 @@ MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
title: configureTrustedDomainCommand.description.description
}
});
class OpenerValidatorContributions implements IWorkbenchContribution {
constructor(
@IOpenerService private readonly _openerService: IOpenerService,
@IStorageService private readonly _storageService: IStorageService,
@IDialogService private readonly _dialogService: IDialogService,
@IProductService private readonly _productService: IProductService,
@IQuickInputService private readonly _quickInputService: IQuickInputService
) {
this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) });
}
async validateLink(resource: URI): Promise<boolean> {
const { scheme, authority } = resource;
if (!equalsIgnoreCase(scheme, Schemas.http) && !equalsIgnoreCase(scheme, Schemas.https)) {
return true;
}
let trustedDomains: string[] = [VSCODE_DOMAIN];
try {
const trustedDomainsSrc = this._storageService.get('http.trustedDomains', StorageScope.GLOBAL);
if (trustedDomainsSrc) {
trustedDomains = JSON.parse(trustedDomainsSrc);
}
} catch (err) { }
const domainToOpen = `${scheme}://${authority}`;
if (isDomainTrusted(domainToOpen, trustedDomains)) {
return true;
} else {
const choice = await this._dialogService.show(
Severity.Info,
localize(
'openExternalLinkAt',
'Do you want {0} to open the external website?\n{1}',
this._productService.nameShort,
resource.toString(true)
),
[
localize('openLink', 'Open Link'),
localize('cancel', 'Cancel'),
localize('configureTrustedDomains', 'Configure Trusted Domains')
],
{
cancelId: 1
}
);
// Open Link
if (choice === 0) {
return true;
}
// Configure Trusted Domains
else if (choice === 2) {
const pickedDomains = await configureTrustedDomainsHandler(this._quickInputService, this._storageService, domainToOpen);
if (pickedDomains.indexOf(domainToOpen) !== -1) {
return true;
}
return false;
}
return false;
}
}
}
Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench).registerWorkbenchContribution(
OpenerValidatorContributions,
LifecyclePhase.Restored
);
/**
* Check whether a domain like https://www.microsoft.com matches
* the list of trusted domains.
*/
function isDomainTrusted(domain: string, trustedDomains: string[]) {
for (let i = 0; i < trustedDomains.length; i++) {
if (trustedDomains[i] === '*') {
return true;
}
if (trustedDomains[i] === domain) {
return true;
}
}
return false;
}

View File

@@ -153,6 +153,7 @@ export class DynamicWebviewEditorOverlay extends Disposable implements WebviewEd
reload(): void { this.withWebview(webview => webview.reload()); }
showFind(): void { this.withWebview(webview => webview.showFind()); }
hideFind(): void { this.withWebview(webview => webview.hideFind()); }
runFindAction(previous: boolean): void { this.withWebview(webview => webview.runFindAction(previous)); }
public getInnerWebview() {
return this._webview.value;

View File

@@ -15,8 +15,8 @@ import { EditorDescriptor, Extensions as EditorExtensions, IEditorRegistry } fro
import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions';
import { Extensions as EditorInputExtensions, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor';
import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/webviewEditorInputFactory';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory } from 'vs/workbench/contrib/webview/browser/webview';
import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand } from '../browser/webviewCommands';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview';
import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands';
import { WebviewEditor } from '../browser/webviewEditor';
import { WebviewEditorInput } from '../browser/webviewEditorInput';
import { IWebviewEditorService, WebviewEditorService } from '../browser/webviewEditorService';
@@ -58,6 +58,28 @@ function registerWebViewCommands(editorId: string): void {
weight: KeybindingWeight.EditorContrib
}
})).register();
(new WebViewEditorFindNextCommand({
id: WebViewEditorFindNextCommand.ID,
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED),
kbOpts: {
primary: KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
})).register();
(new WebViewEditorFindPreviousCommand({
id: WebViewEditorFindPreviousCommand.ID,
precondition: ContextKeyExpr.and(
contextKeyExpr,
KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED),
kbOpts: {
primary: KeyMod.Shift | KeyCode.Enter,
weight: KeybindingWeight.EditorContrib
}
})).register();
}
registerWebViewCommands(WebviewEditor.ID);

View File

@@ -16,6 +16,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
* Set when the find widget in a webview is visible.
*/
export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE = new RawContextKey<boolean>('webviewFindWidgetVisible', false);
export const KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED = new RawContextKey<boolean>('webviewFindWidgetFocused', false);
export const IWebviewService = createDecorator<IWebviewService>('webviewService');
@@ -83,6 +84,7 @@ export interface Webview extends IDisposable {
showFind(): void;
hideFind(): void;
runFindAction(previous: boolean): void;
}
export interface WebviewElement extends Webview {

View File

@@ -32,6 +32,27 @@ export class HideWebViewEditorFindCommand extends Command {
}
}
export class WebViewEditorFindNextCommand extends Command {
public static readonly ID = 'editor.action.webvieweditor.findNext';
public runCommand(accessor: ServicesAccessor, args: any): void {
const webViewEditor = getActiveWebviewEditor(accessor);
if (webViewEditor) {
webViewEditor.find(false);
}
}
}
export class WebViewEditorFindPreviousCommand extends Command {
public static readonly ID = 'editor.action.webvieweditor.findPrevious';
public runCommand(accessor: ServicesAccessor, args: any): void {
const webViewEditor = getActiveWebviewEditor(accessor);
if (webViewEditor) {
webViewEditor.find(true);
}
}
}
export class ReloadWebviewAction extends Action {
static readonly ID = 'workbench.action.webview.reloadWebviewAction';
static readonly LABEL = nls.localize('refreshWebviewLabel', "Reload Webviews");
@@ -62,4 +83,4 @@ function getActiveWebviewEditor(accessor: ServicesAccessor): WebviewEditor | nul
const editorService = accessor.get(IEditorService);
const activeControl = editorService.activeControl as WebviewEditor;
return activeControl.isWebviewEditor ? activeControl : null;
}
}

View File

@@ -25,7 +25,6 @@ export class WebviewEditor extends BaseEditor {
private readonly _scopedContextKeyService = this._register(new MutableDisposable<IContextKeyService>());
private _findWidgetVisible: IContextKey<boolean>;
private _editorFrame?: HTMLElement;
private _content?: HTMLElement;
@@ -79,6 +78,12 @@ export class WebviewEditor extends BaseEditor {
this.withWebview(webview => webview.hideFind());
}
public find(previous: boolean) {
this.withWebview(webview => {
webview.runFindAction(previous);
});
}
public reload() {
this.withWebview(webview => webview.reload());
}

View File

@@ -268,6 +268,10 @@ export class IFrameWebview extends Disposable implements Webview {
throw new Error('Method not implemented.');
}
runFindAction(previous: boolean): void {
throw new Error('Method not implemented.');
}
public set state(state: string | undefined) {
this.content = {
html: this.content.html,

View File

@@ -4,8 +4,9 @@
*--------------------------------------------------------------------------------------------*/
import { SimpleFindWidget } from 'vs/workbench/contrib/codeEditor/browser/find/simpleFindWidget';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
import { IContextViewService } from 'vs/platform/contextview/browser/contextView';
import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview';
export interface WebviewFindDelegate {
find(value: string, previous: boolean): void;
@@ -15,6 +16,7 @@ export interface WebviewFindDelegate {
}
export class WebviewFindWidget extends SimpleFindWidget {
protected _findWidgetFocused: IContextKey<boolean>;
constructor(
private readonly _delegate: WebviewFindDelegate,
@@ -22,6 +24,7 @@ export class WebviewFindWidget extends SimpleFindWidget {
@IContextKeyService contextKeyService: IContextKeyService
) {
super(contextViewService, contextKeyService);
this._findWidgetFocused = KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED.bindTo(contextKeyService);
}
public find(previous: boolean) {
@@ -47,9 +50,13 @@ export class WebviewFindWidget extends SimpleFindWidget {
return false;
}
protected onFocusTrackerFocus() { }
protected onFocusTrackerFocus() {
this._findWidgetFocused.set(true);
}
protected onFocusTrackerBlur() { }
protected onFocusTrackerBlur() {
this._findWidgetFocused.reset();
}
protected onFindInputFocusTrackerFocus() { }

View File

@@ -319,7 +319,7 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
return;
case 'did-click-link':
let [uri] = event.args;
const [uri] = event.args;
this._onDidClickLink.fire(URI.parse(uri));
return;
@@ -334,12 +334,10 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
clientY: rawEvent.clientY + bounds.top,
}));
return;
}
catch (TypeError) {
} catch {
// CustomEvent was treated as MouseEvent so don't do anything - https://github.com/microsoft/vscode/issues/78915
return;
}
}
case 'did-set-content':
@@ -640,6 +638,12 @@ export class ElectronWebviewBasedWebview extends Disposable implements Webview {
}
}
public runFindAction(previous: boolean) {
if (this._webviewFindWidget) {
this._webviewFindWidget.find(previous);
}
}
public reload() {
this.doUpdateContent();
}

View File

@@ -5,7 +5,6 @@ The core editor in VS Code is packed with features. This page highlights a numb
* [IntelliSense](#intellisense) - get code assistance and parameter suggestions for your code and external modules.
* [Line Actions](#line-actions) - quickly move lines around to re-order your code.
* [Rename Refactoring](#rename-refactoring) - quickly rename symbols across your code base.
* [Refactoring via Extraction](#refactoring-via-extraction) - quickly extract common code into a separate function or constant.
* [Formatting](#formatting) - keep your code looking great with inbuilt document & selection formatting.
* [Code Folding](#code-folding) - focus on the most relevant parts of your code by folding other areas.
* [Errors and Warnings](#errors-and-warnings) - see errors and warning as you type.
@@ -90,21 +89,6 @@ function Book(title, author) {
> **JSDoc Tip:** VS Code's IntelliSense uses JSDoc comments to provide richer suggestions. The types and documentation from JSDoc comments show up when you hover over a reference to `Book` or in IntelliSense when you create a new instance of `Book`.
### Refactoring via Extraction
Sometimes you want to refactor already written code into a separate function or constant to reuse it later. Select the lines you want to refactor out and press kb(editor.action.quickFix) or click the little light bulb and choose one of the respective `Extract to...` options. Try it by selecting the code inside the `if`-clause on line 3 or any other common code you want to refactor out.
```js
function findFirstEvenNumber(arr) {
for (const el of arr) {
if (typeof el === 'number' && el % 2 === 0) {
return el;
}
}
return null;
}
```
### Formatting
Keeping your code looking great is hard without a good formatter. Luckily it's easy to format content, either for the entire document with kb(editor.action.formatDocument) or for the current selection with kb(editor.action.formatSelection). Both of these options are also available through the right-click context menu.

View File

@@ -275,7 +275,7 @@ export class EditorService extends Disposable implements EditorServiceImpl {
// Respect option to reveal an editor if it is already visible in any group
if (options && options.revealIfVisible) {
for (const group of groupsByLastActive) {
if (input.matches(group.activeEditor)) {
if (group.isActive(input)) {
targetGroup = group;
break;
}
@@ -283,11 +283,16 @@ export class EditorService extends Disposable implements EditorServiceImpl {
}
// Respect option to reveal an editor if it is open (not necessarily visible)
if ((options && options.revealIfOpened) || this.configurationService.getValue<boolean>('workbench.editor.revealIfOpen')) {
for (const group of groupsByLastActive) {
if (group.isOpened(input)) {
targetGroup = group;
break;
if (!targetGroup) {
if ((options && options.revealIfOpened) || this.configurationService.getValue<boolean>('workbench.editor.revealIfOpen')) {
for (const group of groupsByLastActive) {
if (group.isOpened(input) && group.isActive(input)) {
targetGroup = group;
break;
}
if (group.isOpened(input) && !targetGroup) {
targetGroup = group;
}
}
}
}

View File

@@ -62,11 +62,9 @@ export class BrowserWindowConfiguration implements IWindowConfiguration {
termProgram?: string;
}
export interface IBrowserWindowConfiguration {
interface IBrowserWorkbenchEnvironemntConstructionOptions extends IWorkbenchConstructionOptions {
workspaceId: string;
remoteAuthority?: string;
webviewEndpoint?: string;
connectionToken?: string;
logsPath: URI;
}
export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironmentService {
@@ -75,8 +73,10 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
readonly configuration: IWindowConfiguration = new BrowserWindowConfiguration();
constructor(workspaceId: string, public readonly options: IWorkbenchConstructionOptions) {
constructor(readonly options: IBrowserWorkbenchEnvironemntConstructionOptions) {
this.args = { _: [] };
this.logsPath = options.logsPath.path;
this.logFile = joinPath(options.logsPath, 'window.log');
this.appRoot = '/web/';
this.appNameLong = 'Visual Studio Code - Web';
@@ -88,10 +88,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
this.keyboardLayoutResource = joinPath(this.userRoamingDataHome, 'keyboardLayout.json');
this.localeResource = joinPath(this.userRoamingDataHome, 'locale.json');
this.backupHome = joinPath(this.userRoamingDataHome, BACKUPS);
this.configuration.backupWorkspaceResource = joinPath(this.backupHome, workspaceId);
this.configuration.connectionToken = options.connectionToken || this.getConnectionTokenFromLocation();
this.logsPath = '/web/logs';
this.configuration.backupWorkspaceResource = joinPath(this.backupHome, options.workspaceId);
this.configuration.connectionToken = options.connectionToken || getCookieValue('vscode-tkn');
this.debugExtensionHost = {
port: null,
@@ -183,6 +181,7 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
driverVerbose: boolean;
webviewEndpoint?: string;
galleryMachineIdResource?: URI;
readonly logFile: URI;
get webviewResourceRoot(): string {
return this.webviewEndpoint ? this.webviewEndpoint + '/vscode-resource{{resource}}' : 'vscode-resource:{{resource}}';
@@ -191,21 +190,12 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment
get webviewCspSource(): string {
return this.webviewEndpoint ? this.webviewEndpoint : 'vscode-resource:';
}
private getConnectionTokenFromLocation(): string | undefined {
// TODO: Check with @alexd where the token will be: search or hash?
let connectionToken: string | undefined = undefined;
if (document.location.search) {
connectionToken = this.getConnectionToken(document.location.search);
}
if (!connectionToken && document.location.hash) {
connectionToken = this.getConnectionToken(document.location.hash);
}
return connectionToken;
}
private getConnectionToken(str: string): string | undefined {
const m = str.match(/[#&?]tkn=([^&]+)/);
return m ? m[1] : undefined;
}
}
/**
* See https://stackoverflow.com/a/25490531
*/
function getCookieValue(name: string): string | undefined {
const m = document.cookie.match('(^|[^;]+)\\s*' + name + '\\s*=\\s*([^;]+)');
return m ? m.pop() : undefined;
}

View File

@@ -7,6 +7,7 @@ import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/co
import { IWindowConfiguration } from 'vs/platform/windows/common/windows';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api';
import { URI } from 'vs/base/common/uri';
export const IWorkbenchEnvironmentService = createDecorator<IWorkbenchEnvironmentService>('environmentService');
@@ -16,5 +17,7 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService {
readonly configuration: IWindowConfiguration;
readonly logFile: URI;
readonly options?: IWorkbenchConstructionOptions;
}

View File

@@ -11,6 +11,7 @@ import { URI } from 'vs/base/common/uri';
import { Schemas } from 'vs/base/common/network';
import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup';
import { ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation';
import { join } from 'vs/base/common/path';
export class WorkbenchEnvironmentService extends EnvironmentService implements IWorkbenchEnvironmentService {
@@ -31,4 +32,7 @@ export class WorkbenchEnvironmentService extends EnvironmentService implements I
@memoize
get userRoamingDataHome(): URI { return this.appSettingsHome.with({ scheme: Schemas.userData }); }
@memoize
get logFile(): URI { return URI.file(join(this.logsPath, `renderer${this.configuration.windowId}.log`)); }
}

View File

@@ -86,7 +86,7 @@ export class ExtensionService extends AbstractExtensionService implements IExten
const result: ExtensionHostProcessManager[] = [];
const webExtensions = this.getExtensions().then(extensions => extensions.filter(ext => isWebExtension(ext, this._configService)));
const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.parse('empty:value')); //todo@joh
const webHostProcessWorker = this._instantiationService.createInstance(WebWorkerExtensionHostStarter, true, webExtensions, URI.file(this._environmentService.logsPath).with({ scheme: this._environmentService.logFile.scheme }));
const webHostProcessManager = this._instantiationService.createInstance(ExtensionHostProcessManager, false, webHostProcessWorker, null, initialActivationEvents);
result.push(webHostProcessManager);

View File

@@ -176,6 +176,7 @@ export class BrowserKeyboardMapperFactoryBase {
}
setActiveKeyMapping(keymap: IKeyboardMapping | null) {
let keymapUpdated = false;
let matchedKeyboardLayout = this.getMatchedKeymapInfo(keymap);
if (matchedKeyboardLayout) {
// let score = matchedKeyboardLayout.score;
@@ -209,18 +210,21 @@ export class BrowserKeyboardMapperFactoryBase {
if (!this._activeKeymapInfo) {
this._activeKeymapInfo = matchedKeyboardLayout.result;
keymapUpdated = true;
} else if (keymap) {
if (matchedKeyboardLayout.result.getScore(keymap) > this._activeKeymapInfo.getScore(keymap)) {
this._activeKeymapInfo = matchedKeyboardLayout.result;
keymapUpdated = true;
}
}
}
if (!this._activeKeymapInfo) {
this._activeKeymapInfo = this.getUSStandardLayout();
keymapUpdated = true;
}
if (!this._activeKeymapInfo) {
if (!this._activeKeymapInfo || !keymapUpdated) {
return;
}

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