mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-06 09:35:41 -05:00
Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c (#8525)
* Merge from vscode a5cf1da01d5db3d2557132be8d30f89c38019f6c * remove files we don't want * fix hygiene * update distro * update distro * fix hygiene * fix strict nulls * distro * distro * fix tests * fix tests * add another edit * fix viewlet icon * fix azure dialog * fix some padding * fix more padding issues
This commit is contained in:
@@ -16,6 +16,7 @@ import { ICommandAction, IMenu, IMenuActionOptions, MenuItemAction, SubmenuItemA
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
// The alternative key on all platforms is alt. On windows we also support shift as an alternative key #44136
|
||||
class AlternativeKeyEmitter extends Emitter<boolean> {
|
||||
@@ -148,7 +149,7 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
||||
@INotificationService protected _notificationService: INotificationService,
|
||||
@IContextMenuService _contextMenuService: IContextMenuService
|
||||
) {
|
||||
super(undefined, _action, { icon: !!(_action.class || _action.item.iconLocation), label: !_action.class && !_action.item.iconLocation });
|
||||
super(undefined, _action, { icon: !!(_action.class || _action.item.icon), label: !_action.class && !_action.item.icon });
|
||||
this._altKey = AlternativeKeyEmitter.getInstance(_contextMenuService);
|
||||
}
|
||||
|
||||
@@ -237,28 +238,45 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
||||
_updateItemClass(item: ICommandAction): void {
|
||||
this._itemClassDispose.value = undefined;
|
||||
|
||||
if (item.iconLocation) {
|
||||
let iconClass: string;
|
||||
|
||||
const iconPathMapKey = item.iconLocation.dark.toString();
|
||||
|
||||
if (MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) {
|
||||
iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
|
||||
} else {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.light || item.iconLocation.dark)}`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.dark)}`);
|
||||
MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
|
||||
}
|
||||
|
||||
if (this.label) {
|
||||
addClasses(this.label, 'icon', iconClass);
|
||||
if (ThemeIcon.isThemeIcon(item.icon)) {
|
||||
// theme icons
|
||||
const iconClass = ThemeIcon.asClassName(item.icon);
|
||||
if (this.label && iconClass) {
|
||||
addClasses(this.label, iconClass);
|
||||
this._itemClassDispose.value = toDisposable(() => {
|
||||
if (this.label) {
|
||||
removeClasses(this.label, 'icon', iconClass);
|
||||
removeClasses(this.label, iconClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
} else if (item.icon) {
|
||||
// icon path
|
||||
let iconClass: string;
|
||||
|
||||
if (item.icon?.dark?.scheme) {
|
||||
|
||||
const iconPathMapKey = item.icon.dark.toString();
|
||||
|
||||
if (MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) {
|
||||
iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
|
||||
} else {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.icon.light || item.icon.dark)}`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.icon.dark)}`);
|
||||
MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
|
||||
}
|
||||
|
||||
if (this.label) {
|
||||
|
||||
addClasses(this.label, 'icon', iconClass);
|
||||
this._itemClassDispose.value = toDisposable(() => {
|
||||
if (this.label) {
|
||||
removeClasses(this.label, 'icon', iconClass);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -304,17 +322,19 @@ export class LabeledMenuItemActionItem extends MenuEntryActionViewItem {
|
||||
dispose(this._labeledItemClassDispose);
|
||||
this._labeledItemClassDispose = undefined;
|
||||
|
||||
if (item.iconLocation) {
|
||||
if (ThemeIcon.isThemeIcon(item.icon)) {
|
||||
// TODO
|
||||
} else if (item.icon) {
|
||||
let iconClass: string;
|
||||
|
||||
const iconPathMapKey = item.iconLocation.dark.toString();
|
||||
const iconPathMapKey = item.icon.dark.toString();
|
||||
|
||||
if (MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.has(iconPathMapKey)) {
|
||||
iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
|
||||
} else {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.light || item.iconLocation.dark)}`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.iconLocation.dark)}`);
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: ${asCSSUrl(item.icon.light || item.icon.dark)}`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: ${asCSSUrl(item.icon.dark)}`);
|
||||
MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
|
||||
}
|
||||
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
import { Action } from 'vs/base/common/actions';
|
||||
import { SyncDescriptor0, createSyncDescriptor } from 'vs/platform/instantiation/common/descriptors';
|
||||
import { IConstructorSignature2, createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IConstructorSignature2, createDecorator, BrandedService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindings, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
|
||||
import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
|
||||
|
||||
export interface ILocalizedString {
|
||||
value: string;
|
||||
@@ -22,7 +23,7 @@ export interface ICommandAction {
|
||||
id: string;
|
||||
title: string | ILocalizedString;
|
||||
category?: string | ILocalizedString;
|
||||
iconLocation?: { dark: URI; light?: URI; };
|
||||
icon?: { dark?: URI; light?: URI; } | ThemeIcon;
|
||||
precondition?: ContextKeyExpr;
|
||||
toggled?: ContextKeyExpr;
|
||||
}
|
||||
@@ -64,6 +65,7 @@ export const enum MenuId {
|
||||
DebugWatchContext,
|
||||
DebugToolBar,
|
||||
EditorContext,
|
||||
EditorContextPeek,
|
||||
EditorTitle,
|
||||
EditorTitleContext,
|
||||
EmptyEditorGroupContext,
|
||||
@@ -95,6 +97,9 @@ export const enum MenuId {
|
||||
StatusBarWindowIndicatorMenu,
|
||||
TouchBarContext,
|
||||
TitleBarContext,
|
||||
TunnelContext,
|
||||
TunnelInline,
|
||||
TunnelTitle,
|
||||
ViewItemContext,
|
||||
ViewTitle,
|
||||
ObjectExplorerItemContext, // {{SQL CARBON EDIT}}
|
||||
@@ -300,7 +305,13 @@ export class SyncActionDescriptor {
|
||||
private readonly _keybindingContext: ContextKeyExpr | undefined;
|
||||
private readonly _keybindingWeight: number | undefined;
|
||||
|
||||
constructor(ctor: IConstructorSignature2<string, string, Action>,
|
||||
public static create<Services extends BrandedService[]>(ctor: { new(id: string, label: string, ...services: Services): Action },
|
||||
id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number
|
||||
): SyncActionDescriptor {
|
||||
return new SyncActionDescriptor(ctor as IConstructorSignature2<string, string, Action>, id, label, keybindings, keybindingContext, keybindingWeight);
|
||||
}
|
||||
|
||||
private constructor(ctor: IConstructorSignature2<string, string, Action>,
|
||||
id: string, label: string | undefined, keybindings?: IKeybindings, keybindingContext?: ContextKeyExpr, keybindingWeight?: number
|
||||
) {
|
||||
this._id = id;
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IMenu, IMenuActionOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions';
|
||||
import { DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IMenu, IMenuActionOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction, ILocalizedString } from 'vs/platform/actions/common/actions';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { ContextKeyExpr, IContextKeyService, IContextKeyChangeEvent } from 'vs/platform/contextkey/common/contextkey';
|
||||
|
||||
@@ -27,24 +27,24 @@ export class MenuService implements IMenuService {
|
||||
|
||||
type MenuItemGroup = [string, Array<IMenuItem | ISubmenuItem>];
|
||||
|
||||
class Menu extends Disposable implements IMenu {
|
||||
class Menu implements IMenu {
|
||||
|
||||
private readonly _onDidChange = this._register(new Emitter<IMenu | undefined>());
|
||||
private readonly _onDidChange = new Emitter<IMenu | undefined>();
|
||||
private readonly _dispoables = new DisposableStore();
|
||||
|
||||
private _menuGroups!: MenuItemGroup[];
|
||||
private _contextKeys!: Set<string>;
|
||||
private _menuGroups: MenuItemGroup[] = [];
|
||||
private _contextKeys: Set<string> = new Set();
|
||||
|
||||
constructor(
|
||||
private readonly _id: MenuId,
|
||||
@ICommandService private readonly _commandService: ICommandService,
|
||||
@IContextKeyService private readonly _contextKeyService: IContextKeyService
|
||||
) {
|
||||
super();
|
||||
this._build();
|
||||
|
||||
// rebuild this menu whenever the menu registry reports an
|
||||
// event for this MenuId
|
||||
this._register(Event.debounce(
|
||||
this._dispoables.add(Event.debounce(
|
||||
Event.filter(MenuRegistry.onDidChangeMenu, menuId => menuId === this._id),
|
||||
() => { },
|
||||
50
|
||||
@@ -52,18 +52,23 @@ class Menu extends Disposable implements IMenu {
|
||||
|
||||
// when context keys change we need to check if the menu also
|
||||
// has changed
|
||||
this._register(Event.debounce<IContextKeyChangeEvent, boolean>(
|
||||
this._dispoables.add(Event.debounce<IContextKeyChangeEvent, boolean>(
|
||||
this._contextKeyService.onDidChangeContext,
|
||||
(last, event) => last || event.affectsSome(this._contextKeys),
|
||||
50
|
||||
)(e => e && this._onDidChange.fire(undefined), this));
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._dispoables.dispose();
|
||||
this._onDidChange.dispose();
|
||||
}
|
||||
|
||||
private _build(): void {
|
||||
|
||||
// reset
|
||||
this._menuGroups = [];
|
||||
this._contextKeys = new Set();
|
||||
this._menuGroups.length = 0;
|
||||
this._contextKeys.clear();
|
||||
|
||||
const menuItems = MenuRegistry.getMenuItems(this._id);
|
||||
|
||||
@@ -106,7 +111,10 @@ class Menu extends Disposable implements IMenu {
|
||||
const activeActions: Array<MenuItemAction | SubmenuItemAction> = [];
|
||||
for (const item of items) {
|
||||
if (this._contextKeyService.contextMatchesRules(item.when)) {
|
||||
const action = isIMenuItem(item) ? new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService) : new SubmenuItemAction(item);
|
||||
const action = isIMenuItem(item)
|
||||
? new MenuItemAction(item.command, item.alt, options, this._contextKeyService, this._commandService)
|
||||
: new SubmenuItemAction(item);
|
||||
|
||||
activeActions.push(action);
|
||||
}
|
||||
}
|
||||
@@ -125,7 +133,7 @@ class Menu extends Disposable implements IMenu {
|
||||
}
|
||||
}
|
||||
|
||||
private static _compareMenuItems(a: IMenuItem, b: IMenuItem): number {
|
||||
private static _compareMenuItems(a: IMenuItem | ISubmenuItem, b: IMenuItem | ISubmenuItem): number {
|
||||
|
||||
let aGroup = a.group;
|
||||
let bGroup = b.group;
|
||||
@@ -163,8 +171,15 @@ class Menu extends Disposable implements IMenu {
|
||||
}
|
||||
|
||||
// sort on titles
|
||||
const aTitle = typeof a.command.title === 'string' ? a.command.title : a.command.title.value;
|
||||
const bTitle = typeof b.command.title === 'string' ? b.command.title : b.command.title.value;
|
||||
return aTitle.localeCompare(bTitle);
|
||||
return Menu._compareTitles(
|
||||
isIMenuItem(a) ? a.command.title : a.title,
|
||||
isIMenuItem(b) ? b.command.title : b.title
|
||||
);
|
||||
}
|
||||
|
||||
private static _compareTitles(a: string | ILocalizedString, b: string | ILocalizedString) {
|
||||
const aStr = typeof a === 'string' ? a : a.value;
|
||||
const bStr = typeof b === 'string' ? b : b.value;
|
||||
return aStr.localeCompare(bStr);
|
||||
}
|
||||
}
|
||||
|
||||
100
src/vs/platform/auth/common/auth.css
Normal file
100
src/vs/platform/auth/common/auth.css
Normal file
@@ -0,0 +1,100 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
html {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
min-height: 100%;
|
||||
margin: 0;
|
||||
padding: 15px 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
color: white;
|
||||
font-family: "Segoe UI","Helvetica Neue","Helvetica",Arial,sans-serif;
|
||||
background-color: #373277;
|
||||
}
|
||||
|
||||
.branding {
|
||||
background-image: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI+PGRlZnM+PHN0eWxlPi5pY29uLWNhbnZhcy10cmFuc3BhcmVudHtmaWxsOiNmNmY2ZjY7b3BhY2l0eTowO30uaWNvbi13aGl0ZXtmaWxsOiNmZmY7fTwvc3R5bGU+PC9kZWZzPjx0aXRsZT5CcmFuZFZpc3VhbFN0dWRpb0NvZGUyMDE3UlRXXzI0eF93aGl0ZV8yNHg8L3RpdGxlPjxwYXRoIGNsYXNzPSJpY29uLWNhbnZhcy10cmFuc3BhcmVudCIgZD0iTTI0LDBWMjRIMFYwWiIvPjxwYXRoIGNsYXNzPSJpY29uLXdoaXRlIiBkPSJNMjQsMi41VjIxLjVMMTgsMjQsMCwxOC41di0uNTYxbDE4LDEuNTQ1VjBaTTEsMTMuMTExLDQuMzg1LDEwLDEsNi44ODlsMS40MTgtLjgyN0w1Ljg1Myw4LjY1LDEyLDNsMywxLjQ1NlYxNS41NDRMMTIsMTcsNS44NTMsMTEuMzUsMi40MTksMTMuOTM5Wk03LjY0NCwxMCwxMiwxMy4yODNWNi43MTdaIi8+PC9zdmc+");
|
||||
background-size: 24px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: left 50%;
|
||||
padding-left: 36px;
|
||||
font-size: 20px;
|
||||
letter-spacing: -0.04rem;
|
||||
font-weight: 400;
|
||||
color: white;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.message-container {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin: 0 30px;
|
||||
}
|
||||
|
||||
.message {
|
||||
font-weight: 300;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
body.error .message {
|
||||
display: none;
|
||||
}
|
||||
|
||||
body.error .error-message {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
display: none;
|
||||
font-weight: 300;
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: red;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Segoe UI';
|
||||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.eot?#iefix") format("embedded-opentype");
|
||||
src: local("Segoe UI Light"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/light/latest.svg#web") format("svg");
|
||||
font-weight: 200
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Segoe UI';
|
||||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.eot?#iefix") format("embedded-opentype");
|
||||
src: local("Segoe UI Semilight"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff2") format("woff2"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semilight/latest.svg#web") format("svg");
|
||||
font-weight: 300
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Segoe UI';
|
||||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.eot?#iefix") format("embedded-opentype");
|
||||
src: local("Segoe UI"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/normal/latest.svg#web") format("svg");
|
||||
font-weight: 400
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Segoe UI';
|
||||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.eot?#iefix") format("embedded-opentype");
|
||||
src: local("Segoe UI Semibold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/semibold/latest.svg#web") format("svg");
|
||||
font-weight: 600
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Segoe UI';
|
||||
src: url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.eot?#iefix") format("embedded-opentype");
|
||||
src: local("Segoe UI Bold"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff2") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.woff") format("woff"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.ttf") format("truetype"),url("https://c.s-microsoft.com/static/fonts/segoe-ui/west-european/bold/latest.svg#web") format("svg");
|
||||
font-weight: 700
|
||||
}
|
||||
35
src/vs/platform/auth/common/auth.html
Normal file
35
src/vs/platform/auth/common/auth.html
Normal file
@@ -0,0 +1,35 @@
|
||||
<!-- Copyright (C) Microsoft Corporation. All rights reserved. -->
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<title>Azure Account - Sign In</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="stylesheet" type="text/css" media="screen" href="auth.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a class="branding" href="https://code.visualstudio.com/">
|
||||
Visual Studio Code
|
||||
</a>
|
||||
<div class="message-container">
|
||||
<div class="message">
|
||||
You are signed in now and can close this page.
|
||||
</div>
|
||||
<div class="error-message">
|
||||
An error occurred while signing in:
|
||||
<div class="error-text"></div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var search = window.location.search;
|
||||
var error = (/[?&^]error=([^&]+)/.exec(search) || [])[1];
|
||||
if (error) {
|
||||
document.querySelector('.error-text')
|
||||
.textContent = decodeURIComponent(error);
|
||||
document.querySelector('body')
|
||||
.classList.add('error');
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,12 +4,15 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
export const enum AuthTokenStatus {
|
||||
Disabled = 'Disabled',
|
||||
Inactive = 'Inactive',
|
||||
Active = 'Active'
|
||||
Initializing = 'Initializing',
|
||||
SignedOut = 'SignedOut',
|
||||
SignedIn = 'SignedIn',
|
||||
SigningIn = 'SigningIn',
|
||||
RefreshingToken = 'RefreshingToken'
|
||||
}
|
||||
|
||||
export const IAuthTokenService = createDecorator<IAuthTokenService>('IAuthTokenService');
|
||||
@@ -19,11 +22,10 @@ export interface IAuthTokenService {
|
||||
|
||||
readonly status: AuthTokenStatus;
|
||||
readonly onDidChangeStatus: Event<AuthTokenStatus>;
|
||||
readonly _onDidGetCallback: Emitter<URI>;
|
||||
|
||||
getToken(): Promise<string | null>;
|
||||
updateToken(token: string): Promise<void>;
|
||||
getToken(): Promise<string | undefined>;
|
||||
refreshToken(): Promise<void>;
|
||||
deleteToken(): Promise<void>;
|
||||
|
||||
login(): Promise<void>;
|
||||
logout(): Promise<void>;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,9 +22,9 @@ export class AuthTokenChannel implements IServerChannel {
|
||||
switch (command) {
|
||||
case '_getInitialStatus': return Promise.resolve(this.service.status);
|
||||
case 'getToken': return this.service.getToken();
|
||||
case 'updateToken': return this.service.updateToken(args[0]);
|
||||
case 'refreshToken': return this.service.refreshToken();
|
||||
case 'deleteToken': return this.service.deleteToken();
|
||||
case 'login': return this.service.login();
|
||||
case 'logout': return this.service.logout();
|
||||
}
|
||||
throw new Error('Invalid call');
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
const SERVICE_NAME = 'VS Code';
|
||||
const ACCOUNT = 'MyAccount';
|
||||
|
||||
export class AuthTokenService extends Disposable implements IAuthTokenService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private _status: AuthTokenStatus = AuthTokenStatus.Disabled;
|
||||
get status(): AuthTokenStatus { return this._status; }
|
||||
private _onDidChangeStatus: Emitter<AuthTokenStatus> = this._register(new Emitter<AuthTokenStatus>());
|
||||
readonly onDidChangeStatus: Event<AuthTokenStatus> = this._onDidChangeStatus.event;
|
||||
|
||||
constructor(
|
||||
@ICredentialsService private readonly credentialsService: ICredentialsService,
|
||||
@IProductService productService: IProductService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
) {
|
||||
super();
|
||||
if (productService.settingsSyncStoreUrl && configurationService.getValue('configurationSync.enableAuth')) {
|
||||
this._status = AuthTokenStatus.Inactive;
|
||||
this.getToken().then(token => {
|
||||
if (token) {
|
||||
this.setStatus(AuthTokenStatus.Active);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getToken(): Promise<string | null> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
return this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT);
|
||||
}
|
||||
|
||||
async updateToken(token: string): Promise<void> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
await this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token);
|
||||
this.setStatus(AuthTokenStatus.Active);
|
||||
}
|
||||
|
||||
async refreshToken(): Promise<void> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
await this.deleteToken();
|
||||
}
|
||||
|
||||
async deleteToken(): Promise<void> {
|
||||
if (this.status === AuthTokenStatus.Disabled) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
await this.credentialsService.deletePassword(SERVICE_NAME, ACCOUNT);
|
||||
this.setStatus(AuthTokenStatus.Inactive);
|
||||
}
|
||||
|
||||
private setStatus(status: AuthTokenStatus): void {
|
||||
if (this._status !== status) {
|
||||
this._status = status;
|
||||
this._onDidChangeStatus.fire(status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
165
src/vs/platform/auth/electron-browser/authServer.ts
Normal file
165
src/vs/platform/auth/electron-browser/authServer.ts
Normal file
@@ -0,0 +1,165 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as http from 'http';
|
||||
import * as url from 'url';
|
||||
import * as fs from 'fs';
|
||||
import * as net from 'net';
|
||||
import { getPathFromAmdModule } from 'vs/base/common/amd';
|
||||
import { assertIsDefined } from 'vs/base/common/types';
|
||||
|
||||
interface Deferred<T> {
|
||||
resolve: (result: T | Promise<T>) => void;
|
||||
reject: (reason: any) => void;
|
||||
}
|
||||
|
||||
export function createTerminateServer(server: http.Server) {
|
||||
const sockets: Record<number, net.Socket> = {};
|
||||
let socketCount = 0;
|
||||
server.on('connection', socket => {
|
||||
const id = socketCount++;
|
||||
sockets[id] = socket;
|
||||
socket.on('close', () => {
|
||||
delete sockets[id];
|
||||
});
|
||||
});
|
||||
return async () => {
|
||||
const result = new Promise<Error | undefined>(resolve => server.close(resolve));
|
||||
for (const id in sockets) {
|
||||
sockets[id].destroy();
|
||||
}
|
||||
return result;
|
||||
};
|
||||
}
|
||||
|
||||
export async function startServer(server: http.Server): Promise<string> {
|
||||
let portTimer: NodeJS.Timer;
|
||||
|
||||
function cancelPortTimer() {
|
||||
clearTimeout(portTimer);
|
||||
}
|
||||
|
||||
const port = new Promise<string>((resolve, reject) => {
|
||||
portTimer = setTimeout(() => {
|
||||
reject(new Error('Timeout waiting for port'));
|
||||
}, 5000);
|
||||
|
||||
server.on('listening', () => {
|
||||
const address = server.address();
|
||||
if (typeof address === 'string') {
|
||||
resolve(address);
|
||||
} else {
|
||||
resolve(assertIsDefined(address).port.toString());
|
||||
}
|
||||
});
|
||||
|
||||
server.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
server.on('close', () => {
|
||||
reject(new Error('Closed'));
|
||||
});
|
||||
|
||||
server.listen(0);
|
||||
});
|
||||
|
||||
port.then(cancelPortTimer, cancelPortTimer);
|
||||
return port;
|
||||
}
|
||||
|
||||
function sendFile(res: http.ServerResponse, filepath: string, contentType: string) {
|
||||
fs.readFile(filepath, (err, body) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
} else {
|
||||
res.writeHead(200, {
|
||||
'Content-Length': body.length,
|
||||
'Content-Type': contentType
|
||||
});
|
||||
res.end(body);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function callback(nonce: string, reqUrl: url.Url): Promise<string> {
|
||||
const query = reqUrl.query;
|
||||
if (!query || typeof query === 'string') {
|
||||
throw new Error('No query received.');
|
||||
}
|
||||
|
||||
let error = query.error_description || query.error;
|
||||
|
||||
if (!error) {
|
||||
const state = (query.state as string) || '';
|
||||
const receivedNonce = (state.split(',')[1] || '').replace(/ /g, '+');
|
||||
if (receivedNonce !== nonce) {
|
||||
error = 'Nonce does not match.';
|
||||
}
|
||||
}
|
||||
|
||||
const code = query.code as string;
|
||||
if (!error && code) {
|
||||
return code;
|
||||
}
|
||||
|
||||
throw new Error((error as string) || 'No code received.');
|
||||
}
|
||||
|
||||
export function createServer(nonce: string) {
|
||||
type RedirectResult = { req: http.IncomingMessage; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; };
|
||||
let deferredRedirect: Deferred<RedirectResult>;
|
||||
const redirectPromise = new Promise<RedirectResult>((resolve, reject) => deferredRedirect = { resolve, reject });
|
||||
|
||||
type CodeResult = { code: string; res: http.ServerResponse; } | { err: any; res: http.ServerResponse; };
|
||||
let deferredCode: Deferred<CodeResult>;
|
||||
const codePromise = new Promise<CodeResult>((resolve, reject) => deferredCode = { resolve, reject });
|
||||
|
||||
const codeTimer = setTimeout(() => {
|
||||
deferredCode.reject(new Error('Timeout waiting for code'));
|
||||
}, 5 * 60 * 1000);
|
||||
|
||||
function cancelCodeTimer() {
|
||||
clearTimeout(codeTimer);
|
||||
}
|
||||
|
||||
const server = http.createServer(function (req, res) {
|
||||
const reqUrl = url.parse(req.url!, /* parseQueryString */ true);
|
||||
switch (reqUrl.pathname) {
|
||||
case '/signin':
|
||||
const receivedNonce = ((reqUrl.query.nonce as string) || '').replace(/ /g, '+');
|
||||
if (receivedNonce === nonce) {
|
||||
deferredRedirect.resolve({ req, res });
|
||||
} else {
|
||||
const err = new Error('Nonce does not match.');
|
||||
deferredRedirect.resolve({ err, res });
|
||||
}
|
||||
break;
|
||||
case '/':
|
||||
sendFile(res, getPathFromAmdModule(require, '../common/auth.html'), 'text/html; charset=utf-8');
|
||||
break;
|
||||
case '/auth.css':
|
||||
sendFile(res, getPathFromAmdModule(require, '../common/auth.css'), 'text/css; charset=utf-8');
|
||||
break;
|
||||
case '/callback':
|
||||
deferredCode.resolve(callback(nonce, reqUrl)
|
||||
.then(code => ({ code, res }), err => ({ err, res })));
|
||||
break;
|
||||
default:
|
||||
res.writeHead(404);
|
||||
res.end();
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
codePromise.then(cancelCodeTimer, cancelCodeTimer);
|
||||
return {
|
||||
server,
|
||||
redirectPromise,
|
||||
codePromise
|
||||
};
|
||||
}
|
||||
276
src/vs/platform/auth/electron-browser/authTokenService.ts
Normal file
276
src/vs/platform/auth/electron-browser/authTokenService.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as crypto from 'crypto';
|
||||
import * as https from 'https';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { ICredentialsService } from 'vs/platform/credentials/common/credentials';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { shell } from 'electron';
|
||||
import { createServer, startServer } from 'vs/platform/auth/electron-browser/authServer';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
|
||||
const SERVICE_NAME = 'VS Code';
|
||||
const ACCOUNT = 'MyAccount';
|
||||
|
||||
const activeDirectoryResourceId = 'https://management.core.windows.net/';
|
||||
|
||||
function toQuery(obj: any): string {
|
||||
return Object.keys(obj).map(key => `${key}=${obj[key]}`).join('&');
|
||||
}
|
||||
|
||||
function toBase64UrlEncoding(base64string: string) {
|
||||
return base64string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_'); // Need to use base64url encoding
|
||||
}
|
||||
|
||||
export interface IToken {
|
||||
expiresIn: string; // How long access token is valid, in seconds
|
||||
expiresOn: string; // When the access token expires in epoch time
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
}
|
||||
|
||||
export class AuthTokenService extends Disposable implements IAuthTokenService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private _status: AuthTokenStatus = AuthTokenStatus.Initializing;
|
||||
get status(): AuthTokenStatus { return this._status; }
|
||||
private _onDidChangeStatus: Emitter<AuthTokenStatus> = this._register(new Emitter<AuthTokenStatus>());
|
||||
readonly onDidChangeStatus: Event<AuthTokenStatus> = this._onDidChangeStatus.event;
|
||||
|
||||
public readonly _onDidGetCallback: Emitter<URI> = this._register(new Emitter<URI>());
|
||||
readonly onDidGetCallback: Event<URI> = this._onDidGetCallback.event;
|
||||
|
||||
private _activeToken: IToken | undefined;
|
||||
|
||||
constructor(
|
||||
@ICredentialsService private readonly credentialsService: ICredentialsService,
|
||||
@IProductService private readonly productService: IProductService
|
||||
) {
|
||||
super();
|
||||
if (!this.productService.auth) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT).then(storedRefreshToken => {
|
||||
if (storedRefreshToken) {
|
||||
this.refresh(storedRefreshToken);
|
||||
} else {
|
||||
this.setStatus(AuthTokenStatus.SignedOut);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public async login(): Promise<void> {
|
||||
if (!this.productService.auth) {
|
||||
throw new Error('Authentication is not configured.');
|
||||
}
|
||||
|
||||
this.setStatus(AuthTokenStatus.SigningIn);
|
||||
|
||||
const nonce = generateUuid();
|
||||
const { server, redirectPromise, codePromise } = createServer(nonce);
|
||||
|
||||
try {
|
||||
const port = await startServer(server);
|
||||
shell.openExternal(`http://localhost:${port}/signin?nonce=${encodeURIComponent(nonce)}`);
|
||||
|
||||
const redirectReq = await redirectPromise;
|
||||
if ('err' in redirectReq) {
|
||||
const { err, res } = redirectReq;
|
||||
res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unkown error')}` });
|
||||
res.end();
|
||||
throw err;
|
||||
}
|
||||
|
||||
const host = redirectReq.req.headers.host || '';
|
||||
const updatedPortStr = (/^[^:]+:(\d+)$/.exec(Array.isArray(host) ? host[0] : host) || [])[1];
|
||||
const updatedPort = updatedPortStr ? parseInt(updatedPortStr, 10) : port;
|
||||
|
||||
const state = `${updatedPort},${encodeURIComponent(nonce)}`;
|
||||
|
||||
const codeVerifier = toBase64UrlEncoding(crypto.randomBytes(32).toString('base64'));
|
||||
const codeChallenge = toBase64UrlEncoding(crypto.createHash('sha256').update(codeVerifier).digest('base64'));
|
||||
|
||||
let uri = URI.parse(this.productService.auth.loginUrl);
|
||||
uri = uri.with({
|
||||
query: `response_type=code&client_id=${encodeURIComponent(this.productService.auth.clientId)}&redirect_uri=${this.productService.auth.redirectUrl}&state=${encodeURIComponent(state)}&resource=${activeDirectoryResourceId}&prompt=select_account&code_challenge_method=S256&code_challenge=${codeChallenge}`
|
||||
});
|
||||
|
||||
await redirectReq.res.writeHead(302, { Location: uri.toString(true) });
|
||||
redirectReq.res.end();
|
||||
|
||||
const codeRes = await codePromise;
|
||||
const res = codeRes.res;
|
||||
|
||||
try {
|
||||
if ('err' in codeRes) {
|
||||
throw codeRes.err;
|
||||
}
|
||||
const token = await this.exchangeCodeForToken(codeRes.code, codeVerifier);
|
||||
this.setToken(token);
|
||||
res.writeHead(302, { Location: '/' });
|
||||
res.end();
|
||||
} catch (err) {
|
||||
res.writeHead(302, { Location: `/?error=${encodeURIComponent(err && err.message || 'Unkown error')}` });
|
||||
res.end();
|
||||
}
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
server.close();
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public getToken(): Promise<string | undefined> {
|
||||
return Promise.resolve(this._activeToken?.accessToken);
|
||||
}
|
||||
|
||||
public async refreshToken(): Promise<void> {
|
||||
if (!this._activeToken) {
|
||||
throw new Error('No token to refresh');
|
||||
}
|
||||
|
||||
this.refresh(this._activeToken.refreshToken);
|
||||
}
|
||||
|
||||
private setToken(token: IToken) {
|
||||
this._activeToken = token;
|
||||
this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token.refreshToken);
|
||||
this.setStatus(AuthTokenStatus.SignedIn);
|
||||
}
|
||||
|
||||
private exchangeCodeForToken(code: string, codeVerifier: string): Promise<IToken> {
|
||||
return new Promise((resolve: (value: IToken) => void, reject) => {
|
||||
try {
|
||||
if (!this.productService.auth) {
|
||||
throw new Error('Authentication is not configured.');
|
||||
}
|
||||
|
||||
const postData = toQuery({
|
||||
grant_type: 'authorization_code',
|
||||
code: code,
|
||||
client_id: this.productService.auth?.clientId,
|
||||
code_verifier: codeVerifier,
|
||||
redirect_uri: this.productService.auth?.redirectUrl
|
||||
});
|
||||
|
||||
const tokenUrl = URI.parse(this.productService.auth.tokenUrl);
|
||||
|
||||
const post = https.request({
|
||||
host: tokenUrl.authority,
|
||||
path: tokenUrl.path,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': postData.length
|
||||
}
|
||||
}, result => {
|
||||
const buffer: Buffer[] = [];
|
||||
result.on('data', (chunk: Buffer) => {
|
||||
buffer.push(chunk);
|
||||
});
|
||||
result.on('end', () => {
|
||||
if (result.statusCode === 200) {
|
||||
const json = JSON.parse(Buffer.concat(buffer).toString());
|
||||
resolve({
|
||||
expiresIn: json.access_token,
|
||||
expiresOn: json.expires_on,
|
||||
accessToken: json.access_token,
|
||||
refreshToken: json.refresh_token
|
||||
});
|
||||
} else {
|
||||
reject(new Error('Bad!'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
post.write(postData);
|
||||
|
||||
post.end();
|
||||
post.on('error', err => {
|
||||
reject(err);
|
||||
});
|
||||
|
||||
} catch (e) {
|
||||
reject(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async refresh(refreshToken: string): Promise<void> {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.productService.auth) {
|
||||
throw new Error('Authentication is not configured.');
|
||||
}
|
||||
|
||||
this.setStatus(AuthTokenStatus.RefreshingToken);
|
||||
const postData = toQuery({
|
||||
refresh_token: refreshToken,
|
||||
client_id: this.productService.auth?.clientId,
|
||||
grant_type: 'refresh_token',
|
||||
resource: activeDirectoryResourceId
|
||||
});
|
||||
|
||||
const tokenUrl = URI.parse(this.productService.auth.tokenUrl);
|
||||
|
||||
const post = https.request({
|
||||
host: tokenUrl.authority,
|
||||
path: tokenUrl.path,
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded',
|
||||
'Content-Length': postData.length
|
||||
}
|
||||
}, result => {
|
||||
const buffer: Buffer[] = [];
|
||||
result.on('data', (chunk: Buffer) => {
|
||||
buffer.push(chunk);
|
||||
});
|
||||
result.on('end', () => {
|
||||
if (result.statusCode === 200) {
|
||||
const json = JSON.parse(Buffer.concat(buffer).toString());
|
||||
this.setToken({
|
||||
expiresIn: json.access_token,
|
||||
expiresOn: json.expires_on,
|
||||
accessToken: json.access_token,
|
||||
refreshToken: json.refresh_token
|
||||
});
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error('Refreshing token failed.'));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
post.write(postData);
|
||||
|
||||
post.end();
|
||||
post.on('error', err => {
|
||||
this.setStatus(AuthTokenStatus.SignedOut);
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
await this.credentialsService.deletePassword(SERVICE_NAME, ACCOUNT);
|
||||
this._activeToken = undefined;
|
||||
this.setStatus(AuthTokenStatus.SignedOut);
|
||||
}
|
||||
|
||||
private setStatus(status: AuthTokenStatus): void {
|
||||
if (this._status !== status) {
|
||||
this._status = status;
|
||||
this._onDidChangeStatus.fire(status);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import * as uuid from 'vs/base/common/uuid';
|
||||
import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry';
|
||||
import { testFile } from 'vs/base/test/node/utils';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { ConfigurationTarget } from 'vs/platform/configuration/common/configuration';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
suite('ConfigurationService - Node', () => {
|
||||
|
||||
@@ -94,10 +96,11 @@ suite('ConfigurationService - Node', () => {
|
||||
const service = new ConfigurationService(URI.file(res.testFile));
|
||||
await service.initialize();
|
||||
return new Promise((c, e) => {
|
||||
const disposable = service.onDidChangeConfiguration(() => {
|
||||
const disposable = Event.filter(service.onDidChangeConfiguration, e => e.source === ConfigurationTarget.USER)(async (e) => {
|
||||
disposable.dispose();
|
||||
assert.equal(service.getValue('foo'), 'bar');
|
||||
service.dispose();
|
||||
await res.cleanUp();
|
||||
c();
|
||||
});
|
||||
fs.writeFileSync(res.testFile, '{ "foo": "bar" }');
|
||||
|
||||
@@ -640,7 +640,7 @@ export class ContextKeyAndExpr implements ContextKeyExpr {
|
||||
|
||||
if (e instanceof ContextKeyOrExpr) {
|
||||
// Not allowed, because we don't have parens!
|
||||
throw new Error(`It is not allowed to have an or expression here due to lack of parens!`);
|
||||
throw new Error(`It is not allowed to have an or expression here due to lack of parens! For example "a && (b||c)" is not supported, use "(a&&b) || (a&&c)" instead.`);
|
||||
}
|
||||
|
||||
expr.push(e);
|
||||
|
||||
@@ -7,11 +7,11 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IContextMenuDelegate } from 'vs/base/browser/contextmenu';
|
||||
import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview';
|
||||
import { AnchorAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview';
|
||||
|
||||
export const IContextViewService = createDecorator<IContextViewService>('contextViewService');
|
||||
|
||||
export interface IContextViewService {
|
||||
export interface IContextViewService extends IContextViewProvider {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
@@ -41,4 +41,4 @@ export interface IContextMenuService {
|
||||
|
||||
showContextMenu(delegate: IContextMenuDelegate): void;
|
||||
onDidContextMenu: Event<void>; // TODO@isidor these event should be removed once we get async context menus
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,8 +102,6 @@ export class ExtensionHostDebugChannelClient extends Disposable implements IExte
|
||||
}
|
||||
|
||||
openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise<void> {
|
||||
// TODO@Isidor
|
||||
//return this.channel.call('openExtensionDevelopmentHostWindow', [args, env]);
|
||||
return Promise.resolve();
|
||||
return this.channel.call('openExtensionDevelopmentHostWindow', [args, env]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ import { virtualMachineHint } from 'vs/base/node/id';
|
||||
import { IMachineInfo, WorkspaceStats, WorkspaceStatItem, PerformanceInfo, SystemInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError, isRemoteDiagnosticError, IWorkspaceInformation } from 'vs/platform/diagnostics/common/diagnostics';
|
||||
import { readdir, stat, exists, readFile } from 'fs';
|
||||
import { join, basename } from 'vs/base/common/path';
|
||||
import { parse, ParseError } from 'vs/base/common/json';
|
||||
import { parse, ParseError, getNodeType } from 'vs/base/common/json';
|
||||
import { listProcesses } from 'vs/base/node/ps';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { repeat, pad } from 'vs/base/common/strings';
|
||||
@@ -223,7 +223,7 @@ export function collectLaunchConfigs(folder: string): Promise<WorkspaceStatItem[
|
||||
return resolve([]);
|
||||
}
|
||||
|
||||
if (json['configurations']) {
|
||||
if (getNodeType(json) === 'object' && json['configurations']) {
|
||||
for (const each of json['configurations']) {
|
||||
const type = each['type'];
|
||||
if (type) {
|
||||
|
||||
@@ -141,7 +141,6 @@ export interface IOpenDialogOptions {
|
||||
availableFileSystems?: readonly string[];
|
||||
}
|
||||
|
||||
|
||||
export const IDialogService = createDecorator<IDialogService>('dialogService');
|
||||
|
||||
export interface IDialogOptions {
|
||||
@@ -240,23 +239,34 @@ export interface IFileDialogService {
|
||||
*/
|
||||
showSaveDialog(options: ISaveDialogOptions): Promise<URI | undefined>;
|
||||
|
||||
/**
|
||||
* Shows a confirm dialog for saving 1-N files.
|
||||
*/
|
||||
showSaveConfirm(fileNamesOrResources: (string | URI)[]): Promise<ConfirmResult>;
|
||||
|
||||
/**
|
||||
* Shows a open file dialog and returns the chosen file URI.
|
||||
*/
|
||||
showOpenDialog(options: IOpenDialogOptions): Promise<URI[] | undefined>;
|
||||
}
|
||||
|
||||
export const enum ConfirmResult {
|
||||
SAVE,
|
||||
DONT_SAVE,
|
||||
CANCEL
|
||||
}
|
||||
|
||||
const MAX_CONFIRM_FILES = 10;
|
||||
export function getConfirmMessage(start: string, resourcesToConfirm: readonly URI[]): string {
|
||||
export function getConfirmMessage(start: string, fileNamesOrResources: readonly (string | URI)[]): string {
|
||||
const message = [start];
|
||||
message.push('');
|
||||
message.push(...resourcesToConfirm.slice(0, MAX_CONFIRM_FILES).map(r => basename(r)));
|
||||
message.push(...fileNamesOrResources.slice(0, MAX_CONFIRM_FILES).map(fileNameOrResource => typeof fileNameOrResource === 'string' ? fileNameOrResource : basename(fileNameOrResource)));
|
||||
|
||||
if (resourcesToConfirm.length > MAX_CONFIRM_FILES) {
|
||||
if (resourcesToConfirm.length - MAX_CONFIRM_FILES === 1) {
|
||||
if (fileNamesOrResources.length > MAX_CONFIRM_FILES) {
|
||||
if (fileNamesOrResources.length - MAX_CONFIRM_FILES === 1) {
|
||||
message.push(localize('moreFile', "...1 additional file not shown"));
|
||||
} else {
|
||||
message.push(localize('moreFiles', "...{0} additional files not shown", resourcesToConfirm.length - MAX_CONFIRM_FILES));
|
||||
message.push(localize('moreFiles', "...{0} additional files not shown", fileNamesOrResources.length - MAX_CONFIRM_FILES));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ export class DownloadService implements IDownloadService {
|
||||
await this.fileService.writeFile(target, context.stream);
|
||||
} else {
|
||||
const message = await asText(context);
|
||||
return Promise.reject(new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`));
|
||||
throw new Error(`Expected 200, got back ${context.res.statusCode} instead.\n\n${message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ export class Driver implements IDriver, IWindowDriverRegistry {
|
||||
private options: IDriverOptions,
|
||||
@IWindowsMainService private readonly windowsMainService: IWindowsMainService,
|
||||
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
|
||||
@IElectronMainService private readonly electronMainService: any // {{SQL CARBON EDIT}} remove interface, naster work around
|
||||
@IElectronMainService private readonly electronMainService: IElectronMainService
|
||||
) { }
|
||||
|
||||
async registerWindowDriver(windowId: number): Promise<IDriverOptions> {
|
||||
@@ -212,7 +212,7 @@ export async function serve(
|
||||
instantiationService: IInstantiationService
|
||||
): Promise<IDisposable> {
|
||||
const verbose = environmentService.driverVerbose;
|
||||
const driver = instantiationService.createInstance(Driver, windowServer, { verbose });
|
||||
const driver = instantiationService.createInstance(Driver as any, windowServer, { verbose }) as Driver; // {{SQL CARBON EDIT}} strict-null-check...i guess?
|
||||
|
||||
const windowDriverRegistryChannel = new WindowDriverRegistryChannel(driver);
|
||||
windowServer.registerChannel('windowDriverRegistry', windowDriverRegistryChannel);
|
||||
|
||||
@@ -10,7 +10,7 @@ import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window';
|
||||
import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
|
||||
import { IOpenedWindow, OpenContext, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows';
|
||||
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
|
||||
import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { isMacintosh } from 'vs/base/common/platform';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
@@ -18,7 +18,6 @@ import { AddFirstParameterToFunctions } from 'vs/base/common/types';
|
||||
import { IDialogMainService } from 'vs/platform/dialogs/electron-main/dialogs';
|
||||
import { dirExists } from 'vs/base/node/pfs';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv';
|
||||
import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
|
||||
@@ -100,6 +99,7 @@ export class ElectronMainService implements IElectronMainService {
|
||||
cli: this.environmentService.args,
|
||||
forceNewWindow: options.forceNewWindow,
|
||||
forceReuseWindow: options.forceReuseWindow,
|
||||
preferNewWindow: options.preferNewWindow,
|
||||
diffMode: options.diffMode,
|
||||
addMode: options.addMode,
|
||||
gotoLineMode: options.gotoLineMode,
|
||||
@@ -407,24 +407,6 @@ export class ElectronMainService implements IElectronMainService {
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Debug
|
||||
|
||||
// TODO@Isidor move into debug IPC channel (https://github.com/microsoft/vscode/issues/81060)
|
||||
|
||||
async openExtensionDevelopmentHostWindow(windowId: number, args: string[], env: IProcessEnvironment): Promise<void> {
|
||||
const pargs = parseArgs(args, OPTIONS);
|
||||
const extDevPaths = pargs.extensionDevelopmentPath;
|
||||
if (extDevPaths) {
|
||||
this.windowsMainService.openExtensionDevelopmentHostWindow(extDevPaths, {
|
||||
context: OpenContext.API,
|
||||
cli: pargs,
|
||||
userEnv: Object.keys(env).length > 0 ? env : undefined
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
private windowById(windowId: number | undefined): ICodeWindow | undefined {
|
||||
if (typeof windowId !== 'number') {
|
||||
return undefined;
|
||||
|
||||
@@ -9,7 +9,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { IWindowOpenable, IOpenEmptyWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows';
|
||||
import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs';
|
||||
import { ISerializableCommandAction } from 'vs/platform/actions/common/actions';
|
||||
import { IProcessEnvironment } from 'vs/base/common/platform';
|
||||
import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window';
|
||||
|
||||
export const IElectronService = createDecorator<IElectronService>('electronService');
|
||||
@@ -85,7 +84,4 @@ export interface IElectronService {
|
||||
|
||||
// Connectivity
|
||||
resolveProxy(url: string): Promise<string | undefined>;
|
||||
|
||||
// Debug (TODO@Isidor move into debug IPC channel (https://github.com/microsoft/vscode/issues/81060)
|
||||
openExtensionDevelopmentHostWindow(args: string[], env: IProcessEnvironment): Promise<void>;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { IUserHomeProvider } from 'vs/base/common/labels';
|
||||
|
||||
export interface ParsedArgs {
|
||||
_: string[];
|
||||
@@ -91,6 +92,8 @@ export interface ParsedArgs {
|
||||
'js-flags'?: string;
|
||||
'disable-gpu'?: boolean;
|
||||
'nolazy'?: boolean;
|
||||
'force-device-scale-factor'?: string;
|
||||
'force-renderer-accessibility'?: boolean;
|
||||
}
|
||||
|
||||
export const IEnvironmentService = createDecorator<IEnvironmentService>('environmentService');
|
||||
@@ -106,7 +109,7 @@ export interface IExtensionHostDebugParams extends IDebugParams {
|
||||
|
||||
export const BACKUPS = 'Backups';
|
||||
|
||||
export interface IEnvironmentService {
|
||||
export interface IEnvironmentService extends IUserHomeProvider {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
@@ -119,8 +122,6 @@ export interface IEnvironmentService {
|
||||
userHome: string;
|
||||
userDataPath: string;
|
||||
|
||||
appNameLong: string;
|
||||
appQuality?: string;
|
||||
appSettingsHome: URI;
|
||||
|
||||
// user roaming data
|
||||
@@ -133,6 +134,7 @@ export interface IEnvironmentService {
|
||||
// sync resources
|
||||
userDataSyncLogResource: URI;
|
||||
settingsSyncPreviewResource: URI;
|
||||
keybindingsSyncPreviewResource: URI;
|
||||
|
||||
machineSettingsHome: URI;
|
||||
machineSettingsResource: URI;
|
||||
@@ -151,6 +153,7 @@ export interface IEnvironmentService {
|
||||
extensionsPath?: string;
|
||||
extensionDevelopmentLocationURI?: URI[];
|
||||
extensionTestsLocationURI?: URI;
|
||||
logExtensionHostCommunication?: boolean;
|
||||
|
||||
debugExtensionHost: IExtensionHostDebugParams;
|
||||
|
||||
|
||||
@@ -7,13 +7,10 @@ import * as minimist from 'vscode-minimist';
|
||||
import * as os from 'os';
|
||||
import { localize } from 'vs/nls';
|
||||
import { ParsedArgs } from 'vs/platform/environment/common/environment';
|
||||
import { join } from 'vs/base/common/path';
|
||||
import { writeFileSync } from 'vs/base/node/pfs';
|
||||
|
||||
/**
|
||||
* This code is also used by standalone cli's. Avoid adding any other dependencies.
|
||||
*/
|
||||
|
||||
const helpCategories = {
|
||||
o: localize('optionsUpperCase', "Options"),
|
||||
e: localize('extensionsManagement', "Extensions Management"),
|
||||
@@ -129,6 +126,8 @@ export const OPTIONS: OptionDescriptions<Required<ParsedArgs>> = {
|
||||
'inspect': { type: 'string' },
|
||||
'inspect-brk': { type: 'string' },
|
||||
'nolazy': { type: 'boolean' }, // node inspect
|
||||
'force-device-scale-factor': { type: 'string' },
|
||||
'force-renderer-accessibility': { type: 'boolean' },
|
||||
'_urls': { type: 'string[]' },
|
||||
|
||||
_: { type: 'string[]' } // main arguments
|
||||
@@ -170,7 +169,7 @@ export function parseArgs<T>(args: string[], options: OptionDescriptions<T>, err
|
||||
}
|
||||
}
|
||||
}
|
||||
// remote aliases to avoid confusion
|
||||
// remove aliases to avoid confusion
|
||||
const parsedArgs = minimist(args, { string, boolean, alias });
|
||||
|
||||
const cleanedArgs: any = {};
|
||||
@@ -193,7 +192,7 @@ export function parseArgs<T>(args: string[], options: OptionDescriptions<T>, err
|
||||
delete parsedArgs[o.deprecates];
|
||||
}
|
||||
|
||||
if (val) {
|
||||
if (typeof val !== 'undefined') {
|
||||
if (o.type === 'string[]') {
|
||||
if (val && !Array.isArray(val)) {
|
||||
val = [val];
|
||||
@@ -319,33 +318,3 @@ export function buildVersionMessage(version: string | undefined, commit: string
|
||||
return `${version || localize('unknownVersion', "Unknown version")}\n${commit || localize('unknownCommit', "Unknown commit")}\n${process.arch}`;
|
||||
}
|
||||
|
||||
export function addArg(argv: string[], ...args: string[]): string[] {
|
||||
const endOfArgsMarkerIndex = argv.indexOf('--');
|
||||
if (endOfArgsMarkerIndex === -1) {
|
||||
argv.push(...args);
|
||||
} else {
|
||||
// if the we have an argument "--" (end of argument marker)
|
||||
// we cannot add arguments at the end. rather, we add
|
||||
// arguments before the "--" marker.
|
||||
argv.splice(endOfArgsMarkerIndex, 0, ...args);
|
||||
}
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
export function createWaitMarkerFile(verbose?: boolean): string | undefined {
|
||||
const randomWaitMarkerPath = join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10));
|
||||
|
||||
try {
|
||||
writeFileSync(randomWaitMarkerPath, '');
|
||||
if (verbose) {
|
||||
console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`);
|
||||
}
|
||||
return randomWaitMarkerPath;
|
||||
} catch (err) {
|
||||
if (verbose) {
|
||||
console.error(`Failed to create marker file for --wait: ${err}`);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,3 +69,17 @@ export function parseCLIProcessArgv(processArgv: string[]): ParsedArgs {
|
||||
|
||||
return parseAndValidate(args, true);
|
||||
}
|
||||
|
||||
export function addArg(argv: string[], ...args: string[]): string[] {
|
||||
const endOfArgsMarkerIndex = argv.indexOf('--');
|
||||
if (endOfArgsMarkerIndex === -1) {
|
||||
argv.push(...args);
|
||||
} else {
|
||||
// if the we have an argument "--" (end of argument marker)
|
||||
// we cannot add arguments at the end. rather, we add
|
||||
// arguments before the "--" marker.
|
||||
argv.splice(endOfArgsMarkerIndex, 0, ...args);
|
||||
}
|
||||
|
||||
return argv;
|
||||
}
|
||||
|
||||
@@ -102,10 +102,6 @@ export class EnvironmentService implements IEnvironmentService {
|
||||
return parseUserDataDir(this._args, process);
|
||||
}
|
||||
|
||||
get appNameLong(): string { return product.nameLong; }
|
||||
|
||||
get appQuality(): string | undefined { return product.quality; }
|
||||
|
||||
@memoize
|
||||
get appSettingsHome(): URI { return URI.file(path.join(this.userDataPath, 'User')); }
|
||||
|
||||
@@ -118,6 +114,9 @@ export class EnvironmentService implements IEnvironmentService {
|
||||
@memoize
|
||||
get settingsSyncPreviewResource(): URI { return resources.joinPath(this.userRoamingDataHome, '.settings.json'); }
|
||||
|
||||
@memoize
|
||||
get keybindingsSyncPreviewResource(): URI { return resources.joinPath(this.userRoamingDataHome, '.keybindings.json'); }
|
||||
|
||||
@memoize
|
||||
get userDataSyncLogResource(): URI { return URI.file(path.join(this.logsPath, 'userDataSync.log')); }
|
||||
|
||||
@@ -239,6 +238,8 @@ export class EnvironmentService implements IEnvironmentService {
|
||||
|
||||
@memoize
|
||||
get debugExtensionHost(): IExtensionHostDebugParams { return parseExtensionHostPort(this._args, this.isBuilt); }
|
||||
@memoize
|
||||
get logExtensionHostCommunication(): boolean { return !!this.args.logExtensionHostCommunication; }
|
||||
|
||||
get isBuilt(): boolean { return !process.env['VSCODE_DEV']; }
|
||||
get verbose(): boolean { return !!this._args.verbose; }
|
||||
|
||||
28
src/vs/platform/environment/node/waitMarkerFile.ts
Normal file
28
src/vs/platform/environment/node/waitMarkerFile.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* This code is also used by standalone cli's. Avoid adding dependencies to keep the size of the cli small.
|
||||
*/
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as os from 'os';
|
||||
import * as fs from 'fs';
|
||||
|
||||
export function createWaitMarkerFile(verbose?: boolean): string | undefined {
|
||||
const randomWaitMarkerPath = path.join(os.tmpdir(), Math.random().toString(36).replace(/[^a-z]+/g, '').substr(0, 10));
|
||||
|
||||
try {
|
||||
fs.writeFileSync(randomWaitMarkerPath, ''); // use built-in fs to avoid dragging in more dependencies
|
||||
if (verbose) {
|
||||
console.log(`Marker file for --wait created: ${randomWaitMarkerPath}`);
|
||||
}
|
||||
return randomWaitMarkerPath;
|
||||
} catch (err) {
|
||||
if (verbose) {
|
||||
console.error(`Failed to create marker file for --wait: ${err}`);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
@@ -746,7 +746,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
if (extension.assets.manifest) {
|
||||
return this.getAsset(extension.assets.manifest, {}, token)
|
||||
.then(asText)
|
||||
.then(JSON.parse);
|
||||
.then(text => text ? JSON.parse(text) : null);
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
@@ -756,7 +756,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
if (asset) {
|
||||
return this.getAsset(asset[1])
|
||||
.then(asText)
|
||||
.then(JSON.parse);
|
||||
.then(text => text ? JSON.parse(text) : null);
|
||||
}
|
||||
return Promise.resolve(null);
|
||||
}
|
||||
@@ -823,17 +823,6 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
}
|
||||
|
||||
const message = getErrorMessage(err);
|
||||
type GalleryServiceRequestErrorClassification = {
|
||||
url: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
cdn: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true };
|
||||
message: { classification: 'CallstackOrException', purpose: 'FeatureInsight' };
|
||||
};
|
||||
type GalleryServiceRequestErrorEvent = {
|
||||
url: string;
|
||||
cdn: boolean;
|
||||
message: string;
|
||||
};
|
||||
this.telemetryService.publicLog2<GalleryServiceRequestErrorEvent, GalleryServiceRequestErrorClassification>('galleryService:requestError', { url, cdn: true, message });
|
||||
type GalleryServiceCDNFallbackClassification = {
|
||||
url: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
message: { classification: 'SystemMetaData', purpose: 'FeatureInsight' };
|
||||
@@ -845,15 +834,7 @@ export class ExtensionGalleryService implements IExtensionGalleryService {
|
||||
this.telemetryService.publicLog2<GalleryServiceCDNFallbackEvent, GalleryServiceCDNFallbackClassification>('galleryService:cdnFallback', { url, message });
|
||||
|
||||
const fallbackOptions = assign({}, options, { url: fallbackUrl });
|
||||
return this.requestService.request(fallbackOptions, token).then(undefined, err => {
|
||||
if (isPromiseCanceledError(err)) {
|
||||
return Promise.reject(err);
|
||||
}
|
||||
|
||||
const message = getErrorMessage(err);
|
||||
this.telemetryService.publicLog2<GalleryServiceRequestErrorEvent, GalleryServiceRequestErrorClassification>('galleryService:requestError', { url: fallbackUrl, cdn: false, message });
|
||||
return Promise.reject(err);
|
||||
});
|
||||
return this.requestService.request(fallbackOptions, token);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ export interface DidUninstallExtensionEvent {
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export const INSTALL_ERROR_NOT_SUPPORTED = 'notsupported';
|
||||
export const INSTALL_ERROR_MALICIOUS = 'malicious';
|
||||
export const INSTALL_ERROR_INCOMPATIBLE = 'incompatible';
|
||||
|
||||
|
||||
@@ -98,11 +98,11 @@ export class ExtensionsLifecycle extends Disposable {
|
||||
|
||||
// Catch all output coming from the process
|
||||
type Output = { data: string, format: string[] };
|
||||
extensionUninstallProcess.stdout.setEncoding('utf8');
|
||||
extensionUninstallProcess.stderr.setEncoding('utf8');
|
||||
extensionUninstallProcess.stdout!.setEncoding('utf8');
|
||||
extensionUninstallProcess.stderr!.setEncoding('utf8');
|
||||
|
||||
const onStdout = Event.fromNodeEventEmitter<string>(extensionUninstallProcess.stdout, 'data');
|
||||
const onStderr = Event.fromNodeEventEmitter<string>(extensionUninstallProcess.stderr, 'data');
|
||||
const onStdout = Event.fromNodeEventEmitter<string>(extensionUninstallProcess.stdout!, 'data');
|
||||
const onStderr = Event.fromNodeEventEmitter<string>(extensionUninstallProcess.stderr!, 'data');
|
||||
|
||||
// Log output
|
||||
onStdout(data => this.logService.info(extension.identifier.id, extension.manifest.version, `post-${lifecycleType}`, data));
|
||||
|
||||
@@ -153,7 +153,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
this.logService.trace('ExtensionManagementService#zip', extension.identifier.id);
|
||||
return this.collectFiles(extension)
|
||||
.then(files => zip(path.join(tmpdir(), generateUuid()), files))
|
||||
.then(path => URI.file(path));
|
||||
.then<URI>(path => URI.file(path));
|
||||
}
|
||||
|
||||
unzip(zipLocation: URI, type: ExtensionType): Promise<IExtensionIdentifier> {
|
||||
@@ -222,6 +222,16 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
} else if (semver.gt(existing.manifest.version, manifest.version)) {
|
||||
return this.uninstall(existing, true);
|
||||
}
|
||||
} else {
|
||||
// Remove the extension with same version if it is already uninstalled.
|
||||
// Installing a VSIX extension shall replace the existing extension always.
|
||||
return this.unsetUninstalledAndGetLocal(identifierWithVersion)
|
||||
.then(existing => {
|
||||
if (existing) {
|
||||
return this.removeExtension(existing, 'existing').then(null, e => Promise.reject(new Error(nls.localize('restartCode', "Please restart Azure Data Studio before reinstalling {0}.", manifest.displayName || manifest.name))));
|
||||
}
|
||||
return undefined;
|
||||
});
|
||||
}
|
||||
return undefined;
|
||||
})
|
||||
@@ -390,7 +400,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
return this.setUninstalled(extension)
|
||||
.then(() => this.removeUninstalledExtension(extension)
|
||||
.then(
|
||||
() => this.installFromGallery(galleryExtension),
|
||||
() => this.installFromGallery(galleryExtension).then(),
|
||||
e => Promise.reject(new Error(nls.localize('removeError', "Error while removing the extension: {0}. Please Quit and Start VS Code before trying again.", toErrorMessage(e))))));
|
||||
}
|
||||
return Promise.reject(new Error(nls.localize('Not a Marketplace extension', "Only Marketplace Extensions can be reinstalled")));
|
||||
@@ -541,10 +551,10 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
.then(galleryResult => {
|
||||
const extensionsToInstall = galleryResult.firstPage;
|
||||
return Promise.all(extensionsToInstall.map(e => this.installFromGallery(e)))
|
||||
.then(() => null, errors => this.rollback(extensionsToInstall).then(() => Promise.reject(errors), () => Promise.reject(errors)));
|
||||
.then(undefined, errors => this.rollback(extensionsToInstall).then(() => Promise.reject(errors), () => Promise.reject(errors)));
|
||||
});
|
||||
}
|
||||
return null;
|
||||
return undefined; // {{SQL CARBON EDIT}} strict-null-checks
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -565,7 +575,7 @@ export class ExtensionManagementService extends Disposable implements IExtension
|
||||
.then(installed => {
|
||||
const extensionToUninstall = installed.filter(e => areSameExtensions(e.identifier, extension.identifier))[0];
|
||||
if (extensionToUninstall) {
|
||||
return this.checkForDependenciesAndUninstall(extensionToUninstall, installed).then(() => null, error => Promise.reject(this.joinErrors(error)));
|
||||
return this.checkForDependenciesAndUninstall(extensionToUninstall, installed).then(undefined, error => Promise.reject(this.joinErrors(error)));
|
||||
} else {
|
||||
return Promise.reject(new Error(nls.localize('notInstalled', "Extension '{0}' is not installed.", extension.manifest.displayName || extension.manifest.name)));
|
||||
}
|
||||
|
||||
@@ -103,6 +103,17 @@ export interface IWebviewEditor {
|
||||
}[];
|
||||
}
|
||||
|
||||
export interface ICodeActionContributionAction {
|
||||
readonly kind: string;
|
||||
readonly title: string;
|
||||
readonly description?: string;
|
||||
}
|
||||
|
||||
export interface ICodeActionContribution {
|
||||
readonly languages: readonly string[];
|
||||
readonly actions: readonly ICodeActionContributionAction[];
|
||||
}
|
||||
|
||||
export interface IExtensionContributions {
|
||||
commands?: ICommand[];
|
||||
configuration?: IConfiguration | IConfiguration[];
|
||||
@@ -120,6 +131,7 @@ export interface IExtensionContributions {
|
||||
colors?: IColor[];
|
||||
localizations?: ILocalization[];
|
||||
readonly webviewEditors?: readonly IWebviewEditor[];
|
||||
readonly codeActions?: readonly ICodeActionContribution[];
|
||||
}
|
||||
|
||||
export type ExtensionKind = 'ui' | 'workspace' | 'web';
|
||||
@@ -151,7 +163,7 @@ export interface IExtensionManifest {
|
||||
readonly activationEvents?: string[];
|
||||
readonly extensionDependencies?: string[];
|
||||
readonly extensionPack?: string[];
|
||||
readonly extensionKind?: ExtensionKind;
|
||||
readonly extensionKind?: ExtensionKind | ExtensionKind[];
|
||||
readonly contributes?: IExtensionContributions;
|
||||
readonly repository?: { url: string; };
|
||||
readonly bugs?: { url: string; };
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, IDisposable, toDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle';
|
||||
import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED } from 'vs/platform/files/common/files';
|
||||
import { IFileService, IResolveFileOptions, FileChangesEvent, FileOperationEvent, IFileSystemProviderRegistrationEvent, IFileSystemProvider, IFileStat, IResolveFileResult, ICreateFileOptions, IFileSystemProviderActivationEvent, FileOperationError, FileOperationResult, FileOperation, FileSystemProviderCapabilities, FileType, toFileSystemProviderErrorCode, FileSystemProviderErrorCode, IStat, IFileStatWithMetadata, IResolveMetadataFileOptions, etag, hasReadWriteCapability, hasFileFolderCopyCapability, hasOpenReadWriteCloseCapability, toFileOperationResult, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileSystemProviderWithFileReadWriteCapability, IResolveFileResultWithMetadata, IWatchOptions, IWriteFileOptions, IReadFileOptions, IFileStreamContent, IFileContent, ETAG_DISABLED, hasFileReadStreamCapability, IFileSystemProviderWithFileReadStreamCapability, ensureFileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { isAbsolutePath, dirname, basename, joinPath, isEqual, isEqualOrParent } from 'vs/base/common/resources';
|
||||
@@ -13,10 +13,13 @@ import { TernarySearchTree } from 'vs/base/common/map';
|
||||
import { isNonEmptyArray, coalesce } from 'vs/base/common/arrays';
|
||||
import { getBaseLabel } from 'vs/base/common/labels';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { VSBuffer, VSBufferReadable, readableToBuffer, bufferToReadable, streamToBuffer, bufferToStream, VSBufferReadableStream, writeableBufferStream, VSBufferWriteableStream, isVSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { VSBuffer, VSBufferReadable, readableToBuffer, bufferToReadable, streamToBuffer, bufferToStream, VSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { isReadableStream, transform, ReadableStreamEvents, consumeReadableWithLimit, consumeStreamWithLimit } from 'vs/base/common/stream';
|
||||
import { Queue } from 'vs/base/common/async';
|
||||
import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { assign } from 'vs/base/common/objects';
|
||||
import { createReadStream } from 'vs/platform/files/common/io';
|
||||
|
||||
export class FileService extends Disposable implements IFileService {
|
||||
|
||||
@@ -118,14 +121,24 @@ export class FileService extends Disposable implements IFileService {
|
||||
return provider;
|
||||
}
|
||||
|
||||
private async withReadWriteProvider(resource: URI): Promise<IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability> {
|
||||
private async withReadProvider(resource: URI): Promise<IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability | IFileSystemProviderWithFileReadStreamCapability> {
|
||||
const provider = await this.withProvider(resource);
|
||||
|
||||
if (hasOpenReadWriteCloseCapability(provider) || hasReadWriteCapability(provider) || hasFileReadStreamCapability(provider)) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
throw new Error('Provider neither has FileReadWrite, FileReadStream nor FileOpenReadWriteClose capability which is needed for the read operation.');
|
||||
}
|
||||
|
||||
private async withWriteProvider(resource: URI): Promise<IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability> {
|
||||
const provider = await this.withProvider(resource);
|
||||
|
||||
if (hasOpenReadWriteCloseCapability(provider) || hasReadWriteCapability(provider)) {
|
||||
return provider;
|
||||
}
|
||||
|
||||
throw new Error('Provider neither has FileReadWrite nor FileOpenReadWriteClose capability which is needed for the operation.');
|
||||
throw new Error('Provider neither has FileReadWrite nor FileOpenReadWriteClose capability which is needed for the write operation.');
|
||||
}
|
||||
|
||||
//#endregion
|
||||
@@ -154,7 +167,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
}
|
||||
|
||||
// Bubble up any other error as is
|
||||
throw this.ensureError(error);
|
||||
throw ensureFileSystemProviderError(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,16 +209,19 @@ export class FileService extends Disposable implements IFileService {
|
||||
});
|
||||
}
|
||||
|
||||
private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat | { type: FileType } & Partial<IStat>, siblings: number | undefined, resolveMetadata: boolean, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStat>;
|
||||
private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat, siblings: number | undefined, resolveMetadata: true, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStatWithMetadata>;
|
||||
private async toFileStat(provider: IFileSystemProvider, resource: URI, stat: IStat | { type: FileType } & Partial<IStat>, siblings: number | undefined, resolveMetadata: boolean, recurse: (stat: IFileStat, siblings?: number) => boolean): Promise<IFileStat> {
|
||||
|
||||
// convert to file stat
|
||||
const fileStat: IFileStat = {
|
||||
resource,
|
||||
name: getBaseLabel(resource),
|
||||
isFile: (stat.type & FileType.File) !== 0,
|
||||
isDirectory: (stat.type & FileType.Directory) !== 0,
|
||||
isSymbolicLink: (stat.type & FileType.SymbolicLink) !== 0,
|
||||
isReadonly: !!(provider.capabilities & FileSystemProviderCapabilities.Readonly),
|
||||
mtime: stat.mtime,
|
||||
ctime: stat.ctime,
|
||||
size: stat.size,
|
||||
etag: etag({ mtime: stat.mtime, size: stat.size })
|
||||
};
|
||||
@@ -288,7 +304,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
}
|
||||
|
||||
async writeFile(resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: IWriteFileOptions): Promise<IFileStatWithMetadata> {
|
||||
const provider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(resource));
|
||||
const provider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(resource));
|
||||
|
||||
try {
|
||||
|
||||
@@ -300,17 +316,29 @@ export class FileService extends Disposable implements IFileService {
|
||||
await this.mkdirp(provider, dirname(resource));
|
||||
}
|
||||
|
||||
// write file: buffered
|
||||
if (hasOpenReadWriteCloseCapability(provider)) {
|
||||
await this.doWriteBuffered(provider, resource, bufferOrReadableOrStream instanceof VSBuffer ? bufferToReadable(bufferOrReadableOrStream) : bufferOrReadableOrStream);
|
||||
// optimization: if the provider has unbuffered write capability and the data
|
||||
// to write is a Readable, we consume up to 3 chunks and try to write the data
|
||||
// unbuffered to reduce the overhead. If the Readable has more data to provide
|
||||
// we continue to write buffered.
|
||||
if (hasReadWriteCapability(provider) && !(bufferOrReadableOrStream instanceof VSBuffer)) {
|
||||
if (isReadableStream(bufferOrReadableOrStream)) {
|
||||
bufferOrReadableOrStream = await consumeStreamWithLimit(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3);
|
||||
} else {
|
||||
bufferOrReadableOrStream = consumeReadableWithLimit(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3);
|
||||
}
|
||||
}
|
||||
|
||||
// write file: unbuffered
|
||||
else {
|
||||
// write file: unbuffered (only if data to write is a buffer, or the provider has no buffered write capability)
|
||||
if (!hasOpenReadWriteCloseCapability(provider) || (hasReadWriteCapability(provider) && bufferOrReadableOrStream instanceof VSBuffer)) {
|
||||
await this.doWriteUnbuffered(provider, resource, bufferOrReadableOrStream);
|
||||
}
|
||||
|
||||
// write file: buffered
|
||||
else {
|
||||
await this.doWriteBuffered(provider, resource, bufferOrReadableOrStream instanceof VSBuffer ? bufferToReadable(bufferOrReadableOrStream) : bufferOrReadableOrStream);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new FileOperationError(localize('err.write', "Unable to write file ({0})", this.ensureError(error).toString()), toFileOperationResult(error), options);
|
||||
throw new FileOperationError(localize('err.write', "Unable to write file ({0})", ensureFileSystemProviderError(error).toString()), toFileOperationResult(error), options);
|
||||
}
|
||||
|
||||
return this.resolve(resource, { resolveMetadata: true });
|
||||
@@ -333,7 +361,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
// mtime and etag, we bail out to prevent dirty writing.
|
||||
//
|
||||
// First, we check for a mtime that is in the future before we do more checks. The assumption is
|
||||
// that only the mtime is an indicator for a file that has changd on disk.
|
||||
// that only the mtime is an indicator for a file that has changed on disk.
|
||||
//
|
||||
// Second, if the mtime has advanced, we compare the size of the file on disk with our previous
|
||||
// one using the etag() function. Relying only on the mtime check has prooven to produce false
|
||||
@@ -353,7 +381,16 @@ export class FileService extends Disposable implements IFileService {
|
||||
}
|
||||
|
||||
async readFile(resource: URI, options?: IReadFileOptions): Promise<IFileContent> {
|
||||
const stream = await this.readFileStream(resource, options);
|
||||
const provider = await this.withReadProvider(resource);
|
||||
|
||||
const stream = await this.doReadAsFileStream(provider, resource, assign({
|
||||
// optimization: since we know that the caller does not
|
||||
// care about buffering, we indicate this to the reader.
|
||||
// this reduces all the overhead the buffered reading
|
||||
// has (open, read, close) if the provider supports
|
||||
// unbuffered reading.
|
||||
preferUnbuffered: true
|
||||
}, options || Object.create(null)));
|
||||
|
||||
return {
|
||||
...stream,
|
||||
@@ -362,7 +399,12 @@ export class FileService extends Disposable implements IFileService {
|
||||
}
|
||||
|
||||
async readFileStream(resource: URI, options?: IReadFileOptions): Promise<IFileStreamContent> {
|
||||
const provider = await this.withReadWriteProvider(resource);
|
||||
const provider = await this.withReadProvider(resource);
|
||||
|
||||
return this.doReadAsFileStream(provider, resource, options);
|
||||
}
|
||||
|
||||
private async doReadAsFileStream(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability | IFileSystemProviderWithFileReadStreamCapability, resource: URI, options?: IReadFileOptions & { preferUnbuffered?: boolean }): Promise<IFileStreamContent> {
|
||||
|
||||
// install a cancellation token that gets cancelled
|
||||
// when any error occurs. this allows us to resolve
|
||||
@@ -389,14 +431,19 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
let fileStreamPromise: Promise<VSBufferReadableStream>;
|
||||
|
||||
// read buffered
|
||||
if (hasOpenReadWriteCloseCapability(provider)) {
|
||||
fileStreamPromise = Promise.resolve(this.readFileBuffered(provider, resource, cancellableSource.token, options));
|
||||
// read unbuffered (only if either preferred, or the provider has no buffered read capability)
|
||||
if (!(hasOpenReadWriteCloseCapability(provider) || hasFileReadStreamCapability(provider)) || (hasReadWriteCapability(provider) && options?.preferUnbuffered)) {
|
||||
fileStreamPromise = this.readFileUnbuffered(provider, resource, options);
|
||||
}
|
||||
|
||||
// read unbuffered
|
||||
// read streamed (always prefer over primitive buffered read)
|
||||
else if (hasFileReadStreamCapability(provider)) {
|
||||
fileStreamPromise = Promise.resolve(this.readFileStreamed(provider, resource, cancellableSource.token, options));
|
||||
}
|
||||
|
||||
// read buffered
|
||||
else {
|
||||
fileStreamPromise = this.readFileUnbuffered(provider, resource, options);
|
||||
fileStreamPromise = Promise.resolve(this.readFileBuffered(provider, resource, cancellableSource.token, options));
|
||||
}
|
||||
|
||||
const [fileStat, fileStream] = await Promise.all([statPromise, fileStreamPromise]);
|
||||
@@ -406,74 +453,30 @@ export class FileService extends Disposable implements IFileService {
|
||||
value: fileStream
|
||||
};
|
||||
} catch (error) {
|
||||
throw new FileOperationError(localize('err.read', "Unable to read file ({0})", this.ensureError(error).toString()), toFileOperationResult(error), options);
|
||||
throw new FileOperationError(localize('err.read', "Unable to read file ({0})", ensureFileSystemProviderError(error).toString()), toFileOperationResult(error), options);
|
||||
}
|
||||
}
|
||||
|
||||
private readFileBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, token: CancellationToken, options?: IReadFileOptions): VSBufferReadableStream {
|
||||
const stream = writeableBufferStream();
|
||||
private readFileStreamed(provider: IFileSystemProviderWithFileReadStreamCapability, resource: URI, token: CancellationToken, options: IReadFileOptions = Object.create(null)): VSBufferReadableStream {
|
||||
const fileStream = provider.readFileStream(resource, options, token);
|
||||
|
||||
// do not await reading but simply return
|
||||
// the stream directly since it operates
|
||||
// via events. finally end the stream and
|
||||
// send through the possible error
|
||||
let error: Error | undefined = undefined;
|
||||
this.doReadFileBuffered(provider, resource, stream, token, options).then(undefined, err => error = err).finally(() => stream.end(error));
|
||||
|
||||
return stream;
|
||||
return this.transformFileReadStream(fileStream, options);
|
||||
}
|
||||
|
||||
private async doReadFileBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, stream: VSBufferWriteableStream, token: CancellationToken, options?: IReadFileOptions): Promise<void> {
|
||||
private readFileBuffered(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, token: CancellationToken, options: IReadFileOptions = Object.create(null)): VSBufferReadableStream {
|
||||
const fileStream = createReadStream(provider, resource, {
|
||||
...options,
|
||||
bufferSize: this.BUFFER_SIZE
|
||||
}, token);
|
||||
|
||||
// open handle through provider
|
||||
const handle = await provider.open(resource, { create: false });
|
||||
return this.transformFileReadStream(fileStream, options);
|
||||
}
|
||||
|
||||
try {
|
||||
let totalBytesRead = 0;
|
||||
let bytesRead = 0;
|
||||
let allowedRemainingBytes = (options && typeof options.length === 'number') ? options.length : undefined;
|
||||
|
||||
let buffer = VSBuffer.alloc(Math.min(this.BUFFER_SIZE, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : this.BUFFER_SIZE));
|
||||
|
||||
let posInFile = options && typeof options.position === 'number' ? options.position : 0;
|
||||
let posInBuffer = 0;
|
||||
do {
|
||||
// read from source (handle) at current position (pos) into buffer (buffer) at
|
||||
// buffer position (posInBuffer) up to the size of the buffer (buffer.byteLength).
|
||||
bytesRead = await provider.read(handle, posInFile, buffer.buffer, posInBuffer, buffer.byteLength - posInBuffer);
|
||||
|
||||
posInFile += bytesRead;
|
||||
posInBuffer += bytesRead;
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
if (typeof allowedRemainingBytes === 'number') {
|
||||
allowedRemainingBytes -= bytesRead;
|
||||
}
|
||||
|
||||
// when buffer full, create a new one and emit it through stream
|
||||
if (posInBuffer === buffer.byteLength) {
|
||||
stream.write(buffer);
|
||||
|
||||
buffer = VSBuffer.alloc(Math.min(this.BUFFER_SIZE, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : this.BUFFER_SIZE));
|
||||
|
||||
posInBuffer = 0;
|
||||
}
|
||||
} while (bytesRead > 0 && (typeof allowedRemainingBytes !== 'number' || allowedRemainingBytes > 0) && this.throwIfCancelled(token) && this.throwIfTooLarge(totalBytesRead, options));
|
||||
|
||||
// wrap up with last buffer (also respect maxBytes if provided)
|
||||
if (posInBuffer > 0) {
|
||||
let lastChunkLength = posInBuffer;
|
||||
if (typeof allowedRemainingBytes === 'number') {
|
||||
lastChunkLength = Math.min(posInBuffer, allowedRemainingBytes);
|
||||
}
|
||||
|
||||
stream.write(buffer.slice(0, lastChunkLength));
|
||||
}
|
||||
} catch (error) {
|
||||
throw this.ensureError(error);
|
||||
} finally {
|
||||
await provider.close(handle);
|
||||
}
|
||||
private transformFileReadStream(stream: ReadableStreamEvents<Uint8Array | VSBuffer>, options: IReadFileOptions): VSBufferReadableStream {
|
||||
return transform(stream, {
|
||||
data: data => data instanceof VSBuffer ? data : VSBuffer.wrap(data),
|
||||
error: error => new FileOperationError(localize('err.read', "Unable to read file ({0})", ensureFileSystemProviderError(error).toString()), toFileOperationResult(error), options)
|
||||
}, data => VSBuffer.concat(data));
|
||||
}
|
||||
|
||||
private async readFileUnbuffered(provider: IFileSystemProviderWithFileReadWriteCapability, resource: URI, options?: IReadFileOptions): Promise<VSBufferReadableStream> {
|
||||
@@ -489,43 +492,56 @@ export class FileService extends Disposable implements IFileService {
|
||||
buffer = buffer.slice(0, options.length);
|
||||
}
|
||||
|
||||
// Throw if file is too large to load
|
||||
this.validateReadFileLimits(buffer.byteLength, options);
|
||||
|
||||
return bufferToStream(VSBuffer.wrap(buffer));
|
||||
}
|
||||
|
||||
private async validateReadFile(resource: URI, options?: IReadFileOptions): Promise<IFileStatWithMetadata> {
|
||||
const stat = await this.resolve(resource, { resolveMetadata: true });
|
||||
|
||||
// Return early if resource is a directory
|
||||
// Throw if resource is a directory
|
||||
if (stat.isDirectory) {
|
||||
throw new FileOperationError(localize('fileIsDirectoryError', "Expected file {0} is actually a directory", this.resourceForError(resource)), FileOperationResult.FILE_IS_DIRECTORY, options);
|
||||
}
|
||||
|
||||
// Return early if file not modified since (unless disabled)
|
||||
// Throw if file not modified since (unless disabled)
|
||||
if (options && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED && options.etag === stat.etag) {
|
||||
throw new FileOperationError(localize('fileNotModifiedError', "File not modified since"), FileOperationResult.FILE_NOT_MODIFIED_SINCE, options);
|
||||
}
|
||||
|
||||
// Return early if file is too large to load
|
||||
if (options?.limits) {
|
||||
if (typeof options.limits.memory === 'number' && stat.size > options.limits.memory) {
|
||||
throw new FileOperationError(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow it to use more memory"), FileOperationResult.FILE_EXCEED_MEMORY_LIMIT);
|
||||
}
|
||||
|
||||
if (typeof options.limits.size === 'number' && stat.size > options.limits.size) {
|
||||
throw new FileOperationError(localize('fileTooLargeError', "File is too large to open"), FileOperationResult.FILE_TOO_LARGE);
|
||||
}
|
||||
}
|
||||
// Throw if file is too large to load
|
||||
this.validateReadFileLimits(stat.size, options);
|
||||
|
||||
return stat;
|
||||
}
|
||||
|
||||
private validateReadFileLimits(size: number, options?: IReadFileOptions): void {
|
||||
if (options?.limits) {
|
||||
let tooLargeErrorResult: FileOperationResult | undefined = undefined;
|
||||
|
||||
if (typeof options.limits.memory === 'number' && size > options.limits.memory) {
|
||||
tooLargeErrorResult = FileOperationResult.FILE_EXCEEDS_MEMORY_LIMIT;
|
||||
}
|
||||
|
||||
if (typeof options.limits.size === 'number' && size > options.limits.size) {
|
||||
tooLargeErrorResult = FileOperationResult.FILE_TOO_LARGE;
|
||||
}
|
||||
|
||||
if (typeof tooLargeErrorResult === 'number') {
|
||||
throw new FileOperationError(localize('fileTooLargeError', "File is too large to open"), tooLargeErrorResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region Move/Copy/Delete/Create Folder
|
||||
|
||||
async move(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> {
|
||||
const sourceProvider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(source));
|
||||
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(target));
|
||||
const sourceProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(source));
|
||||
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target));
|
||||
|
||||
// move
|
||||
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'move', !!overwrite);
|
||||
@@ -538,8 +554,8 @@ export class FileService extends Disposable implements IFileService {
|
||||
}
|
||||
|
||||
async copy(source: URI, target: URI, overwrite?: boolean): Promise<IFileStatWithMetadata> {
|
||||
const sourceProvider = await this.withReadWriteProvider(source);
|
||||
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withReadWriteProvider(target));
|
||||
const sourceProvider = await this.withReadProvider(source);
|
||||
const targetProvider = this.throwIfFileSystemIsReadonly(await this.withWriteProvider(target));
|
||||
|
||||
// copy
|
||||
const mode = await this.doMoveCopy(sourceProvider, source, targetProvider, target, 'copy', !!overwrite);
|
||||
@@ -551,7 +567,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
return fileStat;
|
||||
}
|
||||
|
||||
private async doMoveCopy(sourceProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, source: URI, targetProvider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, target: URI, mode: 'move' | 'copy', overwrite: boolean): Promise<'move' | 'copy'> {
|
||||
private async doMoveCopy(sourceProvider: IFileSystemProvider, source: URI, targetProvider: IFileSystemProvider, target: URI, mode: 'move' | 'copy', overwrite: boolean): Promise<'move' | 'copy'> {
|
||||
if (source.toString() === target.toString()) {
|
||||
return mode; // simulate node.js behaviour here and do a no-op if paths match
|
||||
}
|
||||
@@ -666,7 +682,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
}
|
||||
|
||||
if (!isSameResourceWithDifferentPathCase && isEqualOrParent(target, source, !isPathCaseSensitive)) {
|
||||
throw new Error(localize('unableToMoveCopyError2', "Unable to move/copy when source is parent of target"));
|
||||
throw new Error(localize('unableToMoveCopyError2', "Unable to move/copy when source is parent of target."));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -676,7 +692,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// Bail out if target exists and we are not about to overwrite
|
||||
if (!overwrite) {
|
||||
throw new FileOperationError(localize('unableToMoveCopyError3', "Unable to move/copy. File already exists at destination."), FileOperationResult.FILE_MOVE_CONFLICT);
|
||||
throw new FileOperationError(localize('unableToMoveCopyError3', "Unable to move/copy since a file already exists at destination."), FileOperationResult.FILE_MOVE_CONFLICT);
|
||||
}
|
||||
|
||||
// Special case: if the target is a parent of the source, we cannot delete
|
||||
@@ -684,7 +700,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
if (sourceProvider === targetProvider) {
|
||||
const isPathCaseSensitive = !!(sourceProvider.capabilities & FileSystemProviderCapabilities.PathCaseSensitive);
|
||||
if (isEqualOrParent(source, target, !isPathCaseSensitive)) {
|
||||
throw new Error(localize('unableToMoveCopyError4', "Unable to move/copy. File would replace folder it is contained in."));
|
||||
throw new Error(localize('unableToMoveCopyError4', "Unable to move/copy since a file would replace the folder it is contained in."));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -871,13 +887,13 @@ export class FileService extends Disposable implements IFileService {
|
||||
|
||||
// write into handle until all bytes from buffer have been written
|
||||
try {
|
||||
if (isVSBufferReadableStream(readableOrStream)) {
|
||||
if (isReadableStream(readableOrStream)) {
|
||||
await this.doWriteStreamBufferedQueued(provider, handle, readableOrStream);
|
||||
} else {
|
||||
await this.doWriteReadableBufferedQueued(provider, handle, readableOrStream);
|
||||
}
|
||||
} catch (error) {
|
||||
throw this.ensureError(error);
|
||||
throw ensureFileSystemProviderError(error);
|
||||
} finally {
|
||||
|
||||
// close handle always
|
||||
@@ -919,7 +935,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
let posInFile = 0;
|
||||
|
||||
let chunk: VSBuffer | null;
|
||||
while (chunk = readable.read()) {
|
||||
while ((chunk = readable.read()) !== null) {
|
||||
await this.doWriteBuffer(provider, handle, chunk, chunk.byteLength, posInFile, 0);
|
||||
|
||||
posInFile += chunk.byteLength;
|
||||
@@ -942,7 +958,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
let buffer: VSBuffer;
|
||||
if (bufferOrReadableOrStream instanceof VSBuffer) {
|
||||
buffer = bufferOrReadableOrStream;
|
||||
} else if (isVSBufferReadableStream(bufferOrReadableOrStream)) {
|
||||
} else if (isReadableStream(bufferOrReadableOrStream)) {
|
||||
buffer = await streamToBuffer(bufferOrReadableOrStream);
|
||||
} else {
|
||||
buffer = readableToBuffer(bufferOrReadableOrStream);
|
||||
@@ -988,7 +1004,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
}
|
||||
} while (bytesRead > 0);
|
||||
} catch (error) {
|
||||
throw this.ensureError(error);
|
||||
throw ensureFileSystemProviderError(error);
|
||||
} finally {
|
||||
await Promise.all([
|
||||
typeof sourceHandle === 'number' ? sourceProvider.close(sourceHandle) : Promise.resolve(),
|
||||
@@ -1019,7 +1035,7 @@ export class FileService extends Disposable implements IFileService {
|
||||
const buffer = await sourceProvider.readFile(source);
|
||||
await this.doWriteBuffer(targetProvider, targetHandle, VSBuffer.wrap(buffer), buffer.byteLength, 0, 0);
|
||||
} catch (error) {
|
||||
throw this.ensureError(error);
|
||||
throw ensureFileSystemProviderError(error);
|
||||
} finally {
|
||||
await targetProvider.close(targetHandle);
|
||||
}
|
||||
@@ -1042,38 +1058,6 @@ export class FileService extends Disposable implements IFileService {
|
||||
return provider;
|
||||
}
|
||||
|
||||
private throwIfCancelled(token: CancellationToken): boolean {
|
||||
if (token.isCancellationRequested) {
|
||||
throw new Error('cancelled');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private ensureError(error?: Error): Error {
|
||||
if (!error) {
|
||||
return new Error(localize('unknownError', "Unknown Error")); // https://github.com/Microsoft/vscode/issues/72798
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
private throwIfTooLarge(totalBytesRead: number, options?: IReadFileOptions): boolean {
|
||||
|
||||
// Return early if file is too large to load
|
||||
if (options?.limits) {
|
||||
if (typeof options.limits.memory === 'number' && totalBytesRead > options.limits.memory) {
|
||||
throw new FileOperationError(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow it to use more memory"), FileOperationResult.FILE_EXCEED_MEMORY_LIMIT);
|
||||
}
|
||||
|
||||
if (typeof options.limits.size === 'number' && totalBytesRead > options.limits.size) {
|
||||
throw new FileOperationError(localize('fileTooLargeError', "File is too large to open"), FileOperationResult.FILE_TOO_LARGE);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private resourceForError(resource: URI): string {
|
||||
if (resource.scheme === Schemas.file) {
|
||||
return resource.fsPath;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { sep } from 'vs/base/common/path';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import * as glob from 'vs/base/common/glob';
|
||||
@@ -13,6 +14,8 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { isEqualOrParent, isEqual } from 'vs/base/common/resources';
|
||||
import { isUndefinedOrNull } from 'vs/base/common/types';
|
||||
import { VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { ReadableStreamEvents } from 'vs/base/common/stream';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
|
||||
export const IFileService = createDecorator<IFileService>('fileService');
|
||||
|
||||
@@ -158,6 +161,29 @@ export interface FileOverwriteOptions {
|
||||
overwrite: boolean;
|
||||
}
|
||||
|
||||
export interface FileReadStreamOptions {
|
||||
|
||||
/**
|
||||
* Is an integer specifying where to begin reading from in the file. If position is undefined,
|
||||
* data will be read from the current file position.
|
||||
*/
|
||||
readonly position?: number;
|
||||
|
||||
/**
|
||||
* Is an integer specifying how many bytes to read from the file. By default, all bytes
|
||||
* will be read.
|
||||
*/
|
||||
readonly length?: number;
|
||||
|
||||
/**
|
||||
* If provided, the size of the file will be checked against the limits.
|
||||
*/
|
||||
limits?: {
|
||||
readonly size?: number;
|
||||
readonly memory?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FileWriteOptions {
|
||||
overwrite: boolean;
|
||||
create: boolean;
|
||||
@@ -181,8 +207,17 @@ export enum FileType {
|
||||
|
||||
export interface IStat {
|
||||
type: FileType;
|
||||
|
||||
/**
|
||||
* The last modification date represented as millis from unix epoch.
|
||||
*/
|
||||
mtime: number;
|
||||
|
||||
/**
|
||||
* The creation date represented as millis from unix epoch.
|
||||
*/
|
||||
ctime: number;
|
||||
|
||||
size: number;
|
||||
}
|
||||
|
||||
@@ -194,6 +229,8 @@ export interface IWatchOptions {
|
||||
export const enum FileSystemProviderCapabilities {
|
||||
FileReadWrite = 1 << 1,
|
||||
FileOpenReadWriteClose = 1 << 2,
|
||||
FileReadStream = 1 << 4,
|
||||
|
||||
FileFolderCopy = 1 << 3,
|
||||
|
||||
PathCaseSensitive = 1 << 10,
|
||||
@@ -223,6 +260,8 @@ export interface IFileSystemProvider {
|
||||
readFile?(resource: URI): Promise<Uint8Array>;
|
||||
writeFile?(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void>;
|
||||
|
||||
readFileStream?(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents<Uint8Array>;
|
||||
|
||||
open?(resource: URI, opts: FileOpenOptions): Promise<number>;
|
||||
close?(fd: number): Promise<void>;
|
||||
read?(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number>;
|
||||
@@ -257,11 +296,21 @@ export function hasOpenReadWriteCloseCapability(provider: IFileSystemProvider):
|
||||
return !!(provider.capabilities & FileSystemProviderCapabilities.FileOpenReadWriteClose);
|
||||
}
|
||||
|
||||
export interface IFileSystemProviderWithFileReadStreamCapability extends IFileSystemProvider {
|
||||
readFileStream(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents<Uint8Array>;
|
||||
}
|
||||
|
||||
export function hasFileReadStreamCapability(provider: IFileSystemProvider): provider is IFileSystemProviderWithFileReadStreamCapability {
|
||||
return !!(provider.capabilities & FileSystemProviderCapabilities.FileReadStream);
|
||||
}
|
||||
|
||||
export enum FileSystemProviderErrorCode {
|
||||
FileExists = 'EntryExists',
|
||||
FileNotFound = 'EntryNotFound',
|
||||
FileNotADirectory = 'EntryNotADirectory',
|
||||
FileIsADirectory = 'EntryIsADirectory',
|
||||
FileExceedsMemoryLimit = 'EntryExceedsMemoryLimit',
|
||||
FileTooLarge = 'EntryTooLarge',
|
||||
NoPermissions = 'NoPermissions',
|
||||
Unavailable = 'Unavailable',
|
||||
Unknown = 'Unknown'
|
||||
@@ -274,13 +323,21 @@ export class FileSystemProviderError extends Error {
|
||||
}
|
||||
}
|
||||
|
||||
export function createFileSystemProviderError(error: Error, code: FileSystemProviderErrorCode): FileSystemProviderError {
|
||||
export function createFileSystemProviderError(error: Error | string, code: FileSystemProviderErrorCode): FileSystemProviderError {
|
||||
const providerError = new FileSystemProviderError(error.toString(), code);
|
||||
markAsFileSystemProviderError(providerError, code);
|
||||
|
||||
return providerError;
|
||||
}
|
||||
|
||||
export function ensureFileSystemProviderError(error?: Error): Error {
|
||||
if (!error) {
|
||||
return createFileSystemProviderError(localize('unknownError', "Unknown Error"), FileSystemProviderErrorCode.Unknown); // https://github.com/Microsoft/vscode/issues/72798
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
export function markAsFileSystemProviderError(error: Error, code: FileSystemProviderErrorCode): Error {
|
||||
error.name = code ? `${code} (FileSystemError)` : `FileSystemError`;
|
||||
|
||||
@@ -311,6 +368,8 @@ export function toFileSystemProviderErrorCode(error: Error | undefined | null):
|
||||
case FileSystemProviderErrorCode.FileIsADirectory: return FileSystemProviderErrorCode.FileIsADirectory;
|
||||
case FileSystemProviderErrorCode.FileNotADirectory: return FileSystemProviderErrorCode.FileNotADirectory;
|
||||
case FileSystemProviderErrorCode.FileNotFound: return FileSystemProviderErrorCode.FileNotFound;
|
||||
case FileSystemProviderErrorCode.FileExceedsMemoryLimit: return FileSystemProviderErrorCode.FileExceedsMemoryLimit;
|
||||
case FileSystemProviderErrorCode.FileTooLarge: return FileSystemProviderErrorCode.FileTooLarge;
|
||||
case FileSystemProviderErrorCode.NoPermissions: return FileSystemProviderErrorCode.NoPermissions;
|
||||
case FileSystemProviderErrorCode.Unavailable: return FileSystemProviderErrorCode.Unavailable;
|
||||
}
|
||||
@@ -335,7 +394,10 @@ export function toFileOperationResult(error: Error): FileOperationResult {
|
||||
return FileOperationResult.FILE_PERMISSION_DENIED;
|
||||
case FileSystemProviderErrorCode.FileExists:
|
||||
return FileOperationResult.FILE_MOVE_CONFLICT;
|
||||
case FileSystemProviderErrorCode.FileNotADirectory:
|
||||
case FileSystemProviderErrorCode.FileExceedsMemoryLimit:
|
||||
return FileOperationResult.FILE_EXCEEDS_MEMORY_LIMIT;
|
||||
case FileSystemProviderErrorCode.FileTooLarge:
|
||||
return FileOperationResult.FILE_TOO_LARGE;
|
||||
default:
|
||||
return FileOperationResult.FILE_OTHER_ERROR;
|
||||
}
|
||||
@@ -530,14 +592,21 @@ interface IBaseStat {
|
||||
size?: number;
|
||||
|
||||
/**
|
||||
* The last modification date represented
|
||||
* as millis from unix epoch.
|
||||
* The last modification date represented as millis from unix epoch.
|
||||
*
|
||||
* The value may or may not be resolved as
|
||||
* it is optional.
|
||||
*/
|
||||
mtime?: number;
|
||||
|
||||
/**
|
||||
* The creation date represented as millis from unix epoch.
|
||||
*
|
||||
* The value may or may not be resolved as
|
||||
* it is optional.
|
||||
*/
|
||||
ctime?: number;
|
||||
|
||||
/**
|
||||
* A unique identifier thet represents the
|
||||
* current state of the file or directory.
|
||||
@@ -546,15 +615,11 @@ interface IBaseStat {
|
||||
* it is optional.
|
||||
*/
|
||||
etag?: string;
|
||||
|
||||
/**
|
||||
* The resource is readonly.
|
||||
*/
|
||||
isReadonly?: boolean;
|
||||
}
|
||||
|
||||
export interface IBaseStatWithMetadata extends IBaseStat {
|
||||
mtime: number;
|
||||
ctime: number;
|
||||
etag: string;
|
||||
size: number;
|
||||
}
|
||||
@@ -565,14 +630,19 @@ export interface IBaseStatWithMetadata extends IBaseStat {
|
||||
export interface IFileStat extends IBaseStat {
|
||||
|
||||
/**
|
||||
* The resource is a directory
|
||||
* The resource is a file.
|
||||
*/
|
||||
isFile: boolean;
|
||||
|
||||
/**
|
||||
* The resource is a directory.
|
||||
*/
|
||||
isDirectory: boolean;
|
||||
|
||||
/**
|
||||
* The resource is a symbolic link.
|
||||
*/
|
||||
isSymbolicLink?: boolean;
|
||||
isSymbolicLink: boolean;
|
||||
|
||||
/**
|
||||
* The children of the file stat or undefined if none.
|
||||
@@ -582,6 +652,7 @@ export interface IFileStat extends IBaseStat {
|
||||
|
||||
export interface IFileStatWithMetadata extends IFileStat, IBaseStatWithMetadata {
|
||||
mtime: number;
|
||||
ctime: number;
|
||||
etag: string;
|
||||
size: number;
|
||||
children?: IFileStatWithMetadata[];
|
||||
@@ -612,7 +683,7 @@ export interface IFileStreamContent extends IBaseStatWithMetadata {
|
||||
value: VSBufferReadableStream;
|
||||
}
|
||||
|
||||
export interface IReadFileOptions {
|
||||
export interface IReadFileOptions extends FileReadStreamOptions {
|
||||
|
||||
/**
|
||||
* The optional etag parameter allows to return early from resolving the resource if
|
||||
@@ -621,26 +692,6 @@ export interface IReadFileOptions {
|
||||
* It is the task of the caller to makes sure to handle this error case from the promise.
|
||||
*/
|
||||
readonly etag?: string;
|
||||
|
||||
/**
|
||||
* Is an integer specifying where to begin reading from in the file. If position is null,
|
||||
* data will be read from the current file position.
|
||||
*/
|
||||
readonly position?: number;
|
||||
|
||||
/**
|
||||
* Is an integer specifying how many bytes to read from the file. By default, all bytes
|
||||
* will be read.
|
||||
*/
|
||||
readonly length?: number;
|
||||
|
||||
/**
|
||||
* If provided, the size of the file will be checked against the limits.
|
||||
*/
|
||||
limits?: {
|
||||
readonly size?: number;
|
||||
readonly memory?: number;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IWriteFileOptions {
|
||||
@@ -670,7 +721,7 @@ export interface IResolveFileOptions {
|
||||
readonly resolveSingleChildDescendants?: boolean;
|
||||
|
||||
/**
|
||||
* Will resolve mtime, size and etag of files if enabled. This can have a negative impact
|
||||
* Will resolve mtime, ctime, size and etag of files if enabled. This can have a negative impact
|
||||
* on performance and thus should only be used when these values are required.
|
||||
*/
|
||||
readonly resolveMetadata?: boolean;
|
||||
@@ -709,7 +760,7 @@ export const enum FileOperationResult {
|
||||
FILE_PERMISSION_DENIED,
|
||||
FILE_TOO_LARGE,
|
||||
FILE_INVALID_PATH,
|
||||
FILE_EXCEED_MEMORY_LIMIT,
|
||||
FILE_EXCEEDS_MEMORY_LIMIT,
|
||||
FILE_OTHER_ERROR
|
||||
}
|
||||
|
||||
|
||||
114
src/vs/platform/files/common/io.ts
Normal file
114
src/vs/platform/files/common/io.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { localize } from 'vs/nls';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { VSBuffer, VSBufferWriteableStream, newWriteableBufferStream, VSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IFileSystemProviderWithOpenReadWriteCloseCapability, FileReadStreamOptions, createFileSystemProviderError, FileSystemProviderErrorCode, ensureFileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
|
||||
export interface ICreateReadStreamOptions extends FileReadStreamOptions {
|
||||
|
||||
/**
|
||||
* The size of the buffer to use before sending to the stream.
|
||||
*/
|
||||
bufferSize: number;
|
||||
}
|
||||
|
||||
export function createReadStream(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, options: ICreateReadStreamOptions, token?: CancellationToken): VSBufferReadableStream {
|
||||
const stream = newWriteableBufferStream();
|
||||
|
||||
// do not await reading but simply return the stream directly since it operates
|
||||
// via events. finally end the stream and send through the possible error
|
||||
let error: Error | undefined = undefined;
|
||||
|
||||
doReadFileIntoStream(provider, resource, stream, options, token).then(undefined, err => error = err).finally(() => stream.end(error));
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
async function doReadFileIntoStream(provider: IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, stream: VSBufferWriteableStream, options: ICreateReadStreamOptions, token?: CancellationToken): Promise<void> {
|
||||
|
||||
// Check for cancellation
|
||||
throwIfCancelled(token);
|
||||
|
||||
// open handle through provider
|
||||
const handle = await provider.open(resource, { create: false });
|
||||
|
||||
// Check for cancellation
|
||||
throwIfCancelled(token);
|
||||
|
||||
try {
|
||||
let totalBytesRead = 0;
|
||||
let bytesRead = 0;
|
||||
let allowedRemainingBytes = (options && typeof options.length === 'number') ? options.length : undefined;
|
||||
|
||||
let buffer = VSBuffer.alloc(Math.min(options.bufferSize, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : options.bufferSize));
|
||||
|
||||
let posInFile = options && typeof options.position === 'number' ? options.position : 0;
|
||||
let posInBuffer = 0;
|
||||
do {
|
||||
// read from source (handle) at current position (pos) into buffer (buffer) at
|
||||
// buffer position (posInBuffer) up to the size of the buffer (buffer.byteLength).
|
||||
bytesRead = await provider.read(handle, posInFile, buffer.buffer, posInBuffer, buffer.byteLength - posInBuffer);
|
||||
|
||||
posInFile += bytesRead;
|
||||
posInBuffer += bytesRead;
|
||||
totalBytesRead += bytesRead;
|
||||
|
||||
if (typeof allowedRemainingBytes === 'number') {
|
||||
allowedRemainingBytes -= bytesRead;
|
||||
}
|
||||
|
||||
// when buffer full, create a new one and emit it through stream
|
||||
if (posInBuffer === buffer.byteLength) {
|
||||
stream.write(buffer);
|
||||
|
||||
buffer = VSBuffer.alloc(Math.min(options.bufferSize, typeof allowedRemainingBytes === 'number' ? allowedRemainingBytes : options.bufferSize));
|
||||
|
||||
posInBuffer = 0;
|
||||
}
|
||||
} while (bytesRead > 0 && (typeof allowedRemainingBytes !== 'number' || allowedRemainingBytes > 0) && throwIfCancelled(token) && throwIfTooLarge(totalBytesRead, options));
|
||||
|
||||
// wrap up with last buffer (also respect maxBytes if provided)
|
||||
if (posInBuffer > 0) {
|
||||
let lastChunkLength = posInBuffer;
|
||||
if (typeof allowedRemainingBytes === 'number') {
|
||||
lastChunkLength = Math.min(posInBuffer, allowedRemainingBytes);
|
||||
}
|
||||
|
||||
stream.write(buffer.slice(0, lastChunkLength));
|
||||
}
|
||||
} catch (error) {
|
||||
throw ensureFileSystemProviderError(error);
|
||||
} finally {
|
||||
await provider.close(handle);
|
||||
}
|
||||
}
|
||||
|
||||
function throwIfCancelled(token?: CancellationToken): boolean {
|
||||
if (token && token.isCancellationRequested) {
|
||||
throw canceled();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function throwIfTooLarge(totalBytesRead: number, options: ICreateReadStreamOptions): boolean {
|
||||
|
||||
// Return early if file is too large to load and we have configured limits
|
||||
if (options?.limits) {
|
||||
if (typeof options.limits.memory === 'number' && totalBytesRead > options.limits.memory) {
|
||||
throw createFileSystemProviderError(localize('fileTooLargeForHeapError', "To open a file of this size, you need to restart and allow it to use more memory"), FileSystemProviderErrorCode.FileExceedsMemoryLimit);
|
||||
}
|
||||
|
||||
if (typeof options.limits.size === 'number' && totalBytesRead > options.limits.size) {
|
||||
throw createFileSystemProviderError(localize('fileTooLargeError', "File is too large to open"), FileSystemProviderErrorCode.FileTooLarge);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
import { mkdir, open, close, read, write, fdatasync, Dirent, Stats } from 'fs';
|
||||
import { promisify } from 'util';
|
||||
import { IDisposable, Disposable, toDisposable, dispose, combinedDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, FileSystemProviderErrorCode, createFileSystemProviderError, FileSystemProviderError } from 'vs/platform/files/common/files';
|
||||
import { FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, FileType, FileDeleteOptions, FileOverwriteOptions, FileWriteOptions, FileOpenOptions, FileSystemProviderErrorCode, createFileSystemProviderError, FileSystemProviderError, IFileSystemProviderWithFileReadWriteCapability, IFileSystemProviderWithFileReadStreamCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, FileReadStreamOptions, IFileSystemProviderWithFileFolderCopyCapability } from 'vs/platform/files/common/files';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { isLinux, isWindows } from 'vs/base/common/platform';
|
||||
@@ -22,15 +22,30 @@ import { FileWatcher as UnixWatcherService } from 'vs/platform/files/node/watche
|
||||
import { FileWatcher as WindowsWatcherService } from 'vs/platform/files/node/watcher/win32/watcherService';
|
||||
import { FileWatcher as NsfwWatcherService } from 'vs/platform/files/node/watcher/nsfw/watcherService';
|
||||
import { FileWatcher as NodeJSWatcherService } from 'vs/platform/files/node/watcher/nodejs/watcherService';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { ReadableStreamEvents, transform } from 'vs/base/common/stream';
|
||||
import { createReadStream } from 'vs/platform/files/common/io';
|
||||
|
||||
export interface IWatcherOptions {
|
||||
pollingInterval?: number;
|
||||
usePolling: boolean;
|
||||
}
|
||||
|
||||
export class DiskFileSystemProvider extends Disposable implements IFileSystemProvider {
|
||||
export interface IDiskFileSystemProviderOptions {
|
||||
bufferSize?: number;
|
||||
watcher?: IWatcherOptions;
|
||||
}
|
||||
|
||||
constructor(private logService: ILogService, private watcherOptions?: IWatcherOptions) {
|
||||
export class DiskFileSystemProvider extends Disposable implements
|
||||
IFileSystemProviderWithFileReadWriteCapability,
|
||||
IFileSystemProviderWithOpenReadWriteCloseCapability,
|
||||
IFileSystemProviderWithFileReadStreamCapability,
|
||||
IFileSystemProviderWithFileFolderCopyCapability {
|
||||
|
||||
private readonly BUFFER_SIZE = this.options?.bufferSize || 64 * 1024;
|
||||
|
||||
constructor(private logService: ILogService, private options?: IDiskFileSystemProviderOptions) {
|
||||
super();
|
||||
}
|
||||
|
||||
@@ -44,6 +59,7 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
this._capabilities =
|
||||
FileSystemProviderCapabilities.FileReadWrite |
|
||||
FileSystemProviderCapabilities.FileOpenReadWriteClose |
|
||||
FileSystemProviderCapabilities.FileReadStream |
|
||||
FileSystemProviderCapabilities.FileFolderCopy;
|
||||
|
||||
if (isLinux) {
|
||||
@@ -64,7 +80,7 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
|
||||
return {
|
||||
type: this.toType(stat, isSymbolicLink),
|
||||
ctime: stat.ctime.getTime(),
|
||||
ctime: stat.birthtime.getTime(), // intentionally not using ctime here, we want the creation time
|
||||
mtime: stat.mtime.getTime(),
|
||||
size: stat.size
|
||||
};
|
||||
@@ -121,17 +137,32 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
}
|
||||
}
|
||||
|
||||
readFileStream(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents<Uint8Array> {
|
||||
const fileStream = createReadStream(this, resource, {
|
||||
...opts,
|
||||
bufferSize: this.BUFFER_SIZE
|
||||
}, token);
|
||||
|
||||
return transform(fileStream, { data: data => data.buffer }, data => VSBuffer.concat(data.map(data => VSBuffer.wrap(data))).buffer);
|
||||
}
|
||||
|
||||
async writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
|
||||
let handle: number | undefined = undefined;
|
||||
try {
|
||||
const filePath = this.toFilePath(resource);
|
||||
|
||||
// Validate target
|
||||
const fileExists = await exists(filePath);
|
||||
if (fileExists && !opts.overwrite) {
|
||||
throw createFileSystemProviderError(new Error(localize('fileExists', "File already exists")), FileSystemProviderErrorCode.FileExists);
|
||||
} else if (!fileExists && !opts.create) {
|
||||
throw createFileSystemProviderError(new Error(localize('fileNotExists', "File does not exist")), FileSystemProviderErrorCode.FileNotFound);
|
||||
// Validate target unless { create: true, overwrite: true }
|
||||
if (!opts.create || !opts.overwrite) {
|
||||
const fileExists = await exists(filePath);
|
||||
if (fileExists) {
|
||||
if (!opts.overwrite) {
|
||||
throw createFileSystemProviderError(localize('fileExists', "File already exists"), FileSystemProviderErrorCode.FileExists);
|
||||
}
|
||||
} else {
|
||||
if (!opts.create) {
|
||||
throw createFileSystemProviderError(localize('fileNotExists', "File does not exist"), FileSystemProviderErrorCode.FileNotFound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open
|
||||
@@ -435,13 +466,13 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
}
|
||||
|
||||
if (isSameResourceWithDifferentPathCase && mode === 'copy') {
|
||||
throw createFileSystemProviderError(new Error('File cannot be copied to same path with different path case'), FileSystemProviderErrorCode.FileExists);
|
||||
throw createFileSystemProviderError(localize('fileCopyErrorPathCase', "'File cannot be copied to same path with different path case"), FileSystemProviderErrorCode.FileExists);
|
||||
}
|
||||
|
||||
// handle existing target (unless this is a case change)
|
||||
if (!isSameResourceWithDifferentPathCase && await exists(toFilePath)) {
|
||||
if (!overwrite) {
|
||||
throw createFileSystemProviderError(new Error('File at target already exists'), FileSystemProviderErrorCode.FileExists);
|
||||
throw createFileSystemProviderError(localize('fileCopyErrorExists', "File at target already exists"), FileSystemProviderErrorCode.FileExists);
|
||||
}
|
||||
|
||||
// Delete target
|
||||
@@ -532,9 +563,9 @@ export class DiskFileSystemProvider extends Disposable implements IFileSystemPro
|
||||
let watcherOptions: IWatcherOptions | undefined = undefined;
|
||||
|
||||
// requires a polling watcher
|
||||
if (this.watcherOptions && this.watcherOptions.usePolling) {
|
||||
if (this.options?.watcher?.usePolling) {
|
||||
watcherImpl = UnixWatcherService;
|
||||
watcherOptions = this.watcherOptions;
|
||||
watcherOptions = this.options?.watcher;
|
||||
}
|
||||
|
||||
// Single Folder Watcher
|
||||
|
||||
@@ -8,7 +8,7 @@ import * as extpath from 'vs/base/common/extpath';
|
||||
import * as path from 'vs/base/common/path';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { IDiskFileChange, normalizeFileChanges, ILogMessage } from 'vs/platform/files/node/watcher/watcher';
|
||||
import * as nsfw from 'nsfw';
|
||||
import * as nsfw from 'vscode-nsfw';
|
||||
import { IWatcherService, IWatcherRequest, IWatcherOptions } from 'vs/platform/files/node/watcher/nsfw/watcher';
|
||||
import { ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { FileChangeType } from 'vs/platform/files/common/files';
|
||||
|
||||
@@ -55,7 +55,7 @@ export class OutOfProcessWin32FolderWatcher {
|
||||
const stdoutLineDecoder = new decoder.LineDecoder();
|
||||
|
||||
// Events over stdout
|
||||
this.handle.stdout.on('data', (data: Buffer) => {
|
||||
this.handle.stdout!.on('data', (data: Buffer) => {
|
||||
|
||||
// Collect raw events from output
|
||||
const rawEvents: IDiskFileChange[] = [];
|
||||
@@ -99,7 +99,7 @@ export class OutOfProcessWin32FolderWatcher {
|
||||
|
||||
// Errors
|
||||
this.handle.on('error', (error: Error) => this.onError(error));
|
||||
this.handle.stderr.on('data', (data: Buffer) => this.onError(data));
|
||||
this.handle.stderr!.on('data', (data: Buffer) => this.onError(data));
|
||||
|
||||
// Exit
|
||||
this.handle.on('exit', (code: number, signal: string) => this.onExit(code, signal));
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -280,4 +280,856 @@ Cras aliquet metus ut purus sagittis, vel venenatis ante consectetur. Pellentesq
|
||||
|
||||
Donec vehicula mauris eget lacus mollis venenatis et sed nibh. Nam sodales ligula ipsum, scelerisque lacinia ligula sagittis in. Nam sit amet ipsum at erat malesuada congue. Aenean ut sollicitudin sapien. Etiam at tempor odio. Mauris vitae purus ut magna suscipit consequat. Vivamus quis sapien neque. Nulla vulputate sem sit amet massa pellentesque, eleifend tristique ligula egestas. Suspendisse tincidunt gravida mi, in pulvinar lectus egestas non. Aenean imperdiet ex sit amet nunc sollicitudin porta. Integer justo odio, ultricies at interdum in, rhoncus vitae sem. Sed porttitor arcu quis purus aliquet hendrerit. Praesent tempor tortor at dolor dictum pulvinar. Nulla aliquet nunc non ligula scelerisque accumsan. Donec nulla justo, congue vitae massa in, faucibus hendrerit magna. Donec non egestas purus.
|
||||
|
||||
Vivamus iaculis, lacus efficitur faucibus porta, dui nulla facilisis ligula, ut sodales odio nunc id sapien. Cras viverra auctor ipsum, dapibus mattis neque dictum sed. Sed convallis fermentum molestie. Nulla facilisi turpis duis.
|
||||
Vivamus iaculis, lacus efficitur faucibus porta, dui nulla facilisis ligula, ut sodales odio nunc id sapien. Cras viverra auctor ipsum, dapibu
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vulputate, ipsum quis interdum fermentum, lorem sem fermentum eros, vitae auctor neque lacus in nisi. Suspendisse potenti. Maecenas et scelerisque elit, in tincidunt quam. Sed eu tincidunt quam. Nullam justo ex, imperdiet a imperdiet et, fermentum sit amet eros. Aenean quis tempus sem. Pellentesque accumsan magna mi, ut mollis velit sagittis id. Etiam quis ipsum orci. Fusce purus ante, accumsan a lobortis at, venenatis eu nisl. Praesent ornare sed ante placerat accumsan. Suspendisse tempus dignissim fermentum. Nunc a leo ac lacus sodales iaculis eu vitae mi. In feugiat ante at massa finibus cursus. Suspendisse posuere fringilla ornare. Mauris elementum ac quam id convallis. Vestibulum non elit quis urna volutpat aliquam a eu lacus.
|
||||
|
||||
Aliquam vestibulum imperdiet neque, suscipit aliquam elit ultrices bibendum. Suspendisse ultrices pulvinar cursus. Morbi risus nisi, cursus consequat rutrum vitae, molestie sed dui. Fusce posuere, augue quis dignissim aliquam, nisi ipsum porttitor ante, quis fringilla nisl turpis ac nisi. Nulla varius enim eget lorem vehicula gravida. Donec finibus malesuada leo nec semper. Proin ac enim eros. Vivamus non tincidunt nisi, vel tristique lorem.
|
||||
|
||||
Nunc consequat ex id eros dignissim, id rutrum risus laoreet. Sed euismod non erat eu ultricies. Etiam vehicula gravida lacus ut porta. Vestibulum eu eros quis nunc aliquet luctus. Cras quis semper ligula. Nullam gravida vehicula quam sed porta. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In porta cursus vulputate. Quisque porta a nisi eget cursus. Aliquam risus leo, luctus ac magna in, efficitur cursus magna. In condimentum non mi id semper. Donec interdum ante eget commodo maximus.
|
||||
|
||||
Vivamus sit amet vestibulum lectus. Fusce tincidunt mi sapien, dictum sollicitudin diam vulputate in. Integer fringilla consequat mollis. Cras aliquet consequat felis eget feugiat. Nunc tempor cursus arcu, vitae ornare nunc varius et. Vestibulum et tortor vel ante viverra porttitor. Nam at tortor ullamcorper, facilisis augue quis, tristique erat. Aenean ut euismod nibh. Quisque eu tincidunt est, nec euismod eros.
|
||||
|
||||
Proin vehicula nibh non viverra egestas. Phasellus sem dolor, ultricies ac sagittis tristique, lacinia a purus. Vestibulum in ante eros. Pellentesque lacus nulla, tristique vitae interdum vel, malesuada ac diam. Aenean bibendum posuere turpis in accumsan. Ut est nulla, ullamcorper quis turpis at, viverra sagittis mauris. Sed in interdum purus. Praesent scelerisque nibh eget sem euismod, ut imperdiet mi venenatis. Vivamus pulvinar orci sed dapibus auctor. Nulla facilisi. Vestibulum tincidunt erat nec porttitor egestas. Mauris quis risus ante. Nulla facilisi.
|
||||
|
||||
Aliquam ullamcorper ornare lobortis. Phasellus quis sem et ipsum mollis malesuada sed in ex. Ut aliquam ex eget metus finibus maximus. Proin suscipit mauris eu nibh lacinia, quis feugiat dui dapibus. Nam sed libero est. Aenean vulputate orci sit amet diam faucibus, eu sagittis sapien volutpat. Nam imperdiet felis turpis, at pretium odio pulvinar in. Sed vestibulum id eros nec ultricies. Sed quis aliquam tortor, vitae ullamcorper tellus. Donec egestas laoreet eros, id suscipit est rutrum nec. Sed auctor nulla eget metus aliquam, ut condimentum enim elementum.
|
||||
|
||||
Aliquam suscipit non turpis sit amet bibendum. Fusce velit ligula, euismod et maximus at, luctus sed neque. Quisque pretium, nisl at ullamcorper finibus, lectus leo mattis sapien, vel euismod mauris diam ullamcorper ex. Nulla ut risus finibus, lacinia ligula at, auctor erat. Mauris consectetur sagittis ligula vel dapibus. Nullam libero libero, lobortis aliquam libero vel, venenatis ultricies leo. Duis porttitor, nibh congue fermentum posuere, erat libero pulvinar tortor, a pellentesque nunc ipsum vel sem. Nullam volutpat, eros sit amet facilisis consectetur, ipsum est vehicula massa, non vestibulum neque elit in mauris. Nunc hendrerit ipsum non enim bibendum, vitae rhoncus mi egestas. Etiam ullamcorper massa vel nisl sagittis, nec bibendum arcu malesuada. Aenean aliquet turpis justo, a consectetur arcu mollis convallis. Etiam tellus ipsum, ultricies vitae lorem et, ornare facilisis orci. Praesent fringilla justo urna, vel mollis neque pulvinar vestibulum.
|
||||
|
||||
Donec non iaculis erat. Aliquam et mi sed nunc pulvinar ultricies in ut ipsum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent feugiat lacus ac dignissim semper. Phasellus vitae quam nisi. Morbi vel diam ultricies risus lobortis ornare. Fusce maximus et ligula quis iaculis. Sed congue ex eget felis convallis, sit amet hendrerit elit tempor. Donec vehicula blandit ante eget commodo. Vestibulum eleifend diam at feugiat euismod. Etiam magna tellus, dignissim eget fermentum vel, vestibulum vitae mauris. Nam accumsan et erat id sagittis. Donec lacinia, odio ut ornare ultricies, dolor velit accumsan tortor, non finibus erat tellus quis ligula. Nunc quis metus in leo volutpat ornare vulputate eu nisl.
|
||||
|
||||
Donec quis viverra ex. Nullam id feugiat mauris, eu fringilla nulla. Vestibulum id maximus elit. Cras elementum elit sed felis lobortis, eget sagittis nisi hendrerit. Vivamus vitae elit neque. Donec vulputate lacus ut libero ultrices accumsan. Vivamus accumsan nulla orci, in dignissim est laoreet sagittis. Proin at commodo velit. Curabitur in velit felis. Aliquam erat volutpat. Sed consequat, nulla et cursus sodales, nisi lacus mattis risus, quis eleifend erat ex nec turpis. Sed suscipit ultrices lorem in hendrerit.
|
||||
|
||||
Morbi vitae lacus nec libero ornare tempus eu et diam. Suspendisse magna ipsum, fermentum vel odio quis, molestie aliquam urna. Fusce mollis turpis a eros accumsan porttitor. Pellentesque rhoncus dolor sit amet magna rutrum, et dapibus justo tempor. Sed purus nisi, maximus vitae fringilla eu, molestie nec urna. Fusce malesuada finibus pretium. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec sed aliquet eros. Pellentesque luctus diam ante, eget euismod nisl aliquet eu. Sed accumsan elit purus, tempor varius ligula tempus nec. Curabitur ornare leo suscipit suscipit fermentum. Morbi eget nulla est. Maecenas faucibus interdum tristique.
|
||||
|
||||
Etiam ut elit eros. Nulla pharetra suscipit molestie. Nulla facilisis bibendum nisl non molestie. Curabitur turpis lectus, facilisis vel diam non, vulputate ultrices mauris. Aenean placerat aliquam convallis. Suspendisse sed scelerisque tellus. Vivamus lacinia neque eget risus cursus suscipit. Proin consequat dolor vel neque tempor, eu aliquam sem scelerisque. Duis non eros a purus malesuada pharetra non et nulla. Suspendisse potenti. Mauris libero eros, finibus vel nulla id, sagittis dapibus ante. Proin iaculis sed nunc et cursus.
|
||||
|
||||
Quisque accumsan lorem sit amet lorem aliquet euismod. Curabitur fermentum rutrum posuere. Etiam ultricies, sem id pellentesque suscipit, urna magna lacinia eros, quis efficitur risus nisl at lacus. Nulla quis lacus tortor. Mauris placerat ex in dolor tincidunt, vel aliquet nisi pretium. Cras iaculis risus vitae pellentesque aliquet. Quisque a enim imperdiet, ullamcorper arcu vitae, rutrum risus. Nullam consectetur libero at felis fringilla, nec congue nibh dignissim. Nam et lobortis felis, eu pellentesque ligula. Aenean facilisis, ligula non imperdiet maximus, massa orci gravida sapien, at sagittis lacus nisl in lacus. Nulla quis mauris luctus, scelerisque felis consequat, tempus risus. Fusce auctor nisl non nulla luctus molestie. Maecenas sapien nisl, auctor non dolor et, iaculis scelerisque lorem. Suspendisse egestas enim aliquet, accumsan mauris nec, posuere quam. Nulla iaculis dui dui, sit amet vestibulum erat ultricies ac.
|
||||
|
||||
Cras eget dolor erat. Proin at nisl ut leo consectetur ultricies vel ut arcu. Nulla in felis malesuada, ullamcorper tortor et, convallis massa. Nunc urna justo, ornare in nibh vitae, hendrerit condimentum libero. Etiam vitae libero in purus venenatis fringilla. Nullam velit nulla, consequat ut turpis non, egestas hendrerit nibh. Duis tortor turpis, interdum non ante ac, cursus accumsan lectus. Cras pharetra bibendum augue quis dictum. Sed euismod vestibulum justo. Proin porta lobortis purus. Duis venenatis diam tortor, sit amet condimentum eros rhoncus a. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc at magna nec diam lobortis efficitur sit amet ut lacus. Nulla quis orci tortor. Pellentesque tempus velit a odio finibus porta.
|
||||
|
||||
Proin feugiat mauris a tellus scelerisque convallis. Maecenas libero magna, blandit nec ultrices id, congue vel mi. Aliquam lacinia, quam vel condimentum convallis, tortor turpis aliquam odio, sed blandit libero lacus et eros. In eleifend iaculis magna ac finibus. Praesent auctor facilisis tellus in congue. Sed molestie lobortis dictum. Nam quis dignissim augue, vel euismod lorem. Curabitur posuere dapibus luctus. Donec ultricies dictum lectus, quis blandit arcu commodo ac. Aenean tincidunt ligula in nunc imperdiet dignissim. Curabitur egestas sollicitudin sapien ut semper. Aenean nec dignissim lacus.
|
||||
|
||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec aliquam dictum vehicula. Donec tortor est, volutpat non nisi nec, varius gravida ex. Nunc vel tristique nunc, vitae mattis nisi. Nunc nec luctus ex, vitae tincidunt lectus. In hac habitasse platea dictumst. Curabitur lobortis ex eget tincidunt tempor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut a vehicula mi.
|
||||
|
||||
Fusce eu libero finibus, interdum nulla a, placerat neque. Cras bibendum tempor libero nec feugiat. Cras ut sodales eros. Proin viverra, massa sit amet viverra egestas, neque nisl porta ex, sit amet hendrerit libero ligula vel urna. Mauris suscipit lacus id justo rhoncus suscipit. Etiam vel libero tellus. Maecenas non diam molestie, condimentum tellus a, bibendum enim. Mauris aliquet imperdiet tellus, eget sagittis dolor. Sed blandit in neque et luctus. Cras elementum sagittis nunc, vel mollis lorem euismod et. Donec posuere at lacus eget suscipit.
|
||||
|
||||
Nulla nunc mi, pretium non massa vel, tempor semper magna. Nunc a leo pulvinar, tincidunt nunc at, dignissim mi. Aliquam erat volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut viverra nulla a nisl finibus, at hendrerit ligula ullamcorper. Donec a lorem semper, tempor magna et, lobortis libero. Mauris id sapien leo. Donec dignissim, quam vitae porttitor dignissim, quam justo mattis dui, vel consequat odio elit quis orci. Etiam nec pretium neque, sit amet pretium orci. Duis ac tortor venenatis, feugiat purus non, feugiat nunc. Proin scelerisque nisl in turpis aliquam vulputate.
|
||||
|
||||
Praesent sed est semper, fringilla lorem vitae, tincidunt nibh. Cras eros metus, auctor at mauris sit amet, sodales semper orci. Nunc a ornare ex. Curabitur bibendum arcu congue urna vulputate egestas. Vestibulum finibus id risus et accumsan. Aenean ut volutpat tellus. Aenean tincidunt malesuada urna sit amet vestibulum. Mauris vel tellus dictum, varius lacus quis, dictum arcu.
|
||||
|
||||
Aenean quis metus eu erat feugiat cursus vel at ligula. Proin dapibus sodales urna, id euismod lectus tempus id. Pellentesque ex ligula, convallis et erat vel, vulputate condimentum nisl. Pellentesque pharetra nulla quis massa eleifend hendrerit. Praesent sed massa ipsum. Maecenas vehicula dolor massa, id sodales urna faucibus et. Mauris ac quam non massa tincidunt feugiat et at lacus. Fusce libero massa, vulputate vel scelerisque non, mollis in leo. Ut sit amet ultricies odio. Suspendisse in sapien viverra, facilisis purus ut, pretium libero.
|
||||
|
||||
Vivamus tristique pharetra molestie. Nam a volutpat purus. Praesent consequat gravida nisi, ac blandit nisi suscipit ut. Quisque posuere, ligula a ultrices laoreet, ligula nunc vulputate libero, ut rutrum erat odio tincidunt justo. Sed vitae leo at leo fringilla bibendum. Vestibulum ut augue nec dolor auctor accumsan. Praesent laoreet id eros pulvinar commodo. Suspendisse potenti. Ut pharetra, mauris vitae blandit fringilla, odio ante tincidunt lorem, sit amet tempor metus diam ut turpis.
|
||||
|
||||
Praesent quis egestas arcu. Nullam at porta arcu. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi vulputate ligula malesuada ligula luctus, vulputate tempus erat bibendum. Nunc ullamcorper non lectus at euismod. Etiam nibh felis, tincidunt a metus vel, pellentesque rhoncus neque. Etiam at diam in erat luctus interdum. Nunc vel ipsum pulvinar, sollicitudin lacus ac, tempus urna. Etiam vel lacinia sapien. Pellentesque sagittis velit vel mi efficitur iaculis. Integer euismod sit amet urna in sagittis. Cras eleifend ut nibh in facilisis. Donec et lacus vitae nunc placerat sodales. Nulla sed hendrerit ligula, at dapibus sapien.
|
||||
|
||||
Praesent at iaculis ex. Curabitur est purus, cursus a faucibus quis, dictum id velit. Donec dignissim fringilla viverra. Nunc mauris felis, laoreet sit amet sagittis at, vestibulum in libero. Maecenas quis orci turpis. Quisque ut nibh vitae magna mollis consequat id at mauris. Aliquam eu odio eget nulla bibendum sodales. Quisque vel orci eleifend nisi pretium lacinia. Suspendisse eget risus eget mi volutpat molestie eget quis lacus. Duis nisi libero, tincidunt nec nulla id, faucibus cursus felis.
|
||||
|
||||
Donec tempor eget risus pellentesque molestie. Phasellus porta neque vel arcu egestas, nec blandit velit fringilla. Nullam porta faucibus justo vitae laoreet. Pellentesque viverra id nunc eu varius. Nulla pulvinar lobortis iaculis. Etiam vestibulum odio nec velit tristique, a tristique nisi mattis. In sed fringilla orci, vitae efficitur odio. Quisque dui odio, ornare eget velit at, lacinia consequat libero. Quisque lectus nulla, aliquet eu leo in, porta rutrum diam. Donec nec mattis neque. Nam rutrum, odio ac eleifend bibendum, dolor arcu rutrum neque, eget porta elit tellus a lacus. Sed massa metus, sollicitudin et sapien eu, finibus tempus orci. Proin et sapien sit amet erat molestie interdum. In quis rutrum velit, faucibus ultrices tellus.
|
||||
|
||||
Sed sagittis sed justo eget tincidunt. Maecenas ut leo sagittis, feugiat magna et, viverra velit. Maecenas ex arcu, feugiat at consequat vitae, auctor eu massa. Integer egestas, enim vitae maximus convallis, est lectus pretium mauris, ac posuere lectus nisl quis quam. Aliquam tempus laoreet mi, vitae dapibus dolor varius dapibus. Suspendisse potenti. Donec sit amet purus nec libero dapibus tristique. Pellentesque viverra bibendum ligula. Donec sed felis et ex lobortis laoreet. Phasellus a fringilla libero, vitae malesuada nulla. Pellentesque blandit mattis lacus, et blandit tortor laoreet consequat. Suspendisse libero nunc, viverra sed fermentum in, accumsan egestas arcu. Proin in placerat elit. Sed interdum imperdiet malesuada. Suspendisse aliquet quis mauris eget sollicitudin.
|
||||
|
||||
Vivamus accumsan tellus non erat volutpat, quis dictum dolor feugiat. Praesent rutrum nunc ac est mollis cursus. Fusce semper volutpat dui ut egestas. Curabitur sit amet posuere massa. Cras tincidunt nulla et mi mollis imperdiet. Suspendisse scelerisque ex id sodales vulputate. In nunc augue, pharetra in placerat eu, mattis id tellus. Vivamus cursus efficitur vehicula. Nulla aliquet vehicula aliquet.
|
||||
|
||||
Sed cursus tellus sed porta pulvinar. Sed vitae nisi neque. Nullam aliquet, lorem et efficitur scelerisque, arcu diam aliquam felis, sed pulvinar lorem odio et turpis. Praesent convallis pulvinar turpis eu iaculis. Aliquam nec gravida mi. Curabitur eu nibh tempor, blandit justo in, ultrices felis. Fusce placerat metus non mi sagittis rutrum. Morbi sed dui fringilla, sagittis mauris eget, imperdiet nunc. Phasellus hendrerit sem elit, id hendrerit libero auctor sit amet. Integer sodales elit sit amet consequat cursus.
|
||||
|
||||
Nam semper est eget nunc mollis, in pellentesque lectus fringilla. In finibus vel diam id semper. Nunc mattis quis erat eu consectetur. In hac habitasse platea dictumst. Nullam et ipsum vestibulum ex pulvinar ultricies sit amet id velit. Aenean suscipit mi tortor, a lobortis magna viverra non. Nulla condimentum aliquet ante et ullamcorper. Pellentesque porttitor arcu a posuere tempus. Aenean lacus quam, imperdiet eu justo vitae, pretium efficitur ex. Duis id purus id magna rhoncus ultrices id eu risus. Nunc dignissim et libero id dictum.
|
||||
|
||||
Quisque a tincidunt neque. Phasellus commodo mi sit amet tempor fringilla. Ut rhoncus, neque non porttitor elementum, libero nulla egestas augue, sed fringilla sapien felis ac velit. Phasellus viverra rhoncus mollis. Nam ullamcorper leo vel erat laoreet luctus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus semper a metus a cursus. Nulla sed orci egestas, efficitur purus ac, malesuada tellus. Aenean rutrum velit at tellus fermentum mollis. Aliquam eleifend euismod metus.
|
||||
|
||||
In hac habitasse platea dictumst. Vestibulum volutpat neque vitae porttitor laoreet. Nam at tellus consequat, sodales quam in, pulvinar arcu. Maecenas varius convallis diam, ac lobortis tellus pellentesque quis. Maecenas eget augue massa. Nullam volutpat nibh ac justo rhoncus, ut iaculis tellus rutrum. Fusce efficitur efficitur libero quis condimentum. Curabitur congue neque non tincidunt tristique. Fusce eget tempor ex, at pellentesque odio. Praesent luctus dictum vestibulum. Etiam non orci nunc. Vivamus vitae laoreet purus, a lobortis velit. Curabitur tincidunt purus ac lectus elementum pellentesque. Quisque sed tincidunt est.
|
||||
|
||||
Sed vel ultrices massa, vitae ultricies justo. Cras finibus mauris nec lacus tempus dignissim. Cras faucibus maximus velit, eget faucibus orci luctus vehicula. Nulla massa nunc, porta ac consequat eget, rhoncus non tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce sed maximus metus, vel imperdiet ipsum. Ut scelerisque lectus at blandit porttitor. Ut vulputate nunc pharetra, aliquet sapien ac, sollicitudin sapien. Aenean eget ante lorem. Nam accumsan venenatis tellus id dignissim.
|
||||
|
||||
Curabitur fringilla, magna non maximus dapibus, nulla sapien vestibulum lectus, sit amet semper dolor neque vitae nisl. Nunc ultrices vehicula augue sed iaculis. Maecenas nec diam mollis, suscipit orci et, vestibulum ante. Pellentesque eu nisl tortor. Nunc eleifend, lacus quis volutpat volutpat, nisi mi molestie sem, quis mollis ipsum libero a tellus. Ut viverra dolor mattis convallis interdum. Sed tempus nisl at nunc scelerisque aliquet. Quisque tempor tempor lorem id feugiat. Nullam blandit lectus velit, vitae porta lacus tincidunt a. Vivamus sit amet arcu ultrices, tincidunt mi quis, viverra quam. Aenean fringilla libero elementum lorem semper, quis pulvinar eros gravida. Nullam sodales blandit mauris, sed fermentum velit fermentum sit amet. Donec malesuada mauris in augue sodales vulputate. Vestibulum gravida turpis id elit rhoncus dignissim. Integer non congue lorem, eu viverra orci.
|
||||
|
||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec at dolor magna. Aliquam consectetur erat augue, id iaculis velit pharetra ac. Integer rutrum venenatis dignissim. Integer non sodales elit. Curabitur ut magna ut nibh feugiat aliquam ac ut risus. Morbi nibh quam, aliquam id placerat nec, vestibulum eget velit. Suspendisse at dignissim quam. Vivamus aliquet sem sed nisl volutpat, ut cursus orci ultrices. Aliquam ultrices lacinia enim, vitae aliquet neque.
|
||||
|
||||
Quisque scelerisque finibus diam in mattis. Cras cursus auctor velit. Aliquam sem leo, fermentum et maximus et, molestie a libero. Aenean justo elit, rutrum a ornare id, egestas eget enim. Aenean auctor tristique erat. Curabitur condimentum libero lacus, nec consequat orci vestibulum sed. Fusce elit ligula, blandit vitae sapien vitae, dictum ultrices risus. Nam laoreet suscipit sapien, at interdum velit faucibus sit amet. Duis quis metus egestas lectus elementum posuere non nec libero. Aliquam a dolor bibendum, facilisis nunc a, maximus diam. Vestibulum suscipit tristique magna, non dignissim turpis sodales sed. Nunc ornare, velit ac facilisis fringilla, dolor mi consectetur lorem, vitae finibus erat justo suscipit urna. Maecenas sit amet eros erat. Nunc non arcu ornare, suscipit lorem eget, sodales mauris. Aliquam tincidunt, quam nec mollis lacinia, nisi orci fermentum libero, consequat eleifend lectus quam et sapien. Vestibulum a quam urna.
|
||||
|
||||
Cras arcu leo, euismod ac ullamcorper at, faucibus sed massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus porttitor velit in enim interdum, non commodo metus ornare. Morbi vel lorem quis nisl luctus tristique quis vitae nisl. Suspendisse condimentum tortor enim, nec eleifend ipsum euismod et. Sed gravida quam ut tristique lacinia. Mauris eu interdum ipsum, ac ultrices odio. Nullam auctor tellus a risus porttitor vehicula. Nulla blandit euismod dictum. In pharetra, enim iaculis pulvinar interdum, dui nunc placerat nunc, sit amet pretium lectus nulla vitae quam. Phasellus quis enim sollicitudin, varius nulla id, ornare purus. Donec quam lacus, vestibulum quis nunc ac, mollis dictum nisi. Cras ut mollis elit. Maecenas ultrices ligula at risus faucibus scelerisque. Etiam vitae porttitor purus. Curabitur blandit lectus urna, ut hendrerit tortor feugiat ut.
|
||||
|
||||
Phasellus fringilla, sapien pellentesque commodo pharetra, ante libero aliquam tellus, ut consectetur augue libero a sapien. Maecenas blandit luctus nisl eget aliquet. Maecenas vitae porta dolor, faucibus laoreet sapien. Suspendisse lobortis, ipsum sed vehicula aliquam, elit purus scelerisque dui, rutrum consectetur diam odio et lorem. In nec lacinia metus. Donec viverra libero est, vel bibendum erat condimentum quis. Donec feugiat purus leo. In laoreet vitae felis a porttitor. Mauris ullamcorper, lacus id condimentum suscipit, neque magna pellentesque arcu, eget cursus neque tellus id metus. Curabitur volutpat ac orci vel ultricies.
|
||||
|
||||
Sed ut finibus erat. Sed diam purus, varius non tincidunt quis, ultrices sit amet ipsum. Donec et egestas nulla. Suspendisse placerat nisi at dui laoreet iaculis. Aliquam aliquet leo at augue faucibus molestie. Nullam lacus augue, hendrerit sed nisi eu, faucibus porta est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam ut leo aliquet sem fermentum rutrum quis ac justo. Integer placerat aliquam nisl ut sagittis. Proin erat orci, lobortis et sem eget, eleifend fringilla augue. Mauris varius laoreet arcu, sed tincidunt felis. Pellentesque venenatis lorem odio, id pulvinar velit molestie feugiat. Donec mattis lacus sed eleifend pulvinar.
|
||||
|
||||
Sed condimentum ex in tincidunt hendrerit. Etiam eget risus lacinia, euismod nibh eu, pellentesque quam. Proin elit eros, convallis id mauris ac, bibendum ultrices lectus. Morbi venenatis, purus id fermentum consequat, nunc libero tincidunt ligula, non dictum ligula orci nec quam. Nulla nec ultrices lorem. Aenean maximus augue vel dictum pharetra. Etiam turpis urna, pellentesque quis malesuada eu, molestie faucibus felis.
|
||||
|
||||
Vestibulum pharetra augue ut quam blandit congue in nec risus. Proin eu nibh eu dui eleifend porta vitae id lectus. Proin lacus nibh, lobortis sed ligula vitae, interdum lobortis erat. Suspendisse potenti. In sollicitudin quis sapien ut aliquet. Mauris ac nulla arcu. Fusce tristique justo quis lectus mollis, eu volutpat lectus finibus. Vivamus venenatis facilisis ex ut vestibulum.
|
||||
|
||||
Etiam varius lobortis purus, in hendrerit elit tristique at. In tempus, augue vestibulum fermentum gravida, ligula tellus vulputate arcu, eu molestie ex sapien at purus. Vestibulum nec egestas metus. Duis pulvinar quam nec consequat interdum. Aenean non dapibus lacus. Aliquam sit amet aliquet nulla. Sed venenatis volutpat purus nec convallis. Phasellus aliquet semper sodales. Cras risus sapien, condimentum auctor urna a, pulvinar ornare nisl. Sed tincidunt felis elit, ut elementum est bibendum ac. Morbi interdum justo vel dui faucibus condimentum.
|
||||
|
||||
Sed convallis eu sem at tincidunt. Nullam at auctor est, et ullamcorper ipsum. Pellentesque eget ante ante. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer euismod, sapien sed dapibus ornare, nibh enim maximus lacus, lacinia placerat urna quam quis felis. Morbi accumsan id nisl ut condimentum. Donec bibendum nisi est, sed volutpat lorem rhoncus in. Vestibulum ac lacinia nunc, eget volutpat magna. Integer aliquam pharetra ipsum, id placerat nunc volutpat quis. Etiam urna diam, rhoncus sit amet varius vel, euismod vel sem. Nullam vel molestie urna. Vivamus ornare erat at venenatis euismod. Suspendisse potenti. Fusce diam justo, tincidunt vel sem at, commodo faucibus nisl. Duis gravida efficitur diam, vel sagittis erat pulvinar ut.
|
||||
|
||||
Quisque vel pharetra felis. Duis efficitur tortor dolor, vitae porttitor erat fermentum sed. Sed eu mi purus. Etiam dignissim tortor eu tempus molestie. Aenean pretium erat enim, in hendrerit ante hendrerit at. Sed ut risus vel nunc venenatis ultricies quis in lacus. Pellentesque vitae purus euismod, placerat risus non, ullamcorper augue. Quisque varius quam ligula, nec aliquet ex faucibus vitae. Quisque rhoncus sit amet leo tincidunt mattis. Cras id mauris eget purus pretium gravida sit amet eu augue. Aliquam dapibus odio augue, id lacinia velit pulvinar eu.
|
||||
|
||||
Mauris fringilla, tellus nec pharetra iaculis, neque nisi ultrices massa, et tincidunt sem dui sed mi. Curabitur erat lorem, venenatis quis tempus lacinia, tempus sit amet nunc. Aliquam at neque ac metus commodo dictum quis vitae justo. Phasellus eget lacus tempus, blandit lorem vel, rutrum est. Aenean pharetra sem ut augue lobortis dignissim. Sed rhoncus at nulla id ultrices. Cras id condimentum felis. In suscipit luctus vulputate. Donec tincidunt lacus nec enim tincidunt sollicitudin ut quis enim. Nam at libero urna. Praesent sit amet massa vitae massa ullamcorper vehicula.
|
||||
|
||||
Nullam bibendum augue ut turpis condimentum bibendum. Proin sit amet urna hendrerit, sodales tortor a, lobortis lectus. Integer sagittis velit turpis, et tincidunt nisi commodo eget. Duis tincidunt elit finibus accumsan cursus. Aenean dignissim scelerisque felis vel lacinia. Nunc lacinia maximus luctus. In hac habitasse platea dictumst. Vestibulum eget urna et enim tempor tempor. Nam feugiat, felis vel vestibulum tempus, orci justo viverra diam, id dapibus lorem justo in ligula.
|
||||
|
||||
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In ac pellentesque sem. Vestibulum lacinia magna dui, eu lacinia augue placerat et. Maecenas pulvinar congue est. Pellentesque commodo dui non pulvinar scelerisque. Etiam interdum est posuere sem bibendum, ac commodo magna dictum. Cras ipsum turpis, rhoncus nec posuere vitae, laoreet a arcu. Integer ac massa sit amet enim placerat lacinia sed ultrices arcu. Suspendisse sem nibh, luctus sit amet volutpat in, pellentesque eu metus. Ut gravida neque eget mi accumsan tempus. Nam sit amet aliquet nibh.
|
||||
|
||||
Pellentesque a purus cursus nulla hendrerit congue quis et odio. Aenean hendrerit, leo ullamcorper sagittis hendrerit, erat dui molestie quam, sed condimentum lacus risus sed tellus. Morbi a dapibus lectus, ut feugiat ex. Phasellus pretium quam et sapien mollis, vel iaculis dui dignissim. Sed ullamcorper est turpis, a viverra lorem consectetur in. Aenean aliquet nibh non cursus rutrum. Suspendisse at tristique urna, id lobortis urna. In hac habitasse platea dictumst. Phasellus libero velit, rutrum sed tellus nec, dapibus tincidunt ligula. Quisque vel dui venenatis, consequat nisl ut, lacinia ipsum. Phasellus vitae magna pellentesque, lobortis est id, faucibus quam. Nam eleifend faucibus dui vel pellentesque.
|
||||
|
||||
Etiam ut est non lacus tincidunt interdum. Maecenas sed massa urna. Quisque ut nibh tortor. Pellentesque felis ipsum, tempor finibus ipsum et, euismod pretium metus. Donec sit amet est ipsum. Quisque rhoncus justo non finibus elementum. Nulla nec lectus ac tortor placerat fringilla. Phasellus ac ultrices nunc, eu efficitur nisl. Nulla rhoncus nunc vitae ante dictum tincidunt. Nunc ultrices, massa sit amet malesuada dignissim, lectus lacus consequat sapien, non eleifend metus sem in eros. Phasellus mauris ante, dictum sit amet suscipit ac, rhoncus eget nisi. Phasellus at orci mollis, imperdiet neque eget, faucibus nulla. In at purus massa. Pellentesque quis rutrum lectus.
|
||||
|
||||
Integer eu faucibus turpis, sit amet mollis massa. Vestibulum id nulla commodo, rutrum ipsum sed, semper ante. Phasellus condimentum orci nec nibh convallis, ac maximus orci ullamcorper. Maecenas vitae sollicitudin mi. Integer et finibus lectus, et condimentum ligula. Donec elementum tristique quam vitae dapibus. Morbi euismod ipsum in tristique ullamcorper.
|
||||
|
||||
Duis fermentum non enim eu auctor. Quisque lacinia nibh vehicula nibh posuere, eu volutpat turpis facilisis. Ut ac faucibus nulla. Sed eleifend quis ex et pellentesque. Vestibulum sollicitudin in libero id fringilla. Phasellus dignissim purus consequat, condimentum dui sit amet, condimentum ante. Pellentesque ac consectetur massa, quis sagittis est. Nulla maximus tristique risus accumsan convallis. Curabitur imperdiet ac lacus a ultrices. Nulla facilisi. Sed quis quam quis lectus placerat lobortis vel sed turpis. In mollis dui id neque iaculis, ut aliquet tellus malesuada. Proin at luctus odio, vel blandit sapien. Praesent dignissim tortor vehicula libero fringilla, nec ultrices erat suscipit. Maecenas scelerisque purus in dapibus fermentum.
|
||||
|
||||
Curabitur magna odio, mattis in tortor ut, porttitor congue est. Vestibulum mollis lacinia elementum. Fusce maximus erat vitae nunc rutrum lobortis. Integer ligula eros, auctor vel elit non, posuere luctus lacus. Maecenas quis auctor massa. Ut ipsum lacus, efficitur posuere euismod et, hendrerit efficitur est. Phasellus fringilla, quam id tincidunt pretium, nunc dui sollicitudin orci, eu dignissim nisi metus ut magna. Integer lobortis interdum dolor, non bibendum purus posuere et. Donec non lectus aliquet, pretium dolor eu, cursus massa. Sed ut dui sapien. In sed vestibulum massa. Pellentesque blandit, dui non sodales vehicula, orci metus mollis nunc, non pharetra ex tellus ac est. Mauris sagittis metus et fermentum pretium. Nulla facilisi. Quisque quis ante ut nulla placerat mattis ut quis nisi.
|
||||
|
||||
Sed quis nulla ligula. Quisque dignissim ligula urna, sed aliquam purus semper at. Suspendisse potenti. Nunc massa lectus, pharetra vehicula arcu bibendum, imperdiet sodales ipsum. Nam ac sapien diam. Mauris iaculis fringilla mattis. Pellentesque tempus eros sit amet justo volutpat mollis. Phasellus ac turpis ipsum. Morbi vel ante elit. Aenean posuere quam consequat velit varius suscipit. Donec tempor quam ut nibh cursus efficitur.
|
||||
|
||||
Morbi molestie dolor nec sem egestas suscipit. Etiam placerat pharetra lectus, et ullamcorper risus tristique in. Sed faucibus ullamcorper lectus eget fringilla. Maecenas malesuada hendrerit congue. Sed eget neque a erat placerat tincidunt. Aliquam vitae dignissim turpis. Fusce at placerat magna, a laoreet lectus. Maecenas a purus nec diam gravida fringilla. Nam malesuada euismod ante non vehicula. In faucibus bibendum leo, faucibus posuere nisl pretium quis. Fusce finibus bibendum finibus. Vestibulum eu justo maximus, hendrerit diam nec, dignissim sapien. Aenean dolor lacus, malesuada quis vestibulum ac, venenatis ac ipsum. Cras a est id nunc finibus facilisis. Cras lacinia neque et interdum vehicula. Suspendisse vulputate tellus elit, eget tempor dui finibus vel.
|
||||
|
||||
Cras sed pretium odio. Proin hendrerit elementum felis in tincidunt. Nam sed turpis vel justo molestie accumsan condimentum eu nunc. Praesent lobortis euismod rhoncus. Nulla vitae euismod nibh, quis mattis mi. Fusce ultrices placerat porttitor. Duis sem ipsum, pellentesque sit amet odio a, molestie vulputate mauris.
|
||||
|
||||
Duis blandit mollis ligula, sit amet mattis ligula finibus sit amet. Nunc a leo molestie, placerat diam et, vestibulum leo. Suspendisse facilisis neque purus, nec pellentesque ligula fermentum nec. Aenean malesuada mauris lorem, eu blandit arcu pulvinar quis. Duis laoreet urna lacus, non maximus arcu rutrum ultricies. Nulla augue dolor, suscipit eu mollis eu, aliquam condimentum diam. Ut semper orci luctus, pharetra turpis at, euismod mi. Nulla leo diam, finibus sit amet purus sed, maximus dictum lorem. Integer eu mi id turpis laoreet rhoncus.
|
||||
|
||||
Integer a mauris tincidunt, finibus orci ut, pretium mauris. Nulla molestie nunc mi, id finibus lorem elementum sed. Proin quis laoreet ante. Integer nulla augue, commodo id molestie quis, rutrum ut turpis. Suspendisse et tortor turpis. Sed ut pharetra massa. Pellentesque elementum blandit sem, ut elementum tellus egestas a. Fusce eu purus nibh.
|
||||
|
||||
Cras dignissim ligula scelerisque magna faucibus ullamcorper. Proin at condimentum risus, auctor malesuada quam. Nullam interdum interdum egestas. Nulla aliquam nisi vitae felis mollis dictum. Suspendisse dapibus consectetur tortor. Ut ut nisi non sem bibendum tincidunt. Vivamus suscipit leo quis gravida dignissim.
|
||||
|
||||
Aliquam interdum, leo id vehicula mollis, eros eros rhoncus diam, non mollis ligula mi eu mauris. Sed ultrices vel velit sollicitudin tincidunt. Nunc auctor metus at ligula gravida elementum. Praesent interdum eu elit et mollis. Duis egestas quam sit amet velit dignissim consequat. Aliquam ac turpis nec nunc convallis sagittis. Fusce blandit, erat ac fringilla consectetur, dolor eros sodales leo, vel aliquet risus nisl et diam. Aliquam luctus felis vitae est eleifend euismod facilisis et lacus. Sed leo tellus, auctor eu arcu in, volutpat sagittis nisl. Pellentesque nisl ligula, placerat vel ullamcorper at, vulputate ac odio. Morbi ac faucibus orci, et tempus nulla. Proin rhoncus rutrum dolor, in venenatis mauris. Suspendisse a fermentum augue, non semper mi. Nunc eget pretium neque. Phasellus augue erat, feugiat ac aliquam congue, rutrum non sapien. Pellentesque ac diam gravida, consectetur felis at, ornare neque.
|
||||
|
||||
Nullam interdum mattis sapien quis porttitor. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus aliquet rutrum ipsum id euismod. Maecenas consectetur massa et mi porta viverra. Nunc quam nibh, dignissim vitae maximus et, ullamcorper nec lorem. Nunc vitae justo dapibus, luctus lacus vitae, pretium elit. Maecenas et efficitur leo. Curabitur mauris lectus, placerat quis vehicula vitae, auctor ut urna. Quisque rhoncus pharetra luctus. In hac habitasse platea dictumst. Integer sit amet metus nec eros malesuada aliquam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi hendrerit mi ac leo aliquam, sit amet ultricies libero commodo. Mauris dapibus purus metus, sit amet viverra nibh imperdiet et. Nullam porta nulla tellus, quis vehicula diam imperdiet non. Vivamus enim massa, bibendum in fermentum in, ultrices at ex.
|
||||
|
||||
Suspendisse fermentum id nibh eget accumsan. Duis dapibus bibendum erat ut sollicitudin. Aliquam nec felis risus. Pellentesque rhoncus ligula id sem maximus mollis sed nec massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ipsum ipsum, sodales sed enim id, convallis faucibus eros. Donec ultricies dictum tincidunt. Cras vitae nibh arcu. Pellentesque cursus, sapien nec consequat fermentum, ipsum ante suscipit dui, imperdiet hendrerit est nisl eu massa. Quisque vitae sem ligula. Aenean iaculis metus ut mauris interdum laoreet. Vivamus sed gravida dolor.
|
||||
|
||||
Morbi nulla metus, porttitor sed eros sit amet, efficitur efficitur est. In vel nisl urna. Ut aliquet tellus at congue convallis. Phasellus imperdiet lobortis sollicitudin. Integer sodales, sem eu ultricies pharetra, erat erat porttitor odio, eget dapibus libero ipsum eget velit. Phasellus gravida nulla nisl, eu pharetra mi auctor vel. Sed blandit pharetra velit, ut egestas libero placerat non. Aliquam a interdum quam. Proin at tortor nec dui sollicitudin tempus sed vestibulum elit. Nunc non sollicitudin velit.
|
||||
|
||||
Aenean consequat diam velit, sed rutrum tortor faucibus dictum. Quisque at semper augue. Duis ut est eget mi ornare bibendum id et ligula. Phasellus consequat tortor non leo pulvinar posuere. Proin vestibulum eleifend felis, in hendrerit tortor sollicitudin eu. Phasellus hendrerit, lacus vel laoreet interdum, dui tortor consequat justo, commodo ultricies arcu felis vitae enim. Vivamus eu sapien at leo suscipit rutrum eu at justo. Aenean et dolor a libero ullamcorper posuere. Integer laoreet placerat nisi in vulputate. Mauris laoreet eget risus sed cursus. Donec scelerisque neque a libero eleifend hendrerit. Nulla varius condimentum nunc sit amet fermentum. Aliquam lorem ex, varius nec mollis ut, ultrices in neque. Morbi sit amet porta leo. Integer iaculis fermentum lacus in vestibulum.
|
||||
|
||||
Ut gravida, tellus ut maximus ultrices, erat est venenatis nisl, vitae pretium massa ex ac magna. Sed non purus eget ligula aliquet volutpat non quis arcu. Nam aliquam tincidunt risus, sit amet fringilla sapien vulputate ut. Mauris luctus suscipit pellentesque. Nunc porttitor dapibus ex quis tempus. Ut ullamcorper metus a eros vulputate, vitae viverra lectus convallis. Mauris semper imperdiet augue quis tincidunt. Integer porta pretium magna, sed cursus sem scelerisque sollicitudin. Nam efficitur, nibh pretium eleifend vestibulum, purus diam posuere sem, in egestas mauris augue sit amet urna.
|
||||
|
||||
Vestibulum tincidunt euismod massa in congue. Duis interdum metus non laoreet fringilla. Donec at ligula congue, tincidunt nunc non, scelerisque nunc. Donec bibendum magna non est scelerisque feugiat at nec neque. Ut orci tortor, tempus eget massa non, dignissim faucibus dolor. Nam odio risus, accumsan pretium neque eget, accumsan dignissim dui. In ut neque auctor, scelerisque tellus sed, ullamcorper nisi. Suspendisse varius cursus quam at hendrerit. Vivamus elit libero, sagittis vitae sem ac, vulputate iaculis ligula.
|
||||
|
||||
Sed lobortis laoreet purus sit amet rutrum. Pellentesque feugiat non leo vel lacinia. Quisque feugiat nisl a orci bibendum vestibulum. In et sollicitudin urna. Morbi a arcu ac metus faucibus tempus. Nam eu imperdiet sapien, suscipit mattis tortor. Aenean blandit ipsum nisi, a eleifend ligula euismod at. Integer tincidunt pharetra felis, mollis placerat mauris hendrerit at. Curabitur convallis, est sit amet luctus volutpat, massa lacus cursus augue, sed eleifend magna quam et risus. Aliquam lobortis tincidunt metus vitae porttitor. Suspendisse potenti. Aenean ullamcorper, neque id commodo luctus, nulla nunc lobortis quam, id dapibus neque dui nec mauris. Etiam quis lorem quis elit commodo ornare. Ut pharetra purus ultricies enim ultrices efficitur. Proin vehicula tincidunt molestie. Mauris et placerat sem.
|
||||
|
||||
Aliquam erat volutpat. Suspendisse velit turpis, posuere ac lacus eu, lacinia laoreet velit. Sed interdum felis neque, id blandit sem malesuada sit amet. Ut sagittis justo erat, efficitur semper orci tempor sed. Donec enim massa, posuere varius lectus egestas, pellentesque posuere mi. Cras tincidunt ut libero sed mattis. Suspendisse quis magna et tellus posuere interdum vel at purus. Pellentesque fringilla tristique neque, id aliquet tellus ultricies non. Duis ut tellus vel odio lobortis vulputate.
|
||||
|
||||
Integer at magna ac erat convallis vestibulum. Sed lobortis porttitor mauris. Fusce varius lorem et volutpat pulvinar. Aenean ac vulputate lectus, vitae consequat velit. Suspendisse ex dui, varius ut risus ut, dictum scelerisque sem. Vivamus urna orci, volutpat ut convallis ac, venenatis vitae urna. In hac habitasse platea dictumst. Etiam eu purus arcu. Aenean vulputate leo urna, vel tristique dui sagittis euismod. Suspendisse non tellus efficitur ante rhoncus volutpat at et sapien.
|
||||
|
||||
Sed dapibus accumsan porttitor. Phasellus facilisis lectus finibus ligula dignissim, id pulvinar lectus feugiat. Nullam egestas commodo nisi posuere aliquet. Morbi sit amet tortor sagittis, rutrum dui nec, dapibus sapien. Sed posuere tortor tortor, interdum auctor magna varius vitae. Vestibulum id sagittis augue. Curabitur fermentum arcu sem, eu condimentum quam rutrum non. Phasellus rutrum nibh quis lectus rhoncus pretium. Curabitur dictum interdum elit. Vestibulum maximus sodales imperdiet. Mauris auctor nec purus sed venenatis. In in urna purus.
|
||||
|
||||
Duis placerat molestie suscipit. Morbi a elit id purus efficitur consequat. Nunc ac commodo turpis. Etiam sit amet lacus a ipsum tempus venenatis sed vel nibh. Duis elementum aliquam mi sed tristique. Morbi ligula tortor, semper ac est vel, lobortis maximus erat. Curabitur ipsum felis, laoreet vel condimentum eget, ullamcorper sit amet mauris. Nulla facilisi. Nam at purus sed mi egestas placerat vitae vel magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse at dignissim diam. Phasellus consectetur eget neque vel viverra. Donec sollicitudin mattis dolor vel malesuada. Vivamus vehicula leo neque, vitae fermentum leo posuere et. Praesent dui est, finibus sit amet tristique quis, pharetra vel nibh.
|
||||
|
||||
Duis nulla leo, accumsan eu odio eget, sagittis semper orci. Quisque ullamcorper ligula quam, commodo porttitor mauris ullamcorper eu. Cras varius sagittis felis in aliquam. Duis sodales risus ac justo vehicula, nec mattis diam lacinia. Cras eget lectus ipsum. Ut commodo, enim vitae malesuada hendrerit, ex dolor egestas lectus, sit amet hendrerit metus diam nec est. Vestibulum tortor metus, lobortis sit amet ante eget, tempor molestie lacus. In molestie et urna et semper. Mauris mollis, sem non hendrerit condimentum, sapien nisi cursus est, non suscipit quam justo non metus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam enim est, porta ac feugiat vitae, rutrum in lorem. Duis vehicula tortor ut posuere maximus.
|
||||
|
||||
Nullam vestibulum non tellus sed commodo. Quisque mattis elit sit amet sapien sollicitudin, ut condimentum nisl congue. Aenean sagittis massa vel elit faucibus fermentum. Donec tincidunt nisi nec nisl sodales pellentesque. Mauris congue congue ligula ut suscipit. Vivamus velit tortor, tempor et gravida eget, fermentum sit amet ante. Nullam fringilla, lorem at ultrices cursus, urna neque ornare dolor, eu lacinia orci enim sed nibh. Ut a ullamcorper lectus, id mattis purus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean maximus sollicitudin posuere. Nunc at augue lacus. Aenean efficitur leo sit amet lacinia efficitur.
|
||||
|
||||
Quisque venenatis quam mi, in pharetra odio vulputate eu. In vel nisl pulvinar, pulvinar ligula ut, sodales risus. Sed efficitur lectus at vestibulum tincidunt. Vestibulum eu ullamcorper elit. Fusce vestibulum magna enim, et tempor lacus posuere vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer leo elit, luctus nec mattis sit amet, sollicitudin in turpis.
|
||||
|
||||
Proin convallis venenatis leo, vitae tristique erat iaculis nec. Nulla facilisi. Duis porttitor, sapien et bibendum vulputate, sem libero sodales lacus, non malesuada felis erat ut libero. Nam non felis semper, finibus est a, mattis mauris. Praesent nec eros quam. Nulla hendrerit, augue consectetur eleifend ultricies, purus mi condimentum nulla, eget dapibus est nunc sed libero. Nullam elementum dui erat, vitae luctus libero sollicitudin et. Nulla odio magna, placerat in augue eu, dapibus imperdiet odio. Suspendisse imperdiet metus sit amet rhoncus dapibus. Cras at enim et urna vehicula cursus eu a mauris. Integer magna ante, eleifend ac placerat vitae, porta at nisi. Cras eget malesuada orci. Curabitur nunc est, vulputate id viverra et, dignissim sed odio. Curabitur non mattis sem. Sed bibendum, turpis vitae vehicula faucibus, nunc quam ultricies lectus, vitae viverra felis turpis at libero.
|
||||
|
||||
Nullam ut egestas ligula. Proin hendrerit justo a lectus commodo venenatis. Nulla facilisi. Ut cursus lorem quis est bibendum condimentum. Aenean in tristique odio. Fusce tempor hendrerit ipsum. Curabitur mollis felis justo, quis dapibus erat auctor vel. Sed augue lectus, finibus ut urna quis, ullamcorper vestibulum dui. Etiam molestie aliquam tempor. Integer mattis sollicitudin erat, et tristique elit varius vel. Mauris a ex justo.
|
||||
|
||||
Nam eros est, imperdiet non volutpat rutrum, pellentesque accumsan ligula. Duis sit amet turpis metus. Aenean in rhoncus metus, ac fringilla ex. Suspendisse condimentum egestas purus, ut pharetra odio vulputate vel. Duis tincidunt massa a placerat ultrices. Mauris ultricies nibh sit amet condimentum malesuada. Duis tincidunt id ipsum sed congue.
|
||||
|
||||
Praesent eu ex augue. Nullam in porta ligula. In tincidunt accumsan arcu, in pellentesque magna tristique in. Mauris eleifend libero ac nisl viverra faucibus. Nam sollicitudin dolor in commodo hendrerit. Cras at orci metus. Ut quis laoreet orci. Vivamus ultrices leo pellentesque tempor aliquet. Maecenas ut eros vitae purus placerat vestibulum. Etiam vitae gravida dolor, quis rhoncus diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
||||
|
||||
Suspendisse fringilla lacinia sagittis. Integer tincidunt consectetur tristique. Morbi non orci convallis, congue sapien quis, vulputate nunc. Donec a libero vel magna elementum facilisis non quis mi. Mauris posuere tellus non ipsum ultrices elementum. Vivamus massa velit, facilisis quis placerat aliquet, aliquet nec leo. Praesent a maximus sem. Sed neque elit, feugiat vel quam non, molestie sagittis nunc. Etiam luctus nunc ac mauris scelerisque, nec rhoncus lacus convallis. Nunc pharetra, nunc ac pulvinar aliquam, ex ipsum euismod augue, nec porttitor lacus turpis vitae neque. Fusce bibendum odio id tortor faucibus pellentesque. Sed ac porta nibh, eu gravida erat.
|
||||
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aliquam quis ullamcorper felis. Nulla mattis sagittis ante ac tincidunt. Integer ac felis efficitur, viverra libero et, facilisis ligula. Suspendisse a metus a massa rhoncus posuere. Phasellus suscipit ligula ut lacus facilisis, ac pellentesque ex tempor. Quisque consectetur massa mi, ac molestie libero dictum quis. Proin porttitor ligula quis erat tincidunt venenatis. Proin congue nunc sed elit gravida, nec consectetur lectus sodales. Etiam tincidunt convallis ipsum at vestibulum. Quisque maximus enim et mauris porttitor, et molestie magna tristique. Morbi vitae metus elit. Maecenas sed volutpat turpis. Aliquam vitae dolor vestibulum, elementum purus eget, dapibus nibh. Nullam egestas dui ac rutrum semper.
|
||||
|
||||
Etiam hendrerit est metus, et condimentum metus aliquam ac. Pellentesque id neque id ipsum rhoncus vulputate. Aliquam erat nisl, posuere sit amet ligula ac, fermentum blandit felis. Vivamus fermentum mi risus, non lacinia purus viverra id. Aenean ac sapien consequat, finibus mauris nec, porta sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed quis consectetur ex, dignissim bibendum nulla. Phasellus ac libero at quam vehicula euismod non eu leo. Phasellus a sapien augue.
|
||||
|
||||
Maecenas ligula dui, bibendum vitae mauris et, auctor laoreet felis. Duis non libero a mi semper mattis. Quisque consequat luctus massa, quis tristique eros auctor feugiat. Maecenas sodales euismod neque vitae facilisis. Nullam laoreet imperdiet velit at pellentesque. Etiam massa odio, facilisis a consequat vitae, placerat vel magna. Nunc sagittis eros nec urna fringilla, pulvinar vestibulum nibh scelerisque. Sed magna metus, cursus eu consequat et, pharetra a est. Suspendisse elementum neque a dui malesuada lacinia. Donec sed ipsum volutpat, cursus urna id, ullamcorper arcu. Maecenas laoreet nisl eget velit egestas sollicitudin. Etiam nisl turpis, mollis id dignissim vitae, tristique vehicula ante. Maecenas eget placerat est, at rutrum augue. Vivamus faucibus lacinia ullamcorper. Sed pulvinar urna sodales ante sodales, at gravida leo dictum.
|
||||
|
||||
Morbi maximus, quam a lobortis bibendum, enim felis varius elit, ac vehicula elit nisl ut lacus. Quisque ut arcu augue. Praesent id turpis quam. Sed sed arcu eros. Maecenas at cursus lorem, ac eleifend nisi. Fusce mattis felis at commodo pharetra. Praesent ac commodo ipsum. Quisque finibus et eros vitae tincidunt. In hac habitasse platea dictumst. Praesent purus ipsum, luctus lobortis ornare quis, auctor eget justo. Nam vel enim sollicitudin, faucibus tortor eu, sagittis eros. Ut nec consectetur erat. Donec ultricies malesuada ligula, a hendrerit sapien volutpat in. Maecenas sed enim vitae sapien pulvinar faucibus.
|
||||
|
||||
Proin semper nunc nibh, non consequat neque ullamcorper vel. Maecenas lobortis sagittis blandit. Aenean et arcu ultricies turpis malesuada malesuada. Ut quam ex, laoreet ut blandit cursus, feugiat vitae dolor. Etiam ex lacus, scelerisque vel erat vel, efficitur tincidunt magna. Morbi tristique lacinia dolor, in egestas magna ultrices vitae. Integer ultrices leo ac tempus venenatis. Praesent ac porta tortor. Vivamus ornare blandit tristique. Nulla rutrum finibus pellentesque. In non dui elementum, fermentum ipsum vel, varius magna. Pellentesque euismod tortor risus, ac pellentesque nisl faucibus eget.
|
||||
|
||||
Vivamus eu enim purus. Cras ultrices rutrum egestas. Sed mollis erat nibh, at posuere nisl luctus nec. Nunc vulputate, sapien id auctor molestie, nisi diam tristique ante, non convallis tellus nibh at orci. Morbi a posuere purus, in ullamcorper ligula. Etiam elementum sit amet dui imperdiet iaculis. Proin vitae tincidunt ipsum, sit amet placerat lectus. Curabitur commodo sapien quam, et accumsan lectus fringilla non. Nullam eget accumsan enim, ac pharetra mauris. Sed quis tristique velit, vitae commodo nisi. Duis turpis dui, maximus ut risus at, finibus consequat nunc. Maecenas sed est accumsan, aliquet diam in, facilisis risus. Curabitur vehicula rutrum auctor. Nam iaculis risus pulvinar maximus viverra. Nulla vel augue et ex sagittis blandit.
|
||||
|
||||
Ut sem nulla, porta ac ante ac, posuere laoreet eros. Donec sodales posuere justo a auctor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras mollis at orci hendrerit porta. Nullam sodales tortor tortor, non lacinia diam finibus id. Duis libero orci, suscipit ac odio et, dictum consequat ipsum. Pellentesque eu ligula sagittis, volutpat eros at, lacinia lorem. Cras euismod tellus in iaculis tempor. Quisque accumsan, magna a congue venenatis, ante ipsum aliquam lectus, at egestas enim nunc at justo. Quisque sem purus, viverra ut tristique ut, maximus id enim. Etiam quis placerat sem. In sollicitudin, lacus eu rutrum mollis, nulla eros luctus elit, vel dapibus urna purus nec urna. Phasellus egestas massa quam, ac molestie erat hendrerit a. Praesent ultrices neque ut turpis molestie auctor. Etiam molestie placerat purus, et euismod erat aliquam in. Morbi id suscipit justo.
|
||||
|
||||
Proin est ante, consequat at varius a, mattis quis felis. Sed accumsan nibh sit amet ipsum elementum posuere. Vestibulum bibendum id diam sit amet gravida. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi nec dolor vel ipsum dignissim hendrerit vel non ipsum. Praesent facilisis orci quis elit auctor lobortis. Phasellus cursus risus lectus, vel lobortis libero dapibus in. Quisque tristique tempus leo a pulvinar. Pellentesque a magna tincidunt, pellentesque massa nec, laoreet orci. Morbi congue ornare dolor quis commodo. Phasellus massa nisi, tincidunt at eros dictum, hendrerit lobortis urna. Maecenas porta, magna id mattis molestie, nibh tellus lobortis sem, eget tincidunt ipsum quam eu turpis.
|
||||
|
||||
Ut gravida orci risus, vel rutrum mauris vehicula id. Etiam bibendum, neque a placerat condimentum, ex orci imperdiet lectus, quis dapibus arcu lacus eget lectus. Sed consequat non mi sit amet venenatis. Fusce vestibulum erat libero, eget hendrerit risus vulputate sollicitudin. Integer sed eleifend felis. Donec commodo, sem eu mattis placerat, urna odio aliquam tellus, et laoreet justo tellus eget erat. Fusce sed suscipit tortor. Nam hendrerit nibh ac nunc auctor lacinia. Pellentesque placerat condimentum ipsum, eget semper tortor hendrerit vel. Nullam non urna eu lacus pellentesque congue ut id eros.
|
||||
|
||||
Nunc finibus leo in rhoncus tristique. Sed eu ipsum nec nisl egestas faucibus eget a felis. Pellentesque vitae nisi in nulla accumsan fermentum. Sed venenatis feugiat eleifend. Fusce porttitor varius placerat. Aliquam aliquet lacus sit amet mattis mollis. Sed vel nulla quis dolor suscipit vehicula ac viverra lorem. Duis viverra ipsum eget nulla ullamcorper fermentum. Mauris tincidunt arcu quis quam fringilla ornare. Donec et iaculis tortor. Nam ultricies libero vel ipsum aliquet efficitur. Morbi eget dolor aliquam, tempus sapien eget, viverra ante. Donec varius mollis ex, sed efficitur purus euismod interdum. Quisque vel sapien non neque tincidunt semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
|
||||
|
||||
Suspendisse sit amet purus leo. Fusce lectus lorem, aliquam ac nulla eget, imperdiet ornare eros. Nullam sem augue, varius in nisi non, sollicitudin pellentesque ante. Etiam eu odio condimentum, tempor libero et, egestas arcu. Cras pellentesque eleifend aliquet. Pellentesque non blandit ligula. Ut congue viverra rhoncus. Phasellus mattis mi ac eros placerat, eu feugiat tellus ultrices. Aenean mollis laoreet libero eu imperdiet. Cras sed pulvinar mi, ac vehicula ligula. Vestibulum sit amet ex massa. In a egestas eros.
|
||||
|
||||
Mauris pretium ipsum risus, venenatis cursus ante imperdiet id. Praesent eu turpis nec risus feugiat maximus ullamcorper ac lectus. Integer placerat at mi vel dapibus. Vestibulum fermentum turpis sit amet turpis viverra, id aliquet diam suscipit. Nam nec ex sed ante ullamcorper pharetra quis sit amet risus. Sed ac faucibus velit, id feugiat nibh. Nullam eget ipsum ex. Vivamus tincidunt non nunc non faucibus. Quisque bibendum viverra facilisis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur at nisi hendrerit quam suscipit egestas. Curabitur laoreet maximus ultricies. Duis ut tellus ac augue molestie dictum.
|
||||
|
||||
Suspendisse rhoncus iaculis erat, ut ullamcorper est tristique eget. Donec auctor nec risus at gravida. Vivamus volutpat vulputate tellus, vel ultricies eros suscipit eget. Ut pulvinar id mi eu tempus. Morbi malesuada augue in dui varius, nec blandit neque vehicula. Donec ornare nec nisl in mollis. Morbi enim nisi, rhoncus nec est id, dapibus tempus urna. Ut id elit a felis vestibulum consectetur. Duis lectus quam, pharetra sit amet diam sed, posuere vestibulum erat. Fusce vitae maximus massa. Nullam id metus tempus, iaculis risus eu, lobortis urna. Quisque in congue urna. Pellentesque placerat neque in augue dapibus, non varius ex malesuada. Curabitur ut eleifend libero. Fusce vitae ligula luctus, fermentum enim vitae, ultrices erat.
|
||||
|
||||
Sed viverra augue turpis, scelerisque egestas sapien mattis eu. Duis laoreet magna at ex pharetra dapibus. Praesent eget odio vel quam venenatis dictum. Nulla in sollicitudin dolor. Mauris lobortis nec eros vel rhoncus. Vestibulum porta viverra venenatis. Curabitur vel scelerisque quam, a egestas velit. Praesent volutpat tincidunt magna at laoreet.
|
||||
|
||||
Cras nec lorem odio. Pellentesque quis dui urna. Praesent at tellus ac lectus scelerisque placerat nec eu risus. Vestibulum sit amet mattis ligula. Vivamus sed nisi at leo elementum accumsan at sit amet arcu. Aenean mattis tellus nec leo gravida, eget hendrerit nisl faucibus. Mauris pellentesque luctus condimentum. Maecenas pretium sapien nunc, eget commodo dolor maximus id. Mauris vestibulum accumsan massa a dictum. Phasellus interdum quam ligula, ut maximus diam blandit aliquam. Nunc vitae ex eu erat condimentum consectetur. Maecenas interdum condimentum volutpat.
|
||||
|
||||
Donec et enim a libero rutrum laoreet. Praesent a condimentum sem, at tincidunt quam. In vel molestie risus. Sed urna dui, molestie vitae mollis laoreet, tempor quis lectus. Praesent vitae auctor est, et aliquet nunc. Curabitur vulputate blandit nulla, at gravida metus. Maecenas gravida dui eu iaculis tristique. Pellentesque posuere turpis nec auctor eleifend. Suspendisse bibendum diam eu tellus lobortis, et laoreet quam congue. In hac habitasse platea dictumst. Morbi dictum neque velit, eget rutrum eros ultrices sit amet.
|
||||
|
||||
Phasellus fermentum risus pharetra consectetur bibendum. Donec magna tortor, lacinia vitae nibh quis, aliquet pretium lorem. Donec turpis nisi, pretium eu enim volutpat, mattis malesuada augue. Nullam vel tellus iaculis, sollicitudin elit eget, tincidunt lacus. Fusce elementum elementum felis et iaculis. Suspendisse porta eros nec neque malesuada, in malesuada ante sollicitudin. Vivamus bibendum viverra molestie.
|
||||
|
||||
Integer feugiat, erat nec convallis aliquam, velit felis congue erat, molestie eleifend tellus erat in tellus. Nunc et justo purus. Donec egestas fermentum dui non feugiat. Quisque in sapien sagittis, gravida quam id, iaculis lectus. Cras sagittis rhoncus bibendum. Fusce quis metus in velit scelerisque tincidunt at non ipsum. Vivamus efficitur ante eu odio vulputate, vitae ultricies risus vehicula. Proin eget odio eu sem tincidunt feugiat vel id lorem.
|
||||
|
||||
Vestibulum sit amet nulla dignissim, euismod mi in, fermentum tortor. Donec ut aliquet libero, lacinia accumsan velit. Donec et nulla quam. Nullam laoreet odio nec nunc imperdiet, a congue eros venenatis. Quisque nec tellus sit amet neque interdum posuere. Duis quis mi gravida, tincidunt diam convallis, ultricies augue. Mauris consequat risus non porttitor congue. Ut in ligula consequat, viverra nunc a, eleifend enim. Duis ligula urna, imperdiet nec facilisis et, ornare eu ex. Proin lobortis lectus a lobortis porttitor. Nulla leo metus, egestas eu libero sed, pretium faucibus felis. Vestibulum non sem tortor. Nam cursus est leo. Vivamus luctus enim odio, non interdum sem dapibus a. Aenean accumsan consequat lectus in imperdiet.
|
||||
|
||||
Donec vehicula laoreet ipsum in posuere. Quisque vel quam imperdiet, sollicitudin nisi quis, suscipit velit. Morbi id sodales mauris. Curabitur tellus arcu, feugiat sed dui sit amet, sodales sagittis libero. Aenean vel suscipit metus, non placerat leo. Vestibulum quis nulla elit. Proin scelerisque non ante ut commodo. Interdum et malesuada fames ac ante ipsum primis in faucibus.
|
||||
|
||||
Sed non urna dolor. Suspendisse convallis mi porta pulvinar ultrices. Suspendisse quam ipsum, hendrerit non scelerisque molestie, interdum dictum nunc. Morbi condimentum condimentum turpis eu luctus. Pellentesque sagittis sollicitudin odio, sed ultricies felis ornare sit amet. Sed ultrices ex leo, a tincidunt nisl gravida sed. Nullam ornare accumsan porta. Praesent consectetur id est nec sollicitudin.
|
||||
|
||||
In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed sed ultrices nibh. Duis accumsan suscipit eros, a dictum odio tempus sit amet. Aenean imperdiet erat ac lacus finibus, scelerisque cursus massa imperdiet. Mauris molestie risus ut lacinia posuere. Nulla et sodales purus. Maecenas orci erat, placerat in tristique quis, placerat in mi.
|
||||
|
||||
Donec sollicitudin pellentesque odio in feugiat. Morbi eu dolor ut mauris congue sollicitudin. Aliquam erat volutpat. Nulla id varius dui. Curabitur finibus urna ante, consectetur interdum nisi volutpat a. Quisque quis mi tristique, consequat tellus eget, rutrum sapien. Vivamus vitae tellus vulputate, rutrum ex eu, vulputate sem. Suspendisse viverra lorem tellus, vel interdum orci gravida quis. Ut laoreet arcu at mi ullamcorper finibus. Duis porta sagittis vestibulum. Sed commodo nisl vitae urna sollicitudin, nec lacinia est sodales. Curabitur imperdiet sodales dui sed iaculis. Sed ac tellus maximus, eleifend quam sit amet, feugiat elit. Aenean viverra, dui at mattis varius, est odio vestibulum sapien, sit amet mollis libero massa nec velit. Etiam quis sodales justo.
|
||||
|
||||
Ut ultricies, sem eget sodales feugiat, nunc arcu congue elit, ac tempor justo massa nec purus. Maecenas enim nunc, pharetra eget dictum sit amet, tempus pellentesque velit. Suspendisse venenatis ligula in nulla mattis, et imperdiet ex tincidunt. Etiam vulputate, tellus et ultrices suscipit, enim velit laoreet massa, vitae congue odio enim ac urna. Morbi quam lorem, iaculis ac varius sagittis, euismod quis dolor. In ut dui eu purus feugiat consectetur. Vestibulum cursus velit quis lacus pellentesque iaculis. Cras in risus sed mauris porta rutrum. Nulla facilisi. Nullam eu bibendum est, non pellentesque lectus. Sed imperdiet feugiat lorem, quis convallis ante auctor in. Maecenas justo magna, scelerisque sit amet tellus eget, varius elementum risus. Duis placerat et quam sed varius.
|
||||
|
||||
Duis nec nibh vitae nibh dignissim mollis quis sed felis. Curabitur vitae quam placerat, venenatis purus ut, euismod nisl. Curabitur porttitor nibh eu pulvinar ullamcorper. Suspendisse posuere nec ipsum ac dapibus. Cras convallis consectetur urna. Phasellus a nibh in dolor lacinia posuere id eget augue. In eu pharetra lorem, vitae cursus lacus. Aliquam tincidunt nibh lectus. Aenean facilisis ultricies posuere. Sed ut placerat orci. Curabitur scelerisque gravida blandit. Maecenas placerat ligula eget suscipit fringilla. Mauris a tortor justo. Aliquam hendrerit semper mollis. Phasellus et tincidunt libero. Etiam vel quam libero.
|
||||
|
||||
Quisque aliquet tempor ex. Ut ante sem, vehicula at enim vel, gravida porta elit. Etiam vitae lacus a neque lobortis consectetur. Mauris sed interdum odio. Mauris elementum ex blandit tempor cursus. Integer in enim in leo viverra elementum. Fusce consectetur metus et sem rutrum, mattis euismod diam semper. Nunc sed ipsum vel urna consequat vehicula. Donec cursus pretium lorem, vestibulum pretium felis commodo sit amet. Nam blandit felis enim, eget gravida ex faucibus a. In nec neque massa. Etiam laoreet posuere ipsum. Praesent volutpat nunc dolor, ac vulputate magna facilisis non. Aenean congue turpis vel lectus sollicitudin tristique. Sed nec consequat purus, non vehicula quam. Etiam ultricies, est ac dictum tincidunt, turpis turpis pretium massa, a vulputate libero justo at nibh.
|
||||
|
||||
Aliquam erat volutpat. Cras ultrices augue ac sollicitudin lobortis. Curabitur et aliquet purus. Duis feugiat semper facilisis. Phasellus lobortis cursus velit, a sollicitudin tortor. Nam feugiat sapien non dapibus condimentum. Morbi at mi bibendum, commodo quam at, laoreet enim. Integer eu ultrices enim. Sed vestibulum eu urna ut dictum. Curabitur at mattis leo, sed cursus massa. Aliquam porttitor, felis quis fermentum porttitor, justo velit feugiat nulla, eget condimentum sem dui ut sapien.
|
||||
|
||||
In fringilla elit eu orci aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut eget fringilla tellus. Curabitur fermentum, mi et condimentum suscipit, elit neque bibendum dui, et hendrerit nunc metus id ipsum. Morbi placerat mi in hendrerit congue. Ut feugiat mauris eget scelerisque viverra. Vivamus sit amet erat dictum, sagittis lectus nec, pulvinar lorem. Sed non enim ac dui sollicitudin aliquet. Quisque ut lacus dolor. Fusce hendrerit malesuada euismod. Nulla faucibus vel mauris eu mollis. Mauris est diam, fringilla ac arcu feugiat, efficitur volutpat turpis. Aliquam venenatis cursus massa sed porttitor. Ut ac finibus enim, in tincidunt sapien.
|
||||
|
||||
Nunc faucibus semper turpis a lacinia. Phasellus gravida, libero vel pulvinar ornare, ex sem tincidunt lectus, sit amet convallis augue risus at tortor. Quisque sit amet ipsum id nulla posuere vestibulum. Pellentesque scelerisque mauris vel leo viverra sodales. Nulla viverra aliquam ex, ut rutrum enim fermentum venenatis. Aenean eget dapibus ex, eget faucibus metus. Vestibulum volutpat leo in diam semper, eget porta magna suscipit. Sed sit amet nulla blandit, aliquam dolor ac, gravida velit. Sed vel velit viverra, maximus est id, convallis justo.
|
||||
|
||||
Curabitur nulla ante, vulputate at libero vel, ullamcorper rutrum nibh. Pellentesque porttitor eu mauris id mattis. Duis vulputate augue elit, eget interdum justo pretium vel. Maecenas eu vulputate arcu, eget posuere purus. Suspendisse viverra a velit dictum eleifend. Suspendisse vitae dapibus diam. Donec vehicula justo in ante interdum, eu luctus diam placerat. Vivamus convallis ipsum eu orci suscipit, sed fermentum enim euismod. Maecenas faucibus elit vitae ex ornare tristique. Donec vestibulum nec elit sit amet porttitor. Aenean tempor lectus eget tortor hendrerit luctus. Nullam interdum vitae lectus vel feugiat. Cras in risus non magna consectetur lobortis. Sed faucibus enim quis gravida convallis.
|
||||
|
||||
Phasellus eget massa sit amet libero ultrices suscipit. Vivamus at risus sapien. Nam mollis nunc eget velit dictum maximus. Sed pellentesque, nunc ac fringilla lacinia, quam enim mattis ex, sed euismod tortor metus eu neque. Ut mattis nisl ut lectus rhoncus, sodales bibendum eros porta. Nulla porttitor enim nec diam sagittis, eget porta velit efficitur. Vestibulum ultricies eros neque. Phasellus rutrum suscipit enim, in interdum ante gravida vitae. Sed in sagittis diam, non commodo velit.
|
||||
|
||||
Morbi hendrerit odio orci, nec tincidunt odio rhoncus nec. Mauris neque velit, vehicula a lorem at, suscipit tristique dui. Sed finibus, nisl in mattis convallis, turpis neque sodales lacus, eu porta enim magna non diam. Nam commodo sodales risus consectetur malesuada. In eget elementum justo. Phasellus sit amet massa imperdiet, dapibus nunc sit amet, suscipit orci. Fusce condimentum laoreet feugiat. Ut ut viverra ante. Praesent bibendum interdum commodo. Nulla mollis nisi a est ornare volutpat. Sed at ligula eu nisi dapibus tempus. Proin cursus vestibulum justo, nec efficitur justo dignissim vel. Nunc quis maximus eros.
|
||||
|
||||
Cras viverra, diam a tristique mattis, libero felis vulputate tellus, a ornare felis leo a dui. Nulla ante nulla, finibus ut tellus ut, blandit pharetra nibh. Proin eleifend fermentum ex, eget auctor libero vulputate in. Nullam ultricies, mauris placerat pretium placerat, leo urna lobortis leo, vel placerat arcu libero sed mauris. Aliquam mauris ligula, ornare at urna at, eleifend gravida ligula. Vestibulum consectetur ut nulla non scelerisque. Donec ornare, sem nec elementum aliquam, urna nulla bibendum metus, eu euismod dui ligula ac est. Fusce laoreet erat eu ex lobortis, quis bibendum ligula interdum. Sed vel mi erat. Vivamus id lacus ac enim mattis tempor. Nunc ultricies pellentesque enim sed euismod. Fusce tincidunt convallis elit quis aliquam. Mauris nulla ipsum, sollicitudin quis diam ac, feugiat volutpat tellus. In nibh nibh, vulputate quis tincidunt quis, pulvinar eget magna. Pellentesque quis finibus dolor. Suspendisse viverra vitae lectus non eleifend.
|
||||
|
||||
Nunc ut orci et sapien maximus semper. Nulla dignissim sem urna, ac varius lectus ultricies id. Quisque aliquet pulvinar pretium. In ultricies molestie tellus vehicula porta. Nam enim lorem, aliquam eget ex et, hendrerit volutpat quam. Maecenas diam lacus, pellentesque eget tempus ac, pharetra eu elit. Donec vel eros a sem facilisis vulputate. Nullam ac nisi vulputate, laoreet nisl ac, eleifend sem. Nullam mi massa, rhoncus sed pharetra interdum, tincidunt eget nunc. Aliquam viverra mattis posuere. Mauris et dui sed nisl sollicitudin fermentum quis ut arcu. Nam placerat eget orci at tincidunt. Curabitur vel turpis metus. Phasellus nibh nulla, fermentum scelerisque sem vel, gravida tincidunt velit. Pellentesque vel quam tempor, finibus massa pellentesque, condimentum dui.
|
||||
|
||||
Donec at mattis neque. Etiam velit diam, consequat auctor mauris id, hendrerit faucibus metus. Maecenas ullamcorper eros a est sodales, ac consectetur odio scelerisque. Donec leo metus, imperdiet at pellentesque vel, feugiat id erat. Suspendisse at magna enim. Vestibulum placerat sodales lorem id sollicitudin. Aenean at euismod ligula, eget mollis diam. Phasellus pulvinar, orci nec pretium condimentum, est erat facilisis purus, quis feugiat augue elit aliquam nulla. Aenean vitae tortor id risus congue tincidunt. Sed dolor enim, mattis a ullamcorper id, volutpat ac leo.
|
||||
|
||||
Proin vehicula feugiat augue, id feugiat quam sodales quis. Donec et ultricies massa, a lacinia nulla. Duis aliquam augue ornare euismod viverra. Ut lectus risus, rutrum sit amet efficitur a, luctus nec nisl. Cras volutpat ullamcorper congue. Sed vitae odio metus. Phasellus aliquet euismod varius.
|
||||
|
||||
Nullam sem ex, malesuada ut magna ut, pretium mollis arcu. Nam porttitor eros cursus mi lacinia faucibus. Suspendisse aliquet eleifend iaculis. Maecenas sit amet viverra tortor. Nunc a mollis risus. Etiam tempus dolor in tortor malesuada mattis. Ut tincidunt venenatis est sit amet dignissim. Vestibulum massa enim, tristique sed scelerisque eu, fringilla ac velit. Donec efficitur quis urna sit amet malesuada. Vestibulum consequat ac ligula in dapibus. Maecenas massa massa, molestie non posuere nec, elementum ut magna. In nisi erat, mollis non venenatis eu, faucibus in justo. Morbi gravida non ex non egestas. Pellentesque finibus laoreet diam, eu commodo augue congue vitae.
|
||||
|
||||
Aenean sem mi, ullamcorper dapibus lobortis vitae, interdum tincidunt tortor. Vivamus eget vulputate libero. Ut bibendum posuere lectus, vel tincidunt tortor aliquet at. Phasellus malesuada orci et bibendum accumsan. Aliquam quis libero vel leo mollis porta. Sed sagittis leo ac lacus dictum, ac malesuada elit finibus. Suspendisse pharetra luctus commodo. Vivamus ultricies a odio non interdum. Vivamus scelerisque tincidunt turpis quis tempor. Pellentesque tortor ligula, varius non nunc eu, blandit sollicitudin neque. Nunc imperdiet, diam et tristique luctus, ipsum ex condimentum nunc, sit amet aliquam justo velit sed libero. Duis vel suscipit ligula. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed tincidunt neque vel massa ultricies, id dictum leo consequat. Curabitur lobortis ultricies tellus, eget mattis nisl aliquam sit amet.
|
||||
|
||||
Proin at suscipit justo. Vivamus ut vestibulum nisl. Pellentesque enim odio, pharetra non magna sed, efficitur auctor magna. Praesent tincidunt ante quis ante hendrerit viverra. Pellentesque vel ipsum id magna vulputate efficitur. Sed nec neque accumsan, pulvinar sapien quis, euismod mauris. Donec condimentum laoreet sapien quis gravida. Quisque sed mattis purus. Vestibulum placerat vel neque maximus scelerisque.
|
||||
|
||||
Vestibulum mattis quam quis efficitur elementum. Duis dictum dolor ac scelerisque commodo. Fusce sollicitudin nisi sit amet dictum placerat. Suspendisse euismod pharetra eleifend. In eros nisl, porttitor sed mauris at, consectetur aliquet mauris. Donec euismod viverra neque sed fermentum. Phasellus libero magna, accumsan ut ultricies vitae, dignissim eget metus. Donec tellus turpis, interdum eget maximus nec, hendrerit eget massa. Curabitur auctor ligula in iaculis auctor. In ultrices quam suscipit cursus finibus. Aenean id mi at dolor interdum iaculis vitae ut lorem. Nullam sed nibh fringilla, lacinia odio nec, placerat erat. In dui libero, viverra ac viverra ac, pellentesque sit amet turpis.
|
||||
|
||||
Nulla in enim ex. Sed feugiat est et consectetur venenatis. Cras varius facilisis dui vel convallis. Vestibulum et elit eget tellus feugiat pellentesque. In ut ante eu purus aliquet posuere. Nulla nec ornare sem, sed luctus lorem. Nam varius iaculis odio, eget faucibus nisl ullamcorper in. Sed eget cursus felis, nec efficitur nisi.
|
||||
|
||||
Vivamus commodo et sem quis pulvinar. Pellentesque libero ante, venenatis vitae ligula sit amet, ornare sollicitudin nulla. Mauris eget tellus hendrerit, pulvinar metus quis, tempor nisi. Proin magna ex, laoreet sed tortor quis, varius fermentum enim. Integer eu dolor dictum, vulputate tortor et, aliquet ligula. Vestibulum vitae justo id mauris luctus sollicitudin. Suspendisse eget auctor neque, sodales egestas lorem. Vestibulum lacinia egestas metus vitae euismod. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus ex tellus, volutpat nec pulvinar sit amet, condimentum vitae dui. Curabitur vel felis sodales, lacinia nunc iaculis, ullamcorper augue. Pellentesque consequat dolor quis eros efficitur malesuada. Nulla ut malesuada lectus.
|
||||
|
||||
Morbi et tristique ante. Aliquam erat volutpat. Vivamus vitae dui nec turpis pellentesque fermentum. Quisque eget velit massa. Pellentesque tristique aliquam nisl, eu sollicitudin justo venenatis sed. Duis eleifend sem eros, ut aliquam libero porttitor id. Sed non nunc consequat, rhoncus diam eu, commodo erat. Praesent fermentum in lectus id blandit. Donec quis ipsum at justo volutpat finibus. Nulla blandit justo nulla, at mollis lacus consequat eget. Aenean sollicitudin quis eros ut ullamcorper.
|
||||
|
||||
Pellentesque venenatis nulla ut mi aliquet feugiat. Cras semper vel magna nec pharetra. Integer mattis felis et sapien commodo imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Duis quis luctus felis. Vestibulum justo nibh, aliquam non lectus vitae, molestie placerat justo. Donec lorem nibh, gravida sit amet hendrerit ac, maximus id ipsum. Nunc ac libero sodales risus eleifend sagittis. Phasellus est massa, lobortis elementum ex sed, scelerisque consectetur neque. Nunc faucibus neque id lorem malesuada, eget convallis ex mattis.
|
||||
|
||||
Sed turpis tortor, fermentum non turpis id, posuere varius nibh. Donec iaculis lorem dui. Etiam eros ante, sodales eget venenatis at, consectetur eget risus. Curabitur non aliquam ante, a pretium justo. Maecenas tempor nisl tortor, vitae dictum nisi ultrices eu. Duis eget dui ultrices, porttitor lacus sed, lobortis purus. Quisque mattis elit nec neque sagittis, sed commodo leo blandit. Mauris sodales interdum eleifend. Vestibulum condimentum consectetur augue, id luctus diam convallis et.
|
||||
|
||||
Nunc suscipit risus in justo accumsan, a placerat magna tincidunt. Proin a nisl ipsum. Sed libero dui, tristique in augue quis, auctor tristique risus. Sed porttitor ex augue, eu porta augue molestie a. Duis rhoncus purus libero, eu tempus turpis condimentum at. Sed mollis nisi id lectus placerat tincidunt. Maecenas non scelerisque elit, quis rutrum orci. Donec in tellus pharetra urna ornare lobortis. Phasellus id risus at nisi varius rutrum eu ut turpis.
|
||||
|
||||
Duis dictum justo quis nisl porta, eget tincidunt magna suscipit. Sed velit massa, ullamcorper eu sodales ac, pretium a massa. Duis et rutrum tortor. Nulla accumsan hendrerit sapien, cursus volutpat eros egestas eget. Donec sollicitudin at ante quis sollicitudin. Aenean blandit feugiat diam, id feugiat eros faucibus eget. Donec viverra dolor vel justo scelerisque dignissim. Nulla semper sem nunc, rhoncus semper tellus ultricies sed. Duis in ornare diam. Donec vehicula feugiat varius. Maecenas ut suscipit est. Vivamus sem sem, finibus at dolor sit amet, euismod dapibus ligula. Vestibulum fringilla odio dapibus, congue massa eget, congue sem. Donec feugiat magna eget tortor lacinia scelerisque non et ipsum.
|
||||
|
||||
Suspendisse potenti. Nunc convallis sollicitudin ex eget venenatis. Sed iaculis nibh ex, vel ornare ligula congue dignissim. Quisque sollicitudin dolor ac dui vestibulum, sit amet molestie nisi aliquet. Donec at risus felis. Aenean sollicitudin metus a feugiat porta. Aenean a tortor ut dolor cursus sagittis. Vivamus consectetur porttitor nunc in facilisis. Proin sit amet mi vel lectus consectetur ultrices.
|
||||
|
||||
Sed cursus lectus vitae nunc tristique, nec commodo turpis dapibus. Pellentesque luctus ex id facilisis ornare. Morbi quis placerat dolor. Donec in lectus in arcu mattis porttitor ac sit amet metus. Cras congue mauris non risus sodales, vitae feugiat ipsum bibendum. Nulla venenatis urna sed libero elementum, a cursus lorem commodo. Mauris faucibus lobortis eros nec commodo.
|
||||
|
||||
Nullam suscipit ligula ullamcorper lorem commodo blandit. Nulla porta nibh quis pulvinar placerat. Vivamus eu arcu justo. Vestibulum imperdiet est ut fermentum porttitor. Pellentesque consectetur libero in sapien efficitur scelerisque. Curabitur ac erat sit amet odio aliquet dignissim. Pellentesque mi sem, rhoncus et luctus at, porttitor rutrum lectus. Vestibulum sollicitudin sollicitudin suscipit. Aenean efficitur dolor non ultrices imperdiet. Donec vel sem ex.
|
||||
|
||||
Sed convallis mauris aliquam rutrum cursus. Ut tempor porttitor sodales. Etiam eu risus ac augue gravida egestas et eu dolor. Proin id magna ex. Suspendisse quis lectus quis lorem ultricies tempus. Donec porttitor velit vitae tincidunt faucibus. Aliquam vitae semper nisi. Morbi ultrices, leo non pretium dapibus, dui libero pellentesque ex, vel placerat enim ante vitae dui. Nunc varius, sem sit amet sagittis lobortis, lectus odio scelerisque mauris, ut vestibulum orci magna quis neque. Sed id congue justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris congue nisi est, malesuada mollis elit tincidunt sed. Curabitur sed ex sit amet felis tristique elementum vitae vel nibh.
|
||||
|
||||
Etiam mollis pretium lobortis. Mauris augue lacus, efficitur at lacus sed, mollis tincidunt lectus. Aliquam erat volutpat. Donec at euismod elit, et mattis felis. Sed id lobortis urna. Morbi imperdiet vestibulum leo, sed maximus leo blandit eu. Aliquam semper lorem neque, nec euismod turpis mattis mollis. Quisque lobortis urna ultrices odio pretium, ac venenatis orci faucibus. Suspendisse bibendum odio ligula, sed lobortis massa pharetra nec. Donec turpis justo, iaculis at dictum ac, finibus eu libero. Maecenas quis porttitor mi, sit amet aliquet neque.
|
||||
|
||||
Vivamus auctor vulputate ante, at egestas lorem. Donec eu risus in nulla mollis ultricies at et urna. Duis accumsan porta egestas. Ut vel euismod augue. Fusce convallis nulla ante, nec fringilla velit aliquet at. Nam malesuada dapibus ligula, a aliquam nibh scelerisque ac. Praesent malesuada neque et pellentesque interdum. Curabitur volutpat at turpis vitae tristique. Vivamus porttitor semper congue. Quisque suscipit lacus mi, rhoncus ultrices tortor auctor quis. Maecenas neque neque, molestie ac facilisis eget, luctus ac lorem. In ut odio ut lacus suscipit pulvinar vitae sed elit. Nulla imperdiet, sem quis euismod sagittis, dui erat luctus dolor, faucibus faucibus erat sem eget nunc. Nam accumsan placerat malesuada. Maecenas convallis finibus pulvinar.
|
||||
|
||||
Cras at placerat tortor. Morbi facilisis auctor felis sit amet molestie. Donec sodales sed lorem vitae suscipit. Etiam fermentum pharetra ipsum, nec luctus orci gravida eu. Pellentesque gravida, est non condimentum tempus, mauris ligula molestie est, in congue dolor nisl vel sapien. Duis congue tempor augue, id rutrum eros porta dapibus. Etiam rutrum eget est eget vestibulum. Aenean mollis arcu vel consequat varius. Praesent at condimentum felis. Duis nec interdum nisl. Donec commodo lorem sed sapien scelerisque malesuada non eu urna. In blandit non ipsum at porta. Nam lobortis leo vitae dui auctor, non feugiat quam bibendum. Donec auctor lectus sagittis laoreet maximus. Maecenas rhoncus laoreet porttitor. Vestibulum porttitor augue ut lectus hendrerit, eget posuere mi gravida.
|
||||
|
||||
Sed mattis ex in erat pulvinar, eu imperdiet magna dapibus. Etiam nisi nibh, tempus non tellus sit amet, mattis tempor odio. Quisque nec lorem feugiat, lobortis odio et, commodo nunc. Maecenas semper purus nisi, nec vehicula nibh eleifend vitae. Nulla fermentum a lectus at maximus. Phasellus finibus metus non euismod ultrices. Etiam a pulvinar ante. Quisque convallis nec metus sit amet facilisis. Praesent laoreet massa et sollicitudin laoreet. Vestibulum in mauris aliquet, convallis mi ut, elementum purus. Nulla purus nulla, sodales at hendrerit quis, tempus sed lectus.
|
||||
|
||||
Nam ut laoreet neque, ut maximus nibh. Maecenas quis justo pellentesque, sollicitudin elit at, venenatis velit. Aenean nunc velit, vehicula scelerisque odio at, consectetur laoreet purus. Duis dui purus, malesuada quis ipsum sit amet, tempor interdum libero. Curabitur porta scelerisque sapien, vitae cursus diam condimentum eu. Phasellus sed orci quam. Nullam vitae dui quis purus tincidunt vestibulum. Curabitur quis nulla porta, cursus arcu non, auctor enim. Etiam sollicitudin ex id sem vehicula mollis. Morbi viverra laoreet tincidunt. Praesent ut semper dui. Nam sit amet pretium neque. Mauris vitae luctus diam, in lacinia purus. Maecenas ut placerat justo, ut porta felis. Integer eu mauris ante.
|
||||
|
||||
Aenean porttitor tellus diam, tempor consequat metus efficitur id. Suspendisse ut felis at erat tempor dictum at nec sapien. Sed vestibulum interdum felis, ac mattis mauris porta in. Nunc et condimentum massa. Sed cursus dictum justo et luctus. Integer convallis enim nisl, a rutrum lectus ultricies in. Donec dapibus lacus at nulla dapibus, id sollicitudin velit hendrerit. Fusce a magna at orci mollis rutrum ac a dolor. Aliquam erat volutpat. Morbi varius porta nunc, sit amet sodales ex hendrerit commodo. Donec tincidunt tortor sapien, vitae egestas sapien vehicula eget.
|
||||
|
||||
Suspendisse potenti. Donec pulvinar felis nec leo malesuada interdum. Integer posuere placerat maximus. Donec nibh ipsum, tincidunt vitae luctus vitae, bibendum at leo. Sed cursus nisl ut ex faucibus aliquet sed nec eros. Curabitur molestie posuere felis. Integer faucibus velit eget consequat iaculis. Mauris sed vulputate odio. Phasellus maximus, elit a pharetra egestas, lorem magna semper tellus, vestibulum semper diam felis at sapien. Suspendisse facilisis, nisl sit amet euismod vehicula, libero nulla vehicula dolor, quis fermentum nibh elit sit amet diam.
|
||||
|
||||
Morbi lorem enim, euismod eu varius ut, scelerisque quis odio. Nam tempus vitae eros id molestie. Nunc pretium in nulla eget accumsan. Quisque mattis est ut semper aliquet. Maecenas eget diam elementum, fermentum ipsum a, euismod sapien. Duis quam ligula, cursus et velit nec, ullamcorper tincidunt magna. Donec vulputate nisl est, et ullamcorper urna tempor sit amet.
|
||||
|
||||
Proin lacinia dui non turpis congue pretium. Morbi posuere metus vel purus imperdiet interdum. Morbi venenatis vel eros non ultricies. Nulla vel semper elit. Ut quis purus tincidunt, auctor justo ut, faucibus turpis. Proin quis mattis erat, at faucibus ligula. Mauris in mauris enim. Donec facilisis enim at est feugiat hendrerit. Nam vel nisi lorem. Fusce ultricies convallis diam, in feugiat tortor luctus quis. Donec tempor, leo vitae volutpat aliquam, magna elit feugiat leo, quis placerat sapien felis eget arcu. Donec ornare fermentum eleifend. Integer a est orci.
|
||||
|
||||
Proin rhoncus egestas leo. Nulla ultricies porta elit quis ornare. Nunc fermentum interdum vehicula. In in ligula lorem. Donec nec arcu sit amet orci lobortis iaculis. Mauris at mollis erat, sit amet mollis tortor. Mauris laoreet justo ullamcorper porttitor auctor. Aenean sit amet aliquam lectus, id fermentum eros. Praesent urna sem, vehicula ac fermentum id, dapibus ut purus. Vestibulum vitae tempus nunc. Donec at nunc ornare metus volutpat porta at eget magna. Donec varius aliquet metus, eu lobortis risus aliquam sed. Ut dapibus fermentum velit, ac tincidunt libero faucibus at.
|
||||
|
||||
In in purus auctor, feugiat massa quis, facilisis nisi. Donec dolor purus, gravida eget dolor ac, porttitor imperdiet urna. Donec faucibus placerat erat, a sagittis ante finibus ac. Sed venenatis dignissim elit, in iaculis felis posuere faucibus. Praesent sed viverra dolor. Mauris sed nulla consectetur nunc laoreet molestie in ut metus. Proin ac ex sit amet magna vulputate hendrerit ac condimentum urna. Proin ligula metus, gravida et sollicitudin facilisis, iaculis ut odio. Cras tincidunt urna et augue varius, ut facilisis urna consequat. Aenean vehicula finibus quam. Ut iaculis eu diam ac mollis. Nam mi lorem, tristique eget varius at, sodales at urna.
|
||||
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin vitae dictum erat, et auctor ipsum. Nullam nunc nunc, sollicitudin quis magna a, vestibulum fermentum mauris. Praesent at erat dolor. Proin laoreet tristique nulla vel efficitur. Nam sed ultrices nibh, id rutrum nunc. Curabitur eleifend a erat sit amet sollicitudin. Nullam metus quam, laoreet vitae dapibus id, placerat sed leo. Aliquam erat volutpat. Donec turpis nisl, cursus eu ex sit amet, lacinia pellentesque nisl. Sed id ipsum massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec interdum scelerisque lorem eu mattis.
|
||||
|
||||
Vivamus ac tristique massa, nec facilisis nisl. Nam ipsum neque, tincidunt vel urna in, cursus imperdiet enim. Nam pellentesque egestas tempus. Morbi facilisis imperdiet libero vitae fringilla. Nam lacinia ligula at sapien facilisis malesuada. Nullam accumsan pulvinar sem, et cursus libero porta sit amet. Curabitur vulputate erat elit, ut pulvinar erat maximus vel.
|
||||
|
||||
Cras aliquet metus ut purus sagittis, vel venenatis ante consectetur. Pellentesque nulla lacus, viverra viverra mattis non, placerat vitae nibh. Donec enim turpis, accumsan sit amet tincidunt eu, imperdiet non metus. Morbi ipsum eros, tincidunt vel est ac, tristique porttitor nibh. Praesent ut ullamcorper mauris. Sed laoreet sit amet diam congue venenatis. Integer porta purus nec orci sagittis posuere.
|
||||
|
||||
Donec vehicula mauris eget lacus mollis venenatis et sed nibh. Nam sodales ligula ipsum, scelerisque lacinia ligula sagittis in. Nam sit amet ipsum at erat malesuada congue. Aenean ut sollicitudin sapien. Etiam at tempor odio. Mauris vitae purus ut magna suscipit consequat. Vivamus quis sapien neque. Nulla vulputate sem sit amet massa pellentesque, eleifend tristique ligula egestas. Suspendisse tincidunt gravida mi, in pulvinar lectus egestas non. Aenean imperdiet ex sit amet nunc sollicitudin porta. Integer justo odio, ultricies at interdum in, rhoncus vitae sem. Sed porttitor arcu quis purus aliquet hendrerit. Praesent tempor tortor at dolor dictum pulvinar. Nulla aliquet nunc non ligula scelerisque accumsan. Donec nulla justo, congue vitae massa in, faucibus hendrerit magna. Donec non egestas purus.
|
||||
|
||||
Vivamus iaculis, lacus efficitur faucibus porta, dui nulla facilisis ligula, ut sodales odio nunc id sapien. Cras viverra auctor ipsum, dapibus mattis neque dictum sed. Sed convallis fermentum molestie. Nulla facilisi turpis duis.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vulputate, ipsum quis interdum fermentum, lorem sem fermentum eros, vitae auctor neque lacus in nisi. Suspendisse potenti. Maecenas et scelerisque elit, in tincidunt quam. Sed eu tincidunt quam. Nullam justo ex, imperdiet a imperdiet et, fermentum sit amet eros. Aenean quis tempus sem. Pellentesque accumsan magna mi, ut mollis velit sagittis id. Etiam quis ipsum orci. Fusce purus ante, accumsan a lobortis at, venenatis eu nisl. Praesent ornare sed ante placerat accumsan. Suspendisse tempus dignissim fermentum. Nunc a leo ac lacus sodales iaculis eu vitae mi. In feugiat ante at massa finibus cursus. Suspendisse posuere fringilla ornare. Mauris elementum ac quam id convallis. Vestibulum non elit quis urna volutpat aliquam a eu lacus.
|
||||
|
||||
Aliquam vestibulum imperdiet neque, suscipit aliquam elit ultrices bibendum. Suspendisse ultrices pulvinar cursus. Morbi risus nisi, cursus consequat rutrum vitae, molestie sed dui. Fusce posuere, augue quis dignissim aliquam, nisi ipsum porttitor ante, quis fringilla nisl turpis ac nisi. Nulla varius enim eget lorem vehicula gravida. Donec finibus malesuada leo nec semper. Proin ac enim eros. Vivamus non tincidunt nisi, vel tristique lorem.
|
||||
|
||||
Nunc consequat ex id eros dignissim, id rutrum risus laoreet. Sed euismod non erat eu ultricies. Etiam vehicula gravida lacus ut porta. Vestibulum eu eros quis nunc aliquet luctus. Cras quis semper ligula. Nullam gravida vehicula quam sed porta. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In porta cursus vulputate. Quisque porta a nisi eget cursus. Aliquam risus leo, luctus ac magna in, efficitur cursus magna. In condimentum non mi id semper. Donec interdum ante eget commodo maximus.
|
||||
|
||||
Vivamus sit amet vestibulum lectus. Fusce tincidunt mi sapien, dictum sollicitudin diam vulputate in. Integer fringilla consequat mollis. Cras aliquet consequat felis eget feugiat. Nunc tempor cursus arcu, vitae ornare nunc varius et. Vestibulum et tortor vel ante viverra porttitor. Nam at tortor ullamcorper, facilisis augue quis, tristique erat. Aenean ut euismod nibh. Quisque eu tincidunt est, nec euismod eros.
|
||||
|
||||
Proin vehicula nibh non viverra egestas. Phasellus sem dolor, ultricies ac sagittis tristique, lacinia a purus. Vestibulum in ante eros. Pellentesque lacus nulla, tristique vitae interdum vel, malesuada ac diam. Aenean bibendum posuere turpis in accumsan. Ut est nulla, ullamcorper quis turpis at, viverra sagittis mauris. Sed in interdum purus. Praesent scelerisque nibh eget sem euismod, ut imperdiet mi venenatis. Vivamus pulvinar orci sed dapibus auctor. Nulla facilisi. Vestibulum tincidunt erat nec porttitor egestas. Mauris quis risus ante. Nulla facilisi.
|
||||
|
||||
Aliquam ullamcorper ornare lobortis. Phasellus quis sem et ipsum mollis malesuada sed in ex. Ut aliquam ex eget metus finibus maximus. Proin suscipit mauris eu nibh lacinia, quis feugiat dui dapibus. Nam sed libero est. Aenean vulputate orci sit amet diam faucibus, eu sagittis sapien volutpat. Nam imperdiet felis turpis, at pretium odio pulvinar in. Sed vestibulum id eros nec ultricies. Sed quis aliquam tortor, vitae ullamcorper tellus. Donec egestas laoreet eros, id suscipit est rutrum nec. Sed auctor nulla eget metus aliquam, ut condimentum enim elementum.
|
||||
|
||||
Aliquam suscipit non turpis sit amet bibendum. Fusce velit ligula, euismod et maximus at, luctus sed neque. Quisque pretium, nisl at ullamcorper finibus, lectus leo mattis sapien, vel euismod mauris diam ullamcorper ex. Nulla ut risus finibus, lacinia ligula at, auctor erat. Mauris consectetur sagittis ligula vel dapibus. Nullam libero libero, lobortis aliquam libero vel, venenatis ultricies leo. Duis porttitor, nibh congue fermentum posuere, erat libero pulvinar tortor, a pellentesque nunc ipsum vel sem. Nullam volutpat, eros sit amet facilisis consectetur, ipsum est vehicula massa, non vestibulum neque elit in mauris. Nunc hendrerit ipsum non enim bibendum, vitae rhoncus mi egestas. Etiam ullamcorper massa vel nisl sagittis, nec bibendum arcu malesuada. Aenean aliquet turpis justo, a consectetur arcu mollis convallis. Etiam tellus ipsum, ultricies vitae lorem et, ornare facilisis orci. Praesent fringilla justo urna, vel mollis neque pulvinar vestibulum.
|
||||
|
||||
Donec non iaculis erat. Aliquam et mi sed nunc pulvinar ultricies in ut ipsum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent feugiat lacus ac dignissim semper. Phasellus vitae quam nisi. Morbi vel diam ultricies risus lobortis ornare. Fusce maximus et ligula quis iaculis. Sed congue ex eget felis convallis, sit amet hendrerit elit tempor. Donec vehicula blandit ante eget commodo. Vestibulum eleifend diam at feugiat euismod. Etiam magna tellus, dignissim eget fermentum vel, vestibulum vitae mauris. Nam accumsan et erat id sagittis. Donec lacinia, odio ut ornare ultricies, dolor velit accumsan tortor, non finibus erat tellus quis ligula. Nunc quis metus in leo volutpat ornare vulputate eu nisl.
|
||||
|
||||
Donec quis viverra ex. Nullam id feugiat mauris, eu fringilla nulla. Vestibulum id maximus elit. Cras elementum elit sed felis lobortis, eget sagittis nisi hendrerit. Vivamus vitae elit neque. Donec vulputate lacus ut libero ultrices accumsan. Vivamus accumsan nulla orci, in dignissim est laoreet sagittis. Proin at commodo velit. Curabitur in velit felis. Aliquam erat volutpat. Sed consequat, nulla et cursus sodales, nisi lacus mattis risus, quis eleifend erat ex nec turpis. Sed suscipit ultrices lorem in hendrerit.
|
||||
|
||||
Morbi vitae lacus nec libero ornare tempus eu et diam. Suspendisse magna ipsum, fermentum vel odio quis, molestie aliquam urna. Fusce mollis turpis a eros accumsan porttitor. Pellentesque rhoncus dolor sit amet magna rutrum, et dapibus justo tempor. Sed purus nisi, maximus vitae fringilla eu, molestie nec urna. Fusce malesuada finibus pretium. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec sed aliquet eros. Pellentesque luctus diam ante, eget euismod nisl aliquet eu. Sed accumsan elit purus, tempor varius ligula tempus nec. Curabitur ornare leo suscipit suscipit fermentum. Morbi eget nulla est. Maecenas faucibus interdum tristique.
|
||||
|
||||
Etiam ut elit eros. Nulla pharetra suscipit molestie. Nulla facilisis bibendum nisl non molestie. Curabitur turpis lectus, facilisis vel diam non, vulputate ultrices mauris. Aenean placerat aliquam convallis. Suspendisse sed scelerisque tellus. Vivamus lacinia neque eget risus cursus suscipit. Proin consequat dolor vel neque tempor, eu aliquam sem scelerisque. Duis non eros a purus malesuada pharetra non et nulla. Suspendisse potenti. Mauris libero eros, finibus vel nulla id, sagittis dapibus ante. Proin iaculis sed nunc et cursus.
|
||||
|
||||
Quisque accumsan lorem sit amet lorem aliquet euismod. Curabitur fermentum rutrum posuere. Etiam ultricies, sem id pellentesque suscipit, urna magna lacinia eros, quis efficitur risus nisl at lacus. Nulla quis lacus tortor. Mauris placerat ex in dolor tincidunt, vel aliquet nisi pretium. Cras iaculis risus vitae pellentesque aliquet. Quisque a enim imperdiet, ullamcorper arcu vitae, rutrum risus. Nullam consectetur libero at felis fringilla, nec congue nibh dignissim. Nam et lobortis felis, eu pellentesque ligula. Aenean facilisis, ligula non imperdiet maximus, massa orci gravida sapien, at sagittis lacus nisl in lacus. Nulla quis mauris luctus, scelerisque felis consequat, tempus risus. Fusce auctor nisl non nulla luctus molestie. Maecenas sapien nisl, auctor non dolor et, iaculis scelerisque lorem. Suspendisse egestas enim aliquet, accumsan mauris nec, posuere quam. Nulla iaculis dui dui, sit amet vestibulum erat ultricies ac.
|
||||
|
||||
Cras eget dolor erat. Proin at nisl ut leo consectetur ultricies vel ut arcu. Nulla in felis malesuada, ullamcorper tortor et, convallis massa. Nunc urna justo, ornare in nibh vitae, hendrerit condimentum libero. Etiam vitae libero in purus venenatis fringilla. Nullam velit nulla, consequat ut turpis non, egestas hendrerit nibh. Duis tortor turpis, interdum non ante ac, cursus accumsan lectus. Cras pharetra bibendum augue quis dictum. Sed euismod vestibulum justo. Proin porta lobortis purus. Duis venenatis diam tortor, sit amet condimentum eros rhoncus a. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc at magna nec diam lobortis efficitur sit amet ut lacus. Nulla quis orci tortor. Pellentesque tempus velit a odio finibus porta.
|
||||
|
||||
Proin feugiat mauris a tellus scelerisque convallis. Maecenas libero magna, blandit nec ultrices id, congue vel mi. Aliquam lacinia, quam vel condimentum convallis, tortor turpis aliquam odio, sed blandit libero lacus et eros. In eleifend iaculis magna ac finibus. Praesent auctor facilisis tellus in congue. Sed molestie lobortis dictum. Nam quis dignissim augue, vel euismod lorem. Curabitur posuere dapibus luctus. Donec ultricies dictum lectus, quis blandit arcu commodo ac. Aenean tincidunt ligula in nunc imperdiet dignissim. Curabitur egestas sollicitudin sapien ut semper. Aenean nec dignissim lacus.
|
||||
|
||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec aliquam dictum vehicula. Donec tortor est, volutpat non nisi nec, varius gravida ex. Nunc vel tristique nunc, vitae mattis nisi. Nunc nec luctus ex, vitae tincidunt lectus. In hac habitasse platea dictumst. Curabitur lobortis ex eget tincidunt tempor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut a vehicula mi.
|
||||
|
||||
Fusce eu libero finibus, interdum nulla a, placerat neque. Cras bibendum tempor libero nec feugiat. Cras ut sodales eros. Proin viverra, massa sit amet viverra egestas, neque nisl porta ex, sit amet hendrerit libero ligula vel urna. Mauris suscipit lacus id justo rhoncus suscipit. Etiam vel libero tellus. Maecenas non diam molestie, condimentum tellus a, bibendum enim. Mauris aliquet imperdiet tellus, eget sagittis dolor. Sed blandit in neque et luctus. Cras elementum sagittis nunc, vel mollis lorem euismod et. Donec posuere at lacus eget suscipit.
|
||||
|
||||
Nulla nunc mi, pretium non massa vel, tempor semper magna. Nunc a leo pulvinar, tincidunt nunc at, dignissim mi. Aliquam erat volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut viverra nulla a nisl finibus, at hendrerit ligula ullamcorper. Donec a lorem semper, tempor magna et, lobortis libero. Mauris id sapien leo. Donec dignissim, quam vitae porttitor dignissim, quam justo mattis dui, vel consequat odio elit quis orci. Etiam nec pretium neque, sit amet pretium orci. Duis ac tortor venenatis, feugiat purus non, feugiat nunc. Proin scelerisque nisl in turpis aliquam vulputate.
|
||||
|
||||
Praesent sed est semper, fringilla lorem vitae, tincidunt nibh. Cras eros metus, auctor at mauris sit amet, sodales semper orci. Nunc a ornare ex. Curabitur bibendum arcu congue urna vulputate egestas. Vestibulum finibus id risus et accumsan. Aenean ut volutpat tellus. Aenean tincidunt malesuada urna sit amet vestibulum. Mauris vel tellus dictum, varius lacus quis, dictum arcu.
|
||||
|
||||
Aenean quis metus eu erat feugiat cursus vel at ligula. Proin dapibus sodales urna, id euismod lectus tempus id. Pellentesque ex ligula, convallis et erat vel, vulputate condimentum nisl. Pellentesque pharetra nulla quis massa eleifend hendrerit. Praesent sed massa ipsum. Maecenas vehicula dolor massa, id sodales urna faucibus et. Mauris ac quam non massa tincidunt feugiat et at lacus. Fusce libero massa, vulputate vel scelerisque non, mollis in leo. Ut sit amet ultricies odio. Suspendisse in sapien viverra, facilisis purus ut, pretium libero.
|
||||
|
||||
Vivamus tristique pharetra molestie. Nam a volutpat purus. Praesent consequat gravida nisi, ac blandit nisi suscipit ut. Quisque posuere, ligula a ultrices laoreet, ligula nunc vulputate libero, ut rutrum erat odio tincidunt justo. Sed vitae leo at leo fringilla bibendum. Vestibulum ut augue nec dolor auctor accumsan. Praesent laoreet id eros pulvinar commodo. Suspendisse potenti. Ut pharetra, mauris vitae blandit fringilla, odio ante tincidunt lorem, sit amet tempor metus diam ut turpis.
|
||||
|
||||
Praesent quis egestas arcu. Nullam at porta arcu. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi vulputate ligula malesuada ligula luctus, vulputate tempus erat bibendum. Nunc ullamcorper non lectus at euismod. Etiam nibh felis, tincidunt a metus vel, pellentesque rhoncus neque. Etiam at diam in erat luctus interdum. Nunc vel ipsum pulvinar, sollicitudin lacus ac, tempus urna. Etiam vel lacinia sapien. Pellentesque sagittis velit vel mi efficitur iaculis. Integer euismod sit amet urna in sagittis. Cras eleifend ut nibh in facilisis. Donec et lacus vitae nunc placerat sodales. Nulla sed hendrerit ligula, at dapibus sapien.
|
||||
|
||||
Praesent at iaculis ex. Curabitur est purus, cursus a faucibus quis, dictum id velit. Donec dignissim fringilla viverra. Nunc mauris felis, laoreet sit amet sagittis at, vestibulum in libero. Maecenas quis orci turpis. Quisque ut nibh vitae magna mollis consequat id at mauris. Aliquam eu odio eget nulla bibendum sodales. Quisque vel orci eleifend nisi pretium lacinia. Suspendisse eget risus eget mi volutpat molestie eget quis lacus. Duis nisi libero, tincidunt nec nulla id, faucibus cursus felis.
|
||||
|
||||
Donec tempor eget risus pellentesque molestie. Phasellus porta neque vel arcu egestas, nec blandit velit fringilla. Nullam porta faucibus justo vitae laoreet. Pellentesque viverra id nunc eu varius. Nulla pulvinar lobortis iaculis. Etiam vestibulum odio nec velit tristique, a tristique nisi mattis. In sed fringilla orci, vitae efficitur odio. Quisque dui odio, ornare eget velit at, lacinia consequat libero. Quisque lectus nulla, aliquet eu leo in, porta rutrum diam. Donec nec mattis neque. Nam rutrum, odio ac eleifend bibendum, dolor arcu rutrum neque, eget porta elit tellus a lacus. Sed massa metus, sollicitudin et sapien eu, finibus tempus orci. Proin et sapien sit amet erat molestie interdum. In quis rutrum velit, faucibus ultrices tellus.
|
||||
|
||||
Sed sagittis sed justo eget tincidunt. Maecenas ut leo sagittis, feugiat magna et, viverra velit. Maecenas ex arcu, feugiat at consequat vitae, auctor eu massa. Integer egestas, enim vitae maximus convallis, est lectus pretium mauris, ac posuere lectus nisl quis quam. Aliquam tempus laoreet mi, vitae dapibus dolor varius dapibus. Suspendisse potenti. Donec sit amet purus nec libero dapibus tristique. Pellentesque viverra bibendum ligula. Donec sed felis et ex lobortis laoreet. Phasellus a fringilla libero, vitae malesuada nulla. Pellentesque blandit mattis lacus, et blandit tortor laoreet consequat. Suspendisse libero nunc, viverra sed fermentum in, accumsan egestas arcu. Proin in placerat elit. Sed interdum imperdiet malesuada. Suspendisse aliquet quis mauris eget sollicitudin.
|
||||
|
||||
Vivamus accumsan tellus non erat volutpat, quis dictum dolor feugiat. Praesent rutrum nunc ac est mollis cursus. Fusce semper volutpat dui ut egestas. Curabitur sit amet posuere massa. Cras tincidunt nulla et mi mollis imperdiet. Suspendisse scelerisque ex id sodales vulputate. In nunc augue, pharetra in placerat eu, mattis id tellus. Vivamus cursus efficitur vehicula. Nulla aliquet vehicula aliquet.
|
||||
|
||||
Sed cursus tellus sed porta pulvinar. Sed vitae nisi neque. Nullam aliquet, lorem et efficitur scelerisque, arcu diam aliquam felis, sed pulvinar lorem odio et turpis. Praesent convallis pulvinar turpis eu iaculis. Aliquam nec gravida mi. Curabitur eu nibh tempor, blandit justo in, ultrices felis. Fusce placerat metus non mi sagittis rutrum. Morbi sed dui fringilla, sagittis mauris eget, imperdiet nunc. Phasellus hendrerit sem elit, id hendrerit libero auctor sit amet. Integer sodales elit sit amet consequat cursus.
|
||||
|
||||
Nam semper est eget nunc mollis, in pellentesque lectus fringilla. In finibus vel diam id semper. Nunc mattis quis erat eu consectetur. In hac habitasse platea dictumst. Nullam et ipsum vestibulum ex pulvinar ultricies sit amet id velit. Aenean suscipit mi tortor, a lobortis magna viverra non. Nulla condimentum aliquet ante et ullamcorper. Pellentesque porttitor arcu a posuere tempus. Aenean lacus quam, imperdiet eu justo vitae, pretium efficitur ex. Duis id purus id magna rhoncus ultrices id eu risus. Nunc dignissim et libero id dictum.
|
||||
|
||||
Quisque a tincidunt neque. Phasellus commodo mi sit amet tempor fringilla. Ut rhoncus, neque non porttitor elementum, libero nulla egestas augue, sed fringilla sapien felis ac velit. Phasellus viverra rhoncus mollis. Nam ullamcorper leo vel erat laoreet luctus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus semper a metus a cursus. Nulla sed orci egestas, efficitur purus ac, malesuada tellus. Aenean rutrum velit at tellus fermentum mollis. Aliquam eleifend euismod metus.
|
||||
|
||||
In hac habitasse platea dictumst. Vestibulum volutpat neque vitae porttitor laoreet. Nam at tellus consequat, sodales quam in, pulvinar arcu. Maecenas varius convallis diam, ac lobortis tellus pellentesque quis. Maecenas eget augue massa. Nullam volutpat nibh ac justo rhoncus, ut iaculis tellus rutrum. Fusce efficitur efficitur libero quis condimentum. Curabitur congue neque non tincidunt tristique. Fusce eget tempor ex, at pellentesque odio. Praesent luctus dictum vestibulum. Etiam non orci nunc. Vivamus vitae laoreet purus, a lobortis velit. Curabitur tincidunt purus ac lectus elementum pellentesque. Quisque sed tincidunt est.
|
||||
|
||||
Sed vel ultrices massa, vitae ultricies justo. Cras finibus mauris nec lacus tempus dignissim. Cras faucibus maximus velit, eget faucibus orci luctus vehicula. Nulla massa nunc, porta ac consequat eget, rhoncus non tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce sed maximus metus, vel imperdiet ipsum. Ut scelerisque lectus at blandit porttitor. Ut vulputate nunc pharetra, aliquet sapien ac, sollicitudin sapien. Aenean eget ante lorem. Nam accumsan venenatis tellus id dignissim.
|
||||
|
||||
Curabitur fringilla, magna non maximus dapibus, nulla sapien vestibulum lectus, sit amet semper dolor neque vitae nisl. Nunc ultrices vehicula augue sed iaculis. Maecenas nec diam mollis, suscipit orci et, vestibulum ante. Pellentesque eu nisl tortor. Nunc eleifend, lacus quis volutpat volutpat, nisi mi molestie sem, quis mollis ipsum libero a tellus. Ut viverra dolor mattis convallis interdum. Sed tempus nisl at nunc scelerisque aliquet. Quisque tempor tempor lorem id feugiat. Nullam blandit lectus velit, vitae porta lacus tincidunt a. Vivamus sit amet arcu ultrices, tincidunt mi quis, viverra quam. Aenean fringilla libero elementum lorem semper, quis pulvinar eros gravida. Nullam sodales blandit mauris, sed fermentum velit fermentum sit amet. Donec malesuada mauris in augue sodales vulputate. Vestibulum gravida turpis id elit rhoncus dignissim. Integer non congue lorem, eu viverra orci.
|
||||
|
||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec at dolor magna. Aliquam consectetur erat augue, id iaculis velit pharetra ac. Integer rutrum venenatis dignissim. Integer non sodales elit. Curabitur ut magna ut nibh feugiat aliquam ac ut risus. Morbi nibh quam, aliquam id placerat nec, vestibulum eget velit. Suspendisse at dignissim quam. Vivamus aliquet sem sed nisl volutpat, ut cursus orci ultrices. Aliquam ultrices lacinia enim, vitae aliquet neque.
|
||||
|
||||
Quisque scelerisque finibus diam in mattis. Cras cursus auctor velit. Aliquam sem leo, fermentum et maximus et, molestie a libero. Aenean justo elit, rutrum a ornare id, egestas eget enim. Aenean auctor tristique erat. Curabitur condimentum libero lacus, nec consequat orci vestibulum sed. Fusce elit ligula, blandit vitae sapien vitae, dictum ultrices risus. Nam laoreet suscipit sapien, at interdum velit faucibus sit amet. Duis quis metus egestas lectus elementum posuere non nec libero. Aliquam a dolor bibendum, facilisis nunc a, maximus diam. Vestibulum suscipit tristique magna, non dignissim turpis sodales sed. Nunc ornare, velit ac facilisis fringilla, dolor mi consectetur lorem, vitae finibus erat justo suscipit urna. Maecenas sit amet eros erat. Nunc non arcu ornare, suscipit lorem eget, sodales mauris. Aliquam tincidunt, quam nec mollis lacinia, nisi orci fermentum libero, consequat eleifend lectus quam et sapien. Vestibulum a quam urna.
|
||||
|
||||
Cras arcu leo, euismod ac ullamcorper at, faucibus sed massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus porttitor velit in enim interdum, non commodo metus ornare. Morbi vel lorem quis nisl luctus tristique quis vitae nisl. Suspendisse condimentum tortor enim, nec eleifend ipsum euismod et. Sed gravida quam ut tristique lacinia. Mauris eu interdum ipsum, ac ultrices odio. Nullam auctor tellus a risus porttitor vehicula. Nulla blandit euismod dictum. In pharetra, enim iaculis pulvinar interdum, dui nunc placerat nunc, sit amet pretium lectus nulla vitae quam. Phasellus quis enim sollicitudin, varius nulla id, ornare purus. Donec quam lacus, vestibulum quis nunc ac, mollis dictum nisi. Cras ut mollis elit. Maecenas ultrices ligula at risus faucibus scelerisque. Etiam vitae porttitor purus. Curabitur blandit lectus urna, ut hendrerit tortor feugiat ut.
|
||||
|
||||
Phasellus fringilla, sapien pellentesque commodo pharetra, ante libero aliquam tellus, ut consectetur augue libero a sapien. Maecenas blandit luctus nisl eget aliquet. Maecenas vitae porta dolor, faucibus laoreet sapien. Suspendisse lobortis, ipsum sed vehicula aliquam, elit purus scelerisque dui, rutrum consectetur diam odio et lorem. In nec lacinia metus. Donec viverra libero est, vel bibendum erat condimentum quis. Donec feugiat purus leo. In laoreet vitae felis a porttitor. Mauris ullamcorper, lacus id condimentum suscipit, neque magna pellentesque arcu, eget cursus neque tellus id metus. Curabitur volutpat ac orci vel ultricies.
|
||||
|
||||
Sed ut finibus erat. Sed diam purus, varius non tincidunt quis, ultrices sit amet ipsum. Donec et egestas nulla. Suspendisse placerat nisi at dui laoreet iaculis. Aliquam aliquet leo at augue faucibus molestie. Nullam lacus augue, hendrerit sed nisi eu, faucibus porta est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam ut leo aliquet sem fermentum rutrum quis ac justo. Integer placerat aliquam nisl ut sagittis. Proin erat orci, lobortis et sem eget, eleifend fringilla augue. Mauris varius laoreet arcu, sed tincidunt felis. Pellentesque venenatis lorem odio, id pulvinar velit molestie feugiat. Donec mattis lacus sed eleifend pulvinar.
|
||||
|
||||
Sed condimentum ex in tincidunt hendrerit. Etiam eget risus lacinia, euismod nibh eu, pellentesque quam. Proin elit eros, convallis id mauris ac, bibendum ultrices lectus. Morbi venenatis, purus id fermentum consequat, nunc libero tincidunt ligula, non dictum ligula orci nec quam. Nulla nec ultrices lorem. Aenean maximus augue vel dictum pharetra. Etiam turpis urna, pellentesque quis malesuada eu, molestie faucibus felis.
|
||||
|
||||
Vestibulum pharetra augue ut quam blandit congue in nec risus. Proin eu nibh eu dui eleifend porta vitae id lectus. Proin lacus nibh, lobortis sed ligula vitae, interdum lobortis erat. Suspendisse potenti. In sollicitudin quis sapien ut aliquet. Mauris ac nulla arcu. Fusce tristique justo quis lectus mollis, eu volutpat lectus finibus. Vivamus venenatis facilisis ex ut vestibulum.
|
||||
|
||||
Etiam varius lobortis purus, in hendrerit elit tristique at. In tempus, augue vestibulum fermentum gravida, ligula tellus vulputate arcu, eu molestie ex sapien at purus. Vestibulum nec egestas metus. Duis pulvinar quam nec consequat interdum. Aenean non dapibus lacus. Aliquam sit amet aliquet nulla. Sed venenatis volutpat purus nec convallis. Phasellus aliquet semper sodales. Cras risus sapien, condimentum auctor urna a, pulvinar ornare nisl. Sed tincidunt felis elit, ut elementum est bibendum ac. Morbi interdum justo vel dui faucibus condimentum.
|
||||
|
||||
Sed convallis eu sem at tincidunt. Nullam at auctor est, et ullamcorper ipsum. Pellentesque eget ante ante. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer euismod, sapien sed dapibus ornare, nibh enim maximus lacus, lacinia placerat urna quam quis felis. Morbi accumsan id nisl ut condimentum. Donec bibendum nisi est, sed volutpat lorem rhoncus in. Vestibulum ac lacinia nunc, eget volutpat magna. Integer aliquam pharetra ipsum, id placerat nunc volutpat quis. Etiam urna diam, rhoncus sit amet varius vel, euismod vel sem. Nullam vel molestie urna. Vivamus ornare erat at venenatis euismod. Suspendisse potenti. Fusce diam justo, tincidunt vel sem at, commodo faucibus nisl. Duis gravida efficitur diam, vel sagittis erat pulvinar ut.
|
||||
|
||||
Quisque vel pharetra felis. Duis efficitur tortor dolor, vitae porttitor erat fermentum sed. Sed eu mi purus. Etiam dignissim tortor eu tempus molestie. Aenean pretium erat enim, in hendrerit ante hendrerit at. Sed ut risus vel nunc venenatis ultricies quis in lacus. Pellentesque vitae purus euismod, placerat risus non, ullamcorper augue. Quisque varius quam ligula, nec aliquet ex faucibus vitae. Quisque rhoncus sit amet leo tincidunt mattis. Cras id mauris eget purus pretium gravida sit amet eu augue. Aliquam dapibus odio augue, id lacinia velit pulvinar eu.
|
||||
|
||||
Mauris fringilla, tellus nec pharetra iaculis, neque nisi ultrices massa, et tincidunt sem dui sed mi. Curabitur erat lorem, venenatis quis tempus lacinia, tempus sit amet nunc. Aliquam at neque ac metus commodo dictum quis vitae justo. Phasellus eget lacus tempus, blandit lorem vel, rutrum est. Aenean pharetra sem ut augue lobortis dignissim. Sed rhoncus at nulla id ultrices. Cras id condimentum felis. In suscipit luctus vulputate. Donec tincidunt lacus nec enim tincidunt sollicitudin ut quis enim. Nam at libero urna. Praesent sit amet massa vitae massa ullamcorper vehicula.
|
||||
|
||||
Nullam bibendum augue ut turpis condimentum bibendum. Proin sit amet urna hendrerit, sodales tortor a, lobortis lectus. Integer sagittis velit turpis, et tincidunt nisi commodo eget. Duis tincidunt elit finibus accumsan cursus. Aenean dignissim scelerisque felis vel lacinia. Nunc lacinia maximus luctus. In hac habitasse platea dictumst. Vestibulum eget urna et enim tempor tempor. Nam feugiat, felis vel vestibulum tempus, orci justo viverra diam, id dapibus lorem justo in ligula.
|
||||
|
||||
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In ac pellentesque sem. Vestibulum lacinia magna dui, eu lacinia augue placerat et. Maecenas pulvinar congue est. Pellentesque commodo dui non pulvinar scelerisque. Etiam interdum est posuere sem bibendum, ac commodo magna dictum. Cras ipsum turpis, rhoncus nec posuere vitae, laoreet a arcu. Integer ac massa sit amet enim placerat lacinia sed ultrices arcu. Suspendisse sem nibh, luctus sit amet volutpat in, pellentesque eu metus. Ut gravida neque eget mi accumsan tempus. Nam sit amet aliquet nibh.
|
||||
|
||||
Pellentesque a purus cursus nulla hendrerit congue quis et odio. Aenean hendrerit, leo ullamcorper sagittis hendrerit, erat dui molestie quam, sed condimentum lacus risus sed tellus. Morbi a dapibus lectus, ut feugiat ex. Phasellus pretium quam et sapien mollis, vel iaculis dui dignissim. Sed ullamcorper est turpis, a viverra lorem consectetur in. Aenean aliquet nibh non cursus rutrum. Suspendisse at tristique urna, id lobortis urna. In hac habitasse platea dictumst. Phasellus libero velit, rutrum sed tellus nec, dapibus tincidunt ligula. Quisque vel dui venenatis, consequat nisl ut, lacinia ipsum. Phasellus vitae magna pellentesque, lobortis est id, faucibus quam. Nam eleifend faucibus dui vel pellentesque.
|
||||
|
||||
Etiam ut est non lacus tincidunt interdum. Maecenas sed massa urna. Quisque ut nibh tortor. Pellentesque felis ipsum, tempor finibus ipsum et, euismod pretium metus. Donec sit amet est ipsum. Quisque rhoncus justo non finibus elementum. Nulla nec lectus ac tortor placerat fringilla. Phasellus ac ultrices nunc, eu efficitur nisl. Nulla rhoncus nunc vitae ante dictum tincidunt. Nunc ultrices, massa sit amet malesuada dignissim, lectus lacus consequat sapien, non eleifend metus sem in eros. Phasellus mauris ante, dictum sit amet suscipit ac, rhoncus eget nisi. Phasellus at orci mollis, imperdiet neque eget, faucibus nulla. In at purus massa. Pellentesque quis rutrum lectus.
|
||||
|
||||
Integer eu faucibus turpis, sit amet mollis massa. Vestibulum id nulla commodo, rutrum ipsum sed, semper ante. Phasellus condimentum orci nec nibh convallis, ac maximus orci ullamcorper. Maecenas vitae sollicitudin mi. Integer et finibus lectus, et condimentum ligula. Donec elementum tristique quam vitae dapibus. Morbi euismod ipsum in tristique ullamcorper.
|
||||
|
||||
Duis fermentum non enim eu auctor. Quisque lacinia nibh vehicula nibh posuere, eu volutpat turpis facilisis. Ut ac faucibus nulla. Sed eleifend quis ex et pellentesque. Vestibulum sollicitudin in libero id fringilla. Phasellus dignissim purus consequat, condimentum dui sit amet, condimentum ante. Pellentesque ac consectetur massa, quis sagittis est. Nulla maximus tristique risus accumsan convallis. Curabitur imperdiet ac lacus a ultrices. Nulla facilisi. Sed quis quam quis lectus placerat lobortis vel sed turpis. In mollis dui id neque iaculis, ut aliquet tellus malesuada. Proin at luctus odio, vel blandit sapien. Praesent dignissim tortor vehicula libero fringilla, nec ultrices erat suscipit. Maecenas scelerisque purus in dapibus fermentum.
|
||||
|
||||
Curabitur magna odio, mattis in tortor ut, porttitor congue est. Vestibulum mollis lacinia elementum. Fusce maximus erat vitae nunc rutrum lobortis. Integer ligula eros, auctor vel elit non, posuere luctus lacus. Maecenas quis auctor massa. Ut ipsum lacus, efficitur posuere euismod et, hendrerit efficitur est. Phasellus fringilla, quam id tincidunt pretium, nunc dui sollicitudin orci, eu dignissim nisi metus ut magna. Integer lobortis interdum dolor, non bibendum purus posuere et. Donec non lectus aliquet, pretium dolor eu, cursus massa. Sed ut dui sapien. In sed vestibulum massa. Pellentesque blandit, dui non sodales vehicula, orci metus mollis nunc, non pharetra ex tellus ac est. Mauris sagittis metus et fermentum pretium. Nulla facilisi. Quisque quis ante ut nulla placerat mattis ut quis nisi.
|
||||
|
||||
Sed quis nulla ligula. Quisque dignissim ligula urna, sed aliquam purus semper at. Suspendisse potenti. Nunc massa lectus, pharetra vehicula arcu bibendum, imperdiet sodales ipsum. Nam ac sapien diam. Mauris iaculis fringilla mattis. Pellentesque tempus eros sit amet justo volutpat mollis. Phasellus ac turpis ipsum. Morbi vel ante elit. Aenean posuere quam consequat velit varius suscipit. Donec tempor quam ut nibh cursus efficitur.
|
||||
|
||||
Morbi molestie dolor nec sem egestas suscipit. Etiam placerat pharetra lectus, et ullamcorper risus tristique in. Sed faucibus ullamcorper lectus eget fringilla. Maecenas malesuada hendrerit congue. Sed eget neque a erat placerat tincidunt. Aliquam vitae dignissim turpis. Fusce at placerat magna, a laoreet lectus. Maecenas a purus nec diam gravida fringilla. Nam malesuada euismod ante non vehicula. In faucibus bibendum leo, faucibus posuere nisl pretium quis. Fusce finibus bibendum finibus. Vestibulum eu justo maximus, hendrerit diam nec, dignissim sapien. Aenean dolor lacus, malesuada quis vestibulum ac, venenatis ac ipsum. Cras a est id nunc finibus facilisis. Cras lacinia neque et interdum vehicula. Suspendisse vulputate tellus elit, eget tempor dui finibus vel.
|
||||
|
||||
Cras sed pretium odio. Proin hendrerit elementum felis in tincidunt. Nam sed turpis vel justo molestie accumsan condimentum eu nunc. Praesent lobortis euismod rhoncus. Nulla vitae euismod nibh, quis mattis mi. Fusce ultrices placerat porttitor. Duis sem ipsum, pellentesque sit amet odio a, molestie vulputate mauris.
|
||||
|
||||
Duis blandit mollis ligula, sit amet mattis ligula finibus sit amet. Nunc a leo molestie, placerat diam et, vestibulum leo. Suspendisse facilisis neque purus, nec pellentesque ligula fermentum nec. Aenean malesuada mauris lorem, eu blandit arcu pulvinar quis. Duis laoreet urna lacus, non maximus arcu rutrum ultricies. Nulla augue dolor, suscipit eu mollis eu, aliquam condimentum diam. Ut semper orci luctus, pharetra turpis at, euismod mi. Nulla leo diam, finibus sit amet purus sed, maximus dictum lorem. Integer eu mi id turpis laoreet rhoncus.
|
||||
|
||||
Integer a mauris tincidunt, finibus orci ut, pretium mauris. Nulla molestie nunc mi, id finibus lorem elementum sed. Proin quis laoreet ante. Integer nulla augue, commodo id molestie quis, rutrum ut turpis. Suspendisse et tortor turpis. Sed ut pharetra massa. Pellentesque elementum blandit sem, ut elementum tellus egestas a. Fusce eu purus nibh.
|
||||
|
||||
Cras dignissim ligula scelerisque magna faucibus ullamcorper. Proin at condimentum risus, auctor malesuada quam. Nullam interdum interdum egestas. Nulla aliquam nisi vitae felis mollis dictum. Suspendisse dapibus consectetur tortor. Ut ut nisi non sem bibendum tincidunt. Vivamus suscipit leo quis gravida dignissim.
|
||||
|
||||
Aliquam interdum, leo id vehicula mollis, eros eros rhoncus diam, non mollis ligula mi eu mauris. Sed ultrices vel velit sollicitudin tincidunt. Nunc auctor metus at ligula gravida elementum. Praesent interdum eu elit et mollis. Duis egestas quam sit amet velit dignissim consequat. Aliquam ac turpis nec nunc convallis sagittis. Fusce blandit, erat ac fringilla consectetur, dolor eros sodales leo, vel aliquet risus nisl et diam. Aliquam luctus felis vitae est eleifend euismod facilisis et lacus. Sed leo tellus, auctor eu arcu in, volutpat sagittis nisl. Pellentesque nisl ligula, placerat vel ullamcorper at, vulputate ac odio. Morbi ac faucibus orci, et tempus nulla. Proin rhoncus rutrum dolor, in venenatis mauris. Suspendisse a fermentum augue, non semper mi. Nunc eget pretium neque. Phasellus augue erat, feugiat ac aliquam congue, rutrum non sapien. Pellentesque ac diam gravida, consectetur felis at, ornare neque.
|
||||
|
||||
Nullam interdum mattis sapien quis porttitor. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus aliquet rutrum ipsum id euismod. Maecenas consectetur massa et mi porta viverra. Nunc quam nibh, dignissim vitae maximus et, ullamcorper nec lorem. Nunc vitae justo dapibus, luctus lacus vitae, pretium elit. Maecenas et efficitur leo. Curabitur mauris lectus, placerat quis vehicula vitae, auctor ut urna. Quisque rhoncus pharetra luctus. In hac habitasse platea dictumst. Integer sit amet metus nec eros malesuada aliquam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi hendrerit mi ac leo aliquam, sit amet ultricies libero commodo. Mauris dapibus purus metus, sit amet viverra nibh imperdiet et. Nullam porta nulla tellus, quis vehicula diam imperdiet non. Vivamus enim massa, bibendum in fermentum in, ultrices at ex.
|
||||
|
||||
Suspendisse fermentum id nibh eget accumsan. Duis dapibus bibendum erat ut sollicitudin. Aliquam nec felis risus. Pellentesque rhoncus ligula id sem maximus mollis sed nec massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ipsum ipsum, sodales sed enim id, convallis faucibus eros. Donec ultricies dictum tincidunt. Cras vitae nibh arcu. Pellentesque cursus, sapien nec consequat fermentum, ipsum ante suscipit dui, imperdiet hendrerit est nisl eu massa. Quisque vitae sem ligula. Aenean iaculis metus ut mauris interdum laoreet. Vivamus sed gravida dolor.
|
||||
|
||||
Morbi nulla metus, porttitor sed eros sit amet, efficitur efficitur est. In vel nisl urna. Ut aliquet tellus at congue convallis. Phasellus imperdiet lobortis sollicitudin. Integer sodales, sem eu ultricies pharetra, erat erat porttitor odio, eget dapibus libero ipsum eget velit. Phasellus gravida nulla nisl, eu pharetra mi auctor vel. Sed blandit pharetra velit, ut egestas libero placerat non. Aliquam a interdum quam. Proin at tortor nec dui sollicitudin tempus sed vestibulum elit. Nunc non sollicitudin velit.
|
||||
|
||||
Aenean consequat diam velit, sed rutrum tortor faucibus dictum. Quisque at semper augue. Duis ut est eget mi ornare bibendum id et ligula. Phasellus consequat tortor non leo pulvinar posuere. Proin vestibulum eleifend felis, in hendrerit tortor sollicitudin eu. Phasellus hendrerit, lacus vel laoreet interdum, dui tortor consequat justo, commodo ultricies arcu felis vitae enim. Vivamus eu sapien at leo suscipit rutrum eu at justo. Aenean et dolor a libero ullamcorper posuere. Integer laoreet placerat nisi in vulputate. Mauris laoreet eget risus sed cursus. Donec scelerisque neque a libero eleifend hendrerit. Nulla varius condimentum nunc sit amet fermentum. Aliquam lorem ex, varius nec mollis ut, ultrices in neque. Morbi sit amet porta leo. Integer iaculis fermentum lacus in vestibulum.
|
||||
|
||||
Ut gravida, tellus ut maximus ultrices, erat est venenatis nisl, vitae pretium massa ex ac magna. Sed non purus eget ligula aliquet volutpat non quis arcu. Nam aliquam tincidunt risus, sit amet fringilla sapien vulputate ut. Mauris luctus suscipit pellentesque. Nunc porttitor dapibus ex quis tempus. Ut ullamcorper metus a eros vulputate, vitae viverra lectus convallis. Mauris semper imperdiet augue quis tincidunt. Integer porta pretium magna, sed cursus sem scelerisque sollicitudin. Nam efficitur, nibh pretium eleifend vestibulum, purus diam posuere sem, in egestas mauris augue sit amet urna.
|
||||
|
||||
Vestibulum tincidunt euismod massa in congue. Duis interdum metus non laoreet fringilla. Donec at ligula congue, tincidunt nunc non, scelerisque nunc. Donec bibendum magna non est scelerisque feugiat at nec neque. Ut orci tortor, tempus eget massa non, dignissim faucibus dolor. Nam odio risus, accumsan pretium neque eget, accumsan dignissim dui. In ut neque auctor, scelerisque tellus sed, ullamcorper nisi. Suspendisse varius cursus quam at hendrerit. Vivamus elit libero, sagittis vitae sem ac, vulputate iaculis ligula.
|
||||
|
||||
Sed lobortis laoreet purus sit amet rutrum. Pellentesque feugiat non leo vel lacinia. Quisque feugiat nisl a orci bibendum vestibulum. In et sollicitudin urna. Morbi a arcu ac metus faucibus tempus. Nam eu imperdiet sapien, suscipit mattis tortor. Aenean blandit ipsum nisi, a eleifend ligula euismod at. Integer tincidunt pharetra felis, mollis placerat mauris hendrerit at. Curabitur convallis, est sit amet luctus volutpat, massa lacus cursus augue, sed eleifend magna quam et risus. Aliquam lobortis tincidunt metus vitae porttitor. Suspendisse potenti. Aenean ullamcorper, neque id commodo luctus, nulla nunc lobortis quam, id dapibus neque dui nec mauris. Etiam quis lorem quis elit commodo ornare. Ut pharetra purus ultricies enim ultrices efficitur. Proin vehicula tincidunt molestie. Mauris et placerat sem.
|
||||
|
||||
Aliquam erat volutpat. Suspendisse velit turpis, posuere ac lacus eu, lacinia laoreet velit. Sed interdum felis neque, id blandit sem malesuada sit amet. Ut sagittis justo erat, efficitur semper orci tempor sed. Donec enim massa, posuere varius lectus egestas, pellentesque posuere mi. Cras tincidunt ut libero sed mattis. Suspendisse quis magna et tellus posuere interdum vel at purus. Pellentesque fringilla tristique neque, id aliquet tellus ultricies non. Duis ut tellus vel odio lobortis vulputate.
|
||||
|
||||
Integer at magna ac erat convallis vestibulum. Sed lobortis porttitor mauris. Fusce varius lorem et volutpat pulvinar. Aenean ac vulputate lectus, vitae consequat velit. Suspendisse ex dui, varius ut risus ut, dictum scelerisque sem. Vivamus urna orci, volutpat ut convallis ac, venenatis vitae urna. In hac habitasse platea dictumst. Etiam eu purus arcu. Aenean vulputate leo urna, vel tristique dui sagittis euismod. Suspendisse non tellus efficitur ante rhoncus volutpat at et sapien.
|
||||
|
||||
Sed dapibus accumsan porttitor. Phasellus facilisis lectus finibus ligula dignissim, id pulvinar lectus feugiat. Nullam egestas commodo nisi posuere aliquet. Morbi sit amet tortor sagittis, rutrum dui nec, dapibus sapien. Sed posuere tortor tortor, interdum auctor magna varius vitae. Vestibulum id sagittis augue. Curabitur fermentum arcu sem, eu condimentum quam rutrum non. Phasellus rutrum nibh quis lectus rhoncus pretium. Curabitur dictum interdum elit. Vestibulum maximus sodales imperdiet. Mauris auctor nec purus sed venenatis. In in urna purus.
|
||||
|
||||
Duis placerat molestie suscipit. Morbi a elit id purus efficitur consequat. Nunc ac commodo turpis. Etiam sit amet lacus a ipsum tempus venenatis sed vel nibh. Duis elementum aliquam mi sed tristique. Morbi ligula tortor, semper ac est vel, lobortis maximus erat. Curabitur ipsum felis, laoreet vel condimentum eget, ullamcorper sit amet mauris. Nulla facilisi. Nam at purus sed mi egestas placerat vitae vel magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse at dignissim diam. Phasellus consectetur eget neque vel viverra. Donec sollicitudin mattis dolor vel malesuada. Vivamus vehicula leo neque, vitae fermentum leo posuere et. Praesent dui est, finibus sit amet tristique quis, pharetra vel nibh.
|
||||
|
||||
Duis nulla leo, accumsan eu odio eget, sagittis semper orci. Quisque ullamcorper ligula quam, commodo porttitor mauris ullamcorper eu. Cras varius sagittis felis in aliquam. Duis sodales risus ac justo vehicula, nec mattis diam lacinia. Cras eget lectus ipsum. Ut commodo, enim vitae malesuada hendrerit, ex dolor egestas lectus, sit amet hendrerit metus diam nec est. Vestibulum tortor metus, lobortis sit amet ante eget, tempor molestie lacus. In molestie et urna et semper. Mauris mollis, sem non hendrerit condimentum, sapien nisi cursus est, non suscipit quam justo non metus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam enim est, porta ac feugiat vitae, rutrum in lorem. Duis vehicula tortor ut posuere maximus.
|
||||
|
||||
Nullam vestibulum non tellus sed commodo. Quisque mattis elit sit amet sapien sollicitudin, ut condimentum nisl congue. Aenean sagittis massa vel elit faucibus fermentum. Donec tincidunt nisi nec nisl sodales pellentesque. Mauris congue congue ligula ut suscipit. Vivamus velit tortor, tempor et gravida eget, fermentum sit amet ante. Nullam fringilla, lorem at ultrices cursus, urna neque ornare dolor, eu lacinia orci enim sed nibh. Ut a ullamcorper lectus, id mattis purus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean maximus sollicitudin posuere. Nunc at augue lacus. Aenean efficitur leo sit amet lacinia efficitur.
|
||||
|
||||
Quisque venenatis quam mi, in pharetra odio vulputate eu. In vel nisl pulvinar, pulvinar ligula ut, sodales risus. Sed efficitur lectus at vestibulum tincidunt. Vestibulum eu ullamcorper elit. Fusce vestibulum magna enim, et tempor lacus posuere vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer leo elit, luctus nec mattis sit amet, sollicitudin in turpis.
|
||||
|
||||
Proin convallis venenatis leo, vitae tristique erat iaculis nec. Nulla facilisi. Duis porttitor, sapien et bibendum vulputate, sem libero sodales lacus, non malesuada felis erat ut libero. Nam non felis semper, finibus est a, mattis mauris. Praesent nec eros quam. Nulla hendrerit, augue consectetur eleifend ultricies, purus mi condimentum nulla, eget dapibus est nunc sed libero. Nullam elementum dui erat, vitae luctus libero sollicitudin et. Nulla odio magna, placerat in augue eu, dapibus imperdiet odio. Suspendisse imperdiet metus sit amet rhoncus dapibus. Cras at enim et urna vehicula cursus eu a mauris. Integer magna ante, eleifend ac placerat vitae, porta at nisi. Cras eget malesuada orci. Curabitur nunc est, vulputate id viverra et, dignissim sed odio. Curabitur non mattis sem. Sed bibendum, turpis vitae vehicula faucibus, nunc quam ultricies lectus, vitae viverra felis turpis at libero.
|
||||
|
||||
Nullam ut egestas ligula. Proin hendrerit justo a lectus commodo venenatis. Nulla facilisi. Ut cursus lorem quis est bibendum condimentum. Aenean in tristique odio. Fusce tempor hendrerit ipsum. Curabitur mollis felis justo, quis dapibus erat auctor vel. Sed augue lectus, finibus ut urna quis, ullamcorper vestibulum dui. Etiam molestie aliquam tempor. Integer mattis sollicitudin erat, et tristique elit varius vel. Mauris a ex justo.
|
||||
|
||||
Nam eros est, imperdiet non volutpat rutrum, pellentesque accumsan ligula. Duis sit amet turpis metus. Aenean in rhoncus metus, ac fringilla ex. Suspendisse condimentum egestas purus, ut pharetra odio vulputate vel. Duis tincidunt massa a placerat ultrices. Mauris ultricies nibh sit amet condimentum malesuada. Duis tincidunt id ipsum sed congue.
|
||||
|
||||
Praesent eu ex augue. Nullam in porta ligula. In tincidunt accumsan arcu, in pellentesque magna tristique in. Mauris eleifend libero ac nisl viverra faucibus. Nam sollicitudin dolor in commodo hendrerit. Cras at orci metus. Ut quis laoreet orci. Vivamus ultrices leo pellentesque tempor aliquet. Maecenas ut eros vitae purus placerat vestibulum. Etiam vitae gravida dolor, quis rhoncus diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
||||
|
||||
Suspendisse fringilla lacinia sagittis. Integer tincidunt consectetur tristique. Morbi non orci convallis, congue sapien quis, vulputate nunc. Donec a libero vel magna elementum facilisis non quis mi. Mauris posuere tellus non ipsum ultrices elementum. Vivamus massa velit, facilisis quis placerat aliquet, aliquet nec leo. Praesent a maximus sem. Sed neque elit, feugiat vel quam non, molestie sagittis nunc. Etiam luctus nunc ac mauris scelerisque, nec rhoncus lacus convallis. Nunc pharetra, nunc ac pulvinar aliquam, ex ipsum euismod augue, nec porttitor lacus turpis vitae neque. Fusce bibendum odio id tortor faucibus pellentesque. Sed ac porta nibh, eu gravida erat.
|
||||
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aliquam quis ullamcorper felis. Nulla mattis sagittis ante ac tincidunt. Integer ac felis efficitur, viverra libero et, facilisis ligula. Suspendisse a metus a massa rhoncus posuere. Phasellus suscipit ligula ut lacus facilisis, ac pellentesque ex tempor. Quisque consectetur massa mi, ac molestie libero dictum quis. Proin porttitor ligula quis erat tincidunt venenatis. Proin congue nunc sed elit gravida, nec consectetur lectus sodales. Etiam tincidunt convallis ipsum at vestibulum. Quisque maximus enim et mauris porttitor, et molestie magna tristique. Morbi vitae metus elit. Maecenas sed volutpat turpis. Aliquam vitae dolor vestibulum, elementum purus eget, dapibus nibh. Nullam egestas dui ac rutrum semper.
|
||||
|
||||
Etiam hendrerit est metus, et condimentum metus aliquam ac. Pellentesque id neque id ipsum rhoncus vulputate. Aliquam erat nisl, posuere sit amet ligula ac, fermentum blandit felis. Vivamus fermentum mi risus, non lacinia purus viverra id. Aenean ac sapien consequat, finibus mauris nec, porta sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed quis consectetur ex, dignissim bibendum nulla. Phasellus ac libero at quam vehicula euismod non eu leo. Phasellus a sapien augue.
|
||||
|
||||
Maecenas ligula dui, bibendum vitae mauris et, auctor laoreet felis. Duis non libero a mi semper mattis. Quisque consequat luctus massa, quis tristique eros auctor feugiat. Maecenas sodales euismod neque vitae facilisis. Nullam laoreet imperdiet velit at pellentesque. Etiam massa odio, facilisis a consequat vitae, placerat vel magna. Nunc sagittis eros nec urna fringilla, pulvinar vestibulum nibh scelerisque. Sed magna metus, cursus eu consequat et, pharetra a est. Suspendisse elementum neque a dui malesuada lacinia. Donec sed ipsum volutpat, cursus urna id, ullamcorper arcu. Maecenas laoreet nisl eget velit egestas sollicitudin. Etiam nisl turpis, mollis id dignissim vitae, tristique vehicula ante. Maecenas eget placerat est, at rutrum augue. Vivamus faucibus lacinia ullamcorper. Sed pulvinar urna sodales ante sodales, at gravida leo dictum.
|
||||
|
||||
Morbi maximus, quam a lobortis bibendum, enim felis varius elit, ac vehicula elit nisl ut lacus. Quisque ut arcu augue. Praesent id turpis quam. Sed sed arcu eros. Maecenas at cursus lorem, ac eleifend nisi. Fusce mattis felis at commodo pharetra. Praesent ac commodo ipsum. Quisque finibus et eros vitae tincidunt. In hac habitasse platea dictumst. Praesent purus ipsum, luctus lobortis ornare quis, auctor eget justo. Nam vel enim sollicitudin, faucibus tortor eu, sagittis eros. Ut nec consectetur erat. Donec ultricies malesuada ligula, a hendrerit sapien volutpat in. Maecenas sed enim vitae sapien pulvinar faucibus.
|
||||
|
||||
Proin semper nunc nibh, non consequat neque ullamcorper vel. Maecenas lobortis sagittis blandit. Aenean et arcu ultricies turpis malesuada malesuada. Ut quam ex, laoreet ut blandit cursus, feugiat vitae dolor. Etiam ex lacus, scelerisque vel erat vel, efficitur tincidunt magna. Morbi tristique lacinia dolor, in egestas magna ultrices vitae. Integer ultrices leo ac tempus venenatis. Praesent ac porta tortor. Vivamus ornare blandit tristique. Nulla rutrum finibus pellentesque. In non dui elementum, fermentum ipsum vel, varius magna. Pellentesque euismod tortor risus, ac pellentesque nisl faucibus eget.
|
||||
|
||||
Vivamus eu enim purus. Cras ultrices rutrum egestas. Sed mollis erat nibh, at posuere nisl luctus nec. Nunc vulputate, sapien id auctor molestie, nisi diam tristique ante, non convallis tellus nibh at orci. Morbi a posuere purus, in ullamcorper ligula. Etiam elementum sit amet dui imperdiet iaculis. Proin vitae tincidunt ipsum, sit amet placerat lectus. Curabitur commodo sapien quam, et accumsan lectus fringilla non. Nullam eget accumsan enim, ac pharetra mauris. Sed quis tristique velit, vitae commodo nisi. Duis turpis dui, maximus ut risus at, finibus consequat nunc. Maecenas sed est accumsan, aliquet diam in, facilisis risus. Curabitur vehicula rutrum auctor. Nam iaculis risus pulvinar maximus viverra. Nulla vel augue et ex sagittis blandit.
|
||||
|
||||
Ut sem nulla, porta ac ante ac, posuere laoreet eros. Donec sodales posuere justo a auctor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras mollis at orci hendrerit porta. Nullam sodales tortor tortor, non lacinia diam finibus id. Duis libero orci, suscipit ac odio et, dictum consequat ipsum. Pellentesque eu ligula sagittis, volutpat eros at, lacinia lorem. Cras euismod tellus in iaculis tempor. Quisque accumsan, magna a congue venenatis, ante ipsum aliquam lectus, at egestas enim nunc at justo. Quisque sem purus, viverra ut tristique ut, maximus id enim. Etiam quis placerat sem. In sollicitudin, lacus eu rutrum mollis, nulla eros luctus elit, vel dapibus urna purus nec urna. Phasellus egestas massa quam, ac molestie erat hendrerit a. Praesent ultrices neque ut turpis molestie auctor. Etiam molestie placerat purus, et euismod erat aliquam in. Morbi id suscipit justo.
|
||||
|
||||
Proin est ante, consequat at varius a, mattis quis felis. Sed accumsan nibh sit amet ipsum elementum posuere. Vestibulum bibendum id diam sit amet gravida. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi nec dolor vel ipsum dignissim hendrerit vel non ipsum. Praesent facilisis orci quis elit auctor lobortis. Phasellus cursus risus lectus, vel lobortis libero dapibus in. Quisque tristique tempus leo a pulvinar. Pellentesque a magna tincidunt, pellentesque massa nec, laoreet orci. Morbi congue ornare dolor quis commodo. Phasellus massa nisi, tincidunt at eros dictum, hendrerit lobortis urna. Maecenas porta, magna id mattis molestie, nibh tellus lobortis sem, eget tincidunt ipsum quam eu turpis.
|
||||
|
||||
Ut gravida orci risus, vel rutrum mauris vehicula id. Etiam bibendum, neque a placerat condimentum, ex orci imperdiet lectus, quis dapibus arcu lacus eget lectus. Sed consequat non mi sit amet venenatis. Fusce vestibulum erat libero, eget hendrerit risus vulputate sollicitudin. Integer sed eleifend felis. Donec commodo, sem eu mattis placerat, urna odio aliquam tellus, et laoreet justo tellus eget erat. Fusce sed suscipit tortor. Nam hendrerit nibh ac nunc auctor lacinia. Pellentesque placerat condimentum ipsum, eget semper tortor hendrerit vel. Nullam non urna eu lacus pellentesque congue ut id eros.
|
||||
|
||||
Nunc finibus leo in rhoncus tristique. Sed eu ipsum nec nisl egestas faucibus eget a felis. Pellentesque vitae nisi in nulla accumsan fermentum. Sed venenatis feugiat eleifend. Fusce porttitor varius placerat. Aliquam aliquet lacus sit amet mattis mollis. Sed vel nulla quis dolor suscipit vehicula ac viverra lorem. Duis viverra ipsum eget nulla ullamcorper fermentum. Mauris tincidunt arcu quis quam fringilla ornare. Donec et iaculis tortor. Nam ultricies libero vel ipsum aliquet efficitur. Morbi eget dolor aliquam, tempus sapien eget, viverra ante. Donec varius mollis ex, sed efficitur purus euismod interdum. Quisque vel sapien non neque tincidunt semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
|
||||
|
||||
Suspendisse sit amet purus leo. Fusce lectus lorem, aliquam ac nulla eget, imperdiet ornare eros. Nullam sem augue, varius in nisi non, sollicitudin pellentesque ante. Etiam eu odio condimentum, tempor libero et, egestas arcu. Cras pellentesque eleifend aliquet. Pellentesque non blandit ligula. Ut congue viverra rhoncus. Phasellus mattis mi ac eros placerat, eu feugiat tellus ultrices. Aenean mollis laoreet libero eu imperdiet. Cras sed pulvinar mi, ac vehicula ligula. Vestibulum sit amet ex massa. In a egestas eros.
|
||||
|
||||
Mauris pretium ipsum risus, venenatis cursus ante imperdiet id. Praesent eu turpis nec risus feugiat maximus ullamcorper ac lectus. Integer placerat at mi vel dapibus. Vestibulum fermentum turpis sit amet turpis viverra, id aliquet diam suscipit. Nam nec ex sed ante ullamcorper pharetra quis sit amet risus. Sed ac faucibus velit, id feugiat nibh. Nullam eget ipsum ex. Vivamus tincidunt non nunc non faucibus. Quisque bibendum viverra facilisis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur at nisi hendrerit quam suscipit egestas. Curabitur laoreet maximus ultricies. Duis ut tellus ac augue molestie dictum.
|
||||
|
||||
Suspendisse rhoncus iaculis erat, ut ullamcorper est tristique eget. Donec auctor nec risus at gravida. Vivamus volutpat vulputate tellus, vel ultricies eros suscipit eget. Ut pulvinar id mi eu tempus. Morbi malesuada augue in dui varius, nec blandit neque vehicula. Donec ornare nec nisl in mollis. Morbi enim nisi, rhoncus nec est id, dapibus tempus urna. Ut id elit a felis vestibulum consectetur. Duis lectus quam, pharetra sit amet diam sed, posuere vestibulum erat. Fusce vitae maximus massa. Nullam id metus tempus, iaculis risus eu, lobortis urna. Quisque in congue urna. Pellentesque placerat neque in augue dapibus, non varius ex malesuada. Curabitur ut eleifend libero. Fusce vitae ligula luctus, fermentum enim vitae, ultrices erat.
|
||||
|
||||
Sed viverra augue turpis, scelerisque egestas sapien mattis eu. Duis laoreet magna at ex pharetra dapibus. Praesent eget odio vel quam venenatis dictum. Nulla in sollicitudin dolor. Mauris lobortis nec eros vel rhoncus. Vestibulum porta viverra venenatis. Curabitur vel scelerisque quam, a egestas velit. Praesent volutpat tincidunt magna at laoreet.
|
||||
|
||||
Cras nec lorem odio. Pellentesque quis dui urna. Praesent at tellus ac lectus scelerisque placerat nec eu risus. Vestibulum sit amet mattis ligula. Vivamus sed nisi at leo elementum accumsan at sit amet arcu. Aenean mattis tellus nec leo gravida, eget hendrerit nisl faucibus. Mauris pellentesque luctus condimentum. Maecenas pretium sapien nunc, eget commodo dolor maximus id. Mauris vestibulum accumsan massa a dictum. Phasellus interdum quam ligula, ut maximus diam blandit aliquam. Nunc vitae ex eu erat condimentum consectetur. Maecenas interdum condimentum volutpat.
|
||||
|
||||
Donec et enim a libero rutrum laoreet. Praesent a condimentum sem, at tincidunt quam. In vel molestie risus. Sed urna dui, molestie vitae mollis laoreet, tempor quis lectus. Praesent vitae auctor est, et aliquet nunc. Curabitur vulputate blandit nulla, at gravida metus. Maecenas gravida dui eu iaculis tristique. Pellentesque posuere turpis nec auctor eleifend. Suspendisse bibendum diam eu tellus lobortis, et laoreet quam congue. In hac habitasse platea dictumst. Morbi dictum neque velit, eget rutrum eros ultrices sit amet.
|
||||
|
||||
Phasellus fermentum risus pharetra consectetur bibendum. Donec magna tortor, lacinia vitae nibh quis, aliquet pretium lorem. Donec turpis nisi, pretium eu enim volutpat, mattis malesuada augue. Nullam vel tellus iaculis, sollicitudin elit eget, tincidunt lacus. Fusce elementum elementum felis et iaculis. Suspendisse porta eros nec neque malesuada, in malesuada ante sollicitudin. Vivamus bibendum viverra molestie.
|
||||
|
||||
Integer feugiat, erat nec convallis aliquam, velit felis congue erat, molestie eleifend tellus erat in tellus. Nunc et justo purus. Donec egestas fermentum dui non feugiat. Quisque in sapien sagittis, gravida quam id, iaculis lectus. Cras sagittis rhoncus bibendum. Fusce quis metus in velit scelerisque tincidunt at non ipsum. Vivamus efficitur ante eu odio vulputate, vitae ultricies risus vehicula. Proin eget odio eu sem tincidunt feugiat vel id lorem.
|
||||
|
||||
Vestibulum sit amet nulla dignissim, euismod mi in, fermentum tortor. Donec ut aliquet libero, lacinia accumsan velit. Donec et nulla quam. Nullam laoreet odio nec nunc imperdiet, a congue eros venenatis. Quisque nec tellus sit amet neque interdum posuere. Duis quis mi gravida, tincidunt diam convallis, ultricies augue. Mauris consequat risus non porttitor congue. Ut in ligula consequat, viverra nunc a, eleifend enim. Duis ligula urna, imperdiet nec facilisis et, ornare eu ex. Proin lobortis lectus a lobortis porttitor. Nulla leo metus, egestas eu libero sed, pretium faucibus felis. Vestibulum non sem tortor. Nam cursus est leo. Vivamus luctus enim odio, non interdum sem dapibus a. Aenean accumsan consequat lectus in imperdiet.
|
||||
|
||||
Donec vehicula laoreet ipsum in posuere. Quisque vel quam imperdiet, sollicitudin nisi quis, suscipit velit. Morbi id sodales mauris. Curabitur tellus arcu, feugiat sed dui sit amet, sodales sagittis libero. Aenean vel suscipit metus, non placerat leo. Vestibulum quis nulla elit. Proin scelerisque non ante ut commodo. Interdum et malesuada fames ac ante ipsum primis in faucibus.
|
||||
|
||||
Sed non urna dolor. Suspendisse convallis mi porta pulvinar ultrices. Suspendisse quam ipsum, hendrerit non scelerisque molestie, interdum dictum nunc. Morbi condimentum condimentum turpis eu luctus. Pellentesque sagittis sollicitudin odio, sed ultricies felis ornare sit amet. Sed ultrices ex leo, a tincidunt nisl gravida sed. Nullam ornare accumsan porta. Praesent consectetur id est nec sollicitudin.
|
||||
|
||||
In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed sed ultrices nibh. Duis accumsan suscipit eros, a dictum odio tempus sit amet. Aenean imperdiet erat ac lacus finibus, scelerisque cursus massa imperdiet. Mauris molestie risus ut lacinia posuere. Nulla et sodales purus. Maecenas orci erat, placerat in tristique quis, placerat in mi.
|
||||
|
||||
Donec sollicitudin pellentesque odio in feugiat. Morbi eu dolor ut mauris congue sollicitudin. Aliquam erat volutpat. Nulla id varius dui. Curabitur finibus urna ante, consectetur interdum nisi volutpat a. Quisque quis mi tristique, consequat tellus eget, rutrum sapien. Vivamus vitae tellus vulputate, rutrum ex eu, vulputate sem. Suspendisse viverra lorem tellus, vel interdum orci gravida quis. Ut laoreet arcu at mi ullamcorper finibus. Duis porta sagittis vestibulum. Sed commodo nisl vitae urna sollicitudin, nec lacinia est sodales. Curabitur imperdiet sodales dui sed iaculis. Sed ac tellus maximus, eleifend quam sit amet, feugiat elit. Aenean viverra, dui at mattis varius, est odio vestibulum sapien, sit amet mollis libero massa nec velit. Etiam quis sodales justo.
|
||||
|
||||
Ut ultricies, sem eget sodales feugiat, nunc arcu congue elit, ac tempor justo massa nec purus. Maecenas enim nunc, pharetra eget dictum sit amet, tempus pellentesque velit. Suspendisse venenatis ligula in nulla mattis, et imperdiet ex tincidunt. Etiam vulputate, tellus et ultrices suscipit, enim velit laoreet massa, vitae congue odio enim ac urna. Morbi quam lorem, iaculis ac varius sagittis, euismod quis dolor. In ut dui eu purus feugiat consectetur. Vestibulum cursus velit quis lacus pellentesque iaculis. Cras in risus sed mauris porta rutrum. Nulla facilisi. Nullam eu bibendum est, non pellentesque lectus. Sed imperdiet feugiat lorem, quis convallis ante auctor in. Maecenas justo magna, scelerisque sit amet tellus eget, varius elementum risus. Duis placerat et quam sed varius.
|
||||
|
||||
Duis nec nibh vitae nibh dignissim mollis quis sed felis. Curabitur vitae quam placerat, venenatis purus ut, euismod nisl. Curabitur porttitor nibh eu pulvinar ullamcorper. Suspendisse posuere nec ipsum ac dapibus. Cras convallis consectetur urna. Phasellus a nibh in dolor lacinia posuere id eget augue. In eu pharetra lorem, vitae cursus lacus. Aliquam tincidunt nibh lectus. Aenean facilisis ultricies posuere. Sed ut placerat orci. Curabitur scelerisque gravida blandit. Maecenas placerat ligula eget suscipit fringilla. Mauris a tortor justo. Aliquam hendrerit semper mollis. Phasellus et tincidunt libero. Etiam vel quam libero.
|
||||
|
||||
Quisque aliquet tempor ex. Ut ante sem, vehicula at enim vel, gravida porta elit. Etiam vitae lacus a neque lobortis consectetur. Mauris sed interdum odio. Mauris elementum ex blandit tempor cursus. Integer in enim in leo viverra elementum. Fusce consectetur metus et sem rutrum, mattis euismod diam semper. Nunc sed ipsum vel urna consequat vehicula. Donec cursus pretium lorem, vestibulum pretium felis commodo sit amet. Nam blandit felis enim, eget gravida ex faucibus a. In nec neque massa. Etiam laoreet posuere ipsum. Praesent volutpat nunc dolor, ac vulputate magna facilisis non. Aenean congue turpis vel lectus sollicitudin tristique. Sed nec consequat purus, non vehicula quam. Etiam ultricies, est ac dictum tincidunt, turpis turpis pretium massa, a vulputate libero justo at nibh.
|
||||
|
||||
Aliquam erat volutpat. Cras ultrices augue ac sollicitudin lobortis. Curabitur et aliquet purus. Duis feugiat semper facilisis. Phasellus lobortis cursus velit, a sollicitudin tortor. Nam feugiat sapien non dapibus condimentum. Morbi at mi bibendum, commodo quam at, laoreet enim. Integer eu ultrices enim. Sed vestibulum eu urna ut dictum. Curabitur at mattis leo, sed cursus massa. Aliquam porttitor, felis quis fermentum porttitor, justo velit feugiat nulla, eget condimentum sem dui ut sapien.
|
||||
|
||||
In fringilla elit eu orci aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut eget fringilla tellus. Curabitur fermentum, mi et condimentum suscipit, elit neque bibendum dui, et hendrerit nunc metus id ipsum. Morbi placerat mi in hendrerit congue. Ut feugiat mauris eget scelerisque viverra. Vivamus sit amet erat dictum, sagittis lectus nec, pulvinar lorem. Sed non enim ac dui sollicitudin aliquet. Quisque ut lacus dolor. Fusce hendrerit malesuada euismod. Nulla faucibus vel mauris eu mollis. Mauris est diam, fringilla ac arcu feugiat, efficitur volutpat turpis. Aliquam venenatis cursus massa sed porttitor. Ut ac finibus enim, in tincidunt sapien.
|
||||
|
||||
Nunc faucibus semper turpis a lacinia. Phasellus gravida, libero vel pulvinar ornare, ex sem tincidunt lectus, sit amet convallis augue risus at tortor. Quisque sit amet ipsum id nulla posuere vestibulum. Pellentesque scelerisque mauris vel leo viverra sodales. Nulla viverra aliquam ex, ut rutrum enim fermentum venenatis. Aenean eget dapibus ex, eget faucibus metus. Vestibulum volutpat leo in diam semper, eget porta magna suscipit. Sed sit amet nulla blandit, aliquam dolor ac, gravida velit. Sed vel velit viverra, maximus est id, convallis justo.
|
||||
|
||||
Curabitur nulla ante, vulputate at libero vel, ullamcorper rutrum nibh. Pellentesque porttitor eu mauris id mattis. Duis vulputate augue elit, eget interdum justo pretium vel. Maecenas eu vulputate arcu, eget posuere purus. Suspendisse viverra a velit dictum eleifend. Suspendisse vitae dapibus diam. Donec vehicula justo in ante interdum, eu luctus diam placerat. Vivamus convallis ipsum eu orci suscipit, sed fermentum enim euismod. Maecenas faucibus elit vitae ex ornare tristique. Donec vestibulum nec elit sit amet porttitor. Aenean tempor lectus eget tortor hendrerit luctus. Nullam interdum vitae lectus vel feugiat. Cras in risus non magna consectetur lobortis. Sed faucibus enim quis gravida convallis.
|
||||
|
||||
Phasellus eget massa sit amet libero ultrices suscipit. Vivamus at risus sapien. Nam mollis nunc eget velit dictum maximus. Sed pellentesque, nunc ac fringilla lacinia, quam enim mattis ex, sed euismod tortor metus eu neque. Ut mattis nisl ut lectus rhoncus, sodales bibendum eros porta. Nulla porttitor enim nec diam sagittis, eget porta velit efficitur. Vestibulum ultricies eros neque. Phasellus rutrum suscipit enim, in interdum ante gravida vitae. Sed in sagittis diam, non commodo velit.
|
||||
|
||||
Morbi hendrerit odio orci, nec tincidunt odio rhoncus nec. Mauris neque velit, vehicula a lorem at, suscipit tristique dui. Sed finibus, nisl in mattis convallis, turpis neque sodales lacus, eu porta enim magna non diam. Nam commodo sodales risus consectetur malesuada. In eget elementum justo. Phasellus sit amet massa imperdiet, dapibus nunc sit amet, suscipit orci. Fusce condimentum laoreet feugiat. Ut ut viverra ante. Praesent bibendum interdum commodo. Nulla mollis nisi a est ornare volutpat. Sed at ligula eu nisi dapibus tempus. Proin cursus vestibulum justo, nec efficitur justo dignissim vel. Nunc quis maximus eros.
|
||||
|
||||
Cras viverra, diam a tristique mattis, libero felis vulputate tellus, a ornare felis leo a dui. Nulla ante nulla, finibus ut tellus ut, blandit pharetra nibh. Proin eleifend fermentum ex, eget auctor libero vulputate in. Nullam ultricies, mauris placerat pretium placerat, leo urna lobortis leo, vel placerat arcu libero sed mauris. Aliquam mauris ligula, ornare at urna at, eleifend gravida ligula. Vestibulum consectetur ut nulla non scelerisque. Donec ornare, sem nec elementum aliquam, urna nulla bibendum metus, eu euismod dui ligula ac est. Fusce laoreet erat eu ex lobortis, quis bibendum ligula interdum. Sed vel mi erat. Vivamus id lacus ac enim mattis tempor. Nunc ultricies pellentesque enim sed euismod. Fusce tincidunt convallis elit quis aliquam. Mauris nulla ipsum, sollicitudin quis diam ac, feugiat volutpat tellus. In nibh nibh, vulputate quis tincidunt quis, pulvinar eget magna. Pellentesque quis finibus dolor. Suspendisse viverra vitae lectus non eleifend.
|
||||
|
||||
Nunc ut orci et sapien maximus semper. Nulla dignissim sem urna, ac varius lectus ultricies id. Quisque aliquet pulvinar pretium. In ultricies molestie tellus vehicula porta. Nam enim lorem, aliquam eget ex et, hendrerit volutpat quam. Maecenas diam lacus, pellentesque eget tempus ac, pharetra eu elit. Donec vel eros a sem facilisis vulputate. Nullam ac nisi vulputate, laoreet nisl ac, eleifend sem. Nullam mi massa, rhoncus sed pharetra interdum, tincidunt eget nunc. Aliquam viverra mattis posuere. Mauris et dui sed nisl sollicitudin fermentum quis ut arcu. Nam placerat eget orci at tincidunt. Curabitur vel turpis metus. Phasellus nibh nulla, fermentum scelerisque sem vel, gravida tincidunt velit. Pellentesque vel quam tempor, finibus massa pellentesque, condimentum dui.
|
||||
|
||||
Donec at mattis neque. Etiam velit diam, consequat auctor mauris id, hendrerit faucibus metus. Maecenas ullamcorper eros a est sodales, ac consectetur odio scelerisque. Donec leo metus, imperdiet at pellentesque vel, feugiat id erat. Suspendisse at magna enim. Vestibulum placerat sodales lorem id sollicitudin. Aenean at euismod ligula, eget mollis diam. Phasellus pulvinar, orci nec pretium condimentum, est erat facilisis purus, quis feugiat augue elit aliquam nulla. Aenean vitae tortor id risus congue tincidunt. Sed dolor enim, mattis a ullamcorper id, volutpat ac leo.
|
||||
|
||||
Proin vehicula feugiat augue, id feugiat quam sodales quis. Donec et ultricies massa, a lacinia nulla. Duis aliquam augue ornare euismod viverra. Ut lectus risus, rutrum sit amet efficitur a, luctus nec nisl. Cras volutpat ullamcorper congue. Sed vitae odio metus. Phasellus aliquet euismod varius.
|
||||
|
||||
Nullam sem ex, malesuada ut magna ut, pretium mollis arcu. Nam porttitor eros cursus mi lacinia faucibus. Suspendisse aliquet eleifend iaculis. Maecenas sit amet viverra tortor. Nunc a mollis risus. Etiam tempus dolor in tortor malesuada mattis. Ut tincidunt venenatis est sit amet dignissim. Vestibulum massa enim, tristique sed scelerisque eu, fringilla ac velit. Donec efficitur quis urna sit amet malesuada. Vestibulum consequat ac ligula in dapibus. Maecenas massa massa, molestie non posuere nec, elementum ut magna. In nisi erat, mollis non venenatis eu, faucibus in justo. Morbi gravida non ex non egestas. Pellentesque finibus laoreet diam, eu commodo augue congue vitae.
|
||||
|
||||
Aenean sem mi, ullamcorper dapibus lobortis vitae, interdum tincidunt tortor. Vivamus eget vulputate libero. Ut bibendum posuere lectus, vel tincidunt tortor aliquet at. Phasellus malesuada orci et bibendum accumsan. Aliquam quis libero vel leo mollis porta. Sed sagittis leo ac lacus dictum, ac malesuada elit finibus. Suspendisse pharetra luctus commodo. Vivamus ultricies a odio non interdum. Vivamus scelerisque tincidunt turpis quis tempor. Pellentesque tortor ligula, varius non nunc eu, blandit sollicitudin neque. Nunc imperdiet, diam et tristique luctus, ipsum ex condimentum nunc, sit amet aliquam justo velit sed libero. Duis vel suscipit ligula. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed tincidunt neque vel massa ultricies, id dictum leo consequat. Curabitur lobortis ultricies tellus, eget mattis nisl aliquam sit amet.
|
||||
|
||||
Proin at suscipit justo. Vivamus ut vestibulum nisl. Pellentesque enim odio, pharetra non magna sed, efficitur auctor magna. Praesent tincidunt ante quis ante hendrerit viverra. Pellentesque vel ipsum id magna vulputate efficitur. Sed nec neque accumsan, pulvinar sapien quis, euismod mauris. Donec condimentum laoreet sapien quis gravida. Quisque sed mattis purus. Vestibulum placerat vel neque maximus scelerisque.
|
||||
|
||||
Vestibulum mattis quam quis efficitur elementum. Duis dictum dolor ac scelerisque commodo. Fusce sollicitudin nisi sit amet dictum placerat. Suspendisse euismod pharetra eleifend. In eros nisl, porttitor sed mauris at, consectetur aliquet mauris. Donec euismod viverra neque sed fermentum. Phasellus libero magna, accumsan ut ultricies vitae, dignissim eget metus. Donec tellus turpis, interdum eget maximus nec, hendrerit eget massa. Curabitur auctor ligula in iaculis auctor. In ultrices quam suscipit cursus finibus. Aenean id mi at dolor interdum iaculis vitae ut lorem. Nullam sed nibh fringilla, lacinia odio nec, placerat erat. In dui libero, viverra ac viverra ac, pellentesque sit amet turpis.
|
||||
|
||||
Nulla in enim ex. Sed feugiat est et consectetur venenatis. Cras varius facilisis dui vel convallis. Vestibulum et elit eget tellus feugiat pellentesque. In ut ante eu purus aliquet posuere. Nulla nec ornare sem, sed luctus lorem. Nam varius iaculis odio, eget faucibus nisl ullamcorper in. Sed eget cursus felis, nec efficitur nisi.
|
||||
|
||||
Vivamus commodo et sem quis pulvinar. Pellentesque libero ante, venenatis vitae ligula sit amet, ornare sollicitudin nulla. Mauris eget tellus hendrerit, pulvinar metus quis, tempor nisi. Proin magna ex, laoreet sed tortor quis, varius fermentum enim. Integer eu dolor dictum, vulputate tortor et, aliquet ligula. Vestibulum vitae justo id mauris luctus sollicitudin. Suspendisse eget auctor neque, sodales egestas lorem. Vestibulum lacinia egestas metus vitae euismod. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus ex tellus, volutpat nec pulvinar sit amet, condimentum vitae dui. Curabitur vel felis sodales, lacinia nunc iaculis, ullamcorper augue. Pellentesque consequat dolor quis eros efficitur malesuada. Nulla ut malesuada lectus.
|
||||
|
||||
Morbi et tristique ante. Aliquam erat volutpat. Vivamus vitae dui nec turpis pellentesque fermentum. Quisque eget velit massa. Pellentesque tristique aliquam nisl, eu sollicitudin justo venenatis sed. Duis eleifend sem eros, ut aliquam libero porttitor id. Sed non nunc consequat, rhoncus diam eu, commodo erat. Praesent fermentum in lectus id blandit. Donec quis ipsum at justo volutpat finibus. Nulla blandit justo nulla, at mollis lacus consequat eget. Aenean sollicitudin quis eros ut ullamcorper.
|
||||
|
||||
Pellentesque venenatis nulla ut mi aliquet feugiat. Cras semper vel magna nec pharetra. Integer mattis felis et sapien commodo imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Duis quis luctus felis. Vestibulum justo nibh, aliquam non lectus vitae, molestie placerat justo. Donec lorem nibh, gravida sit amet hendrerit ac, maximus id ipsum. Nunc ac libero sodales risus eleifend sagittis. Phasellus est massa, lobortis elementum ex sed, scelerisque consectetur neque. Nunc faucibus neque id lorem malesuada, eget convallis ex mattis.
|
||||
|
||||
Sed turpis tortor, fermentum non turpis id, posuere varius nibh. Donec iaculis lorem dui. Etiam eros ante, sodales eget venenatis at, consectetur eget risus. Curabitur non aliquam ante, a pretium justo. Maecenas tempor nisl tortor, vitae dictum nisi ultrices eu. Duis eget dui ultrices, porttitor lacus sed, lobortis purus. Quisque mattis elit nec neque sagittis, sed commodo leo blandit. Mauris sodales interdum eleifend. Vestibulum condimentum consectetur augue, id luctus diam convallis et.
|
||||
|
||||
Nunc suscipit risus in justo accumsan, a placerat magna tincidunt. Proin a nisl ipsum. Sed libero dui, tristique in augue quis, auctor tristique risus. Sed porttitor ex augue, eu porta augue molestie a. Duis rhoncus purus libero, eu tempus turpis condimentum at. Sed mollis nisi id lectus placerat tincidunt. Maecenas non scelerisque elit, quis rutrum orci. Donec in tellus pharetra urna ornare lobortis. Phasellus id risus at nisi varius rutrum eu ut turpis.
|
||||
|
||||
Duis dictum justo quis nisl porta, eget tincidunt magna suscipit. Sed velit massa, ullamcorper eu sodales ac, pretium a massa. Duis et rutrum tortor. Nulla accumsan hendrerit sapien, cursus volutpat eros egestas eget. Donec sollicitudin at ante quis sollicitudin. Aenean blandit feugiat diam, id feugiat eros faucibus eget. Donec viverra dolor vel justo scelerisque dignissim. Nulla semper sem nunc, rhoncus semper tellus ultricies sed. Duis in ornare diam. Donec vehicula feugiat varius. Maecenas ut suscipit est. Vivamus sem sem, finibus at dolor sit amet, euismod dapibus ligula. Vestibulum fringilla odio dapibus, congue massa eget, congue sem. Donec feugiat magna eget tortor lacinia scelerisque non et ipsum.
|
||||
|
||||
Suspendisse potenti. Nunc convallis sollicitudin ex eget venenatis. Sed iaculis nibh ex, vel ornare ligula congue dignissim. Quisque sollicitudin dolor ac dui vestibulum, sit amet molestie nisi aliquet. Donec at risus felis. Aenean sollicitudin metus a feugiat porta. Aenean a tortor ut dolor cursus sagittis. Vivamus consectetur porttitor nunc in facilisis. Proin sit amet mi vel lectus consectetur ultrices.
|
||||
|
||||
Sed cursus lectus vitae nunc tristique, nec commodo turpis dapibus. Pellentesque luctus ex id facilisis ornare. Morbi quis placerat dolor. Donec in lectus in arcu mattis porttitor ac sit amet metus. Cras congue mauris non risus sodales, vitae feugiat ipsum bibendum. Nulla venenatis urna sed libero elementum, a cursus lorem commodo. Mauris faucibus lobortis eros nec commodo.
|
||||
|
||||
Nullam suscipit ligula ullamcorper lorem commodo blandit. Nulla porta nibh quis pulvinar placerat. Vivamus eu arcu justo. Vestibulum imperdiet est ut fermentum porttitor. Pellentesque consectetur libero in sapien efficitur scelerisque. Curabitur ac erat sit amet odio aliquet dignissim. Pellentesque mi sem, rhoncus et luctus at, porttitor rutrum lectus. Vestibulum sollicitudin sollicitudin suscipit. Aenean efficitur dolor non ultrices imperdiet. Donec vel sem ex.
|
||||
|
||||
Sed convallis mauris aliquam rutrum cursus. Ut tempor porttitor sodales. Etiam eu risus ac augue gravida egestas et eu dolor. Proin id magna ex. Suspendisse quis lectus quis lorem ultricies tempus. Donec porttitor velit vitae tincidunt faucibus. Aliquam vitae semper nisi. Morbi ultrices, leo non pretium dapibus, dui libero pellentesque ex, vel placerat enim ante vitae dui. Nunc varius, sem sit amet sagittis lobortis, lectus odio scelerisque mauris, ut vestibulum orci magna quis neque. Sed id congue justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris congue nisi est, malesuada mollis elit tincidunt sed. Curabitur sed ex sit amet felis tristique elementum vitae vel nibh.
|
||||
|
||||
Etiam mollis pretium lobortis. Mauris augue lacus, efficitur at lacus sed, mollis tincidunt lectus. Aliquam erat volutpat. Donec at euismod elit, et mattis felis. Sed id lobortis urna. Morbi imperdiet vestibulum leo, sed maximus leo blandit eu. Aliquam semper lorem neque, nec euismod turpis mattis mollis. Quisque lobortis urna ultrices odio pretium, ac venenatis orci faucibus. Suspendisse bibendum odio ligula, sed lobortis massa pharetra nec. Donec turpis justo, iaculis at dictum ac, finibus eu libero. Maecenas quis porttitor mi, sit amet aliquet neque.
|
||||
|
||||
Vivamus auctor vulputate ante, at egestas lorem. Donec eu risus in nulla mollis ultricies at et urna. Duis accumsan porta egestas. Ut vel euismod augue. Fusce convallis nulla ante, nec fringilla velit aliquet at. Nam malesuada dapibus ligula, a aliquam nibh scelerisque ac. Praesent malesuada neque et pellentesque interdum. Curabitur volutpat at turpis vitae tristique. Vivamus porttitor semper congue. Quisque suscipit lacus mi, rhoncus ultrices tortor auctor quis. Maecenas neque neque, molestie ac facilisis eget, luctus ac lorem. In ut odio ut lacus suscipit pulvinar vitae sed elit. Nulla imperdiet, sem quis euismod sagittis, dui erat luctus dolor, faucibus faucibus erat sem eget nunc. Nam accumsan placerat malesuada. Maecenas convallis finibus pulvinar.
|
||||
|
||||
Cras at placerat tortor. Morbi facilisis auctor felis sit amet molestie. Donec sodales sed lorem vitae suscipit. Etiam fermentum pharetra ipsum, nec luctus orci gravida eu. Pellentesque gravida, est non condimentum tempus, mauris ligula molestie est, in congue dolor nisl vel sapien. Duis congue tempor augue, id rutrum eros porta dapibus. Etiam rutrum eget est eget vestibulum. Aenean mollis arcu vel consequat varius. Praesent at condimentum felis. Duis nec interdum nisl. Donec commodo lorem sed sapien scelerisque malesuada non eu urna. In blandit non ipsum at porta. Nam lobortis leo vitae dui auctor, non feugiat quam bibendum. Donec auctor lectus sagittis laoreet maximus. Maecenas rhoncus laoreet porttitor. Vestibulum porttitor augue ut lectus hendrerit, eget posuere mi gravida.
|
||||
|
||||
Sed mattis ex in erat pulvinar, eu imperdiet magna dapibus. Etiam nisi nibh, tempus non tellus sit amet, mattis tempor odio. Quisque nec lorem feugiat, lobortis odio et, commodo nunc. Maecenas semper purus nisi, nec vehicula nibh eleifend vitae. Nulla fermentum a lectus at maximus. Phasellus finibus metus non euismod ultrices. Etiam a pulvinar ante. Quisque convallis nec metus sit amet facilisis. Praesent laoreet massa et sollicitudin laoreet. Vestibulum in mauris aliquet, convallis mi ut, elementum purus. Nulla purus nulla, sodales at hendrerit quis, tempus sed lectus.
|
||||
|
||||
Nam ut laoreet neque, ut maximus nibh. Maecenas quis justo pellentesque, sollicitudin elit at, venenatis velit. Aenean nunc velit, vehicula scelerisque odio at, consectetur laoreet purus. Duis dui purus, malesuada quis ipsum sit amet, tempor interdum libero. Curabitur porta scelerisque sapien, vitae cursus diam condimentum eu. Phasellus sed orci quam. Nullam vitae dui quis purus tincidunt vestibulum. Curabitur quis nulla porta, cursus arcu non, auctor enim. Etiam sollicitudin ex id sem vehicula mollis. Morbi viverra laoreet tincidunt. Praesent ut semper dui. Nam sit amet pretium neque. Mauris vitae luctus diam, in lacinia purus. Maecenas ut placerat justo, ut porta felis. Integer eu mauris ante.
|
||||
|
||||
Aenean porttitor tellus diam, tempor consequat metus efficitur id. Suspendisse ut felis at erat tempor dictum at nec sapien. Sed vestibulum interdum felis, ac mattis mauris porta in. Nunc et condimentum massa. Sed cursus dictum justo et luctus. Integer convallis enim nisl, a rutrum lectus ultricies in. Donec dapibus lacus at nulla dapibus, id sollicitudin velit hendrerit. Fusce a magna at orci mollis rutrum ac a dolor. Aliquam erat volutpat. Morbi varius porta nunc, sit amet sodales ex hendrerit commodo. Donec tincidunt tortor sapien, vitae egestas sapien vehicula eget.
|
||||
|
||||
Suspendisse potenti. Donec pulvinar felis nec leo malesuada interdum. Integer posuere placerat maximus. Donec nibh ipsum, tincidunt vitae luctus vitae, bibendum at leo. Sed cursus nisl ut ex faucibus aliquet sed nec eros. Curabitur molestie posuere felis. Integer faucibus velit eget consequat iaculis. Mauris sed vulputate odio. Phasellus maximus, elit a pharetra egestas, lorem magna semper tellus, vestibulum semper diam felis at sapien. Suspendisse facilisis, nisl sit amet euismod vehicula, libero nulla vehicula dolor, quis fermentum nibh elit sit amet diam.
|
||||
|
||||
Morbi lorem enim, euismod eu varius ut, scelerisque quis odio. Nam tempus vitae eros id molestie. Nunc pretium in nulla eget accumsan. Quisque mattis est ut semper aliquet. Maecenas eget diam elementum, fermentum ipsum a, euismod sapien. Duis quam ligula, cursus et velit nec, ullamcorper tincidunt magna. Donec vulputate nisl est, et ullamcorper urna tempor sit amet.
|
||||
|
||||
Proin lacinia dui non turpis congue pretium. Morbi posuere metus vel purus imperdiet interdum. Morbi venenatis vel eros non ultricies. Nulla vel semper elit. Ut quis purus tincidunt, auctor justo ut, faucibus turpis. Proin quis mattis erat, at faucibus ligula. Mauris in mauris enim. Donec facilisis enim at est feugiat hendrerit. Nam vel nisi lorem. Fusce ultricies convallis diam, in feugiat tortor luctus quis. Donec tempor, leo vitae volutpat aliquam, magna elit feugiat leo, quis placerat sapien felis eget arcu. Donec ornare fermentum eleifend. Integer a est orci.
|
||||
|
||||
Proin rhoncus egestas leo. Nulla ultricies porta elit quis ornare. Nunc fermentum interdum vehicula. In in ligula lorem. Donec nec arcu sit amet orci lobortis iaculis. Mauris at mollis erat, sit amet mollis tortor. Mauris laoreet justo ullamcorper porttitor auctor. Aenean sit amet aliquam lectus, id fermentum eros. Praesent urna sem, vehicula ac fermentum id, dapibus ut purus. Vestibulum vitae tempus nunc. Donec at nunc ornare metus volutpat porta at eget magna. Donec varius aliquet metus, eu lobortis risus aliquam sed. Ut dapibus fermentum velit, ac tincidunt libero faucibus at.
|
||||
|
||||
In in purus auctor, feugiat massa quis, facilisis nisi. Donec dolor purus, gravida eget dolor ac, porttitor imperdiet urna. Donec faucibus placerat erat, a sagittis ante finibus ac. Sed venenatis dignissim elit, in iaculis felis posuere faucibus. Praesent sed viverra dolor. Mauris sed nulla consectetur nunc laoreet molestie in ut metus. Proin ac ex sit amet magna vulputate hendrerit ac condimentum urna. Proin ligula metus, gravida et sollicitudin facilisis, iaculis ut odio. Cras tincidunt urna et augue varius, ut facilisis urna consequat. Aenean vehicula finibus quam. Ut iaculis eu diam ac mollis. Nam mi lorem, tristique eget varius at, sodales at urna.
|
||||
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin vitae dictum erat, et auctor ipsum. Nullam nunc nunc, sollicitudin quis magna a, vestibulum fermentum mauris. Praesent at erat dolor. Proin laoreet tristique nulla vel efficitur. Nam sed ultrices nibh, id rutrum nunc. Curabitur eleifend a erat sit amet sollicitudin. Nullam metus quam, laoreet vitae dapibus id, placerat sed leo. Aliquam erat volutpat. Donec turpis nisl, cursus eu ex sit amet, lacinia pellentesque nisl. Sed id ipsum massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec interdum scelerisque lorem eu mattis.
|
||||
|
||||
Vivamus ac tristique massa, nec facilisis nisl. Nam ipsum neque, tincidunt vel urna in, cursus imperdiet enim. Nam pellentesque egestas tempus. Morbi facilisis imperdiet libero vitae fringilla. Nam lacinia ligula at sapien facilisis malesuada. Nullam accumsan pulvinar sem, et cursus libero porta sit amet. Curabitur vulputate erat elit, ut pulvinar erat maximus vel.
|
||||
|
||||
Cras aliquet metus ut purus sagittis, vel venenatis ante consectetur. Pellentesque nulla lacus, viverra viverra mattis non, placerat vitae nibh. Donec enim turpis, accumsan sit amet tincidunt eu, imperdiet non metus. Morbi ipsum eros, tincidunt vel est ac, tristique porttitor nibh. Praesent ut ullamcorper mauris. Sed laoreet sit amet diam congue venenatis. Integer porta purus nec orci sagittis posuere.
|
||||
|
||||
Donec vehicula mauris eget lacus mollis venenatis et sed nibh. Nam sodales ligula ipsum, scelerisque lacinia ligula sagittis in. Nam sit amet ipsum at erat malesuada congue. Aenean ut sollicitudin sapien. Etiam at tempor odio. Mauris vitae purus ut magna suscipit consequat. Vivamus quis sapien neque. Nulla vulputate sem sit amet massa pellentesque, eleifend tristique ligula egestas. Suspendisse tincidunt gravida mi, in pulvinar lectus egestas non. Aenean imperdiet ex sit amet nunc sollicitudin porta. Integer justo odio, ultricies at interdum in, rhoncus vitae sem. Sed porttitor arcu quis purus aliquet hendrerit. Praesent tempor tortor at dolor dictum pulvinar. Nulla aliquet nunc non ligula scelerisque accumsan. Donec nulla justo, congue vitae massa in, faucibus hendrerit magna. Donec non egestas purus.
|
||||
|
||||
Vivamus iaculis, lacus efficitur faucibus porta, dui nulla facilisis ligula, ut sodales odio nunc id sapien. Cras viverra auctor ipsum, dapibus mattis neque dictum sed. Sed convallis fermentum molestie. Nulla facilisi turpis duis.
|
||||
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur vulputate, ipsum quis interdum fermentum, lorem sem fermentum eros, vitae auctor neque lacus in nisi. Suspendisse potenti. Maecenas et scelerisque elit, in tincidunt quam. Sed eu tincidunt quam. Nullam justo ex, imperdiet a imperdiet et, fermentum sit amet eros. Aenean quis tempus sem. Pellentesque accumsan magna mi, ut mollis velit sagittis id. Etiam quis ipsum orci. Fusce purus ante, accumsan a lobortis at, venenatis eu nisl. Praesent ornare sed ante placerat accumsan. Suspendisse tempus dignissim fermentum. Nunc a leo ac lacus sodales iaculis eu vitae mi. In feugiat ante at massa finibus cursus. Suspendisse posuere fringilla ornare. Mauris elementum ac quam id convallis. Vestibulum non elit quis urna volutpat aliquam a eu lacus.
|
||||
|
||||
Aliquam vestibulum imperdiet neque, suscipit aliquam elit ultrices bibendum. Suspendisse ultrices pulvinar cursus. Morbi risus nisi, cursus consequat rutrum vitae, molestie sed dui. Fusce posuere, augue quis dignissim aliquam, nisi ipsum porttitor ante, quis fringilla nisl turpis ac nisi. Nulla varius enim eget lorem vehicula gravida. Donec finibus malesuada leo nec semper. Proin ac enim eros. Vivamus non tincidunt nisi, vel tristique lorem.
|
||||
|
||||
Nunc consequat ex id eros dignissim, id rutrum risus laoreet. Sed euismod non erat eu ultricies. Etiam vehicula gravida lacus ut porta. Vestibulum eu eros quis nunc aliquet luctus. Cras quis semper ligula. Nullam gravida vehicula quam sed porta. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. In porta cursus vulputate. Quisque porta a nisi eget cursus. Aliquam risus leo, luctus ac magna in, efficitur cursus magna. In condimentum non mi id semper. Donec interdum ante eget commodo maximus.
|
||||
|
||||
Vivamus sit amet vestibulum lectus. Fusce tincidunt mi sapien, dictum sollicitudin diam vulputate in. Integer fringilla consequat mollis. Cras aliquet consequat felis eget feugiat. Nunc tempor cursus arcu, vitae ornare nunc varius et. Vestibulum et tortor vel ante viverra porttitor. Nam at tortor ullamcorper, facilisis augue quis, tristique erat. Aenean ut euismod nibh. Quisque eu tincidunt est, nec euismod eros.
|
||||
|
||||
Proin vehicula nibh non viverra egestas. Phasellus sem dolor, ultricies ac sagittis tristique, lacinia a purus. Vestibulum in ante eros. Pellentesque lacus nulla, tristique vitae interdum vel, malesuada ac diam. Aenean bibendum posuere turpis in accumsan. Ut est nulla, ullamcorper quis turpis at, viverra sagittis mauris. Sed in interdum purus. Praesent scelerisque nibh eget sem euismod, ut imperdiet mi venenatis. Vivamus pulvinar orci sed dapibus auctor. Nulla facilisi. Vestibulum tincidunt erat nec porttitor egestas. Mauris quis risus ante. Nulla facilisi.
|
||||
|
||||
Aliquam ullamcorper ornare lobortis. Phasellus quis sem et ipsum mollis malesuada sed in ex. Ut aliquam ex eget metus finibus maximus. Proin suscipit mauris eu nibh lacinia, quis feugiat dui dapibus. Nam sed libero est. Aenean vulputate orci sit amet diam faucibus, eu sagittis sapien volutpat. Nam imperdiet felis turpis, at pretium odio pulvinar in. Sed vestibulum id eros nec ultricies. Sed quis aliquam tortor, vitae ullamcorper tellus. Donec egestas laoreet eros, id suscipit est rutrum nec. Sed auctor nulla eget metus aliquam, ut condimentum enim elementum.
|
||||
|
||||
Aliquam suscipit non turpis sit amet bibendum. Fusce velit ligula, euismod et maximus at, luctus sed neque. Quisque pretium, nisl at ullamcorper finibus, lectus leo mattis sapien, vel euismod mauris diam ullamcorper ex. Nulla ut risus finibus, lacinia ligula at, auctor erat. Mauris consectetur sagittis ligula vel dapibus. Nullam libero libero, lobortis aliquam libero vel, venenatis ultricies leo. Duis porttitor, nibh congue fermentum posuere, erat libero pulvinar tortor, a pellentesque nunc ipsum vel sem. Nullam volutpat, eros sit amet facilisis consectetur, ipsum est vehicula massa, non vestibulum neque elit in mauris. Nunc hendrerit ipsum non enim bibendum, vitae rhoncus mi egestas. Etiam ullamcorper massa vel nisl sagittis, nec bibendum arcu malesuada. Aenean aliquet turpis justo, a consectetur arcu mollis convallis. Etiam tellus ipsum, ultricies vitae lorem et, ornare facilisis orci. Praesent fringilla justo urna, vel mollis neque pulvinar vestibulum.
|
||||
|
||||
Donec non iaculis erat. Aliquam et mi sed nunc pulvinar ultricies in ut ipsum. Interdum et malesuada fames ac ante ipsum primis in faucibus. Praesent feugiat lacus ac dignissim semper. Phasellus vitae quam nisi. Morbi vel diam ultricies risus lobortis ornare. Fusce maximus et ligula quis iaculis. Sed congue ex eget felis convallis, sit amet hendrerit elit tempor. Donec vehicula blandit ante eget commodo. Vestibulum eleifend diam at feugiat euismod. Etiam magna tellus, dignissim eget fermentum vel, vestibulum vitae mauris. Nam accumsan et erat id sagittis. Donec lacinia, odio ut ornare ultricies, dolor velit accumsan tortor, non finibus erat tellus quis ligula. Nunc quis metus in leo volutpat ornare vulputate eu nisl.
|
||||
|
||||
Donec quis viverra ex. Nullam id feugiat mauris, eu fringilla nulla. Vestibulum id maximus elit. Cras elementum elit sed felis lobortis, eget sagittis nisi hendrerit. Vivamus vitae elit neque. Donec vulputate lacus ut libero ultrices accumsan. Vivamus accumsan nulla orci, in dignissim est laoreet sagittis. Proin at commodo velit. Curabitur in velit felis. Aliquam erat volutpat. Sed consequat, nulla et cursus sodales, nisi lacus mattis risus, quis eleifend erat ex nec turpis. Sed suscipit ultrices lorem in hendrerit.
|
||||
|
||||
Morbi vitae lacus nec libero ornare tempus eu et diam. Suspendisse magna ipsum, fermentum vel odio quis, molestie aliquam urna. Fusce mollis turpis a eros accumsan porttitor. Pellentesque rhoncus dolor sit amet magna rutrum, et dapibus justo tempor. Sed purus nisi, maximus vitae fringilla eu, molestie nec urna. Fusce malesuada finibus pretium. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec sed aliquet eros. Pellentesque luctus diam ante, eget euismod nisl aliquet eu. Sed accumsan elit purus, tempor varius ligula tempus nec. Curabitur ornare leo suscipit suscipit fermentum. Morbi eget nulla est. Maecenas faucibus interdum tristique.
|
||||
|
||||
Etiam ut elit eros. Nulla pharetra suscipit molestie. Nulla facilisis bibendum nisl non molestie. Curabitur turpis lectus, facilisis vel diam non, vulputate ultrices mauris. Aenean placerat aliquam convallis. Suspendisse sed scelerisque tellus. Vivamus lacinia neque eget risus cursus suscipit. Proin consequat dolor vel neque tempor, eu aliquam sem scelerisque. Duis non eros a purus malesuada pharetra non et nulla. Suspendisse potenti. Mauris libero eros, finibus vel nulla id, sagittis dapibus ante. Proin iaculis sed nunc et cursus.
|
||||
|
||||
Quisque accumsan lorem sit amet lorem aliquet euismod. Curabitur fermentum rutrum posuere. Etiam ultricies, sem id pellentesque suscipit, urna magna lacinia eros, quis efficitur risus nisl at lacus. Nulla quis lacus tortor. Mauris placerat ex in dolor tincidunt, vel aliquet nisi pretium. Cras iaculis risus vitae pellentesque aliquet. Quisque a enim imperdiet, ullamcorper arcu vitae, rutrum risus. Nullam consectetur libero at felis fringilla, nec congue nibh dignissim. Nam et lobortis felis, eu pellentesque ligula. Aenean facilisis, ligula non imperdiet maximus, massa orci gravida sapien, at sagittis lacus nisl in lacus. Nulla quis mauris luctus, scelerisque felis consequat, tempus risus. Fusce auctor nisl non nulla luctus molestie. Maecenas sapien nisl, auctor non dolor et, iaculis scelerisque lorem. Suspendisse egestas enim aliquet, accumsan mauris nec, posuere quam. Nulla iaculis dui dui, sit amet vestibulum erat ultricies ac.
|
||||
|
||||
Cras eget dolor erat. Proin at nisl ut leo consectetur ultricies vel ut arcu. Nulla in felis malesuada, ullamcorper tortor et, convallis massa. Nunc urna justo, ornare in nibh vitae, hendrerit condimentum libero. Etiam vitae libero in purus venenatis fringilla. Nullam velit nulla, consequat ut turpis non, egestas hendrerit nibh. Duis tortor turpis, interdum non ante ac, cursus accumsan lectus. Cras pharetra bibendum augue quis dictum. Sed euismod vestibulum justo. Proin porta lobortis purus. Duis venenatis diam tortor, sit amet condimentum eros rhoncus a. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Nunc at magna nec diam lobortis efficitur sit amet ut lacus. Nulla quis orci tortor. Pellentesque tempus velit a odio finibus porta.
|
||||
|
||||
Proin feugiat mauris a tellus scelerisque convallis. Maecenas libero magna, blandit nec ultrices id, congue vel mi. Aliquam lacinia, quam vel condimentum convallis, tortor turpis aliquam odio, sed blandit libero lacus et eros. In eleifend iaculis magna ac finibus. Praesent auctor facilisis tellus in congue. Sed molestie lobortis dictum. Nam quis dignissim augue, vel euismod lorem. Curabitur posuere dapibus luctus. Donec ultricies dictum lectus, quis blandit arcu commodo ac. Aenean tincidunt ligula in nunc imperdiet dignissim. Curabitur egestas sollicitudin sapien ut semper. Aenean nec dignissim lacus.
|
||||
|
||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec aliquam dictum vehicula. Donec tortor est, volutpat non nisi nec, varius gravida ex. Nunc vel tristique nunc, vitae mattis nisi. Nunc nec luctus ex, vitae tincidunt lectus. In hac habitasse platea dictumst. Curabitur lobortis ex eget tincidunt tempor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut a vehicula mi.
|
||||
|
||||
Fusce eu libero finibus, interdum nulla a, placerat neque. Cras bibendum tempor libero nec feugiat. Cras ut sodales eros. Proin viverra, massa sit amet viverra egestas, neque nisl porta ex, sit amet hendrerit libero ligula vel urna. Mauris suscipit lacus id justo rhoncus suscipit. Etiam vel libero tellus. Maecenas non diam molestie, condimentum tellus a, bibendum enim. Mauris aliquet imperdiet tellus, eget sagittis dolor. Sed blandit in neque et luctus. Cras elementum sagittis nunc, vel mollis lorem euismod et. Donec posuere at lacus eget suscipit.
|
||||
|
||||
Nulla nunc mi, pretium non massa vel, tempor semper magna. Nunc a leo pulvinar, tincidunt nunc at, dignissim mi. Aliquam erat volutpat. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut viverra nulla a nisl finibus, at hendrerit ligula ullamcorper. Donec a lorem semper, tempor magna et, lobortis libero. Mauris id sapien leo. Donec dignissim, quam vitae porttitor dignissim, quam justo mattis dui, vel consequat odio elit quis orci. Etiam nec pretium neque, sit amet pretium orci. Duis ac tortor venenatis, feugiat purus non, feugiat nunc. Proin scelerisque nisl in turpis aliquam vulputate.
|
||||
|
||||
Praesent sed est semper, fringilla lorem vitae, tincidunt nibh. Cras eros metus, auctor at mauris sit amet, sodales semper orci. Nunc a ornare ex. Curabitur bibendum arcu congue urna vulputate egestas. Vestibulum finibus id risus et accumsan. Aenean ut volutpat tellus. Aenean tincidunt malesuada urna sit amet vestibulum. Mauris vel tellus dictum, varius lacus quis, dictum arcu.
|
||||
|
||||
Aenean quis metus eu erat feugiat cursus vel at ligula. Proin dapibus sodales urna, id euismod lectus tempus id. Pellentesque ex ligula, convallis et erat vel, vulputate condimentum nisl. Pellentesque pharetra nulla quis massa eleifend hendrerit. Praesent sed massa ipsum. Maecenas vehicula dolor massa, id sodales urna faucibus et. Mauris ac quam non massa tincidunt feugiat et at lacus. Fusce libero massa, vulputate vel scelerisque non, mollis in leo. Ut sit amet ultricies odio. Suspendisse in sapien viverra, facilisis purus ut, pretium libero.
|
||||
|
||||
Vivamus tristique pharetra molestie. Nam a volutpat purus. Praesent consequat gravida nisi, ac blandit nisi suscipit ut. Quisque posuere, ligula a ultrices laoreet, ligula nunc vulputate libero, ut rutrum erat odio tincidunt justo. Sed vitae leo at leo fringilla bibendum. Vestibulum ut augue nec dolor auctor accumsan. Praesent laoreet id eros pulvinar commodo. Suspendisse potenti. Ut pharetra, mauris vitae blandit fringilla, odio ante tincidunt lorem, sit amet tempor metus diam ut turpis.
|
||||
|
||||
Praesent quis egestas arcu. Nullam at porta arcu. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi vulputate ligula malesuada ligula luctus, vulputate tempus erat bibendum. Nunc ullamcorper non lectus at euismod. Etiam nibh felis, tincidunt a metus vel, pellentesque rhoncus neque. Etiam at diam in erat luctus interdum. Nunc vel ipsum pulvinar, sollicitudin lacus ac, tempus urna. Etiam vel lacinia sapien. Pellentesque sagittis velit vel mi efficitur iaculis. Integer euismod sit amet urna in sagittis. Cras eleifend ut nibh in facilisis. Donec et lacus vitae nunc placerat sodales. Nulla sed hendrerit ligula, at dapibus sapien.
|
||||
|
||||
Praesent at iaculis ex. Curabitur est purus, cursus a faucibus quis, dictum id velit. Donec dignissim fringilla viverra. Nunc mauris felis, laoreet sit amet sagittis at, vestibulum in libero. Maecenas quis orci turpis. Quisque ut nibh vitae magna mollis consequat id at mauris. Aliquam eu odio eget nulla bibendum sodales. Quisque vel orci eleifend nisi pretium lacinia. Suspendisse eget risus eget mi volutpat molestie eget quis lacus. Duis nisi libero, tincidunt nec nulla id, faucibus cursus felis.
|
||||
|
||||
Donec tempor eget risus pellentesque molestie. Phasellus porta neque vel arcu egestas, nec blandit velit fringilla. Nullam porta faucibus justo vitae laoreet. Pellentesque viverra id nunc eu varius. Nulla pulvinar lobortis iaculis. Etiam vestibulum odio nec velit tristique, a tristique nisi mattis. In sed fringilla orci, vitae efficitur odio. Quisque dui odio, ornare eget velit at, lacinia consequat libero. Quisque lectus nulla, aliquet eu leo in, porta rutrum diam. Donec nec mattis neque. Nam rutrum, odio ac eleifend bibendum, dolor arcu rutrum neque, eget porta elit tellus a lacus. Sed massa metus, sollicitudin et sapien eu, finibus tempus orci. Proin et sapien sit amet erat molestie interdum. In quis rutrum velit, faucibus ultrices tellus.
|
||||
|
||||
Sed sagittis sed justo eget tincidunt. Maecenas ut leo sagittis, feugiat magna et, viverra velit. Maecenas ex arcu, feugiat at consequat vitae, auctor eu massa. Integer egestas, enim vitae maximus convallis, est lectus pretium mauris, ac posuere lectus nisl quis quam. Aliquam tempus laoreet mi, vitae dapibus dolor varius dapibus. Suspendisse potenti. Donec sit amet purus nec libero dapibus tristique. Pellentesque viverra bibendum ligula. Donec sed felis et ex lobortis laoreet. Phasellus a fringilla libero, vitae malesuada nulla. Pellentesque blandit mattis lacus, et blandit tortor laoreet consequat. Suspendisse libero nunc, viverra sed fermentum in, accumsan egestas arcu. Proin in placerat elit. Sed interdum imperdiet malesuada. Suspendisse aliquet quis mauris eget sollicitudin.
|
||||
|
||||
Vivamus accumsan tellus non erat volutpat, quis dictum dolor feugiat. Praesent rutrum nunc ac est mollis cursus. Fusce semper volutpat dui ut egestas. Curabitur sit amet posuere massa. Cras tincidunt nulla et mi mollis imperdiet. Suspendisse scelerisque ex id sodales vulputate. In nunc augue, pharetra in placerat eu, mattis id tellus. Vivamus cursus efficitur vehicula. Nulla aliquet vehicula aliquet.
|
||||
|
||||
Sed cursus tellus sed porta pulvinar. Sed vitae nisi neque. Nullam aliquet, lorem et efficitur scelerisque, arcu diam aliquam felis, sed pulvinar lorem odio et turpis. Praesent convallis pulvinar turpis eu iaculis. Aliquam nec gravida mi. Curabitur eu nibh tempor, blandit justo in, ultrices felis. Fusce placerat metus non mi sagittis rutrum. Morbi sed dui fringilla, sagittis mauris eget, imperdiet nunc. Phasellus hendrerit sem elit, id hendrerit libero auctor sit amet. Integer sodales elit sit amet consequat cursus.
|
||||
|
||||
Nam semper est eget nunc mollis, in pellentesque lectus fringilla. In finibus vel diam id semper. Nunc mattis quis erat eu consectetur. In hac habitasse platea dictumst. Nullam et ipsum vestibulum ex pulvinar ultricies sit amet id velit. Aenean suscipit mi tortor, a lobortis magna viverra non. Nulla condimentum aliquet ante et ullamcorper. Pellentesque porttitor arcu a posuere tempus. Aenean lacus quam, imperdiet eu justo vitae, pretium efficitur ex. Duis id purus id magna rhoncus ultrices id eu risus. Nunc dignissim et libero id dictum.
|
||||
|
||||
Quisque a tincidunt neque. Phasellus commodo mi sit amet tempor fringilla. Ut rhoncus, neque non porttitor elementum, libero nulla egestas augue, sed fringilla sapien felis ac velit. Phasellus viverra rhoncus mollis. Nam ullamcorper leo vel erat laoreet luctus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Vivamus semper a metus a cursus. Nulla sed orci egestas, efficitur purus ac, malesuada tellus. Aenean rutrum velit at tellus fermentum mollis. Aliquam eleifend euismod metus.
|
||||
|
||||
In hac habitasse platea dictumst. Vestibulum volutpat neque vitae porttitor laoreet. Nam at tellus consequat, sodales quam in, pulvinar arcu. Maecenas varius convallis diam, ac lobortis tellus pellentesque quis. Maecenas eget augue massa. Nullam volutpat nibh ac justo rhoncus, ut iaculis tellus rutrum. Fusce efficitur efficitur libero quis condimentum. Curabitur congue neque non tincidunt tristique. Fusce eget tempor ex, at pellentesque odio. Praesent luctus dictum vestibulum. Etiam non orci nunc. Vivamus vitae laoreet purus, a lobortis velit. Curabitur tincidunt purus ac lectus elementum pellentesque. Quisque sed tincidunt est.
|
||||
|
||||
Sed vel ultrices massa, vitae ultricies justo. Cras finibus mauris nec lacus tempus dignissim. Cras faucibus maximus velit, eget faucibus orci luctus vehicula. Nulla massa nunc, porta ac consequat eget, rhoncus non tellus. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Fusce sed maximus metus, vel imperdiet ipsum. Ut scelerisque lectus at blandit porttitor. Ut vulputate nunc pharetra, aliquet sapien ac, sollicitudin sapien. Aenean eget ante lorem. Nam accumsan venenatis tellus id dignissim.
|
||||
|
||||
Curabitur fringilla, magna non maximus dapibus, nulla sapien vestibulum lectus, sit amet semper dolor neque vitae nisl. Nunc ultrices vehicula augue sed iaculis. Maecenas nec diam mollis, suscipit orci et, vestibulum ante. Pellentesque eu nisl tortor. Nunc eleifend, lacus quis volutpat volutpat, nisi mi molestie sem, quis mollis ipsum libero a tellus. Ut viverra dolor mattis convallis interdum. Sed tempus nisl at nunc scelerisque aliquet. Quisque tempor tempor lorem id feugiat. Nullam blandit lectus velit, vitae porta lacus tincidunt a. Vivamus sit amet arcu ultrices, tincidunt mi quis, viverra quam. Aenean fringilla libero elementum lorem semper, quis pulvinar eros gravida. Nullam sodales blandit mauris, sed fermentum velit fermentum sit amet. Donec malesuada mauris in augue sodales vulputate. Vestibulum gravida turpis id elit rhoncus dignissim. Integer non congue lorem, eu viverra orci.
|
||||
|
||||
Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec at dolor magna. Aliquam consectetur erat augue, id iaculis velit pharetra ac. Integer rutrum venenatis dignissim. Integer non sodales elit. Curabitur ut magna ut nibh feugiat aliquam ac ut risus. Morbi nibh quam, aliquam id placerat nec, vestibulum eget velit. Suspendisse at dignissim quam. Vivamus aliquet sem sed nisl volutpat, ut cursus orci ultrices. Aliquam ultrices lacinia enim, vitae aliquet neque.
|
||||
|
||||
Quisque scelerisque finibus diam in mattis. Cras cursus auctor velit. Aliquam sem leo, fermentum et maximus et, molestie a libero. Aenean justo elit, rutrum a ornare id, egestas eget enim. Aenean auctor tristique erat. Curabitur condimentum libero lacus, nec consequat orci vestibulum sed. Fusce elit ligula, blandit vitae sapien vitae, dictum ultrices risus. Nam laoreet suscipit sapien, at interdum velit faucibus sit amet. Duis quis metus egestas lectus elementum posuere non nec libero. Aliquam a dolor bibendum, facilisis nunc a, maximus diam. Vestibulum suscipit tristique magna, non dignissim turpis sodales sed. Nunc ornare, velit ac facilisis fringilla, dolor mi consectetur lorem, vitae finibus erat justo suscipit urna. Maecenas sit amet eros erat. Nunc non arcu ornare, suscipit lorem eget, sodales mauris. Aliquam tincidunt, quam nec mollis lacinia, nisi orci fermentum libero, consequat eleifend lectus quam et sapien. Vestibulum a quam urna.
|
||||
|
||||
Cras arcu leo, euismod ac ullamcorper at, faucibus sed massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus porttitor velit in enim interdum, non commodo metus ornare. Morbi vel lorem quis nisl luctus tristique quis vitae nisl. Suspendisse condimentum tortor enim, nec eleifend ipsum euismod et. Sed gravida quam ut tristique lacinia. Mauris eu interdum ipsum, ac ultrices odio. Nullam auctor tellus a risus porttitor vehicula. Nulla blandit euismod dictum. In pharetra, enim iaculis pulvinar interdum, dui nunc placerat nunc, sit amet pretium lectus nulla vitae quam. Phasellus quis enim sollicitudin, varius nulla id, ornare purus. Donec quam lacus, vestibulum quis nunc ac, mollis dictum nisi. Cras ut mollis elit. Maecenas ultrices ligula at risus faucibus scelerisque. Etiam vitae porttitor purus. Curabitur blandit lectus urna, ut hendrerit tortor feugiat ut.
|
||||
|
||||
Phasellus fringilla, sapien pellentesque commodo pharetra, ante libero aliquam tellus, ut consectetur augue libero a sapien. Maecenas blandit luctus nisl eget aliquet. Maecenas vitae porta dolor, faucibus laoreet sapien. Suspendisse lobortis, ipsum sed vehicula aliquam, elit purus scelerisque dui, rutrum consectetur diam odio et lorem. In nec lacinia metus. Donec viverra libero est, vel bibendum erat condimentum quis. Donec feugiat purus leo. In laoreet vitae felis a porttitor. Mauris ullamcorper, lacus id condimentum suscipit, neque magna pellentesque arcu, eget cursus neque tellus id metus. Curabitur volutpat ac orci vel ultricies.
|
||||
|
||||
Sed ut finibus erat. Sed diam purus, varius non tincidunt quis, ultrices sit amet ipsum. Donec et egestas nulla. Suspendisse placerat nisi at dui laoreet iaculis. Aliquam aliquet leo at augue faucibus molestie. Nullam lacus augue, hendrerit sed nisi eu, faucibus porta est. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Nam ut leo aliquet sem fermentum rutrum quis ac justo. Integer placerat aliquam nisl ut sagittis. Proin erat orci, lobortis et sem eget, eleifend fringilla augue. Mauris varius laoreet arcu, sed tincidunt felis. Pellentesque venenatis lorem odio, id pulvinar velit molestie feugiat. Donec mattis lacus sed eleifend pulvinar.
|
||||
|
||||
Sed condimentum ex in tincidunt hendrerit. Etiam eget risus lacinia, euismod nibh eu, pellentesque quam. Proin elit eros, convallis id mauris ac, bibendum ultrices lectus. Morbi venenatis, purus id fermentum consequat, nunc libero tincidunt ligula, non dictum ligula orci nec quam. Nulla nec ultrices lorem. Aenean maximus augue vel dictum pharetra. Etiam turpis urna, pellentesque quis malesuada eu, molestie faucibus felis.
|
||||
|
||||
Vestibulum pharetra augue ut quam blandit congue in nec risus. Proin eu nibh eu dui eleifend porta vitae id lectus. Proin lacus nibh, lobortis sed ligula vitae, interdum lobortis erat. Suspendisse potenti. In sollicitudin quis sapien ut aliquet. Mauris ac nulla arcu. Fusce tristique justo quis lectus mollis, eu volutpat lectus finibus. Vivamus venenatis facilisis ex ut vestibulum.
|
||||
|
||||
Etiam varius lobortis purus, in hendrerit elit tristique at. In tempus, augue vestibulum fermentum gravida, ligula tellus vulputate arcu, eu molestie ex sapien at purus. Vestibulum nec egestas metus. Duis pulvinar quam nec consequat interdum. Aenean non dapibus lacus. Aliquam sit amet aliquet nulla. Sed venenatis volutpat purus nec convallis. Phasellus aliquet semper sodales. Cras risus sapien, condimentum auctor urna a, pulvinar ornare nisl. Sed tincidunt felis elit, ut elementum est bibendum ac. Morbi interdum justo vel dui faucibus condimentum.
|
||||
|
||||
Sed convallis eu sem at tincidunt. Nullam at auctor est, et ullamcorper ipsum. Pellentesque eget ante ante. Interdum et malesuada fames ac ante ipsum primis in faucibus. Integer euismod, sapien sed dapibus ornare, nibh enim maximus lacus, lacinia placerat urna quam quis felis. Morbi accumsan id nisl ut condimentum. Donec bibendum nisi est, sed volutpat lorem rhoncus in. Vestibulum ac lacinia nunc, eget volutpat magna. Integer aliquam pharetra ipsum, id placerat nunc volutpat quis. Etiam urna diam, rhoncus sit amet varius vel, euismod vel sem. Nullam vel molestie urna. Vivamus ornare erat at venenatis euismod. Suspendisse potenti. Fusce diam justo, tincidunt vel sem at, commodo faucibus nisl. Duis gravida efficitur diam, vel sagittis erat pulvinar ut.
|
||||
|
||||
Quisque vel pharetra felis. Duis efficitur tortor dolor, vitae porttitor erat fermentum sed. Sed eu mi purus. Etiam dignissim tortor eu tempus molestie. Aenean pretium erat enim, in hendrerit ante hendrerit at. Sed ut risus vel nunc venenatis ultricies quis in lacus. Pellentesque vitae purus euismod, placerat risus non, ullamcorper augue. Quisque varius quam ligula, nec aliquet ex faucibus vitae. Quisque rhoncus sit amet leo tincidunt mattis. Cras id mauris eget purus pretium gravida sit amet eu augue. Aliquam dapibus odio augue, id lacinia velit pulvinar eu.
|
||||
|
||||
Mauris fringilla, tellus nec pharetra iaculis, neque nisi ultrices massa, et tincidunt sem dui sed mi. Curabitur erat lorem, venenatis quis tempus lacinia, tempus sit amet nunc. Aliquam at neque ac metus commodo dictum quis vitae justo. Phasellus eget lacus tempus, blandit lorem vel, rutrum est. Aenean pharetra sem ut augue lobortis dignissim. Sed rhoncus at nulla id ultrices. Cras id condimentum felis. In suscipit luctus vulputate. Donec tincidunt lacus nec enim tincidunt sollicitudin ut quis enim. Nam at libero urna. Praesent sit amet massa vitae massa ullamcorper vehicula.
|
||||
|
||||
Nullam bibendum augue ut turpis condimentum bibendum. Proin sit amet urna hendrerit, sodales tortor a, lobortis lectus. Integer sagittis velit turpis, et tincidunt nisi commodo eget. Duis tincidunt elit finibus accumsan cursus. Aenean dignissim scelerisque felis vel lacinia. Nunc lacinia maximus luctus. In hac habitasse platea dictumst. Vestibulum eget urna et enim tempor tempor. Nam feugiat, felis vel vestibulum tempus, orci justo viverra diam, id dapibus lorem justo in ligula.
|
||||
|
||||
Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. In ac pellentesque sem. Vestibulum lacinia magna dui, eu lacinia augue placerat et. Maecenas pulvinar congue est. Pellentesque commodo dui non pulvinar scelerisque. Etiam interdum est posuere sem bibendum, ac commodo magna dictum. Cras ipsum turpis, rhoncus nec posuere vitae, laoreet a arcu. Integer ac massa sit amet enim placerat lacinia sed ultrices arcu. Suspendisse sem nibh, luctus sit amet volutpat in, pellentesque eu metus. Ut gravida neque eget mi accumsan tempus. Nam sit amet aliquet nibh.
|
||||
|
||||
Pellentesque a purus cursus nulla hendrerit congue quis et odio. Aenean hendrerit, leo ullamcorper sagittis hendrerit, erat dui molestie quam, sed condimentum lacus risus sed tellus. Morbi a dapibus lectus, ut feugiat ex. Phasellus pretium quam et sapien mollis, vel iaculis dui dignissim. Sed ullamcorper est turpis, a viverra lorem consectetur in. Aenean aliquet nibh non cursus rutrum. Suspendisse at tristique urna, id lobortis urna. In hac habitasse platea dictumst. Phasellus libero velit, rutrum sed tellus nec, dapibus tincidunt ligula. Quisque vel dui venenatis, consequat nisl ut, lacinia ipsum. Phasellus vitae magna pellentesque, lobortis est id, faucibus quam. Nam eleifend faucibus dui vel pellentesque.
|
||||
|
||||
Etiam ut est non lacus tincidunt interdum. Maecenas sed massa urna. Quisque ut nibh tortor. Pellentesque felis ipsum, tempor finibus ipsum et, euismod pretium metus. Donec sit amet est ipsum. Quisque rhoncus justo non finibus elementum. Nulla nec lectus ac tortor placerat fringilla. Phasellus ac ultrices nunc, eu efficitur nisl. Nulla rhoncus nunc vitae ante dictum tincidunt. Nunc ultrices, massa sit amet malesuada dignissim, lectus lacus consequat sapien, non eleifend metus sem in eros. Phasellus mauris ante, dictum sit amet suscipit ac, rhoncus eget nisi. Phasellus at orci mollis, imperdiet neque eget, faucibus nulla. In at purus massa. Pellentesque quis rutrum lectus.
|
||||
|
||||
Integer eu faucibus turpis, sit amet mollis massa. Vestibulum id nulla commodo, rutrum ipsum sed, semper ante. Phasellus condimentum orci nec nibh convallis, ac maximus orci ullamcorper. Maecenas vitae sollicitudin mi. Integer et finibus lectus, et condimentum ligula. Donec elementum tristique quam vitae dapibus. Morbi euismod ipsum in tristique ullamcorper.
|
||||
|
||||
Duis fermentum non enim eu auctor. Quisque lacinia nibh vehicula nibh posuere, eu volutpat turpis facilisis. Ut ac faucibus nulla. Sed eleifend quis ex et pellentesque. Vestibulum sollicitudin in libero id fringilla. Phasellus dignissim purus consequat, condimentum dui sit amet, condimentum ante. Pellentesque ac consectetur massa, quis sagittis est. Nulla maximus tristique risus accumsan convallis. Curabitur imperdiet ac lacus a ultrices. Nulla facilisi. Sed quis quam quis lectus placerat lobortis vel sed turpis. In mollis dui id neque iaculis, ut aliquet tellus malesuada. Proin at luctus odio, vel blandit sapien. Praesent dignissim tortor vehicula libero fringilla, nec ultrices erat suscipit. Maecenas scelerisque purus in dapibus fermentum.
|
||||
|
||||
Curabitur magna odio, mattis in tortor ut, porttitor congue est. Vestibulum mollis lacinia elementum. Fusce maximus erat vitae nunc rutrum lobortis. Integer ligula eros, auctor vel elit non, posuere luctus lacus. Maecenas quis auctor massa. Ut ipsum lacus, efficitur posuere euismod et, hendrerit efficitur est. Phasellus fringilla, quam id tincidunt pretium, nunc dui sollicitudin orci, eu dignissim nisi metus ut magna. Integer lobortis interdum dolor, non bibendum purus posuere et. Donec non lectus aliquet, pretium dolor eu, cursus massa. Sed ut dui sapien. In sed vestibulum massa. Pellentesque blandit, dui non sodales vehicula, orci metus mollis nunc, non pharetra ex tellus ac est. Mauris sagittis metus et fermentum pretium. Nulla facilisi. Quisque quis ante ut nulla placerat mattis ut quis nisi.
|
||||
|
||||
Sed quis nulla ligula. Quisque dignissim ligula urna, sed aliquam purus semper at. Suspendisse potenti. Nunc massa lectus, pharetra vehicula arcu bibendum, imperdiet sodales ipsum. Nam ac sapien diam. Mauris iaculis fringilla mattis. Pellentesque tempus eros sit amet justo volutpat mollis. Phasellus ac turpis ipsum. Morbi vel ante elit. Aenean posuere quam consequat velit varius suscipit. Donec tempor quam ut nibh cursus efficitur.
|
||||
|
||||
Morbi molestie dolor nec sem egestas suscipit. Etiam placerat pharetra lectus, et ullamcorper risus tristique in. Sed faucibus ullamcorper lectus eget fringilla. Maecenas malesuada hendrerit congue. Sed eget neque a erat placerat tincidunt. Aliquam vitae dignissim turpis. Fusce at placerat magna, a laoreet lectus. Maecenas a purus nec diam gravida fringilla. Nam malesuada euismod ante non vehicula. In faucibus bibendum leo, faucibus posuere nisl pretium quis. Fusce finibus bibendum finibus. Vestibulum eu justo maximus, hendrerit diam nec, dignissim sapien. Aenean dolor lacus, malesuada quis vestibulum ac, venenatis ac ipsum. Cras a est id nunc finibus facilisis. Cras lacinia neque et interdum vehicula. Suspendisse vulputate tellus elit, eget tempor dui finibus vel.
|
||||
|
||||
Cras sed pretium odio. Proin hendrerit elementum felis in tincidunt. Nam sed turpis vel justo molestie accumsan condimentum eu nunc. Praesent lobortis euismod rhoncus. Nulla vitae euismod nibh, quis mattis mi. Fusce ultrices placerat porttitor. Duis sem ipsum, pellentesque sit amet odio a, molestie vulputate mauris.
|
||||
|
||||
Duis blandit mollis ligula, sit amet mattis ligula finibus sit amet. Nunc a leo molestie, placerat diam et, vestibulum leo. Suspendisse facilisis neque purus, nec pellentesque ligula fermentum nec. Aenean malesuada mauris lorem, eu blandit arcu pulvinar quis. Duis laoreet urna lacus, non maximus arcu rutrum ultricies. Nulla augue dolor, suscipit eu mollis eu, aliquam condimentum diam. Ut semper orci luctus, pharetra turpis at, euismod mi. Nulla leo diam, finibus sit amet purus sed, maximus dictum lorem. Integer eu mi id turpis laoreet rhoncus.
|
||||
|
||||
Integer a mauris tincidunt, finibus orci ut, pretium mauris. Nulla molestie nunc mi, id finibus lorem elementum sed. Proin quis laoreet ante. Integer nulla augue, commodo id molestie quis, rutrum ut turpis. Suspendisse et tortor turpis. Sed ut pharetra massa. Pellentesque elementum blandit sem, ut elementum tellus egestas a. Fusce eu purus nibh.
|
||||
|
||||
Cras dignissim ligula scelerisque magna faucibus ullamcorper. Proin at condimentum risus, auctor malesuada quam. Nullam interdum interdum egestas. Nulla aliquam nisi vitae felis mollis dictum. Suspendisse dapibus consectetur tortor. Ut ut nisi non sem bibendum tincidunt. Vivamus suscipit leo quis gravida dignissim.
|
||||
|
||||
Aliquam interdum, leo id vehicula mollis, eros eros rhoncus diam, non mollis ligula mi eu mauris. Sed ultrices vel velit sollicitudin tincidunt. Nunc auctor metus at ligula gravida elementum. Praesent interdum eu elit et mollis. Duis egestas quam sit amet velit dignissim consequat. Aliquam ac turpis nec nunc convallis sagittis. Fusce blandit, erat ac fringilla consectetur, dolor eros sodales leo, vel aliquet risus nisl et diam. Aliquam luctus felis vitae est eleifend euismod facilisis et lacus. Sed leo tellus, auctor eu arcu in, volutpat sagittis nisl. Pellentesque nisl ligula, placerat vel ullamcorper at, vulputate ac odio. Morbi ac faucibus orci, et tempus nulla. Proin rhoncus rutrum dolor, in venenatis mauris. Suspendisse a fermentum augue, non semper mi. Nunc eget pretium neque. Phasellus augue erat, feugiat ac aliquam congue, rutrum non sapien. Pellentesque ac diam gravida, consectetur felis at, ornare neque.
|
||||
|
||||
Nullam interdum mattis sapien quis porttitor. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus aliquet rutrum ipsum id euismod. Maecenas consectetur massa et mi porta viverra. Nunc quam nibh, dignissim vitae maximus et, ullamcorper nec lorem. Nunc vitae justo dapibus, luctus lacus vitae, pretium elit. Maecenas et efficitur leo. Curabitur mauris lectus, placerat quis vehicula vitae, auctor ut urna. Quisque rhoncus pharetra luctus. In hac habitasse platea dictumst. Integer sit amet metus nec eros malesuada aliquam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Morbi hendrerit mi ac leo aliquam, sit amet ultricies libero commodo. Mauris dapibus purus metus, sit amet viverra nibh imperdiet et. Nullam porta nulla tellus, quis vehicula diam imperdiet non. Vivamus enim massa, bibendum in fermentum in, ultrices at ex.
|
||||
|
||||
Suspendisse fermentum id nibh eget accumsan. Duis dapibus bibendum erat ut sollicitudin. Aliquam nec felis risus. Pellentesque rhoncus ligula id sem maximus mollis sed nec massa. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus ipsum ipsum, sodales sed enim id, convallis faucibus eros. Donec ultricies dictum tincidunt. Cras vitae nibh arcu. Pellentesque cursus, sapien nec consequat fermentum, ipsum ante suscipit dui, imperdiet hendrerit est nisl eu massa. Quisque vitae sem ligula. Aenean iaculis metus ut mauris interdum laoreet. Vivamus sed gravida dolor.
|
||||
|
||||
Morbi nulla metus, porttitor sed eros sit amet, efficitur efficitur est. In vel nisl urna. Ut aliquet tellus at congue convallis. Phasellus imperdiet lobortis sollicitudin. Integer sodales, sem eu ultricies pharetra, erat erat porttitor odio, eget dapibus libero ipsum eget velit. Phasellus gravida nulla nisl, eu pharetra mi auctor vel. Sed blandit pharetra velit, ut egestas libero placerat non. Aliquam a interdum quam. Proin at tortor nec dui sollicitudin tempus sed vestibulum elit. Nunc non sollicitudin velit.
|
||||
|
||||
Aenean consequat diam velit, sed rutrum tortor faucibus dictum. Quisque at semper augue. Duis ut est eget mi ornare bibendum id et ligula. Phasellus consequat tortor non leo pulvinar posuere. Proin vestibulum eleifend felis, in hendrerit tortor sollicitudin eu. Phasellus hendrerit, lacus vel laoreet interdum, dui tortor consequat justo, commodo ultricies arcu felis vitae enim. Vivamus eu sapien at leo suscipit rutrum eu at justo. Aenean et dolor a libero ullamcorper posuere. Integer laoreet placerat nisi in vulputate. Mauris laoreet eget risus sed cursus. Donec scelerisque neque a libero eleifend hendrerit. Nulla varius condimentum nunc sit amet fermentum. Aliquam lorem ex, varius nec mollis ut, ultrices in neque. Morbi sit amet porta leo. Integer iaculis fermentum lacus in vestibulum.
|
||||
|
||||
Ut gravida, tellus ut maximus ultrices, erat est venenatis nisl, vitae pretium massa ex ac magna. Sed non purus eget ligula aliquet volutpat non quis arcu. Nam aliquam tincidunt risus, sit amet fringilla sapien vulputate ut. Mauris luctus suscipit pellentesque. Nunc porttitor dapibus ex quis tempus. Ut ullamcorper metus a eros vulputate, vitae viverra lectus convallis. Mauris semper imperdiet augue quis tincidunt. Integer porta pretium magna, sed cursus sem scelerisque sollicitudin. Nam efficitur, nibh pretium eleifend vestibulum, purus diam posuere sem, in egestas mauris augue sit amet urna.
|
||||
|
||||
Vestibulum tincidunt euismod massa in congue. Duis interdum metus non laoreet fringilla. Donec at ligula congue, tincidunt nunc non, scelerisque nunc. Donec bibendum magna non est scelerisque feugiat at nec neque. Ut orci tortor, tempus eget massa non, dignissim faucibus dolor. Nam odio risus, accumsan pretium neque eget, accumsan dignissim dui. In ut neque auctor, scelerisque tellus sed, ullamcorper nisi. Suspendisse varius cursus quam at hendrerit. Vivamus elit libero, sagittis vitae sem ac, vulputate iaculis ligula.
|
||||
|
||||
Sed lobortis laoreet purus sit amet rutrum. Pellentesque feugiat non leo vel lacinia. Quisque feugiat nisl a orci bibendum vestibulum. In et sollicitudin urna. Morbi a arcu ac metus faucibus tempus. Nam eu imperdiet sapien, suscipit mattis tortor. Aenean blandit ipsum nisi, a eleifend ligula euismod at. Integer tincidunt pharetra felis, mollis placerat mauris hendrerit at. Curabitur convallis, est sit amet luctus volutpat, massa lacus cursus augue, sed eleifend magna quam et risus. Aliquam lobortis tincidunt metus vitae porttitor. Suspendisse potenti. Aenean ullamcorper, neque id commodo luctus, nulla nunc lobortis quam, id dapibus neque dui nec mauris. Etiam quis lorem quis elit commodo ornare. Ut pharetra purus ultricies enim ultrices efficitur. Proin vehicula tincidunt molestie. Mauris et placerat sem.
|
||||
|
||||
Aliquam erat volutpat. Suspendisse velit turpis, posuere ac lacus eu, lacinia laoreet velit. Sed interdum felis neque, id blandit sem malesuada sit amet. Ut sagittis justo erat, efficitur semper orci tempor sed. Donec enim massa, posuere varius lectus egestas, pellentesque posuere mi. Cras tincidunt ut libero sed mattis. Suspendisse quis magna et tellus posuere interdum vel at purus. Pellentesque fringilla tristique neque, id aliquet tellus ultricies non. Duis ut tellus vel odio lobortis vulputate.
|
||||
|
||||
Integer at magna ac erat convallis vestibulum. Sed lobortis porttitor mauris. Fusce varius lorem et volutpat pulvinar. Aenean ac vulputate lectus, vitae consequat velit. Suspendisse ex dui, varius ut risus ut, dictum scelerisque sem. Vivamus urna orci, volutpat ut convallis ac, venenatis vitae urna. In hac habitasse platea dictumst. Etiam eu purus arcu. Aenean vulputate leo urna, vel tristique dui sagittis euismod. Suspendisse non tellus efficitur ante rhoncus volutpat at et sapien.
|
||||
|
||||
Sed dapibus accumsan porttitor. Phasellus facilisis lectus finibus ligula dignissim, id pulvinar lectus feugiat. Nullam egestas commodo nisi posuere aliquet. Morbi sit amet tortor sagittis, rutrum dui nec, dapibus sapien. Sed posuere tortor tortor, interdum auctor magna varius vitae. Vestibulum id sagittis augue. Curabitur fermentum arcu sem, eu condimentum quam rutrum non. Phasellus rutrum nibh quis lectus rhoncus pretium. Curabitur dictum interdum elit. Vestibulum maximus sodales imperdiet. Mauris auctor nec purus sed venenatis. In in urna purus.
|
||||
|
||||
Duis placerat molestie suscipit. Morbi a elit id purus efficitur consequat. Nunc ac commodo turpis. Etiam sit amet lacus a ipsum tempus venenatis sed vel nibh. Duis elementum aliquam mi sed tristique. Morbi ligula tortor, semper ac est vel, lobortis maximus erat. Curabitur ipsum felis, laoreet vel condimentum eget, ullamcorper sit amet mauris. Nulla facilisi. Nam at purus sed mi egestas placerat vitae vel magna. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse at dignissim diam. Phasellus consectetur eget neque vel viverra. Donec sollicitudin mattis dolor vel malesuada. Vivamus vehicula leo neque, vitae fermentum leo posuere et. Praesent dui est, finibus sit amet tristique quis, pharetra vel nibh.
|
||||
|
||||
Duis nulla leo, accumsan eu odio eget, sagittis semper orci. Quisque ullamcorper ligula quam, commodo porttitor mauris ullamcorper eu. Cras varius sagittis felis in aliquam. Duis sodales risus ac justo vehicula, nec mattis diam lacinia. Cras eget lectus ipsum. Ut commodo, enim vitae malesuada hendrerit, ex dolor egestas lectus, sit amet hendrerit metus diam nec est. Vestibulum tortor metus, lobortis sit amet ante eget, tempor molestie lacus. In molestie et urna et semper. Mauris mollis, sem non hendrerit condimentum, sapien nisi cursus est, non suscipit quam justo non metus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam enim est, porta ac feugiat vitae, rutrum in lorem. Duis vehicula tortor ut posuere maximus.
|
||||
|
||||
Nullam vestibulum non tellus sed commodo. Quisque mattis elit sit amet sapien sollicitudin, ut condimentum nisl congue. Aenean sagittis massa vel elit faucibus fermentum. Donec tincidunt nisi nec nisl sodales pellentesque. Mauris congue congue ligula ut suscipit. Vivamus velit tortor, tempor et gravida eget, fermentum sit amet ante. Nullam fringilla, lorem at ultrices cursus, urna neque ornare dolor, eu lacinia orci enim sed nibh. Ut a ullamcorper lectus, id mattis purus. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aenean maximus sollicitudin posuere. Nunc at augue lacus. Aenean efficitur leo sit amet lacinia efficitur.
|
||||
|
||||
Quisque venenatis quam mi, in pharetra odio vulputate eu. In vel nisl pulvinar, pulvinar ligula ut, sodales risus. Sed efficitur lectus at vestibulum tincidunt. Vestibulum eu ullamcorper elit. Fusce vestibulum magna enim, et tempor lacus posuere vitae. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Integer leo elit, luctus nec mattis sit amet, sollicitudin in turpis.
|
||||
|
||||
Proin convallis venenatis leo, vitae tristique erat iaculis nec. Nulla facilisi. Duis porttitor, sapien et bibendum vulputate, sem libero sodales lacus, non malesuada felis erat ut libero. Nam non felis semper, finibus est a, mattis mauris. Praesent nec eros quam. Nulla hendrerit, augue consectetur eleifend ultricies, purus mi condimentum nulla, eget dapibus est nunc sed libero. Nullam elementum dui erat, vitae luctus libero sollicitudin et. Nulla odio magna, placerat in augue eu, dapibus imperdiet odio. Suspendisse imperdiet metus sit amet rhoncus dapibus. Cras at enim et urna vehicula cursus eu a mauris. Integer magna ante, eleifend ac placerat vitae, porta at nisi. Cras eget malesuada orci. Curabitur nunc est, vulputate id viverra et, dignissim sed odio. Curabitur non mattis sem. Sed bibendum, turpis vitae vehicula faucibus, nunc quam ultricies lectus, vitae viverra felis turpis at libero.
|
||||
|
||||
Nullam ut egestas ligula. Proin hendrerit justo a lectus commodo venenatis. Nulla facilisi. Ut cursus lorem quis est bibendum condimentum. Aenean in tristique odio. Fusce tempor hendrerit ipsum. Curabitur mollis felis justo, quis dapibus erat auctor vel. Sed augue lectus, finibus ut urna quis, ullamcorper vestibulum dui. Etiam molestie aliquam tempor. Integer mattis sollicitudin erat, et tristique elit varius vel. Mauris a ex justo.
|
||||
|
||||
Nam eros est, imperdiet non volutpat rutrum, pellentesque accumsan ligula. Duis sit amet turpis metus. Aenean in rhoncus metus, ac fringilla ex. Suspendisse condimentum egestas purus, ut pharetra odio vulputate vel. Duis tincidunt massa a placerat ultrices. Mauris ultricies nibh sit amet condimentum malesuada. Duis tincidunt id ipsum sed congue.
|
||||
|
||||
Praesent eu ex augue. Nullam in porta ligula. In tincidunt accumsan arcu, in pellentesque magna tristique in. Mauris eleifend libero ac nisl viverra faucibus. Nam sollicitudin dolor in commodo hendrerit. Cras at orci metus. Ut quis laoreet orci. Vivamus ultrices leo pellentesque tempor aliquet. Maecenas ut eros vitae purus placerat vestibulum. Etiam vitae gravida dolor, quis rhoncus diam. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos.
|
||||
|
||||
Suspendisse fringilla lacinia sagittis. Integer tincidunt consectetur tristique. Morbi non orci convallis, congue sapien quis, vulputate nunc. Donec a libero vel magna elementum facilisis non quis mi. Mauris posuere tellus non ipsum ultrices elementum. Vivamus massa velit, facilisis quis placerat aliquet, aliquet nec leo. Praesent a maximus sem. Sed neque elit, feugiat vel quam non, molestie sagittis nunc. Etiam luctus nunc ac mauris scelerisque, nec rhoncus lacus convallis. Nunc pharetra, nunc ac pulvinar aliquam, ex ipsum euismod augue, nec porttitor lacus turpis vitae neque. Fusce bibendum odio id tortor faucibus pellentesque. Sed ac porta nibh, eu gravida erat.
|
||||
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Aliquam quis ullamcorper felis. Nulla mattis sagittis ante ac tincidunt. Integer ac felis efficitur, viverra libero et, facilisis ligula. Suspendisse a metus a massa rhoncus posuere. Phasellus suscipit ligula ut lacus facilisis, ac pellentesque ex tempor. Quisque consectetur massa mi, ac molestie libero dictum quis. Proin porttitor ligula quis erat tincidunt venenatis. Proin congue nunc sed elit gravida, nec consectetur lectus sodales. Etiam tincidunt convallis ipsum at vestibulum. Quisque maximus enim et mauris porttitor, et molestie magna tristique. Morbi vitae metus elit. Maecenas sed volutpat turpis. Aliquam vitae dolor vestibulum, elementum purus eget, dapibus nibh. Nullam egestas dui ac rutrum semper.
|
||||
|
||||
Etiam hendrerit est metus, et condimentum metus aliquam ac. Pellentesque id neque id ipsum rhoncus vulputate. Aliquam erat nisl, posuere sit amet ligula ac, fermentum blandit felis. Vivamus fermentum mi risus, non lacinia purus viverra id. Aenean ac sapien consequat, finibus mauris nec, porta sem. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Sed quis consectetur ex, dignissim bibendum nulla. Phasellus ac libero at quam vehicula euismod non eu leo. Phasellus a sapien augue.
|
||||
|
||||
Maecenas ligula dui, bibendum vitae mauris et, auctor laoreet felis. Duis non libero a mi semper mattis. Quisque consequat luctus massa, quis tristique eros auctor feugiat. Maecenas sodales euismod neque vitae facilisis. Nullam laoreet imperdiet velit at pellentesque. Etiam massa odio, facilisis a consequat vitae, placerat vel magna. Nunc sagittis eros nec urna fringilla, pulvinar vestibulum nibh scelerisque. Sed magna metus, cursus eu consequat et, pharetra a est. Suspendisse elementum neque a dui malesuada lacinia. Donec sed ipsum volutpat, cursus urna id, ullamcorper arcu. Maecenas laoreet nisl eget velit egestas sollicitudin. Etiam nisl turpis, mollis id dignissim vitae, tristique vehicula ante. Maecenas eget placerat est, at rutrum augue. Vivamus faucibus lacinia ullamcorper. Sed pulvinar urna sodales ante sodales, at gravida leo dictum.
|
||||
|
||||
Morbi maximus, quam a lobortis bibendum, enim felis varius elit, ac vehicula elit nisl ut lacus. Quisque ut arcu augue. Praesent id turpis quam. Sed sed arcu eros. Maecenas at cursus lorem, ac eleifend nisi. Fusce mattis felis at commodo pharetra. Praesent ac commodo ipsum. Quisque finibus et eros vitae tincidunt. In hac habitasse platea dictumst. Praesent purus ipsum, luctus lobortis ornare quis, auctor eget justo. Nam vel enim sollicitudin, faucibus tortor eu, sagittis eros. Ut nec consectetur erat. Donec ultricies malesuada ligula, a hendrerit sapien volutpat in. Maecenas sed enim vitae sapien pulvinar faucibus.
|
||||
|
||||
Proin semper nunc nibh, non consequat neque ullamcorper vel. Maecenas lobortis sagittis blandit. Aenean et arcu ultricies turpis malesuada malesuada. Ut quam ex, laoreet ut blandit cursus, feugiat vitae dolor. Etiam ex lacus, scelerisque vel erat vel, efficitur tincidunt magna. Morbi tristique lacinia dolor, in egestas magna ultrices vitae. Integer ultrices leo ac tempus venenatis. Praesent ac porta tortor. Vivamus ornare blandit tristique. Nulla rutrum finibus pellentesque. In non dui elementum, fermentum ipsum vel, varius magna. Pellentesque euismod tortor risus, ac pellentesque nisl faucibus eget.
|
||||
|
||||
Vivamus eu enim purus. Cras ultrices rutrum egestas. Sed mollis erat nibh, at posuere nisl luctus nec. Nunc vulputate, sapien id auctor molestie, nisi diam tristique ante, non convallis tellus nibh at orci. Morbi a posuere purus, in ullamcorper ligula. Etiam elementum sit amet dui imperdiet iaculis. Proin vitae tincidunt ipsum, sit amet placerat lectus. Curabitur commodo sapien quam, et accumsan lectus fringilla non. Nullam eget accumsan enim, ac pharetra mauris. Sed quis tristique velit, vitae commodo nisi. Duis turpis dui, maximus ut risus at, finibus consequat nunc. Maecenas sed est accumsan, aliquet diam in, facilisis risus. Curabitur vehicula rutrum auctor. Nam iaculis risus pulvinar maximus viverra. Nulla vel augue et ex sagittis blandit.
|
||||
|
||||
Ut sem nulla, porta ac ante ac, posuere laoreet eros. Donec sodales posuere justo a auctor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Cras mollis at orci hendrerit porta. Nullam sodales tortor tortor, non lacinia diam finibus id. Duis libero orci, suscipit ac odio et, dictum consequat ipsum. Pellentesque eu ligula sagittis, volutpat eros at, lacinia lorem. Cras euismod tellus in iaculis tempor. Quisque accumsan, magna a congue venenatis, ante ipsum aliquam lectus, at egestas enim nunc at justo. Quisque sem purus, viverra ut tristique ut, maximus id enim. Etiam quis placerat sem. In sollicitudin, lacus eu rutrum mollis, nulla eros luctus elit, vel dapibus urna purus nec urna. Phasellus egestas massa quam, ac molestie erat hendrerit a. Praesent ultrices neque ut turpis molestie auctor. Etiam molestie placerat purus, et euismod erat aliquam in. Morbi id suscipit justo.
|
||||
|
||||
Proin est ante, consequat at varius a, mattis quis felis. Sed accumsan nibh sit amet ipsum elementum posuere. Vestibulum bibendum id diam sit amet gravida. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Morbi nec dolor vel ipsum dignissim hendrerit vel non ipsum. Praesent facilisis orci quis elit auctor lobortis. Phasellus cursus risus lectus, vel lobortis libero dapibus in. Quisque tristique tempus leo a pulvinar. Pellentesque a magna tincidunt, pellentesque massa nec, laoreet orci. Morbi congue ornare dolor quis commodo. Phasellus massa nisi, tincidunt at eros dictum, hendrerit lobortis urna. Maecenas porta, magna id mattis molestie, nibh tellus lobortis sem, eget tincidunt ipsum quam eu turpis.
|
||||
|
||||
Ut gravida orci risus, vel rutrum mauris vehicula id. Etiam bibendum, neque a placerat condimentum, ex orci imperdiet lectus, quis dapibus arcu lacus eget lectus. Sed consequat non mi sit amet venenatis. Fusce vestibulum erat libero, eget hendrerit risus vulputate sollicitudin. Integer sed eleifend felis. Donec commodo, sem eu mattis placerat, urna odio aliquam tellus, et laoreet justo tellus eget erat. Fusce sed suscipit tortor. Nam hendrerit nibh ac nunc auctor lacinia. Pellentesque placerat condimentum ipsum, eget semper tortor hendrerit vel. Nullam non urna eu lacus pellentesque congue ut id eros.
|
||||
|
||||
Nunc finibus leo in rhoncus tristique. Sed eu ipsum nec nisl egestas faucibus eget a felis. Pellentesque vitae nisi in nulla accumsan fermentum. Sed venenatis feugiat eleifend. Fusce porttitor varius placerat. Aliquam aliquet lacus sit amet mattis mollis. Sed vel nulla quis dolor suscipit vehicula ac viverra lorem. Duis viverra ipsum eget nulla ullamcorper fermentum. Mauris tincidunt arcu quis quam fringilla ornare. Donec et iaculis tortor. Nam ultricies libero vel ipsum aliquet efficitur. Morbi eget dolor aliquam, tempus sapien eget, viverra ante. Donec varius mollis ex, sed efficitur purus euismod interdum. Quisque vel sapien non neque tincidunt semper. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas.
|
||||
|
||||
Suspendisse sit amet purus leo. Fusce lectus lorem, aliquam ac nulla eget, imperdiet ornare eros. Nullam sem augue, varius in nisi non, sollicitudin pellentesque ante. Etiam eu odio condimentum, tempor libero et, egestas arcu. Cras pellentesque eleifend aliquet. Pellentesque non blandit ligula. Ut congue viverra rhoncus. Phasellus mattis mi ac eros placerat, eu feugiat tellus ultrices. Aenean mollis laoreet libero eu imperdiet. Cras sed pulvinar mi, ac vehicula ligula. Vestibulum sit amet ex massa. In a egestas eros.
|
||||
|
||||
Mauris pretium ipsum risus, venenatis cursus ante imperdiet id. Praesent eu turpis nec risus feugiat maximus ullamcorper ac lectus. Integer placerat at mi vel dapibus. Vestibulum fermentum turpis sit amet turpis viverra, id aliquet diam suscipit. Nam nec ex sed ante ullamcorper pharetra quis sit amet risus. Sed ac faucibus velit, id feugiat nibh. Nullam eget ipsum ex. Vivamus tincidunt non nunc non faucibus. Quisque bibendum viverra facilisis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur at nisi hendrerit quam suscipit egestas. Curabitur laoreet maximus ultricies. Duis ut tellus ac augue molestie dictum.
|
||||
|
||||
Suspendisse rhoncus iaculis erat, ut ullamcorper est tristique eget. Donec auctor nec risus at gravida. Vivamus volutpat vulputate tellus, vel ultricies eros suscipit eget. Ut pulvinar id mi eu tempus. Morbi malesuada augue in dui varius, nec blandit neque vehicula. Donec ornare nec nisl in mollis. Morbi enim nisi, rhoncus nec est id, dapibus tempus urna. Ut id elit a felis vestibulum consectetur. Duis lectus quam, pharetra sit amet diam sed, posuere vestibulum erat. Fusce vitae maximus massa. Nullam id metus tempus, iaculis risus eu, lobortis urna. Quisque in congue urna. Pellentesque placerat neque in augue dapibus, non varius ex malesuada. Curabitur ut eleifend libero. Fusce vitae ligula luctus, fermentum enim vitae, ultrices erat.
|
||||
|
||||
Sed viverra augue turpis, scelerisque egestas sapien mattis eu. Duis laoreet magna at ex pharetra dapibus. Praesent eget odio vel quam venenatis dictum. Nulla in sollicitudin dolor. Mauris lobortis nec eros vel rhoncus. Vestibulum porta viverra venenatis. Curabitur vel scelerisque quam, a egestas velit. Praesent volutpat tincidunt magna at laoreet.
|
||||
|
||||
Cras nec lorem odio. Pellentesque quis dui urna. Praesent at tellus ac lectus scelerisque placerat nec eu risus. Vestibulum sit amet mattis ligula. Vivamus sed nisi at leo elementum accumsan at sit amet arcu. Aenean mattis tellus nec leo gravida, eget hendrerit nisl faucibus. Mauris pellentesque luctus condimentum. Maecenas pretium sapien nunc, eget commodo dolor maximus id. Mauris vestibulum accumsan massa a dictum. Phasellus interdum quam ligula, ut maximus diam blandit aliquam. Nunc vitae ex eu erat condimentum consectetur. Maecenas interdum condimentum volutpat.
|
||||
|
||||
Donec et enim a libero rutrum laoreet. Praesent a condimentum sem, at tincidunt quam. In vel molestie risus. Sed urna dui, molestie vitae mollis laoreet, tempor quis lectus. Praesent vitae auctor est, et aliquet nunc. Curabitur vulputate blandit nulla, at gravida metus. Maecenas gravida dui eu iaculis tristique. Pellentesque posuere turpis nec auctor eleifend. Suspendisse bibendum diam eu tellus lobortis, et laoreet quam congue. In hac habitasse platea dictumst. Morbi dictum neque velit, eget rutrum eros ultrices sit amet.
|
||||
|
||||
Phasellus fermentum risus pharetra consectetur bibendum. Donec magna tortor, lacinia vitae nibh quis, aliquet pretium lorem. Donec turpis nisi, pretium eu enim volutpat, mattis malesuada augue. Nullam vel tellus iaculis, sollicitudin elit eget, tincidunt lacus. Fusce elementum elementum felis et iaculis. Suspendisse porta eros nec neque malesuada, in malesuada ante sollicitudin. Vivamus bibendum viverra molestie.
|
||||
|
||||
Integer feugiat, erat nec convallis aliquam, velit felis congue erat, molestie eleifend tellus erat in tellus. Nunc et justo purus. Donec egestas fermentum dui non feugiat. Quisque in sapien sagittis, gravida quam id, iaculis lectus. Cras sagittis rhoncus bibendum. Fusce quis metus in velit scelerisque tincidunt at non ipsum. Vivamus efficitur ante eu odio vulputate, vitae ultricies risus vehicula. Proin eget odio eu sem tincidunt feugiat vel id lorem.
|
||||
|
||||
Vestibulum sit amet nulla dignissim, euismod mi in, fermentum tortor. Donec ut aliquet libero, lacinia accumsan velit. Donec et nulla quam. Nullam laoreet odio nec nunc imperdiet, a congue eros venenatis. Quisque nec tellus sit amet neque interdum posuere. Duis quis mi gravida, tincidunt diam convallis, ultricies augue. Mauris consequat risus non porttitor congue. Ut in ligula consequat, viverra nunc a, eleifend enim. Duis ligula urna, imperdiet nec facilisis et, ornare eu ex. Proin lobortis lectus a lobortis porttitor. Nulla leo metus, egestas eu libero sed, pretium faucibus felis. Vestibulum non sem tortor. Nam cursus est leo. Vivamus luctus enim odio, non interdum sem dapibus a. Aenean accumsan consequat lectus in imperdiet.
|
||||
|
||||
Donec vehicula laoreet ipsum in posuere. Quisque vel quam imperdiet, sollicitudin nisi quis, suscipit velit. Morbi id sodales mauris. Curabitur tellus arcu, feugiat sed dui sit amet, sodales sagittis libero. Aenean vel suscipit metus, non placerat leo. Vestibulum quis nulla elit. Proin scelerisque non ante ut commodo. Interdum et malesuada fames ac ante ipsum primis in faucibus.
|
||||
|
||||
Sed non urna dolor. Suspendisse convallis mi porta pulvinar ultrices. Suspendisse quam ipsum, hendrerit non scelerisque molestie, interdum dictum nunc. Morbi condimentum condimentum turpis eu luctus. Pellentesque sagittis sollicitudin odio, sed ultricies felis ornare sit amet. Sed ultrices ex leo, a tincidunt nisl gravida sed. Nullam ornare accumsan porta. Praesent consectetur id est nec sollicitudin.
|
||||
|
||||
In hac habitasse platea dictumst. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed sed ultrices nibh. Duis accumsan suscipit eros, a dictum odio tempus sit amet. Aenean imperdiet erat ac lacus finibus, scelerisque cursus massa imperdiet. Mauris molestie risus ut lacinia posuere. Nulla et sodales purus. Maecenas orci erat, placerat in tristique quis, placerat in mi.
|
||||
|
||||
Donec sollicitudin pellentesque odio in feugiat. Morbi eu dolor ut mauris congue sollicitudin. Aliquam erat volutpat. Nulla id varius dui. Curabitur finibus urna ante, consectetur interdum nisi volutpat a. Quisque quis mi tristique, consequat tellus eget, rutrum sapien. Vivamus vitae tellus vulputate, rutrum ex eu, vulputate sem. Suspendisse viverra lorem tellus, vel interdum orci gravida quis. Ut laoreet arcu at mi ullamcorper finibus. Duis porta sagittis vestibulum. Sed commodo nisl vitae urna sollicitudin, nec lacinia est sodales. Curabitur imperdiet sodales dui sed iaculis. Sed ac tellus maximus, eleifend quam sit amet, feugiat elit. Aenean viverra, dui at mattis varius, est odio vestibulum sapien, sit amet mollis libero massa nec velit. Etiam quis sodales justo.
|
||||
|
||||
Ut ultricies, sem eget sodales feugiat, nunc arcu congue elit, ac tempor justo massa nec purus. Maecenas enim nunc, pharetra eget dictum sit amet, tempus pellentesque velit. Suspendisse venenatis ligula in nulla mattis, et imperdiet ex tincidunt. Etiam vulputate, tellus et ultrices suscipit, enim velit laoreet massa, vitae congue odio enim ac urna. Morbi quam lorem, iaculis ac varius sagittis, euismod quis dolor. In ut dui eu purus feugiat consectetur. Vestibulum cursus velit quis lacus pellentesque iaculis. Cras in risus sed mauris porta rutrum. Nulla facilisi. Nullam eu bibendum est, non pellentesque lectus. Sed imperdiet feugiat lorem, quis convallis ante auctor in. Maecenas justo magna, scelerisque sit amet tellus eget, varius elementum risus. Duis placerat et quam sed varius.
|
||||
|
||||
Duis nec nibh vitae nibh dignissim mollis quis sed felis. Curabitur vitae quam placerat, venenatis purus ut, euismod nisl. Curabitur porttitor nibh eu pulvinar ullamcorper. Suspendisse posuere nec ipsum ac dapibus. Cras convallis consectetur urna. Phasellus a nibh in dolor lacinia posuere id eget augue. In eu pharetra lorem, vitae cursus lacus. Aliquam tincidunt nibh lectus. Aenean facilisis ultricies posuere. Sed ut placerat orci. Curabitur scelerisque gravida blandit. Maecenas placerat ligula eget suscipit fringilla. Mauris a tortor justo. Aliquam hendrerit semper mollis. Phasellus et tincidunt libero. Etiam vel quam libero.
|
||||
|
||||
Quisque aliquet tempor ex. Ut ante sem, vehicula at enim vel, gravida porta elit. Etiam vitae lacus a neque lobortis consectetur. Mauris sed interdum odio. Mauris elementum ex blandit tempor cursus. Integer in enim in leo viverra elementum. Fusce consectetur metus et sem rutrum, mattis euismod diam semper. Nunc sed ipsum vel urna consequat vehicula. Donec cursus pretium lorem, vestibulum pretium felis commodo sit amet. Nam blandit felis enim, eget gravida ex faucibus a. In nec neque massa. Etiam laoreet posuere ipsum. Praesent volutpat nunc dolor, ac vulputate magna facilisis non. Aenean congue turpis vel lectus sollicitudin tristique. Sed nec consequat purus, non vehicula quam. Etiam ultricies, est ac dictum tincidunt, turpis turpis pretium massa, a vulputate libero justo at nibh.
|
||||
|
||||
Aliquam erat volutpat. Cras ultrices augue ac sollicitudin lobortis. Curabitur et aliquet purus. Duis feugiat semper facilisis. Phasellus lobortis cursus velit, a sollicitudin tortor. Nam feugiat sapien non dapibus condimentum. Morbi at mi bibendum, commodo quam at, laoreet enim. Integer eu ultrices enim. Sed vestibulum eu urna ut dictum. Curabitur at mattis leo, sed cursus massa. Aliquam porttitor, felis quis fermentum porttitor, justo velit feugiat nulla, eget condimentum sem dui ut sapien.
|
||||
|
||||
In fringilla elit eu orci aliquam consequat. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Ut eget fringilla tellus. Curabitur fermentum, mi et condimentum suscipit, elit neque bibendum dui, et hendrerit nunc metus id ipsum. Morbi placerat mi in hendrerit congue. Ut feugiat mauris eget scelerisque viverra. Vivamus sit amet erat dictum, sagittis lectus nec, pulvinar lorem. Sed non enim ac dui sollicitudin aliquet. Quisque ut lacus dolor. Fusce hendrerit malesuada euismod. Nulla faucibus vel mauris eu mollis. Mauris est diam, fringilla ac arcu feugiat, efficitur volutpat turpis. Aliquam venenatis cursus massa sed porttitor. Ut ac finibus enim, in tincidunt sapien.
|
||||
|
||||
Nunc faucibus semper turpis a lacinia. Phasellus gravida, libero vel pulvinar ornare, ex sem tincidunt lectus, sit amet convallis augue risus at tortor. Quisque sit amet ipsum id nulla posuere vestibulum. Pellentesque scelerisque mauris vel leo viverra sodales. Nulla viverra aliquam ex, ut rutrum enim fermentum venenatis. Aenean eget dapibus ex, eget faucibus metus. Vestibulum volutpat leo in diam semper, eget porta magna suscipit. Sed sit amet nulla blandit, aliquam dolor ac, gravida velit. Sed vel velit viverra, maximus est id, convallis justo.
|
||||
|
||||
Curabitur nulla ante, vulputate at libero vel, ullamcorper rutrum nibh. Pellentesque porttitor eu mauris id mattis. Duis vulputate augue elit, eget interdum justo pretium vel. Maecenas eu vulputate arcu, eget posuere purus. Suspendisse viverra a velit dictum eleifend. Suspendisse vitae dapibus diam. Donec vehicula justo in ante interdum, eu luctus diam placerat. Vivamus convallis ipsum eu orci suscipit, sed fermentum enim euismod. Maecenas faucibus elit vitae ex ornare tristique. Donec vestibulum nec elit sit amet porttitor. Aenean tempor lectus eget tortor hendrerit luctus. Nullam interdum vitae lectus vel feugiat. Cras in risus non magna consectetur lobortis. Sed faucibus enim quis gravida convallis.
|
||||
|
||||
Phasellus eget massa sit amet libero ultrices suscipit. Vivamus at risus sapien. Nam mollis nunc eget velit dictum maximus. Sed pellentesque, nunc ac fringilla lacinia, quam enim mattis ex, sed euismod tortor metus eu neque. Ut mattis nisl ut lectus rhoncus, sodales bibendum eros porta. Nulla porttitor enim nec diam sagittis, eget porta velit efficitur. Vestibulum ultricies eros neque. Phasellus rutrum suscipit enim, in interdum ante gravida vitae. Sed in sagittis diam, non commodo velit.
|
||||
|
||||
Morbi hendrerit odio orci, nec tincidunt odio rhoncus nec. Mauris neque velit, vehicula a lorem at, suscipit tristique dui. Sed finibus, nisl in mattis convallis, turpis neque sodales lacus, eu porta enim magna non diam. Nam commodo sodales risus consectetur malesuada. In eget elementum justo. Phasellus sit amet massa imperdiet, dapibus nunc sit amet, suscipit orci. Fusce condimentum laoreet feugiat. Ut ut viverra ante. Praesent bibendum interdum commodo. Nulla mollis nisi a est ornare volutpat. Sed at ligula eu nisi dapibus tempus. Proin cursus vestibulum justo, nec efficitur justo dignissim vel. Nunc quis maximus eros.
|
||||
|
||||
Cras viverra, diam a tristique mattis, libero felis vulputate tellus, a ornare felis leo a dui. Nulla ante nulla, finibus ut tellus ut, blandit pharetra nibh. Proin eleifend fermentum ex, eget auctor libero vulputate in. Nullam ultricies, mauris placerat pretium placerat, leo urna lobortis leo, vel placerat arcu libero sed mauris. Aliquam mauris ligula, ornare at urna at, eleifend gravida ligula. Vestibulum consectetur ut nulla non scelerisque. Donec ornare, sem nec elementum aliquam, urna nulla bibendum metus, eu euismod dui ligula ac est. Fusce laoreet erat eu ex lobortis, quis bibendum ligula interdum. Sed vel mi erat. Vivamus id lacus ac enim mattis tempor. Nunc ultricies pellentesque enim sed euismod. Fusce tincidunt convallis elit quis aliquam. Mauris nulla ipsum, sollicitudin quis diam ac, feugiat volutpat tellus. In nibh nibh, vulputate quis tincidunt quis, pulvinar eget magna. Pellentesque quis finibus dolor. Suspendisse viverra vitae lectus non eleifend.
|
||||
|
||||
Nunc ut orci et sapien maximus semper. Nulla dignissim sem urna, ac varius lectus ultricies id. Quisque aliquet pulvinar pretium. In ultricies molestie tellus vehicula porta. Nam enim lorem, aliquam eget ex et, hendrerit volutpat quam. Maecenas diam lacus, pellentesque eget tempus ac, pharetra eu elit. Donec vel eros a sem facilisis vulputate. Nullam ac nisi vulputate, laoreet nisl ac, eleifend sem. Nullam mi massa, rhoncus sed pharetra interdum, tincidunt eget nunc. Aliquam viverra mattis posuere. Mauris et dui sed nisl sollicitudin fermentum quis ut arcu. Nam placerat eget orci at tincidunt. Curabitur vel turpis metus. Phasellus nibh nulla, fermentum scelerisque sem vel, gravida tincidunt velit. Pellentesque vel quam tempor, finibus massa pellentesque, condimentum dui.
|
||||
|
||||
Donec at mattis neque. Etiam velit diam, consequat auctor mauris id, hendrerit faucibus metus. Maecenas ullamcorper eros a est sodales, ac consectetur odio scelerisque. Donec leo metus, imperdiet at pellentesque vel, feugiat id erat. Suspendisse at magna enim. Vestibulum placerat sodales lorem id sollicitudin. Aenean at euismod ligula, eget mollis diam. Phasellus pulvinar, orci nec pretium condimentum, est erat facilisis purus, quis feugiat augue elit aliquam nulla. Aenean vitae tortor id risus congue tincidunt. Sed dolor enim, mattis a ullamcorper id, volutpat ac leo.
|
||||
|
||||
Proin vehicula feugiat augue, id feugiat quam sodales quis. Donec et ultricies massa, a lacinia nulla. Duis aliquam augue ornare euismod viverra. Ut lectus risus, rutrum sit amet efficitur a, luctus nec nisl. Cras volutpat ullamcorper congue. Sed vitae odio metus. Phasellus aliquet euismod varius.
|
||||
|
||||
Nullam sem ex, malesuada ut magna ut, pretium mollis arcu. Nam porttitor eros cursus mi lacinia faucibus. Suspendisse aliquet eleifend iaculis. Maecenas sit amet viverra tortor. Nunc a mollis risus. Etiam tempus dolor in tortor malesuada mattis. Ut tincidunt venenatis est sit amet dignissim. Vestibulum massa enim, tristique sed scelerisque eu, fringilla ac velit. Donec efficitur quis urna sit amet malesuada. Vestibulum consequat ac ligula in dapibus. Maecenas massa massa, molestie non posuere nec, elementum ut magna. In nisi erat, mollis non venenatis eu, faucibus in justo. Morbi gravida non ex non egestas. Pellentesque finibus laoreet diam, eu commodo augue congue vitae.
|
||||
|
||||
Aenean sem mi, ullamcorper dapibus lobortis vitae, interdum tincidunt tortor. Vivamus eget vulputate libero. Ut bibendum posuere lectus, vel tincidunt tortor aliquet at. Phasellus malesuada orci et bibendum accumsan. Aliquam quis libero vel leo mollis porta. Sed sagittis leo ac lacus dictum, ac malesuada elit finibus. Suspendisse pharetra luctus commodo. Vivamus ultricies a odio non interdum. Vivamus scelerisque tincidunt turpis quis tempor. Pellentesque tortor ligula, varius non nunc eu, blandit sollicitudin neque. Nunc imperdiet, diam et tristique luctus, ipsum ex condimentum nunc, sit amet aliquam justo velit sed libero. Duis vel suscipit ligula. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Sed tincidunt neque vel massa ultricies, id dictum leo consequat. Curabitur lobortis ultricies tellus, eget mattis nisl aliquam sit amet.
|
||||
|
||||
Proin at suscipit justo. Vivamus ut vestibulum nisl. Pellentesque enim odio, pharetra non magna sed, efficitur auctor magna. Praesent tincidunt ante quis ante hendrerit viverra. Pellentesque vel ipsum id magna vulputate efficitur. Sed nec neque accumsan, pulvinar sapien quis, euismod mauris. Donec condimentum laoreet sapien quis gravida. Quisque sed mattis purus. Vestibulum placerat vel neque maximus scelerisque.
|
||||
|
||||
Vestibulum mattis quam quis efficitur elementum. Duis dictum dolor ac scelerisque commodo. Fusce sollicitudin nisi sit amet dictum placerat. Suspendisse euismod pharetra eleifend. In eros nisl, porttitor sed mauris at, consectetur aliquet mauris. Donec euismod viverra neque sed fermentum. Phasellus libero magna, accumsan ut ultricies vitae, dignissim eget metus. Donec tellus turpis, interdum eget maximus nec, hendrerit eget massa. Curabitur auctor ligula in iaculis auctor. In ultrices quam suscipit cursus finibus. Aenean id mi at dolor interdum iaculis vitae ut lorem. Nullam sed nibh fringilla, lacinia odio nec, placerat erat. In dui libero, viverra ac viverra ac, pellentesque sit amet turpis.
|
||||
|
||||
Nulla in enim ex. Sed feugiat est et consectetur venenatis. Cras varius facilisis dui vel convallis. Vestibulum et elit eget tellus feugiat pellentesque. In ut ante eu purus aliquet posuere. Nulla nec ornare sem, sed luctus lorem. Nam varius iaculis odio, eget faucibus nisl ullamcorper in. Sed eget cursus felis, nec efficitur nisi.
|
||||
|
||||
Vivamus commodo et sem quis pulvinar. Pellentesque libero ante, venenatis vitae ligula sit amet, ornare sollicitudin nulla. Mauris eget tellus hendrerit, pulvinar metus quis, tempor nisi. Proin magna ex, laoreet sed tortor quis, varius fermentum enim. Integer eu dolor dictum, vulputate tortor et, aliquet ligula. Vestibulum vitae justo id mauris luctus sollicitudin. Suspendisse eget auctor neque, sodales egestas lorem. Vestibulum lacinia egestas metus vitae euismod. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Vivamus ex tellus, volutpat nec pulvinar sit amet, condimentum vitae dui. Curabitur vel felis sodales, lacinia nunc iaculis, ullamcorper augue. Pellentesque consequat dolor quis eros efficitur malesuada. Nulla ut malesuada lectus.
|
||||
|
||||
Morbi et tristique ante. Aliquam erat volutpat. Vivamus vitae dui nec turpis pellentesque fermentum. Quisque eget velit massa. Pellentesque tristique aliquam nisl, eu sollicitudin justo venenatis sed. Duis eleifend sem eros, ut aliquam libero porttitor id. Sed non nunc consequat, rhoncus diam eu, commodo erat. Praesent fermentum in lectus id blandit. Donec quis ipsum at justo volutpat finibus. Nulla blandit justo nulla, at mollis lacus consequat eget. Aenean sollicitudin quis eros ut ullamcorper.
|
||||
|
||||
Pellentesque venenatis nulla ut mi aliquet feugiat. Cras semper vel magna nec pharetra. Integer mattis felis et sapien commodo imperdiet. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Duis quis luctus felis. Vestibulum justo nibh, aliquam non lectus vitae, molestie placerat justo. Donec lorem nibh, gravida sit amet hendrerit ac, maximus id ipsum. Nunc ac libero sodales risus eleifend sagittis. Phasellus est massa, lobortis elementum ex sed, scelerisque consectetur neque. Nunc faucibus neque id lorem malesuada, eget convallis ex mattis.
|
||||
|
||||
Sed turpis tortor, fermentum non turpis id, posuere varius nibh. Donec iaculis lorem dui. Etiam eros ante, sodales eget venenatis at, consectetur eget risus. Curabitur non aliquam ante, a pretium justo. Maecenas tempor nisl tortor, vitae dictum nisi ultrices eu. Duis eget dui ultrices, porttitor lacus sed, lobortis purus. Quisque mattis elit nec neque sagittis, sed commodo leo blandit. Mauris sodales interdum eleifend. Vestibulum condimentum consectetur augue, id luctus diam convallis et.
|
||||
|
||||
Nunc suscipit risus in justo accumsan, a placerat magna tincidunt. Proin a nisl ipsum. Sed libero dui, tristique in augue quis, auctor tristique risus. Sed porttitor ex augue, eu porta augue molestie a. Duis rhoncus purus libero, eu tempus turpis condimentum at. Sed mollis nisi id lectus placerat tincidunt. Maecenas non scelerisque elit, quis rutrum orci. Donec in tellus pharetra urna ornare lobortis. Phasellus id risus at nisi varius rutrum eu ut turpis.
|
||||
|
||||
Duis dictum justo quis nisl porta, eget tincidunt magna suscipit. Sed velit massa, ullamcorper eu sodales ac, pretium a massa. Duis et rutrum tortor. Nulla accumsan hendrerit sapien, cursus volutpat eros egestas eget. Donec sollicitudin at ante quis sollicitudin. Aenean blandit feugiat diam, id feugiat eros faucibus eget. Donec viverra dolor vel justo scelerisque dignissim. Nulla semper sem nunc, rhoncus semper tellus ultricies sed. Duis in ornare diam. Donec vehicula feugiat varius. Maecenas ut suscipit est. Vivamus sem sem, finibus at dolor sit amet, euismod dapibus ligula. Vestibulum fringilla odio dapibus, congue massa eget, congue sem. Donec feugiat magna eget tortor lacinia scelerisque non et ipsum.
|
||||
|
||||
Suspendisse potenti. Nunc convallis sollicitudin ex eget venenatis. Sed iaculis nibh ex, vel ornare ligula congue dignissim. Quisque sollicitudin dolor ac dui vestibulum, sit amet molestie nisi aliquet. Donec at risus felis. Aenean sollicitudin metus a feugiat porta. Aenean a tortor ut dolor cursus sagittis. Vivamus consectetur porttitor nunc in facilisis. Proin sit amet mi vel lectus consectetur ultrices.
|
||||
|
||||
Sed cursus lectus vitae nunc tristique, nec commodo turpis dapibus. Pellentesque luctus ex id facilisis ornare. Morbi quis placerat dolor. Donec in lectus in arcu mattis porttitor ac sit amet metus. Cras congue mauris non risus sodales, vitae feugiat ipsum bibendum. Nulla venenatis urna sed libero elementum, a cursus lorem commodo. Mauris faucibus lobortis eros nec commodo.
|
||||
|
||||
Nullam suscipit ligula ullamcorper lorem commodo blandit. Nulla porta nibh quis pulvinar placerat. Vivamus eu arcu justo. Vestibulum imperdiet est ut fermentum porttitor. Pellentesque consectetur libero in sapien efficitur scelerisque. Curabitur ac erat sit amet odio aliquet dignissim. Pellentesque mi sem, rhoncus et luctus at, porttitor rutrum lectus. Vestibulum sollicitudin sollicitudin suscipit. Aenean efficitur dolor non ultrices imperdiet. Donec vel sem ex.
|
||||
|
||||
Sed convallis mauris aliquam rutrum cursus. Ut tempor porttitor sodales. Etiam eu risus ac augue gravida egestas et eu dolor. Proin id magna ex. Suspendisse quis lectus quis lorem ultricies tempus. Donec porttitor velit vitae tincidunt faucibus. Aliquam vitae semper nisi. Morbi ultrices, leo non pretium dapibus, dui libero pellentesque ex, vel placerat enim ante vitae dui. Nunc varius, sem sit amet sagittis lobortis, lectus odio scelerisque mauris, ut vestibulum orci magna quis neque. Sed id congue justo. Interdum et malesuada fames ac ante ipsum primis in faucibus. Mauris congue nisi est, malesuada mollis elit tincidunt sed. Curabitur sed ex sit amet felis tristique elementum vitae vel nibh.
|
||||
|
||||
Etiam mollis pretium lobortis. Mauris augue lacus, efficitur at lacus sed, mollis tincidunt lectus. Aliquam erat volutpat. Donec at euismod elit, et mattis felis. Sed id lobortis urna. Morbi imperdiet vestibulum leo, sed maximus leo blandit eu. Aliquam semper lorem neque, nec euismod turpis mattis mollis. Quisque lobortis urna ultrices odio pretium, ac venenatis orci faucibus. Suspendisse bibendum odio ligula, sed lobortis massa pharetra nec. Donec turpis justo, iaculis at dictum ac, finibus eu libero. Maecenas quis porttitor mi, sit amet aliquet neque.
|
||||
|
||||
Vivamus auctor vulputate ante, at egestas lorem. Donec eu risus in nulla mollis ultricies at et urna. Duis accumsan porta egestas. Ut vel euismod augue. Fusce convallis nulla ante, nec fringilla velit aliquet at. Nam malesuada dapibus ligula, a aliquam nibh scelerisque ac. Praesent malesuada neque et pellentesque interdum. Curabitur volutpat at turpis vitae tristique. Vivamus porttitor semper congue. Quisque suscipit lacus mi, rhoncus ultrices tortor auctor quis. Maecenas neque neque, molestie ac facilisis eget, luctus ac lorem. In ut odio ut lacus suscipit pulvinar vitae sed elit. Nulla imperdiet, sem quis euismod sagittis, dui erat luctus dolor, faucibus faucibus erat sem eget nunc. Nam accumsan placerat malesuada. Maecenas convallis finibus pulvinar.
|
||||
|
||||
Cras at placerat tortor. Morbi facilisis auctor felis sit amet molestie. Donec sodales sed lorem vitae suscipit. Etiam fermentum pharetra ipsum, nec luctus orci gravida eu. Pellentesque gravida, est non condimentum tempus, mauris ligula molestie est, in congue dolor nisl vel sapien. Duis congue tempor augue, id rutrum eros porta dapibus. Etiam rutrum eget est eget vestibulum. Aenean mollis arcu vel consequat varius. Praesent at condimentum felis. Duis nec interdum nisl. Donec commodo lorem sed sapien scelerisque malesuada non eu urna. In blandit non ipsum at porta. Nam lobortis leo vitae dui auctor, non feugiat quam bibendum. Donec auctor lectus sagittis laoreet maximus. Maecenas rhoncus laoreet porttitor. Vestibulum porttitor augue ut lectus hendrerit, eget posuere mi gravida.
|
||||
|
||||
Sed mattis ex in erat pulvinar, eu imperdiet magna dapibus. Etiam nisi nibh, tempus non tellus sit amet, mattis tempor odio. Quisque nec lorem feugiat, lobortis odio et, commodo nunc. Maecenas semper purus nisi, nec vehicula nibh eleifend vitae. Nulla fermentum a lectus at maximus. Phasellus finibus metus non euismod ultrices. Etiam a pulvinar ante. Quisque convallis nec metus sit amet facilisis. Praesent laoreet massa et sollicitudin laoreet. Vestibulum in mauris aliquet, convallis mi ut, elementum purus. Nulla purus nulla, sodales at hendrerit quis, tempus sed lectus.
|
||||
|
||||
Nam ut laoreet neque, ut maximus nibh. Maecenas quis justo pellentesque, sollicitudin elit at, venenatis velit. Aenean nunc velit, vehicula scelerisque odio at, consectetur laoreet purus. Duis dui purus, malesuada quis ipsum sit amet, tempor interdum libero. Curabitur porta scelerisque sapien, vitae cursus diam condimentum eu. Phasellus sed orci quam. Nullam vitae dui quis purus tincidunt vestibulum. Curabitur quis nulla porta, cursus arcu non, auctor enim. Etiam sollicitudin ex id sem vehicula mollis. Morbi viverra laoreet tincidunt. Praesent ut semper dui. Nam sit amet pretium neque. Mauris vitae luctus diam, in lacinia purus. Maecenas ut placerat justo, ut porta felis. Integer eu mauris ante.
|
||||
|
||||
Aenean porttitor tellus diam, tempor consequat metus efficitur id. Suspendisse ut felis at erat tempor dictum at nec sapien. Sed vestibulum interdum felis, ac mattis mauris porta in. Nunc et condimentum massa. Sed cursus dictum justo et luctus. Integer convallis enim nisl, a rutrum lectus ultricies in. Donec dapibus lacus at nulla dapibus, id sollicitudin velit hendrerit. Fusce a magna at orci mollis rutrum ac a dolor. Aliquam erat volutpat. Morbi varius porta nunc, sit amet sodales ex hendrerit commodo. Donec tincidunt tortor sapien, vitae egestas sapien vehicula eget.
|
||||
|
||||
Suspendisse potenti. Donec pulvinar felis nec leo malesuada interdum. Integer posuere placerat maximus. Donec nibh ipsum, tincidunt vitae luctus vitae, bibendum at leo. Sed cursus nisl ut ex faucibus aliquet sed nec eros. Curabitur molestie posuere felis. Integer faucibus velit eget consequat iaculis. Mauris sed vulputate odio. Phasellus maximus, elit a pharetra egestas, lorem magna semper tellus, vestibulum semper diam felis at sapien. Suspendisse facilisis, nisl sit amet euismod vehicula, libero nulla vehicula dolor, quis fermentum nibh elit sit amet diam.
|
||||
|
||||
Morbi lorem enim, euismod eu varius ut, scelerisque quis odio. Nam tempus vitae eros id molestie. Nunc pretium in nulla eget accumsan. Quisque mattis est ut semper aliquet. Maecenas eget diam elementum, fermentum ipsum a, euismod sapien. Duis quam ligula, cursus et velit nec, ullamcorper tincidunt magna. Donec vulputate nisl est, et ullamcorper urna tempor sit amet.
|
||||
|
||||
Proin lacinia dui non turpis congue pretium. Morbi posuere metus vel purus imperdiet interdum. Morbi venenatis vel eros non ultricies. Nulla vel semper elit. Ut quis purus tincidunt, auctor justo ut, faucibus turpis. Proin quis mattis erat, at faucibus ligula. Mauris in mauris enim. Donec facilisis enim at est feugiat hendrerit. Nam vel nisi lorem. Fusce ultricies convallis diam, in feugiat tortor luctus quis. Donec tempor, leo vitae volutpat aliquam, magna elit feugiat leo, quis placerat sapien felis eget arcu. Donec ornare fermentum eleifend. Integer a est orci.
|
||||
|
||||
Proin rhoncus egestas leo. Nulla ultricies porta elit quis ornare. Nunc fermentum interdum vehicula. In in ligula lorem. Donec nec arcu sit amet orci lobortis iaculis. Mauris at mollis erat, sit amet mollis tortor. Mauris laoreet justo ullamcorper porttitor auctor. Aenean sit amet aliquam lectus, id fermentum eros. Praesent urna sem, vehicula ac fermentum id, dapibus ut purus. Vestibulum vitae tempus nunc. Donec at nunc ornare metus volutpat porta at eget magna. Donec varius aliquet metus, eu lobortis risus aliquam sed. Ut dapibus fermentum velit, ac tincidunt libero faucibus at.
|
||||
|
||||
In in purus auctor, feugiat massa quis, facilisis nisi. Donec dolor purus, gravida eget dolor ac, porttitor imperdiet urna. Donec faucibus placerat erat, a sagittis ante finibus ac. Sed venenatis dignissim elit, in iaculis felis posuere faucibus. Praesent sed viverra dolor. Mauris sed nulla consectetur nunc laoreet molestie in ut metus. Proin ac ex sit amet magna vulputate hendrerit ac condimentum urna. Proin ligula metus, gravida et sollicitudin facilisis, iaculis ut odio. Cras tincidunt urna et augue varius, ut facilisis urna consequat. Aenean vehicula finibus quam. Ut iaculis eu diam ac mollis. Nam mi lorem, tristique eget varius at, sodales at urna.
|
||||
|
||||
Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Proin vitae dictum erat, et auctor ipsum. Nullam nunc nunc, sollicitudin quis magna a, vestibulum fermentum mauris. Praesent at erat dolor. Proin laoreet tristique nulla vel efficitur. Nam sed ultrices nibh, id rutrum nunc. Curabitur eleifend a erat sit amet sollicitudin. Nullam metus quam, laoreet vitae dapibus id, placerat sed leo. Aliquam erat volutpat. Donec turpis nisl, cursus eu ex sit amet, lacinia pellentesque nisl. Sed id ipsum massa. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Donec interdum scelerisque lorem eu mattis.
|
||||
|
||||
Vivamus ac tristique massa, nec facilisis nisl. Nam ipsum neque, tincidunt vel urna in, cursus imperdiet enim. Nam pellentesque egestas tempus. Morbi facilisis imperdiet libero vitae fringilla. Nam lacinia ligula at sapien facilisis malesuada. Nullam accumsan pulvinar sem, et cursus libero porta sit amet. Curabitur vulputate erat elit, ut pulvinar erat maximus vel.
|
||||
|
||||
Cras aliquet metus ut purus sagittis, vel venenatis ante consectetur. Pellentesque nulla lacus, viverra viverra mattis non, placerat vitae nibh. Donec enim turpis, accumsan sit amet tincidunt eu, imperdiet non metus. Morbi ipsum eros, tincidunt vel est ac, tristique porttitor nibh. Praesent ut ullamcorper mauris. Sed laoreet sit amet diam congue venenatis. Integer porta purus nec orci sagittis posuere.
|
||||
|
||||
Donec vehicula mauris eget lacus mollis venenatis et sed nibh. Nam sodales ligula ipsum, scelerisque lacinia ligula sagittis in. Nam sit amet ipsum at erat malesuada congue. Aenean ut sollicitudin sapien. Etiam at tempor odio. Mauris vitae purus ut magna suscipit consequat. Vivamus quis sapien neque. Nulla vulputate sem sit amet massa pellentesque, eleifend tristique ligula egestas. Suspendisse tincidunt gravida mi, in pulvinar lectus egestas non. Aenean imperdiet ex sit amet nunc sollicitudin porta. Integer justo odio, ultricies at interdum in, rhoncus vitae sem. Sed porttitor arcu quis purus aliquet hendrerit. Praesent tempor tortor at dolor dictum pulvinar. Nulla aliquet nunc non ligula scelerisque accumsan. Donec nulla justo, congue vitae massa in, faucibus hendrerit magna. Donec non egestas purus.
|
||||
|
||||
Vivamus iaculis, lacus efficitur faucibus porta, dui nulla facilisis ligula, ut sodales odio nunc id sapien. Cras viverra auctor ipsum, dapibus mattis neque dictum sed. Sed convallis fermentum molestie. Nulla facilisi turpis duis.
|
||||
|
||||
@@ -4,11 +4,11 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { SyncDescriptor } from './descriptors';
|
||||
import { ServiceIdentifier, IConstructorSignature0 } from './instantiation';
|
||||
import { ServiceIdentifier, BrandedService } from './instantiation';
|
||||
|
||||
const _registry: [ServiceIdentifier<any>, SyncDescriptor<any>][] = [];
|
||||
|
||||
export function registerSingleton<T>(id: ServiceIdentifier<T>, ctor: IConstructorSignature0<T>, supportsDelayedInstantiation?: boolean): void {
|
||||
export function registerSingleton<T, Services extends BrandedService[]>(id: ServiceIdentifier<T>, ctor: { new(...services: Services): T }, supportsDelayedInstantiation?: boolean): void {
|
||||
_registry.push([id, new SyncDescriptor<T>(ctor, [], supportsDelayedInstantiation)]);
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ export namespace _util {
|
||||
|
||||
// --- interfaces ------
|
||||
|
||||
type BrandedService = { _serviceBrand: undefined };
|
||||
export type BrandedService = { _serviceBrand: undefined };
|
||||
|
||||
export interface IConstructorSignature0<T> {
|
||||
new(...services: BrandedService[]): T;
|
||||
@@ -67,6 +67,22 @@ export interface ServicesAccessor {
|
||||
|
||||
export const IInstantiationService = createDecorator<IInstantiationService>('instantiationService');
|
||||
|
||||
/**
|
||||
* Given a list of arguments as a tuple, attempt to extract the leading, non-service arguments
|
||||
* to their own tuple.
|
||||
*/
|
||||
type GetLeadingNonServiceArgs<Args> =
|
||||
Args extends [...BrandedService[]] ? []
|
||||
: Args extends [infer A1, ...BrandedService[]] ? [A1]
|
||||
: Args extends [infer A1, infer A2, ...BrandedService[]] ? [A1, A2]
|
||||
: Args extends [infer A1, infer A2, infer A3, ...BrandedService[]] ? [A1, A2, A3]
|
||||
: Args extends [infer A1, infer A2, infer A3, infer A4, ...BrandedService[]] ? [A1, A2, A3, A4]
|
||||
: Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, ...BrandedService[]] ? [A1, A2, A3, A4, A5]
|
||||
: Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, ...BrandedService[]] ? [A1, A2, A3, A4, A5, A6]
|
||||
: Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, ...BrandedService[]] ? [A1, A2, A3, A4, A5, A6, A7]
|
||||
: Args extends [infer A1, infer A2, infer A3, infer A4, infer A5, infer A6, infer A7, infer A8, ...BrandedService[]] ? [A1, A2, A3, A4, A5, A6, A7, A8]
|
||||
: never;
|
||||
|
||||
export interface IInstantiationService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
@@ -85,15 +101,8 @@ export interface IInstantiationService {
|
||||
createInstance<A1, A2, A3, A4, A5, A6, A7, T>(descriptor: descriptors.SyncDescriptor7<A1, A2, A3, A4, A5, A6, A7, T>, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7): T;
|
||||
createInstance<A1, A2, A3, A4, A5, A6, A7, A8, T>(descriptor: descriptors.SyncDescriptor8<A1, A2, A3, A4, A5, A6, A7, A8, T>, a1: A1, a2: A2, a3: A3, a4: A4, a5: A5, a6: A6, a7: A7, a8: A8): T;
|
||||
|
||||
createInstance<T>(ctor: IConstructorSignature0<T>): T;
|
||||
createInstance<A1, T>(ctor: IConstructorSignature1<A1, T>, first: A1): T;
|
||||
createInstance<A1, A2, T>(ctor: IConstructorSignature2<A1, A2, T>, first: A1, second: A2): T;
|
||||
createInstance<A1, A2, A3, T>(ctor: IConstructorSignature3<A1, A2, A3, T>, first: A1, second: A2, third: A3): T;
|
||||
createInstance<A1, A2, A3, A4, T>(ctor: IConstructorSignature4<A1, A2, A3, A4, T>, first: A1, second: A2, third: A3, fourth: A4): T;
|
||||
createInstance<A1, A2, A3, A4, A5, T>(ctor: IConstructorSignature5<A1, A2, A3, A4, A5, T>, first: A1, second: A2, third: A3, fourth: A4, fifth: A5): T;
|
||||
createInstance<A1, A2, A3, A4, A5, A6, T>(ctor: IConstructorSignature6<A1, A2, A3, A4, A5, A6, T>, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6): T;
|
||||
createInstance<A1, A2, A3, A4, A5, A6, A7, T>(ctor: IConstructorSignature7<A1, A2, A3, A4, A5, A6, A7, T>, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7): T;
|
||||
createInstance<A1, A2, A3, A4, A5, A6, A7, A8, T>(ctor: IConstructorSignature8<A1, A2, A3, A4, A5, A6, A7, A8, T>, first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8): T;
|
||||
createInstance<Ctor extends new (...args: any[]) => any, R extends InstanceType<Ctor>>(t: Ctor, ...args: GetLeadingNonServiceArgs<ConstructorParameters<Ctor>>): R;
|
||||
createInstance<Services extends BrandedService[], Ctor extends new (...services: Services) => any, R extends InstanceType<Ctor>>(t: Ctor): R;
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
@@ -157,7 +157,7 @@ export class InstantiationService implements IInstantiationService {
|
||||
graph.lookupOrInsertNode(item);
|
||||
|
||||
// a weak but working heuristic for cycle checks
|
||||
if (cycleCount++ > 150) { // {{SQL CARBON EDIT}} we hit ~102 with our services
|
||||
if (cycleCount++ > 150) {
|
||||
throw new CyclicDependencyError(graph);
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import { KeyCode, Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes
|
||||
import { Disposable, IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextKeyService, IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { IKeybindingEvent, IKeybindingService, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IKeybindingEvent, IKeybindingService, IKeyboardEvent, KeybindingsSchemaContribution } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IResolveResult, KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver';
|
||||
import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem';
|
||||
import { INotificationService } from 'vs/platform/notification/common/notification';
|
||||
@@ -57,6 +57,7 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
|
||||
public abstract resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[];
|
||||
public abstract resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding;
|
||||
public abstract resolveUserBinding(userBinding: string): ResolvedKeybinding[];
|
||||
public abstract registerSchemaContribution(contribution: KeybindingsSchemaContribution): void;
|
||||
public abstract _dumpDebugInfo(): string;
|
||||
public abstract _dumpDebugInfoJSON(): string;
|
||||
|
||||
@@ -64,11 +65,11 @@ export abstract class AbstractKeybindingService extends Disposable implements IK
|
||||
return '';
|
||||
}
|
||||
|
||||
public getDefaultKeybindings(): ResolvedKeybindingItem[] {
|
||||
public getDefaultKeybindings(): readonly ResolvedKeybindingItem[] {
|
||||
return this._getResolver().getDefaultKeybindings();
|
||||
}
|
||||
|
||||
public getKeybindings(): ResolvedKeybindingItem[] {
|
||||
public getKeybindings(): readonly ResolvedKeybindingItem[] {
|
||||
return this._getResolver().getKeybindings();
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { KeyCode, Keybinding, ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { Keybinding, KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes';
|
||||
import { IContextKeyServiceTarget } from 'vs/platform/contextkey/common/contextkey';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IResolveResult } from 'vs/platform/keybinding/common/keybindingResolver';
|
||||
@@ -38,6 +39,12 @@ export interface IKeyboardEvent {
|
||||
readonly code: string;
|
||||
}
|
||||
|
||||
export interface KeybindingsSchemaContribution {
|
||||
readonly onDidChange?: Event<void>;
|
||||
|
||||
getSchemaAdditions(): IJSONSchema[];
|
||||
}
|
||||
|
||||
export const IKeybindingService = createDecorator<IKeybindingService>('keybindingService');
|
||||
|
||||
export interface IKeybindingService {
|
||||
@@ -80,9 +87,9 @@ export interface IKeybindingService {
|
||||
|
||||
getDefaultKeybindingsContent(): string;
|
||||
|
||||
getDefaultKeybindings(): ResolvedKeybindingItem[];
|
||||
getDefaultKeybindings(): readonly ResolvedKeybindingItem[];
|
||||
|
||||
getKeybindings(): ResolvedKeybindingItem[];
|
||||
getKeybindings(): readonly ResolvedKeybindingItem[];
|
||||
|
||||
customKeybindingsCount(): number;
|
||||
|
||||
@@ -92,6 +99,8 @@ export interface IKeybindingService {
|
||||
*/
|
||||
mightProducePrintableCharacter(event: IKeyboardEvent): boolean;
|
||||
|
||||
registerSchemaContribution(contribution: KeybindingsSchemaContribution): void;
|
||||
|
||||
_dumpDebugInfo(): string;
|
||||
_dumpDebugInfoJSON(): string;
|
||||
}
|
||||
|
||||
@@ -215,11 +215,11 @@ export class KeybindingResolver {
|
||||
return this._defaultBoundCommands;
|
||||
}
|
||||
|
||||
public getDefaultKeybindings(): ResolvedKeybindingItem[] {
|
||||
public getDefaultKeybindings(): readonly ResolvedKeybindingItem[] {
|
||||
return this._defaultKeybindings;
|
||||
}
|
||||
|
||||
public getKeybindings(): ResolvedKeybindingItem[] {
|
||||
public getKeybindings(): readonly ResolvedKeybindingItem[] {
|
||||
return this._keybindings;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,10 @@ suite('AbstractKeybindingService', () => {
|
||||
public _dumpDebugInfoJSON(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public registerSchemaContribution() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
let createTestKeybindingService: (items: ResolvedKeybindingItem[], contextValue?: any) => TestKeybindingService = null!;
|
||||
|
||||
@@ -141,4 +141,8 @@ export class MockKeybindingService implements IKeybindingService {
|
||||
public _dumpDebugInfoJSON(): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
public registerSchemaContribution() {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ import { IEditorOptions } from 'vs/platform/editor/common/editor';
|
||||
import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { Registry } from 'vs/platform/registry/common/platform';
|
||||
import { attachListStyler, computeStyles, defaultListStyles } from 'vs/platform/theme/common/styler';
|
||||
import { attachListStyler, computeStyles, defaultListStyles, IColorMapping, attachStyler } from 'vs/platform/theme/common/styler';
|
||||
import { IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { InputFocusedContextKey } from 'vs/platform/contextkey/common/contextkeys';
|
||||
import { ObjectTree, IObjectTreeOptions, ICompressibleTreeRenderer, CompressibleObjectTree, ICompressibleObjectTreeOptions } from 'vs/base/browser/ui/tree/objectTree';
|
||||
import { ITreeEvent, ITreeRenderer, IAsyncDataSource, IDataSource, ITreeMouseEvent } from 'vs/base/browser/ui/tree/tree';
|
||||
import { AsyncDataTree, IAsyncDataTreeOptions, CompressibleAsyncDataTree, ITreeCompressionDelegate } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { AsyncDataTree, IAsyncDataTreeOptions, CompressibleAsyncDataTree, ITreeCompressionDelegate, ICompressibleAsyncDataTreeOptions } from 'vs/base/browser/ui/tree/asyncDataTree';
|
||||
import { DataTree, IDataTreeOptions } from 'vs/base/browser/ui/tree/dataTree';
|
||||
import { IKeyboardNavigationEventFilter, IAbstractTreeOptions, RenderIndentGuides } from 'vs/base/browser/ui/tree/abstractTree';
|
||||
import { IAccessibilityService, AccessibilitySupport } from 'vs/platform/accessibility/common/accessibility';
|
||||
@@ -55,6 +55,7 @@ export class ListService implements IListService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
private disposables = new DisposableStore();
|
||||
private lists: IRegisteredList[] = [];
|
||||
private _lastFocusedWidget: ListWidget | undefined = undefined;
|
||||
|
||||
@@ -62,7 +63,11 @@ export class ListService implements IListService {
|
||||
return this._lastFocusedWidget;
|
||||
}
|
||||
|
||||
constructor(@IContextKeyService contextKeyService: IContextKeyService) { }
|
||||
constructor(@IThemeService themeService: IThemeService) {
|
||||
// create a shared default tree style sheet for performance reasons
|
||||
const styleController = new DefaultStyleController(createStyleSheet(), '');
|
||||
this.disposables.add(attachListStyler(styleController, themeService));
|
||||
}
|
||||
|
||||
register(widget: ListWidget, extraContextKeys?: (IContextKey<boolean>)[]): IDisposable {
|
||||
if (this.lists.some(l => l.widget === widget)) {
|
||||
@@ -89,6 +94,10 @@ export class ListService implements IListService {
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this.disposables.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
const RawWorkbenchListFocusContextKey = new RawContextKey<boolean>('listFocus', true);
|
||||
@@ -221,13 +230,8 @@ function toWorkbenchListOptions<T>(options: IListOptions<T>, configurationServic
|
||||
return [result, disposables];
|
||||
}
|
||||
|
||||
let sharedListStyleSheet: HTMLStyleElement;
|
||||
function getSharedListStyleSheet(): HTMLStyleElement {
|
||||
if (!sharedListStyleSheet) {
|
||||
sharedListStyleSheet = createStyleSheet();
|
||||
}
|
||||
|
||||
return sharedListStyleSheet;
|
||||
export interface IWorkbenchListOptions<T> extends IListOptions<T> {
|
||||
readonly overrideStyles?: IColorMapping;
|
||||
}
|
||||
|
||||
export class WorkbenchList<T> extends List<T> {
|
||||
@@ -246,7 +250,7 @@ export class WorkbenchList<T> extends List<T> {
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: IListRenderer<T, any>[],
|
||||
options: IListOptions<T>,
|
||||
options: IWorkbenchListOptions<T>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -259,7 +263,6 @@ export class WorkbenchList<T> extends List<T> {
|
||||
super(user, container, delegate, renderers,
|
||||
{
|
||||
keyboardSupport: false,
|
||||
styleController: new DefaultStyleController(getSharedListStyleSheet()),
|
||||
...computeStyles(themeService.getTheme(), defaultListStyles),
|
||||
...workbenchListOptions,
|
||||
horizontalScrolling
|
||||
@@ -282,7 +285,11 @@ export class WorkbenchList<T> extends List<T> {
|
||||
|
||||
this.disposables.add(this.contextKeyService);
|
||||
this.disposables.add((listService as ListService).register(this));
|
||||
this.disposables.add(attachListStyler(this, themeService));
|
||||
|
||||
if (options.overrideStyles) {
|
||||
this.disposables.add(attachStyler(themeService, options.overrideStyles, this));
|
||||
}
|
||||
|
||||
this.disposables.add(this.onSelectionChange(() => {
|
||||
const selection = this.getSelection();
|
||||
const focus = this.getFocus();
|
||||
@@ -328,7 +335,7 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<number>,
|
||||
renderers: IPagedRenderer<T, any>[],
|
||||
options: IListOptions<T>,
|
||||
options: IWorkbenchListOptions<T>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -340,7 +347,6 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
|
||||
super(user, container, delegate, renderers,
|
||||
{
|
||||
keyboardSupport: false,
|
||||
styleController: new DefaultStyleController(getSharedListStyleSheet()),
|
||||
...computeStyles(themeService.getTheme(), defaultListStyles),
|
||||
...workbenchListOptions,
|
||||
horizontalScrolling
|
||||
@@ -360,7 +366,10 @@ export class WorkbenchPagedList<T> extends PagedList<T> {
|
||||
|
||||
this.disposables.add(this.contextKeyService);
|
||||
this.disposables.add((listService as ListService).register(this));
|
||||
this.disposables.add(attachListStyler(this, themeService));
|
||||
|
||||
if (options.overrideStyles) {
|
||||
this.disposables.add(attachStyler(themeService, options.overrideStyles, this));
|
||||
}
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
@@ -777,6 +786,10 @@ function createKeyboardNavigationEventFilter(container: HTMLElement, keybindingS
|
||||
};
|
||||
}
|
||||
|
||||
export interface IWorkbenchObjectTreeOptions<T, TFilterData> extends IObjectTreeOptions<T, TFilterData> {
|
||||
readonly overrideStyles?: IColorMapping;
|
||||
}
|
||||
|
||||
export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void> extends ObjectTree<T, TFilterData> {
|
||||
|
||||
private internals: WorkbenchTreeInternals<any, T, TFilterData>;
|
||||
@@ -788,7 +801,7 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
options: IObjectTreeOptions<T, TFilterData>,
|
||||
options: IWorkbenchObjectTreeOptions<T, TFilterData>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -796,14 +809,18 @@ export class WorkbenchObjectTree<T extends NonNullable<any>, TFilterData = void>
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWorkbenchCompressibleObjectTreeOptions<T, TFilterData> extends ICompressibleObjectTreeOptions<T, TFilterData> {
|
||||
readonly overrideStyles?: IColorMapping;
|
||||
}
|
||||
|
||||
export class WorkbenchCompressibleObjectTree<T extends NonNullable<any>, TFilterData = void> extends CompressibleObjectTree<T, TFilterData> {
|
||||
|
||||
private internals: WorkbenchTreeInternals<any, T, TFilterData>;
|
||||
@@ -815,7 +832,7 @@ export class WorkbenchCompressibleObjectTree<T extends NonNullable<any>, TFilter
|
||||
container: HTMLElement,
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ICompressibleTreeRenderer<T, TFilterData, any>[],
|
||||
options: ICompressibleObjectTreeOptions<T, TFilterData>,
|
||||
options: IWorkbenchCompressibleObjectTreeOptions<T, TFilterData>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -823,14 +840,18 @@ export class WorkbenchCompressibleObjectTree<T extends NonNullable<any>, TFilter
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWorkbenchDataTreeOptions<T, TFilterData> extends IDataTreeOptions<T, TFilterData> {
|
||||
readonly overrideStyles?: IColorMapping;
|
||||
}
|
||||
|
||||
export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<TInput, T, TFilterData> {
|
||||
|
||||
private internals: WorkbenchTreeInternals<TInput, T, TFilterData>;
|
||||
@@ -843,7 +864,7 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
dataSource: IDataSource<TInput, T>,
|
||||
options: IDataTreeOptions<T, TFilterData>,
|
||||
options: IWorkbenchDataTreeOptions<T, TFilterData>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -851,14 +872,18 @@ export class WorkbenchDataTree<TInput, T, TFilterData = void> extends DataTree<T
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWorkbenchAsyncDataTreeOptions<T, TFilterData> extends IAsyncDataTreeOptions<T, TFilterData> {
|
||||
readonly overrideStyles?: IColorMapping;
|
||||
}
|
||||
|
||||
export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends AsyncDataTree<TInput, T, TFilterData> {
|
||||
|
||||
private internals: WorkbenchTreeInternals<TInput, T, TFilterData>;
|
||||
@@ -871,7 +896,7 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
|
||||
delegate: IListVirtualDelegate<T>,
|
||||
renderers: ITreeRenderer<T, TFilterData, any>[],
|
||||
dataSource: IAsyncDataSource<TInput, T>,
|
||||
options: IAsyncDataTreeOptions<T, TFilterData>,
|
||||
options: IWorkbenchAsyncDataTreeOptions<T, TFilterData>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -879,14 +904,18 @@ export class WorkbenchAsyncDataTree<TInput, T, TFilterData = void> extends Async
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, delegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
}
|
||||
|
||||
export interface IWorkbenchCompressibleAsyncDataTreeOptions<T, TFilterData> extends ICompressibleAsyncDataTreeOptions<T, TFilterData> {
|
||||
readonly overrideStyles?: IColorMapping;
|
||||
}
|
||||
|
||||
export class WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData = void> extends CompressibleAsyncDataTree<TInput, T, TFilterData> {
|
||||
|
||||
private internals: WorkbenchTreeInternals<TInput, T, TFilterData>;
|
||||
@@ -900,7 +929,7 @@ export class WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData = void> e
|
||||
compressionDelegate: ITreeCompressionDelegate<T>,
|
||||
renderers: ICompressibleTreeRenderer<T, TFilterData, any>[],
|
||||
dataSource: IAsyncDataSource<TInput, T>,
|
||||
options: IAsyncDataTreeOptions<T, TFilterData>,
|
||||
options: IWorkbenchCompressibleAsyncDataTreeOptions<T, TFilterData>,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -908,10 +937,10 @@ export class WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData = void> e
|
||||
@IKeybindingService keybindingService: IKeybindingService,
|
||||
@IAccessibilityService accessibilityService: IAccessibilityService
|
||||
) {
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, themeService, configurationService, keybindingService, accessibilityService);
|
||||
const { options: treeOptions, getAutomaticKeyboardNavigation, disposable } = workbenchTreeDataPreamble(container, options, contextKeyService, configurationService, keybindingService, accessibilityService);
|
||||
super(user, container, virtualDelegate, compressionDelegate, renderers, dataSource, treeOptions);
|
||||
this.disposables.add(disposable);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.internals = new WorkbenchTreeInternals(this, treeOptions, getAutomaticKeyboardNavigation, options.overrideStyles, contextKeyService, listService, themeService, configurationService, accessibilityService);
|
||||
this.disposables.add(this.internals);
|
||||
}
|
||||
}
|
||||
@@ -920,7 +949,6 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
|
||||
container: HTMLElement,
|
||||
options: TOptions,
|
||||
contextKeyService: IContextKeyService,
|
||||
themeService: IThemeService,
|
||||
configurationService: IConfigurationService,
|
||||
keybindingService: IKeybindingService,
|
||||
accessibilityService: IAccessibilityService,
|
||||
@@ -954,9 +982,8 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
|
||||
getAutomaticKeyboardNavigation,
|
||||
disposable,
|
||||
options: {
|
||||
// ...options, // TODO@Joao why is this not splatted here?
|
||||
keyboardSupport: false,
|
||||
styleController: new DefaultStyleController(getSharedListStyleSheet()),
|
||||
...computeStyles(themeService.getTheme(), defaultListStyles),
|
||||
...workbenchListOptions,
|
||||
indent: configurationService.getValue<number>(treeIndentKey),
|
||||
renderIndentGuides: configurationService.getValue<RenderIndentGuides>(treeRenderIndentGuidesKey),
|
||||
@@ -966,7 +993,8 @@ function workbenchTreeDataPreamble<T, TFilterData, TOptions extends IAbstractTre
|
||||
horizontalScrolling,
|
||||
openOnSingleClick,
|
||||
keyboardNavigationEventFilter: createKeyboardNavigationEventFilter(container, keybindingService),
|
||||
additionalScrollHeight
|
||||
additionalScrollHeight,
|
||||
hideTwistiesOfChildlessElements: options.hideTwistiesOfChildlessElements
|
||||
} as TOptions
|
||||
};
|
||||
}
|
||||
@@ -984,6 +1012,7 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
||||
tree: WorkbenchObjectTree<T, TFilterData> | CompressibleObjectTree<T, TFilterData> | WorkbenchDataTree<TInput, T, TFilterData> | WorkbenchAsyncDataTree<TInput, T, TFilterData> | WorkbenchCompressibleAsyncDataTree<TInput, T, TFilterData>,
|
||||
options: IAbstractTreeOptions<T, TFilterData> | IAsyncDataTreeOptions<T, TFilterData>,
|
||||
getAutomaticKeyboardNavigation: () => boolean | undefined,
|
||||
overrideStyles: IColorMapping | undefined,
|
||||
@IContextKeyService contextKeyService: IContextKeyService,
|
||||
@IListService listService: IListService,
|
||||
@IThemeService themeService: IThemeService,
|
||||
@@ -1015,7 +1044,7 @@ class WorkbenchTreeInternals<TInput, T, TFilterData> {
|
||||
this.disposables.push(
|
||||
this.contextKeyService,
|
||||
(listService as ListService).register(tree),
|
||||
attachListStyler(tree, themeService),
|
||||
overrideStyles ? attachStyler(themeService, overrideStyles, tree) : Disposable.None,
|
||||
tree.onDidChangeSelection(() => {
|
||||
const selection = tree.getSelection();
|
||||
const focus = tree.getFocus();
|
||||
|
||||
@@ -560,8 +560,8 @@ export class Menubar {
|
||||
label: this.mnemonicLabel(nls.localize('miCheckForUpdates', "Check for &&Updates...")), click: () => setTimeout(() => {
|
||||
this.reportMenuActionTelemetry('CheckForUpdate');
|
||||
|
||||
const focusedWindow = BrowserWindow.getFocusedWindow();
|
||||
const context = focusedWindow ? { windowId: focusedWindow.id } : null;
|
||||
const window = this.windowsMainService.getLastActiveWindow();
|
||||
const context = window && `window:${window.id}`; // sessionId
|
||||
this.updateService.checkForUpdates(context);
|
||||
}, 0)
|
||||
})];
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
|
||||
import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings';
|
||||
|
||||
export const IOpenerService = createDecorator<IOpenerService>('openerService');
|
||||
|
||||
@@ -28,13 +29,22 @@ type OpenExternalOptions = { readonly openExternal?: boolean; readonly allowTunn
|
||||
|
||||
export type OpenOptions = OpenInternalOptions & OpenExternalOptions;
|
||||
|
||||
export type ResolveExternalUriOptions = { readonly allowTunneling?: boolean };
|
||||
|
||||
export interface IResolvedExternalUri extends IDisposable {
|
||||
resolved: URI;
|
||||
}
|
||||
|
||||
export interface IOpener {
|
||||
open(resource: URI, options?: OpenInternalOptions): Promise<boolean>;
|
||||
open(resource: URI, options?: OpenExternalOptions): Promise<boolean>;
|
||||
open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface IExternalOpener {
|
||||
openExternal(href: string): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface IValidator {
|
||||
shouldOpen(resource: URI): Promise<boolean>;
|
||||
shouldOpen(resource: URI | string): Promise<boolean>;
|
||||
}
|
||||
|
||||
export interface IExternalUriResolver {
|
||||
@@ -61,16 +71,24 @@ export interface IOpenerService {
|
||||
*/
|
||||
registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable;
|
||||
|
||||
/**
|
||||
* Sets the handler for opening externally. If not provided,
|
||||
* a default handler will be used.
|
||||
*/
|
||||
setExternalOpener(opener: IExternalOpener): void;
|
||||
|
||||
/**
|
||||
* Opens a resource, like a webaddress, a document uri, or executes command.
|
||||
*
|
||||
* @param resource A resource
|
||||
* @return A promise that resolves when the opening is done.
|
||||
*/
|
||||
open(resource: URI, options?: OpenInternalOptions): Promise<boolean>;
|
||||
open(resource: URI, options?: OpenExternalOptions): Promise<boolean>;
|
||||
open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
|
||||
|
||||
resolveExternalUri(resource: URI, options?: { readonly allowTunneling?: boolean }): Promise<{ resolved: URI, dispose(): void }>;
|
||||
/**
|
||||
* Resolve a resource to its external form.
|
||||
*/
|
||||
resolveExternalUri(resource: URI, options?: ResolveExternalUriOptions): Promise<IResolvedExternalUri>;
|
||||
}
|
||||
|
||||
export const NullOpenerService: IOpenerService = Object.freeze({
|
||||
@@ -78,6 +96,15 @@ export const NullOpenerService: IOpenerService = Object.freeze({
|
||||
registerOpener() { return Disposable.None; },
|
||||
registerValidator() { return Disposable.None; },
|
||||
registerExternalUriResolver() { return Disposable.None; },
|
||||
open() { return Promise.resolve(false); },
|
||||
setExternalOpener() { },
|
||||
async open() { return false; },
|
||||
async resolveExternalUri(uri: URI) { return { resolved: uri, dispose() { } }; },
|
||||
});
|
||||
|
||||
export function matchesScheme(target: URI | string, scheme: string) {
|
||||
if (URI.isUri(target)) {
|
||||
return equalsIgnoreCase(target.scheme, scheme);
|
||||
} else {
|
||||
return startsWithIgnoreCase(target, scheme + ':');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,10 +21,11 @@ if (isWeb) {
|
||||
// Running out of sources
|
||||
if (Object.keys(product).length === 0) {
|
||||
assign(product, {
|
||||
version: '1.39.0-dev',
|
||||
version: '1.41.0-dev',
|
||||
vscodeVersion: '1.39.0-dev',
|
||||
nameLong: 'Visual Studio Code Web Dev',
|
||||
nameShort: 'VSCode Web Dev'
|
||||
nameShort: 'VSCode Web Dev',
|
||||
urlProtocol: 'code-oss'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ExtensionKind } from 'vs/platform/extensions/common/extensions';
|
||||
|
||||
export const IProductService = createDecorator<IProductService>('productService');
|
||||
|
||||
@@ -101,13 +102,18 @@ export interface IProductConfiguration {
|
||||
|
||||
readonly portable?: string;
|
||||
|
||||
readonly uiExtensions?: readonly string[];
|
||||
readonly extensionKind?: { readonly [extensionId: string]: ExtensionKind | ExtensionKind[]; };
|
||||
readonly extensionAllowedProposedApi?: readonly string[];
|
||||
|
||||
readonly msftInternalDomains?: string[];
|
||||
readonly linkProtectionTrustedDomains?: readonly string[];
|
||||
|
||||
readonly settingsSyncStoreUrl?: string;
|
||||
readonly auth?: {
|
||||
loginUrl: string;
|
||||
tokenUrl: string;
|
||||
redirectUrl: string;
|
||||
clientId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface IExeBasedExtensionTip {
|
||||
|
||||
@@ -168,9 +168,9 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
|
||||
|
||||
customButton: boolean;
|
||||
|
||||
customLabel: string;
|
||||
customLabel: string | undefined;
|
||||
|
||||
customHover: string;
|
||||
customHover: string | undefined;
|
||||
|
||||
buttons: ReadonlyArray<IQuickInputButton>;
|
||||
|
||||
@@ -188,6 +188,8 @@ export interface IQuickPick<T extends IQuickPickItem> extends IQuickInput {
|
||||
|
||||
matchOnLabel: boolean;
|
||||
|
||||
sortByLabel: boolean;
|
||||
|
||||
autoFocusOnList: boolean;
|
||||
|
||||
quickNavigate: IQuickNavigateConfiguration | undefined;
|
||||
|
||||
@@ -89,8 +89,8 @@ async function connectToRemoteExtensionHostAgent(options: ISimpleConnectionOptio
|
||||
options.host,
|
||||
options.port,
|
||||
`reconnectionToken=${options.reconnectionToken}&reconnection=${options.reconnectionProtocol ? 'true' : 'false'}`,
|
||||
(err: any, socket: ISocket) => {
|
||||
if (err) {
|
||||
(err: any, socket: ISocket | undefined) => {
|
||||
if (err || !socket) {
|
||||
options.logService.error(`${logPrefix} socketFactory.connect() failed. Error:`);
|
||||
options.logService.error(err);
|
||||
e(err);
|
||||
|
||||
@@ -8,10 +8,13 @@ import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle'
|
||||
import { URI, UriComponents } from 'vs/base/common/uri';
|
||||
import { generateUuid } from 'vs/base/common/uuid';
|
||||
import { IChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, IFileChange, IFileSystemProvider, IStat, IWatchOptions, FileOpenOptions } from 'vs/platform/files/common/files';
|
||||
import { FileChangeType, FileDeleteOptions, FileOverwriteOptions, FileSystemProviderCapabilities, FileType, IFileChange, IStat, IWatchOptions, FileOpenOptions, IFileSystemProviderWithFileReadWriteCapability, FileWriteOptions, IFileSystemProviderWithFileReadStreamCapability, IFileSystemProviderWithFileFolderCopyCapability, FileReadStreamOptions, IFileSystemProviderWithOpenReadWriteCloseCapability } from 'vs/platform/files/common/files';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment';
|
||||
import { OperatingSystem } from 'vs/base/common/platform';
|
||||
import { newWriteableStream, ReadableStreamEvents, ReadableStreamEventPayload } from 'vs/base/common/stream';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { canceled } from 'vs/base/common/errors';
|
||||
|
||||
export const REMOTE_FILE_SYSTEM_CHANNEL_NAME = 'remotefilesystem';
|
||||
|
||||
@@ -20,7 +23,11 @@ export interface IFileChangeDto {
|
||||
type: FileChangeType;
|
||||
}
|
||||
|
||||
export class RemoteExtensionsFileSystemProvider extends Disposable implements IFileSystemProvider {
|
||||
export class RemoteFileSystemProvider extends Disposable implements
|
||||
IFileSystemProviderWithFileReadWriteCapability,
|
||||
IFileSystemProviderWithOpenReadWriteCloseCapability,
|
||||
IFileSystemProviderWithFileReadStreamCapability,
|
||||
IFileSystemProviderWithFileFolderCopyCapability {
|
||||
|
||||
private readonly session: string = generateUuid();
|
||||
|
||||
@@ -46,7 +53,7 @@ export class RemoteExtensionsFileSystemProvider extends Disposable implements IF
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this._register(this.channel.listen<IFileChangeDto[] | string>('filechange', [this.session])((eventsOrError) => {
|
||||
this._register(this.channel.listen<IFileChangeDto[] | string>('filechange', [this.session])(eventsOrError => {
|
||||
if (Array.isArray(eventsOrError)) {
|
||||
const events = eventsOrError;
|
||||
this._onDidChange.fire(events.map(event => ({ resource: URI.revive(event.resource), type: event.type })));
|
||||
@@ -59,7 +66,9 @@ export class RemoteExtensionsFileSystemProvider extends Disposable implements IF
|
||||
|
||||
setCaseSensitive(isCaseSensitive: boolean) {
|
||||
let capabilities = (
|
||||
FileSystemProviderCapabilities.FileOpenReadWriteClose
|
||||
FileSystemProviderCapabilities.FileReadWrite
|
||||
| FileSystemProviderCapabilities.FileOpenReadWriteClose
|
||||
| FileSystemProviderCapabilities.FileReadStream
|
||||
| FileSystemProviderCapabilities.FileFolderCopy
|
||||
);
|
||||
|
||||
@@ -97,10 +106,57 @@ export class RemoteExtensionsFileSystemProvider extends Disposable implements IF
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
async readFile(resource: URI): Promise<Uint8Array> {
|
||||
const buff = <VSBuffer>await this.channel.call('readFile', [resource]);
|
||||
|
||||
return buff.buffer;
|
||||
}
|
||||
|
||||
readFileStream(resource: URI, opts: FileReadStreamOptions, token?: CancellationToken): ReadableStreamEvents<Uint8Array> {
|
||||
const stream = newWriteableStream<Uint8Array>(data => VSBuffer.concat(data.map(data => VSBuffer.wrap(data))).buffer);
|
||||
|
||||
// Reading as file stream goes through an event to the remote side
|
||||
const listener = this.channel.listen<ReadableStreamEventPayload<VSBuffer>>('readFileStream', [resource, opts])(dataOrErrorOrEnd => {
|
||||
if (dataOrErrorOrEnd instanceof VSBuffer) {
|
||||
|
||||
// data: forward into the stream
|
||||
stream.write(dataOrErrorOrEnd.buffer);
|
||||
} else {
|
||||
|
||||
// error / end: always end the stream on errors too
|
||||
stream.end(dataOrErrorOrEnd === 'end' ? undefined : dataOrErrorOrEnd);
|
||||
|
||||
// Signal to the remote side that we no longer listen
|
||||
listener.dispose();
|
||||
}
|
||||
});
|
||||
|
||||
// Support cancellation
|
||||
if (token) {
|
||||
Event.once(token.onCancellationRequested)(() => {
|
||||
|
||||
// Ensure to end the stream properly with an error
|
||||
// to indicate the cancellation.
|
||||
stream.end(canceled());
|
||||
|
||||
// Ensure to dispose the listener upon cancellation. This will
|
||||
// bubble through the remote side as event and allows to stop
|
||||
// reading the file.
|
||||
listener.dispose();
|
||||
});
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
write(fd: number, pos: number, data: Uint8Array, offset: number, length: number): Promise<number> {
|
||||
return this.channel.call('write', [fd, pos, VSBuffer.wrap(data), offset, length]);
|
||||
}
|
||||
|
||||
writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise<void> {
|
||||
return this.channel.call('writeFile', [resource, VSBuffer.wrap(content), opts]);
|
||||
}
|
||||
|
||||
delete(resource: URI, opts: FileDeleteOptions): Promise<void> {
|
||||
return this.channel.call('delete', [resource, opts]);
|
||||
}
|
||||
|
||||
@@ -5,13 +5,14 @@
|
||||
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
|
||||
export const ITunnelService = createDecorator<ITunnelService>('tunnelService');
|
||||
|
||||
export interface RemoteTunnel {
|
||||
readonly tunnelRemotePort: number;
|
||||
readonly tunnelLocalPort: number;
|
||||
|
||||
readonly localAddress?: string;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
@@ -19,8 +20,11 @@ export interface ITunnelService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
readonly tunnels: Promise<readonly RemoteTunnel[]>;
|
||||
readonly onTunnelOpened: Event<RemoteTunnel>;
|
||||
readonly onTunnelClosed: Event<number>;
|
||||
|
||||
openTunnel(remotePort: number): Promise<RemoteTunnel> | undefined;
|
||||
openTunnel(remotePort: number, localPort?: number): Promise<RemoteTunnel> | undefined;
|
||||
closeTunnel(remotePort: number): Promise<void>;
|
||||
}
|
||||
|
||||
export function extractLocalHostUriMetaDataForPortMapping(uri: URI): { address: string, port: number } | undefined {
|
||||
|
||||
@@ -4,13 +4,19 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
|
||||
export class NoOpTunnelService implements ITunnelService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
public readonly tunnels: Promise<readonly RemoteTunnel[]> = Promise.resolve([]);
|
||||
|
||||
private _onTunnelOpened: Emitter<RemoteTunnel> = new Emitter();
|
||||
public onTunnelOpened: Event<RemoteTunnel> = this._onTunnelOpened.event;
|
||||
private _onTunnelClosed: Emitter<number> = new Emitter();
|
||||
public onTunnelClosed: Event<number> = this._onTunnelClosed.event;
|
||||
openTunnel(_remotePort: number): Promise<RemoteTunnel> | undefined {
|
||||
return undefined;
|
||||
}
|
||||
async closeTunnel(_remotePort: number): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ Registry.as<IConfigurationRegistry>(Extensions.Configuration)
|
||||
properties: {
|
||||
'http.proxy': {
|
||||
type: 'string',
|
||||
pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+)(:\\d+)?/?$|^$',
|
||||
pattern: '^https?://([^:]*(:[^@]*)?@)?([^:]+|\\[[:0-9a-fA-F]+\\])(:\\d+)?/?$|^$',
|
||||
markdownDescription: localize('proxy', "The proxy setting to use. If not set, will be inherited from the `http_proxy` and `https_proxy` environment variables.")
|
||||
},
|
||||
'http.proxyStrictSSL': {
|
||||
|
||||
@@ -39,12 +39,12 @@ export async function getProxyAgent(rawRequestURL: string, options: IOptions = {
|
||||
|
||||
const opts = {
|
||||
host: proxyEndpoint.hostname || '',
|
||||
port: Number(proxyEndpoint.port),
|
||||
port: proxyEndpoint.port || (proxyEndpoint.protocol === 'https' ? '443' : '80'),
|
||||
auth: proxyEndpoint.auth,
|
||||
rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true
|
||||
rejectUnauthorized: isBoolean(options.strictSSL) ? options.strictSSL : true,
|
||||
};
|
||||
|
||||
return requestURL.protocol === 'http:'
|
||||
? new (await import('http-proxy-agent'))(opts)
|
||||
? new (await import('http-proxy-agent'))(opts as any as Url)
|
||||
: new (await import('https-proxy-agent'))(opts);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
import * as https from 'https';
|
||||
import * as http from 'http';
|
||||
import { Stream } from 'stream';
|
||||
import * as streams from 'vs/base/common/stream';
|
||||
import { createGunzip } from 'zlib';
|
||||
import { parse as parseUrl } from 'url';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
@@ -18,7 +18,7 @@ import { IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/r
|
||||
import { getProxyAgent, Agent } from 'vs/platform/request/node/proxy';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { toVSBufferReadableStream } from 'vs/base/common/buffer';
|
||||
import { streamToBufferReadableStream } from 'vs/base/common/buffer';
|
||||
|
||||
export interface IRawRequestFunction {
|
||||
(options: http.RequestOptions, callback?: (res: http.IncomingMessage) => void): http.ClientRequest;
|
||||
@@ -112,13 +112,13 @@ export class RequestService extends Disposable implements IRequestService {
|
||||
followRedirects: followRedirects - 1
|
||||
}), token).then(c, e);
|
||||
} else {
|
||||
let stream: Stream = res;
|
||||
let stream: streams.ReadableStream<Uint8Array> = res;
|
||||
|
||||
if (res.headers['content-encoding'] === 'gzip') {
|
||||
stream = stream.pipe(createGunzip());
|
||||
stream = res.pipe(createGunzip());
|
||||
}
|
||||
|
||||
c({ res, stream: toVSBufferReadableStream(stream) } as IRequestContext);
|
||||
c({ res, stream: streamToBufferReadableStream(stream) } as IRequestContext);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -31,7 +31,8 @@ registerThemingParticipant((theme, collector) => {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .zone-widget .codicon-error,
|
||||
.monaco-workbench .markers-panel .marker-icon.codicon-error,
|
||||
.monaco-workbench .extensions-viewlet > .extensions .codicon-error {
|
||||
.monaco-workbench .extensions-viewlet > .extensions .codicon-error,
|
||||
.monaco-workbench .dialog-box .dialog-message-row .codicon-error {
|
||||
color: ${errorIconForeground};
|
||||
}
|
||||
`);
|
||||
@@ -42,7 +43,9 @@ registerThemingParticipant((theme, collector) => {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .zone-widget .codicon-warning,
|
||||
.monaco-workbench .markers-panel .marker-icon.codicon-warning,
|
||||
.monaco-workbench .extensions-viewlet > .extensions .codicon-warning {
|
||||
.monaco-workbench .extensions-viewlet > .extensions .codicon-warning,
|
||||
.monaco-workbench .extension-editor .codicon-warning,
|
||||
.monaco-workbench .dialog-box .dialog-message-row .codicon-warning {
|
||||
color: ${warningIconForeground};
|
||||
}
|
||||
`);
|
||||
@@ -52,7 +55,10 @@ registerThemingParticipant((theme, collector) => {
|
||||
if (errorIconForeground) {
|
||||
collector.addRule(`
|
||||
.monaco-workbench .zone-widget .codicon-info,
|
||||
.monaco-workbench .markers-panel .marker-icon.codicon-info {
|
||||
.monaco-workbench .markers-panel .marker-icon.codicon-info,
|
||||
.monaco-workbench .extensions-viewlet > .extensions .codicon-info,
|
||||
.monaco-workbench .extension-editor .codicon-info,
|
||||
.monaco-workbench .dialog-box .dialog-message-row .codicon-info {
|
||||
color: ${infoIconForeground};
|
||||
}
|
||||
`);
|
||||
|
||||
@@ -83,7 +83,7 @@ export class GlobalStorageDatabaseChannel extends Disposable implements IServerC
|
||||
|
||||
// Listen for changes in global storage to send to listeners
|
||||
// that are listening. Use a debouncer to reduce IPC traffic.
|
||||
this._register(Event.debounce(this.storageMainService.onDidChangeStorage, (prev: IStorageChangeEvent[], cur: IStorageChangeEvent) => {
|
||||
this._register(Event.debounce(this.storageMainService.onDidChangeStorage, (prev: IStorageChangeEvent[] | undefined, cur: IStorageChangeEvent) => {
|
||||
if (!prev) {
|
||||
prev = [cur];
|
||||
} else {
|
||||
@@ -161,7 +161,7 @@ export class GlobalStorageDatabaseChannelClient extends Disposable implements IS
|
||||
}
|
||||
|
||||
private registerListeners(): void {
|
||||
this.onDidChangeItemsOnMainListener = this.channel.listen('onDidChangeItems')((e: ISerializableItemsChangeEvent) => this.onDidChangeItemsOnMain(e));
|
||||
this.onDidChangeItemsOnMainListener = this.channel.listen<ISerializableItemsChangeEvent>('onDidChangeItems')((e: ISerializableItemsChangeEvent) => this.onDidChangeItemsOnMain(e));
|
||||
}
|
||||
|
||||
private onDidChangeItemsOnMain(e: ISerializableItemsChangeEvent): void {
|
||||
|
||||
@@ -9,6 +9,7 @@ export const instanceStorageKey = 'telemetry.instanceId';
|
||||
export const currentSessionDateStorageKey = 'telemetry.currentSessionDate';
|
||||
export const firstSessionDateStorageKey = 'telemetry.firstSessionDate';
|
||||
export const lastSessionDateStorageKey = 'telemetry.lastSessionDate';
|
||||
export const machineIdKey = 'telemetry.machineId';
|
||||
|
||||
import * as Platform from 'vs/base/common/platform';
|
||||
import * as uuid from 'vs/base/common/uuid';
|
||||
@@ -19,7 +20,6 @@ export async function resolveWorkbenchCommonProperties(
|
||||
storageService: IStorageService,
|
||||
commit: string | undefined,
|
||||
version: string | undefined,
|
||||
machineId: string,
|
||||
remoteAuthority?: string,
|
||||
resolveAdditionalProperties?: () => { [key: string]: any }
|
||||
): Promise<{ [name: string]: string | undefined }> {
|
||||
@@ -27,6 +27,12 @@ export async function resolveWorkbenchCommonProperties(
|
||||
const firstSessionDate = storageService.get(firstSessionDateStorageKey, StorageScope.GLOBAL)!;
|
||||
const lastSessionDate = storageService.get(lastSessionDateStorageKey, StorageScope.GLOBAL)!;
|
||||
|
||||
let machineId = storageService.get(machineIdKey, StorageScope.GLOBAL);
|
||||
if (!machineId) {
|
||||
machineId = uuid.generateUuid();
|
||||
storageService.store(machineIdKey, machineId, StorageScope.GLOBAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Note: In the web, session date information is fetched from browser storage, so these dates are tied to a specific
|
||||
* browser and not the machine overall.
|
||||
|
||||
@@ -42,7 +42,7 @@ export class AppInsightsAppender implements ITelemetryAppender {
|
||||
constructor(
|
||||
private _eventPrefix: string,
|
||||
private _defaultData: { [key: string]: any } | null,
|
||||
aiKeyOrClientFactory: string | (() => appInsights.ITelemetryClient), // allow factory function for testing
|
||||
aiKeyOrClientFactory: string | (() => appInsights.TelemetryClient), // allow factory function for testing
|
||||
@ILogService private _logService?: ILogService
|
||||
) {
|
||||
if (!this._defaultData) {
|
||||
|
||||
@@ -23,7 +23,7 @@ suite('Browser Telemetry - common properties', function () {
|
||||
};
|
||||
};
|
||||
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, resolveCommonTelemetryProperties);
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, commit, version, undefined, resolveCommonTelemetryProperties);
|
||||
|
||||
assert.ok('commitHash' in props);
|
||||
assert.ok('sessionID' in props);
|
||||
@@ -53,10 +53,10 @@ suite('Browser Telemetry - common properties', function () {
|
||||
});
|
||||
};
|
||||
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, resolveCommonTelemetryProperties);
|
||||
const props = await resolveWorkbenchCommonProperties(testStorageService, commit, version, undefined, resolveCommonTelemetryProperties);
|
||||
assert.equal(props['userId'], '1');
|
||||
|
||||
const props2 = await resolveWorkbenchCommonProperties(testStorageService, commit, version, 'someMachineId', undefined, resolveCommonTelemetryProperties);
|
||||
const props2 = await resolveWorkbenchCommonProperties(testStorageService, commit, version, undefined, resolveCommonTelemetryProperties);
|
||||
assert.equal(props2['userId'], '2');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,15 +5,19 @@
|
||||
import * as assert from 'assert';
|
||||
import { AppInsightsAppender } from 'vs/platform/telemetry/node/appInsightsAppender';
|
||||
import { ILogService, AbstractLogService, LogLevel, DEFAULT_LOG_LEVEL } from 'vs/platform/log/common/log';
|
||||
import { ITelemetryClient, EventTelemetry } from 'applicationinsights';
|
||||
import { TelemetryClient, Contracts } from 'applicationinsights';
|
||||
|
||||
class AppInsightsMock implements ITelemetryClient {
|
||||
class AppInsightsMock extends TelemetryClient {
|
||||
public config: any;
|
||||
public channel: any;
|
||||
public events: EventTelemetry[] = [];
|
||||
public events: Contracts.EventTelemetry[] = [];
|
||||
public IsTrackingPageView: boolean = false;
|
||||
public exceptions: any[] = [];
|
||||
|
||||
constructor() {
|
||||
super('testKey');
|
||||
}
|
||||
|
||||
public trackEvent(event: any) {
|
||||
this.events.push(event);
|
||||
}
|
||||
|
||||
@@ -103,7 +103,7 @@ class ColorRegistry implements IColorRegistry {
|
||||
public registerColor(id: string, defaults: ColorDefaults | null, description: string, needsTransparency = false, deprecationMessage?: string): ColorIdentifier {
|
||||
let colorContribution: ColorContribution = { id, description, defaults, needsTransparency, deprecationMessage };
|
||||
this.colorsById[id] = colorContribution;
|
||||
let propertySchema: IJSONSchema = { type: 'string', description, format: 'color-hex', defaultSnippets: [{ body: '#ff0000' }] };
|
||||
let propertySchema: IJSONSchema = { type: 'string', description, format: 'color-hex', defaultSnippets: [{ body: '${1:#ff0000}' }] };
|
||||
if (deprecationMessage) {
|
||||
propertySchema.deprecationMessage = deprecationMessage;
|
||||
}
|
||||
@@ -301,14 +301,22 @@ export const editorFindMatchBorder = registerColor('editor.findMatchBorder', { l
|
||||
export const editorFindMatchHighlightBorder = registerColor('editor.findMatchHighlightBorder', { light: null, dark: null, hc: activeContrastBorder }, nls.localize('findMatchHighlightBorder', "Border color of the other search matches."));
|
||||
export const editorFindRangeHighlightBorder = registerColor('editor.findRangeHighlightBorder', { dark: null, light: null, hc: transparent(activeContrastBorder, 0.4) }, nls.localize('findRangeHighlightBorder', "Border color of the range limiting the search. The color must not be opaque so as not to hide underlying decorations."), true);
|
||||
|
||||
/**
|
||||
* Search Editor query match colors.
|
||||
*
|
||||
* Distinct from normal editor find match to allow for better differentiation
|
||||
*/
|
||||
export const searchEditorFindMatch = registerColor('searchEditor.findMatchBackground', { light: transparent(editorFindMatchHighlight, 0.5), dark: transparent(editorFindMatchHighlight, 0.5), hc: editorFindMatchHighlight }, nls.localize('searchEditor.queryMatch', "Color of the Search Editor query matches."));
|
||||
export const searchEditorFindMatchBorder = registerColor('searchEditor.findMatchBorder', { light: transparent(editorFindMatchHighlightBorder, 0.5), dark: transparent(editorFindMatchHighlightBorder, 0.5), hc: editorFindMatchHighlightBorder }, nls.localize('searchEditor.editorFindMatchBorder', "Border color of the Search Editor query matches."));
|
||||
|
||||
/**
|
||||
* Editor hover
|
||||
*/
|
||||
export const editorHoverHighlight = registerColor('editor.hoverHighlightBackground', { light: '#ADD6FF26', dark: '#264f7840', hc: '#ADD6FF26' }, nls.localize('hoverHighlight', 'Highlight below the word for which a hover is shown. The color must not be opaque so as not to hide underlying decorations.'), true);
|
||||
export const editorHoverBackground = registerColor('editorHoverWidget.background', { light: editorWidgetBackground, dark: editorWidgetBackground, hc: editorWidgetBackground }, nls.localize('hoverBackground', 'Background color of the editor hover.'));
|
||||
export const editorHoverForeground = registerColor('editorHoverWidget.foreground', { light: editorWidgetForeground, dark: editorWidgetForeground, hc: editorWidgetForeground }, nls.localize('hoverForeground', 'Foreground color of the editor hover.'));
|
||||
export const editorHoverBorder = registerColor('editorHoverWidget.border', { light: editorWidgetBorder, dark: editorWidgetBorder, hc: editorWidgetBorder }, nls.localize('hoverBorder', 'Border color of the editor hover.'));
|
||||
export const editorHoverStatusBarBackground = registerColor('editorHoverWidget.statusBarBackground', { dark: lighten(editorHoverBackground, 0.2), light: darken(editorHoverBackground, 0.05), hc: editorWidgetBackground }, nls.localize('statusBarBackground', "Background color of the editor hover status bar."));
|
||||
|
||||
/**
|
||||
* Editor link colors
|
||||
*/
|
||||
@@ -416,6 +424,8 @@ export const overviewRulerSelectionHighlightForeground = registerColor('editorOv
|
||||
|
||||
export const minimapFindMatch = registerColor('minimap.findMatchHighlight', { light: '#d18616', dark: '#d18616', hc: '#AB5A00' }, nls.localize('minimapFindMatchHighlight', 'Minimap marker color for find matches.'), true);
|
||||
export const minimapSelection = registerColor('minimap.selectionHighlight', { light: '#ADD6FF', dark: '#264F78', hc: '#ffffff' }, nls.localize('minimapSelectionHighlight', 'Minimap marker color for the editor selection.'), true);
|
||||
export const minimapError = registerColor('minimap.errorHighlight', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hc: new Color(new RGBA(255, 50, 50, 1)) }, nls.localize('minimapError', 'Minimap marker color for errors.'));
|
||||
export const minimapWarning = registerColor('minimap.warningHighlight', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Minimap marker color for warnings.'));
|
||||
|
||||
export const problemsErrorIconForeground = registerColor('problemsErrorIcon.foreground', { dark: editorErrorForeground, light: editorErrorForeground, hc: editorErrorForeground }, nls.localize('problemsErrorIconForeground', "The color used for the problems error icon."));
|
||||
export const problemsWarningIconForeground = registerColor('problemsWarningIcon.foreground', { dark: editorWarningForeground, light: editorWarningForeground, hc: editorWarningForeground }, nls.localize('problemsWarningIconForeground', "The color used for the problems warning icon."));
|
||||
|
||||
@@ -7,18 +7,12 @@ import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService';
|
||||
import { focusBorder, inputBackground, inputForeground, ColorIdentifier, selectForeground, selectBackground, selectListBackground, selectBorder, inputBorder, foreground, editorBackground, contrastBorder, inputActiveOptionBorder, inputActiveOptionBackground, listFocusBackground, listFocusForeground, listActiveSelectionBackground, listActiveSelectionForeground, listInactiveSelectionForeground, listInactiveSelectionBackground, listInactiveFocusBackground, listHoverBackground, listHoverForeground, listDropBackground, pickerGroupBorder, pickerGroupForeground, widgetShadow, inputValidationInfoBorder, inputValidationInfoBackground, inputValidationWarningBorder, inputValidationWarningBackground, inputValidationErrorBorder, inputValidationErrorBackground, activeContrastBorder, buttonForeground, buttonBackground, buttonHoverBackground, ColorFunction, badgeBackground, badgeForeground, progressBarBackground, breadcrumbsForeground, breadcrumbsFocusForeground, breadcrumbsActiveSelectionForeground, breadcrumbsBackground, editorWidgetBorder, inputValidationInfoForeground, inputValidationWarningForeground, inputValidationErrorForeground, menuForeground, menuBackground, menuSelectionForeground, menuSelectionBackground, menuSelectionBorder, menuBorder, menuSeparatorBackground, darken, listFilterWidgetOutline, listFilterWidgetNoMatchesOutline, listFilterWidgetBackground, editorWidgetBackground, treeIndentGuidesStroke, editorWidgetForeground, simpleCheckboxBackground, simpleCheckboxBorder, simpleCheckboxForeground, ColorValue, resolveColorValue } from 'vs/platform/theme/common/colorRegistry';
|
||||
import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { mixin } from 'vs/base/common/objects';
|
||||
|
||||
export type styleFn = (colors: { [name: string]: Color | undefined }) => void;
|
||||
import { IThemable, styleFn } from 'vs/base/common/styler';
|
||||
|
||||
export interface IStyleOverrides {
|
||||
[color: string]: ColorIdentifier | undefined;
|
||||
}
|
||||
|
||||
export interface IThemable {
|
||||
style: styleFn;
|
||||
}
|
||||
|
||||
export interface IColorMapping {
|
||||
[optionsKey: string]: ColorValue | undefined;
|
||||
}
|
||||
@@ -209,6 +203,7 @@ export function attachQuickOpenStyler(widget: IThemable, themeService: IThemeSer
|
||||
}
|
||||
|
||||
export interface IListStyleOverrides extends IStyleOverrides {
|
||||
listBackground?: ColorIdentifier;
|
||||
listFocusBackground?: ColorIdentifier;
|
||||
listFocusForeground?: ColorIdentifier;
|
||||
listActiveSelectionBackground?: ColorIdentifier;
|
||||
@@ -232,8 +227,8 @@ export interface IListStyleOverrides extends IStyleOverrides {
|
||||
treeIndentGuidesStroke?: ColorIdentifier;
|
||||
}
|
||||
|
||||
export function attachListStyler(widget: IThemable, themeService: IThemeService, overrides?: IListStyleOverrides): IDisposable {
|
||||
return attachStyler(themeService, mixin(overrides || Object.create(null), defaultListStyles, false) as IListStyleOverrides, widget);
|
||||
export function attachListStyler(widget: IThemable, themeService: IThemeService, overrides?: IColorMapping): IDisposable {
|
||||
return attachStyler(themeService, { ...defaultListStyles, ...(overrides || {}) }, widget);
|
||||
}
|
||||
|
||||
export const defaultListStyles: IColorMapping = {
|
||||
|
||||
@@ -26,6 +26,42 @@ export interface ThemeIcon {
|
||||
readonly id: string;
|
||||
}
|
||||
|
||||
export namespace ThemeIcon {
|
||||
export function isThemeIcon(obj: any): obj is ThemeIcon {
|
||||
return obj && typeof obj === 'object' && typeof (<ThemeIcon>obj).id === 'string';
|
||||
}
|
||||
|
||||
const _regexFromString = /^\$\(([a-z.]+\/)?([a-z-~]+)\)$/i;
|
||||
|
||||
export function fromString(str: string): ThemeIcon | undefined {
|
||||
const match = _regexFromString.exec(str);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
let [, owner, name] = match;
|
||||
if (!owner) {
|
||||
owner = `codicon/`;
|
||||
}
|
||||
return { id: owner + name };
|
||||
}
|
||||
|
||||
const _regexAsClassName = /^(codicon\/)?([a-z-]+)(~[a-z]+)?$/i;
|
||||
|
||||
export function asClassName(icon: ThemeIcon): string | undefined {
|
||||
// todo@martin,joh -> this should go into the ThemeService
|
||||
const match = _regexAsClassName.exec(icon.id);
|
||||
if (!match) {
|
||||
return undefined;
|
||||
}
|
||||
let [, , name, modifier] = match;
|
||||
let className = `codicon codicon-${name}`;
|
||||
if (modifier) {
|
||||
className += ` ${modifier.substr(1)}`;
|
||||
}
|
||||
return className;
|
||||
}
|
||||
}
|
||||
|
||||
export const FileThemeIcon = { id: 'file' };
|
||||
export const FolderThemeIcon = { id: 'folder' };
|
||||
|
||||
@@ -59,6 +95,16 @@ export interface ITheme {
|
||||
* default color will be used.
|
||||
*/
|
||||
defines(color: ColorIdentifier): boolean;
|
||||
|
||||
/**
|
||||
* Returns the token style for a given classification. The result uses the <code>MetadataConsts</code> format
|
||||
*/
|
||||
getTokenStyleMetadata(type: string, modifiers: string[]): number | undefined;
|
||||
|
||||
/**
|
||||
* List of all colors used with tokens. <code>getTokenStyleMetadata</code> references the colors by index into this list.
|
||||
*/
|
||||
readonly tokenColorMap: string[];
|
||||
}
|
||||
|
||||
export interface IIconTheme {
|
||||
|
||||
395
src/vs/platform/theme/common/tokenClassificationRegistry.ts
Normal file
395
src/vs/platform/theme/common/tokenClassificationRegistry.ts
Normal file
@@ -0,0 +1,395 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as platform from 'vs/platform/registry/common/platform';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
import { ITheme } from 'vs/platform/theme/common/themeService';
|
||||
import * as nls from 'vs/nls';
|
||||
import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { RunOnceScheduler } from 'vs/base/common/async';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema';
|
||||
|
||||
// ------ API types
|
||||
|
||||
export const TOKEN_TYPE_WILDCARD = '*';
|
||||
export const TOKEN_TYPE_WILDCARD_NUM = -1;
|
||||
|
||||
// qualified string [type|*](.modifier)*
|
||||
export type TokenClassificationString = string;
|
||||
|
||||
export interface TokenClassification {
|
||||
type: number;
|
||||
modifiers: number;
|
||||
}
|
||||
|
||||
export interface TokenTypeOrModifierContribution {
|
||||
readonly num: number;
|
||||
readonly id: string;
|
||||
readonly description: string;
|
||||
readonly deprecationMessage: string | undefined;
|
||||
}
|
||||
|
||||
|
||||
export interface TokenStyleData {
|
||||
foreground?: Color;
|
||||
bold?: boolean;
|
||||
underline?: boolean;
|
||||
italic?: boolean;
|
||||
}
|
||||
|
||||
export class TokenStyle implements Readonly<TokenStyleData> {
|
||||
constructor(
|
||||
public readonly foreground?: Color,
|
||||
public readonly bold?: boolean,
|
||||
public readonly underline?: boolean,
|
||||
public readonly italic?: boolean,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
export namespace TokenStyle {
|
||||
export function fromData(data: { foreground?: Color, bold?: boolean, underline?: boolean, italic?: boolean }) {
|
||||
return new TokenStyle(data.foreground, data.bold, data.underline, data.italic);
|
||||
}
|
||||
}
|
||||
|
||||
export type ProbeScope = string[];
|
||||
|
||||
export interface TokenStyleFunction {
|
||||
(theme: ITheme): TokenStyle | undefined;
|
||||
}
|
||||
|
||||
export interface TokenStyleDefaults {
|
||||
scopesToProbe: ProbeScope[];
|
||||
light: TokenStyleValue | null;
|
||||
dark: TokenStyleValue | null;
|
||||
hc: TokenStyleValue | null;
|
||||
}
|
||||
|
||||
export interface TokenStylingDefaultRule {
|
||||
classification: TokenClassification;
|
||||
matchScore: number;
|
||||
defaults: TokenStyleDefaults;
|
||||
}
|
||||
|
||||
export interface TokenStylingRule {
|
||||
classification: TokenClassification;
|
||||
matchScore: number;
|
||||
value: TokenStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* A TokenStyle Value is either a token style literal, or a TokenClassificationString
|
||||
*/
|
||||
export type TokenStyleValue = TokenStyle | TokenClassificationString;
|
||||
|
||||
// TokenStyle registry
|
||||
export const Extensions = {
|
||||
TokenClassificationContribution: 'base.contributions.tokenClassification'
|
||||
};
|
||||
|
||||
export interface ITokenClassificationRegistry {
|
||||
|
||||
readonly onDidChangeSchema: Event<void>;
|
||||
|
||||
/**
|
||||
* Register a token type to the registry.
|
||||
* @param id The TokenType id as used in theme description files
|
||||
* @description the description
|
||||
*/
|
||||
registerTokenType(id: string, description: string): void;
|
||||
|
||||
/**
|
||||
* Register a token modifier to the registry.
|
||||
* @param id The TokenModifier id as used in theme description files
|
||||
* @description the description
|
||||
*/
|
||||
registerTokenModifier(id: string, description: string): void;
|
||||
|
||||
getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined;
|
||||
|
||||
getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule;
|
||||
|
||||
/**
|
||||
* Register a TokenStyle default to the registry.
|
||||
* @param selector The rule selector
|
||||
* @param defaults The default values
|
||||
*/
|
||||
registerTokenStyleDefault(selector: TokenClassification, defaults: TokenStyleDefaults): void;
|
||||
|
||||
/**
|
||||
* Deregister a TokenType from the registry.
|
||||
*/
|
||||
deregisterTokenType(id: string): void;
|
||||
|
||||
/**
|
||||
* Deregister a TokenModifier from the registry.
|
||||
*/
|
||||
deregisterTokenModifier(id: string): void;
|
||||
|
||||
/**
|
||||
* Get all TokenType contributions
|
||||
*/
|
||||
getTokenTypes(): TokenTypeOrModifierContribution[];
|
||||
|
||||
/**
|
||||
* Get all TokenModifier contributions
|
||||
*/
|
||||
getTokenModifiers(): TokenTypeOrModifierContribution[];
|
||||
|
||||
/**
|
||||
* The styling rules to used when a schema does not define any styling rules.
|
||||
*/
|
||||
getTokenStylingDefaultRules(): TokenStylingDefaultRule[];
|
||||
|
||||
/**
|
||||
* JSON schema for an object to assign styling to token classifications
|
||||
*/
|
||||
getTokenStylingSchema(): IJSONSchema;
|
||||
}
|
||||
|
||||
class TokenClassificationRegistry implements ITokenClassificationRegistry {
|
||||
|
||||
private readonly _onDidChangeSchema = new Emitter<void>();
|
||||
readonly onDidChangeSchema: Event<void> = this._onDidChangeSchema.event;
|
||||
|
||||
private currentTypeNumber = 0;
|
||||
private currentModifierBit = 1;
|
||||
|
||||
private tokenTypeById: { [key: string]: TokenTypeOrModifierContribution };
|
||||
private tokenModifierById: { [key: string]: TokenTypeOrModifierContribution };
|
||||
|
||||
private tokenStylingDefaultRules: TokenStylingDefaultRule[] = [];
|
||||
|
||||
private tokenStylingSchema: IJSONSchema & { properties: IJSONSchemaMap } = {
|
||||
type: 'object',
|
||||
properties: {},
|
||||
additionalProperties: getStylingSchemeEntry(),
|
||||
definitions: {
|
||||
style: {
|
||||
type: 'object',
|
||||
description: nls.localize('schema.token.settings', 'Colors and styles for the token.'),
|
||||
properties: {
|
||||
foreground: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.token.foreground', 'Foreground color for the token.'),
|
||||
format: 'color-hex',
|
||||
default: '#ff0000'
|
||||
},
|
||||
background: {
|
||||
type: 'string',
|
||||
deprecationMessage: nls.localize('schema.token.background.warning', 'Token background colors are currently not supported.')
|
||||
},
|
||||
fontStyle: {
|
||||
type: 'string',
|
||||
description: nls.localize('schema.token.fontStyle', 'Font style of the rule: \'italic\', \'bold\' or \'underline\', \'-italic\', \'-bold\' or \'-underline\'or a combination. The empty string unsets inherited settings.'),
|
||||
pattern: '^(\\s*(-?italic|-?bold|-?underline))*\\s*$',
|
||||
patternErrorMessage: nls.localize('schema.fontStyle.error', 'Font style must be \'italic\', \'bold\' or \'underline\' to set a style or \'-italic\', \'-bold\' or \'-underline\' to unset or a combination. The empty string unsets all styles.'),
|
||||
defaultSnippets: [{ label: nls.localize('schema.token.fontStyle.none', 'None (clear inherited style)'), bodyText: '""' }, { body: 'italic' }, { body: 'bold' }, { body: 'underline' }, { body: '-italic' }, { body: '-bold' }, { body: '-underline' }, { body: 'italic bold' }, { body: 'italic underline' }, { body: 'bold underline' }, { body: 'italic bold underline' }]
|
||||
}
|
||||
},
|
||||
additionalProperties: false,
|
||||
defaultSnippets: [{ body: { foreground: '${1:#FF0000}', fontStyle: '${2:bold}' } }]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
constructor() {
|
||||
this.tokenTypeById = {};
|
||||
this.tokenModifierById = {};
|
||||
|
||||
this.tokenTypeById[TOKEN_TYPE_WILDCARD] = { num: TOKEN_TYPE_WILDCARD_NUM, id: TOKEN_TYPE_WILDCARD, description: '', deprecationMessage: undefined };
|
||||
}
|
||||
|
||||
public registerTokenType(id: string, description: string, deprecationMessage?: string): void {
|
||||
const num = this.currentTypeNumber++;
|
||||
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
|
||||
this.tokenTypeById[id] = tokenStyleContribution;
|
||||
|
||||
this.tokenStylingSchema.properties[id] = getStylingSchemeEntry(description, deprecationMessage);
|
||||
}
|
||||
|
||||
public registerTokenModifier(id: string, description: string, deprecationMessage?: string): void {
|
||||
const num = this.currentModifierBit;
|
||||
this.currentModifierBit = this.currentModifierBit * 2;
|
||||
let tokenStyleContribution: TokenTypeOrModifierContribution = { num, id, description, deprecationMessage };
|
||||
this.tokenModifierById[id] = tokenStyleContribution;
|
||||
|
||||
this.tokenStylingSchema.properties[`*.${id}`] = getStylingSchemeEntry(description, deprecationMessage);
|
||||
}
|
||||
|
||||
public getTokenClassification(type: string, modifiers: string[]): TokenClassification | undefined {
|
||||
const tokenTypeDesc = this.tokenTypeById[type];
|
||||
if (!tokenTypeDesc) {
|
||||
return undefined;
|
||||
}
|
||||
let allModifierBits = 0;
|
||||
for (const modifier of modifiers) {
|
||||
const tokenModifierDesc = this.tokenModifierById[modifier];
|
||||
if (tokenModifierDesc) {
|
||||
allModifierBits |= tokenModifierDesc.num;
|
||||
}
|
||||
}
|
||||
return { type: tokenTypeDesc.num, modifiers: allModifierBits };
|
||||
}
|
||||
|
||||
public getTokenStylingRule(classification: TokenClassification, value: TokenStyle): TokenStylingRule {
|
||||
return { classification, matchScore: getTokenStylingScore(classification), value };
|
||||
}
|
||||
|
||||
public registerTokenStyleDefault(classification: TokenClassification, defaults: TokenStyleDefaults): void {
|
||||
this.tokenStylingDefaultRules.push({ classification, matchScore: getTokenStylingScore(classification), defaults });
|
||||
}
|
||||
|
||||
public deregisterTokenType(id: string): void {
|
||||
delete this.tokenTypeById[id];
|
||||
delete this.tokenStylingSchema.properties[id];
|
||||
}
|
||||
|
||||
public deregisterTokenModifier(id: string): void {
|
||||
delete this.tokenModifierById[id];
|
||||
delete this.tokenStylingSchema.properties[`*.${id}`];
|
||||
}
|
||||
|
||||
public getTokenTypes(): TokenTypeOrModifierContribution[] {
|
||||
return Object.keys(this.tokenTypeById).map(id => this.tokenTypeById[id]);
|
||||
}
|
||||
|
||||
public getTokenModifiers(): TokenTypeOrModifierContribution[] {
|
||||
return Object.keys(this.tokenModifierById).map(id => this.tokenModifierById[id]);
|
||||
}
|
||||
|
||||
public getTokenStylingSchema(): IJSONSchema {
|
||||
return this.tokenStylingSchema;
|
||||
}
|
||||
|
||||
public getTokenStylingDefaultRules(): TokenStylingDefaultRule[] {
|
||||
return this.tokenStylingDefaultRules;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
return a.localeCompare(b);
|
||||
};
|
||||
|
||||
return Object.keys(this.tokenTypeById).sort(sorter).map(k => `- \`${k}\`: ${this.tokenTypeById[k].description}`).join('\n');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export function matchTokenStylingRule(themeSelector: TokenStylingRule | TokenStylingDefaultRule, classification: TokenClassification): number {
|
||||
const selectorType = themeSelector.classification.type;
|
||||
if (selectorType !== TOKEN_TYPE_WILDCARD_NUM && selectorType !== classification.type) {
|
||||
return -1;
|
||||
}
|
||||
const selectorModifier = themeSelector.classification.modifiers;
|
||||
if ((classification.modifiers & selectorModifier) !== selectorModifier) {
|
||||
return -1;
|
||||
}
|
||||
return themeSelector.matchScore;
|
||||
}
|
||||
|
||||
|
||||
const tokenClassificationRegistry = new TokenClassificationRegistry();
|
||||
platform.Registry.add(Extensions.TokenClassificationContribution, tokenClassificationRegistry);
|
||||
|
||||
export function registerTokenType(id: string, description: string, scopesToProbe: ProbeScope[] = [], extendsTC: string | null = null, deprecationMessage?: string): string {
|
||||
tokenClassificationRegistry.registerTokenType(id, description, deprecationMessage);
|
||||
|
||||
if (scopesToProbe || extendsTC) {
|
||||
const classification = tokenClassificationRegistry.getTokenClassification(id, []);
|
||||
tokenClassificationRegistry.registerTokenStyleDefault(classification!, { scopesToProbe, light: extendsTC, dark: extendsTC, hc: extendsTC });
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
export function registerTokenModifier(id: string, description: string, deprecationMessage?: string): string {
|
||||
tokenClassificationRegistry.registerTokenModifier(id, description, deprecationMessage);
|
||||
return id;
|
||||
}
|
||||
|
||||
export function getTokenClassificationRegistry(): ITokenClassificationRegistry {
|
||||
return tokenClassificationRegistry;
|
||||
}
|
||||
|
||||
export const comments = registerTokenType('comments', nls.localize('comments', "Style for comments."), [['comment']]);
|
||||
export const strings = registerTokenType('strings', nls.localize('strings', "Style for strings."), [['string']]);
|
||||
export const keywords = registerTokenType('keywords', nls.localize('keywords', "Style for keywords."), [['keyword.control']]);
|
||||
export const numbers = registerTokenType('numbers', nls.localize('numbers', "Style for numbers."), [['constant.numeric']]);
|
||||
export const regexp = registerTokenType('regexp', nls.localize('regexp', "Style for expressions."), [['constant.regexp']]);
|
||||
export const operators = registerTokenType('operators', nls.localize('operator', "Style for operators."), [['keyword.operator']]);
|
||||
|
||||
export const namespaces = registerTokenType('namespaces', nls.localize('namespace', "Style for namespaces."), [['entity.name.namespace']]);
|
||||
|
||||
export const types = registerTokenType('types', nls.localize('types', "Style for types."), [['entity.name.type'], ['entity.name.class'], ['support.type'], ['support.class']]);
|
||||
export const structs = registerTokenType('structs', nls.localize('struct', "Style for structs."), [['storage.type.struct']], types);
|
||||
export const classes = registerTokenType('classes', nls.localize('class', "Style for classes."), [['entity.name.class']], types);
|
||||
export const interfaces = registerTokenType('interfaces', nls.localize('interface', "Style for interfaces."), undefined, types);
|
||||
export const enums = registerTokenType('enums', nls.localize('enum', "Style for enums."), undefined, types);
|
||||
export const parameterTypes = registerTokenType('parameterTypes', nls.localize('parameterType', "Style for parameter types."), undefined, types);
|
||||
|
||||
export const functions = registerTokenType('functions', nls.localize('functions', "Style for functions"), [['entity.name.function'], ['support.function']]);
|
||||
export const macros = registerTokenType('macros', nls.localize('macro', "Style for macros."), undefined, functions);
|
||||
|
||||
export const variables = registerTokenType('variables', nls.localize('variables', "Style for variables."), [['variable'], ['entity.name.variable']]);
|
||||
export const constants = registerTokenType('constants', nls.localize('constants', "Style for constants."), undefined, variables);
|
||||
export const parameters = registerTokenType('parameters', nls.localize('parameters', "Style for parameters."), undefined, variables);
|
||||
export const property = registerTokenType('properties', nls.localize('properties', "Style for properties."), undefined, variables);
|
||||
|
||||
export const labels = registerTokenType('labels', nls.localize('labels', "Style for labels. "), undefined);
|
||||
|
||||
export const m_declaration = registerTokenModifier('declaration', nls.localize('declaration', "Style for all symbol declarations."), undefined);
|
||||
export const m_documentation = registerTokenModifier('documentation', nls.localize('documentation', "Style to use for references in documentation."), undefined);
|
||||
export const m_member = registerTokenModifier('member', nls.localize('member', "Style to use for member functions, variables (fields) and types."), undefined);
|
||||
export const m_static = registerTokenModifier('static', nls.localize('static', "Style to use for symbols that are static."), undefined);
|
||||
export const m_abstract = registerTokenModifier('abstract', nls.localize('abstract', "Style to use for symbols that are abstract."), undefined);
|
||||
export const m_deprecated = registerTokenModifier('deprecated', nls.localize('deprecated', "Style to use for symbols that are deprecated."), undefined);
|
||||
export const m_modification = registerTokenModifier('modification', nls.localize('modification', "Style to use for write accesses."), undefined);
|
||||
export const m_async = registerTokenModifier('async', nls.localize('async', "Style to use for symbols that are async."), undefined);
|
||||
|
||||
function bitCount(u: number) {
|
||||
// https://blogs.msdn.microsoft.com/jeuge/2005/06/08/bit-fiddling-3/
|
||||
const uCount = u - ((u >> 1) & 0o33333333333) - ((u >> 2) & 0o11111111111);
|
||||
return ((uCount + (uCount >> 3)) & 0o30707070707) % 63;
|
||||
}
|
||||
|
||||
function getTokenStylingScore(classification: TokenClassification) {
|
||||
return bitCount(classification.modifiers) + ((classification.type !== TOKEN_TYPE_WILDCARD_NUM) ? 1 : 0);
|
||||
}
|
||||
|
||||
function getStylingSchemeEntry(description?: string, deprecationMessage?: string): IJSONSchema {
|
||||
return {
|
||||
description,
|
||||
deprecationMessage,
|
||||
defaultSnippets: [{ body: '${1:#ff0000}' }],
|
||||
anyOf: [
|
||||
{
|
||||
type: 'string',
|
||||
format: 'color-hex'
|
||||
},
|
||||
{
|
||||
$ref: '#definitions/style'
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
export const tokenStylingSchemaId = 'vscode://schemas/token-styling';
|
||||
|
||||
let schemaRegistry = platform.Registry.as<IJSONContributionRegistry>(JSONExtensions.JSONContribution);
|
||||
schemaRegistry.registerSchema(tokenStylingSchemaId, tokenClassificationRegistry.getTokenStylingSchema());
|
||||
|
||||
const delayer = new RunOnceScheduler(() => schemaRegistry.notifySchemaChanged(tokenStylingSchemaId), 200);
|
||||
tokenClassificationRegistry.onDidChangeSchema(() => {
|
||||
if (!delayer.isScheduled()) {
|
||||
delayer.schedule();
|
||||
}
|
||||
});
|
||||
@@ -23,6 +23,14 @@ export class TestTheme implements ITheme {
|
||||
defines(color: string): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
getTokenStyleMetadata(type: string, modifiers: string[]): number | undefined {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get tokenColorMap(): string[] {
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
export class TestIconTheme implements IIconTheme {
|
||||
|
||||
@@ -41,7 +41,7 @@ export class LinuxUpdateService extends AbstractUpdateService {
|
||||
|
||||
this.setState(State.CheckingForUpdates(context));
|
||||
this.requestService.request({ url: this.url }, CancellationToken.None)
|
||||
.then<IUpdate>(asJson)
|
||||
.then<IUpdate | null>(asJson)
|
||||
.then(update => {
|
||||
if (!update || !update.url || !update.version || !update.productVersion) {
|
||||
this.telemetryService.publicLog2<{ explicit: boolean }, UpdateNotAvailableClassification>('update:notAvailable', { explicit: !!context });
|
||||
|
||||
@@ -110,7 +110,7 @@ export class Win32UpdateService extends AbstractUpdateService {
|
||||
this.setState(State.CheckingForUpdates(context));
|
||||
|
||||
this.requestService.request({ url: this.url }, CancellationToken.None)
|
||||
.then<IUpdate>(asJson)
|
||||
.then<IUpdate | null>(asJson)
|
||||
.then(update => {
|
||||
const updateType = getUpdateType();
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { IURLService } from 'vs/platform/url/common/url';
|
||||
import product from 'vs/platform/product/common/product';
|
||||
import { app, Event as ElectronEvent } from 'electron';
|
||||
@@ -32,7 +33,8 @@ export class ElectronURLListener {
|
||||
constructor(
|
||||
initial: string | string[],
|
||||
@IURLService private readonly urlService: IURLService,
|
||||
@IWindowsMainService windowsMainService: IWindowsMainService
|
||||
@IWindowsMainService windowsMainService: IWindowsMainService,
|
||||
@IEnvironmentService environmentService: IEnvironmentService
|
||||
) {
|
||||
const globalBuffer = ((<any>global).getOpenUrls() || []) as string[];
|
||||
const rawBuffer = [
|
||||
@@ -43,7 +45,9 @@ export class ElectronURLListener {
|
||||
this.uris = coalesce(rawBuffer.map(uriFromRawUrl));
|
||||
|
||||
if (isWindows) {
|
||||
app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, ['--open-url', '--']);
|
||||
const windowsParameters = environmentService.isBuilt ? [] : [`"${environmentService.appRoot}"`];
|
||||
windowsParameters.push('--open-url', '--');
|
||||
app.setAsDefaultProtocolClient(product.urlProtocol, process.execPath, windowsParameters);
|
||||
}
|
||||
|
||||
const onOpenElectronUrl = Event.map(
|
||||
|
||||
53
src/vs/platform/userDataSync/common/content.ts
Normal file
53
src/vs/platform/userDataSync/common/content.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { JSONPath } from 'vs/base/common/json';
|
||||
import { setProperty } from 'vs/base/common/jsonEdit';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
|
||||
|
||||
export function edit(content: string, originalPath: JSONPath, value: any, formattingOptions: FormattingOptions): string {
|
||||
const edit = setProperty(content, originalPath, value, formattingOptions)[0];
|
||||
if (edit) {
|
||||
content = content.substring(0, edit.offset) + edit.content + content.substring(edit.offset + edit.length);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
export function getLineStartOffset(content: string, eol: string, atOffset: number): number {
|
||||
let lineStartingOffset = atOffset;
|
||||
while (lineStartingOffset >= 0) {
|
||||
if (content.charAt(lineStartingOffset) === eol.charAt(eol.length - 1)) {
|
||||
if (eol.length === 1) {
|
||||
return lineStartingOffset + 1;
|
||||
}
|
||||
}
|
||||
lineStartingOffset--;
|
||||
if (eol.length === 2) {
|
||||
if (lineStartingOffset >= 0 && content.charAt(lineStartingOffset) === eol.charAt(0)) {
|
||||
return lineStartingOffset + 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
export function getLineEndOffset(content: string, eol: string, atOffset: number): number {
|
||||
let lineEndOffset = atOffset;
|
||||
while (lineEndOffset >= 0) {
|
||||
if (content.charAt(lineEndOffset) === eol.charAt(eol.length - 1)) {
|
||||
if (eol.length === 1) {
|
||||
return lineEndOffset;
|
||||
}
|
||||
}
|
||||
lineEndOffset++;
|
||||
if (eol.length === 2) {
|
||||
if (lineEndOffset >= 0 && content.charAt(lineEndOffset) === eol.charAt(1)) {
|
||||
return lineEndOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
return content.length - 1;
|
||||
}
|
||||
@@ -69,11 +69,14 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser
|
||||
}
|
||||
|
||||
async sync(): Promise<boolean> {
|
||||
if (!this.configurationService.getValue<boolean>('configurationSync.enableExtensions')) {
|
||||
if (!this.configurationService.getValue<boolean>('sync.enableExtensions')) {
|
||||
this.logService.trace('Extensions: Skipping synchronizing extensions as it is disabled.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.extensionGalleryService.isEnabled()) {
|
||||
this.logService.trace('Extensions: Skipping synchronizing extensions as gallery is disabled.');
|
||||
return false;
|
||||
}
|
||||
if (this.status !== SyncStatus.Idle) {
|
||||
this.logService.trace('Extensions: Skipping synchronizing extensions as it is running already.');
|
||||
return false;
|
||||
@@ -105,7 +108,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser
|
||||
return this.replaceQueue.queue(async () => {
|
||||
const remoteData = await this.userDataSyncStoreService.read(ExtensionsSynchroniser.EXTERNAL_USER_DATA_EXTENSIONS_KEY, null);
|
||||
const remoteExtensions: ISyncExtension[] = remoteData.content ? JSON.parse(remoteData.content) : [];
|
||||
const ignoredExtensions = this.configurationService.getValue<string[]>('configurationSync.extensionsToIgnore') || [];
|
||||
const ignoredExtensions = this.configurationService.getValue<string[]>('sync.ignoredExtensions') || [];
|
||||
const removedExtensions = remoteExtensions.filter(e => !ignoredExtensions.some(id => areSameExtensions({ id }, e.identifier)) && areSameExtensions(e.identifier, identifier));
|
||||
if (removedExtensions.length) {
|
||||
for (const removedExtension of removedExtensions) {
|
||||
@@ -159,11 +162,11 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser
|
||||
* - Update remote with those local extension which are newly added or updated or removed and untouched in remote.
|
||||
*/
|
||||
private merge(localExtensions: ISyncExtension[], remoteExtensions: ISyncExtension[] | null, lastSyncExtensions: ISyncExtension[] | null): { added: ISyncExtension[], removed: IExtensionIdentifier[], updated: ISyncExtension[], remote: ISyncExtension[] | null } {
|
||||
const ignoredExtensions = this.configurationService.getValue<string[]>('configurationSync.extensionsToIgnore') || [];
|
||||
const ignoredExtensions = this.configurationService.getValue<string[]>('sync.ignoredExtensions') || [];
|
||||
// First time sync
|
||||
if (!remoteExtensions) {
|
||||
this.logService.info('Extensions: Remote extensions does not exist. Synchronizing extensions for the first time.');
|
||||
return { added: [], removed: [], updated: [], remote: localExtensions.filter(({ identifier }) => ignoredExtensions.some(id => id.toLowerCase() === identifier.id.toLowerCase())) };
|
||||
return { added: [], removed: [], updated: [], remote: localExtensions.filter(({ identifier }) => ignoredExtensions.every(id => id.toLowerCase() !== identifier.id.toLowerCase())) };
|
||||
}
|
||||
|
||||
const uuids: Map<string, string> = new Map<string, string>();
|
||||
|
||||
367
src/vs/platform/userDataSync/common/keybindingsMerge.ts
Normal file
367
src/vs/platform/userDataSync/common/keybindingsMerge.ts
Normal file
@@ -0,0 +1,367 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as objects from 'vs/base/common/objects';
|
||||
import { parse } from 'vs/base/common/json';
|
||||
import { values, keys } from 'vs/base/common/map';
|
||||
import { IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { firstIndex as findFirstIndex, equals } from 'vs/base/common/arrays';
|
||||
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
|
||||
import * as contentUtil from 'vs/platform/userDataSync/common/content';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
|
||||
interface ICompareResult {
|
||||
added: Set<string>;
|
||||
removed: Set<string>;
|
||||
updated: Set<string>;
|
||||
}
|
||||
|
||||
interface IMergeResult {
|
||||
hasLocalForwarded: boolean;
|
||||
hasRemoteForwarded: boolean;
|
||||
added: Set<string>;
|
||||
removed: Set<string>;
|
||||
updated: Set<string>;
|
||||
conflicts: Set<string>;
|
||||
}
|
||||
|
||||
export async function merge(localContent: string, remoteContent: string, baseContent: string | null, formattingOptions: FormattingOptions, userDataSyncUtilService: IUserDataSyncUtilService): Promise<{ mergeContent: string, hasChanges: boolean, hasConflicts: boolean }> {
|
||||
const local = <IUserFriendlyKeybinding[]>parse(localContent);
|
||||
const remote = <IUserFriendlyKeybinding[]>parse(remoteContent);
|
||||
const base = baseContent ? <IUserFriendlyKeybinding[]>parse(baseContent) : null;
|
||||
|
||||
const userbindings: string[] = [...local, ...remote, ...(base || [])].map(keybinding => keybinding.key);
|
||||
const normalizedKeys = await userDataSyncUtilService.resolveUserBindings(userbindings);
|
||||
let keybindingsMergeResult = computeMergeResultByKeybinding(local, remote, base, normalizedKeys);
|
||||
|
||||
if (!keybindingsMergeResult.hasLocalForwarded && !keybindingsMergeResult.hasRemoteForwarded) {
|
||||
// No changes found between local and remote.
|
||||
return { mergeContent: localContent, hasChanges: false, hasConflicts: false };
|
||||
}
|
||||
|
||||
if (!keybindingsMergeResult.hasLocalForwarded && keybindingsMergeResult.hasRemoteForwarded) {
|
||||
return { mergeContent: remoteContent, hasChanges: true, hasConflicts: false };
|
||||
}
|
||||
|
||||
if (keybindingsMergeResult.hasLocalForwarded && !keybindingsMergeResult.hasRemoteForwarded) {
|
||||
// Local has moved forward and remote has not. Return local.
|
||||
return { mergeContent: localContent, hasChanges: true, hasConflicts: false };
|
||||
}
|
||||
|
||||
// Both local and remote has moved forward.
|
||||
const localByCommand = byCommand(local);
|
||||
const remoteByCommand = byCommand(remote);
|
||||
const baseByCommand = base ? byCommand(base) : null;
|
||||
const localToRemoteByCommand = compareByCommand(localByCommand, remoteByCommand, normalizedKeys);
|
||||
const baseToLocalByCommand = baseByCommand ? compareByCommand(baseByCommand, localByCommand, normalizedKeys) : { added: keys(localByCommand).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
const baseToRemoteByCommand = baseByCommand ? compareByCommand(baseByCommand, remoteByCommand, normalizedKeys) : { added: keys(remoteByCommand).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
|
||||
const commandsMergeResult = computeMergeResult(localToRemoteByCommand, baseToLocalByCommand, baseToRemoteByCommand);
|
||||
let mergeContent = localContent;
|
||||
|
||||
// Removed commands in Remote
|
||||
for (const command of values(commandsMergeResult.removed)) {
|
||||
if (commandsMergeResult.conflicts.has(command)) {
|
||||
continue;
|
||||
}
|
||||
mergeContent = removeKeybindings(mergeContent, command, formattingOptions);
|
||||
}
|
||||
|
||||
// Added commands in remote
|
||||
for (const command of values(commandsMergeResult.added)) {
|
||||
if (commandsMergeResult.conflicts.has(command)) {
|
||||
continue;
|
||||
}
|
||||
const keybindings = remoteByCommand.get(command)!;
|
||||
// Ignore negated commands
|
||||
if (keybindings.some(keybinding => keybinding.command !== `-${command}` && keybindingsMergeResult.conflicts.has(normalizedKeys[keybinding.key]))) {
|
||||
commandsMergeResult.conflicts.add(command);
|
||||
continue;
|
||||
}
|
||||
mergeContent = addKeybindings(mergeContent, keybindings, formattingOptions);
|
||||
}
|
||||
|
||||
// Updated commands in Remote
|
||||
for (const command of values(commandsMergeResult.updated)) {
|
||||
if (commandsMergeResult.conflicts.has(command)) {
|
||||
continue;
|
||||
}
|
||||
const keybindings = remoteByCommand.get(command)!;
|
||||
// Ignore negated commands
|
||||
if (keybindings.some(keybinding => keybinding.command !== `-${command}` && keybindingsMergeResult.conflicts.has(normalizedKeys[keybinding.key]))) {
|
||||
commandsMergeResult.conflicts.add(command);
|
||||
continue;
|
||||
}
|
||||
mergeContent = updateKeybindings(mergeContent, command, keybindings, formattingOptions);
|
||||
}
|
||||
|
||||
const hasConflicts = commandsMergeResult.conflicts.size > 0;
|
||||
if (hasConflicts) {
|
||||
mergeContent = `<<<<<<< local${formattingOptions.eol}`
|
||||
+ mergeContent
|
||||
+ `${formattingOptions.eol}=======${formattingOptions.eol}`
|
||||
+ remoteContent
|
||||
+ `${formattingOptions.eol}>>>>>>> remote`;
|
||||
}
|
||||
|
||||
return { mergeContent, hasChanges: true, hasConflicts };
|
||||
}
|
||||
|
||||
function computeMergeResult(localToRemote: ICompareResult, baseToLocal: ICompareResult, baseToRemote: ICompareResult): { added: Set<string>, removed: Set<string>, updated: Set<string>, conflicts: Set<string> } {
|
||||
const added: Set<string> = new Set<string>();
|
||||
const removed: Set<string> = new Set<string>();
|
||||
const updated: Set<string> = new Set<string>();
|
||||
const conflicts: Set<string> = new Set<string>();
|
||||
|
||||
// Removed keys in Local
|
||||
for (const key of values(baseToLocal.removed)) {
|
||||
// Got updated in remote
|
||||
if (baseToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Removed keys in Remote
|
||||
for (const key of values(baseToRemote.removed)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got updated in local
|
||||
if (baseToLocal.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
} else {
|
||||
// remove the key
|
||||
removed.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Added keys in Local
|
||||
for (const key of values(baseToLocal.added)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got added in remote
|
||||
if (baseToRemote.added.has(key)) {
|
||||
// Has different value
|
||||
if (localToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Added keys in remote
|
||||
for (const key of values(baseToRemote.added)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got added in local
|
||||
if (baseToLocal.added.has(key)) {
|
||||
// Has different value
|
||||
if (localToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
}
|
||||
} else {
|
||||
added.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
// Updated keys in Local
|
||||
for (const key of values(baseToLocal.updated)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got updated in remote
|
||||
if (baseToRemote.updated.has(key)) {
|
||||
// Has different value
|
||||
if (localToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updated keys in Remote
|
||||
for (const key of values(baseToRemote.updated)) {
|
||||
if (conflicts.has(key)) {
|
||||
continue;
|
||||
}
|
||||
// Got updated in local
|
||||
if (baseToLocal.updated.has(key)) {
|
||||
// Has different value
|
||||
if (localToRemote.updated.has(key)) {
|
||||
conflicts.add(key);
|
||||
}
|
||||
} else {
|
||||
// updated key
|
||||
updated.add(key);
|
||||
}
|
||||
}
|
||||
return { added, removed, updated, conflicts };
|
||||
}
|
||||
|
||||
function computeMergeResultByKeybinding(local: IUserFriendlyKeybinding[], remote: IUserFriendlyKeybinding[], base: IUserFriendlyKeybinding[] | null, normalizedKeys: IStringDictionary<string>): IMergeResult {
|
||||
const empty = new Set<string>();
|
||||
const localByKeybinding = byKeybinding(local, normalizedKeys);
|
||||
const remoteByKeybinding = byKeybinding(remote, normalizedKeys);
|
||||
const baseByKeybinding = base ? byKeybinding(base, normalizedKeys) : null;
|
||||
|
||||
const localToRemoteByKeybinding = compareByKeybinding(localByKeybinding, remoteByKeybinding);
|
||||
if (localToRemoteByKeybinding.added.size === 0 && localToRemoteByKeybinding.removed.size === 0 && localToRemoteByKeybinding.updated.size === 0) {
|
||||
return { hasLocalForwarded: false, hasRemoteForwarded: false, added: empty, removed: empty, updated: empty, conflicts: empty };
|
||||
}
|
||||
|
||||
const baseToLocalByKeybinding = baseByKeybinding ? compareByKeybinding(baseByKeybinding, localByKeybinding) : { added: keys(localByKeybinding).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
if (baseToLocalByKeybinding.added.size === 0 && baseToLocalByKeybinding.removed.size === 0 && baseToLocalByKeybinding.updated.size === 0) {
|
||||
// Remote has moved forward and local has not.
|
||||
return { hasLocalForwarded: false, hasRemoteForwarded: true, added: empty, removed: empty, updated: empty, conflicts: empty };
|
||||
}
|
||||
|
||||
const baseToRemoteByKeybinding = baseByKeybinding ? compareByKeybinding(baseByKeybinding, remoteByKeybinding) : { added: keys(remoteByKeybinding).reduce((r, k) => { r.add(k); return r; }, new Set<string>()), removed: new Set<string>(), updated: new Set<string>() };
|
||||
if (baseToRemoteByKeybinding.added.size === 0 && baseToRemoteByKeybinding.removed.size === 0 && baseToRemoteByKeybinding.updated.size === 0) {
|
||||
return { hasLocalForwarded: true, hasRemoteForwarded: false, added: empty, removed: empty, updated: empty, conflicts: empty };
|
||||
}
|
||||
|
||||
const { added, removed, updated, conflicts } = computeMergeResult(localToRemoteByKeybinding, baseToLocalByKeybinding, baseToRemoteByKeybinding);
|
||||
return { hasLocalForwarded: true, hasRemoteForwarded: true, added, removed, updated, conflicts };
|
||||
}
|
||||
|
||||
function byKeybinding(keybindings: IUserFriendlyKeybinding[], keys: IStringDictionary<string>) {
|
||||
const map: Map<string, IUserFriendlyKeybinding[]> = new Map<string, IUserFriendlyKeybinding[]>();
|
||||
for (const keybinding of keybindings) {
|
||||
const key = keys[keybinding.key];
|
||||
let value = map.get(key);
|
||||
if (!value) {
|
||||
value = [];
|
||||
map.set(key, value);
|
||||
}
|
||||
value.push(keybinding);
|
||||
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
function byCommand(keybindings: IUserFriendlyKeybinding[]): Map<string, IUserFriendlyKeybinding[]> {
|
||||
const map: Map<string, IUserFriendlyKeybinding[]> = new Map<string, IUserFriendlyKeybinding[]>();
|
||||
for (const keybinding of keybindings) {
|
||||
const command = keybinding.command[0] === '-' ? keybinding.command.substring(1) : keybinding.command;
|
||||
let value = map.get(command);
|
||||
if (!value) {
|
||||
value = [];
|
||||
map.set(command, value);
|
||||
}
|
||||
value.push(keybinding);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
|
||||
function compareByKeybinding(from: Map<string, IUserFriendlyKeybinding[]>, to: Map<string, IUserFriendlyKeybinding[]>): ICompareResult {
|
||||
const fromKeys = keys(from);
|
||||
const toKeys = keys(to);
|
||||
const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
|
||||
const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
|
||||
const updated: Set<string> = new Set<string>();
|
||||
|
||||
for (const key of fromKeys) {
|
||||
if (removed.has(key)) {
|
||||
continue;
|
||||
}
|
||||
const value1: IUserFriendlyKeybinding[] = from.get(key)!.map(keybinding => ({ ...keybinding, ...{ key } }));
|
||||
const value2: IUserFriendlyKeybinding[] = to.get(key)!.map(keybinding => ({ ...keybinding, ...{ key } }));
|
||||
if (!equals(value1, value2, (a, b) => isSameKeybinding(a, b))) {
|
||||
updated.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
return { added, removed, updated };
|
||||
}
|
||||
|
||||
function compareByCommand(from: Map<string, IUserFriendlyKeybinding[]>, to: Map<string, IUserFriendlyKeybinding[]>, normalizedKeys: IStringDictionary<string>): ICompareResult {
|
||||
const fromKeys = keys(from);
|
||||
const toKeys = keys(to);
|
||||
const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
|
||||
const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set<string>());
|
||||
const updated: Set<string> = new Set<string>();
|
||||
|
||||
for (const key of fromKeys) {
|
||||
if (removed.has(key)) {
|
||||
continue;
|
||||
}
|
||||
const value1: IUserFriendlyKeybinding[] = from.get(key)!.map(keybinding => ({ ...keybinding, ...{ key: normalizedKeys[keybinding.key] } }));
|
||||
const value2: IUserFriendlyKeybinding[] = to.get(key)!.map(keybinding => ({ ...keybinding, ...{ key: normalizedKeys[keybinding.key] } }));
|
||||
if (!areSameKeybindingsWithSameCommand(value1, value2)) {
|
||||
updated.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
return { added, removed, updated };
|
||||
}
|
||||
|
||||
function areSameKeybindingsWithSameCommand(value1: IUserFriendlyKeybinding[], value2: IUserFriendlyKeybinding[]): boolean {
|
||||
// Compare entries adding keybindings
|
||||
if (!equals(value1.filter(({ command }) => command[0] !== '-'), value2.filter(({ command }) => command[0] !== '-'), (a, b) => isSameKeybinding(a, b))) {
|
||||
return false;
|
||||
}
|
||||
// Compare entries removing keybindings
|
||||
if (!equals(value1.filter(({ command }) => command[0] === '-'), value2.filter(({ command }) => command[0] === '-'), (a, b) => isSameKeybinding(a, b))) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function isSameKeybinding(a: IUserFriendlyKeybinding, b: IUserFriendlyKeybinding): boolean {
|
||||
if (a.command !== b.command) {
|
||||
return false;
|
||||
}
|
||||
if (a.key !== b.key) {
|
||||
return false;
|
||||
}
|
||||
const whenA = ContextKeyExpr.deserialize(a.when);
|
||||
const whenB = ContextKeyExpr.deserialize(b.when);
|
||||
if ((whenA && !whenB) || (!whenA && whenB)) {
|
||||
return false;
|
||||
}
|
||||
if (whenA && whenB && !whenA.equals(whenB)) {
|
||||
return false;
|
||||
}
|
||||
if (!objects.equals(a.args, b.args)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function addKeybindings(content: string, keybindings: IUserFriendlyKeybinding[], formattingOptions: FormattingOptions): string {
|
||||
for (const keybinding of keybindings) {
|
||||
content = contentUtil.edit(content, [-1], keybinding, formattingOptions);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
function removeKeybindings(content: string, command: string, formattingOptions: FormattingOptions): string {
|
||||
const keybindings = <IUserFriendlyKeybinding[]>parse(content);
|
||||
for (let index = keybindings.length - 1; index >= 0; index--) {
|
||||
if (keybindings[index].command === command || keybindings[index].command === `-${command}`) {
|
||||
content = contentUtil.edit(content, [index], undefined, formattingOptions);
|
||||
}
|
||||
}
|
||||
return content;
|
||||
}
|
||||
|
||||
function updateKeybindings(content: string, command: string, keybindings: IUserFriendlyKeybinding[], formattingOptions: FormattingOptions): string {
|
||||
const allKeybindings = <IUserFriendlyKeybinding[]>parse(content);
|
||||
const location = findFirstIndex(allKeybindings, keybinding => keybinding.command === command || keybinding.command === `-${command}`);
|
||||
// Remove all entries with this command
|
||||
for (let index = allKeybindings.length - 1; index >= 0; index--) {
|
||||
if (allKeybindings[index].command === command || allKeybindings[index].command === `-${command}`) {
|
||||
content = contentUtil.edit(content, [index], undefined, formattingOptions);
|
||||
}
|
||||
}
|
||||
// add all entries at the same location where the entry with this command was located.
|
||||
for (let index = keybindings.length - 1; index >= 0; index--) {
|
||||
content = contentUtil.edit(content, [location], keybindings[index], formattingOptions);
|
||||
}
|
||||
return content;
|
||||
}
|
||||
348
src/vs/platform/userDataSync/common/keybindingsSync.ts
Normal file
348
src/vs/platform/userDataSync/common/keybindingsSync.ts
Normal file
@@ -0,0 +1,348 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent } from 'vs/platform/files/common/files';
|
||||
import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, ISynchroniser, SyncStatus, IUserDataSyncStoreService, IUserDataSyncLogService, IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { merge } from 'vs/platform/userDataSync/common/keybindingsMerge';
|
||||
import { VSBuffer } from 'vs/base/common/buffer';
|
||||
import { parse, ParseError } from 'vs/base/common/json';
|
||||
import { localize } from 'vs/nls';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from 'vs/base/common/async';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { OS, OperatingSystem } from 'vs/base/common/platform';
|
||||
import { isUndefined } from 'vs/base/common/types';
|
||||
|
||||
interface ISyncContent {
|
||||
mac?: string;
|
||||
linux?: string;
|
||||
windows?: string;
|
||||
all?: string;
|
||||
}
|
||||
|
||||
interface ISyncPreviewResult {
|
||||
readonly fileContent: IFileContent | null;
|
||||
readonly remoteUserData: IUserData;
|
||||
readonly hasLocalChanged: boolean;
|
||||
readonly hasRemoteChanged: boolean;
|
||||
readonly hasConflicts: boolean;
|
||||
}
|
||||
|
||||
export class KeybindingsSynchroniser extends Disposable implements ISynchroniser {
|
||||
|
||||
private static EXTERNAL_USER_DATA_KEYBINDINGS_KEY: string = 'keybindings';
|
||||
|
||||
private syncPreviewResultPromise: CancelablePromise<ISyncPreviewResult> | null = null;
|
||||
|
||||
private _status: SyncStatus = SyncStatus.Idle;
|
||||
get status(): SyncStatus { return this._status; }
|
||||
private _onDidChangStatus: Emitter<SyncStatus> = this._register(new Emitter<SyncStatus>());
|
||||
readonly onDidChangeStatus: Event<SyncStatus> = this._onDidChangStatus.event;
|
||||
|
||||
private readonly throttledDelayer: ThrottledDelayer<void>;
|
||||
private _onDidChangeLocal: Emitter<void> = this._register(new Emitter<void>());
|
||||
readonly onDidChangeLocal: Event<void> = this._onDidChangeLocal.event;
|
||||
|
||||
private readonly lastSyncKeybindingsResource: URI;
|
||||
|
||||
constructor(
|
||||
@IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService,
|
||||
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IFileService private readonly fileService: IFileService,
|
||||
@IEnvironmentService private readonly environmentService: IEnvironmentService,
|
||||
@IUserDataSyncUtilService private readonly userDataSyncUtilService: IUserDataSyncUtilService,
|
||||
) {
|
||||
super();
|
||||
this.lastSyncKeybindingsResource = joinPath(this.environmentService.userRoamingDataHome, '.lastSyncKeybindings.json');
|
||||
this.throttledDelayer = this._register(new ThrottledDelayer<void>(500));
|
||||
this._register(this.fileService.watch(this.environmentService.keybindingsResource));
|
||||
this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.keybindingsResource))(() => this.throttledDelayer.trigger(() => this.onDidChangeKeybindings())));
|
||||
}
|
||||
|
||||
private async onDidChangeKeybindings(): Promise<void> {
|
||||
const localFileContent = await this.getLocalContent();
|
||||
const lastSyncData = await this.getLastSyncUserData();
|
||||
if (localFileContent && lastSyncData) {
|
||||
if (localFileContent.value.toString() !== lastSyncData.content) {
|
||||
this._onDidChangeLocal.fire();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!localFileContent || !lastSyncData) {
|
||||
this._onDidChangeLocal.fire();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private setStatus(status: SyncStatus): void {
|
||||
if (this._status !== status) {
|
||||
this._status = status;
|
||||
this._onDidChangStatus.fire(status);
|
||||
}
|
||||
}
|
||||
|
||||
async sync(_continue?: boolean): Promise<boolean> {
|
||||
if (!this.configurationService.getValue<boolean>('sync.enableKeybindings')) {
|
||||
this.logService.trace('Keybindings: Skipping synchronizing keybindings as it is disabled.');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_continue) {
|
||||
this.logService.info('Keybindings: Resumed synchronizing keybindings');
|
||||
return this.continueSync();
|
||||
}
|
||||
|
||||
if (this.status !== SyncStatus.Idle) {
|
||||
this.logService.trace('Keybindings: Skipping synchronizing keybindings as it is running already.');
|
||||
return false;
|
||||
}
|
||||
|
||||
this.logService.trace('Keybindings: Started synchronizing keybindings...');
|
||||
this.setStatus(SyncStatus.Syncing);
|
||||
|
||||
try {
|
||||
const result = await this.getPreview();
|
||||
if (result.hasConflicts) {
|
||||
this.logService.info('Keybindings: Detected conflicts while synchronizing keybindings.');
|
||||
this.setStatus(SyncStatus.HasConflicts);
|
||||
return false;
|
||||
}
|
||||
await this.apply();
|
||||
return true;
|
||||
} catch (e) {
|
||||
this.syncPreviewResultPromise = null;
|
||||
this.setStatus(SyncStatus.Idle);
|
||||
if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Rejected) {
|
||||
// Rejected as there is a new remote version. Syncing again,
|
||||
this.logService.info('Keybindings: Failed to synchronise keybindings as there is a new remote version available. Synchronizing again...');
|
||||
return this.sync();
|
||||
}
|
||||
if (e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) {
|
||||
// Rejected as there is a new local version. Syncing again.
|
||||
this.logService.info('Keybindings: Failed to synchronise keybindings as there is a new local version available. Synchronizing again...');
|
||||
return this.sync();
|
||||
}
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (this.syncPreviewResultPromise) {
|
||||
this.syncPreviewResultPromise.cancel();
|
||||
this.syncPreviewResultPromise = null;
|
||||
this.logService.info('Keybindings: Stopped synchronizing keybindings.');
|
||||
}
|
||||
this.fileService.del(this.environmentService.keybindingsSyncPreviewResource);
|
||||
this.setStatus(SyncStatus.Idle);
|
||||
}
|
||||
|
||||
private async continueSync(): Promise<boolean> {
|
||||
if (this.status !== SyncStatus.HasConflicts) {
|
||||
return false;
|
||||
}
|
||||
await this.apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
private async apply(): Promise<void> {
|
||||
if (!this.syncPreviewResultPromise) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await this.fileService.exists(this.environmentService.keybindingsSyncPreviewResource)) {
|
||||
const keybindingsPreivew = await this.fileService.readFile(this.environmentService.keybindingsSyncPreviewResource);
|
||||
const content = keybindingsPreivew.value.toString();
|
||||
if (this.hasErrors(content)) {
|
||||
const error = new Error(localize('errorInvalidKeybindings', "Unable to sync keybindings. Please resolve conflicts without any errors/warnings and try again."));
|
||||
this.logService.error(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
|
||||
if (!hasLocalChanged && !hasRemoteChanged) {
|
||||
this.logService.trace('Keybindings: No changes found during synchronizing keybindings.');
|
||||
}
|
||||
if (hasLocalChanged) {
|
||||
this.logService.info('Keybindings: Updating local keybindings');
|
||||
await this.updateLocalContent(content, fileContent);
|
||||
}
|
||||
if (hasRemoteChanged) {
|
||||
this.logService.info('Keybindings: Updating remote keybindings');
|
||||
const remoteContents = this.updateSyncContent(content, remoteUserData.content);
|
||||
const ref = await this.updateRemoteUserData(remoteContents, remoteUserData.ref);
|
||||
remoteUserData = { ref, content: remoteContents };
|
||||
}
|
||||
if (remoteUserData.content) {
|
||||
this.logService.info('Keybindings: Updating last synchronised keybindings');
|
||||
const lastSyncContent = this.updateSyncContent(content, null);
|
||||
await this.updateLastSyncUserData({ ref: remoteUserData.ref, content: lastSyncContent });
|
||||
}
|
||||
|
||||
// Delete the preview
|
||||
await this.fileService.del(this.environmentService.keybindingsSyncPreviewResource);
|
||||
} else {
|
||||
this.logService.trace('Keybindings: No changes found during synchronizing keybindings.');
|
||||
}
|
||||
|
||||
this.logService.trace('Keybindings: Finised synchronizing keybindings.');
|
||||
this.syncPreviewResultPromise = null;
|
||||
this.setStatus(SyncStatus.Idle);
|
||||
}
|
||||
|
||||
private hasErrors(content: string): boolean {
|
||||
const parseErrors: ParseError[] = [];
|
||||
parse(content, parseErrors, { allowEmptyContent: true, allowTrailingComma: true });
|
||||
return parseErrors.length > 0;
|
||||
}
|
||||
|
||||
private getPreview(): Promise<ISyncPreviewResult> {
|
||||
if (!this.syncPreviewResultPromise) {
|
||||
this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview(token));
|
||||
}
|
||||
return this.syncPreviewResultPromise;
|
||||
}
|
||||
|
||||
private async generatePreview(token: CancellationToken): Promise<ISyncPreviewResult> {
|
||||
const lastSyncData = await this.getLastSyncUserData();
|
||||
const lastSyncContent = lastSyncData && lastSyncData.content ? this.getKeybindingsContentFromSyncContent(lastSyncData.content) : null;
|
||||
const remoteUserData = await this.getRemoteUserData(lastSyncData);
|
||||
const remoteContent = remoteUserData.content ? this.getKeybindingsContentFromSyncContent(remoteUserData.content) : null;
|
||||
// Get file content last to get the latest
|
||||
const fileContent = await this.getLocalContent();
|
||||
let hasLocalChanged: boolean = false;
|
||||
let hasRemoteChanged: boolean = false;
|
||||
let hasConflicts: boolean = false;
|
||||
let previewContent = null;
|
||||
|
||||
if (remoteContent) {
|
||||
const localContent: string = fileContent ? fileContent.value.toString() : '[]';
|
||||
if (this.hasErrors(localContent)) {
|
||||
this.logService.error('Keybindings: Unable to sync keybindings as there are errors/warning in keybindings file.');
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
|
||||
}
|
||||
|
||||
if (!lastSyncContent // First time sync
|
||||
|| lastSyncContent !== localContent // Local has forwarded
|
||||
|| lastSyncContent !== remoteContent // Remote has forwarded
|
||||
) {
|
||||
this.logService.trace('Keybindings: Merging remote keybindings with local keybindings...');
|
||||
const formattingOptions = await this.userDataSyncUtilService.resolveFormattingOptions(this.environmentService.keybindingsResource);
|
||||
const result = await merge(localContent, remoteContent, lastSyncContent, formattingOptions, this.userDataSyncUtilService);
|
||||
// Sync only if there are changes
|
||||
if (result.hasChanges) {
|
||||
hasLocalChanged = result.mergeContent !== localContent;
|
||||
hasRemoteChanged = result.mergeContent !== remoteContent;
|
||||
hasConflicts = result.hasConflicts;
|
||||
previewContent = result.mergeContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// First time syncing to remote
|
||||
else if (fileContent) {
|
||||
this.logService.info('Keybindings: Remote keybindings does not exist. Synchronizing keybindings for the first time.');
|
||||
hasRemoteChanged = true;
|
||||
previewContent = fileContent.value.toString();
|
||||
}
|
||||
|
||||
if (previewContent && !token.isCancellationRequested) {
|
||||
await this.fileService.writeFile(this.environmentService.keybindingsSyncPreviewResource, VSBuffer.fromString(previewContent));
|
||||
}
|
||||
|
||||
return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts };
|
||||
}
|
||||
|
||||
private async getLocalContent(): Promise<IFileContent | null> {
|
||||
try {
|
||||
return await this.fileService.readFile(this.environmentService.keybindingsResource);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async updateLocalContent(newContent: string, oldContent: IFileContent | null): Promise<void> {
|
||||
if (oldContent) {
|
||||
// file exists already
|
||||
await this.fileService.writeFile(this.environmentService.keybindingsResource, VSBuffer.fromString(newContent), oldContent);
|
||||
} else {
|
||||
// file does not exist
|
||||
await this.fileService.createFile(this.environmentService.keybindingsResource, VSBuffer.fromString(newContent), { overwrite: false });
|
||||
}
|
||||
}
|
||||
|
||||
private async getLastSyncUserData(): Promise<IUserData | null> {
|
||||
try {
|
||||
const content = await this.fileService.readFile(this.lastSyncKeybindingsResource);
|
||||
return JSON.parse(content.value.toString());
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private async updateLastSyncUserData(remoteUserData: IUserData): Promise<void> {
|
||||
await this.fileService.writeFile(this.lastSyncKeybindingsResource, VSBuffer.fromString(JSON.stringify(remoteUserData)));
|
||||
}
|
||||
|
||||
private async getRemoteUserData(lastSyncData: IUserData | null): Promise<IUserData> {
|
||||
return this.userDataSyncStoreService.read(KeybindingsSynchroniser.EXTERNAL_USER_DATA_KEYBINDINGS_KEY, lastSyncData);
|
||||
}
|
||||
|
||||
private async updateRemoteUserData(content: string, ref: string | null): Promise<string> {
|
||||
return this.userDataSyncStoreService.write(KeybindingsSynchroniser.EXTERNAL_USER_DATA_KEYBINDINGS_KEY, content, ref);
|
||||
}
|
||||
|
||||
private getKeybindingsContentFromSyncContent(syncContent: string): string | null {
|
||||
try {
|
||||
const parsed = <ISyncContent>JSON.parse(syncContent);
|
||||
if (!this.configurationService.getValue<boolean>('sync.keybindingsPerPlatform')) {
|
||||
return isUndefined(parsed.all) ? null : parsed.all;
|
||||
}
|
||||
switch (OS) {
|
||||
case OperatingSystem.Macintosh:
|
||||
return isUndefined(parsed.mac) ? null : parsed.mac;
|
||||
case OperatingSystem.Linux:
|
||||
return isUndefined(parsed.linux) ? null : parsed.linux;
|
||||
case OperatingSystem.Windows:
|
||||
return isUndefined(parsed.windows) ? null : parsed.windows;
|
||||
}
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private updateSyncContent(keybindingsContent: string, syncContent: string | null): string {
|
||||
let parsed: ISyncContent = {};
|
||||
try {
|
||||
parsed = JSON.parse(syncContent || '{}');
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
if (!this.configurationService.getValue<boolean>('sync.keybindingsPerPlatform')) {
|
||||
parsed.all = keybindingsContent;
|
||||
} else {
|
||||
delete parsed.all;
|
||||
}
|
||||
switch (OS) {
|
||||
case OperatingSystem.Macintosh:
|
||||
parsed.mac = keybindingsContent;
|
||||
break;
|
||||
case OperatingSystem.Linux:
|
||||
parsed.linux = keybindingsContent;
|
||||
break;
|
||||
case OperatingSystem.Windows:
|
||||
parsed.windows = keybindingsContent;
|
||||
break;
|
||||
}
|
||||
return JSON.stringify(parsed);
|
||||
}
|
||||
|
||||
}
|
||||
45
src/vs/platform/userDataSync/common/keybindingsSyncIpc.ts
Normal file
45
src/vs/platform/userDataSync/common/keybindingsSyncIpc.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
|
||||
export class UserDataSycnUtilServiceChannel implements IServerChannel {
|
||||
|
||||
constructor(private readonly service: IUserDataSyncUtilService) { }
|
||||
|
||||
listen(_: unknown, event: string): Event<any> {
|
||||
throw new Error(`Event not found: ${event}`);
|
||||
}
|
||||
|
||||
call(context: any, command: string, args?: any): Promise<any> {
|
||||
switch (command) {
|
||||
case 'resolveUserKeybindings': return this.service.resolveUserBindings(args[0]);
|
||||
case 'resolveFormattingOptions': return this.service.resolveFormattingOptions(URI.revive(args[0]));
|
||||
}
|
||||
throw new Error('Invalid call');
|
||||
}
|
||||
}
|
||||
|
||||
export class UserDataSyncUtilServiceClient implements IUserDataSyncUtilService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
constructor(private readonly channel: IChannel) {
|
||||
}
|
||||
|
||||
async resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>> {
|
||||
return this.channel.call('resolveUserKeybindings', [userbindings]);
|
||||
}
|
||||
|
||||
async resolveFormattingOptions(file: URI): Promise<FormattingOptions> {
|
||||
return this.channel.call('resolveFormattingOptions', [file]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -80,7 +80,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
||||
}
|
||||
|
||||
async sync(_continue?: boolean): Promise<boolean> {
|
||||
if (!this.configurationService.getValue<boolean>('configurationSync.enableSettings')) {
|
||||
if (!this.configurationService.getValue<boolean>('sync.enableSettings')) {
|
||||
this.logService.trace('Settings: Skipping synchronizing settings as it is disabled.');
|
||||
return false;
|
||||
}
|
||||
@@ -135,10 +135,9 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
||||
}
|
||||
|
||||
private async continueSync(): Promise<boolean> {
|
||||
if (this.status !== SyncStatus.HasConflicts) {
|
||||
return false;
|
||||
if (this.status === SyncStatus.HasConflicts) {
|
||||
await this.apply();
|
||||
}
|
||||
await this.apply();
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -153,7 +152,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
||||
if (this.hasErrors(content)) {
|
||||
const error = new Error(localize('errorInvalidSettings', "Unable to sync settings. Please resolve conflicts without any errors/warnings and try again."));
|
||||
this.logService.error(error);
|
||||
return Promise.reject(error);
|
||||
throw error;
|
||||
}
|
||||
|
||||
let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise;
|
||||
@@ -188,7 +187,7 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
||||
|
||||
private hasErrors(content: string): boolean {
|
||||
const parseErrors: ParseError[] = [];
|
||||
parse(content, parseErrors);
|
||||
parse(content, parseErrors, { allowEmptyContent: true, allowTrailingComma: true });
|
||||
return parseErrors.length > 0;
|
||||
}
|
||||
|
||||
@@ -218,8 +217,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
||||
}
|
||||
|
||||
if (!lastSyncData // First time sync
|
||||
|| lastSyncData.content !== localContent // Local has moved forwarded
|
||||
|| lastSyncData.content !== remoteContent // Remote has moved forwarded
|
||||
|| lastSyncData.content !== localContent // Local has forwarded
|
||||
|| lastSyncData.content !== remoteContent // Remote has forwarded
|
||||
) {
|
||||
this.logService.trace('Settings: Merging remote settings with local settings...');
|
||||
const result = await this.settingsMergeService.merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null, this.getIgnoredSettings());
|
||||
@@ -248,13 +247,23 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser {
|
||||
}
|
||||
|
||||
private getIgnoredSettings(settingsContent?: string): string[] {
|
||||
const value: string[] = (settingsContent ? parse(settingsContent)['configurationSync.settingsToIgnore'] : this.configurationService.getValue<string[]>('configurationSync.settingsToIgnore')) || [];
|
||||
let value: string[] = [];
|
||||
if (settingsContent) {
|
||||
const setting = parse(settingsContent);
|
||||
if (setting) {
|
||||
value = setting['sync.ignoredSettings'];
|
||||
}
|
||||
} else {
|
||||
value = this.configurationService.getValue<string[]>('sync.ignoredSettings');
|
||||
}
|
||||
const added: string[] = [], removed: string[] = [];
|
||||
for (const key of value) {
|
||||
if (startsWith(key, '-')) {
|
||||
removed.push(key.substring(1));
|
||||
} else {
|
||||
added.push(key);
|
||||
if (Array.isArray(value)) {
|
||||
for (const key of value) {
|
||||
if (startsWith(key, '-')) {
|
||||
removed.push(key.substring(1));
|
||||
} else {
|
||||
added.push(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
return [...DEFAULT_IGNORED_SETTINGS, ...added].filter(setting => removed.indexOf(setting) === -1);
|
||||
|
||||
69
src/vs/platform/userDataSync/common/userDataAutoSync.ts
Normal file
69
src/vs/platform/userDataSync/common/userDataAutoSync.ts
Normal file
@@ -0,0 +1,69 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IUserDataSyncService, SyncStatus, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
|
||||
export class UserDataAutoSync extends Disposable {
|
||||
|
||||
private enabled: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
|
||||
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
||||
@IAuthTokenService private readonly authTokenService: IAuthTokenService,
|
||||
) {
|
||||
super();
|
||||
this.updateEnablement(false);
|
||||
this._register(Event.any<any>(authTokenService.onDidChangeStatus, userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true)));
|
||||
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('sync.enable'))(() => this.updateEnablement(true)));
|
||||
}
|
||||
|
||||
private updateEnablement(stopIfDisabled: boolean): void {
|
||||
const enabled = this.isSyncEnabled();
|
||||
if (this.enabled === enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.enabled = enabled;
|
||||
if (this.enabled) {
|
||||
this.logService.info('Syncing configuration started');
|
||||
this.sync(true);
|
||||
return;
|
||||
} else {
|
||||
if (stopIfDisabled) {
|
||||
this.userDataSyncService.stop();
|
||||
this.logService.info('Syncing configuration stopped.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected async sync(loop: boolean): Promise<void> {
|
||||
if (this.enabled) {
|
||||
try {
|
||||
await this.userDataSyncService.sync();
|
||||
} catch (e) {
|
||||
this.logService.error(e);
|
||||
}
|
||||
if (loop) {
|
||||
await timeout(1000 * 60 * 5); // Loop sync for every 5 min.
|
||||
this.sync(loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private isSyncEnabled(): boolean {
|
||||
return this.configurationService.getValue<boolean>('sync.enable')
|
||||
&& this.userDataSyncService.status !== SyncStatus.Uninitialized
|
||||
&& this.authTokenService.status === AuthTokenStatus.SignedIn;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,61 +14,74 @@ import { IDisposable } from 'vs/base/common/lifecycle';
|
||||
import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry';
|
||||
import { IJSONSchema } from 'vs/base/common/jsonSchema';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
const CONFIGURATION_SYNC_STORE_KEY = 'configurationSync.store';
|
||||
|
||||
export const DEFAULT_IGNORED_SETTINGS = [
|
||||
'configurationSync.enable',
|
||||
'configurationSync.enableSettings',
|
||||
'configurationSync.enableExtensions',
|
||||
CONFIGURATION_SYNC_STORE_KEY,
|
||||
'sync.enable',
|
||||
'sync.enableSettings',
|
||||
'sync.enableExtensions',
|
||||
];
|
||||
|
||||
export function registerConfiguration(): IDisposable {
|
||||
const ignoredSettingsSchemaId = 'vscode://schemas/ignoredSettings';
|
||||
const configurationRegistry = Registry.as<IConfigurationRegistry>(ConfigurationExtensions.Configuration);
|
||||
configurationRegistry.registerConfiguration({
|
||||
id: 'configurationSync',
|
||||
id: 'sync',
|
||||
order: 30,
|
||||
title: localize('configurationSync', "Configuration Sync"),
|
||||
title: localize('sync', "Sync"),
|
||||
type: 'object',
|
||||
properties: {
|
||||
'configurationSync.enable': {
|
||||
'sync.enable': {
|
||||
type: 'boolean',
|
||||
description: localize('configurationSync.enable', "When enabled, synchronises configuration that includes Settings and Extensions."),
|
||||
default: true,
|
||||
description: localize('sync.enable', "Enable synchronization."),
|
||||
default: false,
|
||||
scope: ConfigurationScope.APPLICATION
|
||||
},
|
||||
'configurationSync.enableSettings': {
|
||||
'sync.enableSettings': {
|
||||
type: 'boolean',
|
||||
description: localize('configurationSync.enableSettings', "When enabled settings are synchronised while synchronizing configuration."),
|
||||
description: localize('sync.enableSettings', "Enable synchronizing settings."),
|
||||
default: true,
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
},
|
||||
'configurationSync.enableExtensions': {
|
||||
'sync.enableExtensions': {
|
||||
type: 'boolean',
|
||||
description: localize('configurationSync.enableExtensions', "When enabled extensions are synchronised while synchronizing configuration."),
|
||||
description: localize('sync.enableExtensions', "Enable synchronizing extensions."),
|
||||
default: true,
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
},
|
||||
'configurationSync.extensionsToIgnore': {
|
||||
'sync.enableKeybindings': {
|
||||
type: 'boolean',
|
||||
description: localize('sync.enableKeybindings', "Enable synchronizing keybindings."),
|
||||
default: true,
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
},
|
||||
'sync.keybindingsPerPlatform': {
|
||||
type: 'boolean',
|
||||
description: localize('sync.keybindingsPerPlatform', "Synchronize keybindings per platform."),
|
||||
default: true,
|
||||
scope: ConfigurationScope.APPLICATION,
|
||||
},
|
||||
'sync.ignoredExtensions': {
|
||||
'type': 'array',
|
||||
description: localize('configurationSync.extensionsToIgnore', "Configure extensions to be ignored while syncing."),
|
||||
description: localize('sync.ignoredExtensions', "Configure extensions to be ignored while synchronizing."),
|
||||
'default': [],
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
uniqueItems: true
|
||||
},
|
||||
'configurationSync.settingsToIgnore': {
|
||||
'sync.ignoredSettings': {
|
||||
'type': 'array',
|
||||
description: localize('configurationSync.settingsToIgnore', "Configure settings to be ignored while syncing. \nDefault Ignored Settings:\n\n{0}", DEFAULT_IGNORED_SETTINGS.sort().map(setting => `- ${setting}`).join('\n')),
|
||||
description: localize('sync.ignoredSettings', "Configure settings to be ignored while synchronizing. \nDefault Ignored Settings:\n\n{0}", DEFAULT_IGNORED_SETTINGS.sort().map(setting => `- ${setting}`).join('\n')),
|
||||
'default': [],
|
||||
'scope': ConfigurationScope.APPLICATION,
|
||||
$ref: ignoredSettingsSchemaId,
|
||||
additionalProperties: true,
|
||||
uniqueItems: true
|
||||
},
|
||||
'configurationSync.enableAuth': {
|
||||
'type': 'boolean',
|
||||
description: localize('configurationSync.enableAuth', "Enables authentication and requires VS Code restart when changed"),
|
||||
'default': false,
|
||||
'scope': ConfigurationScope.APPLICATION
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -104,12 +117,23 @@ export class UserDataSyncStoreError extends Error {
|
||||
|
||||
}
|
||||
|
||||
export interface IUserDataSyncStore {
|
||||
url: string;
|
||||
name: string;
|
||||
account: string;
|
||||
}
|
||||
|
||||
export function getUserDataSyncStore(configurationService: IConfigurationService): IUserDataSyncStore | undefined {
|
||||
const value = configurationService.getValue<IUserDataSyncStore>(CONFIGURATION_SYNC_STORE_KEY);
|
||||
return value && value.url && value.name && value.account ? value : undefined;
|
||||
}
|
||||
|
||||
export const IUserDataSyncStoreService = createDecorator<IUserDataSyncStoreService>('IUserDataSyncStoreService');
|
||||
|
||||
export interface IUserDataSyncStoreService {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
readonly enabled: boolean;
|
||||
readonly userDataSyncStore: IUserDataSyncStore | undefined;
|
||||
|
||||
read(key: string, oldValue: IUserData | null): Promise<IUserData>;
|
||||
write(key: string, content: string, ref: string | null): Promise<string>;
|
||||
@@ -123,6 +147,7 @@ export interface ISyncExtension {
|
||||
|
||||
export const enum SyncSource {
|
||||
Settings = 1,
|
||||
Keybindings,
|
||||
Extensions
|
||||
}
|
||||
|
||||
@@ -164,6 +189,18 @@ export interface ISettingsMergeService {
|
||||
|
||||
}
|
||||
|
||||
export const IUserDataSyncUtilService = createDecorator<IUserDataSyncUtilService>('IUserDataSyncUtilService');
|
||||
|
||||
export interface IUserDataSyncUtilService {
|
||||
|
||||
_serviceBrand: undefined;
|
||||
|
||||
resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>>;
|
||||
|
||||
resolveFormattingOptions(resource: URI): Promise<FormattingOptions>;
|
||||
|
||||
}
|
||||
|
||||
export const IUserDataSyncLogService = createDecorator<IUserDataSyncLogService>('IUserDataSyncLogService');
|
||||
|
||||
export interface IUserDataSyncLogService extends ILogService {
|
||||
|
||||
@@ -3,16 +3,15 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource, IUserDataSyncLogService, UserDataSyncStoreError, UserDataSyncStoreErrorCode } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService, SyncSource } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync';
|
||||
import { Emitter, Event } from 'vs/base/common/event';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { timeout } from 'vs/base/common/async';
|
||||
import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensionsSync';
|
||||
import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { KeybindingsSynchroniser } from 'vs/platform/userDataSync/common/keybindingsSync';
|
||||
|
||||
export class UserDataSyncService extends Disposable implements IUserDataSyncService {
|
||||
|
||||
@@ -31,6 +30,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
get conflictsSource(): SyncSource | null { return this._conflictsSource; }
|
||||
|
||||
private readonly settingsSynchroniser: SettingsSynchroniser;
|
||||
private readonly keybindingsSynchroniser: KeybindingsSynchroniser;
|
||||
private readonly extensionsSynchroniser: ExtensionsSynchroniser;
|
||||
|
||||
constructor(
|
||||
@@ -40,20 +40,25 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
) {
|
||||
super();
|
||||
this.settingsSynchroniser = this._register(this.instantiationService.createInstance(SettingsSynchroniser));
|
||||
this.keybindingsSynchroniser = this._register(this.instantiationService.createInstance(KeybindingsSynchroniser));
|
||||
this.extensionsSynchroniser = this._register(this.instantiationService.createInstance(ExtensionsSynchroniser));
|
||||
this.synchronisers = [this.settingsSynchroniser, this.extensionsSynchroniser];
|
||||
this.synchronisers = [this.settingsSynchroniser, this.keybindingsSynchroniser, this.extensionsSynchroniser];
|
||||
this.updateStatus();
|
||||
this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus()));
|
||||
|
||||
if (this.userDataSyncStoreService.userDataSyncStore) {
|
||||
this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus()));
|
||||
this._register(authTokenService.onDidChangeStatus(() => this.onDidChangeAuthTokenStatus()));
|
||||
}
|
||||
|
||||
this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal));
|
||||
this._register(authTokenService.onDidChangeStatus(() => this.onDidChangeAuthTokenStatus()));
|
||||
}
|
||||
|
||||
async sync(_continue?: boolean): Promise<boolean> {
|
||||
if (!this.userDataSyncStoreService.enabled) {
|
||||
if (!this.userDataSyncStoreService.userDataSyncStore) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
if (this.authTokenService.status === AuthTokenStatus.Inactive) {
|
||||
return Promise.reject('Not Authenticated. Please sign in to start sync.');
|
||||
if (this.authTokenService.status === AuthTokenStatus.SignedOut) {
|
||||
throw new Error('Not Authenticated. Please sign in to start sync.');
|
||||
}
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
if (!await synchroniser.sync(_continue)) {
|
||||
@@ -64,7 +69,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
|
||||
stop(): void {
|
||||
if (!this.userDataSyncStoreService.enabled) {
|
||||
if (!this.userDataSyncStoreService.userDataSyncStore) {
|
||||
throw new Error('Not enabled');
|
||||
}
|
||||
for (const synchroniser of this.synchronisers) {
|
||||
@@ -89,7 +94,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
}
|
||||
|
||||
private computeStatus(): SyncStatus {
|
||||
if (!this.userDataSyncStoreService.enabled) {
|
||||
if (!this.userDataSyncStoreService.userDataSyncStore) {
|
||||
return SyncStatus.Uninitialized;
|
||||
}
|
||||
if (this.synchronisers.some(s => s.status === SyncStatus.HasConflicts)) {
|
||||
@@ -107,83 +112,17 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ
|
||||
if (source instanceof SettingsSynchroniser) {
|
||||
return SyncSource.Settings;
|
||||
}
|
||||
if (source instanceof KeybindingsSynchroniser) {
|
||||
return SyncSource.Keybindings;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private onDidChangeAuthTokenStatus(): void {
|
||||
if (this.authTokenService.status === AuthTokenStatus.Inactive) {
|
||||
if (this.authTokenService.status === AuthTokenStatus.SignedOut) {
|
||||
this.stop();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class UserDataAutoSync extends Disposable {
|
||||
|
||||
private enabled: boolean = false;
|
||||
|
||||
constructor(
|
||||
@IConfigurationService private readonly configurationService: IConfigurationService,
|
||||
@IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService,
|
||||
@IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService,
|
||||
@IUserDataSyncLogService private readonly logService: IUserDataSyncLogService,
|
||||
@IAuthTokenService private readonly authTokenService: IAuthTokenService,
|
||||
) {
|
||||
super();
|
||||
this.updateEnablement(false);
|
||||
this._register(Event.any<any>(authTokenService.onDidChangeStatus, userDataSyncService.onDidChangeStatus)(() => this.updateEnablement(true)));
|
||||
this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateEnablement(true)));
|
||||
|
||||
// Sync immediately if there is a local change.
|
||||
this._register(Event.debounce(this.userDataSyncService.onDidChangeLocal, () => undefined, 500)(() => this.sync(false)));
|
||||
}
|
||||
|
||||
private updateEnablement(stopIfDisabled: boolean): void {
|
||||
const enabled = this.isSyncEnabled();
|
||||
if (this.enabled === enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.enabled = enabled;
|
||||
if (this.enabled) {
|
||||
this.logService.info('Syncing configuration started');
|
||||
this.sync(true);
|
||||
return;
|
||||
} else {
|
||||
if (stopIfDisabled) {
|
||||
this.userDataSyncService.stop();
|
||||
this.logService.info('Syncing configuration stopped.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private async sync(loop: boolean): Promise<void> {
|
||||
if (this.enabled) {
|
||||
try {
|
||||
await this.userDataSyncService.sync();
|
||||
} catch (e) {
|
||||
if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Unauthroized) {
|
||||
if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Unauthroized && this.authTokenService.status === AuthTokenStatus.Disabled) {
|
||||
this.logService.error('Sync failed because the server requires authorization. Please enable authorization.');
|
||||
} else {
|
||||
this.logService.error(e);
|
||||
}
|
||||
}
|
||||
this.logService.error(e);
|
||||
}
|
||||
if (loop) {
|
||||
await timeout(1000 * 5); // Loop sync for every 5s.
|
||||
this.sync(loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private isSyncEnabled(): boolean {
|
||||
return this.configurationService.getValue<boolean>('configurationSync.enable')
|
||||
&& this.userDataSyncService.status !== SyncStatus.Uninitialized
|
||||
&& this.authTokenService.status !== AuthTokenStatus.Inactive;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -4,35 +4,36 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { Disposable, } from 'vs/base/common/lifecycle';
|
||||
import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IProductService } from 'vs/platform/product/common/productService';
|
||||
import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError, IUserDataSyncStore, getUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { IRequestService, asText, isSuccess } from 'vs/platform/request/common/request';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { joinPath } from 'vs/base/common/resources';
|
||||
import { CancellationToken } from 'vs/base/common/cancellation';
|
||||
import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request';
|
||||
import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth';
|
||||
import { IAuthTokenService } from 'vs/platform/auth/common/auth';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
|
||||
export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
get enabled(): boolean { return !!this.productService.settingsSyncStoreUrl; }
|
||||
readonly userDataSyncStore: IUserDataSyncStore | undefined;
|
||||
|
||||
constructor(
|
||||
@IProductService private readonly productService: IProductService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IRequestService private readonly requestService: IRequestService,
|
||||
@IAuthTokenService private readonly authTokenService: IAuthTokenService,
|
||||
) {
|
||||
super();
|
||||
this.userDataSyncStore = getUserDataSyncStore(configurationService);
|
||||
}
|
||||
|
||||
async read(key: string, oldValue: IUserData | null): Promise<IUserData> {
|
||||
if (!this.enabled) {
|
||||
return Promise.reject(new Error('No settings sync store url configured.'));
|
||||
if (!this.userDataSyncStore) {
|
||||
throw new Error('No settings sync store url configured.');
|
||||
}
|
||||
|
||||
const url = joinPath(URI.parse(this.productService.settingsSyncStoreUrl!), 'resource', key, 'latest').toString();
|
||||
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key, 'latest').toString();
|
||||
const headers: IHeaders = {};
|
||||
if (oldValue) {
|
||||
headers['If-None-Match'] = oldValue.ref;
|
||||
@@ -58,11 +59,11 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
|
||||
}
|
||||
|
||||
async write(key: string, data: string, ref: string | null): Promise<string> {
|
||||
if (!this.enabled) {
|
||||
return Promise.reject(new Error('No settings sync store url configured.'));
|
||||
if (!this.userDataSyncStore) {
|
||||
throw new Error('No settings sync store url configured.');
|
||||
}
|
||||
|
||||
const url = joinPath(URI.parse(this.productService.settingsSyncStoreUrl!), 'resource', key).toString();
|
||||
const url = joinPath(URI.parse(this.userDataSyncStore.url), 'resource', key).toString();
|
||||
const headers: IHeaders = { 'Content-Type': 'text/plain' };
|
||||
if (ref) {
|
||||
headers['If-Match'] = ref;
|
||||
@@ -87,21 +88,17 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn
|
||||
}
|
||||
|
||||
private async request(options: IRequestOptions, token: CancellationToken): Promise<IRequestContext> {
|
||||
if (this.authTokenService.status !== AuthTokenStatus.Disabled) {
|
||||
const authToken = await this.authTokenService.getToken();
|
||||
if (!authToken) {
|
||||
return Promise.reject(new Error('No Auth Token Available.'));
|
||||
}
|
||||
options.headers = options.headers || {};
|
||||
options.headers['authorization'] = `Bearer ${authToken}`;
|
||||
const authToken = await this.authTokenService.getToken();
|
||||
if (!authToken) {
|
||||
throw new Error('No Auth Token Available.');
|
||||
}
|
||||
options.headers = options.headers || {};
|
||||
options.headers['authorization'] = `Bearer ${authToken}`;
|
||||
|
||||
const context = await this.requestService.request(options, token);
|
||||
|
||||
if (context.res.statusCode === 401) {
|
||||
if (this.authTokenService.status !== AuthTokenStatus.Disabled) {
|
||||
this.authTokenService.refreshToken();
|
||||
}
|
||||
this.authTokenService.refreshToken();
|
||||
// Throw Unauthorized Error
|
||||
throw new UserDataSyncStoreError('Unauthorized', UserDataSyncStoreErrorCode.Unauthroized);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IUserDataSyncService, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IElectronService } from 'vs/platform/electron/node/electron';
|
||||
import { UserDataAutoSync as BaseUserDataAutoSync } from 'vs/platform/userDataSync/common/userDataAutoSync';
|
||||
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
|
||||
import { IAuthTokenService } from 'vs/platform/auth/common/auth';
|
||||
|
||||
export class UserDataAutoSync extends BaseUserDataAutoSync {
|
||||
|
||||
constructor(
|
||||
@IUserDataSyncService userDataSyncService: IUserDataSyncService,
|
||||
@IElectronService electronService: IElectronService,
|
||||
@IConfigurationService configurationService: IConfigurationService,
|
||||
@IUserDataSyncLogService logService: IUserDataSyncLogService,
|
||||
@IAuthTokenService authTokenService: IAuthTokenService,
|
||||
) {
|
||||
super(configurationService, userDataSyncService, logService, authTokenService);
|
||||
|
||||
// Sync immediately if there is a local change.
|
||||
this._register(Event.debounce(Event.any<any>(
|
||||
electronService.onWindowFocus,
|
||||
electronService.onWindowOpen,
|
||||
userDataSyncService.onDidChangeLocal
|
||||
), () => undefined, 500)(() => this.sync(false)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,735 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* 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 { merge } from 'vs/platform/userDataSync/common/keybindingsMerge';
|
||||
import { IStringDictionary } from 'vs/base/common/collections';
|
||||
import { OperatingSystem, OS } from 'vs/base/common/platform';
|
||||
import { IUserDataSyncUtilService } from 'vs/platform/userDataSync/common/userDataSync';
|
||||
import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
|
||||
suite('KeybindingsMerge - No Conflicts', () => {
|
||||
|
||||
test('merge when local and remote are same with one entry', async () => {
|
||||
const localContent = stringify([{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const remoteContent = stringify([{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when local and remote are same with similar when contexts', async () => {
|
||||
const localContent = stringify([{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const remoteContent = stringify([{ key: 'alt+c', command: 'a', when: '!editorReadonly && editorTextFocus' }]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when local and remote has entries in different order', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+a', command: 'a', when: 'editorTextFocus' }
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+a', command: 'a', when: 'editorTextFocus' },
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' }
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when local and remote are same with multiple entries', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } }
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } }
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when local and remote are same with different base content', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } }
|
||||
]);
|
||||
const baseContent = stringify([
|
||||
{ key: 'ctrl+c', command: 'e' },
|
||||
{ key: 'shift+d', command: 'd', args: { text: '`' } }
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } }
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, baseContent);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when local and remote are same with multiple entries in different order', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } }
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when local and remote are same when remove entry is in different order', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } }
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(!actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when a new entry is added to remote', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
test('merge when multiple new entries are added to remote', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
{ key: 'cmd+d', command: 'c' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
test('merge when multiple new entries are added to remote from base and local has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
{ key: 'cmd+d', command: 'c' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, localContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
test('merge when an entry is removed from remote from base and local has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, localContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
test('merge when an entry (same command) is removed from remote from base and local has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, localContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
test('merge when an entry is updated in remote from base and local has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, localContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
test('merge when a command with multiple entries is updated from remote from base and local has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'shift+c', command: 'c' },
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: 'b' },
|
||||
{ key: 'cmd+c', command: 'a' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'shift+c', command: 'c' },
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: 'b' },
|
||||
{ key: 'cmd+d', command: 'a' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, localContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
test('merge when remote has moved forwareded with multiple changes and local stays with base', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'alt+f', command: 'f' },
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, localContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, remoteContent);
|
||||
});
|
||||
|
||||
test('merge when a new entry is added to local', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when multiple new entries are added to local', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
{ key: 'cmd+d', command: 'c' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when multiple new entries are added to local from base and remote is not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
{ key: 'cmd+d', command: 'c' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, remoteContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when an entry is removed from local from base and remote has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, remoteContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when an entry (with same command) is removed from local from base and remote has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, remoteContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when an entry is updated in local from base and remote has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, remoteContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when a command with multiple entries is updated from local from base and remote has not changed', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'shift+c', command: 'c' },
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: 'b' },
|
||||
{ key: 'cmd+c', command: 'a' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'shift+c', command: 'c' },
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+d', command: 'b' },
|
||||
{ key: 'cmd+d', command: 'a' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, remoteContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, localContent);
|
||||
});
|
||||
|
||||
test('merge when local has moved forwareded with multiple changes and remote stays with base', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'alt+f', command: 'f' },
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'cmd+c', command: 'b', args: { text: '`' } },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
]);
|
||||
const expected = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'alt+d', command: '-a' },
|
||||
{ key: 'alt+f', command: 'f' },
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, remoteContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, expected);
|
||||
});
|
||||
|
||||
test('merge when local and remote has moved forwareded with conflicts', async () => {
|
||||
const baseContent = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'ctrl+c', command: '-a' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'alt+a', command: 'f' },
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
]);
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
{ key: 'alt+a', command: 'f' },
|
||||
{ key: 'alt+e', command: 'e' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+a', command: 'f' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
{ key: 'cmd+d', command: 'd' },
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'alt+c', command: 'c', when: 'context1' },
|
||||
{ key: 'alt+g', command: 'g', when: 'context2' },
|
||||
]);
|
||||
const expected = stringify([
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'cmd+d', command: 'd' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
{ key: 'alt+c', command: 'c', when: 'context1' },
|
||||
{ key: 'alt+a', command: 'f' },
|
||||
{ key: 'alt+e', command: 'e' },
|
||||
{ key: 'alt+g', command: 'g', when: 'context2' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, baseContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(!actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent, expected);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
suite.skip('KeybindingsMerge - Conflicts', () => {
|
||||
|
||||
test('merge when local and remote with one entry but different value', async () => {
|
||||
const localContent = stringify([{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const remoteContent = stringify([{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`<<<<<<< local
|
||||
[
|
||||
{
|
||||
"key": "alt+d",
|
||||
"command": "a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
}
|
||||
]
|
||||
=======
|
||||
[
|
||||
{
|
||||
"key": "alt+c",
|
||||
"command": "a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
}
|
||||
]
|
||||
>>>>>>> remote`);
|
||||
});
|
||||
|
||||
test('merge when local and remote with different keybinding', async () => {
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+a', command: '-a', when: 'editorTextFocus && !editorReadonly' }
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+a', command: '-a', when: 'editorTextFocus && !editorReadonly' }
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, null);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`<<<<<<< local
|
||||
[
|
||||
{
|
||||
"key": "alt+d",
|
||||
"command": "a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
},
|
||||
{
|
||||
"key": "alt+a",
|
||||
"command": "-a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
}
|
||||
]
|
||||
=======
|
||||
[
|
||||
{
|
||||
"key": "alt+c",
|
||||
"command": "a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
},
|
||||
{
|
||||
"key": "alt+a",
|
||||
"command": "-a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
}
|
||||
]
|
||||
>>>>>>> remote`);
|
||||
});
|
||||
|
||||
test('merge when the entry is removed in local but updated in remote', async () => {
|
||||
const baseContent = stringify([{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const localContent = stringify([]);
|
||||
const remoteContent = stringify([{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, baseContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`<<<<<<< local
|
||||
[]
|
||||
=======
|
||||
[
|
||||
{
|
||||
"key": "alt+c",
|
||||
"command": "a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
}
|
||||
]
|
||||
>>>>>>> remote`);
|
||||
});
|
||||
|
||||
test('merge when the entry is removed in local but updated in remote and a new entry is added in local', async () => {
|
||||
const baseContent = stringify([{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const localContent = stringify([{ key: 'alt+b', command: 'b' }]);
|
||||
const remoteContent = stringify([{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, baseContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`<<<<<<< local
|
||||
[
|
||||
{
|
||||
"key": "alt+b",
|
||||
"command": "b"
|
||||
}
|
||||
]
|
||||
=======
|
||||
[
|
||||
{
|
||||
"key": "alt+c",
|
||||
"command": "a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
}
|
||||
]
|
||||
>>>>>>> remote`);
|
||||
});
|
||||
|
||||
test('merge when the entry is removed in remote but updated in local', async () => {
|
||||
const baseContent = stringify([{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const localContent = stringify([{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const remoteContent = stringify([]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, baseContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`<<<<<<< local
|
||||
[
|
||||
{
|
||||
"key": "alt+c",
|
||||
"command": "a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
}
|
||||
]
|
||||
=======
|
||||
[]
|
||||
>>>>>>> remote`);
|
||||
});
|
||||
|
||||
test('merge when the entry is removed in remote but updated in local and a new entry is added in remote', async () => {
|
||||
const baseContent = stringify([{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const localContent = stringify([{ key: 'alt+c', command: 'a', when: 'editorTextFocus && !editorReadonly' }]);
|
||||
const remoteContent = stringify([{ key: 'alt+b', command: 'b' }]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, baseContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`<<<<<<< local
|
||||
[
|
||||
{
|
||||
"key": "alt+c",
|
||||
"command": "a",
|
||||
"when": "editorTextFocus && !editorReadonly"
|
||||
},
|
||||
{
|
||||
"key": "alt+b",
|
||||
"command": "b"
|
||||
}
|
||||
]
|
||||
=======
|
||||
[
|
||||
{
|
||||
"key": "alt+b",
|
||||
"command": "b"
|
||||
}
|
||||
]
|
||||
>>>>>>> remote`);
|
||||
});
|
||||
|
||||
test('merge when local and remote has moved forwareded with conflicts', async () => {
|
||||
const baseContent = stringify([
|
||||
{ key: 'alt+d', command: 'a', when: 'editorTextFocus && !editorReadonly' },
|
||||
{ key: 'alt+c', command: '-a' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'alt+a', command: 'f' },
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
]);
|
||||
const localContent = stringify([
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'cmd+e', command: 'd' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
{ key: 'cmd+d', command: 'c', when: 'context1' },
|
||||
{ key: 'alt+a', command: 'f' },
|
||||
{ key: 'alt+e', command: 'e' },
|
||||
]);
|
||||
const remoteContent = stringify([
|
||||
{ key: 'alt+a', command: 'f' },
|
||||
{ key: 'cmd+c', command: '-c' },
|
||||
{ key: 'cmd+d', command: 'd' },
|
||||
{ key: 'alt+d', command: '-f' },
|
||||
{ key: 'alt+c', command: 'c', when: 'context1' },
|
||||
{ key: 'alt+g', command: 'g', when: 'context2' },
|
||||
]);
|
||||
const actual = await mergeKeybindings(localContent, remoteContent, baseContent);
|
||||
assert.ok(actual.hasChanges);
|
||||
assert.ok(actual.hasConflicts);
|
||||
assert.equal(actual.mergeContent,
|
||||
`<<<<<<< local
|
||||
[
|
||||
{
|
||||
"key": "alt+d",
|
||||
"command": "-f"
|
||||
},
|
||||
{
|
||||
"key": "cmd+d",
|
||||
"command": "d"
|
||||
},
|
||||
{
|
||||
"key": "cmd+c",
|
||||
"command": "-c"
|
||||
},
|
||||
{
|
||||
"key": "cmd+d",
|
||||
"command": "c",
|
||||
"when": "context1"
|
||||
},
|
||||
{
|
||||
"key": "alt+a",
|
||||
"command": "f"
|
||||
},
|
||||
{
|
||||
"key": "alt+e",
|
||||
"command": "e"
|
||||
},
|
||||
{
|
||||
"key": "alt+g",
|
||||
"command": "g",
|
||||
"when": "context2"
|
||||
}
|
||||
]
|
||||
=======
|
||||
[
|
||||
{
|
||||
"key": "alt+a",
|
||||
"command": "f"
|
||||
},
|
||||
{
|
||||
"key": "cmd+c",
|
||||
"command": "-c"
|
||||
},
|
||||
{
|
||||
"key": "cmd+d",
|
||||
"command": "d"
|
||||
},
|
||||
{
|
||||
"key": "alt+d",
|
||||
"command": "-f"
|
||||
},
|
||||
{
|
||||
"key": "alt+c",
|
||||
"command": "c",
|
||||
"when": "context1"
|
||||
},
|
||||
{
|
||||
"key": "alt+g",
|
||||
"command": "g",
|
||||
"when": "context2"
|
||||
}
|
||||
]
|
||||
>>>>>>> remote`);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
async function mergeKeybindings(localContent: string, remoteContent: string, baseContent: string | null) {
|
||||
const userDataSyncUtilService = new MockUserDataSyncUtilService();
|
||||
const formattingOptions = await userDataSyncUtilService.resolveFormattingOptions();
|
||||
return merge(localContent, remoteContent, baseContent, formattingOptions, userDataSyncUtilService);
|
||||
}
|
||||
|
||||
function stringify(value: any): string {
|
||||
return JSON.stringify(value, null, '\t');
|
||||
}
|
||||
|
||||
class MockUserDataSyncUtilService implements IUserDataSyncUtilService {
|
||||
|
||||
_serviceBrand: any;
|
||||
|
||||
async resolveUserBindings(userbindings: string[]): Promise<IStringDictionary<string>> {
|
||||
const keys: IStringDictionary<string> = {};
|
||||
for (const keybinding of userbindings) {
|
||||
keys[keybinding] = keybinding;
|
||||
}
|
||||
return keys;
|
||||
}
|
||||
|
||||
async resolveFormattingOptions(file?: URI): Promise<FormattingOptions> {
|
||||
return { eol: OS === OperatingSystem.Windows ? '\r\n' : '\n', insertSpaces: false, tabSize: 4 };
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ export interface IBaseOpenWindowsOptions {
|
||||
|
||||
export interface IOpenWindowOptions extends IBaseOpenWindowsOptions {
|
||||
forceNewWindow?: boolean;
|
||||
preferNewWindow?: boolean;
|
||||
|
||||
noRecentEntry?: boolean;
|
||||
}
|
||||
@@ -219,8 +220,9 @@ export interface IAddFoldersRequest {
|
||||
}
|
||||
|
||||
export interface IWindowConfiguration extends ParsedArgs {
|
||||
machineId: string;
|
||||
windowId: number;
|
||||
machineId?: string; // NOTE: This is undefined in the web, the telemetry service directly resolves this.
|
||||
windowId: number; // TODO: should we deprecate this in favor of sessionId?
|
||||
sessionId: string;
|
||||
logLevel: LogLevel;
|
||||
|
||||
mainPid: number;
|
||||
@@ -245,19 +247,14 @@ export interface IWindowConfiguration extends ParsedArgs {
|
||||
fullscreen?: boolean;
|
||||
maximized?: boolean;
|
||||
highContrast?: boolean;
|
||||
frameless?: boolean;
|
||||
accessibilitySupport?: boolean;
|
||||
partsSplashPath?: string;
|
||||
|
||||
perfStartTime?: number;
|
||||
perfAppReady?: number;
|
||||
perfWindowLoadTime?: number;
|
||||
perfEntries: ExportData;
|
||||
|
||||
filesToOpenOrCreate?: IPath[];
|
||||
filesToDiff?: IPath[];
|
||||
filesToWait?: IPathsToWaitFor;
|
||||
termProgram?: string;
|
||||
}
|
||||
|
||||
export interface IRunActionInWindowRequest {
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as fs from 'fs';
|
||||
import { basename, normalize, join, } from 'vs/base/common/path';
|
||||
import { basename, normalize, join, posix } from 'vs/base/common/path';
|
||||
import { localize } from 'vs/nls';
|
||||
import * as arrays from 'vs/base/common/arrays';
|
||||
import { assign, mixin } from 'vs/base/common/objects';
|
||||
@@ -496,7 +496,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
// Remember in recent document list (unless this opens for extension development)
|
||||
// Also do not add paths when files are opened for diffing, only if opened individually
|
||||
if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !openConfig.diffMode && !openConfig.noRecentEntry) {
|
||||
const isDiff = fileInputs && fileInputs.filesToDiff.length > 0;
|
||||
if (!usedWindows.some(window => window.isExtensionDevelopmentHost) && !isDiff && !openConfig.noRecentEntry) {
|
||||
const recents: IRecent[] = [];
|
||||
for (let pathToOpen of pathsToOpen) {
|
||||
if (pathToOpen.workspace) {
|
||||
@@ -1107,8 +1108,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
const remoteAuthority = options.remoteAuthority;
|
||||
|
||||
if (remoteAuthority) {
|
||||
// assume it's a folder or workspace file
|
||||
|
||||
const first = anyPath.charCodeAt(0);
|
||||
// make absolute
|
||||
if (first !== CharCode.Slash) {
|
||||
@@ -1120,11 +1119,15 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic
|
||||
|
||||
const uri = URI.from({ scheme: Schemas.vscodeRemote, authority: remoteAuthority, path: anyPath });
|
||||
|
||||
if (hasWorkspaceFileExtension(anyPath)) {
|
||||
if (forceOpenWorkspaceAsFile) {
|
||||
// guess the file type: If it ends with a slash it's a folder. If it has a file extension, it's a file or a workspace. By defaults it's a folder.
|
||||
if (anyPath.charCodeAt(anyPath.length - 1) !== CharCode.Slash) {
|
||||
if (hasWorkspaceFileExtension(anyPath)) {
|
||||
if (forceOpenWorkspaceAsFile) {
|
||||
return { fileUri: uri, remoteAuthority };
|
||||
}
|
||||
} else if (posix.extname(anyPath).length > 0) {
|
||||
return { fileUri: uri, remoteAuthority };
|
||||
}
|
||||
return { workspace: getWorkspaceIdentifier(uri), remoteAuthority };
|
||||
}
|
||||
return { folderUri: uri, remoteAuthority };
|
||||
}
|
||||
|
||||
@@ -9,10 +9,11 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'
|
||||
import { TernarySearchTree } from 'vs/base/common/map';
|
||||
import { Event } from 'vs/base/common/event';
|
||||
import { IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, isRawUriWorkspaceFolder, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspaceFolderProvider } from 'vs/base/common/labels';
|
||||
|
||||
export const IWorkspaceContextService = createDecorator<IWorkspaceContextService>('contextService');
|
||||
|
||||
export interface IWorkspaceContextService {
|
||||
export interface IWorkspaceContextService extends IWorkspaceFolderProvider {
|
||||
_serviceBrand: undefined;
|
||||
|
||||
/**
|
||||
|
||||
@@ -19,6 +19,7 @@ import { FormattingOptions } from 'vs/base/common/jsonFormatter';
|
||||
import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
import { Event as CommonEvent } from 'vs/base/common/event';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
|
||||
export const WORKSPACE_EXTENSION = 'code-workspace';
|
||||
export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }];
|
||||
@@ -172,6 +173,10 @@ export function toWorkspaceIdentifier(workspace: IWorkspace): IWorkspaceIdentifi
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean {
|
||||
return isEqualOrParent(path, environmentService.untitledWorkspacesHome);
|
||||
}
|
||||
|
||||
export type IMultiFolderWorkspaceInitializationPayload = IWorkspaceIdentifier;
|
||||
export interface ISingleFolderWorkspaceInitializationPayload { id: string; folder: ISingleFolderWorkspaceIdentifier; }
|
||||
export interface IEmptyWorkspaceInitializationPayload { id: string; }
|
||||
@@ -280,12 +285,9 @@ function doParseStoredWorkspace(path: URI, contents: string): IStoredWorkspace {
|
||||
let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser
|
||||
|
||||
// Filter out folders which do not have a path or uri set
|
||||
if (Array.isArray(storedWorkspace.folders)) {
|
||||
if (storedWorkspace && Array.isArray(storedWorkspace.folders)) {
|
||||
storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder));
|
||||
}
|
||||
|
||||
// Validate
|
||||
if (!Array.isArray(storedWorkspace.folders)) {
|
||||
} else {
|
||||
throw new Error(`${path} looks like an invalid workspace file.`);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder, IEnterWorkspaceResult } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder, IEnterWorkspaceResult, isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces';
|
||||
import { IEnvironmentService } from 'vs/platform/environment/common/environment';
|
||||
import { join, dirname } from 'vs/base/common/path';
|
||||
import { mkdirp, writeFile, rimrafSync, readdirSync, writeFileSync } from 'vs/base/node/pfs';
|
||||
@@ -17,7 +17,7 @@ import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
import { originalFSPath, isEqualOrParent, joinPath, isEqual, basename } from 'vs/base/common/resources';
|
||||
import { originalFSPath, joinPath, isEqual, basename } from 'vs/base/common/resources';
|
||||
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ICodeWindow } from 'vs/platform/windows/electron-main/windows';
|
||||
import { localize } from 'vs/nls';
|
||||
@@ -104,7 +104,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
|
||||
}
|
||||
|
||||
private isWorkspacePath(uri: URI): boolean {
|
||||
return this.isInsideWorkspacesHome(uri) || hasWorkspaceFileExtension(uri);
|
||||
return isUntitledWorkspace(uri, this.environmentService) || hasWorkspaceFileExtension(uri);
|
||||
}
|
||||
|
||||
private doResolveWorkspace(path: URI, contents: string): IResolvedWorkspace | null {
|
||||
@@ -130,22 +130,15 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
|
||||
let storedWorkspace: IStoredWorkspace = json.parse(contents); // use fault tolerant parser
|
||||
|
||||
// Filter out folders which do not have a path or uri set
|
||||
if (Array.isArray(storedWorkspace.folders)) {
|
||||
if (storedWorkspace && Array.isArray(storedWorkspace.folders)) {
|
||||
storedWorkspace.folders = storedWorkspace.folders.filter(folder => isStoredWorkspaceFolder(folder));
|
||||
}
|
||||
|
||||
// Validate
|
||||
if (!Array.isArray(storedWorkspace.folders)) {
|
||||
} else {
|
||||
throw new Error(`${path.toString()} looks like an invalid workspace file.`);
|
||||
}
|
||||
|
||||
return storedWorkspace;
|
||||
}
|
||||
|
||||
private isInsideWorkspacesHome(path: URI): boolean {
|
||||
return isEqualOrParent(path, this.environmentService.untitledWorkspacesHome);
|
||||
}
|
||||
|
||||
async createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise<IWorkspaceIdentifier> {
|
||||
const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders, remoteAuthority);
|
||||
const configPath = workspace.configPath.fsPath;
|
||||
@@ -196,7 +189,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain
|
||||
}
|
||||
|
||||
isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean {
|
||||
return this.isInsideWorkspacesHome(workspace.configPath);
|
||||
return isUntitledWorkspace(workspace.configPath, this.environmentService);
|
||||
}
|
||||
|
||||
deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void {
|
||||
|
||||
Reference in New Issue
Block a user