mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-16 10:58:30 -05:00
Merge from vscode bd0efff9e3f36d6b3e1045cee9887003af8034d7
This commit is contained in:
@@ -9,11 +9,13 @@ import { IConstructorSignature2, createDecorator, BrandedService, ServicesAccess
|
||||
import { IKeybindings, KeybindingsRegistry, IKeybindingRule } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ContextKeyExpr, IContextKeyService, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandService, CommandsRegistry, ICommandHandlerDescription } from 'vs/platform/commands/common/commands';
|
||||
import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
import { UriDto } from 'vs/base/common/types';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
|
||||
export interface ILocalizedString {
|
||||
value: string;
|
||||
@@ -159,33 +161,51 @@ export interface IMenuService {
|
||||
|
||||
export type ICommandsMap = Map<string, ICommandAction>;
|
||||
|
||||
export interface IMenuRegistryChangeEvent {
|
||||
has(id: MenuId): boolean;
|
||||
}
|
||||
|
||||
export interface IMenuRegistry {
|
||||
readonly onDidChangeMenu: Event<IMenuRegistryChangeEvent>;
|
||||
addCommands(newCommands: Iterable<ICommandAction>): IDisposable;
|
||||
addCommand(userCommand: ICommandAction): IDisposable;
|
||||
getCommand(id: string): ICommandAction | undefined;
|
||||
getCommands(): ICommandsMap;
|
||||
appendMenuItems(items: Iterable<{ id: MenuId, item: IMenuItem | ISubmenuItem }>): IDisposable;
|
||||
appendMenuItem(menu: MenuId, item: IMenuItem | ISubmenuItem): IDisposable;
|
||||
getMenuItems(loc: MenuId): Array<IMenuItem | ISubmenuItem>;
|
||||
readonly onDidChangeMenu: Event<MenuId>;
|
||||
}
|
||||
|
||||
export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
|
||||
|
||||
private readonly _commands = new Map<string, ICommandAction>();
|
||||
private readonly _menuItems = new Map<MenuId, Array<IMenuItem | ISubmenuItem>>();
|
||||
private readonly _onDidChangeMenu = new Emitter<MenuId>();
|
||||
private readonly _menuItems = new Map<MenuId, LinkedList<IMenuItem | ISubmenuItem>>();
|
||||
private readonly _onDidChangeMenu = new Emitter<IMenuRegistryChangeEvent>();
|
||||
|
||||
readonly onDidChangeMenu: Event<MenuId> = this._onDidChangeMenu.event;
|
||||
readonly onDidChangeMenu: Event<IMenuRegistryChangeEvent> = this._onDidChangeMenu.event;
|
||||
|
||||
addCommand(command: ICommandAction): IDisposable {
|
||||
this._commands.set(command.id, command);
|
||||
this._onDidChangeMenu.fire(MenuId.CommandPalette);
|
||||
return {
|
||||
dispose: () => {
|
||||
if (this._commands.delete(command.id)) {
|
||||
this._onDidChangeMenu.fire(MenuId.CommandPalette);
|
||||
}
|
||||
return this.addCommands(Iterable.single(command));
|
||||
}
|
||||
|
||||
private readonly _commandPaletteChangeEvent: IMenuRegistryChangeEvent = {
|
||||
has: id => id === MenuId.CommandPalette
|
||||
};
|
||||
|
||||
addCommands(commands: Iterable<ICommandAction>): IDisposable {
|
||||
for (const command of commands) {
|
||||
this._commands.set(command.id, command);
|
||||
}
|
||||
this._onDidChangeMenu.fire(this._commandPaletteChangeEvent);
|
||||
return toDisposable(() => {
|
||||
let didChange = false;
|
||||
for (const command of commands) {
|
||||
didChange = this._commands.delete(command.id) || didChange;
|
||||
}
|
||||
};
|
||||
if (didChange) {
|
||||
this._onDidChangeMenu.fire(this._commandPaletteChangeEvent);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getCommand(id: string): ICommandAction | undefined {
|
||||
@@ -199,28 +219,44 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
|
||||
}
|
||||
|
||||
appendMenuItem(id: MenuId, item: IMenuItem | ISubmenuItem): IDisposable {
|
||||
let array = this._menuItems.get(id);
|
||||
if (!array) {
|
||||
array = [item];
|
||||
this._menuItems.set(id, array);
|
||||
} else {
|
||||
array.push(item);
|
||||
}
|
||||
this._onDidChangeMenu.fire(id);
|
||||
return {
|
||||
dispose: () => {
|
||||
const idx = array!.indexOf(item);
|
||||
if (idx >= 0) {
|
||||
array!.splice(idx, 1);
|
||||
this._onDidChangeMenu.fire(id);
|
||||
}
|
||||
return this.appendMenuItems(Iterable.single({ id, item }));
|
||||
}
|
||||
|
||||
appendMenuItems(items: Iterable<{ id: MenuId, item: IMenuItem | ISubmenuItem }>): IDisposable {
|
||||
|
||||
const changedIds = new Set<MenuId>();
|
||||
const toRemove = new LinkedList<Function>();
|
||||
|
||||
for (const { id, item } of items) {
|
||||
let list = this._menuItems.get(id);
|
||||
if (!list) {
|
||||
list = new LinkedList();
|
||||
this._menuItems.set(id, list);
|
||||
}
|
||||
};
|
||||
toRemove.push(list.push(item));
|
||||
changedIds.add(id);
|
||||
}
|
||||
|
||||
this._onDidChangeMenu.fire(changedIds);
|
||||
|
||||
return toDisposable(() => {
|
||||
if (toRemove.size > 0) {
|
||||
for (let fn of toRemove) {
|
||||
fn();
|
||||
}
|
||||
this._onDidChangeMenu.fire(changedIds);
|
||||
toRemove.clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getMenuItems(id: MenuId): Array<IMenuItem | ISubmenuItem> {
|
||||
const result = (this._menuItems.get(id) || []).slice(0);
|
||||
|
||||
let result: Array<IMenuItem | ISubmenuItem>;
|
||||
if (this._menuItems.has(id)) {
|
||||
result = [...this._menuItems.get(id)!];
|
||||
} else {
|
||||
result = [];
|
||||
}
|
||||
if (id === MenuId.CommandPalette) {
|
||||
// CommandPalette is special because it shows
|
||||
// all commands by default
|
||||
@@ -232,12 +268,12 @@ export const MenuRegistry: IMenuRegistry = new class implements IMenuRegistry {
|
||||
private _appendImplicitItems(result: Array<IMenuItem | ISubmenuItem>) {
|
||||
const set = new Set<string>();
|
||||
|
||||
const temp = result.filter(item => { return isIMenuItem(item); }) as IMenuItem[];
|
||||
|
||||
for (const { command, alt } of temp) {
|
||||
set.add(command.id);
|
||||
if (alt) {
|
||||
set.add(alt.id);
|
||||
for (const item of result) {
|
||||
if (isIMenuItem(item)) {
|
||||
set.add(item.command.id);
|
||||
if (item.alt) {
|
||||
set.add(item.alt.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
this._commands.forEach((command, id) => {
|
||||
@@ -441,14 +477,13 @@ export function registerAction2(ctor: { new(): Action2 }): IDisposable {
|
||||
|
||||
// menu
|
||||
if (Array.isArray(menu)) {
|
||||
for (let item of menu) {
|
||||
disposables.add(MenuRegistry.appendMenuItem(item.id, { command: { ...command }, ...item }));
|
||||
}
|
||||
disposables.add(MenuRegistry.appendMenuItems(menu.map(item => ({ id: item.id, item: { command, ...item } }))));
|
||||
|
||||
} else if (menu) {
|
||||
disposables.add(MenuRegistry.appendMenuItem(menu.id, { command: { ...command }, ...menu }));
|
||||
disposables.add(MenuRegistry.appendMenuItem(menu.id, { command, ...menu }));
|
||||
}
|
||||
if (f1) {
|
||||
disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: command }));
|
||||
disposables.add(MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command }));
|
||||
}
|
||||
|
||||
// keybinding
|
||||
|
||||
@@ -45,7 +45,7 @@ class Menu implements IMenu {
|
||||
// rebuild this menu whenever the menu registry reports an
|
||||
// event for this MenuId
|
||||
this._dispoables.add(Event.debounce(
|
||||
Event.filter(MenuRegistry.onDidChangeMenu, menuId => menuId === this._id),
|
||||
Event.filter(MenuRegistry.onDidChangeMenu, set => set.has(this._id)),
|
||||
() => { },
|
||||
50
|
||||
)(this._build, this));
|
||||
|
||||
@@ -9,7 +9,6 @@ import { ServicesAccessor, createDecorator } from 'vs/platform/instantiation/com
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { LinkedList } from 'vs/base/common/linkedList';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { keys } from 'vs/base/common/map';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
|
||||
export const ICommandService = createDecorator<ICommandService>('commandService');
|
||||
@@ -130,7 +129,7 @@ export const CommandsRegistry: ICommandRegistry = new class implements ICommandR
|
||||
|
||||
getCommands(): ICommandsMap {
|
||||
const result = new Map<string, ICommand>();
|
||||
for (const key of keys(this._commands)) {
|
||||
for (const key of this._commands.keys()) {
|
||||
const command = this.getCommand(key);
|
||||
if (command) {
|
||||
result.set(key, command);
|
||||
|
||||
@@ -231,7 +231,7 @@ export class ConfigurationModelParser {
|
||||
}
|
||||
|
||||
public parseContent(content: string | null | undefined): void {
|
||||
if (content) {
|
||||
if (!types.isUndefinedOrNull(content)) {
|
||||
const raw = this.doParseContent(content);
|
||||
this.parseRaw(raw);
|
||||
}
|
||||
@@ -677,7 +677,7 @@ export class Configuration {
|
||||
overrides: this._workspaceConfiguration.overrides,
|
||||
keys: this._workspaceConfiguration.keys
|
||||
},
|
||||
folders: this._folderConfigurations.keys().reduce<[UriComponents, IConfigurationModel][]>((result, folder) => {
|
||||
folders: [...this._folderConfigurations.keys()].reduce<[UriComponents, IConfigurationModel][]>((result, folder) => {
|
||||
const { contents, overrides, keys } = this._folderConfigurations.get(folder)!;
|
||||
result.push([folder, { contents, overrides, keys }]);
|
||||
return result;
|
||||
|
||||
@@ -21,14 +21,13 @@ import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { isEqual, joinPath } from 'vs/base/common/resources';
|
||||
import { VSBuffer, VSBufferReadable, streamToBufferReadableStream, VSBufferReadableStream, bufferToReadable, bufferToStream, streamToBuffer } from 'vs/base/common/buffer';
|
||||
import { find } from 'vs/base/common/arrays';
|
||||
|
||||
function getByName(root: IFileStat, name: string): IFileStat | undefined {
|
||||
if (root.children === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return find(root.children, child => child.name === name);
|
||||
return root.children.find(child => child.name === name);
|
||||
}
|
||||
|
||||
function toLineByLineReadable(content: string): VSBufferReadable {
|
||||
@@ -442,7 +441,7 @@ suite('Disk File Service', function () {
|
||||
assert.equal(resolved.isDirectory, true);
|
||||
assert.equal(resolved.children!.length, 9);
|
||||
|
||||
const resolvedLink = resolved.children?.filter(child => child.name === 'bar' && child.isSymbolicLink)[0];
|
||||
const resolvedLink = resolved.children?.find(child => child.name === 'bar' && child.isSymbolicLink);
|
||||
assert.ok(resolvedLink);
|
||||
|
||||
assert.ok(!resolvedLink?.isDirectory);
|
||||
|
||||
@@ -3,86 +3,78 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
import { forEach } from 'vs/base/common/collections';
|
||||
export class Node<T> {
|
||||
|
||||
export interface Node<T> {
|
||||
data: T;
|
||||
incoming: { [key: string]: Node<T> };
|
||||
outgoing: { [key: string]: Node<T> };
|
||||
}
|
||||
readonly data: T;
|
||||
readonly incoming = new Map<string, Node<T>>();
|
||||
readonly outgoing = new Map<string, Node<T>>();
|
||||
|
||||
function newNode<T>(data: T): Node<T> {
|
||||
return {
|
||||
data: data,
|
||||
incoming: Object.create(null),
|
||||
outgoing: Object.create(null)
|
||||
};
|
||||
constructor(data: T) {
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
|
||||
export class Graph<T> {
|
||||
|
||||
private _nodes: { [key: string]: Node<T> } = Object.create(null);
|
||||
private readonly _nodes = new Map<string, Node<T>>();
|
||||
|
||||
constructor(private _hashFn: (element: T) => string) {
|
||||
constructor(private readonly _hashFn: (element: T) => string) {
|
||||
// empty
|
||||
}
|
||||
|
||||
roots(): Node<T>[] {
|
||||
const ret: Node<T>[] = [];
|
||||
forEach(this._nodes, entry => {
|
||||
if (isEmptyObject(entry.value.outgoing)) {
|
||||
ret.push(entry.value);
|
||||
for (let node of this._nodes.values()) {
|
||||
if (node.outgoing.size === 0) {
|
||||
ret.push(node);
|
||||
}
|
||||
});
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
insertEdge(from: T, to: T): void {
|
||||
const fromNode = this.lookupOrInsertNode(from),
|
||||
toNode = this.lookupOrInsertNode(to);
|
||||
const fromNode = this.lookupOrInsertNode(from);
|
||||
const toNode = this.lookupOrInsertNode(to);
|
||||
|
||||
fromNode.outgoing[this._hashFn(to)] = toNode;
|
||||
toNode.incoming[this._hashFn(from)] = fromNode;
|
||||
fromNode.outgoing.set(this._hashFn(to), toNode);
|
||||
toNode.incoming.set(this._hashFn(from), fromNode);
|
||||
}
|
||||
|
||||
removeNode(data: T): void {
|
||||
const key = this._hashFn(data);
|
||||
delete this._nodes[key];
|
||||
forEach(this._nodes, (entry) => {
|
||||
delete entry.value.outgoing[key];
|
||||
delete entry.value.incoming[key];
|
||||
});
|
||||
this._nodes.delete(key);
|
||||
for (let node of this._nodes.values()) {
|
||||
node.outgoing.delete(key);
|
||||
node.incoming.delete(key);
|
||||
}
|
||||
}
|
||||
|
||||
lookupOrInsertNode(data: T): Node<T> {
|
||||
const key = this._hashFn(data);
|
||||
let node = this._nodes[key];
|
||||
let node = this._nodes.get(key);
|
||||
|
||||
if (!node) {
|
||||
node = newNode(data);
|
||||
this._nodes[key] = node;
|
||||
node = new Node(data);
|
||||
this._nodes.set(key, node);
|
||||
}
|
||||
|
||||
return node;
|
||||
}
|
||||
|
||||
lookup(data: T): Node<T> {
|
||||
return this._nodes[this._hashFn(data)];
|
||||
lookup(data: T): Node<T> | undefined {
|
||||
return this._nodes.get(this._hashFn(data));
|
||||
}
|
||||
|
||||
isEmpty(): boolean {
|
||||
for (const _key in this._nodes) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
return this._nodes.size === 0;
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
let data: string[] = [];
|
||||
forEach(this._nodes, entry => {
|
||||
data.push(`${entry.key}, (incoming)[${Object.keys(entry.value.incoming).join(', ')}], (outgoing)[${Object.keys(entry.value.outgoing).join(',')}]`);
|
||||
});
|
||||
for (let [key, value] of this._nodes) {
|
||||
data.push(`${key}, (incoming)[${[...value.incoming.keys()].join(', ')}], (outgoing)[${[...value.outgoing.keys()].join(',')}]`);
|
||||
|
||||
}
|
||||
return data.join('\n');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ suite('Graph', () => {
|
||||
test('inserts nodes when not there yet', function () {
|
||||
assert.deepEqual(graph.lookup('ddd'), null);
|
||||
assert.deepEqual(graph.lookupOrInsertNode('ddd').data, 'ddd');
|
||||
assert.deepEqual(graph.lookup('ddd').data, 'ddd');
|
||||
assert.deepEqual(graph.lookup('ddd')!.data, 'ddd');
|
||||
});
|
||||
|
||||
test('can remove nodes and get length', function () {
|
||||
|
||||
@@ -658,6 +658,7 @@ abstract class ResourceNavigator<T> extends Disposable {
|
||||
onDidChangeFocus: Event<{ browserEvent?: UIEvent }>,
|
||||
onDidChangeSelection: Event<{ browserEvent?: UIEvent }>,
|
||||
onDidOpen: Event<{ browserEvent?: UIEvent }>,
|
||||
readonly openOnSingleClick?: boolean
|
||||
},
|
||||
options?: IResourceNavigatorOptions
|
||||
) {
|
||||
@@ -711,7 +712,7 @@ abstract class ResourceNavigator<T> extends Disposable {
|
||||
!!(<SelectionKeyboardEvent>browserEvent).preserveFocus :
|
||||
!isDoubleClick;
|
||||
|
||||
if (this.options.openOnSingleClick || isDoubleClick || isKeyboardEvent) {
|
||||
if (this.treeOrList.openOnSingleClick || isDoubleClick || isKeyboardEvent) {
|
||||
const sideBySide = browserEvent instanceof MouseEvent && (browserEvent.ctrlKey || browserEvent.metaKey || browserEvent.altKey);
|
||||
this.open(preserveFocus, isDoubleClick || isMiddleClick, sideBySide, browserEvent);
|
||||
}
|
||||
@@ -738,8 +739,8 @@ export class ListResourceNavigator<T> extends ResourceNavigator<number> {
|
||||
}
|
||||
|
||||
export class TreeResourceNavigator<T, TFilterData> extends ResourceNavigator<T> {
|
||||
constructor(tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchCompressibleObjectTree<T, TFilterData> | WorkbenchDataTree<any, T, TFilterData> | WorkbenchAsyncDataTree<any, T, TFilterData> | WorkbenchCompressibleAsyncDataTree<any, T, TFilterData>, options?: IResourceNavigatorOptions) {
|
||||
super(tree, { openOnSingleClick: tree.openOnSingleClick, ...(options || {}) });
|
||||
constructor(tree: WorkbenchObjectTree<T, TFilterData> | WorkbenchCompressibleObjectTree<T, TFilterData> | WorkbenchDataTree<any, T, TFilterData> | WorkbenchAsyncDataTree<any, T, TFilterData> | WorkbenchCompressibleAsyncDataTree<any, T, TFilterData>, options: IResourceNavigatorOptions = {}) {
|
||||
super(tree, options);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,40 +6,64 @@
|
||||
import { isFalsyOrEmpty, isNonEmptyArray } from 'vs/base/common/arrays';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isEmptyObject } from 'vs/base/common/types';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IMarkerService, IMarkerData, IResourceMarker, IMarker, MarkerStatistics, MarkerSeverity } from './markers';
|
||||
import { ResourceMap } from 'vs/base/common/map';
|
||||
import { Iterable } from 'vs/base/common/iterator';
|
||||
|
||||
interface MapMap<V> {
|
||||
[key: string]: { [key: string]: V };
|
||||
}
|
||||
class DoubleResourceMap<V>{
|
||||
|
||||
namespace MapMap {
|
||||
private _byResource = new ResourceMap<Map<string, V>>();
|
||||
private _byOwner = new Map<string, ResourceMap<V>>();
|
||||
|
||||
export function get<V>(map: MapMap<V>, key1: string, key2: string): V | undefined {
|
||||
if (map[key1]) {
|
||||
return map[key1][key2];
|
||||
set(resource: URI, owner: string, value: V) {
|
||||
let ownerMap = this._byResource.get(resource);
|
||||
if (!ownerMap) {
|
||||
ownerMap = new Map();
|
||||
this._byResource.set(resource, ownerMap);
|
||||
}
|
||||
return undefined;
|
||||
ownerMap.set(owner, value);
|
||||
|
||||
let resourceMap = this._byOwner.get(owner);
|
||||
if (!resourceMap) {
|
||||
resourceMap = new ResourceMap();
|
||||
this._byOwner.set(owner, resourceMap);
|
||||
}
|
||||
resourceMap.set(resource, value);
|
||||
}
|
||||
|
||||
export function set<V>(map: MapMap<V>, key1: string, key2: string, value: V): void {
|
||||
if (!map[key1]) {
|
||||
map[key1] = Object.create(null);
|
||||
}
|
||||
map[key1][key2] = value;
|
||||
get(resource: URI, owner: string): V | undefined {
|
||||
let ownerMap = this._byResource.get(resource);
|
||||
return ownerMap?.get(owner);
|
||||
}
|
||||
|
||||
export function remove(map: MapMap<any>, key1: string, key2: string): boolean {
|
||||
if (map[key1] && map[key1][key2]) {
|
||||
delete map[key1][key2];
|
||||
if (isEmptyObject(map[key1])) {
|
||||
delete map[key1];
|
||||
}
|
||||
return true;
|
||||
delete(resource: URI, owner: string): boolean {
|
||||
let removedA = false;
|
||||
let removedB = false;
|
||||
let ownerMap = this._byResource.get(resource);
|
||||
if (ownerMap) {
|
||||
removedA = ownerMap.delete(owner);
|
||||
}
|
||||
return false;
|
||||
let resourceMap = this._byOwner.get(owner);
|
||||
if (resourceMap) {
|
||||
removedB = resourceMap.delete(resource);
|
||||
}
|
||||
if (removedA !== removedB) {
|
||||
throw new Error('illegal state');
|
||||
}
|
||||
return removedA && removedB;
|
||||
}
|
||||
|
||||
values(key?: URI | string): Iterable<V> {
|
||||
if (typeof key === 'string') {
|
||||
return this._byOwner.get(key)?.values() ?? Iterable.empty();
|
||||
}
|
||||
if (URI.isUri(key)) {
|
||||
return this._byResource.get(key)?.values() ?? Iterable.empty();
|
||||
}
|
||||
|
||||
return Iterable.map(Iterable.concat(...this._byOwner.values()), map => map[1]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,9 +74,9 @@ class MarkerStats implements MarkerStatistics {
|
||||
warnings: number = 0;
|
||||
unknowns: number = 0;
|
||||
|
||||
private _data?: { [resource: string]: MarkerStatistics } = Object.create(null);
|
||||
private _service: IMarkerService;
|
||||
private _subscription: IDisposable;
|
||||
private readonly _data = new ResourceMap<MarkerStatistics>();
|
||||
private readonly _service: IMarkerService;
|
||||
private readonly _subscription: IDisposable;
|
||||
|
||||
constructor(service: IMarkerService) {
|
||||
this._service = service;
|
||||
@@ -61,23 +85,17 @@ class MarkerStats implements MarkerStatistics {
|
||||
|
||||
dispose(): void {
|
||||
this._subscription.dispose();
|
||||
this._data = undefined;
|
||||
}
|
||||
|
||||
private _update(resources: readonly URI[]): void {
|
||||
if (!this._data) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const resource of resources) {
|
||||
const key = resource.toString();
|
||||
const oldStats = this._data[key];
|
||||
const oldStats = this._data.get(resource);
|
||||
if (oldStats) {
|
||||
this._substract(oldStats);
|
||||
}
|
||||
const newStats = this._resourceStats(resource);
|
||||
this._add(newStats);
|
||||
this._data[key] = newStats;
|
||||
this._data.set(resource, newStats);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -124,10 +142,10 @@ export class MarkerService implements IMarkerService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private readonly _onMarkerChanged = new Emitter<readonly URI[]>();
|
||||
private _onMarkerChangedEvent: Event<readonly URI[]> = Event.debounce(this._onMarkerChanged.event, MarkerService._debouncer, 0);
|
||||
private _byResource: MapMap<IMarker[]> = Object.create(null);
|
||||
private _byOwner: MapMap<IMarker[]> = Object.create(null);
|
||||
private _stats: MarkerStats;
|
||||
readonly onMarkerChanged: Event<readonly URI[]> = Event.debounce(this._onMarkerChanged.event, MarkerService._debouncer, 0);
|
||||
|
||||
private readonly _data = new DoubleResourceMap<IMarker[]>();
|
||||
private readonly _stats: MarkerStats;
|
||||
|
||||
constructor() {
|
||||
this._stats = new MarkerStats(this);
|
||||
@@ -137,10 +155,6 @@ export class MarkerService implements IMarkerService {
|
||||
this._stats.dispose();
|
||||
}
|
||||
|
||||
get onMarkerChanged(): Event<readonly URI[]> {
|
||||
return this._onMarkerChangedEvent;
|
||||
}
|
||||
|
||||
getStatistics(): MarkerStatistics {
|
||||
return this._stats;
|
||||
}
|
||||
@@ -155,12 +169,8 @@ export class MarkerService implements IMarkerService {
|
||||
|
||||
if (isFalsyOrEmpty(markerData)) {
|
||||
// remove marker for this (owner,resource)-tuple
|
||||
const a = MapMap.remove(this._byResource, resource.toString(), owner);
|
||||
const b = MapMap.remove(this._byOwner, owner, resource.toString());
|
||||
if (a !== b) {
|
||||
throw new Error('invalid marker service state');
|
||||
}
|
||||
if (a && b) {
|
||||
const removed = this._data.delete(resource, owner);
|
||||
if (removed) {
|
||||
this._onMarkerChanged.fire([resource]);
|
||||
}
|
||||
|
||||
@@ -173,8 +183,7 @@ export class MarkerService implements IMarkerService {
|
||||
markers.push(marker);
|
||||
}
|
||||
}
|
||||
MapMap.set(this._byResource, resource.toString(), owner, markers);
|
||||
MapMap.set(this._byOwner, owner, resource.toString(), markers);
|
||||
this._data.set(resource, owner, markers);
|
||||
this._onMarkerChanged.fire([resource]);
|
||||
}
|
||||
}
|
||||
@@ -216,21 +225,15 @@ export class MarkerService implements IMarkerService {
|
||||
|
||||
changeAll(owner: string, data: IResourceMarker[]): void {
|
||||
const changes: URI[] = [];
|
||||
const map = this._byOwner[owner];
|
||||
|
||||
// remove old marker
|
||||
if (map) {
|
||||
delete this._byOwner[owner];
|
||||
for (const resource in map) {
|
||||
const entry = MapMap.get(this._byResource, resource, owner);
|
||||
if (entry) {
|
||||
// remeber what we remove
|
||||
const [first] = entry;
|
||||
if (first) {
|
||||
changes.push(first.resource);
|
||||
}
|
||||
// actual remove
|
||||
MapMap.remove(this._byResource, resource, owner);
|
||||
const existing = this._data.values(owner);
|
||||
if (existing) {
|
||||
for (let data of existing) {
|
||||
const first = Iterable.first(data);
|
||||
if (first) {
|
||||
changes.push(first.resource);
|
||||
this._data.delete(first.resource, owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -239,16 +242,16 @@ export class MarkerService implements IMarkerService {
|
||||
if (isNonEmptyArray(data)) {
|
||||
|
||||
// group by resource
|
||||
const groups: { [resource: string]: IMarker[] } = Object.create(null);
|
||||
const groups = new ResourceMap<IMarker[]>();
|
||||
for (const { resource, marker: markerData } of data) {
|
||||
const marker = MarkerService._toMarker(owner, resource, markerData);
|
||||
if (!marker) {
|
||||
// filter bad markers
|
||||
continue;
|
||||
}
|
||||
const array = groups[resource.toString()];
|
||||
const array = groups.get(resource);
|
||||
if (!array) {
|
||||
groups[resource.toString()] = [marker];
|
||||
groups.set(resource, [marker]);
|
||||
changes.push(resource);
|
||||
} else {
|
||||
array.push(marker);
|
||||
@@ -256,9 +259,8 @@ export class MarkerService implements IMarkerService {
|
||||
}
|
||||
|
||||
// insert all
|
||||
for (const resource in groups) {
|
||||
MapMap.set(this._byResource, resource, owner, groups[resource]);
|
||||
MapMap.set(this._byOwner, owner, resource, groups[resource]);
|
||||
for (const [resource, value] of groups) {
|
||||
this._data.set(resource, owner, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -277,7 +279,7 @@ export class MarkerService implements IMarkerService {
|
||||
|
||||
if (owner && resource) {
|
||||
// exactly one owner AND resource
|
||||
const data = MapMap.get(this._byResource, resource.toString(), owner);
|
||||
const data = this._data.get(resource, owner);
|
||||
if (!data) {
|
||||
return [];
|
||||
} else {
|
||||
@@ -296,14 +298,12 @@ export class MarkerService implements IMarkerService {
|
||||
} else if (!owner && !resource) {
|
||||
// all
|
||||
const result: IMarker[] = [];
|
||||
for (const key1 in this._byResource) {
|
||||
for (const key2 in this._byResource[key1]) {
|
||||
for (const data of this._byResource[key1][key2]) {
|
||||
if (MarkerService._accept(data, severities)) {
|
||||
const newLen = result.push(data);
|
||||
if (take > 0 && newLen === take) {
|
||||
return result;
|
||||
}
|
||||
for (let markers of this._data.values()) {
|
||||
for (let data of markers) {
|
||||
if (MarkerService._accept(data, severities)) {
|
||||
const newLen = result.push(data);
|
||||
if (take > 0 && newLen === take) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -312,17 +312,10 @@ export class MarkerService implements IMarkerService {
|
||||
|
||||
} else {
|
||||
// of one resource OR owner
|
||||
const map: { [key: string]: IMarker[] } | undefined = owner
|
||||
? this._byOwner[owner]
|
||||
: resource ? this._byResource[resource.toString()] : undefined;
|
||||
|
||||
if (!map) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const iterable = this._data.values(resource ?? owner!);
|
||||
const result: IMarker[] = [];
|
||||
for (const key in map) {
|
||||
for (const data of map[key]) {
|
||||
for (const markers of iterable) {
|
||||
for (const data of markers) {
|
||||
if (MarkerService._accept(data, severities)) {
|
||||
const newLen = result.push(data);
|
||||
if (take > 0 && newLen === take) {
|
||||
@@ -341,16 +334,16 @@ export class MarkerService implements IMarkerService {
|
||||
|
||||
// --- event debounce logic
|
||||
|
||||
private static _dedupeMap: { [uri: string]: boolean };
|
||||
private static _dedupeMap: ResourceMap<true>;
|
||||
|
||||
private static _debouncer(last: URI[] | undefined, event: readonly URI[]): URI[] {
|
||||
if (!last) {
|
||||
MarkerService._dedupeMap = Object.create(null);
|
||||
MarkerService._dedupeMap = new ResourceMap();
|
||||
last = [];
|
||||
}
|
||||
for (const uri of event) {
|
||||
if (MarkerService._dedupeMap[uri.toString()] === undefined) {
|
||||
MarkerService._dedupeMap[uri.toString()] = true;
|
||||
if (!MarkerService._dedupeMap.has(uri)) {
|
||||
MarkerService._dedupeMap.set(uri, true);
|
||||
last.push(uri);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -703,7 +703,8 @@ export class Menubar {
|
||||
}
|
||||
|
||||
// DevTools focused
|
||||
if (activeWindow.webContents.isDevToolsFocused()) {
|
||||
if (activeWindow.webContents.isDevToolsFocused() &&
|
||||
activeWindow.webContents.devToolsWebContents) {
|
||||
return contextSpecificHandlers.inDevTools(activeWindow.webContents.devToolsWebContents);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,6 +56,11 @@ export interface IIconRegistry {
|
||||
*/
|
||||
getIcons(): IconContribution[];
|
||||
|
||||
/**
|
||||
* Get the icon for the given id
|
||||
*/
|
||||
getIcon(id: string): IconContribution | undefined;
|
||||
|
||||
/**
|
||||
* JSON schema for an object to assign icon values to one of the color contributions.
|
||||
*/
|
||||
@@ -130,6 +135,10 @@ class IconRegistry implements IIconRegistry {
|
||||
return Object.keys(this.iconsById).map(id => this.iconsById[id]);
|
||||
}
|
||||
|
||||
public getIcon(id: string): IconContribution | undefined {
|
||||
return this.iconsById[id];
|
||||
}
|
||||
|
||||
public getIconSchema(): IJSONSchema {
|
||||
return this.iconSchema;
|
||||
}
|
||||
@@ -139,16 +148,34 @@ class IconRegistry implements IIconRegistry {
|
||||
}
|
||||
|
||||
public toString() {
|
||||
let sorter = (a: string, b: string) => {
|
||||
let cat1 = a.indexOf('.') === -1 ? 0 : 1;
|
||||
let cat2 = b.indexOf('.') === -1 ? 0 : 1;
|
||||
if (cat1 !== cat2) {
|
||||
return cat1 - cat2;
|
||||
const sorter = (i1: IconContribution, i2: IconContribution) => {
|
||||
const isThemeIcon1 = ThemeIcon.isThemeIcon(i1.defaults);
|
||||
const isThemeIcon2 = ThemeIcon.isThemeIcon(i2.defaults);
|
||||
if (isThemeIcon1 !== isThemeIcon2) {
|
||||
return isThemeIcon1 ? -1 : 1;
|
||||
}
|
||||
return a.localeCompare(b);
|
||||
return i1.id.localeCompare(i2.id);
|
||||
};
|
||||
const classNames = (i: IconContribution) => {
|
||||
while (ThemeIcon.isThemeIcon(i.defaults)) {
|
||||
i = this.iconsById[i.defaults.id];
|
||||
}
|
||||
return `codicon codicon-${i ? i.id : ''}`;
|
||||
};
|
||||
|
||||
return Object.keys(this.iconsById).sort(sorter).map(k => `- \`${k}\`: ${this.iconsById[k].description}`).join('\n');
|
||||
let reference = [];
|
||||
let docCss = [];
|
||||
|
||||
const contributions = Object.keys(this.iconsById).map(key => this.iconsById[key]);
|
||||
|
||||
for (const i of contributions.sort(sorter)) {
|
||||
reference.push(`|<i class="${classNames(i)}"></i>|${i.id}|${ThemeIcon.isThemeIcon(i.defaults) ? i.defaults.id : ''}|`);
|
||||
|
||||
if (!ThemeIcon.isThemeIcon((i.defaults))) {
|
||||
docCss.push(`.codicon-${i.id}:before { content: "${i.defaults.character}" }`);
|
||||
}
|
||||
}
|
||||
return reference.join('\n') + '\n\n' + docCss.join('\n');
|
||||
}
|
||||
|
||||
}
|
||||
@@ -186,4 +213,4 @@ iconRegistry.onDidChangeSchema(() => {
|
||||
});
|
||||
|
||||
|
||||
// setTimeout(_ => console.log(colorRegistry.toString()), 5000);
|
||||
//setTimeout(_ => console.log(iconRegistry.toString()), 5000);
|
||||
|
||||
@@ -167,10 +167,16 @@ export abstract class AbstractSynchroniser extends Disposable {
|
||||
if (e instanceof UserDataSyncError) {
|
||||
switch (e.code) {
|
||||
case UserDataSyncErrorCode.RemotePreconditionFailed:
|
||||
// Rejected as there is a new remote version. Syncing again,
|
||||
// Rejected as there is a new remote version. Syncing again...
|
||||
this.logService.info(`${this.syncResourceLogLabel}: Failed to synchronize as there is a new remote version available. Synchronizing again...`);
|
||||
|
||||
// Avoid cache and get latest remote user data - https://github.com/microsoft/vscode/issues/90624
|
||||
remoteUserData = await this.getRemoteUserData(null);
|
||||
|
||||
// Get the latest last sync user data. Because multiples parallel syncs (in Web) could share same last sync data
|
||||
// and one of them successfully updated remote and last sync state.
|
||||
lastSyncUserData = await this.getLastSyncUserData();
|
||||
|
||||
return this.doSync(remoteUserData, lastSyncUserData);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,7 +318,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
if (!currentWindowsState.lastActiveWindow) {
|
||||
let activeWindow = this.getLastActiveWindow();
|
||||
if (!activeWindow || activeWindow.isExtensionDevelopmentHost) {
|
||||
activeWindow = WindowsMainService.WINDOWS.filter(window => !window.isExtensionDevelopmentHost)[0];
|
||||
activeWindow = WindowsMainService.WINDOWS.find(window => !window.isExtensionDevelopmentHost);
|
||||
}
|
||||
|
||||
if (activeWindow) {
|
||||
@@ -327,7 +327,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
}
|
||||
|
||||
// 2.) Find extension host window
|
||||
const extensionHostWindow = WindowsMainService.WINDOWS.filter(window => window.isExtensionDevelopmentHost && !window.isExtensionTestHost)[0];
|
||||
const extensionHostWindow = WindowsMainService.WINDOWS.find(window => window.isExtensionDevelopmentHost && !window.isExtensionTestHost);
|
||||
if (extensionHostWindow) {
|
||||
currentWindowsState.lastPluginDevelopmentHostWindow = this.toWindowState(extensionHostWindow);
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ function findWindowOnFilePath<W extends IWindowContext>(windows: W[], fileUri: U
|
||||
export function getLastActiveWindow<W extends IWindowContext>(windows: W[]): W | undefined {
|
||||
const lastFocusedDate = Math.max.apply(Math, windows.map(window => window.lastFocusTime));
|
||||
|
||||
return windows.filter(window => window.lastFocusTime === lastFocusedDate)[0];
|
||||
return windows.find(window => window.lastFocusTime === lastFocusedDate);
|
||||
}
|
||||
|
||||
export function findWindowOnWorkspace<W extends IWindowContext>(windows: W[], workspace: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier)): W | null {
|
||||
|
||||
Reference in New Issue
Block a user