Merge from vscode 2cd495805cf99b31b6926f08ff4348124b2cf73d

This commit is contained in:
ADS Merger
2020-06-30 04:40:21 +00:00
committed by AzureDataStudio
parent a8a7559229
commit 1388493cc1
602 changed files with 16375 additions and 12940 deletions

View File

@@ -820,6 +820,7 @@ export function isHTMLElement(o: any): o is HTMLElement {
export const EventType = {
// Mouse
CLICK: 'click',
AUXCLICK: 'auxclick',
DBLCLICK: 'dblclick',
MOUSE_UP: 'mouseup',
MOUSE_DOWN: 'mousedown',

View File

@@ -19,4 +19,6 @@
.monaco-count-badge.long {
padding: 2px 3px;
border-radius: 2px;
min-height: auto;
line-height: normal;
}

View File

@@ -55,7 +55,7 @@
padding: 0 10px;
}
.monaco-dialog-box .dialog-message-row > .codicon {
.monaco-dialog-box .dialog-message-row > .dialog-icon.codicon {
flex: 0 0 48px;
height: 48px;
align-self: baseline;

View File

@@ -11,7 +11,7 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { IContextViewProvider, IAnchor, AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { IMenuOptions } from 'vs/base/browser/ui/menu/menu';
import { ResolvedKeybinding, KeyCode } from 'vs/base/common/keyCodes';
import { EventHelper, EventType, removeClass, addClass, append, $, addDisposableListener, addClasses } from 'vs/base/browser/dom';
import { EventHelper, EventType, removeClass, addClass, append, $, addDisposableListener, addClasses, DOMEvent } from 'vs/base/browser/dom';
import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { Emitter } from 'vs/base/common/event';
@@ -122,7 +122,7 @@ export class BaseDropdown extends ActionRunner {
return !!this.visible;
}
protected onEvent(e: Event, activeElement: HTMLElement): void {
protected onEvent(e: DOMEvent, activeElement: HTMLElement): void {
this.hide();
}
@@ -294,6 +294,9 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
private anchorAlignmentProvider: (() => AnchorAlignment) | undefined;
private menuAsChild?: boolean;
private _onDidChangeVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeVisibility = this._onDidChangeVisibility.event;
constructor(action: IAction, menuActions: ReadonlyArray<IAction>, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean);
constructor(action: IAction, actionProvider: IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean);
constructor(action: IAction, menuActionsOrProvider: ReadonlyArray<IAction> | IActionProvider, contextMenuProvider: IContextMenuProvider, actionViewItemProvider: IActionViewItemProvider | undefined, actionRunner: IActionRunner, keybindings: ((action: IAction) => ResolvedKeybinding | undefined) | undefined, clazz: string | undefined, anchorAlignmentProvider?: () => AnchorAlignment, menuAsChild?: boolean) {
@@ -339,7 +342,10 @@ export class DropdownMenuActionViewItem extends BaseActionViewItem {
}
this.dropdownMenu = this._register(new DropdownMenu(container, options));
this._register(this.dropdownMenu.onDidChangeVisibility(visible => this.element?.setAttribute('aria-expanded', `${visible}`)));
this._register(this.dropdownMenu.onDidChangeVisibility(visible => {
this.element?.setAttribute('aria-expanded', `${visible}`);
this._onDidChangeVisibility.fire(visible);
}));
this.dropdownMenu.menuOptions = {
actionViewItemProvider: this.actionViewItemProvider,

View File

@@ -10,7 +10,7 @@ import { tail2 as tail, equals } from 'vs/base/common/arrays';
import { orthogonal, IView as IGridViewView, GridView, Sizing as GridViewSizing, Box, IGridViewStyles, IViewSize, IGridViewOptions, IBoundarySashes } from './gridview';
import { Event } from 'vs/base/common/event';
export { Orientation, Sizing as GridViewSizing, IViewSize, orthogonal, LayoutPriority } from './gridview';
export { Orientation, IViewSize, orthogonal, LayoutPriority } from './gridview';
export const enum Direction {
Up,

View File

@@ -376,6 +376,10 @@ export class ListView<T> implements ISpliceable<T>, IDisposable {
}
updateElementHeight(index: number, size: number, anchorIndex: number | null): void {
if (index < 0 || index >= this.items.length) {
return;
}
if (this.items[index].size === size) {
return;
}

View File

@@ -225,10 +225,26 @@ class TraitSpliceable<T> implements ISpliceable<T> {
}
}
function isInputElement(e: HTMLElement): boolean {
export function isInputElement(e: HTMLElement): boolean {
return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA';
}
export function isMonacoEditor(e: HTMLElement): boolean {
if (DOM.hasClass(e, 'monaco-editor')) {
return true;
}
if (DOM.hasClass(e, 'monaco-list')) {
return false;
}
if (!e.parentElement) {
return false;
}
return isMonacoEditor(e.parentElement);
}
class KeyboardController<T> implements IDisposable {
private readonly disposables = new DisposableStore();
@@ -572,12 +588,20 @@ export class MouseController<T> implements IDisposable {
}
private onMouseDown(e: IListMouseEvent<T> | IListTouchEvent<T>): void {
if (isMonacoEditor(e.browserEvent.target as HTMLElement)) {
return;
}
if (document.activeElement !== e.browserEvent.target) {
this.list.domFocus();
}
}
private onContextMenu(e: IListContextMenuEvent<T>): void {
if (isMonacoEditor(e.browserEvent.target as HTMLElement)) {
return;
}
const focus = typeof e.index === 'undefined' ? [] : [e.index];
this.list.setFocus(focus, e.browserEvent);
}
@@ -587,7 +611,7 @@ export class MouseController<T> implements IDisposable {
return;
}
if (isInputElement(e.browserEvent.target as HTMLElement)) {
if (isInputElement(e.browserEvent.target as HTMLElement) || isMonacoEditor(e.browserEvent.target as HTMLElement)) {
return;
}
@@ -621,7 +645,7 @@ export class MouseController<T> implements IDisposable {
}
protected onDoubleClick(e: IListMouseEvent<T>): void {
if (isInputElement(e.browserEvent.target as HTMLElement)) {
if (isInputElement(e.browserEvent.target as HTMLElement) || isMonacoEditor(e.browserEvent.target as HTMLElement)) {
return;
}

View File

@@ -942,7 +942,6 @@ export class MenuBar extends Disposable {
menuHolder.style.top = `0px`;
menuHolder.style.right = `${this.container.clientWidth}px`;
menuHolder.style.left = 'auto';
console.log(customMenu.buttonElement.getBoundingClientRect().right - this.container.clientWidth);
} else {
menuHolder.style.top = `${this.container.clientHeight}px`;
menuHolder.style.left = `${customMenu.buttonElement.getBoundingClientRect().left}px`;

View File

@@ -12,3 +12,16 @@
font-weight: normal;
text-transform: none;
}
/** Actions */
.monaco-workbench .monaco-action-bar .action-item.select-container {
cursor: default;
}
.monaco-workbench .monaco-action-bar .action-item .monaco-select-box {
cursor: pointer;
min-width: 110px;
min-height: 18px;
padding: 2px 23px 2px 8px;
}

View File

@@ -9,10 +9,11 @@ import { Action, IActionRunner, IAction } from 'vs/base/common/actions';
import { ActionBar, ActionsOrientation, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar';
import { IContextMenuProvider, DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdown';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';
import { Disposable, IDisposable, combinedDisposable } from 'vs/base/common/lifecycle';
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
import { withNullAsUndefined } from 'vs/base/common/types';
import { Codicon, registerIcon } from 'vs/base/common/codicons';
import { Emitter } from 'vs/base/common/event';
export const CONTEXT = 'context.toolbar';
@@ -35,17 +36,21 @@ export class ToolBar extends Disposable {
private options: IToolBarOptions;
private actionBar: ActionBar;
private toggleMenuAction: ToggleMenuAction;
private toggleMenuActionViewItem = this._register(new MutableDisposable<DropdownMenuActionViewItem>());
private toggleMenuActionViewItem: DropdownMenuActionViewItem | undefined;
private toggleMenuActionViewItemDisposable: IDisposable = Disposable.None;
private hasSecondaryActions: boolean = false;
private lookupKeybindings: boolean;
private _onDidChangeDropdownVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeDropdownVisibility = this._onDidChangeDropdownVisibility.event;
constructor(container: HTMLElement, contextMenuProvider: IContextMenuProvider, options: IToolBarOptions = { orientation: ActionsOrientation.HORIZONTAL }) {
super();
this.options = options;
this.lookupKeybindings = typeof this.options.getKeyBinding === 'function';
this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem.value && this.toggleMenuActionViewItem.value.show(), options.toggleMenuTitle));
this.toggleMenuAction = this._register(new ToggleMenuAction(() => this.toggleMenuActionViewItem?.show(), options.toggleMenuTitle));
let element = document.createElement('div');
element.className = 'monaco-toolbar';
@@ -60,8 +65,10 @@ export class ToolBar extends Disposable {
// Return special action item for the toggle menu action
if (action.id === ToggleMenuAction.ID) {
this.toggleMenuActionViewItemDisposable.dispose();
// Create new
this.toggleMenuActionViewItem.value = new DropdownMenuActionViewItem(
this.toggleMenuActionViewItem = new DropdownMenuActionViewItem(
action,
(<ToggleMenuAction>action).menuActions,
contextMenuProvider,
@@ -72,9 +79,14 @@ export class ToolBar extends Disposable {
this.options.anchorAlignmentProvider,
true
);
this.toggleMenuActionViewItem.value.setActionContext(this.actionBar.context);
this.toggleMenuActionViewItem.setActionContext(this.actionBar.context);
return this.toggleMenuActionViewItem.value;
this.toggleMenuActionViewItemDisposable = combinedDisposable(
this.toggleMenuActionViewItem,
this.toggleMenuActionViewItem.onDidChangeVisibility(e => this._onDidChangeDropdownVisibility.fire(e))
);
return this.toggleMenuActionViewItem;
}
return options.actionViewItemProvider ? options.actionViewItemProvider(action) : undefined;
@@ -92,8 +104,8 @@ export class ToolBar extends Disposable {
set context(context: unknown) {
this.actionBar.context = context;
if (this.toggleMenuActionViewItem.value) {
this.toggleMenuActionViewItem.value.setActionContext(context);
if (this.toggleMenuActionViewItem) {
this.toggleMenuActionViewItem.setActionContext(context);
}
}
@@ -113,23 +125,21 @@ export class ToolBar extends Disposable {
this.actionBar.setAriaLabel(label);
}
setActions(primaryActions: ReadonlyArray<IAction>, secondaryActions?: ReadonlyArray<IAction>): () => void {
return () => {
let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : [];
setActions(primaryActions: ReadonlyArray<IAction>, secondaryActions?: ReadonlyArray<IAction>): void {
let primaryActionsToSet = primaryActions ? primaryActions.slice(0) : [];
// Inject additional action to open secondary actions if present
this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0);
if (this.hasSecondaryActions && secondaryActions) {
this.toggleMenuAction.menuActions = secondaryActions.slice(0);
primaryActionsToSet.push(this.toggleMenuAction);
}
// Inject additional action to open secondary actions if present
this.hasSecondaryActions = !!(secondaryActions && secondaryActions.length > 0);
if (this.hasSecondaryActions && secondaryActions) {
this.toggleMenuAction.menuActions = secondaryActions.slice(0);
primaryActionsToSet.push(this.toggleMenuAction);
}
this.actionBar.clear();
this.actionBar.clear();
primaryActionsToSet.forEach(action => {
this.actionBar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) });
});
};
primaryActionsToSet.forEach(action => {
this.actionBar.push(action, { icon: true, label: false, keybinding: this.getKeybindingLabel(action) });
});
}
private getKeybindingLabel(action: IAction): string | undefined {
@@ -153,6 +163,11 @@ export class ToolBar extends Disposable {
}
};
}
dispose(): void {
super.dispose();
this.toggleMenuActionViewItemDisposable.dispose();
}
}
class ToggleMenuAction extends Action {

View File

@@ -5,7 +5,7 @@
import 'vs/css!./media/tree';
import { IDisposable, dispose, Disposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle';
import { IListOptions, List, IListStyles, MouseController, DefaultKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/listWidget';
import { IListOptions, List, IListStyles, MouseController, DefaultKeyboardNavigationDelegate, isInputElement, isMonacoEditor } from 'vs/base/browser/ui/list/listWidget';
import { IListVirtualDelegate, IListRenderer, IListMouseEvent, IListContextMenuEvent, IListDragAndDrop, IListDragOverReaction, IKeyboardNavigationLabelProvider, IIdentityProvider, IKeyboardNavigationDelegate } from 'vs/base/browser/ui/list/list';
import { append, $, toggleClass, getDomNodePagePosition, removeClass, addClass, hasClass, hasParentWithClass, createStyleSheet, clearNode, addClasses, removeClasses } from 'vs/base/browser/dom';
import { Event, Relay, Emitter, EventBufferer } from 'vs/base/common/event';
@@ -917,10 +917,6 @@ class TypeFilterController<T, TFilterData> implements IDisposable {
}
}
function isInputElement(e: HTMLElement): boolean {
return e.tagName === 'INPUT' || e.tagName === 'TEXTAREA';
}
function asTreeMouseEvent<T>(event: IListMouseEvent<ITreeNode<T, any>>): ITreeMouseEvent<T> {
let target: TreeMouseEventTarget = TreeMouseEventTarget.Unknown;
@@ -1084,7 +1080,7 @@ class TreeNodeListMouseController<T, TFilterData, TRef> extends MouseController<
}
protected onViewPointer(e: IListMouseEvent<ITreeNode<T, TFilterData>>): void {
if (isInputElement(e.browserEvent.target as HTMLElement)) {
if (isInputElement(e.browserEvent.target as HTMLElement) || isMonacoEditor(e.browserEvent.target as HTMLElement)) {
return;
}

View File

@@ -3,11 +3,11 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ISpliceable } from 'vs/base/common/sequence';
import { Iterable } from 'vs/base/common/iterator';
import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent, ITreeModelSpliceEvent, TreeError, TreeFilterResult, TreeVisibility, WeakMapper } from 'vs/base/browser/ui/tree/tree';
import { IObjectTreeModelOptions, ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
// Exported only for test reasons, do not use directly
export interface ICompressedTreeElement<T> extends ITreeElement<T> {
@@ -126,7 +126,7 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
constructor(
private user: string,
list: ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>>,
list: IList<ITreeNode<ICompressedTreeNode<T>, TFilterData>>,
options: ICompressedObjectTreeModelOptions<T, TFilterData> = {}
) {
this.model = new ObjectTreeModel(user, list, options);
@@ -290,6 +290,16 @@ export class CompressedObjectTreeModel<T extends NonNullable<any>, TFilterData e
this.model.rerender(compressedNode);
}
updateElementHeight(element: T, height: number): void {
const compressedNode = this.getCompressedNode(element);
if (!compressedNode) {
return;
}
this.model.updateElementHeight(compressedNode, height);
}
refilter(): void {
this.model.refilter();
}
@@ -340,10 +350,13 @@ class CompressedTreeNodeWrapper<T, TFilterData> implements ITreeNode<T | null, T
) { }
}
function mapList<T, TFilterData>(nodeMapper: CompressedNodeWeakMapper<T, TFilterData>, list: ISpliceable<ITreeNode<T, TFilterData>>): ISpliceable<ITreeNode<ICompressedTreeNode<T>, TFilterData>> {
function mapList<T, TFilterData>(nodeMapper: CompressedNodeWeakMapper<T, TFilterData>, list: IList<ITreeNode<T, TFilterData>>): IList<ITreeNode<ICompressedTreeNode<T>, TFilterData>> {
return {
splice(start: number, deleteCount: number, toInsert: ITreeNode<ICompressedTreeNode<T>, TFilterData>[]): void {
list.splice(start, deleteCount, toInsert.map(node => nodeMapper.map(node)) as ITreeNode<T, TFilterData>[]);
},
updateElementHeight(index: number, height: number): void {
list.updateElementHeight(index, height);
}
};
}
@@ -402,7 +415,7 @@ export class CompressibleObjectTreeModel<T extends NonNullable<any>, TFilterData
constructor(
user: string,
list: ISpliceable<ITreeNode<T, TFilterData>>,
list: IList<ITreeNode<T, TFilterData>>,
options: ICompressibleObjectTreeModelOptions<T, TFilterData> = {}
) {
this.elementMapper = options.elementMapper || DefaultElementMapper;
@@ -492,6 +505,10 @@ export class CompressibleObjectTreeModel<T extends NonNullable<any>, TFilterData
return this.model.rerender(location);
}
updateElementHeight(element: T, height: number): void {
this.model.updateElementHeight(element, height);
}
refilter(): void {
return this.model.refilter();
}

View File

@@ -4,11 +4,11 @@
*--------------------------------------------------------------------------------------------*/
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { ISpliceable } from 'vs/base/common/sequence';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, IDataSource, TreeError } from 'vs/base/browser/ui/tree/tree';
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list';
import { Iterable } from 'vs/base/common/iterator';
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
export interface IDataTreeOptions<T, TFilterData = void> extends IAbstractTreeOptions<T, TFilterData> {
readonly sorter?: ITreeSorter<T>;
@@ -171,7 +171,7 @@ export class DataTree<TInput, T, TFilterData = void> extends AbstractTree<T | nu
return { elements, size: children.length };
}
protected createModel(user: string, view: ISpliceable<ITreeNode<T, TFilterData>>, options: IDataTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
protected createModel(user: string, view: IList<ITreeNode<T, TFilterData>>, options: IDataTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
return new ObjectTreeModel(user, view, options);
}

View File

@@ -6,8 +6,7 @@
import 'vs/css!./media/tree';
import { Iterable } from 'vs/base/common/iterator';
import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree';
import { ISpliceable } from 'vs/base/common/sequence';
import { IndexTreeModel } from 'vs/base/browser/ui/tree/indexTreeModel';
import { IndexTreeModel, IList } from 'vs/base/browser/ui/tree/indexTreeModel';
import { ITreeElement, ITreeModel, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree';
import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list';
@@ -41,7 +40,11 @@ export class IndexTree<T, TFilterData = void> extends AbstractTree<T, TFilterDat
this.model.rerender(location);
}
protected createModel(user: string, view: ISpliceable<ITreeNode<T, TFilterData>>, options: IIndexTreeOptions<T, TFilterData>): ITreeModel<T, TFilterData, number[]> {
updateElementHeight(location: number[], height: number): void {
this.model.updateElementHeight(location, height);
}
protected createModel(user: string, view: IList<ITreeNode<T, TFilterData>>, options: IIndexTreeOptions<T, TFilterData>): ITreeModel<T, TFilterData, number[]> {
return new IndexTreeModel(user, view, this.rootElement, options);
}
}

View File

@@ -56,6 +56,10 @@ function isCollapsibleStateUpdate(update: CollapseStateUpdate): update is Collap
return typeof (update as any).collapsible === 'boolean';
}
export interface IList<T> extends ISpliceable<T> {
updateElementHeight(index: number, height: number): void;
}
export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = void> implements ITreeModel<T, TFilterData, number[]> {
readonly rootRef = [];
@@ -78,7 +82,7 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
constructor(
private user: string,
private list: ISpliceable<ITreeNode<T, TFilterData>>,
private list: IList<ITreeNode<T, TFilterData>>,
rootElement: T,
options: IIndexTreeModelOptions<T, TFilterData> = {}
) {
@@ -212,6 +216,15 @@ export class IndexTreeModel<T extends Exclude<any, undefined>, TFilterData = voi
}
}
updateElementHeight(location: number[], height: number): void {
if (location.length === 0) {
throw new TreeError(this.user, 'Invalid tree location');
}
const { listIndex } = this.getTreeNodeWithListIndex(location);
this.list.updateElementHeight(listIndex, height);
}
has(location: number[]): boolean {
return this.hasTreeNode(location);
}

View File

@@ -5,13 +5,13 @@
import { Iterable } from 'vs/base/common/iterator';
import { AbstractTree, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree';
import { ISpliceable } from 'vs/base/common/sequence';
import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree';
import { ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IListVirtualDelegate, IKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/list/list';
import { Event } from 'vs/base/common/event';
import { CompressibleObjectTreeModel, ElementMapper, ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { memoize } from 'vs/base/common/decorators';
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
export interface IObjectTreeOptions<T, TFilterData = void> extends IAbstractTreeOptions<T, TFilterData> {
readonly sorter?: ITreeSorter<T>;
@@ -46,6 +46,10 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
this.model.rerender(element);
}
updateElementHeight(element: T, height: number): void {
this.model.updateElementHeight(element, height);
}
resort(element: T, recursive = true): void {
this.model.resort(element, recursive);
}
@@ -54,7 +58,7 @@ export class ObjectTree<T extends NonNullable<any>, TFilterData = void> extends
return this.model.has(element);
}
protected createModel(user: string, view: ISpliceable<ITreeNode<T, TFilterData>>, options: IObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
protected createModel(user: string, view: IList<ITreeNode<T, TFilterData>>, options: IObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
return new ObjectTreeModel(user, view, options);
}
}
@@ -188,7 +192,7 @@ export class CompressibleObjectTree<T extends NonNullable<any>, TFilterData = vo
this.model.setChildren(element, children);
}
protected createModel(user: string, view: ISpliceable<ITreeNode<T, TFilterData>>, options: ICompressibleObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
protected createModel(user: string, view: IList<ITreeNode<T, TFilterData>>, options: ICompressibleObjectTreeOptions<T, TFilterData>): ITreeModel<T | null, TFilterData, T | null> {
return new CompressibleObjectTreeModel(user, view, options);
}

View File

@@ -3,9 +3,8 @@
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { ISpliceable } from 'vs/base/common/sequence';
import { Iterable } from 'vs/base/common/iterator';
import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel';
import { IndexTreeModel, IIndexTreeModelOptions, IList } from 'vs/base/browser/ui/tree/indexTreeModel';
import { Event } from 'vs/base/common/event';
import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent, TreeError } from 'vs/base/browser/ui/tree/tree';
import { IIdentityProvider } from 'vs/base/browser/ui/list/list';
@@ -16,6 +15,7 @@ export type ITreeNodeCallback<T, TFilterData> = (node: ITreeNode<T, TFilterData>
export interface IObjectTreeModel<T extends NonNullable<any>, TFilterData extends NonNullable<any> = void> extends ITreeModel<T | null, TFilterData, T | null> {
setChildren(element: T | null, children: Iterable<ITreeElement<T>> | undefined): void;
resort(element?: T | null, recursive?: boolean): void;
updateElementHeight(element: T, height: number): void;
}
export interface IObjectTreeModelOptions<T, TFilterData> extends IIndexTreeModelOptions<T, TFilterData> {
@@ -41,7 +41,7 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
constructor(
private user: string,
list: ISpliceable<ITreeNode<T, TFilterData>>,
list: IList<ITreeNode<T, TFilterData>>,
options: IObjectTreeModelOptions<T, TFilterData> = {}
) {
this.model = new IndexTreeModel(user, list, null, options);
@@ -169,6 +169,11 @@ export class ObjectTreeModel<T extends NonNullable<any>, TFilterData extends Non
this.model.rerender(location);
}
updateElementHeight(element: T, height: number): void {
const location = this.getElementLocation(element);
this.model.updateElementHeight(location, height);
}
resort(element: T | null = null, recursive = true): void {
if (!this.sorter) {
return;

View File

@@ -62,6 +62,14 @@ export namespace Schemas {
export const webviewPanel = 'webview-panel';
/**
* Scheme used for loading the wrapper html and script in webviews.
*/
export const vscodeWebview = 'vscode-webview';
/**
* Scheme used for loading resources inside of webviews.
*/
export const vscodeWebviewResource = 'vscode-webview-resource';
/**

View File

@@ -54,7 +54,7 @@ if (typeof navigator === 'object' && !isElectronRenderer) {
_userAgent = navigator.userAgent;
_isWindows = _userAgent.indexOf('Windows') >= 0;
_isMacintosh = _userAgent.indexOf('Macintosh') >= 0;
_isIOS = _userAgent.indexOf('Macintosh') >= 0 && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0;
_isIOS = (_userAgent.indexOf('Macintosh') >= 0 || _userAgent.indexOf('iPad') >= 0 || _userAgent.indexOf('iPhone') >= 0) && !!navigator.maxTouchPoints && navigator.maxTouchPoints > 0;
_isLinux = _userAgent.indexOf('Linux') >= 0;
_isWeb = true;
_locale = navigator.language;
@@ -208,7 +208,7 @@ export const enum OperatingSystem {
Macintosh = 2,
Linux = 3
}
export const OS = (_isMacintosh ? OperatingSystem.Macintosh : (_isWindows ? OperatingSystem.Windows : OperatingSystem.Linux));
export const OS = (_isMacintosh || _isIOS ? OperatingSystem.Macintosh : (_isWindows ? OperatingSystem.Windows : OperatingSystem.Linux));
let _isLittleEndian = true;
let _isLittleEndianComputed = false;

View File

@@ -18,7 +18,7 @@ export function buildReplaceStringWithCasePreserved(matches: string[] | null, pa
return pattern.toUpperCase();
} else if (matches[0].toLowerCase() === matches[0]) {
return pattern.toLowerCase();
} else if (strings.containsUppercaseCharacter(matches[0][0])) {
} else if (strings.containsUppercaseCharacter(matches[0][0]) && pattern.length > 0) {
return pattern[0].toUpperCase() + pattern.substr(1);
} else {
// we don't understand its pattern yet.

View File

@@ -4,6 +4,7 @@
*--------------------------------------------------------------------------------------------*/
import { Event, Emitter } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
export interface ISplice<T> {
readonly start: number;
@@ -32,3 +33,26 @@ export class Sequence<T> implements ISequence<T>, ISpliceable<T> {
this._onDidSplice.fire({ start, deleteCount, toInsert });
}
}
export class SimpleSequence<T> implements ISequence<T> {
private _elements: T[];
get elements(): T[] { return this._elements; }
readonly onDidSplice: Event<ISplice<T>>;
private disposable: IDisposable;
constructor(elements: T[], onDidAdd: Event<T>, onDidRemove: Event<T>) {
this._elements = [...elements];
this.onDidSplice = Event.any(
Event.map(onDidAdd, e => ({ start: this.elements.length, deleteCount: 0, toInsert: [e] })),
Event.map(Event.filter(Event.map(onDidRemove, e => this.elements.indexOf(e)), i => i > -1), i => ({ start: i, deleteCount: 1, toInsert: [] }))
);
this.disposable = this.onDidSplice(({ start, deleteCount, toInsert }) => this._elements.splice(start, deleteCount, ...toInsert));
}
dispose(): void {
this.disposable.dispose();
}
}

View File

@@ -1,377 +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 { DecoderStream } from 'iconv-lite';
import { Readable, ReadableStream, newWriteableStream } from 'vs/base/common/stream';
import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer';
export const UTF8 = 'utf8';
export const UTF8_with_bom = 'utf8bom';
export const UTF16be = 'utf16be';
export const UTF16le = 'utf16le';
export type UTF_ENCODING = typeof UTF8 | typeof UTF8_with_bom | typeof UTF16be | typeof UTF16le;
export function isUTFEncoding(encoding: string): encoding is UTF_ENCODING {
return [UTF8, UTF8_with_bom, UTF16be, UTF16le].some(utfEncoding => utfEncoding === encoding);
}
export const UTF16be_BOM = [0xFE, 0xFF];
export const UTF16le_BOM = [0xFF, 0xFE];
export const UTF8_BOM = [0xEF, 0xBB, 0xBF];
const ZERO_BYTE_DETECTION_BUFFER_MAX_LEN = 512; // number of bytes to look at to decide about a file being binary or not
const NO_ENCODING_GUESS_MIN_BYTES = 512; // when not auto guessing the encoding, small number of bytes are enough
const AUTO_ENCODING_GUESS_MIN_BYTES = 512 * 8; // with auto guessing we want a lot more content to be read for guessing
const AUTO_ENCODING_GUESS_MAX_BYTES = 512 * 128; // set an upper limit for the number of bytes we pass on to jschardet
export interface IDecodeStreamOptions {
guessEncoding: boolean;
minBytesRequiredForDetection?: number;
overwriteEncoding(detectedEncoding: string | null): Promise<string>;
}
export interface IDecodeStreamResult {
stream: ReadableStream<string>;
detected: IDetectedEncodingResult;
}
export function toDecodeStream(source: VSBufferReadableStream, options: IDecodeStreamOptions): Promise<IDecodeStreamResult> {
const minBytesRequiredForDetection = options.minBytesRequiredForDetection ?? options.guessEncoding ? AUTO_ENCODING_GUESS_MIN_BYTES : NO_ENCODING_GUESS_MIN_BYTES;
return new Promise<IDecodeStreamResult>((resolve, reject) => {
const target = newWriteableStream<string>(strings => strings.join(''));
const bufferedChunks: VSBuffer[] = [];
let bytesBuffered = 0;
let decoder: DecoderStream | undefined = undefined;
const createDecoder = async () => {
try {
// detect encoding from buffer
const detected = await detectEncodingFromBuffer({
buffer: VSBuffer.concat(bufferedChunks),
bytesRead: bytesBuffered
}, options.guessEncoding);
// ensure to respect overwrite of encoding
detected.encoding = await options.overwriteEncoding(detected.encoding);
// decode and write buffered content
const iconv = await import('iconv-lite');
decoder = iconv.getDecoder(toNodeEncoding(detected.encoding));
const decoded = decoder.write(Buffer.from(VSBuffer.concat(bufferedChunks).buffer));
target.write(decoded);
bufferedChunks.length = 0;
bytesBuffered = 0;
// signal to the outside our detected encoding and final decoder stream
resolve({
stream: target,
detected
});
} catch (error) {
reject(error);
}
};
// Stream error: forward to target
source.on('error', error => target.error(error));
// Stream data
source.on('data', async chunk => {
// if the decoder is ready, we just write directly
if (decoder) {
target.write(decoder.write(Buffer.from(chunk.buffer)));
}
// otherwise we need to buffer the data until the stream is ready
else {
bufferedChunks.push(chunk);
bytesBuffered += chunk.byteLength;
// buffered enough data for encoding detection, create stream
if (bytesBuffered >= minBytesRequiredForDetection) {
// pause stream here until the decoder is ready
source.pause();
await createDecoder();
// resume stream now that decoder is ready but
// outside of this stack to reduce recursion
setTimeout(() => source.resume());
}
}
});
// Stream end
source.on('end', async () => {
// we were still waiting for data to do the encoding
// detection. thus, wrap up starting the stream even
// without all the data to get things going
if (!decoder) {
await createDecoder();
}
// end the target with the remainders of the decoder
target.end(decoder?.end());
});
});
}
export async function toEncodeReadable(readable: Readable<string>, encoding: string, options?: { addBOM?: boolean }): Promise<VSBufferReadable> {
const iconv = await import('iconv-lite');
const encoder = iconv.getEncoder(toNodeEncoding(encoding), options);
let bytesRead = 0;
let done = false;
return {
read() {
if (done) {
return null;
}
const chunk = readable.read();
if (typeof chunk !== 'string') {
done = true;
// If we are instructed to add a BOM but we detect that no
// bytes have been read, we must ensure to return the BOM
// ourselves so that we comply with the contract.
if (bytesRead === 0 && options?.addBOM) {
switch (encoding) {
case UTF8:
case UTF8_with_bom:
return VSBuffer.wrap(Uint8Array.from(UTF8_BOM));
case UTF16be:
return VSBuffer.wrap(Uint8Array.from(UTF16be_BOM));
case UTF16le:
return VSBuffer.wrap(Uint8Array.from(UTF16le_BOM));
}
}
const leftovers = encoder.end();
if (leftovers && leftovers.length > 0) {
return VSBuffer.wrap(leftovers);
}
return null;
}
bytesRead += chunk.length;
return VSBuffer.wrap(encoder.write(chunk));
}
};
}
export async function encodingExists(encoding: string): Promise<boolean> {
const iconv = await import('iconv-lite');
return iconv.encodingExists(toNodeEncoding(encoding));
}
export function toNodeEncoding(enc: string | null): string {
if (enc === UTF8_with_bom || enc === null) {
return UTF8; // iconv does not distinguish UTF 8 with or without BOM, so we need to help it
}
return enc;
}
export function detectEncodingByBOMFromBuffer(buffer: VSBuffer | null, bytesRead: number): typeof UTF8_with_bom | typeof UTF16le | typeof UTF16be | null {
if (!buffer || bytesRead < UTF16be_BOM.length) {
return null;
}
const b0 = buffer.readUInt8(0);
const b1 = buffer.readUInt8(1);
// UTF-16 BE
if (b0 === UTF16be_BOM[0] && b1 === UTF16be_BOM[1]) {
return UTF16be;
}
// UTF-16 LE
if (b0 === UTF16le_BOM[0] && b1 === UTF16le_BOM[1]) {
return UTF16le;
}
if (bytesRead < UTF8_BOM.length) {
return null;
}
const b2 = buffer.readUInt8(2);
// UTF-8
if (b0 === UTF8_BOM[0] && b1 === UTF8_BOM[1] && b2 === UTF8_BOM[2]) {
return UTF8_with_bom;
}
return null;
}
// we explicitly ignore a specific set of encodings from auto guessing
// - ASCII: we never want this encoding (most UTF-8 files would happily detect as
// ASCII files and then you could not type non-ASCII characters anymore)
// - UTF-16: we have our own detection logic for UTF-16
// - UTF-32: we do not support this encoding in VSCode
const IGNORE_ENCODINGS = ['ascii', 'utf-16', 'utf-32'];
/**
* Guesses the encoding from buffer.
*/
async function guessEncodingByBuffer(buffer: VSBuffer): Promise<string | null> {
const jschardet = await import('jschardet');
const guessed = jschardet.detect(Buffer.from(buffer.slice(0, AUTO_ENCODING_GUESS_MAX_BYTES).buffer)); // ensure to limit buffer for guessing due to https://github.com/aadsm/jschardet/issues/53
if (!guessed || !guessed.encoding) {
return null;
}
const enc = guessed.encoding.toLowerCase();
if (0 <= IGNORE_ENCODINGS.indexOf(enc)) {
return null; // see comment above why we ignore some encodings
}
return toIconvLiteEncoding(guessed.encoding);
}
const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = {
'ibm866': 'cp866',
'big5': 'cp950'
};
function toIconvLiteEncoding(encodingName: string): string {
const normalizedEncodingName = encodingName.replace(/[^a-zA-Z0-9]/g, '').toLowerCase();
const mapped = JSCHARDET_TO_ICONV_ENCODINGS[normalizedEncodingName];
return mapped || normalizedEncodingName;
}
/**
* The encodings that are allowed in a settings file don't match the canonical encoding labels specified by WHATWG.
* See https://encoding.spec.whatwg.org/#names-and-labels
* Iconv-lite strips all non-alphanumeric characters, but ripgrep doesn't. For backcompat, allow these labels.
*/
export function toCanonicalName(enc: string): string {
switch (enc) {
case 'shiftjis':
return 'shift-jis';
case 'utf16le':
return 'utf-16le';
case 'utf16be':
return 'utf-16be';
case 'big5hkscs':
return 'big5-hkscs';
case 'eucjp':
return 'euc-jp';
case 'euckr':
return 'euc-kr';
case 'koi8r':
return 'koi8-r';
case 'koi8u':
return 'koi8-u';
case 'macroman':
return 'x-mac-roman';
case 'utf8bom':
return 'utf8';
default:
const m = enc.match(/windows(\d+)/);
if (m) {
return 'windows-' + m[1];
}
return enc;
}
}
export interface IDetectedEncodingResult {
encoding: string | null;
seemsBinary: boolean;
}
export interface IReadResult {
buffer: VSBuffer | null;
bytesRead: number;
}
export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: false): IDetectedEncodingResult;
export function detectEncodingFromBuffer(readResult: IReadResult, autoGuessEncoding?: boolean): Promise<IDetectedEncodingResult>;
export function detectEncodingFromBuffer({ buffer, bytesRead }: IReadResult, autoGuessEncoding?: boolean): Promise<IDetectedEncodingResult> | IDetectedEncodingResult {
// Always first check for BOM to find out about encoding
let encoding = detectEncodingByBOMFromBuffer(buffer, bytesRead);
// Detect 0 bytes to see if file is binary or UTF-16 LE/BE
// unless we already know that this file has a UTF-16 encoding
let seemsBinary = false;
if (encoding !== UTF16be && encoding !== UTF16le && buffer) {
let couldBeUTF16LE = true; // e.g. 0xAA 0x00
let couldBeUTF16BE = true; // e.g. 0x00 0xAA
let containsZeroByte = false;
// This is a simplified guess to detect UTF-16 BE or LE by just checking if
// the first 512 bytes have the 0-byte at a specific location. For UTF-16 LE
// this would be the odd byte index and for UTF-16 BE the even one.
// Note: this can produce false positives (a binary file that uses a 2-byte
// encoding of the same format as UTF-16) and false negatives (a UTF-16 file
// that is using 4 bytes to encode a character).
for (let i = 0; i < bytesRead && i < ZERO_BYTE_DETECTION_BUFFER_MAX_LEN; i++) {
const isEndian = (i % 2 === 1); // assume 2-byte sequences typical for UTF-16
const isZeroByte = (buffer.readUInt8(i) === 0);
if (isZeroByte) {
containsZeroByte = true;
}
// UTF-16 LE: expect e.g. 0xAA 0x00
if (couldBeUTF16LE && (isEndian && !isZeroByte || !isEndian && isZeroByte)) {
couldBeUTF16LE = false;
}
// UTF-16 BE: expect e.g. 0x00 0xAA
if (couldBeUTF16BE && (isEndian && isZeroByte || !isEndian && !isZeroByte)) {
couldBeUTF16BE = false;
}
// Return if this is neither UTF16-LE nor UTF16-BE and thus treat as binary
if (isZeroByte && !couldBeUTF16LE && !couldBeUTF16BE) {
break;
}
}
// Handle case of 0-byte included
if (containsZeroByte) {
if (couldBeUTF16LE) {
encoding = UTF16le;
} else if (couldBeUTF16BE) {
encoding = UTF16be;
} else {
seemsBinary = true;
}
}
}
// Auto guess encoding if configured
if (autoGuessEncoding && !seemsBinary && !encoding && buffer) {
return guessEncodingByBuffer(buffer.slice(0, bytesRead)).then(guessedEncoding => {
return {
seemsBinary: false,
encoding: guessedEncoding
};
});
}
return { seemsBinary, encoding };
}

View File

@@ -7,7 +7,7 @@
* This code is also used by standalone cli's. Avoid adding dependencies to keep the size of the cli small.
*/
import { exec } from 'child_process';
import * as os from 'os';
import { isWindows } from 'vs/base/common/platform';
const windowsTerminalEncodings = {
'437': 'cp437', // United States
@@ -39,7 +39,6 @@ const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = {
const UTF8 = 'utf8';
export async function resolveTerminalEncoding(verbose?: boolean): Promise<string> {
let rawEncodingPromise: Promise<string>;
@@ -54,7 +53,7 @@ export async function resolveTerminalEncoding(verbose?: boolean): Promise<string
}
// Windows: educated guess
else if (os.platform() === 'win32') {
else if (isWindows) {
rawEncodingPromise = new Promise<string>(resolve => {
if (verbose) {
console.log('Running "chcp" to detect terminal encoding...');

View File

@@ -105,6 +105,8 @@
vertical-align: middle;
padding: 2px 4px;
border-radius: 2px;
min-height: auto;
line-height: normal;
}
.quick-input-action {

View File

@@ -353,10 +353,12 @@ class QuickInput extends Disposable implements IQuickInput {
this.ui.inputBox.showDecoration(severity);
if (severity === Severity.Error) {
const styles = this.ui.inputBox.stylesForType(severity);
this.ui.message.style.color = styles.foreground ? `${styles.foreground}` : '';
this.ui.message.style.backgroundColor = styles.background ? `${styles.background}` : '';
this.ui.message.style.border = styles.border ? `1px solid ${styles.border}` : '';
this.ui.message.style.paddingBottom = '4px';
} else {
this.ui.message.style.color = '';
this.ui.message.style.backgroundColor = '';
this.ui.message.style.border = '';
this.ui.message.style.paddingBottom = '';

View File

@@ -21,7 +21,7 @@ export function request(options: IRequestOptions, token: CancellationToken): Pro
setRequestHeaders(xhr, options);
xhr.responseType = 'arraybuffer';
xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText)));
xhr.onerror = e => reject(new Error(xhr.statusText && ('XHR failed: ' + xhr.statusText) || 'XHR failed'));
xhr.onload = (e) => {
resolve({
res: {

View File

@@ -247,3 +247,37 @@ export interface FileFilter {
extensions: string[];
name: string;
}
export interface InputEvent {
// Docs: http://electronjs.org/docs/api/structures/input-event
/**
* An array of modifiers of the event, can be `shift`, `control`, `alt`, `meta`,
* `isKeypad`, `isAutoRepeat`, `leftButtonDown`, `middleButtonDown`,
* `rightButtonDown`, `capsLock`, `numLock`, `left`, `right`.
*/
modifiers: Array<'shift' | 'control' | 'alt' | 'meta' | 'isKeypad' | 'isAutoRepeat' | 'leftButtonDown' | 'middleButtonDown' | 'rightButtonDown' | 'capsLock' | 'numLock' | 'left' | 'right'>;
}
export interface MouseInputEvent extends InputEvent {
// Docs: http://electronjs.org/docs/api/structures/mouse-input-event
/**
* The button pressed, can be `left`, `middle`, `right`.
*/
button?: ('left' | 'middle' | 'right');
clickCount?: number;
globalX?: number;
globalY?: number;
movementX?: number;
movementY?: number;
/**
* The type of the event, can be `mouseDown`, `mouseUp`, `mouseEnter`,
* `mouseLeave`, `contextMenu`, `mouseWheel` or `mouseMove`.
*/
type: ('mouseDown' | 'mouseUp' | 'mouseEnter' | 'mouseLeave' | 'contextMenu' | 'mouseWheel' | 'mouseMove');
x: number;
y: number;
}

View File

@@ -7,7 +7,7 @@ import * as assert from 'assert';
import { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedObjectTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel';
import { Iterable } from 'vs/base/common/iterator';
import { ITreeNode } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence';
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
interface IResolvedCompressedTreeElement<T> extends ICompressedTreeElement<T> {
readonly element: T;
@@ -289,11 +289,12 @@ suite('CompressedObjectTree', function () {
});
});
function toSpliceable<T>(arr: T[]): ISpliceable<T> {
function toList<T>(arr: T[]): IList<T> {
return {
splice(start: number, deleteCount: number, elements: T[]): void {
arr.splice(start, deleteCount, ...elements);
}
},
updateElementHeight() { }
};
}
@@ -305,7 +306,7 @@ suite('CompressedObjectTree', function () {
test('ctor', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>('test', toSpliceable(list));
const model = new CompressedObjectTreeModel<number>('test', toList(list));
assert(model);
assert.equal(list.length, 0);
assert.equal(model.size, 0);
@@ -313,7 +314,7 @@ suite('CompressedObjectTree', function () {
test('flat', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>('test', toSpliceable(list));
const model = new CompressedObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{ element: 0 },
@@ -340,7 +341,7 @@ suite('CompressedObjectTree', function () {
test('nested', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>('test', toSpliceable(list));
const model = new CompressedObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{
@@ -376,7 +377,7 @@ suite('CompressedObjectTree', function () {
test('compressed', () => {
const list: ITreeNode<ICompressedTreeNode<number>>[] = [];
const model = new CompressedObjectTreeModel<number>('test', toSpliceable(list));
const model = new CompressedObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{

View File

@@ -5,14 +5,14 @@
import * as assert from 'assert';
import { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence';
import { IndexTreeModel, IIndexTreeNode } from 'vs/base/browser/ui/tree/indexTreeModel';
import { IndexTreeModel, IIndexTreeNode, IList } from 'vs/base/browser/ui/tree/indexTreeModel';
function toSpliceable<T>(arr: T[]): ISpliceable<T> {
function toList<T>(arr: T[]): IList<T> {
return {
splice(start: number, deleteCount: number, elements: T[]): void {
arr.splice(start, deleteCount, ...elements);
}
},
updateElementHeight() { }
};
}
@@ -24,14 +24,14 @@ suite('IndexTreeModel', function () {
test('ctor', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
assert(model);
assert.equal(list.length, 0);
});
test('insert', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{ element: 0 },
@@ -53,7 +53,7 @@ suite('IndexTreeModel', function () {
test('deep insert', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -90,7 +90,7 @@ suite('IndexTreeModel', function () {
test('deep insert collapsed', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -118,7 +118,7 @@ suite('IndexTreeModel', function () {
test('delete', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{ element: 0 },
@@ -143,7 +143,7 @@ suite('IndexTreeModel', function () {
test('nested delete', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -177,7 +177,7 @@ suite('IndexTreeModel', function () {
test('deep delete', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -205,7 +205,7 @@ suite('IndexTreeModel', function () {
test('hidden delete', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -230,7 +230,7 @@ suite('IndexTreeModel', function () {
test('collapse', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -261,7 +261,7 @@ suite('IndexTreeModel', function () {
test('expand', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -301,7 +301,7 @@ suite('IndexTreeModel', function () {
test('collapse should recursively adjust visible count', function () {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -334,7 +334,7 @@ suite('IndexTreeModel', function () {
test('setCollapsible', () => {
const list: ITreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -403,7 +403,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1, { filter });
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
model.splice([0], 0, [
{
@@ -437,7 +437,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1, { filter });
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
model.splice([0], 0, [
{
@@ -460,7 +460,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1, { filter });
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
model.splice([0], 0, [
{
@@ -499,7 +499,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<string>('test', toSpliceable(list), 'root', { filter });
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{
@@ -545,7 +545,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<string>('test', toSpliceable(list), 'root', { filter });
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{
@@ -591,7 +591,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<string>('test', toSpliceable(list), 'root', { filter });
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{
@@ -639,7 +639,7 @@ suite('IndexTreeModel', function () {
test('simple', function () {
const list: IIndexTreeNode<number>[] = [];
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1);
const model = new IndexTreeModel<number>('test', toList(list), -1);
model.splice([0], 0, [
{
@@ -669,7 +669,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<number>('test', toSpliceable(list), -1, { filter });
const model = new IndexTreeModel<number>('test', toList(list), -1, { filter });
model.splice([0], 0, [
{
@@ -701,7 +701,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<string>('test', toSpliceable(list), 'root', { filter });
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{ element: 'silver' },
@@ -735,7 +735,7 @@ suite('IndexTreeModel', function () {
}
};
const model = new IndexTreeModel<string>('test', toSpliceable(list), 'root', { filter });
const model = new IndexTreeModel<string>('test', toList(list), 'root', { filter });
model.splice([0], 0, [
{ element: 'a', children: [{ element: 'aa' }] },

View File

@@ -5,15 +5,16 @@
import * as assert from 'assert';
import { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree';
import { ISpliceable } from 'vs/base/common/sequence';
import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel';
import { IList } from 'vs/base/browser/ui/tree/indexTreeModel';
function toSpliceable<T>(arr: T[]): ISpliceable<T> {
function toList<T>(arr: T[]): IList<T> {
return {
splice(start: number, deleteCount: number, elements: T[]): void {
// console.log(`splice (${start}, ${deleteCount}, ${elements.length} [${elements.join(', ')}] )`); // debugging
arr.splice(start, deleteCount, ...elements);
}
},
updateElementHeight() { }
};
}
@@ -25,7 +26,7 @@ suite('ObjectTreeModel', function () {
test('ctor', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toSpliceable(list));
const model = new ObjectTreeModel<number>('test', toList(list));
assert(model);
assert.equal(list.length, 0);
assert.equal(model.size, 0);
@@ -33,7 +34,7 @@ suite('ObjectTreeModel', function () {
test('flat', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toSpliceable(list));
const model = new ObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{ element: 0 },
@@ -60,7 +61,7 @@ suite('ObjectTreeModel', function () {
test('nested', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toSpliceable(list));
const model = new ObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{
@@ -96,7 +97,7 @@ suite('ObjectTreeModel', function () {
test('setChildren on collapsed node', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toSpliceable(list));
const model = new ObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{ element: 0, collapsed: true }
@@ -117,7 +118,7 @@ suite('ObjectTreeModel', function () {
test('setChildren on expanded, unrevealed node', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toSpliceable(list));
const model = new ObjectTreeModel<number>('test', toList(list));
model.setChildren(null, [
{
@@ -143,7 +144,7 @@ suite('ObjectTreeModel', function () {
test('collapse state is preserved with strict identity', () => {
const list: ITreeNode<string>[] = [];
const model = new ObjectTreeModel<string>('test', toSpliceable(list), { collapseByDefault: true });
const model = new ObjectTreeModel<string>('test', toList(list), { collapseByDefault: true });
const data = [{ element: 'father', children: [{ element: 'child' }] }];
model.setChildren(null, data);
@@ -173,7 +174,7 @@ suite('ObjectTreeModel', function () {
let compare: (a: string, b: string) => number = (a, b) => a < b ? -1 : 1;
const list: ITreeNode<string>[] = [];
const model = new ObjectTreeModel<string>('test', toSpliceable(list), { sorter: { compare(a, b) { return compare(a, b); } } });
const model = new ObjectTreeModel<string>('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } });
const data = [
{ element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] },
{ element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] },
@@ -188,7 +189,7 @@ suite('ObjectTreeModel', function () {
let compare: (a: string, b: string) => number = () => 0;
const list: ITreeNode<string>[] = [];
const model = new ObjectTreeModel<string>('test', toSpliceable(list), { sorter: { compare(a, b) { return compare(a, b); } } });
const model = new ObjectTreeModel<string>('test', toList(list), { sorter: { compare(a, b) { return compare(a, b); } } });
const data = [
{ element: 'cars', children: [{ element: 'sedan' }, { element: 'convertible' }, { element: 'compact' }] },
{ element: 'airplanes', children: [{ element: 'passenger' }, { element: 'jet' }] },
@@ -223,7 +224,7 @@ suite('ObjectTreeModel', function () {
test('expandTo', () => {
const list: ITreeNode<number>[] = [];
const model = new ObjectTreeModel<number>('test', toSpliceable(list), { collapseByDefault: true });
const model = new ObjectTreeModel<number>('test', toList(list), { collapseByDefault: true });
model.setChildren(null, [
{
@@ -254,7 +255,7 @@ suite('ObjectTreeModel', function () {
return fn(element) ? TreeVisibility.Visible : parentVisibility;
}
};
const model = new ObjectTreeModel<string>('test', toSpliceable(list), { filter });
const model = new ObjectTreeModel<string>('test', toList(list), { filter });
model.setChildren(null, [{ element: 'file', children: [{ element: 'hello' }] }]);
assert.deepEqual(toArray(list), ['file', 'hello']);

View File

@@ -1,402 +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 * as fs from 'fs';
import * as encoding from 'vs/base/node/encoding';
import * as terminalEncoding from 'vs/base/node/terminalEncoding';
import * as streams from 'vs/base/common/stream';
import * as iconv from 'iconv-lite';
import { getPathFromAmdModule } from 'vs/base/common/amd';
import { newWriteableBufferStream, VSBuffer, VSBufferReadableStream, streamToBufferReadableStream } from 'vs/base/common/buffer';
export async function detectEncodingByBOM(file: string): Promise<typeof encoding.UTF16be | typeof encoding.UTF16le | typeof encoding.UTF8_with_bom | null> {
try {
const { buffer, bytesRead } = await readExactlyByFile(file, 3);
return encoding.detectEncodingByBOMFromBuffer(buffer, bytesRead);
} catch (error) {
return null; // ignore errors (like file not found)
}
}
interface ReadResult {
buffer: VSBuffer | null;
bytesRead: number;
}
function readExactlyByFile(file: string, totalBytes: number): Promise<ReadResult> {
return new Promise<ReadResult>((resolve, reject) => {
fs.open(file, 'r', null, (err, fd) => {
if (err) {
return reject(err);
}
function end(err: Error | null, resultBuffer: Buffer | null, bytesRead: number): void {
fs.close(fd, closeError => {
if (closeError) {
return reject(closeError);
}
if (err && (<any>err).code === 'EISDIR') {
return reject(err); // we want to bubble this error up (file is actually a folder)
}
return resolve({ buffer: resultBuffer ? VSBuffer.wrap(resultBuffer) : null, bytesRead });
});
}
const buffer = Buffer.allocUnsafe(totalBytes);
let offset = 0;
function readChunk(): void {
fs.read(fd, buffer, offset, totalBytes - offset, null, (err, bytesRead) => {
if (err) {
return end(err, null, 0);
}
if (bytesRead === 0) {
return end(null, buffer, offset);
}
offset += bytesRead;
if (offset === totalBytes) {
return end(null, buffer, offset);
}
return readChunk();
});
}
readChunk();
});
});
}
suite('Encoding', () => {
test('detectBOM does not return error for non existing file', async () => {
const file = getPathFromAmdModule(require, './fixtures/not-exist.css');
const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, null);
});
test('detectBOM UTF-8', async () => {
const file = getPathFromAmdModule(require, './fixtures/some_utf8.css');
const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, 'utf8bom');
});
test('detectBOM UTF-16 LE', async () => {
const file = getPathFromAmdModule(require, './fixtures/some_utf16le.css');
const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, 'utf16le');
});
test('detectBOM UTF-16 BE', async () => {
const file = getPathFromAmdModule(require, './fixtures/some_utf16be.css');
const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, 'utf16be');
});
test('detectBOM ANSI', async function () {
const file = getPathFromAmdModule(require, './fixtures/some_ansi.css');
const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, null);
});
test('detectBOM ANSI', async function () {
const file = getPathFromAmdModule(require, './fixtures/empty.txt');
const detectedEncoding = await detectEncodingByBOM(file);
assert.equal(detectedEncoding, null);
});
test('resolve terminal encoding (detect)', async function () {
const enc = await terminalEncoding.resolveTerminalEncoding();
assert.ok(enc.length > 0);
});
test('resolve terminal encoding (environment)', async function () {
process.env['VSCODE_CLI_ENCODING'] = 'utf16le';
const enc = await terminalEncoding.resolveTerminalEncoding();
assert.ok(await encoding.encodingExists(enc));
assert.equal(enc, 'utf16le');
});
test('detectEncodingFromBuffer (JSON saved as PNG)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some.json.png');
const buffer = await readExactlyByFile(file, 512);
const mimes = encoding.detectEncodingFromBuffer(buffer);
assert.equal(mimes.seemsBinary, false);
});
test('detectEncodingFromBuffer (PNG saved as TXT)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some.png.txt');
const buffer = await readExactlyByFile(file, 512);
const mimes = encoding.detectEncodingFromBuffer(buffer);
assert.equal(mimes.seemsBinary, true);
});
test('detectEncodingFromBuffer (XML saved as PNG)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some.xml.png');
const buffer = await readExactlyByFile(file, 512);
const mimes = encoding.detectEncodingFromBuffer(buffer);
assert.equal(mimes.seemsBinary, false);
});
test('detectEncodingFromBuffer (QWOFF saved as TXT)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some.qwoff.txt');
const buffer = await readExactlyByFile(file, 512);
const mimes = encoding.detectEncodingFromBuffer(buffer);
assert.equal(mimes.seemsBinary, true);
});
test('detectEncodingFromBuffer (CSS saved as QWOFF)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some.css.qwoff');
const buffer = await readExactlyByFile(file, 512);
const mimes = encoding.detectEncodingFromBuffer(buffer);
assert.equal(mimes.seemsBinary, false);
});
test('detectEncodingFromBuffer (PDF)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some.pdf');
const buffer = await readExactlyByFile(file, 512);
const mimes = encoding.detectEncodingFromBuffer(buffer);
assert.equal(mimes.seemsBinary, true);
});
test('detectEncodingFromBuffer (guess UTF-16 LE from content without BOM)', async function () {
const file = getPathFromAmdModule(require, './fixtures/utf16_le_nobom.txt');
const buffer = await readExactlyByFile(file, 512);
const mimes = encoding.detectEncodingFromBuffer(buffer);
assert.equal(mimes.encoding, encoding.UTF16le);
assert.equal(mimes.seemsBinary, false);
});
test('detectEncodingFromBuffer (guess UTF-16 BE from content without BOM)', async function () {
const file = getPathFromAmdModule(require, './fixtures/utf16_be_nobom.txt');
const buffer = await readExactlyByFile(file, 512);
const mimes = encoding.detectEncodingFromBuffer(buffer);
assert.equal(mimes.encoding, encoding.UTF16be);
assert.equal(mimes.seemsBinary, false);
});
test('autoGuessEncoding (UTF8)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some_file.css');
const buffer = await readExactlyByFile(file, 512 * 8);
const mimes = await encoding.detectEncodingFromBuffer(buffer, true);
assert.equal(mimes.encoding, 'utf8');
});
test('autoGuessEncoding (ASCII)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some_ansi.css');
const buffer = await readExactlyByFile(file, 512 * 8);
const mimes = await encoding.detectEncodingFromBuffer(buffer, true);
assert.equal(mimes.encoding, null);
});
test('autoGuessEncoding (ShiftJIS)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some.shiftjis.txt');
const buffer = await readExactlyByFile(file, 512 * 8);
const mimes = await encoding.detectEncodingFromBuffer(buffer, true);
assert.equal(mimes.encoding, 'shiftjis');
});
test('autoGuessEncoding (CP1252)', async function () {
const file = getPathFromAmdModule(require, './fixtures/some.cp1252.txt');
const buffer = await readExactlyByFile(file, 512 * 8);
const mimes = await encoding.detectEncodingFromBuffer(buffer, true);
assert.equal(mimes.encoding, 'windows1252');
});
async function readAndDecodeFromDisk(path: string, fileEncoding: string | null) {
return new Promise<string>((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) {
reject(err);
} else {
resolve(iconv.decode(data, encoding.toNodeEncoding(fileEncoding!)));
}
});
});
}
function newTestReadableStream(buffers: Buffer[]): VSBufferReadableStream {
const stream = newWriteableBufferStream();
buffers
.map(VSBuffer.wrap)
.forEach(buffer => {
setTimeout(() => {
stream.write(buffer);
});
});
setTimeout(() => {
stream.end();
});
return stream;
}
async function readAllAsString(stream: streams.ReadableStream<string>) {
return streams.consumeStream(stream, strings => strings.join(''));
}
test('toDecodeStream - some stream', async function () {
const source = newTestReadableStream([
Buffer.from([65, 66, 67]),
Buffer.from([65, 66, 67]),
Buffer.from([65, 66, 67]),
]);
const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 });
assert.ok(detected);
assert.ok(stream);
const content = await readAllAsString(stream);
assert.equal(content, 'ABCABCABC');
});
test('toDecodeStream - some stream, expect too much data', async function () {
const source = newTestReadableStream([
Buffer.from([65, 66, 67]),
Buffer.from([65, 66, 67]),
Buffer.from([65, 66, 67]),
]);
const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 });
assert.ok(detected);
assert.ok(stream);
const content = await readAllAsString(stream);
assert.equal(content, 'ABCABCABC');
});
test('toDecodeStream - some stream, no data', async function () {
const source = newWriteableBufferStream();
source.end();
const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 512, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 });
assert.ok(detected);
assert.ok(stream);
const content = await readAllAsString(stream);
assert.equal(content, '');
});
test('toDecodeStream - encoding, utf16be', async function () {
const path = getPathFromAmdModule(require, './fixtures/some_utf16be.css');
const source = streamToBufferReadableStream(fs.createReadStream(path));
const { detected, stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 64, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 });
assert.equal(detected.encoding, 'utf16be');
assert.equal(detected.seemsBinary, false);
const expected = await readAndDecodeFromDisk(path, detected.encoding);
const actual = await readAllAsString(stream);
assert.equal(actual, expected);
});
test('toDecodeStream - empty file', async function () {
const path = getPathFromAmdModule(require, './fixtures/empty.txt');
const source = streamToBufferReadableStream(fs.createReadStream(path));
const { detected, stream } = await encoding.toDecodeStream(source, { guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 });
const expected = await readAndDecodeFromDisk(path, detected.encoding);
const actual = await readAllAsString(stream);
assert.equal(actual, expected);
});
test('toDecodeStream - decodes buffer entirely', async function () {
const emojis = Buffer.from('🖥️💻💾');
const incompleteEmojis = emojis.slice(0, emojis.length - 1);
const buffers: Buffer[] = [];
for (let i = 0; i < incompleteEmojis.length; i++) {
buffers.push(incompleteEmojis.slice(i, i + 1));
}
const source = newTestReadableStream(buffers);
const { stream } = await encoding.toDecodeStream(source, { minBytesRequiredForDetection: 4, guessEncoding: false, overwriteEncoding: async detected => detected || encoding.UTF8 });
const expected = incompleteEmojis.toString(encoding.UTF8);
const actual = await readAllAsString(stream);
assert.equal(actual, expected);
});
test('toEncodeReadable - encoding, utf16be', async function () {
const path = getPathFromAmdModule(require, './fixtures/some_utf16be.css');
const source = await readAndDecodeFromDisk(path, encoding.UTF16be);
const expected = VSBuffer.wrap(
iconv.encode(source, encoding.toNodeEncoding(encoding.UTF16be))
).toString();
const actual = streams.consumeReadable(
await encoding.toEncodeReadable(streams.toReadable(source), encoding.UTF16be),
VSBuffer.concat
).toString();
assert.equal(actual, expected);
});
test('toEncodeReadable - empty readable to utf8', async function () {
const source: streams.Readable<string> = {
read() {
return null;
}
};
const actual = streams.consumeReadable(
await encoding.toEncodeReadable(source, encoding.UTF8),
VSBuffer.concat
).toString();
assert.equal(actual, '');
});
[{
utfEncoding: encoding.UTF8,
relatedBom: encoding.UTF8_BOM
}, {
utfEncoding: encoding.UTF8_with_bom,
relatedBom: encoding.UTF8_BOM
}, {
utfEncoding: encoding.UTF16be,
relatedBom: encoding.UTF16be_BOM,
}, {
utfEncoding: encoding.UTF16le,
relatedBom: encoding.UTF16le_BOM
}].forEach(({ utfEncoding, relatedBom }) => {
test(`toEncodeReadable - empty readable to ${utfEncoding} with BOM`, async function () {
const source: streams.Readable<string> = {
read() {
return null;
}
};
const encodedReadable = encoding.toEncodeReadable(source, utfEncoding, { addBOM: true });
const expected = VSBuffer.wrap(Buffer.from(relatedBom)).toString();
const actual = streams.consumeReadable(await encodedReadable, VSBuffer.concat).toString();
assert.equal(actual, expected);
});
});
});

View File

@@ -1,23 +0,0 @@
using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using System.Data.OleDb;
using System.Data.Odbc;
using System.IO;
using System.Net.Mail;
using System.Text.RegularExpressions;
using System.DirectoryServices;
using System.Diagnostics;
using System.Resources;
using System.Globalization;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.Serialization;
ObjectCount = LoadObjects("Öffentlicher Ordner");
Private = "Persönliche Information"

View File

@@ -1,35 +0,0 @@
/*----------------------------------------------------------
The base color for this template is #5c87b2. If you'd like
to use a different color start by replacing all instances of
#5c87b2 with your new color.
----------------------------------------------------------*/
body
{
background-color: #5c87b2;
font-size: .75em;
font-family: Segoe UI, Verdana, Helvetica, Sans-Serif;
margin: 8px;
padding: 0;
color: #696969;
}
h1, h2, h3, h4, h5, h6
{
color: #000;
font-size: 40px;
margin: 0px;
}
textarea
{
font-family: Consolas
}
#results
{
margin-top: 2em;
margin-left: 2em;
color: black;
font-size: medium;
}

View File

@@ -1,11 +0,0 @@
{
"type": "typescript",
"sources": [
"examples/company.ts",
"examples/conway.ts",
"examples/employee.ts",
"examples/large.ts",
"examples/small.ts"
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 B

View File

@@ -1 +0,0 @@
VSCODEは最高のエディタだ。

View File

@@ -1,3 +0,0 @@
<?xml>
</xml>

View File

@@ -1,40 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------
The base color for this template is #5c87b2. If you'd like
to use a different color start by replacing all instances of
#5c87b2 with your new color.
----------------------------------------------------------*/
body
{
background-color: #5c87b2;
font-size: .75em;
font-family: Segoe UI, Verdana, Helvetica, Sans-Serif;
margin: 8px;
padding: 0;
color: #696969;
}
h1, h2, h3, h4, h5, h6
{
color: #000;
font-size: 40px;
margin: 0px;
}
textarea
{
font-family: Consolas
}
#results
{
margin-top: 2em;
margin-left: 2em;
color: black;
font-size: medium;
}

View File

@@ -1,42 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------
The base color for this template is #5c87b2. If you'd like
to use a different color start by replacing all instances of
#5c87b2 with your new color.
öäüßßß
----------------------------------------------------------*/
body
{
background-color: #5c87b2;
font-size: .75em;
font-family: Segoe UI, Verdana, Helvetica, Sans-Serif;
margin: 8px;
padding: 0;
color: #696969;
}
h1, h2, h3, h4, h5, h6
{
color: #000;
font-size: 40px;
margin: 0px;
}
textarea
{
font-family: Consolas
}
#results
{
margin-top: 2em;
margin-left: 2em;
color: black;
font-size: medium;
}

View File

@@ -1,42 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the Source EULA. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/*----------------------------------------------------------
The base color for this template is #5c87b2. If you'd like
to use a different color start by replacing all instances of
#5c87b2 with your new color.
öäüßßß
----------------------------------------------------------*/
body
{
background-color: #5c87b2;
font-size: .75em;
font-family: Segoe UI, Verdana, Helvetica, Sans-Serif;
margin: 8px;
padding: 0;
color: #696969;
}
h1, h2, h3, h4, h5, h6
{
color: #000;
font-size: 40px;
margin: 0px;
}
textarea
{
font-family: Consolas
}
#results
{
margin-top: 2em;
margin-left: 2em;
color: black;
font-size: medium;
}