Merge branch 'master' into master

This commit is contained in:
Sandeep Somavarapu 2019-11-27 22:16:40 +01:00 committed by GitHub
commit 121a3425a4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 689 additions and 252 deletions

View file

@ -81,6 +81,7 @@ const vscodeResources = [
'out-build/vs/code/electron-browser/sharedProcess/sharedProcess.js',
'out-build/vs/code/electron-browser/issue/issueReporter.js',
'out-build/vs/code/electron-browser/processExplorer/processExplorer.js',
'out-build/vs/platform/auth/common/auth.css',
'!**/test/**'
];

View file

@ -28,6 +28,10 @@
},
"description": "Remote environment variables."
},
"remoteUser": {
"type": "string",
"description": "The user VS Code Server will be started with. The default is the same user as the container."
},
"extensions": {
"type": "array",
"description": "An array of extensions that should be installed into the container.",

View file

@ -32,6 +32,10 @@
},
"description": "Remote environment variables."
},
"remoteUser": {
"type": "string",
"description": "The user VS Code Server will be started with. The default is the same user as the container."
},
"postCreateCommand": {
"type": [
"string",
@ -72,6 +76,14 @@
},
"description": "Container environment variables."
},
"containerUser": {
"type": "string",
"description": "The user the container will be started with. The default is the user on the Docker image."
},
"updateRemoteUserUID": {
"type": "boolean",
"description": "Controls whether on Linux the container's user should be updated with the local user's UID and GID. On by default."
},
"runArgs": {
"type": "array",
"description": "The arguments required when starting in the container.",

View file

@ -22,8 +22,6 @@ export interface MarkdownRenderOptions extends FormattedTextRenderOptions {
codeBlockRenderCallback?: () => void;
}
const codiconsRegex = /^icon:\/\/vscode\.codicons\/(.*)$/;
/**
* Create html nodes for the given content element.
*/
@ -75,11 +73,8 @@ export function renderMarkdown(markdown: IMarkdownString, options: MarkdownRende
const renderer = new marked.Renderer();
renderer.image = (href: string, title: string, text: string) => {
if (href) {
const match = codiconsRegex.exec(href);
if (match !== null) {
return renderCodicons(`$(${match[1]})`);
}
if (href && href.indexOf('vscode-icon://codicon/') === 0) {
return renderCodicons(`$(${URI.parse(href).path.substr(1)})`);
}
let dimensions: string[] = [];

View file

@ -42,7 +42,7 @@ function toSplitViewView(view: IView, getHeight: () => number): ISplitViewView {
get maximumSize() { return view.maximumWidth; },
get minimumSize() { return view.minimumWidth; },
onDidChange: Event.map(view.onDidChange, e => e && e.width),
layout: size => view.layout(size, getHeight(), Orientation.HORIZONTAL)
layout: (size, offset) => view.layout(size, getHeight(), 0, offset)
};
}
@ -81,7 +81,7 @@ export class CenteredViewLayout implements IDisposable {
this.resizeMargins();
}
} else {
this.view.layout(width, height, Orientation.HORIZONTAL);
this.view.layout(width, height, 0, 0);
}
this.didLayout = true;
}

View file

@ -30,7 +30,7 @@ export interface IView {
readonly onDidChange: Event<IViewSize | undefined>;
readonly priority?: LayoutPriority;
readonly snap?: boolean;
layout(width: number, height: number, orientation: Orientation): void;
layout(width: number, height: number, top: number, left: number): void;
setVisible?(visible: boolean): void;
}
@ -69,10 +69,10 @@ export function orthogonal(orientation: Orientation): Orientation {
}
export interface Box {
top: number;
left: number;
width: number;
height: number;
readonly top: number;
readonly left: number;
readonly width: number;
readonly height: number;
}
export interface GridLeafNode {
@ -117,11 +117,19 @@ export interface IGridViewOptions {
readonly layoutController?: ILayoutController;
}
class BranchNode implements ISplitView, IDisposable {
interface ILayoutContext {
readonly orthogonalSize: number;
readonly absoluteOffset: number;
readonly absoluteOrthogonalOffset: number;
readonly absoluteSize: number;
readonly absoluteOrthogonalSize: number;
}
class BranchNode implements ISplitView<ILayoutContext>, IDisposable {
readonly element: HTMLElement;
readonly children: Node[] = [];
private splitview: SplitView;
private splitview: SplitView<ILayoutContext>;
private _size: number;
get size(): number { return this._size; }
@ -129,6 +137,9 @@ class BranchNode implements ISplitView, IDisposable {
private _orthogonalSize: number;
get orthogonalSize(): number { return this._orthogonalSize; }
private absoluteOffset: number = 0;
private absoluteOrthogonalOffset: number = 0;
private _styles: IGridViewStyles;
get styles(): IGridViewStyles { return this._styles; }
@ -140,6 +151,14 @@ class BranchNode implements ISplitView, IDisposable {
return this.orientation === Orientation.HORIZONTAL ? this.orthogonalSize : this.size;
}
get top(): number {
return this.orientation === Orientation.HORIZONTAL ? this.absoluteOffset : this.absoluteOrthogonalOffset;
}
get left(): number {
return this.orientation === Orientation.HORIZONTAL ? this.absoluteOrthogonalOffset : this.absoluteOffset;
}
get minimumSize(): number {
return this.children.length === 0 ? 0 : Math.max(...this.children.map(c => c.minimumOrthogonalSize));
}
@ -221,7 +240,7 @@ class BranchNode implements ISplitView, IDisposable {
if (!childDescriptors) {
// Normal behavior, we have no children yet, just set up the splitview
this.splitview = new SplitView(this.element, { orientation, styles, proportionalLayout });
this.splitview.layout(size, orthogonalSize);
this.splitview.layout(size, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize });
} else {
// Reconstruction behavior, we want to reconstruct a splitview
const descriptor = {
@ -268,20 +287,32 @@ class BranchNode implements ISplitView, IDisposable {
}
}
layout(size: number, orthogonalSize: number | undefined): void {
layout(size: number, offset: number, ctx: ILayoutContext | undefined): void {
if (!this.layoutController.isLayoutEnabled) {
return;
}
if (typeof orthogonalSize !== 'number') {
if (typeof ctx === 'undefined') {
throw new Error('Invalid state');
}
// branch nodes should flip the normal/orthogonal directions
this._size = orthogonalSize;
this._size = ctx.orthogonalSize;
this._orthogonalSize = size;
this.absoluteOffset = ctx.absoluteOffset + offset;
this.absoluteOrthogonalOffset = ctx.absoluteOrthogonalOffset;
this.splitview.layout(orthogonalSize, size);
this.splitview.layout(ctx.orthogonalSize, {
orthogonalSize: size,
absoluteOffset: this.absoluteOrthogonalOffset,
absoluteOrthogonalOffset: this.absoluteOffset,
absoluteSize: ctx.absoluteOrthogonalSize,
absoluteOrthogonalSize: ctx.absoluteSize
});
// Disable snapping on views which sit on the edges of the grid
this.splitview.startSnappingEnabled = this.absoluteOrthogonalOffset > 0;
this.splitview.endSnappingEnabled = this.absoluteOrthogonalOffset + ctx.orthogonalSize < ctx.absoluteOrthogonalSize;
}
setVisible(visible: boolean): void {
@ -511,7 +542,7 @@ class BranchNode implements ISplitView, IDisposable {
}
}
class LeafNode implements ISplitView, IDisposable {
class LeafNode implements ISplitView<ILayoutContext>, IDisposable {
private _size: number = 0;
get size(): number { return this._size; }
@ -519,6 +550,9 @@ class LeafNode implements ISplitView, IDisposable {
private _orthogonalSize: number;
get orthogonalSize(): number { return this._orthogonalSize; }
private absoluteOffset: number = 0;
private absoluteOrthogonalOffset: number = 0;
readonly onDidSashReset: Event<number[]> = Event.None;
private _onDidLinkedWidthNodeChange = new Relay<number | undefined>();
@ -565,6 +599,14 @@ class LeafNode implements ISplitView, IDisposable {
return this.orientation === Orientation.HORIZONTAL ? this.size : this.orthogonalSize;
}
get top(): number {
return this.orientation === Orientation.HORIZONTAL ? this.absoluteOffset : this.absoluteOrthogonalOffset;
}
get left(): number {
return this.orientation === Orientation.HORIZONTAL ? this.absoluteOrthogonalOffset : this.absoluteOffset;
}
get element(): HTMLElement {
return this.view.element;
}
@ -617,18 +659,20 @@ class LeafNode implements ISplitView, IDisposable {
// noop
}
layout(size: number, orthogonalSize: number | undefined): void {
layout(size: number, offset: number, ctx: ILayoutContext | undefined): void {
if (!this.layoutController.isLayoutEnabled) {
return;
}
if (typeof orthogonalSize !== 'number') {
if (typeof ctx === 'undefined') {
throw new Error('Invalid state');
}
this._size = size;
this._orthogonalSize = orthogonalSize;
this.view.layout(this.width, this.height, orthogonal(this.orientation));
this._orthogonalSize = ctx.orthogonalSize;
this.absoluteOffset = ctx.absoluteOffset + offset;
this.absoluteOrthogonalOffset = ctx.absoluteOrthogonalOffset;
this.view.layout(this.width, this.height, this.top, this.left);
}
setVisible(visible: boolean): void {
@ -715,7 +759,7 @@ export class GridView implements IDisposable {
const { size, orthogonalSize } = this._root;
this.root = flipNode(this._root, orthogonalSize, size);
this.root.layout(size, orthogonalSize);
this.root.layout(size, 0, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize });
}
get width(): number { return this.root.width; }
@ -771,7 +815,7 @@ export class GridView implements IDisposable {
this.firstLayoutController.isLayoutEnabled = true;
const [size, orthogonalSize] = this.root.orientation === Orientation.HORIZONTAL ? [height, width] : [width, height];
this.root.layout(size, orthogonalSize);
this.root.layout(size, 0, { orthogonalSize, absoluteOffset: 0, absoluteOrthogonalOffset: 0, absoluteSize: size, absoluteOrthogonalSize: orthogonalSize });
}
addView(view: IView, size: number | Sizing, location: number[]): void {
@ -1032,7 +1076,7 @@ export class GridView implements IDisposable {
getView(location?: number[]): GridNode;
getView(location?: number[]): GridNode {
const node = location ? this.getNode(location)[1] : this._root;
return this._getViews(node, this.orientation, { top: 0, left: 0, width: this.width, height: this.height });
return this._getViews(node, this.orientation);
}
static deserialize<T extends ISerializableView>(json: ISerializedGridView, deserializer: IViewDeserializer<T>, options: IGridViewOptions = {}): GridView {
@ -1076,24 +1120,20 @@ export class GridView implements IDisposable {
return result;
}
private _getViews(node: Node, orientation: Orientation, box: Box, cachedVisibleSize?: number): GridNode {
private _getViews(node: Node, orientation: Orientation, cachedVisibleSize?: number): GridNode {
const box = { top: node.top, left: node.left, width: node.width, height: node.height };
if (node instanceof LeafNode) {
return { view: node.view, box, cachedVisibleSize };
}
const children: GridNode[] = [];
let i = 0;
let offset = 0;
for (const child of node.children) {
const childOrientation = orthogonal(orientation);
const childBox: Box = orientation === Orientation.HORIZONTAL
? { top: box.top, left: box.left + offset, width: child.width, height: box.height }
: { top: box.top + offset, left: box.left, width: box.width, height: child.height };
const cachedVisibleSize = node.getChildCachedVisibleSize(i++);
for (let i = 0; i < node.children.length; i++) {
const child = node.children[i];
const cachedVisibleSize = node.getChildCachedVisibleSize(i);
children.push(this._getViews(child, childOrientation, childBox, cachedVisibleSize));
offset += orientation === Orientation.HORIZONTAL ? child.width : child.height;
children.push(this._getViews(child, orthogonal(orientation), cachedVisibleSize));
}
return { children, box };

View file

@ -23,14 +23,14 @@ const defaultStyles: ISplitViewStyles = {
separatorBorder: Color.transparent
};
export interface ISplitViewOptions {
export interface ISplitViewOptions<TLayoutContext = undefined> {
readonly orientation?: Orientation; // default Orientation.VERTICAL
readonly styles?: ISplitViewStyles;
readonly orthogonalStartSash?: Sash;
readonly orthogonalEndSash?: Sash;
readonly inverseAltBehavior?: boolean;
readonly proportionalLayout?: boolean; // default true,
readonly descriptor?: ISplitViewDescriptor;
readonly descriptor?: ISplitViewDescriptor<TLayoutContext>;
}
/**
@ -42,14 +42,14 @@ export const enum LayoutPriority {
High
}
export interface IView {
export interface IView<TLayoutContext = undefined> {
readonly element: HTMLElement;
readonly minimumSize: number;
readonly maximumSize: number;
readonly onDidChange: Event<number | undefined>;
readonly priority?: LayoutPriority;
readonly snap?: boolean;
layout(size: number, orthogonalSize: number | undefined): void;
layout(size: number, offset: number, context: TLayoutContext | undefined): void;
setVisible?(visible: boolean): void;
}
@ -62,7 +62,7 @@ interface ISashEvent {
type ViewItemSize = number | { cachedVisibleSize: number };
abstract class ViewItem {
abstract class ViewItem<TLayoutContext> {
private _size: number;
set size(size: number) {
@ -115,7 +115,7 @@ abstract class ViewItem {
constructor(
protected container: HTMLElement,
private view: IView,
private view: IView<TLayoutContext>,
size: ViewItemSize,
private disposable: IDisposable
) {
@ -129,31 +129,31 @@ abstract class ViewItem {
}
}
layout(position: number, orthogonalSize: number | undefined): void {
this.layoutContainer(position);
this.view.layout(this.size, orthogonalSize);
layout(offset: number, layoutContext: TLayoutContext | undefined): void {
this.layoutContainer(offset);
this.view.layout(this.size, offset, layoutContext);
}
abstract layoutContainer(position: number): void;
abstract layoutContainer(offset: number): void;
dispose(): IView {
dispose(): IView<TLayoutContext> {
this.disposable.dispose();
return this.view;
}
}
class VerticalViewItem extends ViewItem {
class VerticalViewItem<TLayoutContext> extends ViewItem<TLayoutContext> {
layoutContainer(position: number): void {
this.container.style.top = `${position}px`;
layoutContainer(offset: number): void {
this.container.style.top = `${offset}px`;
this.container.style.height = `${this.size}px`;
}
}
class HorizontalViewItem extends ViewItem {
class HorizontalViewItem<TLayoutContext> extends ViewItem<TLayoutContext> {
layoutContainer(position: number): void {
this.container.style.left = `${position}px`;
layoutContainer(offset: number): void {
this.container.style.left = `${offset}px`;
this.container.style.width = `${this.size}px`;
}
}
@ -198,26 +198,26 @@ export namespace Sizing {
export function Invisible(cachedVisibleSize: number): InvisibleSizing { return { type: 'invisible', cachedVisibleSize }; }
}
export interface ISplitViewDescriptor {
export interface ISplitViewDescriptor<TLayoutContext> {
size: number;
views: {
visible?: boolean;
size: number;
view: IView;
view: IView<TLayoutContext>;
}[];
}
export class SplitView extends Disposable {
export class SplitView<TLayoutContext = undefined> extends Disposable {
readonly orientation: Orientation;
readonly el: HTMLElement;
private sashContainer: HTMLElement;
private viewContainer: HTMLElement;
private size = 0;
private orthogonalSize: number | undefined;
private layoutContext: TLayoutContext | undefined;
private contentSize = 0;
private proportions: undefined | number[] = undefined;
private viewItems: ViewItem[] = [];
private viewItems: ViewItem<TLayoutContext>[] = [];
private sashItems: ISashItem[] = [];
private sashDragState: ISashDragState | undefined;
private state: State = State.Idle;
@ -266,7 +266,29 @@ export class SplitView extends Disposable {
return this.sashItems.map(s => s.sash);
}
constructor(container: HTMLElement, options: ISplitViewOptions = {}) {
private _startSnappingEnabled = true;
get startSnappingEnabled(): boolean { return this._startSnappingEnabled; }
set startSnappingEnabled(startSnappingEnabled: boolean) {
if (this._startSnappingEnabled === startSnappingEnabled) {
return;
}
this._startSnappingEnabled = startSnappingEnabled;
this.updateSashEnablement();
}
private _endSnappingEnabled = true;
get endSnappingEnabled(): boolean { return this._endSnappingEnabled; }
set endSnappingEnabled(endSnappingEnabled: boolean) {
if (this._endSnappingEnabled === endSnappingEnabled) {
return;
}
this._endSnappingEnabled = endSnappingEnabled;
this.updateSashEnablement();
}
constructor(container: HTMLElement, options: ISplitViewOptions<TLayoutContext> = {}) {
super();
this.orientation = types.isUndefined(options.orientation) ? Orientation.VERTICAL : options.orientation;
@ -309,11 +331,11 @@ export class SplitView extends Disposable {
}
}
addView(view: IView, size: number | Sizing, index = this.viewItems.length): void {
addView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length): void {
this.doAddView(view, size, index, false);
}
removeView(index: number, sizing?: Sizing): IView {
removeView(index: number, sizing?: Sizing): IView<TLayoutContext> {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
@ -405,10 +427,10 @@ export class SplitView extends Disposable {
return viewItem.cachedVisibleSize;
}
layout(size: number, orthogonalSize?: number): void {
layout(size: number, layoutContext?: TLayoutContext): void {
const previousSize = Math.max(this.size, this.contentSize);
this.size = size;
this.orthogonalSize = orthogonalSize;
this.layoutContext = layoutContext;
if (!this.proportions) {
const indexes = range(this.viewItems.length);
@ -549,7 +571,7 @@ export class SplitView extends Disposable {
}
}
private onViewChange(item: ViewItem, size: number | undefined): void {
private onViewChange(item: ViewItem<TLayoutContext>, size: number | undefined): void {
const index = this.viewItems.indexOf(item);
if (index < 0 || index >= this.viewItems.length) {
@ -596,7 +618,7 @@ export class SplitView extends Disposable {
}
distributeViewSizes(): void {
const flexibleViewItems: ViewItem[] = [];
const flexibleViewItems: ViewItem<TLayoutContext>[] = [];
let flexibleSize = 0;
for (const item of this.viewItems) {
@ -627,7 +649,7 @@ export class SplitView extends Disposable {
return this.viewItems[index].size;
}
private doAddView(view: IView, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {
private doAddView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {
if (this.state !== State.Idle) {
throw new Error('Cant modify splitview');
}
@ -861,17 +883,19 @@ export class SplitView extends Disposable {
this.contentSize = this.viewItems.reduce((r, i) => r + i.size, 0);
// Layout views
let position = 0;
let offset = 0;
for (const viewItem of this.viewItems) {
viewItem.layout(position, this.orthogonalSize);
position += viewItem.size;
viewItem.layout(offset, this.layoutContext);
offset += viewItem.size;
}
// Layout sashes
this.sashItems.forEach(item => item.sash.layout());
this.updateSashEnablement();
}
// Update sashes enablement
private updateSashEnablement(): void {
let previous = false;
const collapsesDown = this.viewItems.map(i => previous = (i.size - i.minimumSize > 0) || previous);
@ -885,7 +909,12 @@ export class SplitView extends Disposable {
previous = false;
const expandsUp = reverseViews.map(i => previous = (i.maximumSize - i.size > 0) || previous).reverse();
this.sashItems.forEach(({ sash }, index) => {
let position = 0;
for (let index = 0; index < this.sashItems.length; index++) {
const { sash } = this.sashItems[index];
const viewItem = this.viewItems[index];
position += viewItem.size;
const min = !(collapsesDown[index] && expandsUp[index + 1]);
const max = !(expandsDown[index] && collapsesUp[index + 1]);
@ -898,9 +927,9 @@ export class SplitView extends Disposable {
const snappedBefore = typeof snapBeforeIndex === 'number' && !this.viewItems[snapBeforeIndex].visible;
const snappedAfter = typeof snapAfterIndex === 'number' && !this.viewItems[snapAfterIndex].visible;
if (snappedBefore && collapsesUp[index]) {
if (snappedBefore && collapsesUp[index] && (position > 0 || this.startSnappingEnabled)) {
sash.state = SashState.Minimum;
} else if (snappedAfter && collapsesDown[index]) {
} else if (snappedAfter && collapsesDown[index] && (position < this.contentSize || this.endSnappingEnabled)) {
sash.state = SashState.Maximum;
} else {
sash.state = SashState.Disabled;
@ -912,8 +941,7 @@ export class SplitView extends Disposable {
} else {
sash.state = SashState.Enabled;
}
// }
});
}
}
private getSashPosition(sash: Sash): number {

File diff suppressed because one or more lines are too long

View file

@ -8,7 +8,7 @@ import { Emitter } from 'vs/base/common/event';
import { SplitView, IView, Sizing, LayoutPriority } from 'vs/base/browser/ui/splitview/splitview';
import { Sash, SashState } from 'vs/base/browser/ui/sash/sash';
class TestView implements IView {
class TestView implements IView<number> {
private readonly _onDidChange = new Emitter<number | undefined>();
readonly onDidChange = this._onDidChange.event;
@ -43,7 +43,7 @@ class TestView implements IView {
assert(_minimumSize <= _maximumSize, 'splitview view minimum size must be <= maximum size');
}
layout(size: number, orthogonalSize: number | undefined): void {
layout(size: number, _offset: number, orthogonalSize: number | undefined): void {
this._size = size;
this._orthogonalSize = orthogonalSize;
this._onDidLayout.fire({ size, orthogonalSize });
@ -527,11 +527,11 @@ suite('Splitview', () => {
view1.dispose();
});
test('orthogonal size propagates to views', () => {
test('context propagates to views', () => {
const view1 = new TestView(20, Number.POSITIVE_INFINITY);
const view2 = new TestView(20, Number.POSITIVE_INFINITY);
const view3 = new TestView(20, Number.POSITIVE_INFINITY, LayoutPriority.Low);
const splitview = new SplitView(container, { proportionalLayout: false });
const splitview = new SplitView<number>(container, { proportionalLayout: false });
splitview.layout(200);
splitview.addView(view1, Sizing.Distribute);

View file

@ -492,4 +492,8 @@ suite('Strings', () => {
assertEncodeDecodeUTF8('🧝', [240, 159, 167, 157]);
});
test('getGraphemeBreakType', () => {
assert.equal(strings.getGraphemeBreakType(0xBC1), strings.GraphemeBreakType.SpacingMark);
});
});

View file

@ -523,12 +523,15 @@ export class CursorColumns {
if (codePoint === CharCode.Tab) {
result = CursorColumns.nextRenderTabStop(result, tabSize);
} else {
let graphemeBreakType = strings.getGraphemeBreakType(codePoint);
while (i < endOffset) {
const nextCodePoint = strings.getNextCodePoint(lineContent, endOffset, i);
if (!strings.isUnicodeMark(nextCodePoint)) {
const nextGraphemeBreakType = strings.getGraphemeBreakType(nextCodePoint);
if (strings.breakBetweenGraphemeBreakType(graphemeBreakType, nextGraphemeBreakType)) {
break;
}
i += (nextCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1);
graphemeBreakType = nextGraphemeBreakType;
}
if (strings.isFullWidthCharacter(codePoint) || strings.isEmojiImprecise(codePoint)) {
result = result + 2;
@ -582,12 +585,15 @@ export class CursorColumns {
if (codePoint === CharCode.Tab) {
afterVisibleColumn = CursorColumns.nextRenderTabStop(beforeVisibleColumn, tabSize);
} else {
let graphemeBreakType = strings.getGraphemeBreakType(codePoint);
while (i < lineLength) {
const nextCodePoint = strings.getNextCodePoint(lineContent, lineLength, i);
if (!strings.isUnicodeMark(nextCodePoint)) {
const nextGraphemeBreakType = strings.getGraphemeBreakType(nextCodePoint);
if (strings.breakBetweenGraphemeBreakType(graphemeBreakType, nextGraphemeBreakType)) {
break;
}
i += (nextCodePoint >= Constants.UNICODE_SUPPLEMENTARY_PLANE_BEGIN ? 2 : 1);
graphemeBreakType = nextGraphemeBreakType;
}
if (strings.isFullWidthCharacter(codePoint) || strings.isEmojiImprecise(codePoint)) {
afterVisibleColumn = beforeVisibleColumn + 2;

View file

@ -289,17 +289,26 @@ export class WordOperations {
column = model.getLineMaxColumn(lineNumber);
}
} else if (wordNavigationType === WordNavigationType.WordAccessibility) {
if (movedDown) {
// If we move to the next line, pretend that the cursor is right before the first character.
// This is needed when the first word starts right at the first character - and in order not to miss it,
// we need to start before.
column = 0;
}
while (
nextWordOnLine
&& nextWordOnLine.wordType === WordType.Separator
&& (nextWordOnLine.wordType === WordType.Separator
|| nextWordOnLine.start + 1 <= column
)
) {
// Skip over a word made up of one single separator
// Also skip over word if it begins before current cursor position to ascertain we're moving forward at least 1 character.
nextWordOnLine = WordOperations._findNextWordOnLine(wordSeparators, model, new Position(lineNumber, nextWordOnLine.end + 1));
}
if (nextWordOnLine) {
column = nextWordOnLine.end + 1;
column = nextWordOnLine.start + 1;
} else {
column = model.getLineMaxColumn(lineNumber);
}

View file

@ -351,7 +351,7 @@ suite('WordOperations', () => {
});
test('cursorWordAccessibilityRight', () => {
const EXPECTED = [' /* Just| some| more| text| a|+= 3| +5|-3| + 7| */ |'].join('\n');
const EXPECTED = [' /* |Just |some |more |text |a+= |3 +|5-|3 + |7 */ |'].join('\n');
const [text,] = deserializePipePositions(EXPECTED);
const actualStops = testRepeatedActionAndExtractPositions(
text,

View file

@ -726,7 +726,7 @@ suite('Editor Controller - Cursor', () => {
});
});
test('combining marks', () => {
test('grapheme breaking', () => {
withTestCodeEditor([
'abcabc',
'ãããããã',

View file

@ -755,7 +755,7 @@ suite('Editor Model - TextModel', () => {
assert.deepEqual(actual, expected, `validateRange for ${input}, got ${actual}, expected ${expected}`);
}
test('combining marks', () => {
test('grapheme breaking', () => {
const m = TextModel.createFromString([
'abcabc',
'ãããããã',

View file

@ -5,7 +5,7 @@
import { Emitter, Event } from 'vs/base/common/event';
import { DisposableStore } from 'vs/base/common/lifecycle';
import { IMenu, IMenuActionOptions, IMenuItem, IMenuService, isIMenuItem, ISubmenuItem, MenuId, MenuItemAction, MenuRegistry, SubmenuItemAction } from 'vs/platform/actions/common/actions';
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';
@ -133,7 +133,7 @@ class Menu 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;
@ -171,8 +171,15 @@ class Menu 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);
}
}

View file

@ -74,6 +74,8 @@ function sendFile(res: http.ServerResponse, filepath: string, contentType: strin
fs.readFile(filepath, (err, body) => {
if (err) {
console.error(err);
res.writeHead(404);
res.end();
} else {
res.writeHead(200, {
'Content-Length': body.length,

View file

@ -171,7 +171,7 @@ export class UserDataAutoSync extends Disposable {
this.logService.error(e);
}
if (loop) {
await timeout(1000 * 5); // Loop sync for every 5s.
await timeout(1000 * 30); // Loop sync for every 30s.
this.sync(loop);
}
}

View file

@ -130,7 +130,7 @@ declare module 'vscode' {
//#endregion
// #region Joh - code insets
// #region Joh - code insets, https://github.com/microsoft/vscode/issues/85682
export interface WebviewEditorInset {
readonly editor: TextEditor;
@ -147,7 +147,7 @@ declare module 'vscode' {
//#endregion
//#region Joh - read/write in chunks
//#region Joh - read/write in chunks, https://github.com/microsoft/vscode/issues/84515
export interface FileSystemProvider {
open?(resource: Uri, options: { create: boolean }): number | Thenable<number>;
@ -528,7 +528,7 @@ declare module 'vscode' {
//#endregion
//#region Joao: diff command
//#region Joao: diff command, https://github.com/microsoft/vscode/issues/84899
/**
* The contiguous set of modified lines in a diff.
@ -561,7 +561,7 @@ declare module 'vscode' {
//#endregion
//#region Joh: decorations
//#region Joh: decorations, https://github.com/microsoft/vscode/issues/54938
export class Decoration {
letter?: string;
@ -1206,7 +1206,7 @@ declare module 'vscode' {
//#endregion
//#region Custom editors, mjbvz
//#region Custom editors, mjbvz, https://github.com/microsoft/vscode/issues/77131
/**
* Defines how a webview editor interacts with VS Code.

View file

@ -40,7 +40,7 @@ import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list
import { ITreeRenderer, ITreeNode, IAsyncDataSource, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree';
import { FuzzyScore, createMatches } from 'vs/base/common/filters';
import { CollapseAllAction } from 'vs/base/browser/ui/tree/treeDefaults';
import { isFalsyOrWhitespace } from 'vs/base/common/strings';
import { escape, isFalsyOrWhitespace } from 'vs/base/common/strings';
import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme';
export class CustomTreeViewPane extends ViewletPane {
@ -796,9 +796,26 @@ class TreeRenderer extends Disposable implements ITreeRenderer<ITreeItem, FuzzyS
templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches: matches ? matches : createMatches(element.filterData) });
}
templateData.icon.style.backgroundImage = iconUrl ? DOM.asCSSUrl(iconUrl) : '';
templateData.icon.title = title ? title : '';
DOM.toggleClass(templateData.icon, 'custom-view-tree-node-item-icon', !!iconUrl);
let codicon: string | undefined;
if (iconUrl?.scheme === 'vscode-icon' && iconUrl?.authority === 'codicon') {
codicon = `codicon-${escape(iconUrl.path.substr(1))}`;
}
DOM.toggleClass(templateData.icon, 'codicon', !!codicon);
templateData.icon.classList.forEach((cls, i, list) => {
if (cls !== codicon && cls.indexOf('codicon-') === 0) {
list.remove(cls);
}
});
if (codicon) {
DOM.addClass(templateData.icon, codicon);
}
templateData.icon.style.backgroundImage = iconUrl && !codicon ? DOM.asCSSUrl(iconUrl) : '';
templateData.actionBar.context = <TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle };
templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false });
if (this._actionRunner) {

View file

@ -112,6 +112,10 @@
-moz-osx-font-smoothing: grayscale;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item > .custom-view-tree-node-item-icon.codicon {
margin-top: 3px;
}
.customview-tree .monaco-list .monaco-list-row .custom-view-tree-node-item .custom-view-tree-node-item-resourceLabel .monaco-icon-label-description-container {
flex: 1;
}

View file

@ -107,25 +107,29 @@ async function createCandidateDecorations(model: ITextModel, breakpointDecoratio
const session = debugService.getViewModel().focusedSession;
if (session && session.capabilities.supportsBreakpointLocationsRequest) {
await Promise.all(lineNumbers.map(async lineNumber => {
const positions = await session.breakpointsLocations(model.uri, lineNumber);
if (positions.length > 1) {
// Do not render candidates if there is only one, since it is already covered by the line breakpoint
positions.forEach(p => {
const range = new Range(p.lineNumber, p.column, p.lineNumber, p.column + 1);
const breakpointAtPosition = breakpointDecorations.filter(bpd => bpd.range.equalsRange(range)).pop();
if (breakpointAtPosition && breakpointAtPosition.inlineWidget) {
// Space already occupied, do not render candidate.
return;
}
result.push({
range,
options: {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
beforeContentClassName: `debug-breakpoint-placeholder`
},
breakpoint: breakpointAtPosition ? breakpointAtPosition.breakpoint : undefined
try {
const positions = await session.breakpointsLocations(model.uri, lineNumber);
if (positions.length > 1) {
// Do not render candidates if there is only one, since it is already covered by the line breakpoint
positions.forEach(p => {
const range = new Range(p.lineNumber, p.column, p.lineNumber, p.column + 1);
const breakpointAtPosition = breakpointDecorations.filter(bpd => bpd.range.equalsRange(range)).pop();
if (breakpointAtPosition && breakpointAtPosition.inlineWidget) {
// Space already occupied, do not render candidate.
return;
}
result.push({
range,
options: {
stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges,
beforeContentClassName: `debug-breakpoint-placeholder`
},
breakpoint: breakpointAtPosition ? breakpointAtPosition.breakpoint : undefined
});
});
});
}
} catch (e) {
// If there is an error when fetching breakpoint locations just do not render them
}
}));
}
@ -133,7 +137,6 @@ async function createCandidateDecorations(model: ITextModel, breakpointDecoratio
return result;
}
class BreakpointEditorContribution implements IBreakpointEditorContribution {
private breakpointHintDecoration: string[] = [];

View file

@ -20,7 +20,7 @@ import { CallStackView } from 'vs/workbench/contrib/debug/browser/callStackView'
import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import {
IDebugService, VIEWLET_ID, REPL_ID, CONTEXT_IN_DEBUG_MODE, INTERNAL_CONSOLE_OPTIONS_SCHEMA,
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, VIEW_CONTAINER, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED,
CONTEXT_DEBUG_STATE, VARIABLES_VIEW_ID, CALLSTACK_VIEW_ID, WATCH_VIEW_ID, BREAKPOINTS_VIEW_ID, VIEW_CONTAINER, LOADED_SCRIPTS_VIEW_ID, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_DEBUG_UX,
} from 'vs/workbench/contrib/debug/common/debug';
import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
@ -48,6 +48,7 @@ import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView'
import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl';
import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider';
import { DebugCallStackContribution } from 'vs/workbench/contrib/debug/browser/debugCallStackContribution';
import { StartView } from 'vs/workbench/contrib/debug/browser/startView';
class OpenDebugViewletAction extends ShowViewletAction {
public static readonly ID = VIEWLET_ID;
@ -82,7 +83,7 @@ class OpenDebugPanelAction extends TogglePanelAction {
Registry.as<ViewletRegistry>(ViewletExtensions.Viewlets).registerViewlet(ViewletDescriptor.create(
DebugViewlet,
VIEWLET_ID,
nls.localize('debug', "Debug"),
nls.localize('debugAndRun', "Debug And Run"),
'codicon-debug-alt',
3
));
@ -106,11 +107,12 @@ Registry.as<PanelRegistry>(PanelExtensions.Panels).registerPanel(PanelDescriptor
// Register default debug views
const viewsRegistry = Registry.as<IViewsRegistry>(ViewExtensions.ViewsRegistry);
viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctorDescriptor: { ctor: VariablesView }, order: 10, weight: 40, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' } }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctorDescriptor: { ctor: WatchExpressionsView }, order: 20, weight: 10, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' } }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctorDescriptor: { ctor: CallStackView }, order: 30, weight: 30, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' } }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctorDescriptor: { ctor: BreakpointsView }, order: 40, weight: 20, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' } }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctorDescriptor: { ctor: LoadedScriptsView }, order: 35, weight: 5, canToggleVisibility: true, collapsed: true, when: CONTEXT_LOADED_SCRIPTS_SUPPORTED }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: VARIABLES_VIEW_ID, name: nls.localize('variables', "Variables"), ctorDescriptor: { ctor: VariablesView }, order: 10, weight: 40, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusVariablesView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: WATCH_VIEW_ID, name: nls.localize('watch', "Watch"), ctorDescriptor: { ctor: WatchExpressionsView }, order: 20, weight: 10, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusWatchView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: CALLSTACK_VIEW_ID, name: nls.localize('callStack', "Call Stack"), ctorDescriptor: { ctor: CallStackView }, order: 30, weight: 30, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusCallStackView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: BREAKPOINTS_VIEW_ID, name: nls.localize('breakpoints', "Breakpoints"), ctorDescriptor: { ctor: BreakpointsView }, order: 40, weight: 20, canToggleVisibility: true, focusCommand: { id: 'workbench.debug.action.focusBreakpointsView' }, when: CONTEXT_DEBUG_UX.isEqualTo('default') }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: StartView.ID, name: StartView.LABEL, ctorDescriptor: { ctor: StartView }, order: 10, weight: 40, canToggleVisibility: true, when: CONTEXT_DEBUG_UX.isEqualTo('simple') }], VIEW_CONTAINER);
viewsRegistry.registerViews([{ id: LOADED_SCRIPTS_VIEW_ID, name: nls.localize('loadedScripts', "Loaded Scripts"), ctorDescriptor: { ctor: LoadedScriptsView }, order: 35, weight: 5, canToggleVisibility: true, collapsed: true, when: ContextKeyExpr.and(CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_DEBUG_UX.isEqualTo('default')) }], VIEW_CONTAINER);
registerCommands();
@ -365,7 +367,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarDebugMenu, {
group: '1_debug',
command: {
id: RunAction.ID,
title: nls.localize({ key: 'miStartWithoutDebugging', comment: ['&& denotes a mnemonic'] }, "Start &&Without Debugging")
title: nls.localize({ key: 'miRun', comment: ['&& denotes a mnemonic'] }, "R&&un")
},
order: 2
});

View file

@ -143,7 +143,7 @@ export class StartAction extends AbstractDebugAction {
export class RunAction extends StartAction {
static readonly ID = 'workbench.action.debug.run';
static LABEL = nls.localize('startWithoutDebugging', "Start Without Debugging");
static LABEL = nls.localize('startWithoutDebugging', "Run (Start Without Debugging)");
protected isNoDebug(): boolean {
return true;

View file

@ -239,7 +239,7 @@ export function registerCommands(): void {
id: STEP_INTO_ID,
weight: KeybindingWeight.WorkbenchContrib + 10, // Have a stronger weight to have priority over full screen when debugging
primary: KeyCode.F11,
when: CONTEXT_IN_DEBUG_MODE,
when: CONTEXT_DEBUG_STATE.isEqualTo('stopped'),
handler: (accessor: ServicesAccessor, context: CallStackContext | unknown) => {
getThreadAndRun(accessor, context, (thread: IThread) => thread.stepIn());
}

View file

@ -11,6 +11,7 @@ import * as objects from 'vs/base/common/objects';
import { URI as uri } from 'vs/base/common/uri';
import * as resources from 'vs/base/common/resources';
import { IJSONSchema } from 'vs/base/common/jsonSchema';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ITextModel } from 'vs/editor/common/model';
import { IEditor } from 'vs/workbench/common/editor';
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
@ -56,6 +57,7 @@ export class ConfigurationManager implements IConfigurationManager {
private adapterDescriptorFactories: IDebugAdapterDescriptorFactory[];
private debugAdapterFactories = new Map<string, IDebugAdapterFactory>();
private debugConfigurationTypeContext: IContextKey<string>;
private readonly _onDidRegisterDebugger = new Emitter<void>();
constructor(
@IWorkspaceContextService private readonly contextService: IWorkspaceContextService,
@ -166,6 +168,10 @@ export class ConfigurationManager implements IConfigurationManager {
return Promise.resolve(undefined);
}
get onDidRegisterDebugger(): Event<void> {
return this._onDidRegisterDebugger.event;
}
// debug configurations
registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable {
@ -266,6 +272,7 @@ export class ConfigurationManager implements IConfigurationManager {
});
this.setCompoundSchemaValues();
this._onDidRegisterDebugger.fire();
});
breakpointsExtPoint.setHandler((extensions, delta) => {
@ -388,6 +395,17 @@ export class ConfigurationManager implements IConfigurationManager {
return this.debuggers.filter(dbg => strings.equalsIgnoreCase(dbg.type, type)).pop();
}
getDebuggerLabelsForEditor(editor: editorCommon.IEditor | undefined): string[] {
if (isCodeEditor(editor)) {
const model = editor.getModel();
const language = model ? model.getLanguageIdentifier().language : undefined;
return this.debuggers.filter(a => language && a.languages && a.languages.indexOf(language) >= 0).map(d => d.label);
}
return [];
}
async guessDebugger(type?: string): Promise<Debugger | undefined> {
if (type) {
const adapter = this.getDebugger(type);

View file

@ -40,7 +40,7 @@ import { IAction } from 'vs/base/common/actions';
import { deepClone, equals } from 'vs/base/common/objects';
import { DebugSession } from 'vs/workbench/contrib/debug/browser/debugSession';
import { dispose, IDisposable } from 'vs/base/common/lifecycle';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel, IDebugSessionOptions } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, State, IDebugSession, CONTEXT_DEBUG_TYPE, CONTEXT_DEBUG_STATE, CONTEXT_IN_DEBUG_MODE, IThread, IDebugConfiguration, VIEWLET_ID, REPL_ID, IConfig, ILaunch, IViewModel, IConfigurationManager, IDebugModel, IEnablement, IBreakpoint, IBreakpointData, ICompound, IGlobalConfig, IStackFrame, AdapterEndEvent, getStateLabel, IDebugSessionOptions, CONTEXT_DEBUG_UX } from 'vs/workbench/contrib/debug/common/debug';
import { isExtensionHostDebugging } from 'vs/workbench/contrib/debug/common/debugUtils';
import { isErrorWithActions, createErrorWithActions } from 'vs/base/common/errorsWithActions';
import { RunOnceScheduler } from 'vs/base/common/async';
@ -86,6 +86,7 @@ export class DebugService implements IDebugService {
private debugType: IContextKey<string>;
private debugState: IContextKey<string>;
private inDebugMode: IContextKey<boolean>;
private debugUx: IContextKey<string>;
private breakpointsToSendOnResourceSaved: Set<string>;
private initializing = false;
private previousState: State | undefined;
@ -127,6 +128,8 @@ export class DebugService implements IDebugService {
this.debugType = CONTEXT_DEBUG_TYPE.bindTo(contextKeyService);
this.debugState = CONTEXT_DEBUG_STATE.bindTo(contextKeyService);
this.inDebugMode = CONTEXT_IN_DEBUG_MODE.bindTo(contextKeyService);
this.debugUx = CONTEXT_DEBUG_UX.bindTo(contextKeyService);
this.debugUx.set(!!this.configurationManager.selectedConfiguration.name ? 'default' : 'simple');
this.model = new DebugModel(this.loadBreakpoints(), this.loadFunctionBreakpoints(),
this.loadExceptionBreakpoints(), this.loadDataBreakpoints(), this.loadWatchExpressions(), this.textFileService);
@ -170,6 +173,9 @@ export class DebugService implements IDebugService {
this.toDispose.push(this.viewModel.onDidFocusSession(() => {
this.onStateChange();
}));
this.toDispose.push(this.configurationManager.onDidSelectConfiguration(() => {
this.debugUx.set(!!(this.state !== State.Inactive || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple');
}));
}
getModel(): IDebugModel {
@ -226,6 +232,7 @@ export class DebugService implements IDebugService {
if (this.previousState !== state) {
this.debugState.set(getStateLabel(state));
this.inDebugMode.set(state !== State.Inactive);
this.debugUx.set(!!(state !== State.Inactive || this.configurationManager.selectedConfiguration.name) ? 'default' : 'simple');
this.previousState = state;
this._onDidChangeState.fire(state);
}

View file

@ -9,7 +9,7 @@ import { IAction } from 'vs/base/common/actions';
import * as DOM from 'vs/base/browser/dom';
import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar';
import { ViewContainerViewlet } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration, REPL_ID } from 'vs/workbench/contrib/debug/common/debug';
import { IDebugService, VIEWLET_ID, State, BREAKPOINTS_VIEW_ID, IDebugConfiguration, REPL_ID, CONTEXT_DEBUG_UX, CONTEXT_DEBUG_UX_KEY } from 'vs/workbench/contrib/debug/common/debug';
import { StartAction, ConfigureAction, SelectAndStartAction, FocusSessionAction } from 'vs/workbench/contrib/debug/browser/debugActions';
import { StartDebugActionViewItem, FocusSessionActionViewItem } from 'vs/workbench/contrib/debug/browser/debugActionViewItems';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
@ -33,6 +33,7 @@ import { MenuEntryActionViewItem } from 'vs/platform/actions/browser/menuEntryAc
import { INotificationService } from 'vs/platform/notification/common/notification';
import { TogglePanelAction } from 'vs/workbench/browser/panel';
import { IPanelService } from 'vs/workbench/services/panel/common/panelService';
import { StartView } from 'vs/workbench/contrib/debug/browser/startView';
export class DebugViewlet extends ViewContainerViewlet {
@ -61,10 +62,16 @@ export class DebugViewlet extends ViewContainerViewlet {
@IContextKeyService private readonly contextKeyService: IContextKeyService,
@INotificationService private readonly notificationService: INotificationService
) {
super(VIEWLET_ID, `${VIEWLET_ID}.state`, false, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
super(VIEWLET_ID, `${VIEWLET_ID}.state`, true, configurationService, layoutService, telemetryService, storageService, instantiationService, themeService, contextMenuService, extensionService, contextService);
this._register(this.debugService.onDidChangeState(state => this.onDebugServiceStateChange(state)));
this._register(this.debugService.onDidNewSession(() => this.updateToolBar()));
this._register(this.contextKeyService.onDidChangeContext(e => {
if (e.affectsSome(new Set(CONTEXT_DEBUG_UX_KEY))) {
this.updateTitleArea();
}
}));
this._register(this.contextService.onDidChangeWorkbenchState(() => this.updateTitleArea()));
this._register(this.configurationService.onDidChangeConfiguration(e => {
if (e.affectsConfiguration('debug.toolBarLocation')) {
@ -83,6 +90,8 @@ export class DebugViewlet extends ViewContainerViewlet {
if (this.startDebugActionViewItem) {
this.startDebugActionViewItem.focus();
} else {
this.focusView(StartView.ID);
}
}
@ -107,6 +116,9 @@ export class DebugViewlet extends ViewContainerViewlet {
}
getActions(): IAction[] {
if (CONTEXT_DEBUG_UX.getValue(this.contextKeyService) === 'simple') {
return [];
}
if (this.showInitialDebugActions) {
return [this.startAction, this.configureAction, this.toggleReplAction];
}

View file

@ -13,6 +13,24 @@
height: 100%;
}
.debug-viewlet .debug-start-view {
padding: 0 20px 0 20px;
}
.debug-viewlet .debug-start-view .monaco-button,
.debug-viewlet .debug-start-view .section {
margin-top: 20px;
}
.debug-viewlet .debug-start-view .top-section {
margin-top: 10px;
}
.debug-viewlet .debug-start-view .configure {
cursor: pointer;
color: #007ACC;
}
.monaco-workbench .debug-action.notification:after {
content: '';
width: 6px;

View file

@ -0,0 +1,150 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import * as dom from 'vs/base/browser/dom';
import { Button } from 'vs/base/browser/ui/button/button';
import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet';
import { attachButtonStyler } from 'vs/platform/theme/common/styler';
import { IThemeService } from 'vs/platform/theme/common/themeService';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ViewletPane, IViewletPaneOptions } from 'vs/workbench/browser/parts/views/paneViewlet';
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
import { localize } from 'vs/nls';
import { ICommandService } from 'vs/platform/commands/common/commands';
import { StartAction, RunAction, ConfigureAction } from 'vs/workbench/contrib/debug/browser/debugActions';
import { IDebugService } from 'vs/workbench/contrib/debug/common/debug';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace';
import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs';
import { equals } from 'vs/base/common/arrays';
const $ = dom.$;
export class StartView extends ViewletPane {
static ID = 'workbench.debug.startView';
static LABEL = localize('start', "Start");
private debugButton!: Button;
private runButton!: Button;
private firstMessageContainer!: HTMLElement;
private secondMessageContainer!: HTMLElement;
private debuggerLabels: string[] | undefined = undefined;
constructor(
options: IViewletViewOptions,
@IThemeService private readonly themeService: IThemeService,
@IKeybindingService keybindingService: IKeybindingService,
@IContextMenuService contextMenuService: IContextMenuService,
@IConfigurationService configurationService: IConfigurationService,
@IContextKeyService contextKeyService: IContextKeyService,
@ICommandService private readonly commandService: ICommandService,
@IDebugService private readonly debugService: IDebugService,
@IEditorService private readonly editorService: IEditorService,
@IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService,
@IFileDialogService private readonly dialogService: IFileDialogService
) {
super({ ...(options as IViewletPaneOptions), ariaHeaderLabel: localize('debugStart', "Debug Start Section") }, keybindingService, contextMenuService, configurationService, contextKeyService);
this._register(editorService.onDidActiveEditorChange(() => this.updateView()));
this._register(this.debugService.getConfigurationManager().onDidRegisterDebugger(() => this.updateView()));
}
private updateView(): void {
const activeEditor = this.editorService.activeTextEditorWidget;
const debuggerLabels = this.debugService.getConfigurationManager().getDebuggerLabelsForEditor(activeEditor);
if (!equals(this.debuggerLabels, debuggerLabels)) {
this.debuggerLabels = debuggerLabels;
const enabled = this.debuggerLabels.length > 0;
this.debugButton.enabled = enabled;
this.runButton.enabled = enabled;
this.debugButton.label = this.debuggerLabels.length !== 1 ? localize('debug', "Debug") : localize('debugWith', "Debug with {0}", this.debuggerLabels[0]);
this.runButton.label = this.debuggerLabels.length !== 1 ? localize('run', "Run") : localize('runWith', "Run with {0}", this.debuggerLabels[0]);
const emptyWorkbench = this.workspaceContextService.getWorkbenchState() === WorkbenchState.EMPTY;
this.firstMessageContainer.innerHTML = '';
this.secondMessageContainer.innerHTML = '';
const secondMessageElement = $('span');
this.secondMessageContainer.appendChild(secondMessageElement);
const setFirstMessage = () => {
const firstMessageElement = $('span');
this.firstMessageContainer.appendChild(firstMessageElement);
firstMessageElement.textContent = localize('simplyDebugAndRun', "Open a file which can be debugged or run.");
};
const setSecondMessage = () => {
secondMessageElement.textContent = localize('specifyHowToRun', "To futher configure the Debug and Run experience");
const clickElement = $('span.configure');
clickElement.textContent = localize('configure', " create a launch.json file.");
clickElement.onclick = () => this.commandService.executeCommand(ConfigureAction.ID);
this.secondMessageContainer.appendChild(clickElement);
};
const setSecondMessageWithFolder = () => {
secondMessageElement.textContent = localize('noLaunchConfiguration', "To futher configure the Debug and Run experience, ");
const clickElement = $('span.configure');
clickElement.textContent = localize('openFolder', " open a folder");
clickElement.onclick = () => this.dialogService.pickFolderAndOpen({ forceNewWindow: false });
this.secondMessageContainer.appendChild(clickElement);
const moreText = $('span.moreText');
moreText.textContent = localize('andconfigure', " and create a launch.json file.");
this.secondMessageContainer.appendChild(moreText);
};
if (enabled && !emptyWorkbench) {
setSecondMessage();
}
if (enabled && emptyWorkbench) {
setSecondMessageWithFolder();
}
if (!enabled && !emptyWorkbench) {
setFirstMessage();
setSecondMessage();
}
if (!enabled && emptyWorkbench) {
setFirstMessage();
setSecondMessageWithFolder();
}
}
}
protected renderBody(container: HTMLElement): void {
this.firstMessageContainer = $('.top-section');
container.appendChild(this.firstMessageContainer);
this.debugButton = new Button(container);
this._register(this.debugButton.onDidClick(() => {
this.commandService.executeCommand(StartAction.ID);
}));
attachButtonStyler(this.debugButton, this.themeService);
this.runButton = new Button(container);
this.runButton.label = localize('run', "Run");
dom.addClass(container, 'debug-start-view');
this._register(this.runButton.onDidClick(() => {
this.commandService.executeCommand(RunAction.ID);
}));
attachButtonStyler(this.runButton, this.themeService);
this.secondMessageContainer = $('.section');
container.appendChild(this.secondMessageContainer);
this.updateView();
}
protected layoutBody(_: number, __: number): void {
// no-op
}
focus(): void {
this.runButton.focus();
}
}

View file

@ -9,7 +9,7 @@ import severity from 'vs/base/common/severity';
import { Event } from 'vs/base/common/event';
import { IJSONSchemaSnippet } from 'vs/base/common/jsonSchema';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IEditorContribution } from 'vs/editor/common/editorCommon';
import * as editorCommon from 'vs/editor/common/editorCommon';
import { ITextModel as EditorIModel } from 'vs/editor/common/model';
import { IEditor, ITextEditor } from 'vs/workbench/common/editor';
import { Position, IPosition } from 'vs/editor/common/core/position';
@ -40,6 +40,8 @@ export const DEBUG_SERVICE_ID = 'debugService';
export const CONTEXT_DEBUG_TYPE = new RawContextKey<string>('debugType', undefined);
export const CONTEXT_DEBUG_CONFIGURATION_TYPE = new RawContextKey<string>('debugConfigurationType', undefined);
export const CONTEXT_DEBUG_STATE = new RawContextKey<string>('debugState', 'inactive');
export const CONTEXT_DEBUG_UX_KEY = 'debugUx';
export const CONTEXT_DEBUG_UX = new RawContextKey<string>(CONTEXT_DEBUG_UX_KEY, 'default');
export const CONTEXT_IN_DEBUG_MODE = new RawContextKey<boolean>('inDebugMode', false);
export const CONTEXT_IN_DEBUG_REPL = new RawContextKey<boolean>('inDebugRepl', false);
export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey<boolean>('breakpointWidgetVisible', false);
@ -628,8 +630,11 @@ export interface IConfigurationManager {
*/
onDidSelectConfiguration: Event<void>;
onDidRegisterDebugger: Event<void>;
activateDebuggers(activationEvent: string, debugType?: string): Promise<void>;
getDebuggerLabelsForEditor(editor: editorCommon.IEditor | undefined): string[];
hasDebugConfigurationProvider(debugType: string): boolean;
registerDebugConfigurationProvider(debugConfigurationProvider: IDebugConfigurationProvider): IDisposable;
@ -861,12 +866,12 @@ export const enum BreakpointWidgetContext {
LOG_MESSAGE = 2
}
export interface IDebugEditorContribution extends IEditorContribution {
export interface IDebugEditorContribution extends editorCommon.IEditorContribution {
showHover(range: Range, focus: boolean): Promise<void>;
addLaunchConfiguration(): Promise<any>;
}
export interface IBreakpointEditorContribution extends IEditorContribution {
export interface IBreakpointEditorContribution extends editorCommon.IEditorContribution {
showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void;
closeBreakpointWidget(): void;
}

View file

@ -1074,7 +1074,7 @@ export class DebugModel implements IDebugModel {
if (first.column && second.column) {
return first.column - second.column;
}
return -1;
return 1;
}
return first.lineNumber - second.lineNumber;

View file

@ -230,10 +230,6 @@ export class ExplorerViewlet extends ViewContainerViewlet {
return <OpenEditorsView>this.getView(OpenEditorsView.ID);
}
public getEmptyView(): EmptyView {
return <EmptyView>this.getView(EmptyView.ID);
}
public setVisible(visible: boolean): void {
this.viewletVisibleContextKey.set(visible);
super.setVisible(visible);

View file

@ -18,7 +18,10 @@
color: white;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
font-weight: bold;
font-size: 30px;
font-size: 20px;
display: flex;
justify-content: center;
align-items: center;
}
</style>
</head>
@ -65,9 +68,8 @@
this.setVisible(true);
}
layout(width, height, orientation) {
console.log(`layout@${this.label}`);
this.element.style.lineHeight = `${height}px`;
layout(width, height, top, left) {
this.element.innerHTML = `(${top}, ${left})<br />(${width}, ${height})`;
}
setVisible(visible) {