Merge from vscode 1ec43773e37997841c5af42b33ddb180e9735bf2

This commit is contained in:
ADS Merger
2020-03-29 01:29:32 +00:00
parent 586ec50916
commit a64304602e
316 changed files with 6524 additions and 11687 deletions
+1
View File
@@ -6,6 +6,7 @@ labels: ''
assignees: ''
---
<!-- ⚠️⚠️ Do Not Delete This! bug_report_template ⚠️⚠️ -->
<!-- Please read our Rules of Conduct: https://opensource.microsoft.com/codeofconduct/ -->
<!-- Please search existing issues to avoid creating duplicates. -->
<!-- Also please test using the latest insiders build to make sure your issue has not already been fixed. -->
-6
View File
@@ -1,6 +0,0 @@
{
daysAfterClose: 45,
daysSinceLastUpdate: 3,
ignoredLabels: ['A11y_ADS_OctTestPass', 'A11y_ADS_Schema_Dacpac_Backup', 'A11y_AzureDataStudio', 'A11yExclusion', 'A11yMAS', 'A11yResolved: Will Not Fix', 'A11yTCS'],
perform: true
}
-6
View File
@@ -1,6 +0,0 @@
{
newReleaseLabel: 'new-release',
newReleaseColor: '006b75',
daysAfterRelease: 5,
perform: true
}
@@ -0,0 +1,21 @@
name: Test Plan Item Validator
on:
issues:
types: [edited, labeled]
jobs:
main:
runs-on: ubuntu-latest
steps:
- name: Checkout Actions
uses: actions/checkout@v2
with:
repository: 'JacksonKearl/vscode-triage-github-actions'
ref: v15
- name: Run Test Plan Item Validator
uses: ./test-plan-item-validator
with:
token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}}
label: testplan-item
invalidLabel: invalid-testplan-item
comment: Invalid test plan item. See errors below and the [test plan item spec](https://github.com/microsoft/vscode/wiki/Writing-Test-Plan-Items) for more information. This comment will go away when the issues are resolved.
-1
View File
@@ -123,7 +123,6 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series(
resources: vscodeResources,
loaderConfig: common.loaderConfig(nodeModules),
out: 'out-vscode',
inlineAmdImages: true,
bundleInfo: undefined
})
));
-4
View File
@@ -126,10 +126,6 @@
"name": "vs/workbench/contrib/quickaccess",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/quickopen",
"project": "vscode-workbench"
},
{
"name": "vs/workbench/contrib/userData",
"project": "vscode-workbench"
-42
View File
@@ -6,7 +6,6 @@
Object.defineProperty(exports, "__esModule", { value: true });
exports.minifyTask = exports.optimizeTask = exports.loaderConfig = void 0;
const es = require("event-stream");
const fs = require("fs");
const gulp = require("gulp");
const concat = require("gulp-concat");
const minifyCSS = require("gulp-cssnano");
@@ -136,14 +135,6 @@ function optimizeTask(opts) {
if (err || !result) {
return bundlesStream.emit('error', JSON.stringify(err));
}
if (opts.inlineAmdImages) {
try {
result = inlineAmdImages(src, result);
}
catch (err) {
return bundlesStream.emit('error', JSON.stringify(err));
}
}
toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream);
// Remove css inlined resources
const filteredResources = resources.slice();
@@ -179,39 +170,6 @@ function optimizeTask(opts) {
};
}
exports.optimizeTask = optimizeTask;
function inlineAmdImages(src, result) {
for (const outputFile of result.files) {
for (const sourceFile of outputFile.sources) {
if (sourceFile.path && /\.js$/.test(sourceFile.path)) {
sourceFile.contents = sourceFile.contents.replace(/\([^.]+\.registerAndGetAmdImageURL\(([^)]+)\)\)/g, (_, m0) => {
let imagePath = m0;
// remove `` or ''
if ((imagePath.charAt(0) === '`' && imagePath.charAt(imagePath.length - 1) === '`')
|| (imagePath.charAt(0) === '\'' && imagePath.charAt(imagePath.length - 1) === '\'')) {
imagePath = imagePath.substr(1, imagePath.length - 2);
}
if (!/\.(png|svg)$/.test(imagePath)) {
console.log(`original: ${_}`);
return _;
}
const repoLocation = path.join(src, imagePath);
const absoluteLocation = path.join(REPO_ROOT_PATH, repoLocation);
if (!fs.existsSync(absoluteLocation)) {
const message = `Invalid amd image url in file ${sourceFile.path}: ${imagePath}`;
console.log(message);
throw new Error(message);
}
const fileContents = fs.readFileSync(absoluteLocation);
const mime = /\.svg$/.test(imagePath) ? 'image/svg+xml' : 'image/png';
// Mark the file as inlined so we don't ship it by itself
result.cssInlinedResources.push(repoLocation);
return `("data:${mime};base64,${fileContents.toString('base64')}")`;
});
}
}
}
return result;
}
/**
* Wrap around uglify and allow the preserveComments function
* to have a file "context" to include our copyright only once per file.
-49
View File
@@ -6,7 +6,6 @@
'use strict';
import * as es from 'event-stream';
import * as fs from 'fs';
import * as gulp from 'gulp';
import * as concat from 'gulp-concat';
import * as minifyCSS from 'gulp-cssnano';
@@ -162,10 +161,6 @@ export interface IOptimizeTaskOpts {
* (emit bundleInfo.json file)
*/
bundleInfo: boolean;
/**
* replace calls to `registerAndGetAmdImageURL` with data uris
*/
inlineAmdImages: boolean;
/**
* (out folder name)
*/
@@ -199,14 +194,6 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
bundle.bundle(entryPoints, loaderConfig, function (err, result) {
if (err || !result) { return bundlesStream.emit('error', JSON.stringify(err)); }
if (opts.inlineAmdImages) {
try {
result = inlineAmdImages(src, result);
} catch (err) {
return bundlesStream.emit('error', JSON.stringify(err));
}
}
toBundleStream(src, bundledFileHeader, result.files).pipe(bundlesStream);
// Remove css inlined resources
@@ -251,42 +238,6 @@ export function optimizeTask(opts: IOptimizeTaskOpts): () => NodeJS.ReadWriteStr
};
}
function inlineAmdImages(src: string, result: bundle.IBundleResult): bundle.IBundleResult {
for (const outputFile of result.files) {
for (const sourceFile of outputFile.sources) {
if (sourceFile.path && /\.js$/.test(sourceFile.path)) {
sourceFile.contents = sourceFile.contents.replace(/\([^.]+\.registerAndGetAmdImageURL\(([^)]+)\)\)/g, (_, m0) => {
let imagePath = m0;
// remove `` or ''
if ((imagePath.charAt(0) === '`' && imagePath.charAt(imagePath.length - 1) === '`')
|| (imagePath.charAt(0) === '\'' && imagePath.charAt(imagePath.length - 1) === '\'')) {
imagePath = imagePath.substr(1, imagePath.length - 2);
}
if (!/\.(png|svg)$/.test(imagePath)) {
console.log(`original: ${_}`);
return _;
}
const repoLocation = path.join(src, imagePath);
const absoluteLocation = path.join(REPO_ROOT_PATH, repoLocation);
if (!fs.existsSync(absoluteLocation)) {
const message = `Invalid amd image url in file ${sourceFile.path}: ${imagePath}`;
console.log(message);
throw new Error(message);
}
const fileContents = fs.readFileSync(absoluteLocation);
const mime = /\.svg$/.test(imagePath) ? 'image/svg+xml' : 'image/png';
// Mark the file as inlined so we don't ship it by itself
result.cssInlinedResources.push(repoLocation);
return `("data:${mime};base64,${fileContents.toString('base64')}")`;
});
}
}
}
return result;
}
declare class FileWithCopyright extends VinylFile {
public __hasOurCopyright: boolean;
}
+1 -1
View File
@@ -49,7 +49,7 @@
"rollup-plugin-node-resolve": "^5.2.0",
"service-downloader": "0.2.1",
"terser": "4.3.8",
"typescript": "^3.9.0-dev.20200316",
"typescript": "^3.9.0-dev.20200327",
"vsce": "1.48.0",
"vscode-telemetry-extractor": "^1.5.4",
"xml2js": "^0.4.17"
+4 -4
View File
@@ -3600,10 +3600,10 @@ typescript@^3.0.1:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.5.3.tgz#c830f657f93f1ea846819e929092f5fe5983e977"
integrity sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==
typescript@^3.9.0-dev.20200316:
version "3.9.0-dev.20200316"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200316.tgz#7cbb2fa2eedf58eea27b3250ab38674790ccf999"
integrity sha512-MM67isAuvHM4hwfHR4K9NikB7MFD9RjISB5cXhtKmjkpMFO0QNzFmFq061VmsJqoRVpG9N2KE+cm6BJ9dIjrtQ==
typescript@^3.9.0-dev.20200327:
version "3.9.0-dev.20200327"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.0-dev.20200327.tgz#52179aae816587f772a0526e91143760f2bee42f"
integrity sha512-/TWD/zPvhAcN2Toqx2NBQ+oDVGVj4iqupjWcUAwL45TfcODeHpzszneABR1b/EjHbtUObtLH40vy5Z6rdVvKzg==
typical@^4.0.0:
version "4.0.0"
@@ -24,6 +24,7 @@ export async function activate(context: vscode.ExtensionContext) {
try {
const session = await loginService.login(scopeList.join(' '));
Logger.info('Login success!');
onDidChangeSessions.fire({ added: [session.id], removed: [], changed: [] });
return session;
} catch (e) {
vscode.window.showErrorMessage(`Sign in failed: ${e}`);
@@ -32,7 +33,8 @@ export async function activate(context: vscode.ExtensionContext) {
}
},
logout: async (id: string) => {
return loginService.logout(id);
await loginService.logout(id);
onDidChangeSessions.fire({ added: [], removed: [id], changed: [] });
}
});
@@ -142,7 +142,9 @@ export class GitHubAuthenticationProvider {
public async logout(id: string) {
const sessionIndex = this._sessions.findIndex(session => session.id === id);
if (sessionIndex > -1) {
this._sessions.splice(sessionIndex, 1);
const session = this._sessions.splice(sessionIndex, 1)[0];
const token = await session.getAccessToken();
await this._githubServer.revokeToken(token);
}
this.storeSessions();
@@ -156,4 +156,46 @@ export class GitHubServer {
});
});
}
public async revokeToken(token: string): Promise<void> {
return new Promise(async (resolve, reject) => {
const callbackUri = await vscode.env.asExternalUri(vscode.Uri.parse(`${vscode.env.uriScheme}://vscode.github-authentication/did-authenticate`));
const clientDetails = ClientRegistrar.getClientDetails(callbackUri);
const detailsString = `${clientDetails.id}:${clientDetails.secret}`;
const payload = JSON.stringify({ access_token: token });
Logger.info('Revoking token...');
const post = https.request({
host: 'api.github.com',
path: `/applications/${clientDetails.id}/token`,
method: 'DELETE',
headers: {
Authorization: `Basic ${Buffer.from(detailsString).toString('base64')}`,
'User-Agent': 'Visual-Studio-Code',
'Content-Type': 'application/json',
'Content-Length': Buffer.byteLength(payload)
}
}, result => {
const buffer: Buffer[] = [];
result.on('data', (chunk: Buffer) => {
buffer.push(chunk);
});
result.on('end', () => {
if (result.statusCode === 204) {
Logger.info('Revoked token!');
resolve();
} else {
reject(new Error(result.statusMessage));
}
});
});
post.write(payload);
post.end();
post.on('error', err => {
reject(err);
});
});
}
}
@@ -49,7 +49,7 @@ class OriginDocumentMergeConflictTracker implements interfaces.IDocumentMergeCon
export default class DocumentMergeConflictTracker implements vscode.Disposable, interfaces.IDocumentMergeConflictTrackerService {
private cache: Map<string, ScanTask> = new Map();
private delayExpireTime: number = 250;
private delayExpireTime: number = 0;
getConflicts(document: vscode.TextDocument, origin: string): PromiseLike<interfaces.IDocumentMergeConflict[]> {
// Attempt from cache
@@ -192,7 +192,7 @@
},
{
"name": "Support.constant",
"scope": "support.constant",
"scope": [ "support.constant", "support.variable"],
"settings": {
"fontStyle": "",
"foreground": "#eb939aff"
@@ -182,7 +182,10 @@
},
{
"name": "Library constant",
"scope": "support.constant",
"scope": [
"support.constant",
"support.variable"
],
"settings": {}
},
{
+1 -1
View File
@@ -178,7 +178,7 @@
"style-loader": "^1.0.0",
"ts-loader": "^4.4.2",
"typemoq": "^0.3.2",
"typescript": "^3.9.0-dev.20200316",
"typescript": "^3.9.0-dev.20200327",
"typescript-formatter": "7.1.0",
"underscore": "^1.8.2",
"vinyl": "^2.0.0",
@@ -5,7 +5,6 @@
import { DeleteAction, OpenQueryAction, RunQueryAction, ClearHistoryAction, ToggleQueryHistoryCaptureAction } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryActions';
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { QueryHistoryNode } from 'sql/workbench/contrib/queryHistory/browser/queryHistoryNode';
@@ -13,7 +12,7 @@ import { QueryHistoryNode } from 'sql/workbench/contrib/queryHistory/browser/que
/**
* Provides query history actions
*/
export class QueryHistoryActionProvider extends ContributableActionProvider {
export class QueryHistoryActionProvider {
private _actions: {
openQueryAction: IAction,
@@ -26,7 +25,6 @@ export class QueryHistoryActionProvider extends ContributableActionProvider {
constructor(
@IInstantiationService instantiationService: IInstantiationService
) {
super();
this._actions = {
openQueryAction: instantiationService.createInstance(OpenQueryAction, OpenQueryAction.ID, OpenQueryAction.LABEL),
runQueryAction: instantiationService.createInstance(RunQueryAction, RunQueryAction.ID, RunQueryAction.LABEL),
@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TaskNode, TaskStatus, TaskExecutionMode } from 'sql/workbench/services/tasks/common/tasksNode';
@@ -13,12 +12,11 @@ import { CancelAction, ScriptAction } from 'sql/workbench/contrib/tasks/common/t
/**
* Provides actions for the history tasks
*/
export class TaskHistoryActionProvider extends ContributableActionProvider {
export class TaskHistoryActionProvider {
constructor(
@IInstantiationService private _instantiationService: IInstantiationService
) {
super();
}
public hasActions(tree: ITree, element: any): boolean {
@@ -8,7 +8,6 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { ClearSingleRecentConnectionAction } from 'sql/workbench/services/connection/browser/connectionActions';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IConnectionManagementService } from 'sql/platform/connection/common/connectionManagement';
import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile';
@@ -17,14 +16,13 @@ import { Event, Emitter } from 'vs/base/common/event';
import mouse = require('vs/base/browser/mouseEvent');
import { IConnectionProfile } from 'sql/platform/connection/common/interfaces';
export class RecentConnectionActionsProvider extends ContributableActionProvider {
export class RecentConnectionActionsProvider {
private _onRecentConnectionRemoved = new Emitter<void>();
public onRecentConnectionRemoved: Event<void> = this._onRecentConnectionRemoved.event;
constructor(
@IInstantiationService private _instantiationService: IInstantiationService
) {
super();
}
private getRecentConnectionActions(tree: ITree, element: any): IAction[] {
@@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
import { ITree } from 'vs/base/parts/tree/browser/tree';
import { ContributableActionProvider } from 'vs/workbench/browser/actions';
import { IAction } from 'vs/base/common/actions';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
@@ -31,7 +30,7 @@ import { firstIndex, find } from 'vs/base/common/arrays';
/**
* Provides actions for the server tree elements
*/
export class ServerTreeActionProvider extends ContributableActionProvider {
export class ServerTreeActionProvider {
constructor(
@IInstantiationService private _instantiationService: IInstantiationService,
@@ -40,7 +39,6 @@ export class ServerTreeActionProvider extends ContributableActionProvider {
@IMenuService private menuService: IMenuService,
@IContextKeyService private _contextKeyService: IContextKeyService
) {
super();
}
public hasActions(tree: ITree, element: any): boolean {
-2
View File
@@ -22,8 +22,6 @@
"vs/editor/*",
"vs/base/common/*",
"vs/base/browser/*",
"vs/base/parts/tree/*",
"vs/base/parts/quickopen/*",
"vs/platform/*/common/*",
"vs/platform/*/browser/*"
],
+61 -2
View File
@@ -134,6 +134,18 @@ export class BaseActionViewItem extends Disposable implements IActionViewItem {
}
}));
if (platform.isMacintosh) {
// macOS: allow to trigger the button when holding Ctrl+key and pressing the
// main mouse button. This is for scenarios where e.g. some interaction forces
// the Ctrl+key to be pressed and hold but the user still wants to interact
// with the actions (for example quick access in quick navigation mode).
this._register(DOM.addDisposableListener(element, DOM.EventType.CONTEXT_MENU, e => {
if (e.button === 0 && e.ctrlKey === true) {
this.onClick(e);
}
}));
}
this._register(DOM.addDisposableListener(element, DOM.EventType.CLICK, e => {
DOM.EventHelper.stop(e, true);
// See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Interact_with_the_clipboard
@@ -633,8 +645,7 @@ export class ActionBar extends Disposable implements IActionRunner {
// Prevent native context menu on actions
this._register(DOM.addDisposableListener(actionViewItemElement, DOM.EventType.CONTEXT_MENU, (e: DOM.EventLike) => {
e.preventDefault();
e.stopPropagation();
DOM.EventHelper.stop(e, true);
}));
let item: IActionViewItem | undefined;
@@ -873,3 +884,51 @@ export class SelectActionViewItem extends BaseActionViewItem {
this.selectBox.render(container);
}
}
export function prepareActions(actions: IAction[]): IAction[] {
if (!actions.length) {
return actions;
}
// Clean up leading separators
let firstIndexOfAction = -1;
for (let i = 0; i < actions.length; i++) {
if (actions[i].id === Separator.ID) {
continue;
}
firstIndexOfAction = i;
break;
}
if (firstIndexOfAction === -1) {
return [];
}
actions = actions.slice(firstIndexOfAction);
// Clean up trailing separators
for (let h = actions.length - 1; h >= 0; h--) {
const isSeparator = actions[h].id === Separator.ID;
if (isSeparator) {
actions.splice(h, 1);
} else {
break;
}
}
// Clean up separator duplicates
let foundAction = false;
for (let k = actions.length - 1; k >= 0; k--) {
const isSeparator = actions[k].id === Separator.ID;
if (isSeparator && !foundAction) {
actions.splice(k, 1);
} else if (!isSeparator) {
foundAction = true;
} else if (isSeparator) {
foundAction = false;
}
}
return actions;
}
@@ -81,7 +81,8 @@ export class BreadcrumbsWidget {
private _dimension: dom.Dimension | undefined;
constructor(
container: HTMLElement
container: HTMLElement,
horizontalScrollbarSize: number,
) {
this._domNode = document.createElement('div');
this._domNode.className = 'monaco-breadcrumbs';
@@ -90,7 +91,7 @@ export class BreadcrumbsWidget {
this._scrollable = new DomScrollableElement(this._domNode, {
vertical: ScrollbarVisibility.Hidden,
horizontal: ScrollbarVisibility.Auto,
horizontalScrollbarSize: 3,
horizontalScrollbarSize,
useShadows: false,
scrollYToX: true
});
@@ -106,6 +107,12 @@ export class BreadcrumbsWidget {
this._disposables.add(focusTracker.onDidFocus(_ => this._onDidChangeFocus.fire(true)));
}
setHorizontalScrollbarSize(size: number) {
this._scrollable.updateOptions({
horizontalScrollbarSize: size
});
}
dispose(): void {
this._disposables.dispose();
dispose(this._pendingLayout);
@@ -202,7 +202,7 @@ class Label {
const l = label[i];
const id = options?.domId && `${options?.domId}_${i}`;
dom.append(this.container, dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i }, l));
dom.append(this.container, dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' }, l));
if (i < label.length - 1) {
dom.append(this.container, dom.$('span.label-separator', undefined, options?.separator || '/'));
@@ -270,7 +270,7 @@ class LabelWithHighlights {
const m = matches ? matches[i] : undefined;
const id = options?.domId && `${options?.domId}_${i}`;
const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i });
const name = dom.$('a.label-name', { id, 'data-icon-label-count': label.length, 'data-icon-label-index': i, 'role': 'treeitem' });
const highlightedLabel = new HighlightedLabel(dom.append(this.container, name), this.supportCodicons);
highlightedLabel.set(l, m, options?.title, options?.labelEscapeNewLines);
@@ -74,16 +74,12 @@
}
/* make sure selection color wins when a label is being selected */
.monaco-tree.focused .selected .monaco-icon-label, /* tree */
.monaco-tree.focused .selected .monaco-icon-label::after,
.monaco-list:focus .selected .monaco-icon-label, /* list */
.monaco-list:focus .selected .monaco-icon-label::after
{
color: inherit !important;
}
.monaco-tree-row.focused.selected .label-description,
.monaco-tree-row.selected .label-description,
.monaco-list-row.focused.selected .label-description,
.monaco-list-row.selected .label-description {
opacity: .8;
+5 -3
View File
@@ -32,6 +32,7 @@ export interface IMenuBarOptions {
getKeybinding?: (action: IAction) => ResolvedKeybinding | undefined;
alwaysOnMnemonics?: boolean;
compactMode?: Direction;
getCompactMenuActions?: () => IAction[]
}
export interface MenuBarMenu {
@@ -91,7 +92,7 @@ export class MenuBar extends Disposable {
private menuStyle: IMenuStyles | undefined;
private overflowLayoutScheduled: IDisposable | undefined = undefined;
constructor(private container: HTMLElement, private options: IMenuBarOptions = {}, private compactMenuActions?: IAction[]) {
constructor(private container: HTMLElement, private options: IMenuBarOptions = {}) {
super();
this.container.setAttribute('role', 'menubar');
@@ -492,9 +493,10 @@ export class MenuBar extends Disposable {
this.overflowMenu.buttonElement.style.visibility = 'visible';
}
if (this.compactMenuActions && this.compactMenuActions.length) {
const compactMenuActions = this.options.getCompactMenuActions?.();
if (compactMenuActions && compactMenuActions.length) {
this.overflowMenu.actions.push(new Separator());
this.overflowMenu.actions.push(...this.compactMenuActions);
this.overflowMenu.actions.push(...compactMenuActions);
}
} else {
DOM.removeNode(this.overflowMenu.buttonElement);
@@ -259,6 +259,15 @@ export abstract class AbstractScrollbar extends Widget {
this._scrollable.setScrollPositionNow(desiredScrollPosition);
}
public updateScrollbarSize(scrollbarSize: number): void {
this._updateScrollbarSize(scrollbarSize);
this._scrollbarState.setScrollbarSize(scrollbarSize);
this._shouldRender = true;
if (!this._lazyRender) {
this.render();
}
}
// ----------------- Overwrite these
protected abstract _renderDomNode(largeSize: number, smallSize: number): void;
@@ -267,6 +276,7 @@ export abstract class AbstractScrollbar extends Widget {
protected abstract _mouseDownRelativePosition(offsetX: number, offsetY: number): number;
protected abstract _sliderMousePosition(e: ISimplifiedMouseEvent): number;
protected abstract _sliderOrthogonalMousePosition(e: ISimplifiedMouseEvent): number;
protected abstract _updateScrollbarSize(size: number): void;
public abstract writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void;
}
@@ -92,6 +92,10 @@ export class HorizontalScrollbar extends AbstractScrollbar {
return e.posy;
}
protected _updateScrollbarSize(size: number): void {
this.slider.setHeight(size);
}
public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void {
target.scrollLeft = scrollPosition;
}
@@ -287,12 +287,22 @@ export abstract class AbstractScrollableElement extends Widget {
* depend on Editor.
*/
public updateOptions(newOptions: ScrollableElementChangeOptions): void {
let massagedOptions = resolveOptions(newOptions);
this._options.handleMouseWheel = massagedOptions.handleMouseWheel;
this._options.mouseWheelScrollSensitivity = massagedOptions.mouseWheelScrollSensitivity;
this._options.fastScrollSensitivity = massagedOptions.fastScrollSensitivity;
this._options.scrollPredominantAxis = massagedOptions.scrollPredominantAxis;
this._setListeningToMouseWheel(this._options.handleMouseWheel);
if (typeof newOptions.handleMouseWheel !== 'undefined') {
this._options.handleMouseWheel = newOptions.handleMouseWheel;
this._setListeningToMouseWheel(this._options.handleMouseWheel);
}
if (typeof newOptions.mouseWheelScrollSensitivity !== 'undefined') {
this._options.mouseWheelScrollSensitivity = newOptions.mouseWheelScrollSensitivity;
}
if (typeof newOptions.fastScrollSensitivity !== 'undefined') {
this._options.fastScrollSensitivity = newOptions.fastScrollSensitivity;
}
if (typeof newOptions.scrollPredominantAxis !== 'undefined') {
this._options.scrollPredominantAxis = newOptions.scrollPredominantAxis;
}
if (typeof newOptions.horizontalScrollbarSize !== 'undefined') {
this._horizontalScrollbar.updateScrollbarSize(newOptions.horizontalScrollbarSize);
}
if (!this._options.lazyRender) {
this._render();
@@ -119,8 +119,9 @@ export interface ScrollableElementCreationOptions {
export interface ScrollableElementChangeOptions {
handleMouseWheel?: boolean;
mouseWheelScrollSensitivity?: number;
fastScrollSensitivity: number;
scrollPredominantAxis: boolean;
fastScrollSensitivity?: number;
scrollPredominantAxis?: boolean;
horizontalScrollbarSize?: number;
}
export interface ScrollableElementResolvedOptions {
@@ -14,7 +14,7 @@ export class ScrollbarState {
* For the vertical scrollbar: the width.
* For the horizontal scrollbar: the height.
*/
private readonly _scrollbarSize: number;
private _scrollbarSize: number;
/**
* For the vertical scrollbar: the height of the pair horizontal scrollbar.
@@ -114,6 +114,10 @@ export class ScrollbarState {
return false;
}
public setScrollbarSize(scrollbarSize: number): void {
this._scrollbarSize = scrollbarSize;
}
private static _computeValues(oppositeScrollbarSize: number, arrowSize: number, visibleSize: number, scrollSize: number, scrollPosition: number) {
const computedAvailableSize = Math.max(0, visibleSize - oppositeScrollbarSize);
const computedRepresentableSize = Math.max(0, computedAvailableSize - 2 * arrowSize);
@@ -93,6 +93,10 @@ export class VerticalScrollbar extends AbstractScrollbar {
return e.posx;
}
protected _updateScrollbarSize(size: number): void {
this.slider.setWidth(size);
}
public writeScrollPosition(target: INewScrollPosition, scrollPosition: number): void {
target.scrollTop = scrollPosition;
}
+5 -1
View File
@@ -214,7 +214,11 @@ export abstract class Pane extends Disposable implements IView {
.event(() => this.setExpanded(true), null));
this._register(domEvent(this.header, 'click')
(() => this.setExpanded(!this.isExpanded()), null));
(e => {
if (!e.defaultPrevented) {
this.setExpanded(!this.isExpanded());
}
}, null));
this.body = append(this.element, $('.pane-body'));
this.renderBody(this.body);
+1 -1
View File
@@ -269,7 +269,7 @@ function asObjectTreeOptions<TInput, T, TFilterData>(options?: IAsyncDataTreeOpt
return options.ariaProvider!.getRole!(el.element as T);
} : () => 'treeitem',
isChecked: options.ariaProvider!.isChecked ? (e) => {
return options.ariaProvider?.isChecked!(e.element as T);
return !!(options.ariaProvider?.isChecked!(e.element as T));
} : undefined
},
ariaRole: ListAriaRootRole.TREE,
+2 -4
View File
@@ -10,16 +10,14 @@ import { AsyncDataTree } from 'vs/base/browser/ui/tree/asyncDataTree';
export class CollapseAllAction<TInput, T, TFilterData = void> extends Action {
constructor(private viewer: AsyncDataTree<TInput, T, TFilterData>, enabled: boolean) {
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'monaco-tree-action collapse-all', enabled);
super('vs.tree.collapse', nls.localize('collapse all', "Collapse All"), 'collapse-all', enabled);
}
public run(context?: any): Promise<any> {
async run(context?: any): Promise<any> {
this.viewer.collapseAll();
this.viewer.setSelection([]);
this.viewer.setFocus([]);
this.viewer.domFocus();
this.viewer.focusFirst();
return Promise.resolve();
}
}
-9
View File
@@ -12,12 +12,3 @@ export function getPathFromAmdModule(requirefn: typeof require, relativePath: st
export function getUriFromAmdModule(requirefn: typeof require, relativePath: string): URI {
return URI.parse(requirefn.toUrl(relativePath));
}
/**
* Reference a resource that might be inlined.
* Do not inline icons that will be used by the native mac touchbar.
* Do not rename this method unless you adopt the build scripts.
*/
export function registerAndGetAmdImageURL(absolutePath: string): string {
return require.toUrl(absolutePath);
}
+1 -1
View File
@@ -6,7 +6,7 @@
import { matchesFuzzy, IMatch } from 'vs/base/common/filters';
import { ltrim } from 'vs/base/common/strings';
const codiconStartMarker = '$(';
export const codiconStartMarker = '$(';
export interface IParsedCodicons {
readonly text: string;
+6
View File
@@ -3,6 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { codiconStartMarker } from 'vs/base/common/codicon';
const escapeCodiconsRegex = /(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)/gi;
export function escapeCodicons(text: string): string {
return text.replace(escapeCodiconsRegex, (match, escaped) => escaped ? match : `\\${match}`);
@@ -30,5 +32,9 @@ export function renderCodicons(text: string): string {
const stripCodiconsRegex = /(\s)?(\\)?\$\([a-z0-9\-]+?(?:~[a-z0-9\-]*?)?\)(\s)?/gi;
export function stripCodicons(text: string): string {
if (text.indexOf(codiconStartMarker) === -1) {
return text;
}
return text.replace(stripCodiconsRegex, (match, preWhitespace, escaped, postWhitespace) => escaped ? match : preWhitespace || postWhitespace || '');
}
+83 -11
View File
@@ -9,6 +9,7 @@ import { sep } from 'vs/base/common/path';
import { isWindows, isLinux } from 'vs/base/common/platform';
import { stripWildcards, equalsIgnoreCase } from 'vs/base/common/strings';
import { CharCode } from 'vs/base/common/charCode';
import { distinctES6 } from 'vs/base/common/arrays';
export type Score = [number /* score */, number[] /* match positions */];
export type ScorerCache = { [key: string]: IItemScore };
@@ -19,7 +20,40 @@ const NO_SCORE: Score = [NO_MATCH, []];
// const DEBUG = false;
// const DEBUG_MATRIX = false;
export function score(target: string, query: string, queryLower: string, fuzzy: boolean): Score {
export function score(target: string, query: IPreparedQuery, fuzzy: boolean): Score {
if (query.values && query.values.length > 1) {
return scoreMultiple(target, query.values, fuzzy);
}
return scoreSingle(target, query.value, query.valueLowercase, fuzzy);
}
function scoreMultiple(target: string, query: IPreparedQueryPiece[], fuzzy: boolean): Score {
let totalScore = NO_MATCH;
const totalPositions: number[] = [];
for (const { value, valueLowercase } of query) {
const [scoreValue, positions] = scoreSingle(target, value, valueLowercase, fuzzy);
if (scoreValue === NO_MATCH) {
// if a single query value does not match, return with
// no score entirely, we require all queries to match
return NO_SCORE;
}
totalScore += scoreValue;
totalPositions.push(...positions);
}
if (totalScore === NO_MATCH) {
return NO_SCORE;
}
// if we have a score, ensure that the positions are
// sorted in ascending order and distinct
return [totalScore, distinctES6(totalPositions).sort((a, b) => a - b)];
}
function scoreSingle(target: string, query: string, queryLower: string, fuzzy: boolean): Score {
if (!target || !query) {
return NO_SCORE; // return early if target or query are undefined
}
@@ -303,21 +337,62 @@ const LABEL_PREFIX_SCORE = 1 << 17;
const LABEL_CAMELCASE_SCORE = 1 << 16;
const LABEL_SCORE_THRESHOLD = 1 << 15;
export interface IPreparedQuery {
export interface IPreparedQueryPiece {
original: string;
originalLowercase: string;
value: string;
lowercase: string;
valueLowercase: string;
}
export interface IPreparedQuery extends IPreparedQueryPiece {
// Split by spaces
values: IPreparedQueryPiece[] | undefined;
containsPathSeparator: boolean;
}
/**
* Helper function to prepare a search value for scoring by removing unwanted characters.
* Helper function to prepare a search value for scoring by removing unwanted characters
* and allowing to score on multiple pieces separated by whitespace character.
*/
const MULTIPL_QUERY_VALUES_SEPARATOR = ' ';
export function prepareQuery(original: string): IPreparedQuery {
if (!original) {
if (typeof original !== 'string') {
original = '';
}
const originalLowercase = original.toLowerCase();
const value = prepareQueryValue(original);
const valueLowercase = value.toLowerCase();
const containsPathSeparator = value.indexOf(sep) >= 0;
let values: IPreparedQueryPiece[] | undefined = undefined;
const originalSplit = original.split(MULTIPL_QUERY_VALUES_SEPARATOR);
if (originalSplit.length > 1) {
for (const originalPiece of originalSplit) {
const valuePiece = prepareQueryValue(originalPiece);
if (valuePiece) {
if (!values) {
values = [];
}
values.push({
original: originalPiece,
originalLowercase: originalPiece.toLowerCase(),
value: valuePiece,
valueLowercase: valuePiece.toLowerCase()
});
}
}
}
return { original, originalLowercase, value, valueLowercase, values, containsPathSeparator };
}
function prepareQueryValue(original: string): string {
let value = stripWildcards(original).replace(/\s/g, ''); // get rid of all wildcards and whitespace
if (isWindows) {
value = value.replace(/\//g, sep); // Help Windows users to search for paths when using slash
@@ -325,10 +400,7 @@ export function prepareQuery(original: string): IPreparedQuery {
value = value.replace(/\\/g, sep); // Help macOS/Linux users to search for paths when using backslash
}
const lowercase = value.toLowerCase();
const containsPathSeparator = value.indexOf(sep) >= 0;
return { original, value, lowercase, containsPathSeparator };
return value;
}
export function scoreItem<T>(item: T, query: IPreparedQuery, fuzzy: boolean, accessor: IItemAccessor<T>, cache: ScorerCache): IItemScore {
@@ -404,7 +476,7 @@ function doScoreItem(label: string, description: string | undefined, path: strin
}
// 4.) prefer scores on the label if any
const [labelScore, labelPositions] = score(label, query.value, query.lowercase, fuzzy);
const [labelScore, labelPositions] = score(label, query, fuzzy);
if (labelScore) {
return { score: labelScore + LABEL_SCORE_THRESHOLD, labelMatch: createMatches(labelPositions) };
}
@@ -420,7 +492,7 @@ function doScoreItem(label: string, description: string | undefined, path: strin
const descriptionPrefixLength = descriptionPrefix.length;
const descriptionAndLabel = `${descriptionPrefix}${label}`;
const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query.value, query.lowercase, fuzzy);
const [labelDescriptionScore, labelDescriptionPositions] = score(descriptionAndLabel, query, fuzzy);
if (labelDescriptionScore) {
const labelDescriptionMatches = createMatches(labelDescriptionPositions);
const labelMatch: IMatch[] = [];
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
<title>arrow-left</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="arrow-left" fill="#c5c5c5">
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="10px" height="16px" viewBox="0 0 10 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<!-- Generator: Sketch 40.3 (33839) - http://www.bohemiancoding.com/sketch -->
<title>arrow-left</title>
<desc>Created with Sketch.</desc>
<defs></defs>
<g id="Octicons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
<g id="arrow-left" fill="#424242">
<polygon id="Shape" points="6 3 0 8 6 13 6 10 10 10 10 6 6 6"></polygon>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 594 B

@@ -794,7 +794,7 @@ class QuickPick<T extends IQuickPickItem> extends QuickInput implements IQuickPi
if (firstPart.shiftKey && keyCode === KeyCode.Shift) {
if (keyboardEvent.ctrlKey || keyboardEvent.altKey || keyboardEvent.metaKey) {
return false; // this is an optimistic check for the shift key being used to navigate back in quick open
return false; // this is an optimistic check for the shift key being used to navigate back in quick input
}
return true;
@@ -1053,7 +1053,7 @@ class InputBox extends QuickInput implements IInputBox {
}
export class QuickInputController extends Disposable {
private static readonly MAX_WIDTH = 600; // Max total width of quick open widget
private static readonly MAX_WIDTH = 600; // Max total width of quick input widget
private idPrefix: string;
private ui: QuickInputUI | undefined;
@@ -106,6 +106,11 @@ class ListElementRenderer implements IListRenderer<ListElement, IListElementTemp
// Checkbox
const label = dom.append(data.entry, $('label.quick-input-list-label'));
data.toDisposeTemplate.push(dom.addStandardDisposableListener(label, dom.EventType.CLICK, e => {
if (!data.checkbox.offsetParent) { // If checkbox not visible:
e.preventDefault(); // Prevent toggle of checkbox when it is immediately shown afterwards. #91740
}
}));
data.checkbox = <HTMLInputElement>dom.append(label, $('input.quick-input-list-checkbox'));
data.checkbox.type = 'checkbox';
data.toDisposeTemplate.push(dom.addStandardDisposableListener(data.checkbox, dom.EventType.CHANGE, e => {
@@ -275,7 +280,13 @@ export class QuickInputList {
setRowLineHeight: false,
multipleSelectionSupport: false,
horizontalScrolling: false,
accessibilityProvider
accessibilityProvider,
ariaProvider: {
getRole: () => 'option',
getSetSize: (_: ListElement, _index: number, listLength: number) => listLength,
getPosInSet: (_: ListElement, index: number) => index
},
ariaRole: 'listbox'
} as IListOptions<ListElement>);
this.list.getHTMLElement().id = id;
this.disposables.push(this.list);
@@ -317,6 +328,9 @@ export class QuickInputList {
this._onLeave.fire();
}
}));
this.disposables.push(this.list.onMouseMiddleClick(e => {
this._onLeave.fire();
}));
this.disposables.push(this.list.onContextMenu(e => {
if (typeof e.index === 'number') {
e.browserEvent.preventDefault();
@@ -1,578 +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 * as types from 'vs/base/common/types';
import { URI } from 'vs/base/common/uri';
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
import { IconLabel, IIconLabelValueOptions } from 'vs/base/browser/ui/iconLabel/iconLabel';
import { IQuickNavigateConfiguration, IModel, IDataSource, IFilter, IAccessiblityProvider, IRenderer, IRunner, Mode, IEntryRunContext } from 'vs/base/parts/quickopen/common/quickOpen';
import { IAction, IActionRunner } from 'vs/base/common/actions';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
import * as DOM from 'vs/base/browser/dom';
import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
import { OS } from 'vs/base/common/platform';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { IItemAccessor } from 'vs/base/common/fuzzyScorer';
import { coalesce } from 'vs/base/common/arrays';
import { IMatch } from 'vs/base/common/filters';
export interface IContext {
event: any;
quickNavigateConfiguration: IQuickNavigateConfiguration;
}
export interface IHighlight extends IMatch {
start: number;
end: number;
}
let IDS = 0;
export class QuickOpenItemAccessorClass implements IItemAccessor<QuickOpenEntry> {
getItemLabel(entry: QuickOpenEntry): string | undefined {
return entry.getLabel();
}
getItemDescription(entry: QuickOpenEntry): string | undefined {
return entry.getDescription();
}
getItemPath(entry: QuickOpenEntry): string | undefined {
const resource = entry.getResource();
return resource ? resource.fsPath : undefined;
}
}
export const QuickOpenItemAccessor = new QuickOpenItemAccessorClass();
export class QuickOpenEntry {
private id: string;
private labelHighlights?: IHighlight[];
private descriptionHighlights?: IHighlight[];
private detailHighlights?: IHighlight[];
private hidden: boolean | undefined;
constructor(highlights: IHighlight[] = []) {
this.id = (IDS++).toString();
this.labelHighlights = highlights;
this.descriptionHighlights = [];
}
/**
* A unique identifier for the entry
*/
getId(): string {
return this.id;
}
/**
* The label of the entry to identify it from others in the list
*/
getLabel(): string | undefined {
return undefined;
}
/**
* The options for the label to use for this entry
*/
getLabelOptions(): IIconLabelValueOptions | undefined {
return undefined;
}
/**
* The label of the entry to use when a screen reader wants to read about the entry
*/
getAriaLabel(): string {
return coalesce([this.getLabel(), this.getDescription(), this.getDetail()])
.join(', ');
}
/**
* Detail information about the entry that is optional and can be shown below the label
*/
getDetail(): string | undefined {
return undefined;
}
/**
* The icon of the entry to identify it from others in the list
*/
getIcon(): string | undefined {
return undefined;
}
/**
* A secondary description that is optional and can be shown right to the label
*/
getDescription(): string | undefined {
return undefined;
}
/**
* A tooltip to show when hovering over the entry.
*/
getTooltip(): string | undefined {
return undefined;
}
/**
* A tooltip to show when hovering over the description portion of the entry.
*/
getDescriptionTooltip(): string | undefined {
return undefined;
}
/**
* An optional keybinding to show for an entry.
*/
getKeybinding(): ResolvedKeybinding | undefined {
return undefined;
}
/**
* A resource for this entry. Resource URIs can be used to compare different kinds of entries and group
* them together.
*/
getResource(): URI | undefined {
return undefined;
}
/**
* Allows to reuse the same model while filtering. Hidden entries will not show up in the viewer.
*/
isHidden(): boolean {
return !!this.hidden;
}
/**
* Allows to reuse the same model while filtering. Hidden entries will not show up in the viewer.
*/
setHidden(hidden: boolean): void {
this.hidden = hidden;
}
/**
* Allows to set highlight ranges that should show up for the entry label and optionally description if set.
*/
setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void {
this.labelHighlights = labelHighlights;
this.descriptionHighlights = descriptionHighlights;
this.detailHighlights = detailHighlights;
}
/**
* Allows to return highlight ranges that should show up for the entry label and description.
*/
getHighlights(): [IHighlight[] | undefined /* Label */, IHighlight[] | undefined /* Description */, IHighlight[] | undefined /* Detail */] {
return [this.labelHighlights, this.descriptionHighlights, this.detailHighlights];
}
/**
* Called when the entry is selected for opening. Returns a boolean value indicating if an action was performed or not.
* The mode parameter gives an indication if the element is previewed (using arrow keys) or opened.
*
* The context parameter provides additional context information how the run was triggered.
*/
run(mode: Mode, context: IEntryRunContext): boolean {
return false;
}
/**
* Determines if this quick open entry should merge with the editor history in quick open. If set to true
* and the resource of this entry is the same as the resource for an editor history, it will not show up
* because it is considered to be a duplicate of an editor history.
*/
mergeWithEditorHistory(): boolean {
return false;
}
}
export class QuickOpenEntryGroup extends QuickOpenEntry {
private entry?: QuickOpenEntry;
private groupLabel?: string;
private withBorder?: boolean;
constructor(entry?: QuickOpenEntry, groupLabel?: string, withBorder?: boolean) {
super();
this.entry = entry;
this.groupLabel = groupLabel;
this.withBorder = withBorder;
}
/**
* The label of the group or null if none.
*/
getGroupLabel(): string | undefined {
return this.groupLabel;
}
setGroupLabel(groupLabel: string | undefined): void {
this.groupLabel = groupLabel;
}
/**
* Whether to show a border on top of the group entry or not.
*/
showBorder(): boolean {
return !!this.withBorder;
}
setShowBorder(showBorder: boolean): void {
this.withBorder = showBorder;
}
getLabel(): string | undefined {
return this.entry ? this.entry.getLabel() : super.getLabel();
}
getLabelOptions(): IIconLabelValueOptions | undefined {
return this.entry ? this.entry.getLabelOptions() : super.getLabelOptions();
}
getAriaLabel(): string {
return this.entry ? this.entry.getAriaLabel() : super.getAriaLabel();
}
getDetail(): string | undefined {
return this.entry ? this.entry.getDetail() : super.getDetail();
}
getResource(): URI | undefined {
return this.entry ? this.entry.getResource() : super.getResource();
}
getIcon(): string | undefined {
return this.entry ? this.entry.getIcon() : super.getIcon();
}
getDescription(): string | undefined {
return this.entry ? this.entry.getDescription() : super.getDescription();
}
getEntry(): QuickOpenEntry | undefined {
return this.entry;
}
getHighlights(): [IHighlight[] | undefined, IHighlight[] | undefined, IHighlight[] | undefined] {
return this.entry ? this.entry.getHighlights() : super.getHighlights();
}
isHidden(): boolean {
return this.entry ? this.entry.isHidden() : super.isHidden();
}
setHighlights(labelHighlights?: IHighlight[], descriptionHighlights?: IHighlight[], detailHighlights?: IHighlight[]): void {
this.entry ? this.entry.setHighlights(labelHighlights, descriptionHighlights, detailHighlights) : super.setHighlights(labelHighlights, descriptionHighlights, detailHighlights);
}
setHidden(hidden: boolean): void {
this.entry ? this.entry.setHidden(hidden) : super.setHidden(hidden);
}
run(mode: Mode, context: IEntryRunContext): boolean {
return this.entry ? this.entry.run(mode, context) : super.run(mode, context);
}
}
class NoActionProvider implements IActionProvider {
hasActions(tree: ITree, element: any): boolean {
return false;
}
getActions(tree: ITree, element: any): IAction[] | null {
return null;
}
}
export interface IQuickOpenEntryTemplateData {
container: HTMLElement;
entry: HTMLElement;
icon: HTMLSpanElement;
label: IconLabel;
detail: HighlightedLabel;
keybinding: KeybindingLabel;
actionBar: ActionBar;
}
export interface IQuickOpenEntryGroupTemplateData extends IQuickOpenEntryTemplateData {
group?: HTMLDivElement;
}
const templateEntry = 'quickOpenEntry';
const templateEntryGroup = 'quickOpenEntryGroup';
class Renderer implements IRenderer<QuickOpenEntry> {
private actionProvider: IActionProvider;
private actionRunner?: IActionRunner;
constructor(actionProvider: IActionProvider = new NoActionProvider(), actionRunner?: IActionRunner) {
this.actionProvider = actionProvider;
this.actionRunner = actionRunner;
}
getHeight(entry: QuickOpenEntry): number {
if (entry.getDetail()) {
return 44;
}
return 22;
}
getTemplateId(entry: QuickOpenEntry): string {
if (entry instanceof QuickOpenEntryGroup) {
return templateEntryGroup;
}
return templateEntry;
}
renderTemplate(templateId: string, container: HTMLElement, styles: IQuickOpenStyles): IQuickOpenEntryGroupTemplateData {
const entryContainer = document.createElement('div');
DOM.addClass(entryContainer, 'sub-content');
container.appendChild(entryContainer);
// Entry
const row1 = DOM.$('.quick-open-row');
const row2 = DOM.$('.quick-open-row');
const entry = DOM.$('.quick-open-entry', undefined, row1, row2);
entryContainer.appendChild(entry);
// Icon
const icon = document.createElement('span');
row1.appendChild(icon);
// Label
const label = new IconLabel(row1, { supportHighlights: true, supportDescriptionHighlights: true, supportCodicons: true });
// Keybinding
const keybindingContainer = document.createElement('span');
row1.appendChild(keybindingContainer);
DOM.addClass(keybindingContainer, 'quick-open-entry-keybinding');
const keybinding = new KeybindingLabel(keybindingContainer, OS);
// Detail
const detailContainer = document.createElement('div');
row2.appendChild(detailContainer);
DOM.addClass(detailContainer, 'quick-open-entry-meta');
const detail = new HighlightedLabel(detailContainer, true);
// Entry Group
let group: HTMLDivElement | undefined;
if (templateId === templateEntryGroup) {
group = document.createElement('div');
DOM.addClass(group, 'results-group');
container.appendChild(group);
}
// Actions
DOM.addClass(container, 'actions');
const actionBarContainer = document.createElement('div');
DOM.addClass(actionBarContainer, 'primary-action-bar');
container.appendChild(actionBarContainer);
const actionBar = new ActionBar(actionBarContainer, {
actionRunner: this.actionRunner
});
return {
container,
entry,
icon,
label,
detail,
keybinding,
group,
actionBar
};
}
renderElement(entry: QuickOpenEntry, templateId: string, data: IQuickOpenEntryGroupTemplateData, styles: IQuickOpenStyles): void {
// Action Bar
if (this.actionProvider.hasActions(null, entry)) {
DOM.addClass(data.container, 'has-actions');
} else {
DOM.removeClass(data.container, 'has-actions');
}
data.actionBar.context = entry; // make sure the context is the current element
const actions = this.actionProvider.getActions(null, entry);
if (data.actionBar.isEmpty() && actions && actions.length > 0) {
data.actionBar.push(actions, { icon: true, label: false });
} else if (!data.actionBar.isEmpty() && (!actions || actions.length === 0)) {
data.actionBar.clear();
}
// Entry group class
if (entry instanceof QuickOpenEntryGroup && entry.getGroupLabel()) {
DOM.addClass(data.container, 'has-group-label');
} else {
DOM.removeClass(data.container, 'has-group-label');
}
// Entry group
if (entry instanceof QuickOpenEntryGroup) {
const group = <QuickOpenEntryGroup>entry;
const groupData = data;
// Border
if (group.showBorder()) {
DOM.addClass(groupData.container, 'results-group-separator');
if (styles.pickerGroupBorder) {
groupData.container.style.borderTopColor = styles.pickerGroupBorder.toString();
}
} else {
DOM.removeClass(groupData.container, 'results-group-separator');
groupData.container.style.borderTopColor = '';
}
// Group Label
const groupLabel = group.getGroupLabel() || '';
if (groupData.group) {
groupData.group.textContent = groupLabel;
if (styles.pickerGroupForeground) {
groupData.group.style.color = styles.pickerGroupForeground.toString();
}
}
}
// Normal Entry
if (entry instanceof QuickOpenEntry) {
const [labelHighlights, descriptionHighlights, detailHighlights] = entry.getHighlights();
// Icon
const iconClass = entry.getIcon() ? ('quick-open-entry-icon ' + entry.getIcon()) : '';
data.icon.className = iconClass;
// Label
const options: IIconLabelValueOptions = entry.getLabelOptions() || Object.create(null);
options.matches = labelHighlights || [];
options.title = entry.getTooltip();
options.descriptionTitle = entry.getDescriptionTooltip() || entry.getDescription(); // tooltip over description because it could overflow
options.descriptionMatches = descriptionHighlights || [];
data.label.setLabel(entry.getLabel() || '', entry.getDescription(), options);
// Meta
data.detail.set(entry.getDetail(), detailHighlights);
// Keybinding
data.keybinding.set(entry.getKeybinding()!);
}
}
disposeTemplate(templateId: string, templateData: IQuickOpenEntryGroupTemplateData): void {
templateData.actionBar.dispose();
templateData.actionBar = null!;
templateData.container = null!;
templateData.entry = null!;
templateData.keybinding = null!;
templateData.detail = null!;
templateData.group = null!;
templateData.icon = null!;
templateData.label.dispose();
templateData.label = null!;
}
}
export class QuickOpenModel implements
IModel<QuickOpenEntry>,
IDataSource<QuickOpenEntry>,
IFilter<QuickOpenEntry>,
IRunner<QuickOpenEntry>,
IAccessiblityProvider<QuickOpenEntry>
{
private _entries: QuickOpenEntry[];
private _dataSource: IDataSource<QuickOpenEntry>;
private _renderer: IRenderer<QuickOpenEntry>;
private _filter: IFilter<QuickOpenEntry>;
private _runner: IRunner<QuickOpenEntry>;
private _accessibilityProvider: IAccessiblityProvider<QuickOpenEntry>;
constructor(entries: QuickOpenEntry[] = [], actionProvider: IActionProvider = new NoActionProvider()) {
this._entries = entries;
this._dataSource = this;
this._renderer = new Renderer(actionProvider);
this._filter = this;
this._runner = this;
this._accessibilityProvider = this;
}
get entries() { return this._entries; }
get dataSource() { return this._dataSource; }
get renderer() { return this._renderer; }
get filter() { return this._filter; }
get runner() { return this._runner; }
get accessibilityProvider() { return this._accessibilityProvider; }
set entries(entries: QuickOpenEntry[]) {
this._entries = entries;
}
/**
* Adds entries that should show up in the quick open viewer.
*/
addEntries(entries: QuickOpenEntry[]): void {
if (types.isArray(entries)) {
this._entries = this._entries.concat(entries);
}
}
/**
* Set the entries that should show up in the quick open viewer.
*/
setEntries(entries: QuickOpenEntry[]): void {
if (types.isArray(entries)) {
this._entries = entries;
}
}
/**
* Get the entries that should show up in the quick open viewer.
*
* @visibleOnly optional parameter to only return visible entries
*/
getEntries(visibleOnly?: boolean): QuickOpenEntry[] {
if (visibleOnly) {
return this._entries.filter((e) => !e.isHidden());
}
return this._entries;
}
getId(entry: QuickOpenEntry): string {
return entry.getId();
}
getLabel(entry: QuickOpenEntry): string | null {
return types.withUndefinedAsNull(entry.getLabel());
}
getAriaLabel(entry: QuickOpenEntry): string {
const ariaLabel = entry.getAriaLabel();
if (ariaLabel) {
return nls.localize('quickOpenAriaLabelEntry', "{0}, picker", entry.getAriaLabel());
}
return nls.localize('quickOpenAriaLabel', "picker");
}
isVisible(entry: QuickOpenEntry): boolean {
return !entry.isHidden();
}
run(entry: QuickOpenEntry, mode: Mode, context: IEntryRunContext): boolean {
return entry.run(mode, context);
}
}
@@ -1,142 +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 { isFunction } from 'vs/base/common/types';
import { ITree, IRenderer, IFilter, IDataSource, IAccessibilityProvider } from 'vs/base/parts/tree/browser/tree';
import { IModel } from 'vs/base/parts/quickopen/common/quickOpen';
import { IQuickOpenStyles } from 'vs/base/parts/quickopen/browser/quickOpenWidget';
export interface IModelProvider {
getModel<T>(): IModel<T>;
}
export class DataSource implements IDataSource {
private modelProvider: IModelProvider;
constructor(model: IModel<any>);
constructor(modelProvider: IModelProvider);
constructor(arg: any) {
this.modelProvider = isFunction(arg.getModel) ? arg : { getModel: () => arg };
}
getId(tree: ITree, element: any): string {
if (!element) {
return null!;
}
const model = this.modelProvider.getModel();
return model === element ? '__root__' : model.dataSource.getId(element);
}
hasChildren(tree: ITree, element: any): boolean {
const model = this.modelProvider.getModel();
return !!(model && model === element && model.entries.length > 0);
}
getChildren(tree: ITree, element: any): Promise<any[]> {
const model = this.modelProvider.getModel();
return Promise.resolve(model === element ? model.entries : []);
}
getParent(tree: ITree, element: any): Promise<any> {
return Promise.resolve(null);
}
}
export class AccessibilityProvider implements IAccessibilityProvider {
constructor(private modelProvider: IModelProvider) { }
getAriaLabel(tree: ITree, element: any): string | null {
const model = this.modelProvider.getModel();
return model.accessibilityProvider ? model.accessibilityProvider.getAriaLabel(element) : null;
}
getPosInSet(tree: ITree, element: any): string {
const model = this.modelProvider.getModel();
let i = 0;
if (model.filter) {
for (const entry of model.entries) {
if (model.filter.isVisible(entry)) {
i++;
}
if (entry === element) {
break;
}
}
} else {
i = model.entries.indexOf(element) + 1;
}
return String(i);
}
getSetSize(): string {
const model = this.modelProvider.getModel();
let n = 0;
if (model.filter) {
for (const entry of model.entries) {
if (model.filter.isVisible(entry)) {
n++;
}
}
} else {
n = model.entries.length;
}
return String(n);
}
}
export class Filter implements IFilter {
constructor(private modelProvider: IModelProvider) { }
isVisible(tree: ITree, element: any): boolean {
const model = this.modelProvider.getModel();
if (!model.filter) {
return true;
}
return model.filter.isVisible(element);
}
}
export class Renderer implements IRenderer {
private styles: IQuickOpenStyles;
constructor(private modelProvider: IModelProvider, styles: IQuickOpenStyles) {
this.styles = styles;
}
updateStyles(styles: IQuickOpenStyles): void {
this.styles = styles;
}
getHeight(tree: ITree, element: any): number {
const model = this.modelProvider.getModel();
return model.renderer.getHeight(element);
}
getTemplateId(tree: ITree, element: any): string {
const model = this.modelProvider.getModel();
return model.renderer.getTemplateId(element);
}
renderTemplate(tree: ITree, templateId: string, container: HTMLElement): any {
const model = this.modelProvider.getModel();
return model.renderer.renderTemplate(templateId, container, this.styles);
}
renderElement(tree: ITree, element: any, templateId: string, templateData: any): void {
const model = this.modelProvider.getModel();
model.renderer.renderElement(element, templateId, templateData, this.styles);
}
disposeTemplate(tree: ITree, templateId: string, templateData: any): void {
const model = this.modelProvider.getModel();
model.renderer.disposeTemplate(templateId, templateData);
}
}
File diff suppressed because it is too large Load Diff
@@ -1,169 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
.monaco-quick-open-widget {
position: absolute;
width: 600px;
z-index: 2000;
padding-bottom: 6px;
left: 50%;
margin-left: -300px;
}
.monaco-quick-open-widget .monaco-progress-container {
position: absolute;
left: 0;
top: 38px;
z-index: 1;
height: 2px;
}
.monaco-quick-open-widget .monaco-progress-container .progress-bit {
height: 2px;
}
.monaco-quick-open-widget .quick-open-input {
width: 588px;
border: none;
margin: 6px;
}
.monaco-quick-open-widget .quick-open-input .monaco-inputbox {
width: 100%;
height: 25px;
}
.monaco-quick-open-widget .quick-open-result-count {
position: absolute;
left: -10000px;
}
.monaco-quick-open-widget .quick-open-tree {
line-height: 22px;
}
.monaco-quick-open-widget .quick-open-tree .monaco-tree-row > .content > .sub-content {
overflow: hidden;
}
.monaco-quick-open-widget.content-changing .quick-open-tree .monaco-scrollable-element .slider {
display: none; /* scrollbar slider causes some hectic updates when input changes quickly, so hide it while quick open changes */
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry {
overflow: hidden;
text-overflow: ellipsis;
display: flex;
flex-direction: column;
height: 100%;
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry > .quick-open-row {
display: flex;
align-items: center;
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry .quick-open-entry-icon {
overflow: hidden;
width: 16px;
height: 16px;
margin-right: 4px;
display: flex;
align-items: center;
vertical-align: middle;
flex-shrink: 0;
}
.monaco-quick-open-widget .quick-open-tree .monaco-icon-label,
.monaco-quick-open-widget .quick-open-tree .monaco-icon-label .monaco-icon-label-container > .monaco-icon-name-container {
flex: 1; /* make sure the icon label grows within the row */
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry .monaco-highlighted-label span {
opacity: 1;
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry .monaco-highlighted-label .codicon {
vertical-align: sub; /* vertically align codicon */
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry-meta {
opacity: 0.7;
line-height: normal;
}
.monaco-quick-open-widget .quick-open-tree .content.has-group-label .quick-open-entry-keybinding {
margin-right: 8px;
}
.monaco-quick-open-widget .quick-open-tree .quick-open-entry-keybinding .monaco-keybinding-key {
vertical-align: text-bottom;
}
.monaco-quick-open-widget .quick-open-tree .results-group {
margin-right: 18px;
}
.monaco-quick-open-widget .quick-open-tree .monaco-tree-row.focused > .content.has-actions > .results-group,
.monaco-quick-open-widget .quick-open-tree .monaco-tree-row:hover:not(.highlighted) > .content.has-actions > .results-group,
.monaco-quick-open-widget .quick-open-tree .focused .monaco-tree-row.focused > .content.has-actions > .results-group {
margin-right: 0px;
}
.monaco-quick-open-widget .quick-open-tree .results-group-separator {
border-top-width: 1px;
border-top-style: solid;
box-sizing: border-box;
margin-left: -11px;
padding-left: 11px;
}
/* Actions in Quick Open Items */
.monaco-tree .monaco-tree-row > .content.actions {
position: relative;
display: flex;
}
.monaco-tree .monaco-tree-row > .content.actions > .sub-content {
flex: 1;
}
.monaco-tree .monaco-tree-row > .content.actions .action-item {
margin: 0;
}
.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar {
line-height: 22px;
}
.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar {
display: none;
padding: 0 0.8em 0 0.4em;
}
.monaco-tree .monaco-tree-row.focused > .content.has-actions > .primary-action-bar {
width: 0; /* in order to support a11y with keyboard, we use width: 0 to hide the actions, which still allows to "Tab" into the actions */
display: block;
}
.monaco-tree .monaco-tree-row:hover:not(.highlighted) > .content.has-actions > .primary-action-bar,
.monaco-tree.focused .monaco-tree-row.focused > .content.has-actions > .primary-action-bar,
.monaco-tree .monaco-tree-row > .content.has-actions.more > .primary-action-bar {
width: inherit;
display: block;
}
.monaco-tree .monaco-tree-row > .content.actions > .primary-action-bar .action-label {
margin-right: 0.4em;
margin-top: 4px;
background-repeat: no-repeat;
width: 16px;
height: 16px;
}
.monaco-quick-open-widget .quick-open-tree .monaco-highlighted-label .highlight {
font-weight: bold;
}
@@ -1,95 +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 { ResolvedKeybinding } from 'vs/base/common/keyCodes';
export interface IQuickNavigateConfiguration {
keybindings: ResolvedKeybinding[];
}
export interface IAutoFocus {
/**
* The index of the element to focus in the result list.
*/
autoFocusIndex?: number;
/**
* If set to true, will automatically select the first entry from the result list.
*/
autoFocusFirstEntry?: boolean;
/**
* If set to true, will automatically select the second entry from the result list.
*/
autoFocusSecondEntry?: boolean;
/**
* If set to true, will automatically select the last entry from the result list.
*/
autoFocusLastEntry?: boolean;
/**
* If set to true, will automatically select any entry whose label starts with the search
* value. Since some entries to the top might match the query but not on the prefix, this
* allows to select the most accurate match (matching the prefix) while still showing other
* elements.
*/
autoFocusPrefixMatch?: string;
}
export const enum Mode {
PREVIEW,
OPEN,
OPEN_IN_BACKGROUND
}
export interface IEntryRunContext {
event: any;
keymods: IKeyMods;
quickNavigateConfiguration: IQuickNavigateConfiguration | undefined;
}
export interface IKeyMods {
ctrlCmd: boolean;
alt: boolean;
}
export interface IDataSource<T> {
getId(entry: T): string;
getLabel(entry: T): string | null;
}
/**
* See vs/base/parts/tree/browser/tree.ts - IRenderer
*/
export interface IRenderer<T> {
getHeight(entry: T): number;
getTemplateId(entry: T): string;
renderTemplate(templateId: string, container: any /* HTMLElement */, styles: any): any;
renderElement(entry: T, templateId: string, templateData: any, styles: any): void;
disposeTemplate(templateId: string, templateData: any): void;
}
export interface IFilter<T> {
isVisible(entry: T): boolean;
}
export interface IAccessiblityProvider<T> {
getAriaLabel(entry: T): string;
}
export interface IRunner<T> {
run(entry: T, mode: Mode, context: IEntryRunContext): boolean;
}
export interface IModel<T> {
entries: T[];
dataSource: IDataSource<T>;
renderer: IRenderer<T>;
runner: IRunner<T>;
filter?: IFilter<T>;
accessibilityProvider?: IAccessiblityProvider<T>;
}
@@ -1,49 +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 assert from 'assert';
import { QuickOpenModel, QuickOpenEntry, QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel';
import { DataSource } from 'vs/base/parts/quickopen/browser/quickOpenViewer';
suite('QuickOpen', () => {
test('QuickOpenModel', () => {
const model = new QuickOpenModel();
const entry1 = new QuickOpenEntry();
const entry2 = new QuickOpenEntry();
const entry3 = new QuickOpenEntryGroup();
assert.notEqual(entry1.getId(), entry2.getId());
assert.notEqual(entry2.getId(), entry3.getId());
model.addEntries([entry1, entry2, entry3]);
assert.equal(3, model.getEntries().length);
model.setEntries([entry1, entry2]);
assert.equal(2, model.getEntries().length);
entry1.setHidden(true);
assert.equal(1, model.getEntries(true).length);
assert.equal(entry2, model.getEntries(true)[0]);
});
test('QuickOpenDataSource', async () => {
const model = new QuickOpenModel();
const entry1 = new QuickOpenEntry();
const entry2 = new QuickOpenEntry();
const entry3 = new QuickOpenEntryGroup();
model.addEntries([entry1, entry2, entry3]);
const ds = new DataSource(model);
assert.equal(entry1.getId(), ds.getId(null!, entry1));
assert.equal(true, ds.hasChildren(null!, model));
assert.equal(false, ds.hasChildren(null!, entry1));
const children = await ds.getChildren(null!, model);
assert.equal(3, children.length);
});
});
+5 -5
View File
@@ -18,17 +18,17 @@ export enum StorageHint {
}
export interface IStorageOptions {
hint?: StorageHint;
readonly hint?: StorageHint;
}
export interface IUpdateRequest {
insert?: Map<string, string>;
delete?: Set<string>;
readonly insert?: Map<string, string>;
readonly delete?: Set<string>;
}
export interface IStorageItemsChangeEvent {
changed?: Map<string, string>;
deleted?: Set<string>;
readonly changed?: Map<string, string>;
readonly deleted?: Set<string>;
}
export interface IStorageDatabase {
+2 -3
View File
@@ -14,7 +14,6 @@ import { IStorageDatabase, IStorageItemsChangeEvent, IUpdateRequest } from 'vs/b
interface IDatabaseConnection {
readonly db: Database;
readonly isInMemory: boolean;
isErroneous?: boolean;
@@ -22,7 +21,7 @@ interface IDatabaseConnection {
}
export interface ISQLiteStorageDatabaseOptions {
logging?: ISQLiteStorageDatabaseLoggingOptions;
readonly logging?: ISQLiteStorageDatabaseLoggingOptions;
}
export interface ISQLiteStorageDatabaseLoggingOptions {
@@ -45,7 +44,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase {
private readonly whenConnected = this.connect(this.path);
constructor(private path: string, private options: ISQLiteStorageDatabaseOptions = Object.create(null)) { }
constructor(private readonly path: string, private readonly options: ISQLiteStorageDatabaseOptions = Object.create(null)) { }
async getItems(): Promise<Map<string, string>> {
const connection = await this.whenConnected;
@@ -4,13 +4,12 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
import { prepareActions } from 'vs/workbench/browser/actions';
import { Separator, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar';
import { Action } from 'vs/base/common/actions';
suite('Workbench action registry', () => {
suite('Actionbar', () => {
test('Workbench Action Bar prepareActions()', function () {
test('prepareActions()', function () {
let a1 = new Separator();
let a2 = new Separator();
let a3 = new Action('a3');
+12
View File
@@ -2,9 +2,11 @@
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import { IMatch } from 'vs/base/common/filters';
import { matchesFuzzyCodiconAware, parseCodicons, IParsedCodicons } from 'vs/base/common/codicon';
import { stripCodicons } from 'vs/base/common/codicons';
export interface ICodiconFilter {
// Returns null if word doesn't match.
@@ -64,3 +66,13 @@ suite('Codicon', () => {
]);
});
});
suite('Codicons', () => {
test('stripCodicons', () => {
assert.equal(stripCodicons('Hello World'), 'Hello World');
assert.equal(stripCodicons('$(Hello World'), '$(Hello World');
assert.equal(stripCodicons('$(Hello) World'), ' World');
assert.equal(stripCodicons('$(Hello) W$(oi)rld'), ' Wrld');
});
});
+70 -3
View File
@@ -43,7 +43,7 @@ class NullAccessorClass implements scorer.IItemAccessor<URI> {
}
function _doScore(target: string, query: string, fuzzy: boolean): scorer.Score {
return scorer.score(target, query, query.toLowerCase(), fuzzy);
return scorer.score(target, scorer.prepareQuery(query), fuzzy);
}
function scoreItem<T>(item: T, query: string, fuzzy: boolean, accessor: scorer.IItemAccessor<T>, cache: scorer.ScorerCache): scorer.IItemScore {
@@ -109,6 +109,42 @@ suite('Fuzzy Scorer', () => {
assert.equal(_doScore(target, 'eo', false)[0], 0);
});
test('score (fuzzy, multiple)', function () {
const target = 'HeLlo-World';
const [firstSingleScore, firstSinglePositions] = _doScore(target, 'HelLo', true);
const [secondSingleScore, secondSinglePositions] = _doScore(target, 'World', true);
const firstAndSecondSinglePositions = [...firstSinglePositions, ...secondSinglePositions];
let [multiScore, multiPositions] = _doScore(target, 'HelLo World', true);
function assertScore() {
assert.ok(multiScore >= firstSingleScore + secondSingleScore);
for (let i = 0; i < multiPositions.length; i++) {
assert.equal(multiPositions[i], firstAndSecondSinglePositions[i]);
}
}
function assertNoScore() {
assert.equal(multiScore, 0);
assert.equal(multiPositions.length, 0);
}
assertScore();
[multiScore, multiPositions] = _doScore(target, 'World HelLo', true);
assertScore();
[multiScore, multiPositions] = _doScore(target, 'World HelLo World', true);
assertScore();
[multiScore, multiPositions] = _doScore(target, 'World HelLo Nothing', true);
assertNoScore();
[multiScore, multiPositions] = _doScore(target, 'More Nothing', true);
assertNoScore();
});
test('scoreItem - matches are proper', function () {
let res = scoreItem(null, 'something', true, ResourceAccessor, cache);
assert.ok(!res.score);
@@ -820,11 +856,42 @@ suite('Fuzzy Scorer', () => {
assert.equal(res[0], resourceB);
});
test('prepareSearchForScoring', () => {
test('prepareQuery', () => {
assert.equal(scorer.prepareQuery(' f*a ').value, 'fa');
assert.equal(scorer.prepareQuery('model Tester.ts').original, 'model Tester.ts');
assert.equal(scorer.prepareQuery('model Tester.ts').originalLowercase, 'model Tester.ts'.toLowerCase());
assert.equal(scorer.prepareQuery('model Tester.ts').value, 'modelTester.ts');
assert.equal(scorer.prepareQuery('Model Tester.ts').lowercase, 'modeltester.ts');
assert.equal(scorer.prepareQuery('Model Tester.ts').valueLowercase, 'modeltester.ts');
assert.equal(scorer.prepareQuery('ModelTester.ts').containsPathSeparator, false);
assert.equal(scorer.prepareQuery('Model' + sep + 'Tester.ts').containsPathSeparator, true);
// with spaces
let query = scorer.prepareQuery('He*llo World');
assert.equal(query.original, 'He*llo World');
assert.equal(query.value, 'HelloWorld');
assert.equal(query.valueLowercase, 'HelloWorld'.toLowerCase());
assert.equal(query.values?.length, 2);
assert.equal(query.values?.[0].original, 'He*llo');
assert.equal(query.values?.[0].value, 'Hello');
assert.equal(query.values?.[0].valueLowercase, 'Hello'.toLowerCase());
assert.equal(query.values?.[1].original, 'World');
assert.equal(query.values?.[1].value, 'World');
assert.equal(query.values?.[1].valueLowercase, 'World'.toLowerCase());
// with spaces that are empty
query = scorer.prepareQuery(' Hello World ');
assert.equal(query.original, ' Hello World ');
assert.equal(query.originalLowercase, ' Hello World '.toLowerCase());
assert.equal(query.value, 'HelloWorld');
assert.equal(query.valueLowercase, 'HelloWorld'.toLowerCase());
assert.equal(query.values?.length, 2);
assert.equal(query.values?.[0].original, 'Hello');
assert.equal(query.values?.[0].originalLowercase, 'Hello'.toLowerCase());
assert.equal(query.values?.[0].value, 'Hello');
assert.equal(query.values?.[0].valueLowercase, 'Hello'.toLowerCase());
assert.equal(query.values?.[1].original, 'World');
assert.equal(query.values?.[1].originalLowercase, 'World'.toLowerCase());
assert.equal(query.values?.[1].value, 'World');
assert.equal(query.values?.[1].valueLowercase, 'World'.toLowerCase());
});
});
+1 -1
View File
@@ -171,7 +171,7 @@ export class CodeApplication extends Disposable {
return false;
}
if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%09%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') {
if (source === 'data:text/html;charset=utf-8,%3C%21DOCTYPE%20html%3E%0D%0A%3Chtml%20lang%3D%22en%22%20style%3D%22width%3A%20100%25%3B%20height%3A%20100%25%22%3E%0D%0A%3Chead%3E%0D%0A%3Ctitle%3EVirtual%20Document%3C%2Ftitle%3E%0D%0A%3C%2Fhead%3E%0D%0A%3Cbody%20style%3D%22margin%3A%200%3B%20overflow%3A%20hidden%3B%20width%3A%20100%25%3B%20height%3A%20100%25%22%20role%3D%22document%22%3E%0D%0A%3C%2Fbody%3E%0D%0A%3C%2Fhtml%3E') {
return true;
}
@@ -457,6 +457,9 @@ export class TextAreaHandler extends ViewPart {
this.textArea.setAttribute('aria-autocomplete', 'both');
this.textArea.removeAttribute('aria-activedescendant');
}
if (options.role) {
this.textArea.setAttribute('role', options.role);
}
}
// --- end view API
+1
View File
@@ -333,6 +333,7 @@ export interface IOverviewRuler {
*/
export interface IEditorAriaOptions {
activeDescendant: string | undefined;
role?: string;
}
/**
@@ -14,17 +14,17 @@ import { IModelDecorationOptions, IModelDecorationOverviewRulerOptions, Overview
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
import { IColorTheme, IThemeService, ThemeColor } from 'vs/platform/theme/common/themeService';
class RefCountedStyleSheet {
export class RefCountedStyleSheet {
private readonly _parent: CodeEditorServiceImpl;
private readonly _editorId: string;
public readonly styleSheet: HTMLStyleElement;
private readonly _styleSheet: HTMLStyleElement;
private _refCount: number;
constructor(parent: CodeEditorServiceImpl, editorId: string, styleSheet: HTMLStyleElement) {
this._parent = parent;
this._editorId = editorId;
this.styleSheet = styleSheet;
this._styleSheet = styleSheet;
this._refCount = 0;
}
@@ -35,17 +35,26 @@ class RefCountedStyleSheet {
public unref(): void {
this._refCount--;
if (this._refCount === 0) {
this.styleSheet.parentNode?.removeChild(this.styleSheet);
this._styleSheet.parentNode?.removeChild(this._styleSheet);
this._parent._removeEditorStyleSheets(this._editorId);
}
}
public insertRule(rule: string, index?: number): void {
const sheet = <CSSStyleSheet>this._styleSheet.sheet;
sheet.insertRule(rule, index);
}
public removeRulesContainingSelector(ruleName: string): void {
dom.removeCSSRulesContainingSelector(ruleName, this._styleSheet);
}
}
class GlobalStyleSheet {
public readonly styleSheet: HTMLStyleElement;
export class GlobalStyleSheet {
private readonly _styleSheet: HTMLStyleElement;
constructor(styleSheet: HTMLStyleElement) {
this.styleSheet = styleSheet;
this._styleSheet = styleSheet;
}
public ref(): void {
@@ -53,6 +62,15 @@ class GlobalStyleSheet {
public unref(): void {
}
public insertRule(rule: string, index?: number): void {
const sheet = <CSSStyleSheet>this._styleSheet.sheet;
sheet.insertRule(rule, index);
}
public removeRulesContainingSelector(ruleName: string): void {
dom.removeCSSRulesContainingSelector(ruleName, this._styleSheet);
}
}
export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
@@ -62,9 +80,9 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
private readonly _editorStyleSheets = new Map<string, RefCountedStyleSheet>();
private readonly _themeService: IThemeService;
constructor(@IThemeService themeService: IThemeService, styleSheet: HTMLStyleElement | null = null) {
constructor(@IThemeService themeService: IThemeService, styleSheet: GlobalStyleSheet | null = null) {
super();
this._globalStyleSheet = styleSheet ? new GlobalStyleSheet(styleSheet) : null;
this._globalStyleSheet = styleSheet ? styleSheet : null;
this._themeService = themeService;
}
@@ -100,7 +118,7 @@ export abstract class CodeEditorServiceImpl extends AbstractCodeEditorService {
if (!provider) {
const styleSheet = this._getOrCreateStyleSheet(editor);
const providerArgs: ProviderArguments = {
styleSheet: styleSheet.styleSheet,
styleSheet: styleSheet,
key: key,
parentTypeKey: parentTypeKey,
options: options || Object.create(null)
@@ -188,7 +206,7 @@ class DecorationSubTypeOptionsProvider implements IModelDecorationOptionsProvide
}
interface ProviderArguments {
styleSheet: HTMLStyleElement;
styleSheet: GlobalStyleSheet | RefCountedStyleSheet;
key: string;
parentTypeKey?: string;
options: IDecorationRenderOptions;
@@ -330,7 +348,7 @@ class DecorationCSSRules {
private readonly _providerArgs: ProviderArguments;
private _usesThemeColors: boolean;
public constructor(ruleType: ModelDecorationCSSRuleType, providerArgs: ProviderArguments, themeService: IThemeService) {
constructor(ruleType: ModelDecorationCSSRuleType, providerArgs: ProviderArguments, themeService: IThemeService) {
this._theme = themeService.getColorTheme();
this._ruleType = ruleType;
this._providerArgs = providerArgs;
@@ -414,7 +432,7 @@ class DecorationCSSRules {
default:
throw new Error('Unknown rule type: ' + this._ruleType);
}
const sheet = <CSSStyleSheet>this._providerArgs.styleSheet.sheet;
const sheet = this._providerArgs.styleSheet;
let hasContent = false;
if (unthemedCSS.length > 0) {
@@ -433,7 +451,7 @@ class DecorationCSSRules {
}
private _removeCSS(): void {
dom.removeCSSRulesContainingSelector(this._unThemedSelector, this._providerArgs.styleSheet);
this._providerArgs.styleSheet.removeRulesContainingSelector(this._unThemedSelector);
}
/**
+15 -8
View File
@@ -40,9 +40,9 @@ class SingleModelEditStackData {
public changes: TextChange[]
) { }
public append(model: ITextModel, operations: IValidEditOperation[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
if (operations.length > 0) {
this.changes = compressConsecutiveTextChanges(this.changes, operations.map(op => op.textChange));
public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
if (textChanges.length > 0) {
this.changes = compressConsecutiveTextChanges(this.changes, textChanges);
}
this.afterEOL = afterEOL;
this.afterVersionId = afterVersionId;
@@ -168,9 +168,9 @@ export class SingleModelEditStackElement implements IResourceUndoRedoElement {
return (this.model === model && this._data instanceof SingleModelEditStackData);
}
public append(model: ITextModel, operations: IValidEditOperation[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
if (this._data instanceof SingleModelEditStackData) {
this._data.append(model, operations, afterEOL, afterVersionId, afterCursorState);
this._data.append(model, textChanges, afterEOL, afterVersionId, afterCursorState);
}
}
@@ -258,10 +258,10 @@ export class MultiModelEditStackElement implements IWorkspaceUndoRedoElement {
return false;
}
public append(model: ITextModel, operations: IValidEditOperation[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
public append(model: ITextModel, textChanges: TextChange[], afterEOL: EndOfLineSequence, afterVersionId: number, afterCursorState: Selection[] | null): void {
const key = uriGetComparisonKey(model.uri);
const editStackElement = this._editStackElementsMap.get(key)!;
editStackElement.append(model, operations, afterEOL, afterVersionId, afterCursorState);
editStackElement.append(model, textChanges, afterEOL, afterVersionId, afterCursorState);
}
public close(): void {
@@ -355,7 +355,14 @@ export class EditStack {
const editStackElement = this._getOrCreateEditStackElement(beforeCursorState);
const inverseEditOperations = this._model.applyEdits(editOperations, true);
const afterCursorState = EditStack._computeCursorState(cursorStateComputer, inverseEditOperations);
editStackElement.append(this._model, inverseEditOperations, getModelEOL(this._model), this._model.getAlternativeVersionId(), afterCursorState);
const textChanges = inverseEditOperations.map((op, index) => ({ index: index, textChange: op.textChange }));
textChanges.sort((a, b) => {
if (a.textChange.oldPosition === b.textChange.oldPosition) {
return a.index - b.index;
}
return a.textChange.oldPosition - b.textChange.oldPosition;
});
editStackElement.append(this._model, textChanges.map(op => op.textChange), getModelEOL(this._model), this._model.getAlternativeVersionId(), afterCursorState);
return afterCursorState;
}
@@ -661,12 +661,14 @@ const enum Constants {
class HashTableEntry {
public readonly tokenTypeIndex: number;
public readonly tokenModifierSet: number;
public readonly languageId: number;
public readonly metadata: number;
public next: HashTableEntry | null;
constructor(tokenTypeIndex: number, tokenModifierSet: number, metadata: number) {
constructor(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number) {
this.tokenTypeIndex = tokenTypeIndex;
this.tokenModifierSet = tokenModifierSet;
this.languageId = languageId;
this.metadata = metadata;
this.next = null;
}
@@ -697,16 +699,17 @@ class HashTable {
}
}
private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number): number {
return ((((tokenTypeIndex << 5) - tokenTypeIndex) + tokenModifierSet) | 0) % this._currentLength; // tokenTypeIndex * 31 + tokenModifierSet, keep as int32
private _hashFunc(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): number {
const hash = (n1: number, n2: number) => (((n1 << 5) - n1) + n2) | 0; // n1 * 31 + n2, keep as int32
return hash(hash(tokenTypeIndex, tokenModifierSet), languageId) % this._currentLength;
}
public get(tokenTypeIndex: number, tokenModifierSet: number): HashTableEntry | null {
const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet);
public get(tokenTypeIndex: number, tokenModifierSet: number, languageId: number): HashTableEntry | null {
const hash = this._hashFunc(tokenTypeIndex, tokenModifierSet, languageId);
let p = this._elements[hash];
while (p) {
if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet) {
if (p.tokenTypeIndex === tokenTypeIndex && p.tokenModifierSet === tokenModifierSet && p.languageId === languageId) {
return p;
}
p = p.next;
@@ -715,7 +718,7 @@ class HashTable {
return null;
}
public add(tokenTypeIndex: number, tokenModifierSet: number, metadata: number): void {
public add(tokenTypeIndex: number, tokenModifierSet: number, languageId: number, metadata: number): void {
this._elementsCount++;
if (this._growCount !== 0 && this._elementsCount >= this._growCount) {
// expand!
@@ -737,11 +740,11 @@ class HashTable {
}
}
}
this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, metadata));
this._add(new HashTableEntry(tokenTypeIndex, tokenModifierSet, languageId, metadata));
}
private _add(element: HashTableEntry): void {
const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet);
const hash = this._hashFunc(element.tokenTypeIndex, element.tokenModifierSet, element.languageId);
element.next = this._elements[hash];
this._elements[hash] = element;
}
@@ -759,8 +762,8 @@ class SemanticColoringProviderStyling {
this._hashTable = new HashTable();
}
public getMetadata(tokenTypeIndex: number, tokenModifierSet: number): number {
const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet);
public getMetadata(tokenTypeIndex: number, tokenModifierSet: number, languageId: LanguageIdentifier): number {
const entry = this._hashTable.get(tokenTypeIndex, tokenModifierSet, languageId.id);
let metadata: number;
if (entry) {
metadata = entry.metadata;
@@ -775,7 +778,7 @@ class SemanticColoringProviderStyling {
modifierSet = modifierSet >> 1;
}
const tokenStyle = this._themeService.getColorTheme().getTokenStyleMetadata(tokenType, tokenModifiers);
const tokenStyle = this._themeService.getColorTheme().getTokenStyleMetadata(tokenType, tokenModifiers, languageId.language);
if (typeof tokenStyle === 'undefined') {
metadata = Constants.NO_STYLING;
} else {
@@ -801,7 +804,7 @@ class SemanticColoringProviderStyling {
metadata = Constants.NO_STYLING;
}
}
this._hashTable.add(tokenTypeIndex, tokenModifierSet, metadata);
this._hashTable.add(tokenTypeIndex, tokenModifierSet, languageId.id, metadata);
}
if (this._logService.getLevel() === LogLevel.Trace) {
const type = this._legend.tokenTypes[tokenTypeIndex];
@@ -1042,6 +1045,8 @@ class ModelSemanticColoring extends Disposable {
const result: MultilineTokens2[] = [];
const languageId = this._model.getLanguageIdentifier();
let tokenIndex = 0;
let lastLineNumber = 1;
let lastStartCharacter = 0;
@@ -1081,7 +1086,7 @@ class ModelSemanticColoring extends Disposable {
const length = srcData[srcOffset + 2];
const tokenTypeIndex = srcData[srcOffset + 3];
const tokenModifierSet = srcData[srcOffset + 4];
const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet);
const metadata = styling.getMetadata(tokenTypeIndex, tokenModifierSet, languageId);
if (metadata !== Constants.NO_STYLING) {
if (areaLine === 0) {
@@ -20,10 +20,6 @@
color: var(--outline-element-color);
}
.monaco-tree .monaco-tree-row.focused .outline-element .outline-element-detail {
visibility: inherit;
}
.monaco-list .outline-element .outline-element-decoration {
opacity: 0.75;
font-size: 90%;
@@ -62,7 +62,6 @@ function flatten(bucket: DocumentSymbol[], entries: DocumentSymbol[], overrideCo
}
}
CommandsRegistry.registerCommand('_executeDocumentSymbolProvider', async function (accessor, ...args) {
const [resource] = args;
assertType(URI.isUri(resource));
@@ -241,7 +241,7 @@ export class ChangeIndentationSizeAction extends EditorAction {
}
}
});
}, 50/* quick open is sensitive to being opened so soon after another */);
}, 50/* quick input is sensitive to being opened so soon after another */);
}
}
@@ -72,7 +72,7 @@ export abstract class AbstractEditorNavigationQuickAccessProvider implements IQu
// Remember view state and update it when the cursor position
// changes even later because it could be that the user has
// configured quick open to remain open when focus is lost and
// configured quick access to remain open when focus is lost and
// we always want to restore the current location.
let lastKnownEditorViewState = withNullAsUndefined(editor.saveViewState());
disposables.add(codeEditor.onDidChangeCursorPosition(() => {
@@ -17,6 +17,7 @@ import { values } from 'vs/base/common/collections';
import { trim, format } from 'vs/base/common/strings';
import { fuzzyScore, FuzzyScore, createMatches } from 'vs/base/common/filters';
import { assign } from 'vs/base/common/objects';
import { prepareQuery, IPreparedQuery } from 'vs/base/common/fuzzyScorer';
export interface IGotoSymbolQuickPickItem extends IQuickPickItem {
kind: SymbolKind,
@@ -155,7 +156,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
// Collect symbol picks
picker.busy = true;
try {
const items = await this.doGetSymbolPicks(symbolsPromise, picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim(), picksCts.token);
const items = await this.doGetSymbolPicks(symbolsPromise, prepareQuery(picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim()), undefined, picksCts.token);
if (token.isCancellationRequested) {
return;
}
@@ -194,18 +195,24 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
return disposables;
}
protected async doGetSymbolPicks(symbolsPromise: Promise<DocumentSymbol[]>, filter: string, token: CancellationToken): Promise<Array<IGotoSymbolQuickPickItem | IQuickPickSeparator>> {
protected async doGetSymbolPicks(symbolsPromise: Promise<DocumentSymbol[]>, query: IPreparedQuery, options: { extraContainerLabel?: string } | undefined, token: CancellationToken): Promise<Array<IGotoSymbolQuickPickItem | IQuickPickSeparator>> {
const symbols = await symbolsPromise;
if (token.isCancellationRequested) {
return [];
}
// Normalize filter
const filterBySymbolKind = filter.indexOf(AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX) === 0;
const filterBySymbolKind = query.original.indexOf(AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX) === 0;
const filterPos = filterBySymbolKind ? 1 : 0;
const [symbolFilter, containerFilter] = filter.split(' ') as [string, string | undefined];
const symbolFilterLow = symbolFilter.toLowerCase();
const containerFilterLow = containerFilter?.toLowerCase();
// Split between symbol and container query if separated by space
let symbolQuery: IPreparedQuery;
let containerQuery: IPreparedQuery | undefined;
if (query.values && query.values.length > 1) {
symbolQuery = prepareQuery(query.values[0].original);
containerQuery = prepareQuery(query.values[1].original);
} else {
symbolQuery = query;
}
// Convert to symbol picks and apply filtering
const filteredSymbolPicks: IGotoSymbolQuickPickItem[] = [];
@@ -213,22 +220,28 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit
const symbol = symbols[index];
const symbolLabel = trim(symbol.name);
const containerLabel = symbol.containerName;
let containerLabel = symbol.containerName;
if (containerLabel && options?.extraContainerLabel) {
containerLabel = `${options.extraContainerLabel}${containerLabel}`;
} else {
containerLabel = options?.extraContainerLabel;
}
let symbolScore: FuzzyScore | undefined = undefined;
let containerScore: FuzzyScore | undefined = undefined;
let includeSymbol = true;
if (filter.length > filterPos) {
if (query.original.length > filterPos) {
// Score by symbol
symbolScore = fuzzyScore(symbolFilter, symbolFilterLow, filterPos, symbolLabel, symbolLabel.toLowerCase(), 0, true);
symbolScore = fuzzyScore(symbolQuery.original, symbolQuery.originalLowercase, filterPos, symbolLabel, symbolLabel.toLowerCase(), 0, true);
includeSymbol = !!symbolScore;
// Score by container if specified
if (includeSymbol && containerFilter && containerFilterLow) {
if (includeSymbol && containerQuery) {
if (containerLabel) {
containerScore = fuzzyScore(containerFilter, containerFilterLow, filterPos, containerLabel, containerLabel.toLowerCase(), 0, true);
containerScore = fuzzyScore(containerQuery.original, containerQuery.originalLowercase, filterPos, containerLabel, containerLabel.toLowerCase(), 0, true);
}
includeSymbol = !!containerScore;
@@ -401,12 +401,13 @@ class WordHighlighter {
private renderDecorations(): void {
this.renderDecorationsTimer = -1;
let decorations: IModelDeltaDecoration[] = [];
for (let i = 0, len = this.workerRequestValue.length; i < len; i++) {
let info = this.workerRequestValue[i];
decorations.push({
range: info.range,
options: WordHighlighter._getDecorationOptions(info.kind)
});
for (const info of this.workerRequestValue) {
if (info.range) {
decorations.push({
range: info.range,
options: WordHighlighter._getDecorationOptions(info.kind)
});
}
}
this._decorationIds = this.editor.deltaDecorations(this._decorationIds, decorations);
@@ -129,10 +129,6 @@ export class StandaloneQuickInputServiceImpl implements IQuickInputService {
cancel(): Promise<void> {
return this.activeService.cancel();
}
hide(focusLost?: boolean | undefined): void {
return this.activeService.hide(focusLost);
}
}
export class QuickInputEditorContribution implements IEditorContribution {
@@ -134,26 +134,6 @@
box-sizing: border-box;
}
/* tree */
.monaco-editor.vs .monaco-tree .monaco-tree-row,
.monaco-editor.vs-dark .monaco-tree .monaco-tree-row {
-ms-high-contrast-adjust: none;
color: windowtext !important;
}
.monaco-editor.vs .monaco-tree .monaco-tree-row.selected,
.monaco-editor.vs-dark .monaco-tree .monaco-tree-row.selected,
.monaco-editor.vs .monaco-tree .monaco-tree-row.focused,
.monaco-editor.vs-dark .monaco-tree .monaco-tree-row.focused {
color: highlighttext !important;
background-color: highlight !important;
}
.monaco-editor.vs .monaco-tree .monaco-tree-row:hover,
.monaco-editor.vs-dark .monaco-tree .monaco-tree-row:hover {
background: transparent !important;
border: 1px solid highlight;
box-sizing: border-box;
}
/* scrollbars */
.monaco-editor.vs .monaco-scrollable-element > .scrollbar,
.monaco-editor.vs-dark .monaco-scrollable-element > .scrollbar {
@@ -131,7 +131,7 @@ class StandaloneTheme implements IStandaloneTheme {
return this._tokenTheme;
}
public getTokenStyleMetadata(type: string, modifiers: string[]): ITokenStyle | undefined {
public getTokenStyleMetadata(type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined {
return undefined;
}
@@ -56,7 +56,7 @@ suite('TokenizationSupport2Adapter', () => {
throw new Error('Not implemented');
},
getTokenStyleMetadata: (type: string, modifiers: string[]): ITokenStyle | undefined => {
getTokenStyleMetadata: (type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined => {
return undefined;
},
@@ -5616,4 +5616,24 @@ suite('Undo stops', () => {
});
});
test('issue #93585: Undo multi cursor edit corrupts document', () => {
let model = createTextModel(
[
'hello world',
'hello world',
].join('\n')
);
withTestCodeEditor(null, { model: model }, (editor, cursor) => {
cursor.setSelections('test', [
new Selection(2, 7, 2, 12),
new Selection(1, 7, 1, 12),
]);
cursorCommand(cursor, H.Type, { text: 'no' }, 'keyboard');
assert.equal(model.getValue(), 'hello no\nhello no');
CoreEditingCommands.Undo.runEditorCommand(null, editor, null);
assert.equal(model.getValue(), 'hello world\nhello world');
});
});
});
@@ -4,18 +4,17 @@
*--------------------------------------------------------------------------------------------*/
import * as assert from 'assert';
import * as dom from 'vs/base/browser/dom';
import * as platform from 'vs/base/common/platform';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { CodeEditorServiceImpl } from 'vs/editor/browser/services/codeEditorServiceImpl';
import { CodeEditorServiceImpl, GlobalStyleSheet } from 'vs/editor/browser/services/codeEditorServiceImpl';
import { IDecorationRenderOptions } from 'vs/editor/common/editorCommon';
import { IResourceEditorInput } from 'vs/platform/editor/common/editor';
import { TestColorTheme, TestThemeService } from 'vs/platform/theme/test/common/testThemeService';
const themeServiceMock = new TestThemeService();
export class TestCodeEditorServiceImpl extends CodeEditorServiceImpl {
class TestCodeEditorServiceImpl extends CodeEditorServiceImpl {
getActiveCodeEditor(): ICodeEditor | null {
return null;
}
@@ -25,6 +24,32 @@ export class TestCodeEditorServiceImpl extends CodeEditorServiceImpl {
}
}
class TestGlobalStyleSheet extends GlobalStyleSheet {
public rules: string[] = [];
constructor() {
super(null!);
}
public insertRule(rule: string, index?: number): void {
this.rules.unshift(rule);
}
public removeRulesContainingSelector(ruleName: string): void {
for (let i = 0; i < this.rules.length; i++) {
if (this.rules[i].indexOf(ruleName) >= 0) {
this.rules.splice(i, 1);
i--;
}
}
}
public read(): string {
return this.rules.join('\n');
}
}
suite.skip('Decoration Render Options', () => { // {{SQL CARBON EDIT}} skip suite
let options: IDecorationRenderOptions = {
gutterIconPath: URI.parse('https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png'),
@@ -45,59 +70,45 @@ suite.skip('Decoration Render Options', () => { // {{SQL CARBON EDIT}} skip suit
assert.throws(() => s.resolveDecorationOptions('example', false));
});
function readStyleSheet(styleSheet: HTMLStyleElement): string {
if ((<any>styleSheet.sheet).rules) {
return Array.prototype.map.call((<any>styleSheet.sheet).rules, (r: { cssText: string }) => r.cssText).join('\n');
}
return styleSheet.sheet!.toString();
function readStyleSheet(styleSheet: TestGlobalStyleSheet): string {
return styleSheet.read();
}
test('css properties', () => {
let styleSheet = dom.createStyleSheet();
let s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
const styleSheet = new TestGlobalStyleSheet();
const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', options);
let sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png") center center / contain no-repeat;') > 0
|| sheet.indexOf('background-image: url("https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png"); background-size: contain; background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
assert(sheet.indexOf('border-color: yellow;') > 0);
assert(sheet.indexOf('background-color: red;') > 0);
const sheet = readStyleSheet(styleSheet);
assert(sheet.indexOf(`{background:url('https://github.com/Microsoft/vscode/blob/master/resources/linux/code.png') center center no-repeat;background-size:contain;}`) >= 0);
assert(sheet.indexOf(`{background-color:red;border-color:yellow;box-sizing: border-box;}`) >= 0);
});
test('theme color', () => {
let options: IDecorationRenderOptions = {
const options: IDecorationRenderOptions = {
backgroundColor: { id: 'editorBackground' },
borderColor: { id: 'editorBorder' },
};
let colors: { [key: string]: string } = {
const styleSheet = new TestGlobalStyleSheet();
const themeService = new TestThemeService(new TestColorTheme({
editorBackground: '#FF0000'
};
let styleSheet = dom.createStyleSheet();
let themeService = new TestThemeService(new TestColorTheme(colors));
let s = new TestCodeEditorServiceImpl(themeService, styleSheet);
}));
const s = new TestCodeEditorServiceImpl(themeService, styleSheet);
s.registerDecorationType('example', options);
let sheet = readStyleSheet(styleSheet);
assert.equal(sheet, '.monaco-editor .ced-example-0 { background-color: rgb(255, 0, 0); border-color: transparent; box-sizing: border-box; }');
assert.equal(readStyleSheet(styleSheet), '.monaco-editor .ced-example-0 {background-color:#ff0000;border-color:transparent;box-sizing: border-box;}');
colors = {
themeService.setTheme(new TestColorTheme({
editorBackground: '#EE0000',
editorBorder: '#00FFFF'
};
themeService.setTheme(new TestColorTheme(colors));
sheet = readStyleSheet(styleSheet);
assert.equal(sheet, '.monaco-editor .ced-example-0 { background-color: rgb(238, 0, 0); border-color: rgb(0, 255, 255); box-sizing: border-box; }');
}));
assert.equal(readStyleSheet(styleSheet), '.monaco-editor .ced-example-0 {background-color:#ee0000;border-color:#00ffff;box-sizing: border-box;}');
s.removeDecorationType('example');
sheet = readStyleSheet(styleSheet);
assert.equal(sheet, '');
assert.equal(readStyleSheet(styleSheet), '');
});
test('theme overrides', () => {
let options: IDecorationRenderOptions = {
const options: IDecorationRenderOptions = {
color: { id: 'editorBackground' },
light: {
color: '#FF00FF'
@@ -109,86 +120,59 @@ suite.skip('Decoration Render Options', () => { // {{SQL CARBON EDIT}} skip suit
}
}
};
let colors: { [key: string]: string } = {
const styleSheet = new TestGlobalStyleSheet();
const themeService = new TestThemeService(new TestColorTheme({
editorBackground: '#FF0000',
infoForeground: '#444444'
};
let styleSheet = dom.createStyleSheet();
let themeService = new TestThemeService(new TestColorTheme(colors));
let s = new TestCodeEditorServiceImpl(themeService, styleSheet);
}));
const s = new TestCodeEditorServiceImpl(themeService, styleSheet);
s.registerDecorationType('example', options);
let sheet = readStyleSheet(styleSheet);
let expected =
'.vs-dark.monaco-editor .ced-example-4::after, .hc-black.monaco-editor .ced-example-4::after { color: rgb(68, 68, 68) !important; }\n' +
'.vs-dark.monaco-editor .ced-example-1, .hc-black.monaco-editor .ced-example-1 { color: rgb(0, 0, 0) !important; }\n' +
'.vs.monaco-editor .ced-example-1 { color: rgb(255, 0, 255) !important; }\n' +
'.monaco-editor .ced-example-1 { color: rgb(255, 0, 0) !important; }';
assert.equal(sheet, expected);
const expected = [
'.vs-dark.monaco-editor .ced-example-4::after, .hc-black.monaco-editor .ced-example-4::after {color:#444444 !important;}',
'.vs-dark.monaco-editor .ced-example-1, .hc-black.monaco-editor .ced-example-1 {color:#000000 !important;}',
'.vs.monaco-editor .ced-example-1 {color:#FF00FF !important;}',
'.monaco-editor .ced-example-1 {color:#ff0000 !important;}'
].join('\n');
assert.equal(readStyleSheet(styleSheet), expected);
s.removeDecorationType('example');
sheet = readStyleSheet(styleSheet);
assert.equal(sheet, '');
assert.equal(readStyleSheet(styleSheet), '');
});
test('css properties, gutterIconPaths', () => {
let styleSheet = dom.createStyleSheet();
const styleSheet = new TestGlobalStyleSheet();
const s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
// unix file path (used as string)
let s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/bar.png') });
let sheet = readStyleSheet(styleSheet);//.innerHTML || styleSheet.sheet.toString();
assert(
sheet.indexOf('background: url(\'file:///Users/foo/bar.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///Users/foo/bar.png") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("file:///Users/foo/bar.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
// URI, only minimal encoding
s.registerDecorationType('example', { gutterIconPath: URI.parse('data:image/svg+xml;base64,PHN2ZyB4b+') });
assert(readStyleSheet(styleSheet).indexOf(`{background:url('data:image/svg+xml;base64,PHN2ZyB4b+') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
// windows file path (used as string)
if (platform.isWindows) {
s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
// windows file path (used as string)
s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\miles\\more.png') });
sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'file:///c%3A/files/miles/more.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///c%3A/files/miles/more.png") center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///c:/files/miles/more.png") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("file:///c:/files/miles/more.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///c:/files/miles/more.png') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
// single quote must always be escaped/encoded
s.registerDecorationType('example', { gutterIconPath: URI.file('c:\\files\\foo\\b\'ar.png') });
assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///c:/files/foo/b%27ar.png') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
} else {
// unix file path (used as string)
s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/bar.png') });
assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///Users/foo/bar.png') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
// single quote must always be escaped/encoded
s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') });
assert(readStyleSheet(styleSheet).indexOf(`{background:url('file:///Users/foo/b%27ar.png') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
}
// URI, only minimal encoding
s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', { gutterIconPath: URI.parse('data:image/svg+xml;base64,PHN2ZyB4b+') });
sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'data:image/svg+xml;base64,PHN2ZyB4b+\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("data:image/svg+xml;base64,PHN2ZyB4b+") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("data:image/svg+xml;base64,PHN2ZyB4b+"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
s.removeDecorationType('example');
// single quote must always be escaped/encoded
s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', { gutterIconPath: URI.file('/Users/foo/b\'ar.png') });
sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'file:///Users/foo/b%27ar.png\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("file:///Users/foo/b%27ar.png") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("file:///Users/foo/b%27ar.png"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
s.removeDecorationType('example');
s = new TestCodeEditorServiceImpl(themeServiceMock, styleSheet);
s.registerDecorationType('example', { gutterIconPath: URI.parse('http://test/pa\'th') });
sheet = readStyleSheet(styleSheet);
assert(
sheet.indexOf('background: url(\'http://test/pa%27th\') center center no-repeat;') > 0
|| sheet.indexOf('background: url("http://test/pa%27th") center center no-repeat;') > 0
|| sheet.indexOf('background-image: url("http://test/pa%27th"); background-position: center center; background-repeat: no-repeat no-repeat;') > 0
);
assert(readStyleSheet(styleSheet).indexOf(`{background:url('http://test/pa%27th') center center no-repeat;}`) > 0);
s.removeDecorationType('example');
});
});
+1 -1
View File
@@ -92,6 +92,7 @@ export class MenuId {
static readonly MenubarSwitchGroupMenu = new MenuId('MenubarSwitchGroupMenu');
static readonly MenubarTerminalMenu = new MenuId('MenubarTerminalMenu');
static readonly MenubarViewMenu = new MenuId('MenubarViewMenu');
static readonly MenubarWebNavigationMenu = new MenuId('MenubarWebNavigationMenu');
static readonly OpenEditorsContext = new MenuId('OpenEditorsContext');
static readonly ProblemsPanelContext = new MenuId('ProblemsPanelContext');
static readonly SCMChangeContext = new MenuId('SCMChangeContext');
@@ -127,7 +128,6 @@ export class MenuId {
static readonly TimelineTitle = new MenuId('TimelineTitle');
static readonly TimelineTitleContext = new MenuId('TimelineTitleContext');
static readonly AccountsContext = new MenuId('AccountsContext');
static readonly WebMenuActions = new MenuId('MenubarWebMenu');
readonly id: number;
readonly _debugName: string;
@@ -265,8 +265,12 @@ export function addToValueTree(settingsTreeRoot: any, key: string, value: any, c
curr = obj;
}
if (typeof curr === 'object') {
curr[last] = value; // workaround https://github.com/Microsoft/vscode/issues/13606
if (typeof curr === 'object' && curr !== null) {
try {
curr[last] = value; // workaround https://github.com/Microsoft/vscode/issues/13606
} catch (e) {
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
}
} else {
conflictReporter(`Ignoring ${key} as ${segments.join('.')} is ${JSON.stringify(curr)}`);
}
@@ -29,6 +29,7 @@ export class ConfigurationService extends Disposable implements IConfigurationSe
fileService: IFileService
) {
super();
this._register(fileService.watch(settingsResource));
this.userConfiguration = this._register(new UserSettings(this.settingsResource, undefined, fileService));
this.configuration = new Configuration(new DefaultConfigurationModel(), new ConfigurationModel());
@@ -22,6 +22,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider';
import { Schemas } from 'vs/base/common/network';
import { IFileService } from 'vs/platform/files/common/files';
import { VSBuffer } from 'vs/base/common/buffer';
suite('ConfigurationService - Node', () => {
@@ -110,10 +111,10 @@ suite('ConfigurationService - Node', () => {
test('trigger configuration change event when file does not exist', async () => {
const res = await testFile('config', 'config.json');
const service = new ConfigurationService(URI.file(res.testFile), fileService);
const settingsFile = URI.file(res.testFile);
const service = new ConfigurationService(settingsFile, fileService);
await service.initialize();
return new Promise((c, e) => {
return new Promise(async (c, e) => {
const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
disposable.dispose();
assert.equal(service.getValue('foo'), 'bar');
@@ -121,7 +122,7 @@ suite('ConfigurationService - Node', () => {
await res.cleanUp();
c();
});
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
await fileService.writeFile(settingsFile, VSBuffer.fromString('{ "foo": "bar" }'));
});
});
@@ -56,7 +56,7 @@ export interface ParsedArgs {
'open-url'?: boolean;
'skip-getting-started'?: boolean;
'skip-release-notes'?: boolean;
'sticky-quickopen'?: boolean;
'sticky-quickinput'?: boolean;
'disable-restore-windows'?: boolean;
'disable-telemetry'?: boolean;
'export-default-configuration'?: string;
+1 -1
View File
@@ -90,7 +90,7 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
'logExtensionHostCommunication': { type: 'boolean' },
'skip-getting-started': { type: 'boolean' },
'skip-release-notes': { type: 'boolean' },
'sticky-quickopen': { type: 'boolean' },
'sticky-quickinput': { type: 'boolean' },
'disable-restore-windows': { type: 'boolean' },
'disable-telemetry': { type: 'boolean' },
'disable-updates': { type: 'boolean' },
+3
View File
@@ -395,6 +395,8 @@ export function toFileOperationResult(error: Error): FileOperationResult {
return FileOperationResult.FILE_NOT_FOUND;
case FileSystemProviderErrorCode.FileIsADirectory:
return FileOperationResult.FILE_IS_DIRECTORY;
case FileSystemProviderErrorCode.FileNotADirectory:
return FileOperationResult.FILE_NOT_DIRECTORY;
case FileSystemProviderErrorCode.NoPermissions:
return FileOperationResult.FILE_PERMISSION_DENIED;
case FileSystemProviderErrorCode.FileExists:
@@ -763,6 +765,7 @@ export const enum FileOperationResult {
FILE_TOO_LARGE,
FILE_INVALID_PATH,
FILE_EXCEEDS_MEMORY_LIMIT,
FILE_NOT_DIRECTORY,
FILE_OTHER_ERROR
}
@@ -668,6 +668,9 @@ export class DiskFileSystemProvider extends Disposable implements
case 'EISDIR':
code = FileSystemProviderErrorCode.FileIsADirectory;
break;
case 'ENOTDIR':
code = FileSystemProviderErrorCode.FileNotADirectory;
break;
case 'EEXIST':
code = FileSystemProviderErrorCode.FileExists;
break;
@@ -1409,6 +1409,24 @@ suite('Disk File Service', function () {
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_IS_DIRECTORY);
});
test('readFile - FILE_NOT_DIRECTORY', async () => {
if (isWindows) {
return; // error code does not seem to be supported on windows
}
const resource = URI.file(join(testDir, 'lorem.txt', 'file.txt'));
let error: FileOperationError | undefined = undefined;
try {
await service.readFile(resource);
} catch (err) {
error = err;
}
assert.ok(error);
assert.equal(error!.fileOperationResult, FileOperationResult.FILE_NOT_DIRECTORY);
});
test('readFile - FILE_NOT_FOUND', async () => {
const resource = URI.file(join(testDir, '404.html'));
+2 -2
View File
@@ -21,8 +21,8 @@ if (isWeb) {
// Running out of sources
if (Object.keys(product).length === 0) {
assign(product, {
version: '1.16.0-dev',
vscodeVersion: '1.43.0-dev',
version: '1.17.0-dev',
vscodeVersion: '1.44.0-dev',
nameLong: 'Azure Data Studio Web Dev',
nameShort: 'Azure Data Studio Web Dev',
urlProtocol: 'azuredatastudio-oss'
@@ -1,60 +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 { Event } from 'vs/base/common/event';
import { IQuickNavigateConfiguration, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
export interface IShowOptions {
quickNavigateConfiguration?: IQuickNavigateConfiguration;
inputSelection?: { start: number; end: number; };
autoFocus?: IAutoFocus;
}
export const IQuickOpenService = createDecorator<IQuickOpenService>('quickOpenService');
export interface IQuickOpenService {
_serviceBrand: undefined;
/**
* Asks the container to show the quick open control with the optional prefix set. If the optional parameter
* is set for quick navigation mode, the quick open control will quickly navigate when the quick navigate
* key is pressed and will run the selection after the ctrl key is released.
*
* The returned promise completes when quick open is closing.
*/
show(prefix?: string, options?: IShowOptions): Promise<void>;
/**
* Allows to navigate from the outside in an opened picker.
*/
navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration): void;
/**
* Accepts the selected value in quick open if visible.
*/
accept(): void;
/**
* Focus into the quick open if visible.
*/
focus(): void;
/**
* Closes any opened quick open.
*/
close(): void;
/**
* Allows to register on the event that quick open is showing
*/
onShow: Event<void>;
/**
* Allows to register on the event that quick open is hiding
*/
onHide: Event<void>;
}
@@ -115,15 +115,27 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
const picksToken = picksCts.token;
const providedPicks = this.getPicks(picker.value.substr(this.prefix.length).trim(), picksDisposables, picksToken);
function applyPicks(picks: Picks<T>): void {
function applyPicks(picks: Picks<T>, skipEmpty?: boolean): boolean {
let items: ReadonlyArray<Pick<T>>;
let activeItem: T | undefined = undefined;
if (isPicksWithActive(picks)) {
picker.items = picks.items;
if (picks.active) {
picker.activeItems = [picks.active];
}
items = picks.items;
activeItem = picks.active;
} else {
picker.items = picks;
items = picks;
}
if (items.length === 0 && skipEmpty) {
return false;
}
picker.items = items;
if (activeItem) {
picker.activeItems = [activeItem];
}
return true;
}
// No Picks
@@ -133,8 +145,8 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
// Fast and Slow Picks
else if (isFastAndSlowPicks(providedPicks)) {
let fastPicksHandlerDone = false;
let slowPicksHandlerDone = false;
let fastPicksApplied = false;
let slowPicksApplied = false;
await Promise.all([
@@ -143,17 +155,13 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
// If the slow picks are faster, we reduce the flicker by
// only setting the items once.
(async () => {
try {
await timeout(PickerQuickAccessProvider.FAST_PICKS_RACE_DELAY);
if (picksToken.isCancellationRequested) {
return;
}
await timeout(PickerQuickAccessProvider.FAST_PICKS_RACE_DELAY);
if (picksToken.isCancellationRequested) {
return;
}
if (!slowPicksHandlerDone) {
applyPicks(providedPicks.picks);
}
} finally {
fastPicksHandlerDone = true;
if (!slowPicksApplied) {
fastPicksApplied = applyPicks(providedPicks.picks, true /* skip over empty to reduce flicker */);
}
})(),
@@ -186,7 +194,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
additionalPicks = awaitedAdditionalPicks;
}
if (additionalPicks.length > 0 || !fastPicksHandlerDone) {
if (additionalPicks.length > 0 || !fastPicksApplied) {
applyPicks({
items: [...picks, ...additionalPicks],
active: activePick as T || additionalActivePick as T // {{SQL CARBON EDIT}} strict-null-checks
@@ -197,7 +205,7 @@ export abstract class PickerQuickAccessProvider<T extends IPickerQuickAccessItem
picker.busy = false;
}
slowPicksHandlerDone = true;
slowPicksApplied = true;
}
})()
]);
@@ -52,7 +52,7 @@ export class QuickInputService extends Themable implements IQuickInputService {
constructor(
@IInstantiationService private readonly instantiationService: IInstantiationService,
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@IContextKeyService protected readonly contextKeyService: IContextKeyService,
@IThemeService themeService: IThemeService,
@IAccessibilityService private readonly accessibilityService: IAccessibilityService,
@ILayoutService protected readonly layoutService: ILayoutService
@@ -166,10 +166,6 @@ export class QuickInputService extends Themable implements IQuickInputService {
return this.controller.cancel();
}
hide(focusLost?: boolean): void {
return this.controller.hide(focusLost);
}
protected updateStyles() {
this.controller.applyStyles(this.computeStyles());
}
@@ -94,7 +94,4 @@ export interface IQuickInputService {
* Cancels quick input and closes it.
*/
cancel(): Promise<void>;
// TODO@Ben remove once quick open is gone
hide(focusLost?: boolean): void;
}
@@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Disposable, IDisposable, dispose } from 'vs/base/common/lifecycle';
import { Event, Emitter } from 'vs/base/common/event';
import { Emitter } from 'vs/base/common/event';
import { IWorkspaceStorageChangeEvent, IStorageService, StorageScope, IWillSaveStateEvent, WillSaveStateReason, logStorage } from 'vs/platform/storage/common/storage';
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces';
@@ -189,8 +189,8 @@ export class BrowserStorageService extends Disposable implements IStorageService
export class FileStorageDatabase extends Disposable implements IStorageDatabase {
private readonly _onDidChangeItemsExternal: Emitter<IStorageItemsChangeEvent> = this._register(new Emitter<IStorageItemsChangeEvent>());
readonly onDidChangeItemsExternal: Event<IStorageItemsChangeEvent> = this._onDidChangeItemsExternal.event;
private readonly _onDidChangeItemsExternal = this._register(new Emitter<IStorageItemsChangeEvent>());
readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event;
private cache: Map<string, string> | undefined;
+4 -4
View File
@@ -125,8 +125,8 @@ export const enum StorageScope {
}
export interface IWorkspaceStorageChangeEvent {
key: string;
scope: StorageScope;
readonly key: string;
readonly scope: StorageScope;
}
export class InMemoryStorageService extends Disposable implements IStorageService {
@@ -139,8 +139,8 @@ export class InMemoryStorageService extends Disposable implements IStorageServic
protected readonly _onWillSaveState = this._register(new Emitter<IWillSaveStateEvent>());
readonly onWillSaveState = this._onWillSaveState.event;
private globalCache: Map<string, string> = new Map<string, string>();
private workspaceCache: Map<string, string> = new Map<string, string>();
private readonly globalCache = new Map<string, string>();
private readonly workspaceCache = new Map<string, string>();
private getCache(scope: StorageScope): Map<string, string> {
return scope === StorageScope.GLOBAL ? this.globalCache : this.workspaceCache;
+5 -7
View File
@@ -23,8 +23,8 @@ interface ISerializableUpdateRequest {
}
interface ISerializableItemsChangeEvent {
changed?: Item[];
deleted?: Key[];
readonly changed?: Item[];
readonly deleted?: Key[];
}
export class GlobalStorageDatabaseChannel extends Disposable implements IServerChannel {
@@ -34,15 +34,13 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
private readonly _onDidChangeItems = this._register(new Emitter<ISerializableItemsChangeEvent>());
readonly onDidChangeItems = this._onDidChangeItems.event;
private whenReady: Promise<void>;
private readonly whenReady = this.init();
constructor(
private logService: ILogService,
private storageMainService: IStorageMainService
) {
super();
this.whenReady = this.init();
}
private async init(): Promise<void> {
@@ -166,8 +164,8 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
_serviceBrand: undefined;
private readonly _onDidChangeItemsExternal: Emitter<IStorageItemsChangeEvent> = this._register(new Emitter<IStorageItemsChangeEvent>());
readonly onDidChangeItemsExternal: Event<IStorageItemsChangeEvent> = this._onDidChangeItemsExternal.event;
private readonly _onDidChangeItemsExternal = this._register(new Emitter<IStorageItemsChangeEvent>());
readonly onDidChangeItemsExternal = this._onDidChangeItemsExternal.event;
private onDidChangeItemsOnMainListener: IDisposable | undefined;
@@ -48,6 +48,11 @@ export class NativeStorageService extends Disposable implements IStorageService
) {
super();
this.registerListeners();
}
private registerListeners(): void {
// Global Storage change events
this._register(this.globalStorage.onDidChangeStorage(key => this.handleDidChangeStorage(key, StorageScope.GLOBAL)));
}
+3 -3
View File
@@ -154,7 +154,7 @@ export function attachFindReplaceInputBoxStyler(widget: IThemable, themeService:
} as IInputBoxStyleOverrides, widget);
}
export interface IQuickOpenStyleOverrides extends IListStyleOverrides, IInputBoxStyleOverrides, IProgressBarStyleOverrides {
export interface IQuickInputStyleOverrides extends IListStyleOverrides, IInputBoxStyleOverrides, IProgressBarStyleOverrides {
foreground?: ColorIdentifier;
background?: ColorIdentifier;
borderColor?: ColorIdentifier;
@@ -163,7 +163,7 @@ export interface IQuickOpenStyleOverrides extends IListStyleOverrides, IInputBox
pickerGroupBorder?: ColorIdentifier;
}
export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeService, style?: IQuickOpenStyleOverrides): IDisposable {
export function attachQuickInputStyler(widget: IThemable, themeService: IThemeService, style?: IQuickInputStyleOverrides): IDisposable {
return attachStyler(themeService, {
foreground: (style && style.foreground) || foreground,
background: (style && style.background) || editorBackground,
@@ -199,7 +199,7 @@ export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeSer
listFocusOutline: (style && style.listFocusOutline) || activeContrastBorder,
listSelectionOutline: (style && style.listSelectionOutline) || activeContrastBorder,
listHoverOutline: (style && style.listHoverOutline) || activeContrastBorder
} as IQuickOpenStyleOverrides, widget);
} as IQuickInputStyleOverrides, widget);
}
export interface IListStyleOverrides extends IStyleOverrides {
+1 -1
View File
@@ -106,7 +106,7 @@ export interface IColorTheme {
/**
* Returns the token style for a given classification. The result uses the <code>MetadataConsts</code> format
*/
getTokenStyleMetadata(type: string, modifiers: string[]): ITokenStyle | undefined;
getTokenStyleMetadata(type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined;
/**
* List of all colors used with tokens. <code>getTokenStyleMetadata</code> references the colors by index into this list.
@@ -13,15 +13,21 @@ import { Event, Emitter } from 'vs/base/common/event';
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
export const TOKEN_TYPE_WILDCARD = '*';
export const TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR = ':';
export const CLASSIFIER_MODIFIER_SEPARATOR = '.';
// qualified string [type|*](.modifier)*
// qualified string [type|*](.modifier)*(/language)!
export type TokenClassificationString = string;
export const typeAndModifierIdPattern = '^\\w+[-_\\w+]*$';
export const idPattern = '\\w+[-_\\w+]*';
export const typeAndModifierIdPattern = `^${idPattern}$`;
export const selectorPattern = `^(${idPattern}|\\*)(\\${CLASSIFIER_MODIFIER_SEPARATOR}${idPattern})*(\\${TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR}${idPattern})?$`;
export const fontStylePattern = '^(\\s*(-?italic|-?bold|-?underline))*\\s*$';
export interface TokenSelector {
match(type: string, modifiers: string[]): number;
match(type: string, modifiers: string[], language: string): number;
readonly selectorString: string;
}
@@ -52,7 +58,36 @@ export class TokenStyle implements Readonly<TokenStyleData> {
}
export namespace TokenStyle {
export function fromData(data: { foreground?: Color, bold?: boolean, underline?: boolean, italic?: boolean }) {
export function toJSONObject(style: TokenStyle): any {
return {
_foreground: style.foreground === undefined ? null : Color.Format.CSS.formatHexA(style.foreground, true),
_bold: style.bold === undefined ? null : style.bold,
_underline: style.underline === undefined ? null : style.underline,
_italic: style.italic === undefined ? null : style.italic,
};
}
export function fromJSONObject(obj: any): TokenStyle | undefined {
if (obj) {
const boolOrUndef = (b: any) => (typeof b === 'boolean') ? b : undefined;
const colorOrUndef = (s: any) => (typeof s === 'string') ? Color.fromHex(s) : undefined;
return new TokenStyle(colorOrUndef(obj._foreground), boolOrUndef(obj._bold), boolOrUndef(obj._underline), boolOrUndef(obj._italic));
}
return undefined;
}
export function equals(s1: any, s2: any): boolean {
if (s1 === s2) {
return true;
}
return s1 !== undefined && s2 !== undefined
&& (s1.foreground instanceof Color ? s1.foreground.equals(s2.foreground) : s2.foreground === undefined)
&& s1.bold === s2.bold
&& s1.underline === s2.underline
&& s1.italic === s2.italic;
}
export function is(s: any): s is TokenStyle {
return s instanceof TokenStyle;
}
export function fromData(data: { foreground?: Color, bold?: boolean, underline?: boolean, italic?: boolean }): TokenStyle {
return new TokenStyle(data.foreground, data.bold, data.underline, data.italic);
}
export function fromSettings(foreground: string | undefined, fontStyle: string | undefined): TokenStyle {
@@ -105,6 +140,38 @@ export interface TokenStylingRule {
selector: TokenSelector;
}
export namespace TokenStylingRule {
export function fromJSONObject(registry: ITokenClassificationRegistry, o: any): TokenStylingRule | undefined {
if (o && typeof o._selector === 'string' && o._style) {
const style = TokenStyle.fromJSONObject(o._style);
if (style) {
try {
return { selector: registry.parseTokenSelector(o._selector), style };
} catch (_ignore) {
}
}
}
return undefined;
}
export function toJSONObject(rule: TokenStylingRule): any {
return {
_selector: rule.selector.selectorString,
_style: TokenStyle.toJSONObject(rule.style)
};
}
export function equals(r1: TokenStylingRule | undefined, r2: TokenStylingRule | undefined) {
if (r1 === r2) {
return true;
}
return r1 !== undefined && r2 !== undefined
&& r1.selector && r2.selector && r1.selector.selectorString === r2.selector.selectorString
&& TokenStyle.equals(r1.style, r2.style);
}
export function is(r: any): r is TokenStylingRule {
return r && r.selector && typeof r.selector.selectorString === 'string' && TokenStyle.is(r.style);
}
}
/**
* A TokenStyle Value is either a token style literal, or a TokenClassificationString
*/
@@ -269,9 +336,9 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
}
public parseTokenSelector(selectorString: string): TokenSelector {
const [selectorType, ...selectorModifiers] = selectorString.split('.');
const selector = parseClassifierString(selectorString);
if (!selectorType) {
if (!selector.type) {
return {
match: () => -1,
selectorString
@@ -279,23 +346,29 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
}
return {
match: (type: string, modifiers: string[]) => {
match: (type: string, modifiers: string[], language: string) => {
let score = 0;
if (selectorType !== TOKEN_TYPE_WILDCARD) {
if (selector.language !== undefined) {
if (selector.language !== language) {
return -1;
}
score += 100;
}
if (selector.type !== TOKEN_TYPE_WILDCARD) {
const hierarchy = this.getTypeHierarchy(type);
const level = hierarchy.indexOf(selectorType);
const level = hierarchy.indexOf(selector.type);
if (level === -1) {
return -1;
}
score = 100 - level;
score += (100 - level);
}
// all selector modifiers must be present
for (const selectorModifier of selectorModifiers) {
for (const selectorModifier of selector.modifiers) {
if (modifiers.indexOf(selectorModifier) === -1) {
return -1;
}
}
return score + selectorModifiers.length * 100;
return score + selector.modifiers.length * 100;
},
selectorString
};
@@ -366,15 +439,41 @@ class TokenClassificationRegistry implements ITokenClassificationRegistry {
}
const CHAR_LANGUAGE = TOKEN_CLASSIFIER_LANGUAGE_SEPARATOR.charCodeAt(0);
const CHAR_MODIFIER = CLASSIFIER_MODIFIER_SEPARATOR.charCodeAt(0);
const tokenClassificationRegistry = new TokenClassificationRegistry();
export function parseClassifierString(s: string): { type: string, modifiers: string[], language: string | undefined; } {
let k = s.length;
let language: string | undefined = undefined;
const modifiers = [];
for (let i = k - 1; i >= 0; i--) {
const ch = s.charCodeAt(i);
if (ch === CHAR_LANGUAGE || ch === CHAR_MODIFIER) {
const segment = s.substring(i + 1, k);
k = i;
if (ch === CHAR_LANGUAGE) {
language = segment;
} else {
modifiers.push(segment);
}
}
}
const type = s.substring(0, k);
return { type, modifiers, language };
}
let tokenClassificationRegistry = createDefaultTokenClassificationRegistry();
platform.Registry.add(Extensions.TokenClassificationContribution, tokenClassificationRegistry);
registerDefaultClassifications();
function registerDefaultClassifications(): void {
function createDefaultTokenClassificationRegistry(): TokenClassificationRegistry {
const registry = new TokenClassificationRegistry();
function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], superType?: string, deprecationMessage?: string): string {
tokenClassificationRegistry.registerTokenType(id, description, superType, deprecationMessage);
registry.registerTokenType(id, description, superType, deprecationMessage);
if (scopesToProbe) {
registerTokenStyleDefault(id, scopesToProbe);
}
@@ -383,8 +482,8 @@ function registerDefaultClassifications(): void {
function registerTokenStyleDefault(selectorString: string, scopesToProbe: ProbeScope[]) {
try {
const selector = tokenClassificationRegistry.parseTokenSelector(selectorString);
tokenClassificationRegistry.registerTokenStyleDefault(selector, { scopesToProbe });
const selector = registry.parseTokenSelector(selectorString);
registry.registerTokenStyleDefault(selector, { scopesToProbe });
} catch (e) {
console.log(e);
}
@@ -422,18 +521,28 @@ function registerDefaultClassifications(): void {
// default token modifiers
tokenClassificationRegistry.registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
tokenClassificationRegistry.registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
tokenClassificationRegistry.registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
tokenClassificationRegistry.registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
tokenClassificationRegistry.registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
tokenClassificationRegistry.registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined);
tokenClassificationRegistry.registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined);
tokenClassificationRegistry.registerTokenModifier('readonly', nls.localize('readonly', "Style to use for symbols that are readonly."), undefined);
registry.registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
registry.registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
registry.registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
registry.registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
registry.registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
registry.registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined);
registry.registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined);
registry.registerTokenModifier('readonly', nls.localize('readonly', "Style to use for symbols that are readonly."), undefined);
registerTokenStyleDefault('variable.readonly', [['variable.other.constant']]);
registerTokenStyleDefault('property.readonly', [['variable.other.constant.property']]);
registerTokenStyleDefault('type.defaultLibrary', [['support.type']]);
registerTokenStyleDefault('class.defaultLibrary', [['support.class']]);
registerTokenStyleDefault('interface.defaultLibrary', [['support.class']]);
registerTokenStyleDefault('variable.defaultLibrary', [['support.variable'], ['support.other.variable']]);
registerTokenStyleDefault('variable.defaultLibrary.readonly', [['support.constant']]);
registerTokenStyleDefault('property.defaultLibrary', [['support.variable.property']]);
registerTokenStyleDefault('property.defaultLibrary.readonly', [['support.constant.property']]);
registerTokenStyleDefault('function.defaultLibrary', [['support.function']]);
registerTokenStyleDefault('member.defaultLibrary', [['support.function']]);
return registry;
}
export function getTokenClassificationRegistry(): ITokenClassificationRegistry {
@@ -24,7 +24,7 @@ export class TestColorTheme implements IColorTheme {
throw new Error('Method not implemented.');
}
getTokenStyleMetadata(type: string, modifiers: string[]): ITokenStyle | undefined {
getTokenStyleMetadata(type: string, modifiers: string[], modelLanguage: string): ITokenStyle | undefined {
return undefined;
}
+166 -18
View File
@@ -1093,7 +1093,7 @@ declare module 'vscode' {
readonly dimensions: TerminalDimensions;
}
namespace window {
export namespace window {
/**
* An event which fires when the [dimensions](#Terminal.dimensions) of the terminal change.
*/
@@ -1114,19 +1114,125 @@ declare module 'vscode' {
//#region Terminal link handlers https://github.com/microsoft/vscode/issues/91606
export namespace window {
/**
* Register a [TerminalLinkHandler](#TerminalLinkHandler) that can be used to intercept and
* handle links that are activated within terminals.
*/
export function registerTerminalLinkHandler(handler: TerminalLinkHandler): Disposable;
}
export interface TerminalLinkHandler {
/**
* @return true when the link was handled (and should not be considered by
* other providers including the default), false when the link was not handled.
* Handles a link that is activated within the terminal.
*
* @return Whether the link was handled, the link was handled this link will not be
* considered by any other extension or by the default built-in link handler.
*/
handleLink(terminal: Terminal, link: string): ProviderResult<boolean>;
}
//#endregion
//#region Contribute to terminal environment https://github.com/microsoft/vscode/issues/46696
export enum EnvironmentVariableMutatorType {
/**
* Replace the variable's existing value.
*/
Replace = 1,
/**
* Append to the end of the variable's existing value.
*/
Append = 2,
/**
* Prepend to the start of the variable's existing value.
*/
Prepend = 3
}
export interface EnvironmentVariableMutator {
/**
* The type of mutation that will occur to the variable.
*/
readonly type: EnvironmentVariableMutatorType;
/**
* The value to use for the variable.
*/
readonly value: string;
}
/**
* A collection of mutations that an extension can apply to a process environment.
*/
export interface EnvironmentVariableCollection {
/**
* Replace an environment variable with a value.
*
* Note that an extension can only make a single change to any one variable, so this will
* overwrite any previous calls to replace, append or prepend.
*/
replace(variable: string, value: string): void;
/**
* Append a value to an environment variable.
*
* Note that an extension can only make a single change to any one variable, so this will
* overwrite any previous calls to replace, append or prepend.
*/
append(variable: string, value: string): void;
/**
* Prepend a value to an environment variable.
*
* Note that an extension can only make a single change to any one variable, so this will
* overwrite any previous calls to replace, append or prepend.
*/
prepend(variable: string, value: string): void;
/**
* Gets the mutator that this collection applies to a variable, if any.
*/
get(variable: string): EnvironmentVariableMutator | undefined;
/**
* Iterate over each mutator in this collection.
*/
forEach(callback: (variable: string, mutator: EnvironmentVariableMutator, collection: EnvironmentVariableCollection) => any, thisArg?: any): void;
/**
* Deletes this collection's mutator for a variable.
*/
delete(variable: string): void;
/**
* Clears all mutators from this collection.
*/
clear(): void;
/**
* Disposes the collection, if the collection was persisted it will no longer be retained
* across reloads.
*/
dispose(): void;
}
export namespace window {
/**
* Creates or returns the extension's environment variable collection for this workspace,
* enabling changes to be applied to terminal environment variables.
*
* @param persistent Whether the collection should be cached for the workspace and applied
* to the terminal across window reloads. When true the collection will be active
* immediately such when the window reloads. Additionally, this API will return the cached
* version if it exists. The collection will be invalidated when the extension is
* uninstalled or when the collection is disposed. Defaults to false.
*/
export function getEnvironmentVariableCollection(persistent?: boolean): EnvironmentVariableCollection;
}
//#endregion
//#region Joh -> exclusive document filters
export interface DocumentFilter {
@@ -1449,9 +1555,20 @@ declare module 'vscode' {
readonly uri: Uri;
/**
* Event fired when there are no more references to the `CustomDocument`.
* Is this document representing an untitled file which has never been saved yet.
*/
readonly onDidDispose: Event<void>;
readonly isUntitled: boolean;
/**
* The version number of this document (it will strictly increase after each
* change, including undo/redo).
*/
readonly version: number;
/**
* `true` if there are unpersisted changes.
*/
readonly isDirty: boolean;
/**
* List of edits from document open to the document's current state.
@@ -1465,6 +1582,17 @@ declare module 'vscode' {
* or in front of the last entry in `appliedEdits` if the user saves and then hits undo.
*/
readonly savedEdits: ReadonlyArray<EditType>;
/**
* `true` if the document has been closed. A closed document isn't synchronized anymore
* and won't be re-used when the same resource is opened again.
*/
readonly isClosed: boolean;
/**
* Event fired when there are no more references to the `CustomDocument`.
*/
readonly onDidDispose: Event<void>;
}
/**
@@ -1674,25 +1802,27 @@ declare module 'vscode' {
/**
* Controls if the content of a cell is editable or not.
*/
editable: boolean;
editable?: boolean;
/**
* Controls if the cell is executable.
* This metadata is ignored for markdown cell.
*/
runnable: boolean;
runnable?: boolean;
/**
* Execution order information of the cell
*/
executionOrder?: number;
}
export interface NotebookCell {
readonly uri: Uri;
handle: number;
readonly cellKind: CellKind;
readonly source: string;
language: string;
cellKind: CellKind;
outputs: CellOutput[];
getContent(): string;
metadata?: NotebookCellMetadata;
metadata: NotebookCellMetadata;
}
export interface NotebookDocumentMetadata {
@@ -1713,18 +1843,24 @@ declare module 'vscode' {
* Default to true.
*/
cellRunnable: boolean;
}
export interface NotebookDocument {
readonly uri: Uri;
readonly fileName: string;
readonly isDirty: boolean;
readonly cells: NotebookCell[];
languages: string[];
cells: NotebookCell[];
displayOrder?: GlobPattern[];
metadata?: NotebookDocumentMetadata;
}
export interface NotebookEditorCellEdit {
insert(index: number, content: string, language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): void;
delete(index: number): void;
}
export interface NotebookEditor {
readonly document: NotebookDocument;
viewColumn?: ViewColumn;
@@ -1741,10 +1877,7 @@ declare module 'vscode' {
*/
postMessage(message: any): Thenable<boolean>;
/**
* Create a notebook cell. The cell is not inserted into current document when created. Extensions should insert the cell into the document by [TextDocument.cells](#TextDocument.cells)
*/
createCell(content: string, language: string, type: CellKind, outputs: CellOutput[], metadata: NotebookCellMetadata | undefined): NotebookCell;
edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable<boolean>;
}
export interface NotebookProvider {
@@ -1764,11 +1897,24 @@ declare module 'vscode' {
* @returns HTML fragment. We can probably return `CellOutput` instead of string ?
*
*/
render(document: NotebookDocument, cell: NotebookCell, output: CellOutput, mimeType: string): string;
render(document: NotebookDocument, output: CellOutput, mimeType: string): string;
preloads?: Uri[];
}
namespace window {
export interface NotebookDocumentChangeEvent {
/**
* The affected document.
*/
readonly document: NotebookDocument;
/**
* An array of content changes.
*/
// readonly contentChanges: ReadonlyArray<TextDocumentContentChangeEvent>;
}
export namespace notebook {
export function registerNotebookProvider(
notebookType: string,
provider: NotebookProvider
@@ -1777,6 +1923,8 @@ declare module 'vscode' {
export function registerNotebookOutputRenderer(type: string, outputSelector: NotebookOutputSelector, renderer: NotebookOutputRenderer): Disposable;
export let activeNotebookDocument: NotebookDocument | undefined;
// export const onDidChangeNotebookDocument: Event<NotebookDocumentChangeEvent>;
}
//#endregion
@@ -32,6 +32,23 @@ const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [
}
];
interface AllowedExtension {
id: string;
name: string;
}
function readAllowedExtensions(storageService: IStorageService, providerId: string, accountName: string): AllowedExtension[] {
let trustedExtensions: AllowedExtension[] = [];
try {
const trustedExtensionSrc = storageService.get(`${providerId}-${accountName}`, StorageScope.GLOBAL);
if (trustedExtensionSrc) {
trustedExtensions = JSON.parse(trustedExtensionSrc);
}
} catch (err) { }
return trustedExtensions;
}
export class MainThreadAuthenticationProvider extends Disposable {
private _sessionMenuItems = new Map<string, IDisposable[]>();
private _accounts = new Map<string, string[]>(); // Map account name to session ids
@@ -48,30 +65,25 @@ export class MainThreadAuthenticationProvider extends Disposable {
this.registerCommandsAndContextMenuItems();
}
private setPermissionsForAccount(quickInputService: IQuickInputService, doLogin?: boolean) {
const quickPick = quickInputService.createQuickPick();
private manageTrustedExtensions(quickInputService: IQuickInputService, storageService: IStorageService, accountName: string) {
const quickPick = quickInputService.createQuickPick<{ label: string, extension: AllowedExtension }>();
quickPick.canSelectMany = true;
const items = this.dependents.map(dependent => {
const allowedExtensions = readAllowedExtensions(storageService, this.id, accountName);
const items = allowedExtensions.map(extension => {
return {
label: dependent.label,
description: dependent.scopeDescriptions,
picked: true,
scopes: dependent.scopes
label: extension.name,
extension
};
});
quickPick.items = items;
// TODO read from storage and filter is not doLogin
quickPick.selectedItems = items;
quickPick.title = nls.localize('signInTo', "Sign in to {0}", this.displayName);
quickPick.placeholder = nls.localize('accountPermissions', "Choose what features and extensions to authorize to use this account");
quickPick.title = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions");
quickPick.placeholder = nls.localize('manageExensions', "Choose which extensions can access this account");
quickPick.onDidAccept(() => {
const scopes = quickPick.selectedItems.reduce((previous, current) => previous.concat((current as any).scopes), []);
if (scopes.length && doLogin) {
this.login(scopes);
}
const updatedAllowedList = quickPick.selectedItems.map(item => item.extension);
storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL);
quickPick.dispose();
});
@@ -87,7 +99,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
this._register(CommandsRegistry.registerCommand({
id: `signIn${this.id}`,
handler: (accessor, args) => {
this.setPermissionsForAccount(accessor.get(IQuickInputService), true);
this.login(this.dependents.reduce((previous: string[], current) => previous.concat(current.scopes), []));
},
}));
@@ -130,19 +142,26 @@ export class MainThreadAuthenticationProvider extends Disposable {
id: `configureSessions${session.id}`,
handler: (accessor, args) => {
const quickInputService = accessor.get(IQuickInputService);
const storageService = accessor.get(IStorageService);
const quickPick = quickInputService.createQuickPick();
const items = [{ label: 'Sign Out' }];
const manage = nls.localize('manageTrustedExtensions', "Manage Trusted Extensions");
const signOut = nls.localize('signOut', "Sign Out");
const items = ([{ label: manage }, { label: signOut }]);
quickPick.items = items;
quickPick.onDidAccept(e => {
const selected = quickPick.selectedItems[0];
if (selected.label === 'Sign Out') {
if (selected.label === signOut) {
const sessionsForAccount = this._accounts.get(session.accountName);
sessionsForAccount?.forEach(sessionId => this.logout(sessionId));
}
if (selected.label === manage) {
this.manageTrustedExtensions(quickInputService, storageService, session.accountName);
}
quickPick.dispose();
});
@@ -185,6 +204,7 @@ export class MainThreadAuthenticationProvider extends Disposable {
disposeables.forEach(disposeable => disposeable.dispose());
this._sessionMenuItems.delete(accountName);
}
this._accounts.delete(accountName);
}
}
});
@@ -242,55 +262,45 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu
this.authenticationService.sessionsUpdate(id, event);
}
async $getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
if (alwaysAllow) {
return alwaysAllow === 'true';
async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
let allowList = readAllowedExtensions(this.storageService, providerId, accountName);
if (allowList.some(extension => extension.id === extensionId)) {
return true;
}
const { choice, checkboxChecked } = await this.dialogService.show(
const { choice } = await this.dialogService.show(
Severity.Info,
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information from {1}.", extensionName, providerName),
nls.localize('confirmAuthenticationAccess', "The extension '{0}' is trying to access authentication information for the {1} account '{2}'.", extensionName, providerName, accountName),
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
{
cancelId: 0,
checkbox: {
label: nls.localize('neverAgain', "Don't Show Again")
}
cancelId: 0
}
);
const allow = choice === 1;
if (checkboxChecked) {
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
if (allow) {
allowList = allowList.concat({ id: extensionId, name: extensionName });
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
}
return allow;
}
async $loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean> {
const alwaysAllow = this.storageService.get(`${extensionId}-${providerId}`, StorageScope.GLOBAL);
if (alwaysAllow) {
return alwaysAllow === 'true';
}
const { choice, checkboxChecked } = await this.dialogService.show(
async $loginPrompt(providerName: string, extensionName: string): Promise<boolean> {
const { choice } = await this.dialogService.show(
Severity.Info,
nls.localize('confirmLogin', "The extension '{0}' wants to sign in using {1}.", extensionName, providerName),
[nls.localize('cancel', "Cancel"), nls.localize('continue', "Continue")],
[nls.localize('cancel', "Cancel"), nls.localize('allow', "Allow")],
{
cancelId: 0,
checkbox: {
label: nls.localize('neverAgain', "Don't Show Again")
}
cancelId: 0
}
);
const allow = choice === 1;
if (checkboxChecked) {
this.storageService.store(`${extensionId}-${providerId}`, allow ? 'true' : 'false', StorageScope.GLOBAL);
}
return choice === 1;
}
return allow;
async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void> {
const allowList = readAllowedExtensions(this.storageService, providerId, accountName).concat({ id: extensionId, name: extensionName });
this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL);
}
}
@@ -8,9 +8,8 @@ import { MainContext, MainThreadNotebookShape, NotebookExtensionDescription, IEx
import { Disposable } from 'vs/base/common/lifecycle';
import { URI, UriComponents } from 'vs/base/common/uri';
import { INotebookService, IMainNotebookController } from 'vs/workbench/contrib/notebook/browser/notebookService';
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellsSplice, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { INotebookTextModel, INotebookMimeTypeSelector, NOTEBOOK_DISPLAY_ORDER, NotebookCellOutputsSplice, CellKind, NotebookDocumentMetadata, NotebookCellMetadata, ICellEditOperation } from 'vs/workbench/contrib/notebook/common/notebookCommon';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel';
import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser';
@@ -30,16 +29,17 @@ export class MainThreadNotebookDocument extends Disposable {
) {
super();
this._textModel = new NotebookTextModel(handle, viewType, uri);
this._register(this._textModel.onDidModelChange(e => {
this._proxy.$acceptModelChanged(this.uri, e);
}));
}
async deleteCell(uri: URI, index: number): Promise<boolean> {
let deleteExtHostCell = await this._proxy.$deleteCell(this.viewType, uri, index);
if (deleteExtHostCell) {
this._textModel.removeCell(index);
return true;
}
applyEdit(modelVersionId: number, edits: ICellEditOperation[]): boolean {
return this._textModel.applyEdit(modelVersionId, edits);
}
return false;
updateRenderers(renderers: number[]) {
this._textModel.updateRenderers(renderers);
}
dispose() {
@@ -65,6 +65,16 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
this.registerListeners();
}
async $tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
let controller = this._notebookProviders.get(viewType);
if (controller) {
return controller.tryApplyEdits(resource, modelVersionId, edits, renderers);
}
return false;
}
registerListeners() {
this._register(this._notebookService.onDidChangeActiveEditor(e => {
this._proxy.$updateActiveEditor(e.viewType, e.uri);
@@ -148,11 +158,6 @@ export class MainThreadNotebooks extends Disposable implements MainThreadNoteboo
return handle;
}
async $spliceNotebookCells(viewType: string, resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): Promise<void> {
let controller = this._notebookProviders.get(viewType);
controller?.spliceNotebookCells(resource, splices, renderers);
}
async $spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void> {
let controller = this._notebookProviders.get(viewType);
controller?.spliceNotebookCellOutputs(resource, cellHandle, splices, renderers);
@@ -201,11 +206,8 @@ export class MainThreadNotebookController implements IMainNotebookController {
mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
if (mainthreadNotebook && mainthreadNotebook.textModel.cells.length === 0) {
// it's empty, we should create an empty template one
const templateCell = await this._proxy.$createEmptyCell(this._viewType, uri, 0, mainthreadNotebook.textModel.languages.length ? mainthreadNotebook.textModel.languages[0] : '', CellKind.Code);
if (templateCell) {
let mainCell = new NotebookCellTextModel(URI.revive(templateCell.uri), templateCell.handle, templateCell.source, templateCell.language, templateCell.cellKind, templateCell.outputs, templateCell.metadata);
mainthreadNotebook.textModel.insertTemplateCell(mainCell);
}
const mainCell = mainthreadNotebook.textModel.createCellTextModel([''], mainthreadNotebook.textModel.languages.length ? mainthreadNotebook.textModel.languages[0] : '', CellKind.Code, [], undefined);
mainthreadNotebook.textModel.insertTemplateCell(mainCell);
}
return mainthreadNotebook?.textModel;
}
@@ -213,10 +215,15 @@ export class MainThreadNotebookController implements IMainNotebookController {
return undefined;
}
spliceNotebookCells(resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): void {
async tryApplyEdits(resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean> {
let mainthreadNotebook = this._mapping.get(URI.from(resource).toString());
mainthreadNotebook?.textModel.updateRenderers(renderers);
mainthreadNotebook?.textModel.$spliceNotebookCells(splices);
if (mainthreadNotebook) {
mainthreadNotebook.updateRenderers(renderers);
return mainthreadNotebook.applyEdit(modelVersionId, edits);
}
return false;
}
spliceNotebookCellOutputs(resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): void {
@@ -259,26 +266,6 @@ export class MainThreadNotebookController implements IMainNotebookController {
document?.textModel.updateRenderers(renderers);
}
async createRawCell(uri: URI, index: number, language: string, type: CellKind): Promise<NotebookCellTextModel | undefined> {
let cell = await this._proxy.$createEmptyCell(this._viewType, uri, index, language, type);
if (cell) {
let mainCell = new NotebookCellTextModel(URI.revive(cell.uri), cell.handle, cell.source, cell.language, cell.cellKind, cell.outputs, cell.metadata);
return mainCell;
}
return undefined;
}
async deleteCell(uri: URI, index: number): Promise<boolean> {
let mainthreadNotebook = this._mapping.get(URI.from(uri).toString());
if (mainthreadNotebook) {
return mainthreadNotebook.deleteCell(uri, index);
}
return false;
}
async executeNotebookCell(uri: URI, handle: number): Promise<void> {
return this._proxy.$executeNotebook(this._viewType, uri, handle);
}
@@ -9,7 +9,7 @@ import { localize } from 'vs/nls';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { IProgressStep, IProgress } from 'vs/platform/progress/common/progress';
import { extHostCustomer } from 'vs/workbench/api/common/extHostCustomers';
import { ITextFileSaveParticipant, IResolvedTextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles';
import { ITextFileSaveParticipant, ITextFileService, ITextFileEditorModel } from 'vs/workbench/services/textfile/common/textfiles';
import { SaveReason } from 'vs/workbench/common/editor';
import { ExtHostContext, ExtHostDocumentSaveParticipantShape, IExtHostContext } from '../common/extHost.protocol';
import { canceled } from 'vs/base/common/errors';
@@ -23,9 +23,9 @@ class ExtHostSaveParticipant implements ITextFileSaveParticipant {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostDocumentSaveParticipant);
}
async participate(editorModel: IResolvedTextFileEditorModel, env: { reason: SaveReason; }, _progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
async participate(editorModel: ITextFileEditorModel, env: { reason: SaveReason; }, _progress: IProgress<IProgressStep>, token: CancellationToken): Promise<void> {
if (!shouldSynchronizeModel(editorModel.textEditorModel)) {
if (!editorModel.textEditorModel || !shouldSynchronizeModel(editorModel.textEditorModel)) {
// the model never made it to the extension
// host meaning we cannot participate in its save
return undefined;
@@ -13,6 +13,8 @@ import { ITerminalInstanceService, ITerminalService, ITerminalInstance, ITermina
import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
import { IEnvironmentVariableService, ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
import { deserializeEnvironmentVariableCollection, serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
@extHostNamedCustomer(MainContext.MainThreadTerminalService)
export class MainThreadTerminalService implements MainThreadTerminalServiceShape {
@@ -29,8 +31,9 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
extHostContext: IExtHostContext,
@ITerminalService private readonly _terminalService: ITerminalService,
@ITerminalInstanceService readonly terminalInstanceService: ITerminalInstanceService,
@IRemoteAgentService readonly _remoteAgentService: IRemoteAgentService,
@IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService,
@IInstantiationService private readonly _instantiationService: IInstantiationService,
@IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService,
) {
this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostTerminalService);
this._remoteAuthority = extHostContext.remoteAuthority;
@@ -71,6 +74,13 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
if (activeInstance) {
this._proxy.$acceptActiveTerminalChanged(activeInstance.id);
}
if (this._environmentVariableService.collections.size > 0) {
const collectionAsArray = [...this._environmentVariableService.collections.entries()];
const serializedCollections: [string, ISerializableEnvironmentVariableCollection][] = collectionAsArray.map(e => {
return [e[0], serializeEnvironmentVariableCollection(e[1].map)];
});
this._proxy.$initEnvironmentVariableCollections(serializedCollections);
}
this._terminalService.extHostReady(extHostContext.remoteAuthority);
}
@@ -346,6 +356,18 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape
}
return terminal;
}
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void {
if (collection) {
const translatedCollection = {
persistent,
map: deserializeEnvironmentVariableCollection(collection)
};
this._environmentVariableService.set(extensionIdentifier, translatedCollection);
} else {
this._environmentVariableService.delete(extensionIdentifier);
}
}
}
/**

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