Archived
Merge from vscode 1ec43773e37997841c5af42b33ddb180e9735bf2
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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/*"
|
||||
],
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 || '');
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
+3
-4
@@ -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');
|
||||
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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());
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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%;
|
||||
|
||||
-1
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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' },
|
||||
|
||||
@@ -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'));
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)));
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Vendored
+166
-18
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -11,7 +11,7 @@ import { Disposable, DisposableStore, dispose, IDisposable, IReference } from 'v
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { basename } from 'vs/base/common/path';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { isEqual } from 'vs/base/common/resources';
|
||||
import { isEqual, isEqualOrParent } from 'vs/base/common/resources';
|
||||
import { escape } from 'vs/base/common/strings';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import * as modes from 'vs/editor/common/modes';
|
||||
@@ -35,9 +35,11 @@ import { CustomTextEditorModel } from 'vs/workbench/contrib/customEditor/common/
|
||||
import { WebviewExtensionDescription, WebviewIcons } from 'vs/workbench/contrib/webview/browser/webview';
|
||||
import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput';
|
||||
import { ICreateWebViewShowOptions, IWebviewWorkbenchService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewWorkbenchService';
|
||||
import { IBackupFileService } from 'vs/workbench/services/backup/common/backup';
|
||||
import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { IWorkingCopyFileService } from 'vs/workbench/services/workingCopy/common/workingCopyFileService';
|
||||
import { IWorkingCopy, IWorkingCopyBackup, IWorkingCopyService, WorkingCopyCapabilities } from 'vs/workbench/services/workingCopy/common/workingCopyService';
|
||||
import { extHostNamedCustomer } from '../common/extHostCustomers';
|
||||
|
||||
@@ -121,6 +123,8 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
constructor(
|
||||
context: extHostProtocol.IExtHostContext,
|
||||
@IExtensionService extensionService: IExtensionService,
|
||||
@IWorkingCopyService workingCopyService: IWorkingCopyService,
|
||||
@IWorkingCopyFileService workingCopyFileService: IWorkingCopyFileService,
|
||||
@ICustomEditorService private readonly _customEditorService: ICustomEditorService,
|
||||
@IEditorGroupsService private readonly _editorGroupService: IEditorGroupsService,
|
||||
@IEditorService private readonly _editorService: IEditorService,
|
||||
@@ -164,6 +168,20 @@ export class MainThreadWebviews extends Disposable implements extHostProtocol.Ma
|
||||
},
|
||||
resolveWebview: () => { throw new Error('not implemented'); }
|
||||
}));
|
||||
|
||||
workingCopyFileService.registerWorkingCopyProvider((editorResource) => {
|
||||
const matchedWorkingCopies: IWorkingCopy[] = [];
|
||||
|
||||
for (const workingCopy of workingCopyService.workingCopies) {
|
||||
if (workingCopy instanceof MainThreadCustomEditorModel) {
|
||||
if (isEqualOrParent(editorResource, workingCopy.editorResource)) {
|
||||
matchedWorkingCopies.push(workingCopy);
|
||||
}
|
||||
}
|
||||
}
|
||||
return matchedWorkingCopies;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public $createWebviewPanel(
|
||||
@@ -564,6 +582,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
private _currentEditIndex: number = -1;
|
||||
private _savePoint: number = -1;
|
||||
private readonly _edits: Array<number> = [];
|
||||
private _fromBackup: boolean = false;
|
||||
|
||||
public static async create(
|
||||
instantiationService: IInstantiationService,
|
||||
@@ -574,7 +593,9 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
cancellation: CancellationToken,
|
||||
) {
|
||||
const { editable } = await proxy.$createWebviewCustomEditorDocument(resource, viewType, cancellation);
|
||||
return instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable, getEditors);
|
||||
const model = instantiationService.createInstance(MainThreadCustomEditorModel, proxy, viewType, resource, editable, getEditors);
|
||||
await model.init();
|
||||
return model;
|
||||
}
|
||||
|
||||
constructor(
|
||||
@@ -587,6 +608,7 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
@ILabelService private readonly _labelService: ILabelService,
|
||||
@IFileService private readonly _fileService: IFileService,
|
||||
@IUndoRedoService private readonly _undoService: IUndoRedoService,
|
||||
@IBackupFileService private readonly _backupFileService: IBackupFileService,
|
||||
) {
|
||||
super();
|
||||
|
||||
@@ -595,6 +617,15 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
}
|
||||
|
||||
get editorResource() {
|
||||
return this._editorResource;
|
||||
}
|
||||
|
||||
async init(): Promise<void> {
|
||||
const backup = await this._backupFileService.resolve<CustomDocumentBackupData>(this.resource);
|
||||
this._fromBackup = !!backup;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
if (this._editable) {
|
||||
this._undoService.removeElements(this._editorResource);
|
||||
@@ -624,7 +655,10 @@ class MainThreadCustomEditorModel extends Disposable implements ICustomEditorMod
|
||||
}
|
||||
|
||||
public isDirty(): boolean {
|
||||
return this._edits.length > 0 && this._savePoint !== this._currentEditIndex;
|
||||
if (this._edits.length > 0) {
|
||||
return this._savePoint !== this._currentEditIndex;
|
||||
}
|
||||
return this._fromBackup;
|
||||
}
|
||||
|
||||
private readonly _onDidChangeDirty: Emitter<void> = this._register(new Emitter<void>());
|
||||
|
||||
@@ -490,6 +490,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTerminalService.onDidWriteTerminalData(listener, thisArg, disposables);
|
||||
},
|
||||
getEnvironmentVariableCollection(persistent?: boolean): vscode.EnvironmentVariableCollection {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTerminalService.getEnvironmentVariableCollection(extension, persistent);
|
||||
},
|
||||
get state() {
|
||||
return extHostWindow.state;
|
||||
},
|
||||
@@ -600,15 +604,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
createInputBox(): vscode.InputBox {
|
||||
return extHostQuickOpen.createInputBox(extension.identifier);
|
||||
},
|
||||
registerNotebookProvider: (viewType: string, provider: vscode.NotebookProvider) => {
|
||||
return extHostNotebook.registerNotebookProvider(extension, viewType, provider);
|
||||
},
|
||||
registerNotebookOutputRenderer: (type: string, outputFilter: vscode.NotebookOutputSelector, renderer: vscode.NotebookOutputRenderer) => {
|
||||
return extHostNotebook.registerNotebookOutputRenderer(type, extension, outputFilter, renderer);
|
||||
},
|
||||
get activeNotebookDocument(): vscode.NotebookDocument | undefined {
|
||||
return extHostNotebook.activeNotebookDocument;
|
||||
},
|
||||
get activeColorTheme(): vscode.ColorTheme {
|
||||
checkProposedApiEnabled(extension);
|
||||
return extHostTheming.activeColorTheme;
|
||||
@@ -925,6 +920,19 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
}
|
||||
};
|
||||
|
||||
// namespace: notebook
|
||||
const notebook: typeof vscode.notebook = {
|
||||
registerNotebookProvider: (viewType: string, provider: vscode.NotebookProvider) => {
|
||||
return extHostNotebook.registerNotebookProvider(extension, viewType, provider);
|
||||
},
|
||||
registerNotebookOutputRenderer: (type: string, outputFilter: vscode.NotebookOutputSelector, renderer: vscode.NotebookOutputRenderer) => {
|
||||
return extHostNotebook.registerNotebookOutputRenderer(type, extension, outputFilter, renderer);
|
||||
},
|
||||
get activeNotebookDocument(): vscode.NotebookDocument | undefined {
|
||||
return extHostNotebook.activeNotebookDocument;
|
||||
}
|
||||
};
|
||||
|
||||
return <typeof vscode>{
|
||||
// {{SQL CARBON EDIT}} - Expose the VS Code version here for extensions that rely on it
|
||||
version: initData.vscodeVersion,
|
||||
@@ -939,6 +947,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
comment,
|
||||
comments,
|
||||
tasks,
|
||||
notebook,
|
||||
window,
|
||||
workspace,
|
||||
// types
|
||||
@@ -974,6 +983,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I
|
||||
DocumentLink: extHostTypes.DocumentLink,
|
||||
DocumentSymbol: extHostTypes.DocumentSymbol,
|
||||
EndOfLine: extHostTypes.EndOfLine,
|
||||
EnvironmentVariableMutatorType: extHostTypes.EnvironmentVariableMutatorType,
|
||||
EvaluatableExpression: extHostTypes.EvaluatableExpression,
|
||||
EventEmitter: Emitter,
|
||||
ExtensionKind: extHostTypes.ExtensionKind,
|
||||
|
||||
@@ -51,9 +51,10 @@ import { TunnelDto } from 'vs/workbench/api/common/extHostTunnelService';
|
||||
import { TunnelOptions } from 'vs/platform/remote/common/tunnel';
|
||||
import { Timeline, TimelineChangeEvent, TimelineOptions, TimelineProviderDescriptor, InternalTimelineOptions } from 'vs/workbench/contrib/timeline/common/timeline';
|
||||
import { revive } from 'vs/base/common/marshalling';
|
||||
import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { INotebookMimeTypeSelector, IOutput, INotebookDisplayOrder, NotebookCellMetadata, NotebookDocumentMetadata, ICellEditOperation, NotebookCellsChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { CallHierarchyItem } from 'vs/workbench/contrib/callHierarchy/common/callHierarchy';
|
||||
import { Dto } from 'vs/base/common/types';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
|
||||
// {{SQL CARBON EDIT}}
|
||||
import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views';
|
||||
@@ -161,8 +162,9 @@ export interface MainThreadAuthenticationShape extends IDisposable {
|
||||
$registerAuthenticationProvider(id: string, displayName: string): void;
|
||||
$unregisterAuthenticationProvider(id: string): void;
|
||||
$onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void;
|
||||
$getSessionsPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerId: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise<boolean>;
|
||||
$loginPrompt(providerName: string, extensionName: string): Promise<boolean>;
|
||||
$setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise<void>;
|
||||
}
|
||||
|
||||
export interface MainThreadConfigurationShape extends IDisposable {
|
||||
@@ -440,6 +442,7 @@ export interface MainThreadTerminalServiceShape extends IDisposable {
|
||||
$stopSendingDataEvents(): void;
|
||||
$startHandlingLinks(): void;
|
||||
$stopHandlingLinks(): void;
|
||||
$setEnvironmentVariableCollection(extensionIdentifier: string, persistent: boolean, collection: ISerializableEnvironmentVariableCollection | undefined): void;
|
||||
|
||||
// Process
|
||||
$sendProcessTitle(terminalId: number, title: string): void;
|
||||
@@ -696,10 +699,10 @@ export interface MainThreadNotebookShape extends IDisposable {
|
||||
$registerNotebookRenderer(extension: NotebookExtensionDescription, type: string, selectors: INotebookMimeTypeSelector, handle: number, preloads: UriComponents[]): Promise<void>;
|
||||
$unregisterNotebookRenderer(handle: number): Promise<void>;
|
||||
$createNotebookDocument(handle: number, viewType: string, resource: UriComponents): Promise<void>;
|
||||
$tryApplyEdits(viewType: string, resource: UriComponents, modelVersionId: number, edits: ICellEditOperation[], renderers: number[]): Promise<boolean>;
|
||||
$updateNotebookLanguages(viewType: string, resource: UriComponents, languages: string[]): Promise<void>;
|
||||
$updateNotebookMetadata(viewType: string, resource: UriComponents, metadata: NotebookDocumentMetadata): Promise<void>;
|
||||
$updateNotebookCellMetadata(viewType: string, resource: UriComponents, handle: number, metadata: NotebookCellMetadata | undefined): Promise<void>;
|
||||
$spliceNotebookCells(viewType: string, resource: UriComponents, splices: NotebookCellsSplice[], renderers: number[]): Promise<void>;
|
||||
$spliceNotebookCellOutputs(viewType: string, resource: UriComponents, cellHandle: number, splices: NotebookCellOutputsSplice[], renderers: number[]): Promise<void>;
|
||||
$postMessage(handle: number, value: any): Promise<boolean>;
|
||||
}
|
||||
@@ -1391,6 +1394,7 @@ export interface ExtHostTerminalServiceShape {
|
||||
$getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||
$getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
$handleLink(id: number, link: string): Promise<boolean>;
|
||||
$initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void;
|
||||
}
|
||||
|
||||
export interface ExtHostSCMShape {
|
||||
@@ -1535,13 +1539,12 @@ export interface ExtHostCommentsShape {
|
||||
export interface ExtHostNotebookShape {
|
||||
$resolveNotebook(viewType: string, uri: UriComponents): Promise<number | undefined>;
|
||||
$executeNotebook(viewType: string, uri: UriComponents, cellHandle: number | undefined): Promise<void>;
|
||||
$createEmptyCell(viewType: string, uri: UriComponents, index: number, language: string, type: CellKind): Promise<ICellDto | undefined>;
|
||||
$deleteCell(viewType: string, uri: UriComponents, index: number): Promise<boolean>;
|
||||
$saveNotebook(viewType: string, uri: UriComponents): Promise<boolean>;
|
||||
$updateActiveEditor(viewType: string, uri: UriComponents): Promise<void>;
|
||||
$destoryNotebookDocument(viewType: string, uri: UriComponents): Promise<boolean>;
|
||||
$acceptDisplayOrder(displayOrder: INotebookDisplayOrder): void;
|
||||
$onDidReceiveMessage(uri: UriComponents, message: any): void;
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void;
|
||||
}
|
||||
|
||||
export interface ExtHostStorageShape {
|
||||
|
||||
@@ -50,6 +50,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
getAccessToken: async () => {
|
||||
const isAllowed = await this._proxy.$getSessionsPrompt(
|
||||
provider.id,
|
||||
session.accountName,
|
||||
provider.displayName,
|
||||
ExtensionIdentifier.toKey(requestingExtension.identifier),
|
||||
requestingExtension.displayName || requestingExtension.name);
|
||||
@@ -70,12 +71,15 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape {
|
||||
throw new Error(`No authentication provider with id '${providerId}' is currently registered.`);
|
||||
}
|
||||
|
||||
const isAllowed = await this._proxy.$loginPrompt(provider.id, provider.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), requestingExtension.displayName || requestingExtension.name);
|
||||
const extensionName = requestingExtension.displayName || requestingExtension.name;
|
||||
const isAllowed = await this._proxy.$loginPrompt(provider.displayName, extensionName);
|
||||
if (!isAllowed) {
|
||||
throw new Error('User did not consent to login.');
|
||||
}
|
||||
|
||||
return provider.login(scopes);
|
||||
const newSession = await provider.login(scopes);
|
||||
await this._proxy.$setTrustedExtension(provider.id, newSession.accountName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName);
|
||||
return newSession;
|
||||
}
|
||||
|
||||
registerAuthenticationProvider(provider: vscode.AuthenticationProvider): vscode.Disposable {
|
||||
|
||||
@@ -3,17 +3,17 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { readonly } from 'vs/base/common/errors';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ISplice } from 'vs/base/common/sequence';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { CellKind, CellOutputKind, ExtHostNotebookShape, ICellDto, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice, NotebookCellsSplice } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { CellKind, CellOutputKind, ExtHostNotebookShape, IMainContext, MainContext, MainThreadNotebookShape, NotebookCellOutputsSplice } from 'vs/workbench/api/common/extHost.protocol';
|
||||
import { ExtHostCommands } from 'vs/workbench/api/common/extHostCommands';
|
||||
import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { CellUri, diff, IErrorOutput, INotebookDisplayOrder, IOrderedMimeType, IOutput, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, sortMimeTypes } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import * as vscode from 'vscode';
|
||||
import { CellEditType, CellUri, diff, ICellEditOperation, ICellInsertEdit, IErrorOutput, INotebookDisplayOrder, INotebookEditData, IOrderedMimeType, IStreamOutput, ITransformedDisplayOutputDto, mimeTypeSupportedByCore, NotebookCellsChangedEvent, NotebookCellsSplice2, sortMimeTypes, ICellDeleteEdit } from 'vs/workbench/contrib/notebook/common/notebookCommon';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
|
||||
interface IObservable<T> {
|
||||
@@ -44,7 +44,8 @@ const notebookDocumentMetadataDefaults: vscode.NotebookDocumentMetadata = {
|
||||
};
|
||||
|
||||
export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
public source: string[];
|
||||
|
||||
private originalSource: string[];
|
||||
private _outputs: any[];
|
||||
private _onDidChangeOutputs = new Emitter<ISplice<vscode.CellOutput>[]>();
|
||||
onDidChangeOutputs: Event<ISplice<vscode.CellOutput>[]> = this._onDidChangeOutputs.event;
|
||||
@@ -55,21 +56,28 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
|
||||
private _metadataChangeListener: IDisposable;
|
||||
|
||||
get source() {
|
||||
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
|
||||
return this._textDocument.getText();
|
||||
} else {
|
||||
return this.originalSource.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
constructor(
|
||||
private viewType: string,
|
||||
private documentUri: URI,
|
||||
readonly handle: number,
|
||||
readonly uri: URI,
|
||||
private _content: string,
|
||||
public cellKind: CellKind,
|
||||
public readonly cellKind: CellKind,
|
||||
public language: string,
|
||||
outputs: any[],
|
||||
_metadata: vscode.NotebookCellMetadata | undefined,
|
||||
private _proxy: MainThreadNotebookShape
|
||||
) {
|
||||
super();
|
||||
|
||||
this.source = this._content.split(/\r|\n|\r\n/g);
|
||||
this.originalSource = this._content.split(/\r|\n|\r\n/g);
|
||||
this._outputs = outputs;
|
||||
|
||||
const observableMetadata = getObservable(_metadata || {} as any);
|
||||
@@ -106,7 +114,7 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
return this._metadata;
|
||||
}
|
||||
|
||||
set metadata(newMetadata: vscode.NotebookCellMetadata | undefined) {
|
||||
set metadata(newMetadata: vscode.NotebookCellMetadata) {
|
||||
this._metadataChangeListener.dispose();
|
||||
const observableMetadata = getObservable(newMetadata || {} as any); // TODO defaults
|
||||
this._metadata = observableMetadata.proxy;
|
||||
@@ -125,7 +133,7 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
if (this._textDocument && this._initalVersion !== this._textDocument?.version) {
|
||||
return this._textDocument.getText();
|
||||
} else {
|
||||
return this.source.join('\n');
|
||||
return this.originalSource.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,9 +142,9 @@ export class ExtHostCell extends Disposable implements vscode.NotebookCell {
|
||||
this._initalVersion = this._textDocument.version;
|
||||
}
|
||||
|
||||
detachTextDocument(document: vscode.TextDocument) {
|
||||
detachTextDocument() {
|
||||
if (this._textDocument && this._textDocument.version !== this._initalVersion) {
|
||||
this.source = this._textDocument.getText().split(/\r|\n|\r\n/g);
|
||||
this.originalSource = this._textDocument.getText().split(/\r|\n|\r\n/g);
|
||||
}
|
||||
|
||||
this._textDocument = undefined;
|
||||
@@ -156,29 +164,6 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
return this._cells;
|
||||
}
|
||||
|
||||
set cells(newCells: ExtHostCell[]) {
|
||||
let diffs = diff<ExtHostCell>(this._cells, newCells, (a) => {
|
||||
return this._cellDisposableMapping.has(a.handle);
|
||||
});
|
||||
|
||||
diffs.forEach(diff => {
|
||||
for (let i = diff.start; i < diff.start + diff.deleteCount; i++) {
|
||||
this._cellDisposableMapping.get(this._cells[i].handle)?.clear();
|
||||
this._cellDisposableMapping.delete(this._cells[i].handle);
|
||||
}
|
||||
|
||||
diff.toInsert.forEach(cell => {
|
||||
this._cellDisposableMapping.set(cell.handle, new DisposableStore());
|
||||
this._cellDisposableMapping.get(cell.handle)?.add(cell.onDidChangeOutputs((outputDiffs) => {
|
||||
this.eventuallyUpdateCellOutputs(cell, outputDiffs);
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
this._cells = newCells;
|
||||
this.eventuallyUpdateCells(diffs);
|
||||
}
|
||||
|
||||
private _languages: string[] = [];
|
||||
|
||||
get languages() {
|
||||
@@ -211,8 +196,15 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
this._displayOrder = newOrder;
|
||||
}
|
||||
|
||||
private _versionId = 0;
|
||||
|
||||
get versionId() {
|
||||
return this._versionId;
|
||||
}
|
||||
|
||||
constructor(
|
||||
private readonly _proxy: MainThreadNotebookShape,
|
||||
private _documentsAndEditors: ExtHostDocumentsAndEditors,
|
||||
public viewType: string,
|
||||
public uri: URI,
|
||||
public renderingHandler: ExtHostNotebookOutputRenderingHandler
|
||||
@@ -229,51 +221,47 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
|
||||
get isDirty() { return false; }
|
||||
|
||||
eventuallyUpdateCells(diffs: ISplice<ExtHostCell>[]) {
|
||||
let renderers = new Set<number>();
|
||||
let diffDtos: NotebookCellsSplice[] = [];
|
||||
accpetModelChanged(event: NotebookCellsChangedEvent) {
|
||||
this.$spliceNotebookCells(event.changes);
|
||||
this._versionId = event.versionId;
|
||||
}
|
||||
|
||||
diffDtos = diffs.map(diff => {
|
||||
let inserts = diff.toInsert;
|
||||
private $spliceNotebookCells(splices: NotebookCellsSplice2[]): void {
|
||||
if (!splices.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let cellDtos = inserts.map(cell => {
|
||||
let outputs: IOutput[] = [];
|
||||
if (cell.outputs.length) {
|
||||
outputs = cell.outputs.map(output => {
|
||||
if (output.outputKind === CellOutputKind.Rich) {
|
||||
const ret = this.transformMimeTypes(cell, output);
|
||||
splices.reverse().forEach(splice => {
|
||||
let cellDtos = splice[2];
|
||||
let newCells = cellDtos.map(cell => {
|
||||
const extCell = new ExtHostCell(this.viewType, this.uri, cell.handle, URI.revive(cell.uri), cell.source.join('\n'), cell.cellKind, cell.language, cell.outputs, cell.metadata, this._proxy);
|
||||
const document = this._documentsAndEditors.getDocument(URI.revive(cell.uri));
|
||||
|
||||
if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
|
||||
renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return output as IStreamOutput | IErrorOutput;
|
||||
}
|
||||
});
|
||||
if (document) {
|
||||
extCell.attachTextDocument(document.document);
|
||||
}
|
||||
|
||||
return {
|
||||
uri: cell.uri,
|
||||
handle: cell.handle,
|
||||
source: cell.source,
|
||||
language: cell.language,
|
||||
cellKind: cell.cellKind,
|
||||
outputs: outputs,
|
||||
metadata: cell.metadata,
|
||||
isDirty: false
|
||||
};
|
||||
if (!this._cellDisposableMapping.has(extCell.handle)) {
|
||||
this._cellDisposableMapping.set(extCell.handle, new DisposableStore());
|
||||
}
|
||||
|
||||
let store = this._cellDisposableMapping.get(extCell.handle)!;
|
||||
|
||||
store.add(extCell.onDidChangeOutputs((diffs) => {
|
||||
this.eventuallyUpdateCellOutputs(extCell, diffs);
|
||||
}));
|
||||
|
||||
return extCell;
|
||||
});
|
||||
|
||||
return [diff.start, diff.deleteCount, cellDtos];
|
||||
});
|
||||
for (let j = splice[0]; j < splice[0] + splice[1]; j++) {
|
||||
this._cellDisposableMapping.get(this.cells[j].handle)?.dispose();
|
||||
this._cellDisposableMapping.delete(this.cells[j].handle);
|
||||
|
||||
this._proxy.$spliceNotebookCells(
|
||||
this.viewType,
|
||||
this.uri,
|
||||
diffDtos,
|
||||
Array.from(renderers)
|
||||
);
|
||||
}
|
||||
|
||||
this.cells.splice(splice[0], splice[1], ...newCells);
|
||||
});
|
||||
}
|
||||
|
||||
eventuallyUpdateCellOutputs(cell: ExtHostCell, diffs: ISplice<vscode.CellOutput>[]) {
|
||||
@@ -283,7 +271,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
|
||||
let transformedOutputs = outputs.map(output => {
|
||||
if (output.outputKind === CellOutputKind.Rich) {
|
||||
const ret = this.transformMimeTypes(cell, output);
|
||||
const ret = this.transformMimeTypes(output);
|
||||
|
||||
if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
|
||||
renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
|
||||
@@ -300,35 +288,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
this._proxy.$spliceNotebookCellOutputs(this.viewType, this.uri, cell.handle, outputDtos, Array.from(renderers));
|
||||
}
|
||||
|
||||
insertCell(index: number, cell: ExtHostCell) {
|
||||
this.cells.splice(index, 0, cell);
|
||||
|
||||
if (!this._cellDisposableMapping.has(cell.handle)) {
|
||||
this._cellDisposableMapping.set(cell.handle, new DisposableStore());
|
||||
}
|
||||
|
||||
let store = this._cellDisposableMapping.get(cell.handle)!;
|
||||
|
||||
store.add(cell.onDidChangeOutputs((diffs) => {
|
||||
this.eventuallyUpdateCellOutputs(cell, diffs);
|
||||
}));
|
||||
}
|
||||
|
||||
deleteCell(index: number): boolean {
|
||||
if (index >= this.cells.length) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let cell = this.cells[index];
|
||||
this._cellDisposableMapping.get(cell.handle)?.dispose();
|
||||
this._cellDisposableMapping.delete(cell.handle);
|
||||
|
||||
this.cells.splice(index, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
transformMimeTypes(cell: ExtHostCell, output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
|
||||
transformMimeTypes(output: vscode.CellDisplayOutput): ITransformedDisplayOutputDto {
|
||||
let mimeTypes = Object.keys(output.data);
|
||||
|
||||
// TODO@rebornix, the document display order might be assigned a bit later. We need to postpone sending the outputs to the core side.
|
||||
@@ -341,7 +301,7 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
let handlers = this.renderingHandler.findBestMatchedRenderer(mimeType);
|
||||
|
||||
if (handlers.length) {
|
||||
let renderedOutput = handlers[0].render(this, cell, output, mimeType);
|
||||
let renderedOutput = handlers[0].render(this, output, mimeType);
|
||||
|
||||
orderMimeTypes.push({
|
||||
mimeType: mimeType,
|
||||
@@ -395,14 +355,84 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo
|
||||
detachCellTextDocument(textDocument: vscode.TextDocument) {
|
||||
let cell = this.cells.find(cell => cell.uri.toString() === textDocument.uri.toString());
|
||||
if (cell) {
|
||||
cell.detachTextDocument(textDocument);
|
||||
cell.detachTextDocument();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class NotebookEditorCellEdit {
|
||||
private _finalized: boolean = false;
|
||||
private readonly _documentVersionId: number;
|
||||
private _collectedEdits: ICellEditOperation[] = [];
|
||||
private _renderers = new Set<number>();
|
||||
|
||||
constructor(
|
||||
readonly editor: ExtHostNotebookEditor
|
||||
) {
|
||||
this._documentVersionId = editor.document.versionId;
|
||||
}
|
||||
|
||||
finalize(): INotebookEditData {
|
||||
this._finalized = true;
|
||||
return {
|
||||
documentVersionId: this._documentVersionId,
|
||||
edits: this._collectedEdits,
|
||||
renderers: Array.from(this._renderers)
|
||||
};
|
||||
}
|
||||
|
||||
private _throwIfFinalized() {
|
||||
if (this._finalized) {
|
||||
throw new Error('Edit is only valid while callback runs');
|
||||
}
|
||||
}
|
||||
|
||||
insert(index: number, content: string, language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): void {
|
||||
this._throwIfFinalized();
|
||||
|
||||
let cell = {
|
||||
source: [content],
|
||||
language,
|
||||
cellKind: type,
|
||||
outputs: (outputs as any[]), // TODO@rebornix
|
||||
metadata
|
||||
};
|
||||
|
||||
const transformedOutputs = outputs.map(output => {
|
||||
if (output.outputKind === CellOutputKind.Rich) {
|
||||
const ret = this.editor.document.transformMimeTypes(output);
|
||||
|
||||
if (ret.orderedMimeTypes[ret.pickedMimeTypeIndex].isResolved) {
|
||||
this._renderers.add(ret.orderedMimeTypes[ret.pickedMimeTypeIndex].rendererId!);
|
||||
}
|
||||
return ret;
|
||||
} else {
|
||||
return output as IStreamOutput | IErrorOutput;
|
||||
}
|
||||
});
|
||||
|
||||
cell.outputs = transformedOutputs;
|
||||
|
||||
this._collectedEdits.push({
|
||||
editType: CellEditType.Insert,
|
||||
index,
|
||||
cells: [cell]
|
||||
});
|
||||
}
|
||||
|
||||
delete(index: number): void {
|
||||
this._throwIfFinalized();
|
||||
|
||||
this._collectedEdits.push({
|
||||
editType: CellEditType.Delete,
|
||||
index,
|
||||
count: 1
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export class ExtHostNotebookEditor extends Disposable implements vscode.NotebookEditor {
|
||||
private _viewColumn: vscode.ViewColumn | undefined;
|
||||
private static _cellhandlePool: number = 0;
|
||||
onDidReceiveMessage: vscode.Event<any> = this._onDidReceiveMessage.event;
|
||||
|
||||
constructor(
|
||||
@@ -438,11 +468,52 @@ export class ExtHostNotebookEditor extends Disposable implements vscode.Notebook
|
||||
}));
|
||||
}
|
||||
|
||||
createCell(content: string, language: string, type: CellKind, outputs: vscode.CellOutput[], metadata: vscode.NotebookCellMetadata | undefined): vscode.NotebookCell {
|
||||
const handle = ExtHostNotebookEditor._cellhandlePool++;
|
||||
const uri = CellUri.generate(this.document.uri, handle);
|
||||
const cell = new ExtHostCell(this.viewType, this.uri, handle, uri, content, type, language, outputs, metadata, this._proxy);
|
||||
return cell;
|
||||
edit(callback: (editBuilder: NotebookEditorCellEdit) => void): Thenable<boolean> {
|
||||
const edit = new NotebookEditorCellEdit(this);
|
||||
callback(edit);
|
||||
return this._applyEdit(edit);
|
||||
}
|
||||
|
||||
private _applyEdit(editBuilder: NotebookEditorCellEdit): Promise<boolean> {
|
||||
const editData = editBuilder.finalize();
|
||||
|
||||
// return when there is nothing to do
|
||||
if (editData.edits.length === 0) {
|
||||
return Promise.resolve(true);
|
||||
}
|
||||
|
||||
let compressedEdits: ICellEditOperation[] = [];
|
||||
let compressedEditsIndex = -1;
|
||||
|
||||
for (let i = 0; i < editData.edits.length; i++) {
|
||||
if (compressedEditsIndex < 0) {
|
||||
compressedEdits.push(editData.edits[i]);
|
||||
compressedEditsIndex++;
|
||||
continue;
|
||||
}
|
||||
|
||||
let prevIndex = compressedEditsIndex;
|
||||
let prev = compressedEdits[prevIndex];
|
||||
|
||||
if (prev.editType === CellEditType.Insert && editData.edits[i].editType === CellEditType.Insert) {
|
||||
if (prev.index === editData.edits[i].index) {
|
||||
prev.cells.push(...(editData.edits[i] as ICellInsertEdit).cells);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (prev.editType === CellEditType.Delete && editData.edits[i].editType === CellEditType.Delete) {
|
||||
if (prev.index === editData.edits[i].index) {
|
||||
prev.count += (editData.edits[i] as ICellDeleteEdit).count;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
compressedEdits.push(editData.edits[i]);
|
||||
compressedEditsIndex++;
|
||||
}
|
||||
|
||||
return this._proxy.$tryApplyEdits(this.viewType, this.uri, editData.documentVersionId, compressedEdits, editData.renderers);
|
||||
}
|
||||
|
||||
get viewColumn(): vscode.ViewColumn | undefined {
|
||||
@@ -480,8 +551,8 @@ export class ExtHostNotebookOutputRenderer {
|
||||
return false;
|
||||
}
|
||||
|
||||
render(document: ExtHostNotebookDocument, cell: ExtHostCell, output: vscode.CellOutput, mimeType: string): string {
|
||||
let html = this.renderer.render(document, cell, output, mimeType);
|
||||
render(document: ExtHostNotebookDocument, output: vscode.CellOutput, mimeType: string): string {
|
||||
let html = this.renderer.render(document, output, mimeType);
|
||||
|
||||
return html;
|
||||
}
|
||||
@@ -584,7 +655,7 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
|
||||
if (provider) {
|
||||
if (!this._documents.has(URI.revive(uri).toString())) {
|
||||
let document = new ExtHostNotebookDocument(this._proxy, viewType, URI.revive(uri), this);
|
||||
let document = new ExtHostNotebookDocument(this._proxy, this._documentsAndEditors, viewType, URI.revive(uri), this);
|
||||
await this._proxy.$createNotebookDocument(
|
||||
document.handle,
|
||||
viewType,
|
||||
@@ -632,55 +703,6 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
return provider.provider.executeCell(document!, cell);
|
||||
}
|
||||
|
||||
async $createEmptyCell(viewType: string, uri: URI, index: number, language: string, type: CellKind): Promise<ICellDto | undefined> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (provider) {
|
||||
let editor = this._editors.get(URI.revive(uri).toString());
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
|
||||
let rawCell = editor?.editor.createCell('', language, type, [], { editable: true, runnable: true }) as ExtHostCell;
|
||||
document?.insertCell(index, rawCell!);
|
||||
|
||||
let allDocuments = this._documentsAndEditors.allDocuments();
|
||||
for (let { document: textDocument } of allDocuments) {
|
||||
let data = CellUri.parse(textDocument.uri);
|
||||
if (data) {
|
||||
if (uri.toString() === data.notebook.toString() && textDocument.uri.toString() === rawCell.uri.toString()) {
|
||||
rawCell.attachTextDocument(textDocument);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
uri: rawCell.uri,
|
||||
handle: rawCell.handle,
|
||||
source: rawCell.source,
|
||||
language: rawCell.language,
|
||||
cellKind: rawCell.cellKind,
|
||||
metadata: rawCell.metadata,
|
||||
outputs: []
|
||||
};
|
||||
}
|
||||
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-check
|
||||
}
|
||||
|
||||
async $deleteCell(viewType: string, uri: UriComponents, index: number): Promise<boolean> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
|
||||
if (!provider) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
|
||||
if (document) {
|
||||
return document.deleteCell(index);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async $saveNotebook(viewType: string, uri: UriComponents): Promise<boolean> {
|
||||
let provider = this._notebookProviders.get(viewType);
|
||||
let document = this._documents.get(URI.revive(uri).toString());
|
||||
@@ -732,4 +754,13 @@ export class ExtHostNotebookController implements ExtHostNotebookShape, ExtHostN
|
||||
editor.onDidReceiveMessage.fire(message);
|
||||
}
|
||||
}
|
||||
|
||||
$acceptModelChanged(uriComponents: UriComponents, event: NotebookCellsChangedEvent): void {
|
||||
let editor = this._editors.get(URI.revive(uriComponents).toString());
|
||||
|
||||
if (editor) {
|
||||
editor.editor.document.accpetModelChanged(event);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ import { timeout } from 'vs/base/common/async';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { TerminalDataBufferer } from 'vs/workbench/contrib/terminal/common/terminalDataBuffering';
|
||||
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { Disposable as VSCodeDisposable } from './extHostTypes';
|
||||
import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType } from './extHostTypes';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
|
||||
export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
|
||||
|
||||
@@ -35,7 +37,8 @@ export interface IExtHostTerminalService extends ExtHostTerminalServiceShape {
|
||||
attachPtyToTerminal(id: number, pty: vscode.Pseudoterminal): void;
|
||||
getDefaultShell(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string;
|
||||
getDefaultShellArgs(useAutomationShell: boolean, configProvider: ExtHostConfigProvider): string[] | string;
|
||||
registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable
|
||||
registerLinkHandler(handler: vscode.TerminalLinkHandler): vscode.Disposable;
|
||||
getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection;
|
||||
}
|
||||
|
||||
export const IExtHostTerminalService = createDecorator<IExtHostTerminalService>('IExtHostTerminalService');
|
||||
@@ -333,6 +336,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
||||
public abstract $getAvailableShells(): Promise<IShellDefinitionDto[]>;
|
||||
public abstract $getDefaultShellAndArgs(useAutomationShell: boolean): Promise<IShellAndArgsDto>;
|
||||
public abstract $acceptWorkspacePermissionsChanged(isAllowed: boolean): void;
|
||||
public abstract getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection;
|
||||
public abstract $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void;
|
||||
|
||||
public createExtensionTerminal(options: vscode.ExtensionTerminalOptions): vscode.Terminal {
|
||||
const terminal = new ExtHostTerminal(this._proxy, options, options.name);
|
||||
@@ -637,6 +642,67 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ
|
||||
}
|
||||
}
|
||||
|
||||
export class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollection {
|
||||
readonly map: Map<string, vscode.EnvironmentVariableMutator> = new Map();
|
||||
|
||||
protected readonly _onDidChangeCollection: Emitter<void> = new Emitter<void>();
|
||||
get onDidChangeCollection(): Event<void> { return this._onDidChangeCollection && this._onDidChangeCollection.event; }
|
||||
|
||||
constructor(
|
||||
readonly persistent: boolean,
|
||||
serialized?: ISerializableEnvironmentVariableCollection
|
||||
) {
|
||||
this.map = new Map(serialized);
|
||||
}
|
||||
|
||||
get size(): number {
|
||||
return this.map.size;
|
||||
}
|
||||
|
||||
replace(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace });
|
||||
}
|
||||
|
||||
append(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append });
|
||||
}
|
||||
|
||||
prepend(variable: string, value: string): void {
|
||||
this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend });
|
||||
}
|
||||
|
||||
private _setIfDiffers(variable: string, mutator: vscode.EnvironmentVariableMutator): void {
|
||||
const current = this.map.get(variable);
|
||||
if (!current || current.value !== mutator.value || current.type !== mutator.type) {
|
||||
this.map.set(variable, mutator);
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
}
|
||||
|
||||
get(variable: string): vscode.EnvironmentVariableMutator | undefined {
|
||||
return this.map.get(variable);
|
||||
}
|
||||
|
||||
forEach(callback: (variable: string, mutator: vscode.EnvironmentVariableMutator, collection: vscode.EnvironmentVariableCollection) => any, thisArg?: any): void {
|
||||
this.map.forEach((value, key) => callback(key, value, this));
|
||||
}
|
||||
|
||||
delete(variable: string): void {
|
||||
this.map.delete(variable);
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
|
||||
clear(): void {
|
||||
this.map.clear();
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.map.clear();
|
||||
this._onDidChangeCollection.fire();
|
||||
}
|
||||
}
|
||||
|
||||
export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
public createTerminal(name?: string, shellPath?: string, shellArgs?: string[] | string): vscode.Terminal {
|
||||
throw new Error('Not implemented');
|
||||
@@ -669,4 +735,13 @@ export class WorkerExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
// No-op for web worker ext host as workspace permissions aren't used
|
||||
}
|
||||
|
||||
public getEnvironmentVariableCollection(extension: IExtensionDescription, persistent?: boolean): vscode.EnvironmentVariableCollection {
|
||||
// This is not implemented so worker ext host extensions cannot influence terminal envs
|
||||
throw new Error('Not implemented');
|
||||
}
|
||||
|
||||
public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void {
|
||||
// No-op for web worker ext host as collections aren't used
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remo
|
||||
import type * as vscode from 'vscode';
|
||||
import { Cache } from './cache';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
function es5ClassCompat(target: Function): any {
|
||||
///@ts-ignore
|
||||
@@ -483,6 +484,12 @@ export enum EndOfLine {
|
||||
CRLF = 2
|
||||
}
|
||||
|
||||
export enum EnvironmentVariableMutatorType {
|
||||
Replace = 1,
|
||||
Append = 2,
|
||||
Prepend = 3
|
||||
}
|
||||
|
||||
@es5ClassCompat
|
||||
export class TextEdit {
|
||||
|
||||
@@ -2591,22 +2598,22 @@ interface EditState {
|
||||
|
||||
export class CustomDocument<EditType = unknown> implements vscode.CustomDocument<EditType> {
|
||||
|
||||
|
||||
readonly #edits = new Cache<EditType>('edits');
|
||||
|
||||
#editState: EditState;
|
||||
|
||||
readonly #viewType: string;
|
||||
readonly #uri: vscode.Uri;
|
||||
|
||||
#editState: EditState = {
|
||||
allEdits: [],
|
||||
currentIndex: -1,
|
||||
saveIndex: -1,
|
||||
};
|
||||
#isDisposed = false;
|
||||
#version = 1;
|
||||
|
||||
constructor(viewType: string, uri: vscode.Uri) {
|
||||
this.#viewType = viewType;
|
||||
this.#uri = uri;
|
||||
this.#editState = {
|
||||
allEdits: [],
|
||||
currentIndex: 0,
|
||||
saveIndex: 0
|
||||
};
|
||||
}
|
||||
|
||||
//#region Public API
|
||||
@@ -2615,15 +2622,27 @@ export class CustomDocument<EditType = unknown> implements vscode.CustomDocument
|
||||
|
||||
public get uri(): vscode.Uri { return this.#uri; }
|
||||
|
||||
public get fileName(): string { return this.uri.fsPath; }
|
||||
|
||||
public get isUntitled() { return this.uri.scheme === Schemas.untitled; }
|
||||
|
||||
#onDidDispose = new Emitter<void>();
|
||||
public readonly onDidDispose = this.#onDidDispose.event;
|
||||
|
||||
get appliedEdits() {
|
||||
public get isClosed() { return this.#isDisposed; }
|
||||
|
||||
public get version() { return this.#version; }
|
||||
|
||||
public get isDirty() {
|
||||
return this.#editState.currentIndex !== this.#editState.saveIndex;
|
||||
}
|
||||
|
||||
public get appliedEdits() {
|
||||
return this.#editState.allEdits.slice(0, this.#editState.currentIndex + 1)
|
||||
.map(id => this._getEdit(id));
|
||||
}
|
||||
|
||||
get savedEdits() {
|
||||
public get savedEdits() {
|
||||
return this.#editState.allEdits.slice(0, this.#editState.saveIndex + 1)
|
||||
.map(id => this._getEdit(id));
|
||||
}
|
||||
@@ -2631,11 +2650,13 @@ export class CustomDocument<EditType = unknown> implements vscode.CustomDocument
|
||||
//#endregion
|
||||
|
||||
/** @internal */ _dispose(): void {
|
||||
this.#isDisposed = true;
|
||||
this.#onDidDispose.fire();
|
||||
this.#onDidDispose.dispose();
|
||||
}
|
||||
|
||||
/** @internal */ _updateEditState(state: EditState) {
|
||||
++this.#version;
|
||||
this.#editState = state;
|
||||
}
|
||||
|
||||
|
||||
@@ -24,73 +24,91 @@ import * as extHostTypes from './extHostTypes';
|
||||
type IconPath = URI | { light: URI, dark: URI };
|
||||
|
||||
export class ExtHostWebview implements vscode.Webview {
|
||||
private _html: string = '';
|
||||
private _isDisposed: boolean = false;
|
||||
private _hasCalledAsWebviewUri = false;
|
||||
|
||||
public readonly _onMessageEmitter = new Emitter<any>();
|
||||
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
|
||||
readonly #handle: extHostProtocol.WebviewPanelHandle;
|
||||
readonly #proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
readonly #deprecationService: IExtHostApiDeprecationService;
|
||||
|
||||
readonly #initData: WebviewInitData;
|
||||
readonly #workspace: IExtHostWorkspace | undefined;
|
||||
readonly #extension: IExtensionDescription;
|
||||
|
||||
#html: string = '';
|
||||
#options: vscode.WebviewOptions;
|
||||
#isDisposed: boolean = false;
|
||||
#hasCalledAsWebviewUri = false;
|
||||
|
||||
constructor(
|
||||
private readonly _handle: extHostProtocol.WebviewPanelHandle,
|
||||
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape,
|
||||
private _options: vscode.WebviewOptions,
|
||||
private readonly _initData: WebviewInitData,
|
||||
private readonly _workspace: IExtHostWorkspace | undefined,
|
||||
private readonly _extension: IExtensionDescription,
|
||||
private readonly _deprecationService: IExtHostApiDeprecationService,
|
||||
) { }
|
||||
handle: extHostProtocol.WebviewPanelHandle,
|
||||
proxy: extHostProtocol.MainThreadWebviewsShape,
|
||||
options: vscode.WebviewOptions,
|
||||
initData: WebviewInitData,
|
||||
workspace: IExtHostWorkspace | undefined,
|
||||
extension: IExtensionDescription,
|
||||
deprecationService: IExtHostApiDeprecationService,
|
||||
) {
|
||||
this.#handle = handle;
|
||||
this.#proxy = proxy;
|
||||
this.#options = options;
|
||||
this.#initData = initData;
|
||||
this.#workspace = workspace;
|
||||
this.#extension = extension;
|
||||
this.#deprecationService = deprecationService;
|
||||
}
|
||||
|
||||
/* internal */ readonly _onMessageEmitter = new Emitter<any>();
|
||||
public readonly onDidReceiveMessage: Event<any> = this._onMessageEmitter.event;
|
||||
|
||||
public dispose() {
|
||||
this._onMessageEmitter.dispose();
|
||||
}
|
||||
|
||||
public asWebviewUri(resource: vscode.Uri): vscode.Uri {
|
||||
this._hasCalledAsWebviewUri = true;
|
||||
return asWebviewUri(this._initData, this._handle, resource);
|
||||
this.#hasCalledAsWebviewUri = true;
|
||||
return asWebviewUri(this.#initData, this.#handle, resource);
|
||||
}
|
||||
|
||||
public get cspSource(): string {
|
||||
return this._initData.webviewCspSource
|
||||
.replace('{{uuid}}', this._handle);
|
||||
return this.#initData.webviewCspSource
|
||||
.replace('{{uuid}}', this.#handle);
|
||||
}
|
||||
|
||||
public get html(): string {
|
||||
this.assertNotDisposed();
|
||||
return this._html;
|
||||
return this.#html;
|
||||
}
|
||||
|
||||
public set html(value: string) {
|
||||
this.assertNotDisposed();
|
||||
if (this._html !== value) {
|
||||
this._html = value;
|
||||
if (!this._hasCalledAsWebviewUri && /(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) {
|
||||
this._hasCalledAsWebviewUri = true;
|
||||
this._deprecationService.report('Webview vscode-resource: uris', this._extension,
|
||||
if (this.#html !== value) {
|
||||
this.#html = value;
|
||||
if (!this.#hasCalledAsWebviewUri && /(["'])vscode-resource:([^\s'"]+?)(["'])/i.test(value)) {
|
||||
this.#hasCalledAsWebviewUri = true;
|
||||
this.#deprecationService.report('Webview vscode-resource: uris', this.#extension,
|
||||
`Please migrate to use the 'webview.asWebviewUri' api instead: https://aka.ms/vscode-webview-use-aswebviewuri`);
|
||||
}
|
||||
this._proxy.$setHtml(this._handle, value);
|
||||
this.#proxy.$setHtml(this.#handle, value);
|
||||
}
|
||||
}
|
||||
|
||||
public get options(): vscode.WebviewOptions {
|
||||
this.assertNotDisposed();
|
||||
return this._options;
|
||||
return this.#options;
|
||||
}
|
||||
|
||||
public set options(newOptions: vscode.WebviewOptions) {
|
||||
this.assertNotDisposed();
|
||||
this._proxy.$setOptions(this._handle, convertWebviewOptions(this._extension, this._workspace, newOptions));
|
||||
this._options = newOptions;
|
||||
this.#proxy.$setOptions(this.#handle, convertWebviewOptions(this.#extension, this.#workspace, newOptions));
|
||||
this.#options = newOptions;
|
||||
}
|
||||
|
||||
public postMessage(message: any): Promise<boolean> {
|
||||
this.assertNotDisposed();
|
||||
return this._proxy.$postMessage(this._handle, message);
|
||||
return this.#proxy.$postMessage(this.#handle, message);
|
||||
}
|
||||
|
||||
private assertNotDisposed() {
|
||||
if (this._isDisposed) {
|
||||
if (this.#isDisposed) {
|
||||
throw new Error('Webview is disposed');
|
||||
}
|
||||
}
|
||||
@@ -98,19 +116,18 @@ export class ExtHostWebview implements vscode.Webview {
|
||||
|
||||
export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPanel {
|
||||
|
||||
private readonly _handle: extHostProtocol.WebviewPanelHandle;
|
||||
private readonly _proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
private readonly _viewType: string;
|
||||
private _title: string;
|
||||
private _iconPath?: IconPath;
|
||||
readonly #handle: extHostProtocol.WebviewPanelHandle;
|
||||
readonly #proxy: extHostProtocol.MainThreadWebviewsShape;
|
||||
readonly #viewType: string;
|
||||
|
||||
readonly #options: vscode.WebviewPanelOptions;
|
||||
readonly #webview: ExtHostWebview;
|
||||
readonly #options: vscode.WebviewPanelOptions;
|
||||
|
||||
#title: string;
|
||||
#iconPath?: IconPath;
|
||||
#viewColumn: vscode.ViewColumn | undefined = undefined;
|
||||
#visible: boolean = true;
|
||||
#active: boolean = true;
|
||||
|
||||
#isDisposed: boolean = false;
|
||||
|
||||
readonly #onDidDispose = this._register(new Emitter<void>());
|
||||
@@ -129,12 +146,12 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
webview: ExtHostWebview
|
||||
) {
|
||||
super();
|
||||
this._handle = handle;
|
||||
this._proxy = proxy;
|
||||
this._viewType = viewType;
|
||||
this.#handle = handle;
|
||||
this.#proxy = proxy;
|
||||
this.#viewType = viewType;
|
||||
this.#options = editorOptions;
|
||||
this.#viewColumn = viewColumn;
|
||||
this._title = title;
|
||||
this.#title = title;
|
||||
this.#webview = webview;
|
||||
}
|
||||
|
||||
@@ -145,7 +162,7 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
this.#isDisposed = true;
|
||||
this.#onDidDispose.fire();
|
||||
this._proxy.$disposeWebview(this._handle);
|
||||
this.#proxy.$disposeWebview(this.#handle);
|
||||
this.#webview.dispose();
|
||||
|
||||
super.dispose();
|
||||
@@ -158,33 +175,33 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
get viewType(): string {
|
||||
this.assertNotDisposed();
|
||||
return this._viewType;
|
||||
return this.#viewType;
|
||||
}
|
||||
|
||||
get title(): string {
|
||||
this.assertNotDisposed();
|
||||
return this._title;
|
||||
return this.#title;
|
||||
}
|
||||
|
||||
set title(value: string) {
|
||||
this.assertNotDisposed();
|
||||
if (this._title !== value) {
|
||||
this._title = value;
|
||||
this._proxy.$setTitle(this._handle, value);
|
||||
if (this.#title !== value) {
|
||||
this.#title = value;
|
||||
this.#proxy.$setTitle(this.#handle, value);
|
||||
}
|
||||
}
|
||||
|
||||
get iconPath(): IconPath | undefined {
|
||||
this.assertNotDisposed();
|
||||
return this._iconPath;
|
||||
return this.#iconPath;
|
||||
}
|
||||
|
||||
set iconPath(value: IconPath | undefined) {
|
||||
this.assertNotDisposed();
|
||||
if (this._iconPath !== value) {
|
||||
this._iconPath = value;
|
||||
if (this.#iconPath !== value) {
|
||||
this.#iconPath = value;
|
||||
|
||||
this._proxy.$setIconPath(this._handle, URI.isUri(value) ? { light: value, dark: value } : value);
|
||||
this.#proxy.$setIconPath(this.#handle, URI.isUri(value) ? { light: value, dark: value } : value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,12 +244,12 @@ export class ExtHostWebviewEditor extends Disposable implements vscode.WebviewPa
|
||||
|
||||
public postMessage(message: any): Promise<boolean> {
|
||||
this.assertNotDisposed();
|
||||
return this._proxy.$postMessage(this._handle, message);
|
||||
return this.#proxy.$postMessage(this.#handle, message);
|
||||
}
|
||||
|
||||
public reveal(viewColumn?: vscode.ViewColumn, preserveFocus?: boolean): void {
|
||||
this.assertNotDisposed();
|
||||
this._proxy.$reveal(this._handle, {
|
||||
this.#proxy.$reveal(this.#handle, {
|
||||
viewColumn: viewColumn ? typeConverters.ViewColumn.from(viewColumn) : undefined,
|
||||
preserveFocus: !!preserveFocus
|
||||
});
|
||||
|
||||
@@ -38,6 +38,7 @@ namespace schema {
|
||||
case 'debug/toolbar': return MenuId.DebugToolBar;
|
||||
case 'debug/toolBar': return MenuId.DebugToolBar;
|
||||
case 'menuBar/file': return MenuId.MenubarFileMenu;
|
||||
case 'menuBar/webNavigation': return MenuId.MenubarWebNavigationMenu;
|
||||
case 'scm/title': return MenuId.SCMTitle;
|
||||
case 'scm/sourceControl': return MenuId.SCMSourceControl;
|
||||
case 'scm/resourceState/context': return MenuId.SCMResourceContext;
|
||||
@@ -70,6 +71,7 @@ namespace schema {
|
||||
switch (menuId) {
|
||||
case MenuId.StatusBarWindowIndicatorMenu:
|
||||
case MenuId.MenubarFileMenu:
|
||||
case MenuId.MenubarWebNavigationMenu:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@@ -20,14 +20,21 @@ import { ExtHostVariableResolverService } from 'vs/workbench/api/common/extHostD
|
||||
import { ExtHostDocumentsAndEditors, IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors';
|
||||
import { getSystemShell, detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal';
|
||||
import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment';
|
||||
import { BaseExtHostTerminalService, ExtHostTerminal } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { BaseExtHostTerminalService, ExtHostTerminal, EnvironmentVariableCollection } from 'vs/workbench/api/common/extHostTerminalService';
|
||||
import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService';
|
||||
import { IExtensionDescription } from 'vs/platform/extensions/common/extensions';
|
||||
import { dispose } from 'vs/base/common/lifecycle';
|
||||
import { serializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared';
|
||||
import { ISerializableEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable';
|
||||
import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection';
|
||||
|
||||
export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
|
||||
private _variableResolver: ExtHostVariableResolverService | undefined;
|
||||
private _lastActiveWorkspace: IWorkspaceFolder | undefined;
|
||||
|
||||
private _environmentVariableCollections: Map<string, EnvironmentVariableCollection> = new Map();
|
||||
|
||||
// TODO: Pull this from main side
|
||||
private _isWorkspaceShellAllowed: boolean = false;
|
||||
|
||||
@@ -191,6 +198,10 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
baseEnv
|
||||
);
|
||||
|
||||
// Apply extension environment variable collections to the environment
|
||||
const mergedCollection = new MergedEnvironmentVariableCollection(this._environmentVariableCollections);
|
||||
mergedCollection.applyToProcessEnvironment(env);
|
||||
|
||||
this._proxy.$sendResolvedLaunchConfig(id, shellLaunchConfig);
|
||||
// Fork the process and listen for messages
|
||||
this._logService.debug(`Terminal process launching on ext host`, shellLaunchConfig, initialCwd, cols, rows, env);
|
||||
@@ -215,4 +226,50 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService {
|
||||
public $acceptWorkspacePermissionsChanged(isAllowed: boolean): void {
|
||||
this._isWorkspaceShellAllowed = isAllowed;
|
||||
}
|
||||
|
||||
public getEnvironmentVariableCollection(extension: IExtensionDescription, persistent: boolean = false): vscode.EnvironmentVariableCollection {
|
||||
let collection: EnvironmentVariableCollection | undefined;
|
||||
if (persistent) {
|
||||
// If persistent is specified, return the current collection if it exists
|
||||
collection = this._environmentVariableCollections.get(extension.identifier.value);
|
||||
|
||||
// If persistence changed then create a new collection
|
||||
if (collection && !collection.persistent) {
|
||||
collection = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if (!collection) {
|
||||
// If not persistent, clear out the current collection and create a new one
|
||||
dispose(this._environmentVariableCollections.get(extension.identifier.value));
|
||||
collection = new EnvironmentVariableCollection(persistent);
|
||||
this._setEnvironmentVariableCollection(extension.identifier.value, collection);
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
private _syncEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void {
|
||||
const serialized = serializeEnvironmentVariableCollection(collection.map);
|
||||
this._proxy.$setEnvironmentVariableCollection(extensionIdentifier, collection.persistent, serialized.length === 0 ? undefined : serialized);
|
||||
}
|
||||
|
||||
public $initEnvironmentVariableCollections(collections: [string, ISerializableEnvironmentVariableCollection][]): void {
|
||||
collections.forEach(entry => {
|
||||
const extensionIdentifier = entry[0];
|
||||
const collection = new EnvironmentVariableCollection(true, entry[1]);
|
||||
this._setEnvironmentVariableCollection(extensionIdentifier, collection);
|
||||
});
|
||||
}
|
||||
|
||||
private _setEnvironmentVariableCollection(extensionIdentifier: string, collection: EnvironmentVariableCollection): void {
|
||||
this._environmentVariableCollections.set(extensionIdentifier, collection);
|
||||
collection.onDidChangeCollection(() => {
|
||||
// When any collection value changes send this immediately, this is done to ensure
|
||||
// following calls to createTerminal will be created with the new environment. It will
|
||||
// result in more noise by sending multiple updates when called but collections are
|
||||
// expected to be small.
|
||||
this._syncEnvironmentVariableCollection(extensionIdentifier, collection!);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,196 +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 { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ITree, IActionProvider } from 'vs/base/parts/tree/browser/tree';
|
||||
import { IInstantiationService, IConstructorSignature0, ServicesAccessor, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
/**
|
||||
* The action bar contributor allows to add actions to an actionbar in a given context.
|
||||
*/
|
||||
export class ActionBarContributor {
|
||||
|
||||
/**
|
||||
* Returns true if this contributor has actions for the given context.
|
||||
*/
|
||||
hasActions(context: unknown): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of primary actions in the given context.
|
||||
*/
|
||||
getActions(context: unknown): ReadonlyArray<IAction> {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some predefined scopes to contribute actions to
|
||||
*/
|
||||
export const Scope = {
|
||||
|
||||
/**
|
||||
* Actions inside tree widgets.
|
||||
*/
|
||||
VIEWER: 'viewer'
|
||||
};
|
||||
|
||||
/**
|
||||
* The ContributableActionProvider leverages the actionbar contribution model to find actions.
|
||||
*/
|
||||
export class ContributableActionProvider implements IActionProvider {
|
||||
private readonly registry: IActionBarRegistry = Registry.as<IActionBarRegistry>(Extensions.Actionbar);
|
||||
|
||||
private toContext(tree: ITree, element: unknown): unknown {
|
||||
return {
|
||||
viewer: tree,
|
||||
element: element
|
||||
};
|
||||
}
|
||||
|
||||
hasActions(tree: ITree, element: unknown): boolean {
|
||||
const context = this.toContext(tree, element);
|
||||
|
||||
const contributors = this.registry.getActionBarContributors(Scope.VIEWER);
|
||||
return contributors.some(contributor => contributor.hasActions(context));
|
||||
}
|
||||
|
||||
getActions(tree: ITree, element: unknown): ReadonlyArray<IAction> {
|
||||
const actions: IAction[] = [];
|
||||
const context = this.toContext(tree, element);
|
||||
|
||||
// Collect Actions
|
||||
const contributors = this.registry.getActionBarContributors(Scope.VIEWER);
|
||||
for (const contributor of contributors) {
|
||||
if (contributor.hasActions(context)) {
|
||||
actions.push(...contributor.getActions(context));
|
||||
}
|
||||
}
|
||||
|
||||
return prepareActions(actions);
|
||||
}
|
||||
}
|
||||
|
||||
// Helper function used in parts to massage actions before showing in action areas
|
||||
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;
|
||||
}
|
||||
|
||||
export const Extensions = {
|
||||
Actionbar: 'workbench.contributions.actionbar'
|
||||
};
|
||||
|
||||
export interface IActionBarRegistry {
|
||||
/**
|
||||
* Registers an Actionbar contributor. It will be called to contribute actions to all the action bars
|
||||
* that are used in the Workbench in the given scope.
|
||||
*/
|
||||
registerActionBarContributor<Services extends BrandedService[]>(scope: string, ctor: { new(...services: Services): ActionBarContributor }): void;
|
||||
|
||||
/**
|
||||
* Returns an array of registered action bar contributors known to the workbench for the given scope.
|
||||
*/
|
||||
getActionBarContributors(scope: string): ActionBarContributor[];
|
||||
|
||||
/**
|
||||
* Starts the registry by providing the required services.
|
||||
*/
|
||||
start(accessor: ServicesAccessor): void;
|
||||
}
|
||||
|
||||
class ActionBarRegistry implements IActionBarRegistry {
|
||||
private readonly actionBarContributorConstructors: { scope: string; ctor: IConstructorSignature0<ActionBarContributor>; }[] = [];
|
||||
private readonly actionBarContributorInstances: Map<string, ActionBarContributor[]> = new Map();
|
||||
private instantiationService: IInstantiationService | undefined;
|
||||
|
||||
start(accessor: ServicesAccessor): void {
|
||||
this.instantiationService = accessor.get(IInstantiationService);
|
||||
|
||||
while (this.actionBarContributorConstructors.length > 0) {
|
||||
const entry = this.actionBarContributorConstructors.shift()!;
|
||||
this.createActionBarContributor(entry.scope, entry.ctor);
|
||||
}
|
||||
}
|
||||
|
||||
private createActionBarContributor(scope: string, ctor: IConstructorSignature0<ActionBarContributor>): void {
|
||||
if (this.instantiationService) {
|
||||
const instance = this.instantiationService.createInstance(ctor);
|
||||
let target = this.actionBarContributorInstances.get(scope);
|
||||
if (!target) {
|
||||
target = [];
|
||||
this.actionBarContributorInstances.set(scope, target);
|
||||
}
|
||||
target.push(instance);
|
||||
}
|
||||
}
|
||||
|
||||
private getContributors(scope: string): ActionBarContributor[] {
|
||||
return this.actionBarContributorInstances.get(scope) || [];
|
||||
}
|
||||
|
||||
registerActionBarContributor(scope: string, ctor: IConstructorSignature0<ActionBarContributor>): void {
|
||||
if (!this.instantiationService) {
|
||||
this.actionBarContributorConstructors.push({
|
||||
scope: scope,
|
||||
ctor: ctor
|
||||
});
|
||||
} else {
|
||||
this.createActionBarContributor(scope, ctor);
|
||||
}
|
||||
}
|
||||
|
||||
getActionBarContributors(scope: string): ActionBarContributor[] {
|
||||
return this.getContributors(scope).slice(0);
|
||||
}
|
||||
}
|
||||
|
||||
Registry.add(Extensions.Actionbar, new ActionBarRegistry());
|
||||
@@ -0,0 +1,251 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
|
||||
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { IQuickInputService, ItemActivation } from 'vs/platform/quickinput/common/quickInput';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { inQuickPickContext, defaultQuickAccessContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess';
|
||||
|
||||
//#region Quick access management commands and keys
|
||||
|
||||
const globalQuickAccessKeybinding = {
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_P,
|
||||
secondary: [KeyMod.CtrlCmd | KeyCode.KEY_E],
|
||||
mac: { primary: KeyMod.CtrlCmd | KeyCode.KEY_P, secondary: undefined }
|
||||
};
|
||||
|
||||
const QUICKACCESS_ACTION_ID = 'workbench.action.quickOpen';
|
||||
|
||||
MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
|
||||
command: { id: QUICKACCESS_ACTION_ID, title: { value: localize('quickOpen', "Go to File..."), original: 'Go to File...' } }
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerKeybindingRule({
|
||||
id: QUICKACCESS_ACTION_ID,
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: undefined,
|
||||
primary: globalQuickAccessKeybinding.primary,
|
||||
secondary: globalQuickAccessKeybinding.secondary,
|
||||
mac: globalQuickAccessKeybinding.mac
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.closeQuickOpen',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickPickContext,
|
||||
primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape],
|
||||
handler: accessor => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
return quickInputService.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.acceptSelectedQuickOpenItem',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickPickContext,
|
||||
primary: 0,
|
||||
handler: accessor => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
return quickInputService.accept();
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.alternativeAcceptSelectedQuickOpenItem',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickPickContext,
|
||||
primary: 0,
|
||||
handler: accessor => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
return quickInputService.accept({ ctrlCmd: true, alt: false });
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.focusQuickOpen',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickPickContext,
|
||||
primary: 0,
|
||||
handler: accessor => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
quickInputService.focus();
|
||||
}
|
||||
});
|
||||
|
||||
const quickAccessNavigateNextInFilePickerId = 'workbench.action.quickOpenNavigateNextInFilePicker';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: quickAccessNavigateNextInFilePickerId,
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickAccessNavigateNextInFilePickerId, true),
|
||||
when: defaultQuickAccessContext,
|
||||
primary: globalQuickAccessKeybinding.primary,
|
||||
secondary: globalQuickAccessKeybinding.secondary,
|
||||
mac: globalQuickAccessKeybinding.mac
|
||||
});
|
||||
|
||||
const quickAccessNavigatePreviousInFilePickerId = 'workbench.action.quickOpenNavigatePreviousInFilePicker';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: quickAccessNavigatePreviousInFilePickerId,
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickAccessNavigatePreviousInFilePickerId, false),
|
||||
when: defaultQuickAccessContext,
|
||||
primary: globalQuickAccessKeybinding.primary | KeyMod.Shift,
|
||||
secondary: [globalQuickAccessKeybinding.secondary[0] | KeyMod.Shift],
|
||||
mac: {
|
||||
primary: globalQuickAccessKeybinding.mac.primary | KeyMod.Shift,
|
||||
secondary: undefined
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.quickPickManyToggle',
|
||||
weight: KeybindingWeight.WorkbenchContrib,
|
||||
when: inQuickPickContext,
|
||||
primary: 0,
|
||||
handler: accessor => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
quickInputService.toggle();
|
||||
}
|
||||
});
|
||||
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: 'workbench.action.quickInputBack',
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
when: inQuickPickContext,
|
||||
primary: 0,
|
||||
win: { primary: KeyMod.Alt | KeyCode.LeftArrow },
|
||||
mac: { primary: KeyMod.WinCtrl | KeyCode.US_MINUS },
|
||||
linux: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.US_MINUS },
|
||||
handler: accessor => {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
quickInputService.back();
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand({
|
||||
id: QUICKACCESS_ACTION_ID,
|
||||
handler: async function (accessor: ServicesAccessor, prefix: unknown) {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
|
||||
quickInputService.quickAccess.show(typeof prefix === 'string' ? prefix : undefined);
|
||||
},
|
||||
description: {
|
||||
description: `Quick access`,
|
||||
args: [{
|
||||
name: 'prefix',
|
||||
schema: {
|
||||
'type': 'string'
|
||||
}
|
||||
}]
|
||||
}
|
||||
});
|
||||
|
||||
CommandsRegistry.registerCommand('workbench.action.quickOpenPreviousEditor', async function (accessor: ServicesAccessor, prefix: string | null = null) {
|
||||
const quickInputService = accessor.get(IQuickInputService);
|
||||
|
||||
quickInputService.quickAccess.show('', { itemActivation: ItemActivation.SECOND });
|
||||
});
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Workbench actions
|
||||
|
||||
export class BaseQuickAccessNavigateAction extends Action {
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
private next: boolean,
|
||||
private quickNavigate: boolean,
|
||||
@IQuickInputService private readonly quickInputService: IQuickInputService,
|
||||
@IKeybindingService private readonly keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label);
|
||||
}
|
||||
|
||||
async run(): Promise<void> {
|
||||
const keys = this.keybindingService.lookupKeybindings(this.id);
|
||||
const quickNavigate = this.quickNavigate ? { keybindings: keys } : undefined;
|
||||
|
||||
this.quickInputService.navigate(this.next, quickNavigate);
|
||||
}
|
||||
}
|
||||
|
||||
export class QuickAccessNavigateNextAction extends BaseQuickAccessNavigateAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenNavigateNext';
|
||||
static readonly LABEL = localize('quickNavigateNext', "Navigate Next in Quick Open");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, true, true, quickInputService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
class QuickAccessNavigatePreviousAction extends BaseQuickAccessNavigateAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenNavigatePrevious';
|
||||
static readonly LABEL = localize('quickNavigatePrevious', "Navigate Previous in Quick Open");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, false, true, quickInputService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
class QuickAccessSelectNextAction extends BaseQuickAccessNavigateAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenSelectNext';
|
||||
static readonly LABEL = localize('quickSelectNext', "Select Next in Quick Open");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, true, false, quickInputService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
class QuickAccessSelectPreviousAction extends BaseQuickAccessNavigateAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenSelectPrevious';
|
||||
static readonly LABEL = localize('quickSelectPrevious', "Select Previous in Quick Open");
|
||||
|
||||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IQuickInputService quickInputService: IQuickInputService,
|
||||
@IKeybindingService keybindingService: IKeybindingService
|
||||
) {
|
||||
super(id, label, false, false, quickInputService, keybindingService);
|
||||
}
|
||||
}
|
||||
|
||||
const registry = Registry.as<IWorkbenchActionRegistry>(ActionExtensions.WorkbenchActions);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickAccessSelectNextAction, QuickAccessSelectNextAction.ID, QuickAccessSelectNextAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_N } }, inQuickPickContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Next in Quick Open');
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickAccessSelectPreviousAction, QuickAccessSelectPreviousAction.ID, QuickAccessSelectPreviousAction.LABEL, { primary: 0, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_P } }, inQuickPickContext, KeybindingWeight.WorkbenchContrib + 50), 'Select Previous in Quick Open');
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickAccessNavigateNextAction, QuickAccessNavigateNextAction.ID, QuickAccessNavigateNextAction.LABEL), 'Navigate Next in Quick Open');
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickAccessNavigatePreviousAction, QuickAccessNavigatePreviousAction.ID, QuickAccessNavigatePreviousAction.LABEL), 'Navigate Previous in Quick Open');
|
||||
|
||||
//#endregion
|
||||
@@ -29,7 +29,7 @@ import { FileKind } from 'vs/platform/files/common/files';
|
||||
import { splitName } from 'vs/base/common/labels';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { inQuickOpenContext, getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen';
|
||||
import { inQuickPickContext, getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess';
|
||||
import { IHostService } from 'vs/workbench/services/host/browser/host';
|
||||
|
||||
export const inRecentFilesPickerContextKey = 'inRecentFilesPicker';
|
||||
@@ -165,7 +165,7 @@ export class OpenRecentAction extends BaseOpenRecentAction {
|
||||
}
|
||||
}
|
||||
|
||||
class QuickOpenRecentAction extends BaseOpenRecentAction {
|
||||
class QuickPickRecentAction extends BaseOpenRecentAction {
|
||||
|
||||
static readonly ID = 'workbench.action.quickOpenRecent';
|
||||
static readonly LABEL = nls.localize('quickOpenRecent', "Quick Open Recent...");
|
||||
@@ -270,7 +270,7 @@ const registry = Registry.as<IWorkbenchActionRegistry>(Extensions.WorkbenchActio
|
||||
|
||||
const fileCategory = nls.localize('file', "File");
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(NewWindowAction, NewWindowAction.ID, NewWindowAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_N }), 'New Window');
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickOpenRecentAction, QuickOpenRecentAction.ID, QuickOpenRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(QuickPickRecentAction, QuickPickRecentAction.ID, QuickPickRecentAction.LABEL), 'File: Quick Open Recent...', fileCategory);
|
||||
registry.registerWorkbenchAction(SyncActionDescriptor.create(OpenRecentAction, OpenRecentAction.ID, OpenRecentAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_R, mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R } }), 'File: Open Recent...', fileCategory);
|
||||
|
||||
const viewCategory = nls.localize('view', "View");
|
||||
@@ -284,23 +284,23 @@ registry.registerWorkbenchAction(SyncActionDescriptor.create(ShowAboutDialogActi
|
||||
|
||||
// --- Commands/Keybindings Registration
|
||||
|
||||
const recentFilesPickerContext = ContextKeyExpr.and(inQuickOpenContext, ContextKeyExpr.has(inRecentFilesPickerContextKey));
|
||||
const recentFilesPickerContext = ContextKeyExpr.and(inQuickPickContext, ContextKeyExpr.has(inRecentFilesPickerContextKey));
|
||||
|
||||
const quickOpenNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker';
|
||||
const quickPickNavigateNextInRecentFilesPickerId = 'workbench.action.quickOpenNavigateNextInRecentFilesPicker';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: quickOpenNavigateNextInRecentFilesPickerId,
|
||||
id: quickPickNavigateNextInRecentFilesPickerId,
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickOpenNavigateNextInRecentFilesPickerId, true),
|
||||
handler: getQuickNavigateHandler(quickPickNavigateNextInRecentFilesPickerId, true),
|
||||
when: recentFilesPickerContext,
|
||||
primary: KeyMod.CtrlCmd | KeyCode.KEY_R,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyCode.KEY_R }
|
||||
});
|
||||
|
||||
const quickOpenNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker';
|
||||
const quickPickNavigatePreviousInRecentFilesPicker = 'workbench.action.quickOpenNavigatePreviousInRecentFilesPicker';
|
||||
KeybindingsRegistry.registerCommandAndKeybindingRule({
|
||||
id: quickOpenNavigatePreviousInRecentFilesPicker,
|
||||
id: quickPickNavigatePreviousInRecentFilesPicker,
|
||||
weight: KeybindingWeight.WorkbenchContrib + 50,
|
||||
handler: getQuickNavigateHandler(quickOpenNavigatePreviousInRecentFilesPicker, false),
|
||||
handler: getQuickNavigateHandler(quickPickNavigatePreviousInRecentFilesPicker, false),
|
||||
when: recentFilesPickerContext,
|
||||
primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_R,
|
||||
mac: { primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.KEY_R }
|
||||
|
||||
@@ -206,7 +206,12 @@ export class WorkbenchContextKeysHandler extends Disposable {
|
||||
|
||||
if (activeEditorPane) {
|
||||
this.activeEditorContext.set(activeEditorPane.getId());
|
||||
this.activeEditorIsReadonly.set(activeEditorPane.input.isReadonly());
|
||||
try {
|
||||
this.activeEditorIsReadonly.set(activeEditorPane.input.isReadonly());
|
||||
} catch (error) {
|
||||
// TODO@ben for https://github.com/microsoft/vscode/issues/93224
|
||||
throw new Error(`${error.message}: editor id ${activeEditorPane.getId()}`);
|
||||
}
|
||||
} else {
|
||||
this.activeEditorContext.reset();
|
||||
this.activeEditorIsReadonly.reset();
|
||||
|
||||
@@ -223,9 +223,7 @@ body.web {
|
||||
.monaco-workbench [tabindex="-1"]:active,
|
||||
.monaco-workbench select:active,
|
||||
.monaco-workbench input[type="button"]:active,
|
||||
.monaco-workbench input[type="checkbox"]:active,
|
||||
.monaco-workbench .monaco-tree .monaco-tree-row
|
||||
.monaco-workbench .monaco-tree.focused.no-focused-item:active:before {
|
||||
.monaco-workbench input[type="checkbox"]:active {
|
||||
outline: 0 !important; /* fixes some flashing outlines from showing up when clicking */
|
||||
}
|
||||
|
||||
@@ -233,12 +231,6 @@ body.web {
|
||||
border-color: transparent; /* outline is a square, but border has a radius, so we avoid this glitch when focused (https://github.com/Microsoft/vscode/issues/26045) */
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-tree.focused .monaco-tree-row.focused [tabindex="0"]:focus {
|
||||
outline-width: 1px; /* higher contrast color for focusable elements in a row that shows focus feedback */
|
||||
outline-style: solid;
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-tree.focused.no-focused-item:focus:before,
|
||||
.monaco-workbench .monaco-list:not(.element-focused):focus:before {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
@@ -267,7 +259,6 @@ body.web {
|
||||
outline: 0 !important; /* outline is not going well with decoration */
|
||||
}
|
||||
|
||||
.monaco-workbench .monaco-tree.focused:focus,
|
||||
.monaco-workbench .monaco-list:focus {
|
||||
outline: 0 !important; /* tree indicates focus not via outline but through the focused item */
|
||||
}
|
||||
|
||||
@@ -40,6 +40,8 @@ import { getMenuBarVisibility } from 'vs/platform/windows/common/windows';
|
||||
import { isWeb } from 'vs/base/common/platform';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys';
|
||||
import { getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
|
||||
interface IPlaceholderViewlet {
|
||||
id: string;
|
||||
@@ -109,7 +111,8 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService
|
||||
@IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService);
|
||||
this.migrateFromOldCachedViewletsValue();
|
||||
@@ -395,13 +398,16 @@ export class ActivitybarPart extends Part implements IActivityBarService {
|
||||
cssClass: 'codicon-settings-gear'
|
||||
});
|
||||
|
||||
const profileAction = new ActivityAction({
|
||||
id: 'workbench.actions.accounts',
|
||||
name: nls.localize('accounts', "Accounts"),
|
||||
cssClass: 'codicon-account'
|
||||
});
|
||||
if (getUserDataSyncStore(this.productService, this.configurationService)) {
|
||||
const profileAction = new ActivityAction({
|
||||
id: 'workbench.actions.accounts',
|
||||
name: nls.localize('accounts', "Accounts"),
|
||||
cssClass: 'codicon-account'
|
||||
});
|
||||
|
||||
this.globalActivityActionBar.push(profileAction);
|
||||
}
|
||||
|
||||
this.globalActivityActionBar.push(profileAction);
|
||||
this.globalActivityActionBar.push(this.globalActivityAction);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,9 +11,8 @@ import * as strings from 'vs/base/common/strings';
|
||||
import { Emitter } from 'vs/base/common/event';
|
||||
import * as errors from 'vs/base/common/errors';
|
||||
import { ToolBar } from 'vs/base/browser/ui/toolbar/toolbar';
|
||||
import { IActionViewItem, ActionsOrientation } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IActionViewItem, ActionsOrientation, prepareActions } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar';
|
||||
import { prepareActions } from 'vs/workbench/browser/actions';
|
||||
import { IAction, WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions';
|
||||
import { Part, IPartOptions } from 'vs/workbench/browser/part';
|
||||
import { Composite, CompositeRegistry } from 'vs/workbench/browser/composite';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user