From 1ef8084da7eb5a49d1b3cd20cefc84b865dbe120 Mon Sep 17 00:00:00 2001 From: Jacob Jaeggli <13710580+jjaeggli@users.noreply.github.com> Date: Wed, 9 Aug 2023 13:14:38 -0700 Subject: [PATCH 0001/2222] Document focus suggest details with ctrl+alt+space --- .../contrib/suggest/browser/suggestWidget.ts | 29 +++++++++++++++++-- .../suggest/browser/suggestWidgetDetails.ts | 11 +++++++ 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 8cbfc6ef9cb..7fcfb080b9e 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -434,6 +434,14 @@ export class SuggestWidget implements IDisposable { if (this._state === state) { return; } + + // Exit current state. + switch (this._state) { + case State.Details: + this._details.widget.removeFocusIndicator(); + break; + } + this._state = state; this.element.domNode.classList.toggle('frozen', state === State.Frozen); @@ -488,6 +496,7 @@ export class SuggestWidget implements IDisposable { dom.show(this._listElement, this._status.element); this._details.show(); this._show(); + this._details.widget.focus(); break; } } @@ -596,6 +605,10 @@ export class SuggestWidget implements IDisposable { return false; case State.Loading: return !this._isAuto; + case State.Details: + // The list item will be focused, so remove details focus + // indicator and fall through to the default action. + this._setState(State.Open); default: this._list.focusNext(1, true); return true; @@ -638,6 +651,10 @@ export class SuggestWidget implements IDisposable { return false; case State.Loading: return !this._isAuto; + case State.Details: + // The list item should be focused, so remove details focus + // indicator and fall through to the default action. + this._setState(State.Open); default: this._list.focusPrevious(1, true); return false; @@ -678,12 +695,14 @@ export class SuggestWidget implements IDisposable { toggleDetailsFocus(): void { if (this._state === State.Details) { + // Should return the focus to the list item. + this.focusSelected(); this._setState(State.Open); - this._details.widget.domNode.classList.remove('focused'); - } else if (this._state === State.Open && this._isDetailsVisible()) { this._setState(State.Details); - this._details.widget.domNode.classList.add('focused'); + } else if (this._state === State.Open) { + this.toggleDetails(); + this._setState(State.Details); } } @@ -696,6 +715,10 @@ export class SuggestWidget implements IDisposable { this._details.hide(); this.element.domNode.classList.remove('shows-details'); + if (this._state === State.Details) { + this._setState(State.Open); + } + } else if ((canExpandCompletionItem(this._list.getFocusedElements()[0]) || this._explainMode) && (this._state === State.Open || this._state === State.Details || this._state === State.Frozen)) { // show details widget (iff possible) this._ctxSuggestWidgetDetailsVisible.set(true); diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts b/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts index d6e95dd5a71..d3297e48ba1 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidgetDetails.ts @@ -65,6 +65,8 @@ export class SuggestDetailsWidget { this._header = dom.append(this._body, dom.$('.header')); this._close = dom.append(this._header, dom.$('span' + ThemeIcon.asCSSSelector(Codicon.close))); this._close.title = nls.localize('details.close', "Close"); + this._close.role = 'button'; + this._close.tabIndex = -1; this._type = dom.append(this._header, dom.$('p.type')); this._docs = dom.append(this._body, dom.$('p.docs')); @@ -250,6 +252,15 @@ export class SuggestDetailsWidget { get borderWidth() { return this._borderWidth; } + + focus() { + this.domNode.classList.add('focused'); + this.domNode.focus(); + } + + removeFocusIndicator() { + this.domNode.classList.remove('focused'); + } } interface TopLeftPosition { From 0977e3f8a87da8dc835d095e4d3fcabf0e234909 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 18 Mar 2024 09:00:20 -0700 Subject: [PATCH 0002/2222] Hack rendering to canvas in monaco --- src/vs/editor/browser/view/viewLayer.ts | 69 ++++++++++++++++++- src/vs/editor/browser/view/viewOverlays.ts | 2 +- .../browser/viewParts/lines/viewLines.ts | 9 +-- 3 files changed, 72 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index bbbb0dd9d73..6e16f0da6a5 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getActiveWindow } from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; import { BugIndicatingError } from 'vs/base/common/errors'; @@ -255,9 +256,17 @@ export class VisibleLinesCollection { public readonly domNode: FastDomNode; private readonly _linesCollection: RenderedLinesCollection; + private readonly _canvas: HTMLCanvasElement; + constructor(host: IVisibleLinesHost) { this._host = host; this.domNode = this._createDomNode(); + + this._canvas = document.createElement('canvas'); + this._canvas.style.height = '100%'; + this._canvas.style.width = '100%'; + this.domNode.domNode.appendChild(this._canvas); + this._linesCollection = new RenderedLinesCollection(() => this._host.createVisibleLine()); } @@ -345,11 +354,18 @@ export class VisibleLinesCollection { return this._linesCollection.getLine(lineNumber); } - public renderLines(viewportData: ViewportData): void { - + public renderLines(viewportData: ViewportData, viewOverlays?: boolean): void { const inp = this._linesCollection._get(); - const renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData); + let renderer; + if (viewOverlays) { + renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData); + } else { + this._canvas.width = this.domNode.domNode.clientWidth; + this._canvas.height = this.domNode.domNode.clientHeight; + renderer = new CanvasViewLayerRenderer(this._canvas, this._host, viewportData); + } + const ctx: IRendererContext = { rendLineNumberStart: inp.rendLineNumberStart, @@ -619,3 +635,50 @@ class ViewLayerRenderer { } } } + + +class CanvasViewLayerRenderer { + + readonly domNode: HTMLCanvasElement; + readonly host: IVisibleLinesHost; + readonly viewportData: ViewportData; + + private readonly _ctx: CanvasRenderingContext2D; + + constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { + this.domNode = domNode; + this.host = host; + this.viewportData = viewportData; + + this._ctx = this.domNode.getContext('2d')!; + this._ctx.fillStyle = '#fff'; + const style = getActiveWindow().getComputedStyle(this.domNode); + this._ctx.font = `${style.fontSize} ${style.fontFamily}`; + this._ctx.textBaseline = 'top'; + } + + public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { + + this._ctx.clearRect(0, 0, this.domNode.width, this.domNode.height); + + const ctx: IRendererContext = { + rendLineNumberStart: inContext.rendLineNumberStart, + lines: inContext.lines.slice(0), + linesLength: inContext.linesLength + }; + + let i = 0; + let scrollTop = parseInt(this.domNode.parentElement!.getAttribute('data-adjusted-scroll-top')!); + if (Number.isNaN(scrollTop)) { + scrollTop = 0; + } + for (let lineNumber = startLineNumber; lineNumber <= stopLineNumber; lineNumber++) { + const y = Math.round((-scrollTop + deltaTop[lineNumber - startLineNumber]) * getActiveWindow().devicePixelRatio); + // console.log(this.viewportData.getViewLineRenderingData(lineNumber).content, 0, y); + this._ctx.fillText(this.viewportData.getViewLineRenderingData(lineNumber).content, 0, y); + i++; + } + + return ctx; + } +} diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index 1041fd58a58..4d1b5a99581 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -136,7 +136,7 @@ export class ViewOverlays extends ViewPart implements IVisibleLinesHost, // (1) render lines - ensures lines are in the DOM this._visibleLines.renderLines(viewportData); this._lastRenderedData.setCurrentVisibleRange(viewportData.visibleRange); - this.domNode.setWidth(this._context.viewLayout.getScrollWidth()); - this.domNode.setHeight(Math.min(this._context.viewLayout.getScrollHeight(), 1000000)); + this.domNode.setWidth(this._context.viewLayout.getCurrentViewport().width); + this.domNode.setHeight(Math.min(this._context.viewLayout.getCurrentViewport().height, 1000000)); // (2) compute horizontal scroll position: // - this must happen after the lines are in the DOM since it might need a line that rendered just now @@ -663,8 +663,9 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, this._linesContent.setLayerHinting(this._canUseLayerHinting); this._linesContent.setContain('strict'); const adjustedScrollTop = this._context.viewLayout.getCurrentScrollTop() - viewportData.bigNumbersDelta; - this._linesContent.setTop(-adjustedScrollTop); - this._linesContent.setLeft(-this._context.viewLayout.getCurrentScrollLeft()); + this.domNode.domNode.setAttribute('data-adjusted-scroll-top', adjustedScrollTop.toString()); + // this._linesContent.setTop(-adjustedScrollTop); + // this._linesContent.setLeft(-this._context.viewLayout.getCurrentScrollLeft()); } // --- width From 5fe2da599700f44420c7c798c49f247fc8b0d500 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 18 Mar 2024 10:22:33 -0700 Subject: [PATCH 0003/2222] More --- src/vs/editor/browser/view/viewLayer.ts | 55 ++++++++++++++++--------- 1 file changed, 36 insertions(+), 19 deletions(-) diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 6e16f0da6a5..c23dc8e4e60 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -257,6 +257,7 @@ export class VisibleLinesCollection { private readonly _linesCollection: RenderedLinesCollection; private readonly _canvas: HTMLCanvasElement; + private readonly _textureAtlasCanvas: HTMLCanvasElement; constructor(host: IVisibleLinesHost) { this._host = host; @@ -267,6 +268,15 @@ export class VisibleLinesCollection { this._canvas.style.width = '100%'; this.domNode.domNode.appendChild(this._canvas); + const textureAtlasCanvas = this._textureAtlasCanvas = document.createElement('canvas'); + const textureAtlasCtx = textureAtlasCanvas.getContext('2d')!; + const style = getActiveWindow().getComputedStyle(this.domNode.domNode); + textureAtlasCtx.fillStyle = '#fff'; + textureAtlasCtx.font = `${style.fontSize} ${style.fontFamily}`; + textureAtlasCtx.textBaseline = 'top'; + textureAtlasCtx.fillText(' ', 0, 0); + textureAtlasCtx.fillText('x', 50, 0); + this._linesCollection = new RenderedLinesCollection(() => this._host.createVisibleLine()); } @@ -363,7 +373,7 @@ export class VisibleLinesCollection { } else { this._canvas.width = this.domNode.domNode.clientWidth; this._canvas.height = this.domNode.domNode.clientHeight; - renderer = new CanvasViewLayerRenderer(this._canvas, this._host, viewportData); + renderer = new CanvasViewLayerRenderer(this._canvas, this._host, viewportData, this._textureAtlasCanvas); } @@ -636,6 +646,7 @@ class ViewLayerRenderer { } } +const useWebgpu = true; class CanvasViewLayerRenderer { @@ -645,38 +656,44 @@ class CanvasViewLayerRenderer { private readonly _ctx: CanvasRenderingContext2D; - constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { + constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData, private readonly _textureAtlasCanvas: HTMLCanvasElement) { this.domNode = domNode; this.host = host; this.viewportData = viewportData; this._ctx = this.domNode.getContext('2d')!; - this._ctx.fillStyle = '#fff'; - const style = getActiveWindow().getComputedStyle(this.domNode); - this._ctx.font = `${style.fontSize} ${style.fontFamily}`; - this._ctx.textBaseline = 'top'; + // const style = getActiveWindow().getComputedStyle(this.domNode); + // this._ctx.font = `${style.fontSize} ${style.fontFamily}`; + // this._ctx.textBaseline = 'top'; } public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { - - this._ctx.clearRect(0, 0, this.domNode.width, this.domNode.height); - const ctx: IRendererContext = { rendLineNumberStart: inContext.rendLineNumberStart, lines: inContext.lines.slice(0), linesLength: inContext.linesLength }; + if (useWebgpu) { + } else { + this._ctx.clearRect(0, 0, this.domNode.width, this.domNode.height); - let i = 0; - let scrollTop = parseInt(this.domNode.parentElement!.getAttribute('data-adjusted-scroll-top')!); - if (Number.isNaN(scrollTop)) { - scrollTop = 0; - } - for (let lineNumber = startLineNumber; lineNumber <= stopLineNumber; lineNumber++) { - const y = Math.round((-scrollTop + deltaTop[lineNumber - startLineNumber]) * getActiveWindow().devicePixelRatio); - // console.log(this.viewportData.getViewLineRenderingData(lineNumber).content, 0, y); - this._ctx.fillText(this.viewportData.getViewLineRenderingData(lineNumber).content, 0, y); - i++; + let i = 0; + let scrollTop = parseInt(this.domNode.parentElement!.getAttribute('data-adjusted-scroll-top')!); + if (Number.isNaN(scrollTop)) { + scrollTop = 0; + } + for (let lineNumber = startLineNumber; lineNumber <= stopLineNumber; lineNumber++) { + const y = Math.round((-scrollTop + deltaTop[lineNumber - startLineNumber])); + // console.log(this.viewportData.getViewLineRenderingData(lineNumber).content, 0, y); + const content = this.viewportData.getViewLineRenderingData(lineNumber).content; + for (let x = 0; x < content.length; x++) { + if (content.charAt(x) === ' ') { + continue; + } + this._ctx.drawImage(this._textureAtlasCanvas, 50, 0, 7, this.viewportData.lineHeight, x * 7, y + 2/* offset x char */, 7, this.viewportData.lineHeight); + } + i++; + } } return ctx; From 679935d556b9115fefdbd24bd3460c47f610806a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 22 Mar 2024 23:23:35 -0700 Subject: [PATCH 0004/2222] Get webgpu rendering a char --- package.json | 1 + src/tsconfig.json | 3 +- src/vs/editor/browser/view/viewLayer.ts | 431 +++++++++++++++++++++++- yarn.lock | 5 + 4 files changed, 430 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 40300428d56..47c795ab720 100644 --- a/package.json +++ b/package.json @@ -140,6 +140,7 @@ "@vscode/test-web": "^0.0.50", "@vscode/v8-heap-parser": "^0.1.0", "@vscode/vscode-perf": "^0.0.14", + "@webgpu/types": "^0.1.40", "ansi-colors": "^3.2.3", "asar": "^3.0.3", "chromium-pickle-js": "^0.2.0", diff --git a/src/tsconfig.json b/src/tsconfig.json index 1e0c8b14a1a..080fac86bb2 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -13,7 +13,8 @@ "sinon", "winreg", "trusted-types", - "wicg-file-system-access" + "wicg-file-system-access", + "@webgpu/types" ], "plugins": [ { diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index c23dc8e4e60..dc57233c6d1 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -269,8 +269,14 @@ export class VisibleLinesCollection { this.domNode.domNode.appendChild(this._canvas); const textureAtlasCanvas = this._textureAtlasCanvas = document.createElement('canvas'); + textureAtlasCanvas.width = 100; + textureAtlasCanvas.height = 100; const textureAtlasCtx = textureAtlasCanvas.getContext('2d')!; const style = getActiveWindow().getComputedStyle(this.domNode.domNode); + for (let i = 0; i < textureAtlasCanvas.width; i++) { + textureAtlasCtx.fillStyle = `#00${((i / 100) * 255).toString(16)}00`; + textureAtlasCtx.fillRect(i, 0, 1, textureAtlasCanvas.height); + } textureAtlasCtx.fillStyle = '#fff'; textureAtlasCtx.font = `${style.fontSize} ${style.fontFamily}`; textureAtlasCtx.textBaseline = 'top'; @@ -364,6 +370,8 @@ export class VisibleLinesCollection { return this._linesCollection.getLine(lineNumber); } + private _canvasRenderer: CanvasViewLayerRenderer | undefined; + public renderLines(viewportData: ViewportData, viewOverlays?: boolean): void { const inp = this._linesCollection._get(); @@ -373,10 +381,13 @@ export class VisibleLinesCollection { } else { this._canvas.width = this.domNode.domNode.clientWidth; this._canvas.height = this.domNode.domNode.clientHeight; - renderer = new CanvasViewLayerRenderer(this._canvas, this._host, viewportData, this._textureAtlasCanvas); + if (!this._canvasRenderer) { + this._canvasRenderer = new CanvasViewLayerRenderer(this._canvas, this._host, viewportData, this._textureAtlasCanvas); + } + renderer = this._canvasRenderer; + renderer.update(viewportData); } - const ctx: IRendererContext = { rendLineNumberStart: inp.rendLineNumberStart, lines: inp.lines, @@ -648,23 +659,355 @@ class ViewLayerRenderer { const useWebgpu = true; +const enum Constants { + IndicesPerCell = 6 +} + +const enum BindingId { + SpriteInfo = 0, + DynamicUnitInfo = 1, + TextureSampler = 2, + Texture = 3, + Uniforms = 4, + TextureInfoUniform = 5, +} + +const wgsl = ` +struct Uniforms { + canvasDimensions: vec2f, +}; + +struct TextureInfoUniform { + spriteSheetSize: vec2f, +} + +struct SpriteInfo { + position: vec2f, + size: vec2f, +}; + +struct Vertex { + @location(0) position: vec2f, +}; + +struct DynamicUnitInfo { + position: vec2f, + dimensions: vec2f, + unused: f32, + textureId: f32, +}; + +struct VSOutput { + @builtin(position) position: vec4f, + @location(0) texcoord: vec2f, +}; + +@group(0) @binding(${BindingId.Uniforms}) var uniforms: Uniforms; +@group(0) @binding(${BindingId.TextureInfoUniform}) var textureInfoUniform: TextureInfoUniform; + +@group(0) @binding(${BindingId.SpriteInfo}) var spriteInfo: array; +@group(0) @binding(${BindingId.DynamicUnitInfo}) var dynamicUnitInfoStructs: array; + +@vertex fn vs( + vert: Vertex, + @builtin(instance_index) instanceIndex: u32, + @builtin(vertex_index) vertexIndex : u32 +) -> VSOutput { + let dynamicUnitInfo = dynamicUnitInfoStructs[instanceIndex]; + let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureId)]; + + var vsOut: VSOutput; + vsOut.position = vec4f( + (((vert.position * 2 - 1) / uniforms.canvasDimensions)) * dynamicUnitInfo.dimensions + dynamicUnitInfo.position, + 0.0, + 1.0 + ); + + // Textures are flipped from natural direction on the y-axis, so flip it back + vsOut.texcoord = vec2f(vert.position.x, 1.0 - vert.position.y); + vsOut.texcoord = ( + // Sprite offset (0-1) + (spriteInfo.position / textureInfoUniform.spriteSheetSize) + + // Sprite coordinate (0-1) + (vsOut.texcoord * (spriteInfo.size / textureInfoUniform.spriteSheetSize)) + ); + + return vsOut; +} + +@group(0) @binding(${BindingId.TextureSampler}) var ourSampler: sampler; +@group(0) @binding(${BindingId.Texture}) var ourTexture: texture_2d; + +@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f { + // var a = textureSample(ourTexture, ourSampler, vsOut.texcoord); + // return vec4f(1.0, 0.0, 0.0, 1.0); + return textureSample(ourTexture, ourSampler, vsOut.texcoord); +} +`; + class CanvasViewLayerRenderer { readonly domNode: HTMLCanvasElement; - readonly host: IVisibleLinesHost; - readonly viewportData: ViewportData; + host: IVisibleLinesHost; + viewportData: ViewportData; + + private readonly _ctx!: CanvasRenderingContext2D; + + private readonly _gpuCtx!: GPUCanvasContext; + + private _adapter!: GPUAdapter; + private _device!: GPUDevice; + private _renderPassDescriptor!: GPURenderPassDescriptor; + private _bindGroup!: GPUBindGroup; + private _pipeline!: GPURenderPipeline; + + private _dataBindBuffer!: GPUBuffer; + private _dataValueBuffers!: ArrayBuffer[]; + private _dataValuesBufferActiveIndex: number = 0; - private readonly _ctx: CanvasRenderingContext2D; + private _vertexBuffer!: GPUBuffer; + private _squareVertices!: { vertexData: Float32Array; numVertices: number }; + + private _initialized = false; constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData, private readonly _textureAtlasCanvas: HTMLCanvasElement) { this.domNode = domNode; this.host = host; this.viewportData = viewportData; - this._ctx = this.domNode.getContext('2d')!; - // const style = getActiveWindow().getComputedStyle(this.domNode); - // this._ctx.font = `${style.fontSize} ${style.fontFamily}`; - // this._ctx.textBaseline = 'top'; + if (useWebgpu) { + this._gpuCtx = this.domNode.getContext('webgpu')!; + this.initWebgpu(); + } else { + this._ctx = this.domNode.getContext('2d')!; + } + } + + async initWebgpu() { + if (!navigator.gpu) { + throw new Error('this browser does not support WebGPU'); + } + + this._adapter = (await navigator.gpu.requestAdapter())!; + if (!this._adapter) { + throw new Error('this browser supports webgpu but it appears disabled'); + } + + this._device = await this._adapter.requestDevice(); + + const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); + this._gpuCtx.configure({ + device: this._device, + format: presentationFormat, + }); + + const module = this._device.createShaderModule({ + label: 'ViewLayer shader module', + code: wgsl, + }); + + this._pipeline = this._device.createRenderPipeline({ + label: 'ViewLayer render pipeline', + layout: 'auto', + vertex: { + module, + entryPoint: 'vs', + buffers: [ + { + arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT, // 2 floats, 4 bytes each + attributes: [ + { shaderLocation: 0, offset: 0, format: 'float32x2' }, // position + ], + } + ] + }, + fragment: { + module, + entryPoint: 'fs', + targets: [ + { + format: presentationFormat, + blend: { + color: { + srcFactor: 'one', + dstFactor: 'one-minus-src-alpha' + }, + alpha: { + srcFactor: 'one', + dstFactor: 'one-minus-src-alpha' + }, + }, + } + ], + }, + }); + + + + // Write standard uniforms + const enum UniformBufferInfo { + Size = 2, // 2x 32 bit floats + OffsetCanvasWidth = 0, + OffsetCanvasHeight = 1 + } + const uniformBuffer = this._device.createBuffer({ + size: UniformBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + { + const uniformValues = new Float32Array(UniformBufferInfo.Size); + // TODO: Update on canvas resize + uniformValues[UniformBufferInfo.OffsetCanvasWidth] = this.domNode.width; + uniformValues[UniformBufferInfo.OffsetCanvasHeight] = this.domNode.height; + this._device.queue.writeBuffer(uniformBuffer, 0, uniformValues); + } + + + + // Upload texture bitmap from atlas + const textureAtlasGpuTexture = this._device.createTexture({ + format: 'rgba8unorm', + size: { width: this._textureAtlasCanvas.width, height: this._textureAtlasCanvas.height }, + usage: GPUTextureUsage.TEXTURE_BINDING | + GPUTextureUsage.COPY_DST | + GPUTextureUsage.RENDER_ATTACHMENT, + }); + this._device.queue.copyExternalImageToTexture( + { source: this._textureAtlasCanvas }, + { texture: textureAtlasGpuTexture }, + { width: this._textureAtlasCanvas.width, height: this._textureAtlasCanvas.height }, + ); + + + + const enum TextureInfoUniformBufferInfo { + Size = 2, + SpriteSheetSize = 0, + } + const textureInfoUniformBufferSize = TextureInfoUniformBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; + const textureInfoUniformBuffer = this._device.createBuffer({ + size: textureInfoUniformBufferSize, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + { + const uniformValues = new Float32Array(TextureInfoUniformBufferInfo.Size); + // TODO: Update on canvas resize + uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize] = this._textureAtlasCanvas.width; + uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize + 1] = this._textureAtlasCanvas.height; + this._device.queue.writeBuffer(textureInfoUniformBuffer, 0, uniformValues); + } + + + const maxRenderedObjects = 10; + + /////////////////// + // Static buffer // + /////////////////// + const enum SpriteInfoStorageBufferInfo { + Size = 2 + 2, + Offset_TexturePosition = 0, + Offset_TextureSize = 2, + } + const spriteInfoStorageBufferByteSize = SpriteInfoStorageBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; + const spriteInfoStorageBuffer = this._device.createBuffer({ + label: 'Entity static info buffer', + size: spriteInfoStorageBufferByteSize * maxRenderedObjects, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }); + { + const sprites: { x: number; y: number; w: number; h: number }[] = [ + { x: 0, y: 0, w: 7, h: 16 }, + { x: 50, y: 0, w: 7, h: 10 } + ]; + const bufferSize = spriteInfoStorageBufferByteSize * sprites.length; + const values = new Float32Array(bufferSize / 4); + let entryOffset = 0; + for (const t of sprites) { + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition] = t.x; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition + 1] = t.y; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize] = t.w; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize + 1] = t.h; + entryOffset += SpriteInfoStorageBufferInfo.Size; + } + this._device.queue.writeBuffer(spriteInfoStorageBuffer, 0, values); + } + + + + const cellCount = 2; + const bufferSize = cellCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; + this._dataBindBuffer = this._device.createBuffer({ + label: 'Entity dynamic info buffer', + size: bufferSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }); + this._dataValueBuffers = [ + new ArrayBuffer(bufferSize), + new ArrayBuffer(bufferSize), + ]; + this._updateSquareVertices(); + + + + const sampler = this._device.createSampler({ + magFilter: 'nearest', + minFilter: 'nearest', + }); + this._bindGroup = this._device.createBindGroup({ + label: 'ViewLayer bind group', + layout: this._pipeline.getBindGroupLayout(0), + entries: [ + { binding: BindingId.SpriteInfo, resource: { buffer: spriteInfoStorageBuffer } }, + { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._dataBindBuffer } }, + { binding: BindingId.TextureSampler, resource: sampler }, + { binding: BindingId.Texture, resource: textureAtlasGpuTexture.createView() }, + { binding: BindingId.Uniforms, resource: { buffer: uniformBuffer } }, + { binding: BindingId.TextureInfoUniform, resource: { buffer: textureInfoUniformBuffer } }, + ], + }); + + this._renderPassDescriptor = { + label: 'ViewLayer render pass', + colorAttachments: [ + ( + { + // view: <- to be filled out when we render + loadValue: [0, 0, 0, 0], + loadOp: 'load', + storeOp: 'store', + } as Omit + ) as any as GPURenderPassColorAttachment, + ] as any as Iterable, + }; + + + this._initialized = true; + } + + private _updateSquareVertices() { + this._squareVertices = { + vertexData: new Float32Array([ + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 0, 1, + 1, 0, + ]), + numVertices: 6 + }; + const { vertexData } = this._squareVertices; + + this._vertexBuffer = this._device.createBuffer({ + label: 'vertex buffer vertices', + size: vertexData.byteLength, + usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, + }); + this._device.queue.writeBuffer(this._vertexBuffer, 0, vertexData); + } + + update(viewportData: ViewportData) { + this.viewportData = viewportData; } public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { @@ -673,7 +1016,12 @@ class CanvasViewLayerRenderer { lines: inContext.lines.slice(0), linesLength: inContext.linesLength }; + if (useWebgpu) { + if (!this._initialized) { + return ctx; + } + return this._renderWebgpu(ctx, startLineNumber, stopLineNumber, deltaTop); } else { this._ctx.clearRect(0, 0, this.domNode.width, this.domNode.height); @@ -698,4 +1046,69 @@ class CanvasViewLayerRenderer { return ctx; } + + private _renderWebgpu(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { + + const visibleObjectCount = this._updateDataBuffer(); + + // Write buffer and swap it out to unblock writes + const dataBuffer = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); + this._device.queue.writeBuffer(this._dataBindBuffer, 0, dataBuffer, 0, visibleObjectCount * Constants.IndicesPerCell); + + this._dataValuesBufferActiveIndex = (this._dataValuesBufferActiveIndex + 1) % 2; + + const encoder = this._device.createCommandEncoder(); + + (this._renderPassDescriptor.colorAttachments as any)[0].view = this._gpuCtx.getCurrentTexture().createView(); + const pass = encoder.beginRenderPass(this._renderPassDescriptor); + pass.setPipeline(this._pipeline); + pass.setVertexBuffer(0, this._vertexBuffer); + + pass.setBindGroup(0, this._bindGroup); + // TODO: Draws could be split by chunk, this would help minimize moving data around in arrays + pass.draw(this._squareVertices.numVertices, visibleObjectCount); + + pass.end(); + + const commandBuffer = encoder.finish(); + + this._device.queue.submit([commandBuffer]); + + return ctx; + } + + private _updateDataBuffer() { + let screenAbsoluteX: number = 0; + let screenAbsoluteY: number = 0; + let zeroToOneX: number = 0; + let zeroToOneY: number = 0; + let wgslX: number = 0; + let wgslY: number = 0; + + screenAbsoluteX = 100; + screenAbsoluteY = 100; + + screenAbsoluteX = Math.round(screenAbsoluteX); + screenAbsoluteY = Math.round(screenAbsoluteY); + zeroToOneX = screenAbsoluteX / this.domNode.width; + zeroToOneY = screenAbsoluteY / this.domNode.height; + wgslX = zeroToOneX * 2 - 1; + wgslY = zeroToOneY * 2 - 1; + + const offset = 0; + const objectCount = 1; + const data = new Float32Array(objectCount * Constants.IndicesPerCell); + data[offset] = wgslX; // x + data[offset + 1] = -wgslY; // y + data[offset + 2] = 7; // width + data[offset + 3] = 10; // height + data[offset + 4] = 0; // unused + data[offset + 5] = 1; // textureIndex + + + + const storageValues = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); + storageValues.set(data); + return objectCount; + } } diff --git a/yarn.lock b/yarn.lock index 997b34f02cf..49735b48cea 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1685,6 +1685,11 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" +"@webgpu/types@^0.1.40": + version "0.1.40" + resolved "https://registry.yarnpkg.com/@webgpu/types/-/types-0.1.40.tgz#cf72d1df6f9f8adc5d39556041f20ff2e8a58885" + integrity sha512-/BBkHLS6/eQjyWhY2H7Dx5DHcVrS2ICj9owvSRdgtQT6KcafLZA86tPze0xAOsd4FbsYKCUBUQyNi87q7gV7kw== + "@webpack-cli/configtest@^2.0.1": version "2.0.1" resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.0.1.tgz#a69720f6c9bad6aef54a8fa6ba9c3533e7ef4c7f" From dea43019c534792227426b41f5918fc94cda6dcf Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 23 Mar 2024 17:59:14 -0700 Subject: [PATCH 0005/2222] Basic texture atlas allocation --- src/vs/editor/browser/view/gpu/gpuUtils.ts | 11 + .../editor/browser/view/gpu/textureAtlas.ts | 197 ++++++++++++++++++ src/vs/editor/browser/view/viewLayer.ts | 87 ++------ 3 files changed, 230 insertions(+), 65 deletions(-) create mode 100644 src/vs/editor/browser/view/gpu/gpuUtils.ts create mode 100644 src/vs/editor/browser/view/gpu/textureAtlas.ts diff --git a/src/vs/editor/browser/view/gpu/gpuUtils.ts b/src/vs/editor/browser/view/gpu/gpuUtils.ts new file mode 100644 index 00000000000..4f1d64edb0c --- /dev/null +++ b/src/vs/editor/browser/view/gpu/gpuUtils.ts @@ -0,0 +1,11 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function ensureNonNullable(value: T | null): T { + if (!value) { + throw new Error(`Value "${value}" cannot be null`); + } + return value; +} diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts new file mode 100644 index 00000000000..266b3ccf585 --- /dev/null +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -0,0 +1,197 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getActiveWindow } from 'vs/base/browser/dom'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; + +export class TextureAtlas extends Disposable { + private _canvas: OffscreenCanvas; + private _ctx: OffscreenCanvasRenderingContext2D; + + private _glyphMap: Map = new Map(); + + private _glyphRasterizer: GlyphRasterizer; + + public get source(): OffscreenCanvas { + return this._canvas; + } + + // TODO: Should pull in the font size from config instead of random dom node + constructor(parentDomNode: HTMLElement, maxTextureSize: number) { + super(); + + this._canvas = new OffscreenCanvas(maxTextureSize, maxTextureSize); + this._ctx = ensureNonNullable(this._canvas.getContext('2d')); + + const style = getActiveWindow().getComputedStyle(parentDomNode); + this._ctx.font = `${style.fontSize} ${style.fontFamily}`; + this._ctx.textBaseline = 'top'; + + // TODO: Device pixel ratio? + const fontSize = parseInt(style.fontSize.replace('px', '')); + this._glyphRasterizer = new GlyphRasterizer(fontSize); + + // Reduce impact of a memory leak if this object is not released + this._register(toDisposable(() => { + this._canvas.width = 1; + this._canvas.height = 1; + })); + } + + // TODO: Color, style etc. + public getGlyph(lineContent: string, glyphIndex: number): ITextureAtlasGlyph { + const chars = lineContent.charAt(glyphIndex); + const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars); + this._ctx.drawImage( + rasterizedGlyph.source, + // source + rasterizedGlyph.boundingBox.left, + rasterizedGlyph.boundingBox.top, + rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, + rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top, + // destination + 0, + 0, + rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, + rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + ); + let glyph: ITextureAtlasGlyph | undefined = this._glyphMap.get(chars); + if (!glyph) { + // TODO: Implement allocation + glyph = { + x: 0, + y: 0, + width: rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, + height: rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + }; + console.log('Allocating glyph', { + rasterizedGlyph, + glyph + }); + this._glyphMap.set(chars, glyph); + } + return glyph; + } +} + +class GlyphRasterizer extends Disposable { + private _canvas: OffscreenCanvas; + // A temporary context that glyphs are drawn to before being transfered to the atlas. + private _ctx: OffscreenCanvasRenderingContext2D; + + constructor(private readonly _fontSize: number) { + super(); + + this._canvas = new OffscreenCanvas(this._fontSize * 3, this._fontSize * 3); + this._ctx = ensureNonNullable(this._canvas.getContext('2d')); + } + + public rasterizeGlyph(chars: string): IRasterizedGlyph { + this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); + + this._ctx.fillStyle = '#00FF00'; + this._ctx.fillText(chars, this._fontSize, this._fontSize); + const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); + // TODO: Hot path: Reuse object + const result: IRasterizedGlyph = { + source: this._canvas, + boundingBox: this._findGlyphBoundingBox(imageData) + }; + return result; + } + + private _findGlyphBoundingBox(imageData: ImageData): IBoundingBox { + // TODO: Hot path: Reuse object + const boundingBox = { + left: 0, + top: 0, + right: 0, + bottom: 0 + }; + // TODO: This could be optimized to be aware of the font size padding on all sides + const height = this._canvas.height; + const width = this._canvas.width; + let found = false; + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const alphaOffset = y * width * 4 + x * 4 + 3; + if (imageData.data[alphaOffset] !== 0) { + boundingBox.top = y; + found = true; + break; + } + } + if (found) { + break; + } + } + boundingBox.left = 0; + found = false; + for (let x = 0; x < width; x++) { + for (let y = 0; y < height; y++) { + const alphaOffset = y * width * 4 + x * 4 + 3; + if (imageData.data[alphaOffset] !== 0) { + boundingBox.left = x; + found = true; + break; + } + } + if (found) { + break; + } + } + boundingBox.right = width; + found = false; + for (let x = width - 1; x >= boundingBox.left; x--) { + for (let y = 0; y < height; y++) { + const alphaOffset = y * width * 4 + x * 4 + 3; + if (imageData.data[alphaOffset] !== 0) { + boundingBox.right = x; + found = true; + break; + } + } + if (found) { + break; + } + } + boundingBox.bottom = boundingBox.top; + found = false; + for (let y = height - 1; y >= 0; y--) { + for (let x = 0; x < width; x++) { + const alphaOffset = y * width * 4 + x * 4 + 3; + if (imageData.data[alphaOffset] !== 0) { + boundingBox.bottom = y; + found = true; + break; + } + } + if (found) { + break; + } + } + return boundingBox; + } +} + +export interface ITextureAtlasGlyph { + x: number; + y: number; + width: number; + height: number; +} + +interface IBoundingBox { + left: number; + top: number; + right: number; + bottom: number; +} + +interface IRasterizedGlyph { + source: CanvasImageSource; + boundingBox: IBoundingBox; +} diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index dc57233c6d1..2c7124b730e 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getActiveWindow } from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; import { BugIndicatingError } from 'vs/base/common/errors'; +import { TextureAtlas } from 'vs/editor/browser/view/gpu/textureAtlas'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { StringBuilder } from 'vs/editor/common/core/stringBuilder'; import * as viewEvents from 'vs/editor/common/viewEvents'; @@ -257,7 +257,6 @@ export class VisibleLinesCollection { private readonly _linesCollection: RenderedLinesCollection; private readonly _canvas: HTMLCanvasElement; - private readonly _textureAtlasCanvas: HTMLCanvasElement; constructor(host: IVisibleLinesHost) { this._host = host; @@ -268,21 +267,6 @@ export class VisibleLinesCollection { this._canvas.style.width = '100%'; this.domNode.domNode.appendChild(this._canvas); - const textureAtlasCanvas = this._textureAtlasCanvas = document.createElement('canvas'); - textureAtlasCanvas.width = 100; - textureAtlasCanvas.height = 100; - const textureAtlasCtx = textureAtlasCanvas.getContext('2d')!; - const style = getActiveWindow().getComputedStyle(this.domNode.domNode); - for (let i = 0; i < textureAtlasCanvas.width; i++) { - textureAtlasCtx.fillStyle = `#00${((i / 100) * 255).toString(16)}00`; - textureAtlasCtx.fillRect(i, 0, 1, textureAtlasCanvas.height); - } - textureAtlasCtx.fillStyle = '#fff'; - textureAtlasCtx.font = `${style.fontSize} ${style.fontFamily}`; - textureAtlasCtx.textBaseline = 'top'; - textureAtlasCtx.fillText(' ', 0, 0); - textureAtlasCtx.fillText('x', 50, 0); - this._linesCollection = new RenderedLinesCollection(() => this._host.createVisibleLine()); } @@ -382,7 +366,7 @@ export class VisibleLinesCollection { this._canvas.width = this.domNode.domNode.clientWidth; this._canvas.height = this.domNode.domNode.clientHeight; if (!this._canvasRenderer) { - this._canvasRenderer = new CanvasViewLayerRenderer(this._canvas, this._host, viewportData, this._textureAtlasCanvas); + this._canvasRenderer = new CanvasViewLayerRenderer(this._canvas, this._host, viewportData); } renderer = this._canvasRenderer; renderer.update(viewportData); @@ -657,8 +641,6 @@ class ViewLayerRenderer { } } -const useWebgpu = true; - const enum Constants { IndicesPerCell = 6 } @@ -751,8 +733,6 @@ class CanvasViewLayerRenderer { host: IVisibleLinesHost; viewportData: ViewportData; - private readonly _ctx!: CanvasRenderingContext2D; - private readonly _gpuCtx!: GPUCanvasContext; private _adapter!: GPUAdapter; @@ -770,17 +750,13 @@ class CanvasViewLayerRenderer { private _initialized = false; - constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData, private readonly _textureAtlasCanvas: HTMLCanvasElement) { + constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { this.domNode = domNode; this.host = host; this.viewportData = viewportData; - if (useWebgpu) { - this._gpuCtx = this.domNode.getContext('webgpu')!; - this.initWebgpu(); - } else { - this._ctx = this.domNode.getContext('2d')!; - } + this._gpuCtx = this.domNode.getContext('webgpu')!; + this.initWebgpu(); } async initWebgpu() { @@ -863,19 +839,24 @@ class CanvasViewLayerRenderer { } + // Create texture atlas + const textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); + textureAtlas.getGlyph('ABC', 0); + + // Upload texture bitmap from atlas const textureAtlasGpuTexture = this._device.createTexture({ format: 'rgba8unorm', - size: { width: this._textureAtlasCanvas.width, height: this._textureAtlasCanvas.height }, + size: { width: textureAtlas.source.width, height: textureAtlas.source.height }, usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT, }); this._device.queue.copyExternalImageToTexture( - { source: this._textureAtlasCanvas }, + { source: textureAtlas.source }, { texture: textureAtlasGpuTexture }, - { width: this._textureAtlasCanvas.width, height: this._textureAtlasCanvas.height }, + { width: textureAtlas.source.width, height: textureAtlas.source.height }, ); @@ -892,8 +873,8 @@ class CanvasViewLayerRenderer { { const uniformValues = new Float32Array(TextureInfoUniformBufferInfo.Size); // TODO: Update on canvas resize - uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize] = this._textureAtlasCanvas.width; - uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize + 1] = this._textureAtlasCanvas.height; + uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize] = textureAtlas.source.width; + uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize + 1] = textureAtlas.source.height; this._device.queue.writeBuffer(textureInfoUniformBuffer, 0, uniformValues); } @@ -916,8 +897,8 @@ class CanvasViewLayerRenderer { }); { const sprites: { x: number; y: number; w: number; h: number }[] = [ - { x: 0, y: 0, w: 7, h: 16 }, - { x: 50, y: 0, w: 7, h: 10 } + { x: 0, y: 0, w: 7, h: 10 }, + { x: 0, y: 0, w: 50, h: 50 } ]; const bufferSize = spriteInfoStorageBufferByteSize * sprites.length; const values = new Float32Array(bufferSize / 4); @@ -1017,34 +998,10 @@ class CanvasViewLayerRenderer { linesLength: inContext.linesLength }; - if (useWebgpu) { - if (!this._initialized) { - return ctx; - } - return this._renderWebgpu(ctx, startLineNumber, stopLineNumber, deltaTop); - } else { - this._ctx.clearRect(0, 0, this.domNode.width, this.domNode.height); - - let i = 0; - let scrollTop = parseInt(this.domNode.parentElement!.getAttribute('data-adjusted-scroll-top')!); - if (Number.isNaN(scrollTop)) { - scrollTop = 0; - } - for (let lineNumber = startLineNumber; lineNumber <= stopLineNumber; lineNumber++) { - const y = Math.round((-scrollTop + deltaTop[lineNumber - startLineNumber])); - // console.log(this.viewportData.getViewLineRenderingData(lineNumber).content, 0, y); - const content = this.viewportData.getViewLineRenderingData(lineNumber).content; - for (let x = 0; x < content.length; x++) { - if (content.charAt(x) === ' ') { - continue; - } - this._ctx.drawImage(this._textureAtlasCanvas, 50, 0, 7, this.viewportData.lineHeight, x * 7, y + 2/* offset x char */, 7, this.viewportData.lineHeight); - } - i++; - } + if (!this._initialized) { + return ctx; } - - return ctx; + return this._renderWebgpu(ctx, startLineNumber, stopLineNumber, deltaTop); } private _renderWebgpu(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { @@ -1100,8 +1057,8 @@ class CanvasViewLayerRenderer { const data = new Float32Array(objectCount * Constants.IndicesPerCell); data[offset] = wgslX; // x data[offset + 1] = -wgslY; // y - data[offset + 2] = 7; // width - data[offset + 3] = 10; // height + data[offset + 2] = 50;// 7; // width + data[offset + 3] = 50;//10; // height data[offset + 4] = 0; // unused data[offset + 5] = 1; // textureIndex From 2a7668408bf95b8c5999b4f25db6efed4abf0537 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 24 Mar 2024 06:14:30 -0700 Subject: [PATCH 0006/2222] Move to gpuViewLayer.ts --- .../editor/browser/view/gpu/gpuViewLayer.ts | 443 ++++++++++++++++++ src/vs/editor/browser/view/viewLayer.ts | 438 +---------------- 2 files changed, 448 insertions(+), 433 deletions(-) create mode 100644 src/vs/editor/browser/view/gpu/gpuViewLayer.ts diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts new file mode 100644 index 00000000000..e9e8fd9d819 --- /dev/null +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -0,0 +1,443 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { TextureAtlas } from 'vs/editor/browser/view/gpu/textureAtlas'; +import type { IVisibleLine, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; +import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; + +interface IRendererContext { + rendLineNumberStart: number; + lines: T[]; + linesLength: number; +} + +const enum Constants { + IndicesPerCell = 6 +} + +const enum BindingId { + SpriteInfo = 0, + DynamicUnitInfo = 1, + TextureSampler = 2, + Texture = 3, + Uniforms = 4, + TextureInfoUniform = 5, +} + +const wgsl = ` +struct Uniforms { + canvasDimensions: vec2f, +}; + +struct TextureInfoUniform { + spriteSheetSize: vec2f, +} + +struct SpriteInfo { + position: vec2f, + size: vec2f, +}; + +struct Vertex { + @location(0) position: vec2f, +}; + +struct DynamicUnitInfo { + position: vec2f, + dimensions: vec2f, + unused: f32, + textureId: f32, +}; + +struct VSOutput { + @builtin(position) position: vec4f, + @location(0) texcoord: vec2f, +}; + +@group(0) @binding(${BindingId.Uniforms}) var uniforms: Uniforms; +@group(0) @binding(${BindingId.TextureInfoUniform}) var textureInfoUniform: TextureInfoUniform; + +@group(0) @binding(${BindingId.SpriteInfo}) var spriteInfo: array; +@group(0) @binding(${BindingId.DynamicUnitInfo}) var dynamicUnitInfoStructs: array; + +@vertex fn vs( + vert: Vertex, + @builtin(instance_index) instanceIndex: u32, + @builtin(vertex_index) vertexIndex : u32 +) -> VSOutput { + let dynamicUnitInfo = dynamicUnitInfoStructs[instanceIndex]; + let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureId)]; + + var vsOut: VSOutput; + vsOut.position = vec4f( + (((vert.position * 2 - 1) / uniforms.canvasDimensions)) * dynamicUnitInfo.dimensions + dynamicUnitInfo.position, + 0.0, + 1.0 + ); + + // Textures are flipped from natural direction on the y-axis, so flip it back + vsOut.texcoord = vec2f(vert.position.x, 1.0 - vert.position.y); + vsOut.texcoord = ( + // Sprite offset (0-1) + (spriteInfo.position / textureInfoUniform.spriteSheetSize) + + // Sprite coordinate (0-1) + (vsOut.texcoord * (spriteInfo.size / textureInfoUniform.spriteSheetSize)) + ); + + return vsOut; +} + +@group(0) @binding(${BindingId.TextureSampler}) var ourSampler: sampler; +@group(0) @binding(${BindingId.Texture}) var ourTexture: texture_2d; + +@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f { + // var a = textureSample(ourTexture, ourSampler, vsOut.texcoord); + // return vec4f(1.0, 0.0, 0.0, 1.0); + return textureSample(ourTexture, ourSampler, vsOut.texcoord); +} +`; + +export class GpuViewLayerRenderer { + + readonly domNode: HTMLCanvasElement; + host: IVisibleLinesHost; + viewportData: ViewportData; + + private readonly _gpuCtx!: GPUCanvasContext; + + private _adapter!: GPUAdapter; + private _device!: GPUDevice; + private _renderPassDescriptor!: GPURenderPassDescriptor; + private _bindGroup!: GPUBindGroup; + private _pipeline!: GPURenderPipeline; + + private _dataBindBuffer!: GPUBuffer; + private _dataValueBuffers!: ArrayBuffer[]; + private _dataValuesBufferActiveIndex: number = 0; + + private _vertexBuffer!: GPUBuffer; + private _squareVertices!: { vertexData: Float32Array; numVertices: number }; + + private _initialized = false; + + constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { + this.domNode = domNode; + this.host = host; + this.viewportData = viewportData; + + this._gpuCtx = this.domNode.getContext('webgpu')!; + this.initWebgpu(); + } + + async initWebgpu() { + if (!navigator.gpu) { + throw new Error('this browser does not support WebGPU'); + } + + this._adapter = (await navigator.gpu.requestAdapter())!; + if (!this._adapter) { + throw new Error('this browser supports webgpu but it appears disabled'); + } + + this._device = await this._adapter.requestDevice(); + + const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); + this._gpuCtx.configure({ + device: this._device, + format: presentationFormat, + }); + + const module = this._device.createShaderModule({ + label: 'ViewLayer shader module', + code: wgsl, + }); + + this._pipeline = this._device.createRenderPipeline({ + label: 'ViewLayer render pipeline', + layout: 'auto', + vertex: { + module, + entryPoint: 'vs', + buffers: [ + { + arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT, // 2 floats, 4 bytes each + attributes: [ + { shaderLocation: 0, offset: 0, format: 'float32x2' }, // position + ], + } + ] + }, + fragment: { + module, + entryPoint: 'fs', + targets: [ + { + format: presentationFormat, + blend: { + color: { + srcFactor: 'one', + dstFactor: 'one-minus-src-alpha' + }, + alpha: { + srcFactor: 'one', + dstFactor: 'one-minus-src-alpha' + }, + }, + } + ], + }, + }); + + + + // Write standard uniforms + const enum UniformBufferInfo { + Size = 2, // 2x 32 bit floats + OffsetCanvasWidth = 0, + OffsetCanvasHeight = 1 + } + const uniformBuffer = this._device.createBuffer({ + size: UniformBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + { + const uniformValues = new Float32Array(UniformBufferInfo.Size); + // TODO: Update on canvas resize + uniformValues[UniformBufferInfo.OffsetCanvasWidth] = this.domNode.width; + uniformValues[UniformBufferInfo.OffsetCanvasHeight] = this.domNode.height; + this._device.queue.writeBuffer(uniformBuffer, 0, uniformValues); + } + + + // Create texture atlas + const textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); + textureAtlas.getGlyph('ABC', 0); + + + + // Upload texture bitmap from atlas + const textureAtlasGpuTexture = this._device.createTexture({ + format: 'rgba8unorm', + size: { width: textureAtlas.source.width, height: textureAtlas.source.height }, + usage: GPUTextureUsage.TEXTURE_BINDING | + GPUTextureUsage.COPY_DST | + GPUTextureUsage.RENDER_ATTACHMENT, + }); + this._device.queue.copyExternalImageToTexture( + { source: textureAtlas.source }, + { texture: textureAtlasGpuTexture }, + { width: textureAtlas.source.width, height: textureAtlas.source.height }, + ); + + + + const enum TextureInfoUniformBufferInfo { + Size = 2, + SpriteSheetSize = 0, + } + const textureInfoUniformBufferSize = TextureInfoUniformBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; + const textureInfoUniformBuffer = this._device.createBuffer({ + size: textureInfoUniformBufferSize, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + { + const uniformValues = new Float32Array(TextureInfoUniformBufferInfo.Size); + // TODO: Update on canvas resize + uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize] = textureAtlas.source.width; + uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize + 1] = textureAtlas.source.height; + this._device.queue.writeBuffer(textureInfoUniformBuffer, 0, uniformValues); + } + + + const maxRenderedObjects = 10; + + /////////////////// + // Static buffer // + /////////////////// + const enum SpriteInfoStorageBufferInfo { + Size = 2 + 2, + Offset_TexturePosition = 0, + Offset_TextureSize = 2, + } + const spriteInfoStorageBufferByteSize = SpriteInfoStorageBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; + const spriteInfoStorageBuffer = this._device.createBuffer({ + label: 'Entity static info buffer', + size: spriteInfoStorageBufferByteSize * maxRenderedObjects, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }); + { + const sprites: { x: number; y: number; w: number; h: number }[] = [ + { x: 0, y: 0, w: 7, h: 10 }, + { x: 0, y: 0, w: 50, h: 50 } + ]; + const bufferSize = spriteInfoStorageBufferByteSize * sprites.length; + const values = new Float32Array(bufferSize / 4); + let entryOffset = 0; + for (const t of sprites) { + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition] = t.x; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition + 1] = t.y; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize] = t.w; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize + 1] = t.h; + entryOffset += SpriteInfoStorageBufferInfo.Size; + } + this._device.queue.writeBuffer(spriteInfoStorageBuffer, 0, values); + } + + + + const cellCount = 2; + const bufferSize = cellCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; + this._dataBindBuffer = this._device.createBuffer({ + label: 'Entity dynamic info buffer', + size: bufferSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }); + this._dataValueBuffers = [ + new ArrayBuffer(bufferSize), + new ArrayBuffer(bufferSize), + ]; + this._updateSquareVertices(); + + + + const sampler = this._device.createSampler({ + magFilter: 'nearest', + minFilter: 'nearest', + }); + this._bindGroup = this._device.createBindGroup({ + label: 'ViewLayer bind group', + layout: this._pipeline.getBindGroupLayout(0), + entries: [ + { binding: BindingId.SpriteInfo, resource: { buffer: spriteInfoStorageBuffer } }, + { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._dataBindBuffer } }, + { binding: BindingId.TextureSampler, resource: sampler }, + { binding: BindingId.Texture, resource: textureAtlasGpuTexture.createView() }, + { binding: BindingId.Uniforms, resource: { buffer: uniformBuffer } }, + { binding: BindingId.TextureInfoUniform, resource: { buffer: textureInfoUniformBuffer } }, + ], + }); + + this._renderPassDescriptor = { + label: 'ViewLayer render pass', + colorAttachments: [ + ( + { + // view: <- to be filled out when we render + loadValue: [0, 0, 0, 0], + loadOp: 'load', + storeOp: 'store', + } as Omit + ) as any as GPURenderPassColorAttachment, + ] as any as Iterable, + }; + + + this._initialized = true; + } + + private _updateSquareVertices() { + this._squareVertices = { + vertexData: new Float32Array([ + 1, 0, + 1, 1, + 0, 1, + 0, 0, + 0, 1, + 1, 0, + ]), + numVertices: 6 + }; + const { vertexData } = this._squareVertices; + + this._vertexBuffer = this._device.createBuffer({ + label: 'vertex buffer vertices', + size: vertexData.byteLength, + usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, + }); + this._device.queue.writeBuffer(this._vertexBuffer, 0, vertexData); + } + + update(viewportData: ViewportData) { + this.viewportData = viewportData; + } + + public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { + const ctx: IRendererContext = { + rendLineNumberStart: inContext.rendLineNumberStart, + lines: inContext.lines.slice(0), + linesLength: inContext.linesLength + }; + + if (!this._initialized) { + return ctx; + } + return this._renderWebgpu(ctx, startLineNumber, stopLineNumber, deltaTop); + } + + private _renderWebgpu(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { + + const visibleObjectCount = this._updateDataBuffer(); + + // Write buffer and swap it out to unblock writes + const dataBuffer = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); + this._device.queue.writeBuffer(this._dataBindBuffer, 0, dataBuffer, 0, visibleObjectCount * Constants.IndicesPerCell); + + this._dataValuesBufferActiveIndex = (this._dataValuesBufferActiveIndex + 1) % 2; + + const encoder = this._device.createCommandEncoder(); + + (this._renderPassDescriptor.colorAttachments as any)[0].view = this._gpuCtx.getCurrentTexture().createView(); + const pass = encoder.beginRenderPass(this._renderPassDescriptor); + pass.setPipeline(this._pipeline); + pass.setVertexBuffer(0, this._vertexBuffer); + + pass.setBindGroup(0, this._bindGroup); + // TODO: Draws could be split by chunk, this would help minimize moving data around in arrays + pass.draw(this._squareVertices.numVertices, visibleObjectCount); + + pass.end(); + + const commandBuffer = encoder.finish(); + + this._device.queue.submit([commandBuffer]); + + return ctx; + } + + private _updateDataBuffer() { + let screenAbsoluteX: number = 0; + let screenAbsoluteY: number = 0; + let zeroToOneX: number = 0; + let zeroToOneY: number = 0; + let wgslX: number = 0; + let wgslY: number = 0; + + screenAbsoluteX = 100; + screenAbsoluteY = 100; + + screenAbsoluteX = Math.round(screenAbsoluteX); + screenAbsoluteY = Math.round(screenAbsoluteY); + zeroToOneX = screenAbsoluteX / this.domNode.width; + zeroToOneY = screenAbsoluteY / this.domNode.height; + wgslX = zeroToOneX * 2 - 1; + wgslY = zeroToOneY * 2 - 1; + + const offset = 0; + const objectCount = 1; + const data = new Float32Array(objectCount * Constants.IndicesPerCell); + data[offset] = wgslX; // x + data[offset + 1] = -wgslY; // y + data[offset + 2] = 50;// 7; // width + data[offset + 3] = 50;//10; // height + data[offset + 4] = 0; // unused + data[offset + 5] = 1; // textureIndex + + + + const storageValues = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); + storageValues.set(data); + return objectCount; + } +} diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 2c7124b730e..5b79f9cebab 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -6,7 +6,7 @@ import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; import { BugIndicatingError } from 'vs/base/common/errors'; -import { TextureAtlas } from 'vs/editor/browser/view/gpu/textureAtlas'; +import { GpuViewLayerRenderer } from 'vs/editor/browser/view/gpu/gpuViewLayer'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { StringBuilder } from 'vs/editor/common/core/stringBuilder'; import * as viewEvents from 'vs/editor/common/viewEvents'; @@ -354,7 +354,7 @@ export class VisibleLinesCollection { return this._linesCollection.getLine(lineNumber); } - private _canvasRenderer: CanvasViewLayerRenderer | undefined; + private _gpuRenderer: GpuViewLayerRenderer | undefined; public renderLines(viewportData: ViewportData, viewOverlays?: boolean): void { const inp = this._linesCollection._get(); @@ -365,10 +365,10 @@ export class VisibleLinesCollection { } else { this._canvas.width = this.domNode.domNode.clientWidth; this._canvas.height = this.domNode.domNode.clientHeight; - if (!this._canvasRenderer) { - this._canvasRenderer = new CanvasViewLayerRenderer(this._canvas, this._host, viewportData); + if (!this._gpuRenderer) { + this._gpuRenderer = new GpuViewLayerRenderer(this._canvas, this._host, viewportData); } - renderer = this._canvasRenderer; + renderer = this._gpuRenderer; renderer.update(viewportData); } @@ -641,431 +641,3 @@ class ViewLayerRenderer { } } -const enum Constants { - IndicesPerCell = 6 -} - -const enum BindingId { - SpriteInfo = 0, - DynamicUnitInfo = 1, - TextureSampler = 2, - Texture = 3, - Uniforms = 4, - TextureInfoUniform = 5, -} - -const wgsl = ` -struct Uniforms { - canvasDimensions: vec2f, -}; - -struct TextureInfoUniform { - spriteSheetSize: vec2f, -} - -struct SpriteInfo { - position: vec2f, - size: vec2f, -}; - -struct Vertex { - @location(0) position: vec2f, -}; - -struct DynamicUnitInfo { - position: vec2f, - dimensions: vec2f, - unused: f32, - textureId: f32, -}; - -struct VSOutput { - @builtin(position) position: vec4f, - @location(0) texcoord: vec2f, -}; - -@group(0) @binding(${BindingId.Uniforms}) var uniforms: Uniforms; -@group(0) @binding(${BindingId.TextureInfoUniform}) var textureInfoUniform: TextureInfoUniform; - -@group(0) @binding(${BindingId.SpriteInfo}) var spriteInfo: array; -@group(0) @binding(${BindingId.DynamicUnitInfo}) var dynamicUnitInfoStructs: array; - -@vertex fn vs( - vert: Vertex, - @builtin(instance_index) instanceIndex: u32, - @builtin(vertex_index) vertexIndex : u32 -) -> VSOutput { - let dynamicUnitInfo = dynamicUnitInfoStructs[instanceIndex]; - let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureId)]; - - var vsOut: VSOutput; - vsOut.position = vec4f( - (((vert.position * 2 - 1) / uniforms.canvasDimensions)) * dynamicUnitInfo.dimensions + dynamicUnitInfo.position, - 0.0, - 1.0 - ); - - // Textures are flipped from natural direction on the y-axis, so flip it back - vsOut.texcoord = vec2f(vert.position.x, 1.0 - vert.position.y); - vsOut.texcoord = ( - // Sprite offset (0-1) - (spriteInfo.position / textureInfoUniform.spriteSheetSize) + - // Sprite coordinate (0-1) - (vsOut.texcoord * (spriteInfo.size / textureInfoUniform.spriteSheetSize)) - ); - - return vsOut; -} - -@group(0) @binding(${BindingId.TextureSampler}) var ourSampler: sampler; -@group(0) @binding(${BindingId.Texture}) var ourTexture: texture_2d; - -@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f { - // var a = textureSample(ourTexture, ourSampler, vsOut.texcoord); - // return vec4f(1.0, 0.0, 0.0, 1.0); - return textureSample(ourTexture, ourSampler, vsOut.texcoord); -} -`; - -class CanvasViewLayerRenderer { - - readonly domNode: HTMLCanvasElement; - host: IVisibleLinesHost; - viewportData: ViewportData; - - private readonly _gpuCtx!: GPUCanvasContext; - - private _adapter!: GPUAdapter; - private _device!: GPUDevice; - private _renderPassDescriptor!: GPURenderPassDescriptor; - private _bindGroup!: GPUBindGroup; - private _pipeline!: GPURenderPipeline; - - private _dataBindBuffer!: GPUBuffer; - private _dataValueBuffers!: ArrayBuffer[]; - private _dataValuesBufferActiveIndex: number = 0; - - private _vertexBuffer!: GPUBuffer; - private _squareVertices!: { vertexData: Float32Array; numVertices: number }; - - private _initialized = false; - - constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { - this.domNode = domNode; - this.host = host; - this.viewportData = viewportData; - - this._gpuCtx = this.domNode.getContext('webgpu')!; - this.initWebgpu(); - } - - async initWebgpu() { - if (!navigator.gpu) { - throw new Error('this browser does not support WebGPU'); - } - - this._adapter = (await navigator.gpu.requestAdapter())!; - if (!this._adapter) { - throw new Error('this browser supports webgpu but it appears disabled'); - } - - this._device = await this._adapter.requestDevice(); - - const presentationFormat = navigator.gpu.getPreferredCanvasFormat(); - this._gpuCtx.configure({ - device: this._device, - format: presentationFormat, - }); - - const module = this._device.createShaderModule({ - label: 'ViewLayer shader module', - code: wgsl, - }); - - this._pipeline = this._device.createRenderPipeline({ - label: 'ViewLayer render pipeline', - layout: 'auto', - vertex: { - module, - entryPoint: 'vs', - buffers: [ - { - arrayStride: 2 * Float32Array.BYTES_PER_ELEMENT, // 2 floats, 4 bytes each - attributes: [ - { shaderLocation: 0, offset: 0, format: 'float32x2' }, // position - ], - } - ] - }, - fragment: { - module, - entryPoint: 'fs', - targets: [ - { - format: presentationFormat, - blend: { - color: { - srcFactor: 'one', - dstFactor: 'one-minus-src-alpha' - }, - alpha: { - srcFactor: 'one', - dstFactor: 'one-minus-src-alpha' - }, - }, - } - ], - }, - }); - - - - // Write standard uniforms - const enum UniformBufferInfo { - Size = 2, // 2x 32 bit floats - OffsetCanvasWidth = 0, - OffsetCanvasHeight = 1 - } - const uniformBuffer = this._device.createBuffer({ - size: UniformBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT, - usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, - }); - { - const uniformValues = new Float32Array(UniformBufferInfo.Size); - // TODO: Update on canvas resize - uniformValues[UniformBufferInfo.OffsetCanvasWidth] = this.domNode.width; - uniformValues[UniformBufferInfo.OffsetCanvasHeight] = this.domNode.height; - this._device.queue.writeBuffer(uniformBuffer, 0, uniformValues); - } - - - // Create texture atlas - const textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); - textureAtlas.getGlyph('ABC', 0); - - - - // Upload texture bitmap from atlas - const textureAtlasGpuTexture = this._device.createTexture({ - format: 'rgba8unorm', - size: { width: textureAtlas.source.width, height: textureAtlas.source.height }, - usage: GPUTextureUsage.TEXTURE_BINDING | - GPUTextureUsage.COPY_DST | - GPUTextureUsage.RENDER_ATTACHMENT, - }); - this._device.queue.copyExternalImageToTexture( - { source: textureAtlas.source }, - { texture: textureAtlasGpuTexture }, - { width: textureAtlas.source.width, height: textureAtlas.source.height }, - ); - - - - const enum TextureInfoUniformBufferInfo { - Size = 2, - SpriteSheetSize = 0, - } - const textureInfoUniformBufferSize = TextureInfoUniformBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; - const textureInfoUniformBuffer = this._device.createBuffer({ - size: textureInfoUniformBufferSize, - usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, - }); - { - const uniformValues = new Float32Array(TextureInfoUniformBufferInfo.Size); - // TODO: Update on canvas resize - uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize] = textureAtlas.source.width; - uniformValues[TextureInfoUniformBufferInfo.SpriteSheetSize + 1] = textureAtlas.source.height; - this._device.queue.writeBuffer(textureInfoUniformBuffer, 0, uniformValues); - } - - - const maxRenderedObjects = 10; - - /////////////////// - // Static buffer // - /////////////////// - const enum SpriteInfoStorageBufferInfo { - Size = 2 + 2, - Offset_TexturePosition = 0, - Offset_TextureSize = 2, - } - const spriteInfoStorageBufferByteSize = SpriteInfoStorageBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; - const spriteInfoStorageBuffer = this._device.createBuffer({ - label: 'Entity static info buffer', - size: spriteInfoStorageBufferByteSize * maxRenderedObjects, - usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - }); - { - const sprites: { x: number; y: number; w: number; h: number }[] = [ - { x: 0, y: 0, w: 7, h: 10 }, - { x: 0, y: 0, w: 50, h: 50 } - ]; - const bufferSize = spriteInfoStorageBufferByteSize * sprites.length; - const values = new Float32Array(bufferSize / 4); - let entryOffset = 0; - for (const t of sprites) { - values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition] = t.x; - values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition + 1] = t.y; - values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize] = t.w; - values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize + 1] = t.h; - entryOffset += SpriteInfoStorageBufferInfo.Size; - } - this._device.queue.writeBuffer(spriteInfoStorageBuffer, 0, values); - } - - - - const cellCount = 2; - const bufferSize = cellCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; - this._dataBindBuffer = this._device.createBuffer({ - label: 'Entity dynamic info buffer', - size: bufferSize, - usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - }); - this._dataValueBuffers = [ - new ArrayBuffer(bufferSize), - new ArrayBuffer(bufferSize), - ]; - this._updateSquareVertices(); - - - - const sampler = this._device.createSampler({ - magFilter: 'nearest', - minFilter: 'nearest', - }); - this._bindGroup = this._device.createBindGroup({ - label: 'ViewLayer bind group', - layout: this._pipeline.getBindGroupLayout(0), - entries: [ - { binding: BindingId.SpriteInfo, resource: { buffer: spriteInfoStorageBuffer } }, - { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._dataBindBuffer } }, - { binding: BindingId.TextureSampler, resource: sampler }, - { binding: BindingId.Texture, resource: textureAtlasGpuTexture.createView() }, - { binding: BindingId.Uniforms, resource: { buffer: uniformBuffer } }, - { binding: BindingId.TextureInfoUniform, resource: { buffer: textureInfoUniformBuffer } }, - ], - }); - - this._renderPassDescriptor = { - label: 'ViewLayer render pass', - colorAttachments: [ - ( - { - // view: <- to be filled out when we render - loadValue: [0, 0, 0, 0], - loadOp: 'load', - storeOp: 'store', - } as Omit - ) as any as GPURenderPassColorAttachment, - ] as any as Iterable, - }; - - - this._initialized = true; - } - - private _updateSquareVertices() { - this._squareVertices = { - vertexData: new Float32Array([ - 1, 0, - 1, 1, - 0, 1, - 0, 0, - 0, 1, - 1, 0, - ]), - numVertices: 6 - }; - const { vertexData } = this._squareVertices; - - this._vertexBuffer = this._device.createBuffer({ - label: 'vertex buffer vertices', - size: vertexData.byteLength, - usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST, - }); - this._device.queue.writeBuffer(this._vertexBuffer, 0, vertexData); - } - - update(viewportData: ViewportData) { - this.viewportData = viewportData; - } - - public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { - const ctx: IRendererContext = { - rendLineNumberStart: inContext.rendLineNumberStart, - lines: inContext.lines.slice(0), - linesLength: inContext.linesLength - }; - - if (!this._initialized) { - return ctx; - } - return this._renderWebgpu(ctx, startLineNumber, stopLineNumber, deltaTop); - } - - private _renderWebgpu(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { - - const visibleObjectCount = this._updateDataBuffer(); - - // Write buffer and swap it out to unblock writes - const dataBuffer = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); - this._device.queue.writeBuffer(this._dataBindBuffer, 0, dataBuffer, 0, visibleObjectCount * Constants.IndicesPerCell); - - this._dataValuesBufferActiveIndex = (this._dataValuesBufferActiveIndex + 1) % 2; - - const encoder = this._device.createCommandEncoder(); - - (this._renderPassDescriptor.colorAttachments as any)[0].view = this._gpuCtx.getCurrentTexture().createView(); - const pass = encoder.beginRenderPass(this._renderPassDescriptor); - pass.setPipeline(this._pipeline); - pass.setVertexBuffer(0, this._vertexBuffer); - - pass.setBindGroup(0, this._bindGroup); - // TODO: Draws could be split by chunk, this would help minimize moving data around in arrays - pass.draw(this._squareVertices.numVertices, visibleObjectCount); - - pass.end(); - - const commandBuffer = encoder.finish(); - - this._device.queue.submit([commandBuffer]); - - return ctx; - } - - private _updateDataBuffer() { - let screenAbsoluteX: number = 0; - let screenAbsoluteY: number = 0; - let zeroToOneX: number = 0; - let zeroToOneY: number = 0; - let wgslX: number = 0; - let wgslY: number = 0; - - screenAbsoluteX = 100; - screenAbsoluteY = 100; - - screenAbsoluteX = Math.round(screenAbsoluteX); - screenAbsoluteY = Math.round(screenAbsoluteY); - zeroToOneX = screenAbsoluteX / this.domNode.width; - zeroToOneY = screenAbsoluteY / this.domNode.height; - wgslX = zeroToOneX * 2 - 1; - wgslY = zeroToOneY * 2 - 1; - - const offset = 0; - const objectCount = 1; - const data = new Float32Array(objectCount * Constants.IndicesPerCell); - data[offset] = wgslX; // x - data[offset + 1] = -wgslY; // y - data[offset + 2] = 50;// 7; // width - data[offset + 3] = 50;//10; // height - data[offset + 4] = 0; // unused - data[offset + 5] = 1; // textureIndex - - - - const storageValues = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); - storageValues.set(data); - return objectCount; - } -} From 6273f2b99a7a2745b029d2d827992e82d63d4633 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 24 Mar 2024 06:30:25 -0700 Subject: [PATCH 0007/2222] Render using glyph dims --- .../editor/browser/view/gpu/gpuViewLayer.ts | 58 +++++++++---------- .../editor/browser/view/gpu/textureAtlas.ts | 27 +++++---- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index e9e8fd9d819..2f911dca8b7 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextureAtlas } from 'vs/editor/browser/view/gpu/textureAtlas'; +import { TextureAtlas, type ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; import type { IVisibleLine, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; @@ -14,10 +14,11 @@ interface IRendererContext { } const enum Constants { - IndicesPerCell = 6 + IndicesPerCell = 3 } const enum BindingId { + // TODO: Improve names SpriteInfo = 0, DynamicUnitInfo = 1, TextureSampler = 2, @@ -46,8 +47,6 @@ struct Vertex { struct DynamicUnitInfo { position: vec2f, - dimensions: vec2f, - unused: f32, textureId: f32, }; @@ -72,7 +71,7 @@ struct VSOutput { var vsOut: VSOutput; vsOut.position = vec4f( - (((vert.position * 2 - 1) / uniforms.canvasDimensions)) * dynamicUnitInfo.dimensions + dynamicUnitInfo.position, + (((vert.position * 2 - 1) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position, 0.0, 1.0 ); @@ -213,23 +212,6 @@ export class GpuViewLayerRenderer { // Create texture atlas const textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); - textureAtlas.getGlyph('ABC', 0); - - - - // Upload texture bitmap from atlas - const textureAtlasGpuTexture = this._device.createTexture({ - format: 'rgba8unorm', - size: { width: textureAtlas.source.width, height: textureAtlas.source.height }, - usage: GPUTextureUsage.TEXTURE_BINDING | - GPUTextureUsage.COPY_DST | - GPUTextureUsage.RENDER_ATTACHMENT, - }); - this._device.queue.copyExternalImageToTexture( - { source: textureAtlas.source }, - { texture: textureAtlasGpuTexture }, - { width: textureAtlas.source.width, height: textureAtlas.source.height }, - ); @@ -268,9 +250,11 @@ export class GpuViewLayerRenderer { usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, }); { - const sprites: { x: number; y: number; w: number; h: number }[] = [ - { x: 0, y: 0, w: 7, h: 10 }, - { x: 0, y: 0, w: 50, h: 50 } + const glyph = textureAtlas.getGlyph('ABC', 0); + const sprites: ITextureAtlasGlyph[] = [ + glyph, + glyph + // { x: 0, y: 0, w: 50, h: 50 }, ]; const bufferSize = spriteInfoStorageBufferByteSize * sprites.length; const values = new Float32Array(bufferSize / 4); @@ -287,6 +271,25 @@ export class GpuViewLayerRenderer { + + + + // Upload texture bitmap from atlas + const textureAtlasGpuTexture = this._device.createTexture({ + format: 'rgba8unorm', + size: { width: textureAtlas.source.width, height: textureAtlas.source.height }, + usage: GPUTextureUsage.TEXTURE_BINDING | + GPUTextureUsage.COPY_DST | + GPUTextureUsage.RENDER_ATTACHMENT, + }); + this._device.queue.copyExternalImageToTexture( + { source: textureAtlas.source }, + { texture: textureAtlasGpuTexture }, + { width: textureAtlas.source.width, height: textureAtlas.source.height }, + ); + + + const cellCount = 2; const bufferSize = cellCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; this._dataBindBuffer = this._device.createBuffer({ @@ -429,10 +432,7 @@ export class GpuViewLayerRenderer { const data = new Float32Array(objectCount * Constants.IndicesPerCell); data[offset] = wgslX; // x data[offset + 1] = -wgslY; // y - data[offset + 2] = 50;// 7; // width - data[offset + 3] = 50;//10; // height - data[offset + 4] = 0; // unused - data[offset + 5] = 1; // textureIndex + data[offset + 2] = 1; // textureIndex diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 266b3ccf585..86ee8896067 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -26,13 +26,13 @@ export class TextureAtlas extends Disposable { this._canvas = new OffscreenCanvas(maxTextureSize, maxTextureSize); this._ctx = ensureNonNullable(this._canvas.getContext('2d')); - const style = getActiveWindow().getComputedStyle(parentDomNode); - this._ctx.font = `${style.fontSize} ${style.fontFamily}`; + const activeWindow = getActiveWindow(); + const style = activeWindow.getComputedStyle(parentDomNode); + const fontSize = Math.ceil(parseInt(style.fontSize.replace('px', '')) * activeWindow.devicePixelRatio); + this._ctx.font = `${fontSize}px ${style.fontFamily}`; this._ctx.textBaseline = 'top'; - // TODO: Device pixel ratio? - const fontSize = parseInt(style.fontSize.replace('px', '')); - this._glyphRasterizer = new GlyphRasterizer(fontSize); + this._glyphRasterizer = new GlyphRasterizer(fontSize, style.fontFamily); // Reduce impact of a memory leak if this object is not released this._register(toDisposable(() => { @@ -64,8 +64,8 @@ export class TextureAtlas extends Disposable { glyph = { x: 0, y: 0, - width: rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, - height: rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + w: rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, + h: rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top }; console.log('Allocating glyph', { rasterizedGlyph, @@ -82,18 +82,23 @@ class GlyphRasterizer extends Disposable { // A temporary context that glyphs are drawn to before being transfered to the atlas. private _ctx: OffscreenCanvasRenderingContext2D; - constructor(private readonly _fontSize: number) { + constructor(private readonly _fontSize: number, fontFamily: string) { super(); this._canvas = new OffscreenCanvas(this._fontSize * 3, this._fontSize * 3); this._ctx = ensureNonNullable(this._canvas.getContext('2d')); + this._ctx.font = `${this._fontSize}px ${fontFamily}`; + this._ctx.fillStyle = '#00FF00'; } + // TODO: Support drawing multiple fonts and sizes + // TODO: Should pull in the font size from config instead of random dom node public rasterizeGlyph(chars: string): IRasterizedGlyph { this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); - this._ctx.fillStyle = '#00FF00'; + // TODO: Draw in middle using alphabetical baseline this._ctx.fillText(chars, this._fontSize, this._fontSize); + const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); // TODO: Hot path: Reuse object const result: IRasterizedGlyph = { @@ -180,8 +185,8 @@ class GlyphRasterizer extends Disposable { export interface ITextureAtlasGlyph { x: number; y: number; - width: number; - height: number; + w: number; + h: number; } interface IBoundingBox { From 207fb91a209608de35e6688a1a192a3e5d7e345f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 24 Mar 2024 06:43:13 -0700 Subject: [PATCH 0008/2222] Fix text alpha --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 2f911dca8b7..4386a0217eb 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -176,11 +176,11 @@ export class GpuViewLayerRenderer { format: presentationFormat, blend: { color: { - srcFactor: 'one', + srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha' }, alpha: { - srcFactor: 'one', + srcFactor: 'src-alpha', dstFactor: 'one-minus-src-alpha' }, }, From cf4008bb7463b195a6acb3efef0761fb609b8fce Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 24 Mar 2024 06:44:56 -0700 Subject: [PATCH 0009/2222] Fix canvas scale --- src/vs/editor/browser/view/viewLayer.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 5b79f9cebab..6798fd03968 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getActiveWindow } from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; import { BugIndicatingError } from 'vs/base/common/errors'; @@ -363,8 +364,9 @@ export class VisibleLinesCollection { if (viewOverlays) { renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData); } else { - this._canvas.width = this.domNode.domNode.clientWidth; - this._canvas.height = this.domNode.domNode.clientHeight; + const activeWindow = getActiveWindow(); + this._canvas.width = this.domNode.domNode.clientWidth * activeWindow.devicePixelRatio; + this._canvas.height = this.domNode.domNode.clientHeight * activeWindow.devicePixelRatio; if (!this._gpuRenderer) { this._gpuRenderer = new GpuViewLayerRenderer(this._canvas, this._host, viewportData); } From 239c15e4a972aa9079c77a0e78fa5b3e3cd30113 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 24 Mar 2024 06:46:30 -0700 Subject: [PATCH 0010/2222] Exit early on hot path --- .../editor/browser/view/gpu/textureAtlas.ts | 29 ++++++++++--------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 86ee8896067..d02ce16bb13 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -59,20 +59,21 @@ export class TextureAtlas extends Disposable { rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top ); let glyph: ITextureAtlasGlyph | undefined = this._glyphMap.get(chars); - if (!glyph) { - // TODO: Implement allocation - glyph = { - x: 0, - y: 0, - w: rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, - h: rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top - }; - console.log('Allocating glyph', { - rasterizedGlyph, - glyph - }); - this._glyphMap.set(chars, glyph); + if (glyph) { + return glyph; } + // TODO: Implement allocation + glyph = { + x: 0, + y: 0, + w: rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, + h: rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + }; + console.log('Allocating glyph', { + rasterizedGlyph, + glyph + }); + this._glyphMap.set(chars, glyph); return glyph; } } @@ -88,7 +89,7 @@ class GlyphRasterizer extends Disposable { this._canvas = new OffscreenCanvas(this._fontSize * 3, this._fontSize * 3); this._ctx = ensureNonNullable(this._canvas.getContext('2d')); this._ctx.font = `${this._fontSize}px ${fontFamily}`; - this._ctx.fillStyle = '#00FF00'; + this._ctx.fillStyle = '#FFFFFF'; } // TODO: Support drawing multiple fonts and sizes From ab97abc95b99be79a76819bd5e2d5c587def57f8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 24 Mar 2024 06:53:21 -0700 Subject: [PATCH 0011/2222] Track device pixel dimensions of canvas --- src/vs/editor/browser/view/gpu/gpuUtils.ts | 36 +++++++++++++++++++ .../editor/browser/view/gpu/gpuViewLayer.ts | 2 -- .../editor/browser/view/gpu/textureAtlas.ts | 1 - src/vs/editor/browser/view/viewLayer.ts | 15 +++++--- 4 files changed, 47 insertions(+), 7 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuUtils.ts b/src/vs/editor/browser/view/gpu/gpuUtils.ts index 4f1d64edb0c..66497f1c205 100644 --- a/src/vs/editor/browser/view/gpu/gpuUtils.ts +++ b/src/vs/editor/browser/view/gpu/gpuUtils.ts @@ -3,9 +3,45 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { toDisposable, type IDisposable } from 'vs/base/common/lifecycle'; + export function ensureNonNullable(value: T | null): T { if (!value) { throw new Error(`Value "${value}" cannot be null`); } return value; } + +export function observeDevicePixelDimensions(element: HTMLElement, parentWindow: Window & typeof globalThis, callback: (deviceWidth: number, deviceHeight: number) => void): IDisposable { + // Observe any resizes to the element and extract the actual pixel size of the element if the + // devicePixelContentBoxSize API is supported. This allows correcting rounding errors when + // converting between CSS pixels and device pixels which causes blurry rendering when device + // pixel ratio is not a round number. + let observer: ResizeObserver | undefined = new parentWindow.ResizeObserver((entries) => { + const entry = entries.find((entry) => entry.target === element); + if (!entry) { + return; + } + + // Disconnect if devicePixelContentBoxSize isn't supported by the browser + if (!('devicePixelContentBoxSize' in entry)) { + observer?.disconnect(); + observer = undefined; + return; + } + + // Fire the callback, ignore events where the dimensions are 0x0 as the canvas is likely hidden + const width = entry.devicePixelContentBoxSize[0].inlineSize; + const height = entry.devicePixelContentBoxSize[0].blockSize; + if (width > 0 && height > 0) { + callback(width, height); + } + }); + try { + observer.observe(element, { box: ['device-pixel-content-box'] } as any); + } catch { + observer.disconnect(); + observer = undefined; + } + return toDisposable(() => observer?.disconnect()); +} diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 4386a0217eb..a96cdb33ab3 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -434,8 +434,6 @@ export class GpuViewLayerRenderer { data[offset + 1] = -wgslY; // y data[offset + 2] = 1; // textureIndex - - const storageValues = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); storageValues.set(data); return objectCount; diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index d02ce16bb13..e52bd360d41 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -30,7 +30,6 @@ export class TextureAtlas extends Disposable { const style = activeWindow.getComputedStyle(parentDomNode); const fontSize = Math.ceil(parseInt(style.fontSize.replace('px', '')) * activeWindow.devicePixelRatio); this._ctx.font = `${fontSize}px ${style.fontFamily}`; - this._ctx.textBaseline = 'top'; this._glyphRasterizer = new GlyphRasterizer(fontSize, style.fontFamily); diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 6798fd03968..948a739b4fd 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -7,6 +7,7 @@ import { getActiveWindow } from 'vs/base/browser/dom'; import { FastDomNode, createFastDomNode } from 'vs/base/browser/fastDomNode'; import { createTrustedTypesPolicy } from 'vs/base/browser/trustedTypes'; import { BugIndicatingError } from 'vs/base/common/errors'; +import { observeDevicePixelDimensions } from 'vs/editor/browser/view/gpu/gpuUtils'; import { GpuViewLayerRenderer } from 'vs/editor/browser/view/gpu/gpuViewLayer'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { StringBuilder } from 'vs/editor/common/core/stringBuilder'; @@ -266,7 +267,6 @@ export class VisibleLinesCollection { this._canvas = document.createElement('canvas'); this._canvas.style.height = '100%'; this._canvas.style.width = '100%'; - this.domNode.domNode.appendChild(this._canvas); this._linesCollection = new RenderedLinesCollection(() => this._host.createVisibleLine()); } @@ -364,9 +364,16 @@ export class VisibleLinesCollection { if (viewOverlays) { renderer = new ViewLayerRenderer(this.domNode.domNode, this._host, viewportData); } else { - const activeWindow = getActiveWindow(); - this._canvas.width = this.domNode.domNode.clientWidth * activeWindow.devicePixelRatio; - this._canvas.height = this.domNode.domNode.clientHeight * activeWindow.devicePixelRatio; + // If not yet attached, listen for device pixel size and attach + if (!this._canvas.parentElement) { + // TODO: Track disposable + observeDevicePixelDimensions(this._canvas, getActiveWindow(), (w, h) => { + this._canvas.width = w; + this._canvas.height = h; + }); + this.domNode.domNode.appendChild(this._canvas); + } + if (!this._gpuRenderer) { this._gpuRenderer = new GpuViewLayerRenderer(this._canvas, this._host, viewportData); } From bcb90fc6f23e750afe3673c28ca8885deea4a3d9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 24 Mar 2024 08:48:08 -0700 Subject: [PATCH 0012/2222] Render characters in approximately the right position --- .../editor/browser/view/gpu/gpuViewLayer.ts | 112 +++++++++++++----- .../editor/browser/view/gpu/textureAtlas.ts | 5 + 2 files changed, 86 insertions(+), 31 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index a96cdb33ab3..b5270ce35cf 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getActiveWindow } from 'vs/base/browser/dom'; import { TextureAtlas, type ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; import type { IVisibleLine, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; @@ -14,7 +15,7 @@ interface IRendererContext { } const enum Constants { - IndicesPerCell = 3 + IndicesPerCell = 6 } const enum BindingId { @@ -47,7 +48,9 @@ struct Vertex { struct DynamicUnitInfo { position: vec2f, + unused1: vec2f, textureId: f32, + unused2: f32 }; struct VSOutput { @@ -70,14 +73,15 @@ struct VSOutput { let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureId)]; var vsOut: VSOutput; + // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 vsOut.position = vec4f( - (((vert.position * 2 - 1) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position, + (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position, 0.0, 1.0 ); // Textures are flipped from natural direction on the y-axis, so flip it back - vsOut.texcoord = vec2f(vert.position.x, 1.0 - vert.position.y); + vsOut.texcoord = vert.position; vsOut.texcoord = ( // Sprite offset (0-1) (spriteInfo.position / textureInfoUniform.spriteSheetSize) + @@ -92,8 +96,6 @@ struct VSOutput { @group(0) @binding(${BindingId.Texture}) var ourTexture: texture_2d; @fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f { - // var a = textureSample(ourTexture, ourSampler, vsOut.texcoord); - // return vec4f(1.0, 0.0, 0.0, 1.0); return textureSample(ourTexture, ourSampler, vsOut.texcoord); } `; @@ -119,6 +121,8 @@ export class GpuViewLayerRenderer { private _vertexBuffer!: GPUBuffer; private _squareVertices!: { vertexData: Float32Array; numVertices: number }; + private _textureAtlas!: TextureAtlas; + private _initialized = false; constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { @@ -211,7 +215,7 @@ export class GpuViewLayerRenderer { // Create texture atlas - const textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); + const textureAtlas = this._textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); @@ -290,7 +294,8 @@ export class GpuViewLayerRenderer { - const cellCount = 2; + // TODO: Grow/shrink buffer size dynamically + const cellCount = 10000; const bufferSize = cellCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; this._dataBindBuffer = this._device.createBuffer({ label: 'Entity dynamic info buffer', @@ -380,11 +385,11 @@ export class GpuViewLayerRenderer { } private _renderWebgpu(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { - - const visibleObjectCount = this._updateDataBuffer(); + // TODO: Improve "data" name + const dataBuffer = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); + const visibleObjectCount = this._updateDataBuffer(dataBuffer, ctx, startLineNumber, stopLineNumber, deltaTop); // Write buffer and swap it out to unblock writes - const dataBuffer = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); this._device.queue.writeBuffer(this._dataBindBuffer, 0, dataBuffer, 0, visibleObjectCount * Constants.IndicesPerCell); this._dataValuesBufferActiveIndex = (this._dataValuesBufferActiveIndex + 1) % 2; @@ -409,7 +414,9 @@ export class GpuViewLayerRenderer { return ctx; } - private _updateDataBuffer() { + // TODO: This update could be moved to an arbitrary task thread if expensive? + private _updateDataBuffer(dataBuffer: Float32Array, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { + let chars: string = ''; let screenAbsoluteX: number = 0; let screenAbsoluteY: number = 0; let zeroToOneX: number = 0; @@ -417,25 +424,68 @@ export class GpuViewLayerRenderer { let wgslX: number = 0; let wgslY: number = 0; - screenAbsoluteX = 100; - screenAbsoluteY = 100; - - screenAbsoluteX = Math.round(screenAbsoluteX); - screenAbsoluteY = Math.round(screenAbsoluteY); - zeroToOneX = screenAbsoluteX / this.domNode.width; - zeroToOneY = screenAbsoluteY / this.domNode.height; - wgslX = zeroToOneX * 2 - 1; - wgslY = zeroToOneY * 2 - 1; - - const offset = 0; - const objectCount = 1; - const data = new Float32Array(objectCount * Constants.IndicesPerCell); - data[offset] = wgslX; // x - data[offset + 1] = -wgslY; // y - data[offset + 2] = 1; // textureIndex - - const storageValues = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); - storageValues.set(data); - return objectCount; + const activeWindow = getActiveWindow(); + let charCount = 0; + let scrollTop = parseInt(this.domNode.parentElement!.getAttribute('data-adjusted-scroll-top')!); + if (Number.isNaN(scrollTop)) { + scrollTop = 0; + } + for (let lineNumber = startLineNumber; lineNumber <= stopLineNumber; lineNumber++) { + const y = Math.round((-scrollTop + deltaTop[lineNumber - startLineNumber])); + // Offscreen + if (y < 0) { + continue; + } + const content = this.viewportData.getViewLineRenderingData(lineNumber).content; + // console.log(content, 0, y); + for (let x = 0; x < content.length; x++) { + if (content.charAt(x) === ' ') { + continue; + } + // TODO: Handle tab + + chars = content[x]; + // TODO: Get glyph + + // TODO: Move math to gpu + // TODO: Render using a line offset for partial line scrolling + // TODO: Sub-pixel rendering + screenAbsoluteX = x * 7 * activeWindow.devicePixelRatio; + // TODO: This +10 is because the glyph is being rendered in the wrong position + screenAbsoluteY = Math.round(y * activeWindow.devicePixelRatio); + zeroToOneX = screenAbsoluteX / this.domNode.width; + zeroToOneY = screenAbsoluteY / this.domNode.height; + wgslX = zeroToOneX * 2 - 1; + wgslY = zeroToOneY * 2 - 1; + + dataBuffer[charCount * Constants.IndicesPerCell + 0] = wgslX; // x + dataBuffer[charCount * Constants.IndicesPerCell + 1] = -wgslY; // y + dataBuffer[charCount * Constants.IndicesPerCell + 2] = 0; + dataBuffer[charCount * Constants.IndicesPerCell + 3] = 0; + dataBuffer[charCount * Constants.IndicesPerCell + 4] = 1; // textureIndex + dataBuffer[charCount * Constants.IndicesPerCell + 5] = 0; + + charCount++; + } + } + // console.log('charCount: ' + charCount); + return charCount; + + + + + // screenAbsoluteX = 100; + // screenAbsoluteY = 100; + + + // const offset = 0; + // const objectCount = 1; + // const data = new Float32Array(objectCount * Constants.IndicesPerCell); + // data[offset] = wgslX; // x + // data[offset + 1] = -wgslY; // y + // data[offset + 2] = 1; // textureIndex + + // storageValues.set(data); + // return objectCount; } } diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index e52bd360d41..aee130db4cc 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -15,6 +15,8 @@ export class TextureAtlas extends Disposable { private _glyphRasterizer: GlyphRasterizer; + private _nextId = 0; + public get source(): OffscreenCanvas { return this._canvas; } @@ -63,6 +65,7 @@ export class TextureAtlas extends Disposable { } // TODO: Implement allocation glyph = { + id: this._nextId++, x: 0, y: 0, w: rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, @@ -108,6 +111,7 @@ class GlyphRasterizer extends Disposable { return result; } + // TODO: Pass back origin offset private _findGlyphBoundingBox(imageData: ImageData): IBoundingBox { // TODO: Hot path: Reuse object const boundingBox = { @@ -183,6 +187,7 @@ class GlyphRasterizer extends Disposable { } export interface ITextureAtlasGlyph { + id: number; x: number; y: number; w: number; From 738438cfd8474eea16440fc1a064c2d879f7e54a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 24 Mar 2024 08:54:34 -0700 Subject: [PATCH 0013/2222] Fix glyph caching order --- .../editor/browser/view/gpu/gpuViewLayer.ts | 21 ++----------------- .../editor/browser/view/gpu/textureAtlas.ts | 11 +++++----- 2 files changed, 8 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index b5270ce35cf..1aeea9fb7c8 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -445,7 +445,7 @@ export class GpuViewLayerRenderer { // TODO: Handle tab chars = content[x]; - // TODO: Get glyph + const glyph = this._textureAtlas.getGlyph(content, x); // TODO: Move math to gpu // TODO: Render using a line offset for partial line scrolling @@ -462,7 +462,7 @@ export class GpuViewLayerRenderer { dataBuffer[charCount * Constants.IndicesPerCell + 1] = -wgslY; // y dataBuffer[charCount * Constants.IndicesPerCell + 2] = 0; dataBuffer[charCount * Constants.IndicesPerCell + 3] = 0; - dataBuffer[charCount * Constants.IndicesPerCell + 4] = 1; // textureIndex + dataBuffer[charCount * Constants.IndicesPerCell + 4] = glyph.id; // textureIndex dataBuffer[charCount * Constants.IndicesPerCell + 5] = 0; charCount++; @@ -470,22 +470,5 @@ export class GpuViewLayerRenderer { } // console.log('charCount: ' + charCount); return charCount; - - - - - // screenAbsoluteX = 100; - // screenAbsoluteY = 100; - - - // const offset = 0; - // const objectCount = 1; - // const data = new Float32Array(objectCount * Constants.IndicesPerCell); - // data[offset] = wgslX; // x - // data[offset + 1] = -wgslY; // y - // data[offset + 2] = 1; // textureIndex - - // storageValues.set(data); - // return objectCount; } } diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index aee130db4cc..a141ff07a27 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -45,6 +45,10 @@ export class TextureAtlas extends Disposable { // TODO: Color, style etc. public getGlyph(lineContent: string, glyphIndex: number): ITextureAtlasGlyph { const chars = lineContent.charAt(glyphIndex); + let glyph: ITextureAtlasGlyph | undefined = this._glyphMap.get(chars); + if (glyph) { + return glyph; + } const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars); this._ctx.drawImage( rasterizedGlyph.source, @@ -59,13 +63,10 @@ export class TextureAtlas extends Disposable { rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top ); - let glyph: ITextureAtlasGlyph | undefined = this._glyphMap.get(chars); - if (glyph) { - return glyph; - } // TODO: Implement allocation glyph = { - id: this._nextId++, + // TODO: Set real id + id: 1, //this._nextId++, x: 0, y: 0, w: rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, From 781654e3a54dbeefa7fd0374d8db2faa858ea83d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 Mar 2024 06:17:46 -0700 Subject: [PATCH 0014/2222] Implement most of a simple texture allocator --- .../editor/browser/view/gpu/gpuViewLayer.ts | 120 +++++++++++------- .../editor/browser/view/gpu/textureAtlas.ts | 58 ++++----- .../browser/view/gpu/textureAtlasAllocator.ts | 76 +++++++++++ 3 files changed, 175 insertions(+), 79 deletions(-) create mode 100644 src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 1aeea9fb7c8..ed9b1973156 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -3,8 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getActiveWindow } from 'vs/base/browser/dom'; -import { TextureAtlas, type ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; +import { getActiveDocument, getActiveWindow } from 'vs/base/browser/dom'; +import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; +import { TextureAtlas } from 'vs/editor/browser/view/gpu/textureAtlas'; import type { IVisibleLine, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; @@ -18,6 +19,13 @@ const enum Constants { IndicesPerCell = 6 } +const enum SpriteInfoStorageBufferInfo { + Size = 2 + 2, + Offset_TexturePosition = 0, + Offset_TextureSize = 2, +} +const spriteInfoStorageBufferByteSize = SpriteInfoStorageBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; + const enum BindingId { // TODO: Improve names SpriteInfo = 0, @@ -122,9 +130,17 @@ export class GpuViewLayerRenderer { private _squareVertices!: { vertexData: Float32Array; numVertices: number }; private _textureAtlas!: TextureAtlas; + private _spriteInfoStorageBuffer!: GPUBuffer; + private _textureAtlasGpuTexture!: GPUTexture; private _initialized = false; + + + private readonly _testCanvas: HTMLCanvasElement; + private readonly _testCtx: CanvasRenderingContext2D; + + constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { this.domNode = domNode; this.host = host; @@ -132,6 +148,19 @@ export class GpuViewLayerRenderer { this._gpuCtx = this.domNode.getContext('webgpu')!; this.initWebgpu(); + + + + this._testCanvas = document.createElement('canvas'); + this._testCanvas.width = 2048; + this._testCanvas.height = 2048; + this._testCanvas.style.position = 'absolute'; + this._testCanvas.style.top = '0'; + this._testCanvas.style.left = '0'; + this._testCanvas.style.zIndex = '10000'; + this._testCanvas.style.pointerEvents = 'none'; + this._testCtx = ensureNonNullable(this._testCanvas.getContext('2d')); + getActiveDocument().body.appendChild(this._testCanvas); } async initWebgpu() { @@ -237,60 +266,34 @@ export class GpuViewLayerRenderer { } - const maxRenderedObjects = 10; + const maxRenderedObjects = 10000; /////////////////// // Static buffer // /////////////////// - const enum SpriteInfoStorageBufferInfo { - Size = 2 + 2, - Offset_TexturePosition = 0, - Offset_TextureSize = 2, - } - const spriteInfoStorageBufferByteSize = SpriteInfoStorageBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; - const spriteInfoStorageBuffer = this._device.createBuffer({ + this._spriteInfoStorageBuffer = this._device.createBuffer({ label: 'Entity static info buffer', size: spriteInfoStorageBufferByteSize * maxRenderedObjects, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, }); - { - const glyph = textureAtlas.getGlyph('ABC', 0); - const sprites: ITextureAtlasGlyph[] = [ - glyph, - glyph - // { x: 0, y: 0, w: 50, h: 50 }, - ]; - const bufferSize = spriteInfoStorageBufferByteSize * sprites.length; - const values = new Float32Array(bufferSize / 4); - let entryOffset = 0; - for (const t of sprites) { - values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition] = t.x; - values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition + 1] = t.y; - values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize] = t.w; - values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize + 1] = t.h; - entryOffset += SpriteInfoStorageBufferInfo.Size; - } - this._device.queue.writeBuffer(spriteInfoStorageBuffer, 0, values); - } - - - - - // Upload texture bitmap from atlas - const textureAtlasGpuTexture = this._device.createTexture({ + this._textureAtlasGpuTexture = this._device.createTexture({ format: 'rgba8unorm', size: { width: textureAtlas.source.width, height: textureAtlas.source.height }, usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT, }); - this._device.queue.copyExternalImageToTexture( - { source: textureAtlas.source }, - { texture: textureAtlasGpuTexture }, - { width: textureAtlas.source.width, height: textureAtlas.source.height }, - ); + + + this._updateTextureAtlas(); + + + + + + @@ -318,10 +321,10 @@ export class GpuViewLayerRenderer { label: 'ViewLayer bind group', layout: this._pipeline.getBindGroupLayout(0), entries: [ - { binding: BindingId.SpriteInfo, resource: { buffer: spriteInfoStorageBuffer } }, + { binding: BindingId.SpriteInfo, resource: { buffer: this._spriteInfoStorageBuffer } }, { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._dataBindBuffer } }, { binding: BindingId.TextureSampler, resource: sampler }, - { binding: BindingId.Texture, resource: textureAtlasGpuTexture.createView() }, + { binding: BindingId.Texture, resource: this._textureAtlasGpuTexture.createView() }, { binding: BindingId.Uniforms, resource: { buffer: uniformBuffer } }, { binding: BindingId.TextureInfoUniform, resource: { buffer: textureInfoUniformBuffer } }, ], @@ -371,6 +374,30 @@ export class GpuViewLayerRenderer { this.viewportData = viewportData; } + private _updateTextureAtlas() { + // TODO: Dynamically set buffer size + const bufferSize = spriteInfoStorageBufferByteSize * 10000; + const values = new Float32Array(bufferSize / 4); + let entryOffset = 0; + for (const glyph of this._textureAtlas.glyphs) { + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition] = glyph.x; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition + 1] = glyph.y; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize] = glyph.w; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize + 1] = glyph.h; + entryOffset += SpriteInfoStorageBufferInfo.Size; + } + this._device.queue.writeBuffer(this._spriteInfoStorageBuffer, 0, values); + + this._device.queue.copyExternalImageToTexture( + { source: this._textureAtlas.source }, + { texture: this._textureAtlasGpuTexture }, + { width: this._textureAtlas.source.width, height: this._textureAtlas.source.height }, + ); + + + this._testCtx.drawImage(this._textureAtlas.source, 0, 0); + } + public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { const ctx: IRendererContext = { rendLineNumberStart: inContext.rendLineNumberStart, @@ -394,6 +421,9 @@ export class GpuViewLayerRenderer { this._dataValuesBufferActiveIndex = (this._dataValuesBufferActiveIndex + 1) % 2; + // TODO: Only do this when needed + this._updateTextureAtlas(); + const encoder = this._device.createCommandEncoder(); (this._renderPassDescriptor.colorAttachments as any)[0].view = this._gpuCtx.getCurrentTexture().createView(); @@ -416,7 +446,7 @@ export class GpuViewLayerRenderer { // TODO: This update could be moved to an arbitrary task thread if expensive? private _updateDataBuffer(dataBuffer: Float32Array, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { - let chars: string = ''; + // let chars: string = ''; let screenAbsoluteX: number = 0; let screenAbsoluteY: number = 0; let zeroToOneX: number = 0; @@ -444,7 +474,7 @@ export class GpuViewLayerRenderer { } // TODO: Handle tab - chars = content[x]; + // chars = content[x]; const glyph = this._textureAtlas.getGlyph(content, x); // TODO: Move math to gpu @@ -462,7 +492,7 @@ export class GpuViewLayerRenderer { dataBuffer[charCount * Constants.IndicesPerCell + 1] = -wgslY; // y dataBuffer[charCount * Constants.IndicesPerCell + 2] = 0; dataBuffer[charCount * Constants.IndicesPerCell + 3] = 0; - dataBuffer[charCount * Constants.IndicesPerCell + 4] = glyph.id; // textureIndex + dataBuffer[charCount * Constants.IndicesPerCell + 4] = glyph.id; // textureId dataBuffer[charCount * Constants.IndicesPerCell + 5] = 0; charCount++; diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index a141ff07a27..41df7fb22c9 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -6,16 +6,19 @@ import { getActiveWindow } from 'vs/base/browser/dom'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; +import { ITextureAtlasAllocator, TextureAtlasShelfAllocator } from 'vs/editor/browser/view/gpu/textureAtlasAllocator'; export class TextureAtlas extends Disposable { - private _canvas: OffscreenCanvas; - private _ctx: OffscreenCanvasRenderingContext2D; - - private _glyphMap: Map = new Map(); + private readonly _canvas: OffscreenCanvas; + private readonly _ctx: OffscreenCanvasRenderingContext2D; - private _glyphRasterizer: GlyphRasterizer; + private readonly _glyphMap: Map = new Map(); + public get glyphs(): IterableIterator { + return this._glyphMap.values(); + } - private _nextId = 0; + private readonly _glyphRasterizer: GlyphRasterizer; + private readonly _allocator: ITextureAtlasAllocator; public get source(): OffscreenCanvas { return this._canvas; @@ -26,7 +29,9 @@ export class TextureAtlas extends Disposable { super(); this._canvas = new OffscreenCanvas(maxTextureSize, maxTextureSize); - this._ctx = ensureNonNullable(this._canvas.getContext('2d')); + this._ctx = ensureNonNullable(this._canvas.getContext('2d', { + willReadFrequently: true + })); const activeWindow = getActiveWindow(); const style = activeWindow.getComputedStyle(parentDomNode); @@ -34,6 +39,7 @@ export class TextureAtlas extends Disposable { this._ctx.font = `${fontSize}px ${style.fontFamily}`; this._glyphRasterizer = new GlyphRasterizer(fontSize, style.fontFamily); + this._allocator = new TextureAtlasShelfAllocator(this._canvas, this._ctx); // Reduce impact of a memory leak if this object is not released this._register(toDisposable(() => { @@ -50,33 +56,15 @@ export class TextureAtlas extends Disposable { return glyph; } const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars); - this._ctx.drawImage( - rasterizedGlyph.source, - // source - rasterizedGlyph.boundingBox.left, - rasterizedGlyph.boundingBox.top, - rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, - rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top, - // destination - 0, - 0, - rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, - rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top - ); - // TODO: Implement allocation - glyph = { - // TODO: Set real id - id: 1, //this._nextId++, - x: 0, - y: 0, - w: rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left, - h: rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top - }; - console.log('Allocating glyph', { + glyph = this._allocator.allocate(rasterizedGlyph); + this._glyphMap.set(chars, glyph); + + console.log('New glyph', { + chars, rasterizedGlyph, glyph }); - this._glyphMap.set(chars, glyph); + return glyph; } } @@ -90,7 +78,9 @@ class GlyphRasterizer extends Disposable { super(); this._canvas = new OffscreenCanvas(this._fontSize * 3, this._fontSize * 3); - this._ctx = ensureNonNullable(this._canvas.getContext('2d')); + this._ctx = ensureNonNullable(this._canvas.getContext('2d', { + willReadFrequently: true + })); this._ctx.font = `${this._fontSize}px ${fontFamily}`; this._ctx.fillStyle = '#FFFFFF'; } @@ -195,14 +185,14 @@ export interface ITextureAtlasGlyph { h: number; } -interface IBoundingBox { +export interface IBoundingBox { left: number; top: number; right: number; bottom: number; } -interface IRasterizedGlyph { +export interface IRasterizedGlyph { source: CanvasImageSource; boundingBox: IBoundingBox; } diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts new file mode 100644 index 00000000000..d9e16a7e07c --- /dev/null +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { IRasterizedGlyph, ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; + +export interface ITextureAtlasAllocator { + allocate(rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph; +} + +export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { + private _currentRow: ITextureAtlasShelf = { + x: 0, + y: 0, + h: 0 + }; + // TODO: Allow for multiple active rows + // public readonly fixedRows: ICharAtlasActiveRow[] = []; + + private _nextId = 1; + + constructor( + private readonly _canvas: OffscreenCanvas, + private readonly _ctx: OffscreenCanvasRenderingContext2D, + ) { + } + + public allocate(rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph { + // Finalize row if it doesn't fix + if (rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left > this._canvas.width - this._currentRow.x) { + this._currentRow.x = 0; + this._currentRow.y += this._currentRow.h; + this._currentRow.h = 1; + } + + // TODO: Handle end of atlas page + + // Draw glyph + const glyphWidth = rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left; + const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top; + this._ctx.drawImage( + rasterizedGlyph.source, + // source + rasterizedGlyph.boundingBox.left, + rasterizedGlyph.boundingBox.top, + glyphWidth, + glyphHeight, + // destination + this._currentRow.x, + this._currentRow.y, + glyphWidth, + glyphHeight + ); + + // Shift current row + this._currentRow.x += glyphWidth; + this._currentRow.h = Math.max(this._currentRow.h, glyphHeight); + + // Return glyph + const glyph = { + id: this._nextId++, + x: this._currentRow.x, + y: this._currentRow.y, + w: glyphWidth, + h: glyphHeight, + }; + return glyph; + } +} + +interface ITextureAtlasShelf { + x: number; + y: number; + h: number; +} From 8936849eecc7e5972acce4a664b80ec2cb60cf55 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 Mar 2024 06:25:16 -0700 Subject: [PATCH 0015/2222] Correct glyph index --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 6 +++--- src/vs/editor/browser/view/gpu/textureAtlas.ts | 2 +- .../browser/view/gpu/textureAtlasAllocator.ts | 17 +++++++++-------- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index ed9b1973156..f0273ad30a4 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -57,7 +57,7 @@ struct Vertex { struct DynamicUnitInfo { position: vec2f, unused1: vec2f, - textureId: f32, + textureIndex: f32, unused2: f32 }; @@ -78,7 +78,7 @@ struct VSOutput { @builtin(vertex_index) vertexIndex : u32 ) -> VSOutput { let dynamicUnitInfo = dynamicUnitInfoStructs[instanceIndex]; - let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureId)]; + let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureIndex)]; var vsOut: VSOutput; // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 @@ -492,7 +492,7 @@ export class GpuViewLayerRenderer { dataBuffer[charCount * Constants.IndicesPerCell + 1] = -wgslY; // y dataBuffer[charCount * Constants.IndicesPerCell + 2] = 0; dataBuffer[charCount * Constants.IndicesPerCell + 3] = 0; - dataBuffer[charCount * Constants.IndicesPerCell + 4] = glyph.id; // textureId + dataBuffer[charCount * Constants.IndicesPerCell + 4] = glyph.index; // textureIndex dataBuffer[charCount * Constants.IndicesPerCell + 5] = 0; charCount++; diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 41df7fb22c9..576b5ba1079 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -178,7 +178,7 @@ class GlyphRasterizer extends Disposable { } export interface ITextureAtlasGlyph { - id: number; + index: number; x: number; y: number; w: number; diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index d9e16a7e07c..64eb13729a8 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -18,7 +18,7 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { // TODO: Allow for multiple active rows // public readonly fixedRows: ICharAtlasActiveRow[] = []; - private _nextId = 1; + private _nextIndex = 0; constructor( private readonly _canvas: OffscreenCanvas, @@ -53,18 +53,19 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { glyphHeight ); - // Shift current row - this._currentRow.x += glyphWidth; - this._currentRow.h = Math.max(this._currentRow.h, glyphHeight); - - // Return glyph - const glyph = { - id: this._nextId++, + // Create glyph object + const glyph: ITextureAtlasGlyph = { + index: this._nextIndex++, x: this._currentRow.x, y: this._currentRow.y, w: glyphWidth, h: glyphHeight, }; + + // Shift current row + this._currentRow.x += glyphWidth; + this._currentRow.h = Math.max(this._currentRow.h, glyphHeight); + return glyph; } } From 1e2295c06b309c837290d3d26fffd4b1e4396255 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 Mar 2024 06:35:38 -0700 Subject: [PATCH 0016/2222] Fix origin offset, kind of --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 8 ++++++-- src/vs/editor/browser/view/gpu/textureAtlas.ts | 10 +++++++++- .../editor/browser/view/gpu/textureAtlasAllocator.ts | 2 ++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index f0273ad30a4..9ecf463594f 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -20,9 +20,10 @@ const enum Constants { } const enum SpriteInfoStorageBufferInfo { - Size = 2 + 2, + Size = 2 + 2 + 2, Offset_TexturePosition = 0, Offset_TextureSize = 2, + Offset_OriginPosition = 4, } const spriteInfoStorageBufferByteSize = SpriteInfoStorageBufferInfo.Size * Float32Array.BYTES_PER_ELEMENT; @@ -48,6 +49,7 @@ struct TextureInfoUniform { struct SpriteInfo { position: vec2f, size: vec2f, + origin: vec2f, }; struct Vertex { @@ -83,7 +85,7 @@ struct VSOutput { var vsOut: VSOutput; // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 vsOut.position = vec4f( - (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position, + (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position - ((spriteInfo.origin * 2) / uniforms.canvasDimensions), 0.0, 1.0 ); @@ -384,6 +386,8 @@ export class GpuViewLayerRenderer { values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition + 1] = glyph.y; values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize] = glyph.w; values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize + 1] = glyph.h; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_OriginPosition] = glyph.originOffsetX; + values[entryOffset + SpriteInfoStorageBufferInfo.Offset_OriginPosition + 1] = glyph.originOffsetY; entryOffset += SpriteInfoStorageBufferInfo.Size; } this._device.queue.writeBuffer(this._spriteInfoStorageBuffer, 0, values); diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 576b5ba1079..0c55dc072ff 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -95,9 +95,14 @@ class GlyphRasterizer extends Disposable { const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); // TODO: Hot path: Reuse object + const boundingBox = this._findGlyphBoundingBox(imageData); const result: IRasterizedGlyph = { source: this._canvas, - boundingBox: this._findGlyphBoundingBox(imageData) + boundingBox, + originOffset: { + x: boundingBox.left - this._fontSize, + y: boundingBox.top - this._fontSize + } }; return result; } @@ -183,6 +188,8 @@ export interface ITextureAtlasGlyph { y: number; w: number; h: number; + originOffsetX: number; + originOffsetY: number; } export interface IBoundingBox { @@ -195,4 +202,5 @@ export interface IBoundingBox { export interface IRasterizedGlyph { source: CanvasImageSource; boundingBox: IBoundingBox; + originOffset: { x: number; y: number }; } diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index 64eb13729a8..788caac2f53 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -60,6 +60,8 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { y: this._currentRow.y, w: glyphWidth, h: glyphHeight, + originOffsetX: rasterizedGlyph.originOffset.x, + originOffsetY: rasterizedGlyph.originOffset.y }; // Shift current row From f5b0aa26497bbd7d0ee90bc8f70d14e352613a2f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 Mar 2024 06:55:55 -0700 Subject: [PATCH 0017/2222] Don't upload texture atlas unless changed --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 10 +++++++++- src/vs/editor/browser/view/gpu/textureAtlas.ts | 6 +++++- .../editor/browser/view/gpu/textureAtlasAllocator.ts | 1 + 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 9ecf463594f..ce03e0f050e 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -377,6 +377,10 @@ export class GpuViewLayerRenderer { } private _updateTextureAtlas() { + if (!this._textureAtlas.hasChanges) { + return; + } + this._textureAtlas.hasChanges = false; // TODO: Dynamically set buffer size const bufferSize = spriteInfoStorageBufferByteSize * 10000; const values = new Float32Array(bufferSize / 4); @@ -465,7 +469,7 @@ export class GpuViewLayerRenderer { scrollTop = 0; } for (let lineNumber = startLineNumber; lineNumber <= stopLineNumber; lineNumber++) { - const y = Math.round((-scrollTop + deltaTop[lineNumber - startLineNumber])); + const y = Math.round(-scrollTop + deltaTop[lineNumber - startLineNumber]); // Offscreen if (y < 0) { continue; @@ -492,6 +496,10 @@ export class GpuViewLayerRenderer { wgslX = zeroToOneX * 2 - 1; wgslY = zeroToOneY * 2 - 1; + // TODO: We could upload the entire file as a grid, capping out lines at some reasonable amount (200?) + // Optimize for the common case and fallback to a slower path for long line files + // Doing the fast grid path would mean only the cell needs to change on data change, scrolling would simply change the start line index + dataBuffer[charCount * Constants.IndicesPerCell + 0] = wgslX; // x dataBuffer[charCount * Constants.IndicesPerCell + 1] = -wgslY; // y dataBuffer[charCount * Constants.IndicesPerCell + 2] = 0; diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 0c55dc072ff..8e00750242c 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -24,6 +24,8 @@ export class TextureAtlas extends Disposable { return this._canvas; } + public hasChanges = false; + // TODO: Should pull in the font size from config instead of random dom node constructor(parentDomNode: HTMLElement, maxTextureSize: number) { super(); @@ -58,6 +60,7 @@ export class TextureAtlas extends Disposable { const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars); glyph = this._allocator.allocate(rasterizedGlyph); this._glyphMap.set(chars, glyph); + this.hasChanges = true; console.log('New glyph', { chars, @@ -82,6 +85,7 @@ class GlyphRasterizer extends Disposable { willReadFrequently: true })); this._ctx.font = `${this._fontSize}px ${fontFamily}`; + this._ctx.textBaseline = 'alphabetic'; this._ctx.fillStyle = '#FFFFFF'; } @@ -91,7 +95,7 @@ class GlyphRasterizer extends Disposable { this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); // TODO: Draw in middle using alphabetical baseline - this._ctx.fillText(chars, this._fontSize, this._fontSize); + this._ctx.fillText(chars, this._fontSize * 2, this._fontSize); const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); // TODO: Hot path: Reuse object diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index 788caac2f53..a5904eeb8d9 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -39,6 +39,7 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { // Draw glyph const glyphWidth = rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left; const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top; + // TODO: Prefer putImageData as it doesn't do blending or scaling this._ctx.drawImage( rasterizedGlyph.source, // source From 1ae361ba55207a58d1b4a6fbc30dba8775a4af43 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 26 Mar 2024 07:40:52 -0700 Subject: [PATCH 0018/2222] Notes --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index ce03e0f050e..d0fdb540207 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -499,6 +499,7 @@ export class GpuViewLayerRenderer { // TODO: We could upload the entire file as a grid, capping out lines at some reasonable amount (200?) // Optimize for the common case and fallback to a slower path for long line files // Doing the fast grid path would mean only the cell needs to change on data change, scrolling would simply change the start line index + // Even better would be a grid for standard sized lines (~120?) and then another buffer that handles larger lines in a slower but more dynamic way dataBuffer[charCount * Constants.IndicesPerCell + 0] = wgslX; // x dataBuffer[charCount * Constants.IndicesPerCell + 1] = -wgslY; // y From 80918f852606e33e9e557542aa0cad39bc99df92 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 06:19:17 -0700 Subject: [PATCH 0019/2222] Static texture atlas, debounce drawing test atlas --- .../editor/browser/view/gpu/gpuViewLayer.ts | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index d0fdb540207..66c05e2443c 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getActiveDocument, getActiveWindow } from 'vs/base/browser/dom'; +import { debounce } from 'vs/base/common/decorators'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { TextureAtlas } from 'vs/editor/browser/view/gpu/textureAtlas'; import type { IVisibleLine, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; @@ -131,7 +132,7 @@ export class GpuViewLayerRenderer { private _vertexBuffer!: GPUBuffer; private _squareVertices!: { vertexData: Float32Array; numVertices: number }; - private _textureAtlas!: TextureAtlas; + private static _textureAtlas: TextureAtlas; private _spriteInfoStorageBuffer!: GPUBuffer; private _textureAtlasGpuTexture!: GPUTexture; @@ -139,8 +140,8 @@ export class GpuViewLayerRenderer { - private readonly _testCanvas: HTMLCanvasElement; - private readonly _testCtx: CanvasRenderingContext2D; + private static _testCanvas: HTMLCanvasElement; + private static _testCtx: CanvasRenderingContext2D; constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { @@ -150,19 +151,6 @@ export class GpuViewLayerRenderer { this._gpuCtx = this.domNode.getContext('webgpu')!; this.initWebgpu(); - - - - this._testCanvas = document.createElement('canvas'); - this._testCanvas.width = 2048; - this._testCanvas.height = 2048; - this._testCanvas.style.position = 'absolute'; - this._testCanvas.style.top = '0'; - this._testCanvas.style.left = '0'; - this._testCanvas.style.zIndex = '10000'; - this._testCanvas.style.pointerEvents = 'none'; - this._testCtx = ensureNonNullable(this._testCanvas.getContext('2d')); - getActiveDocument().body.appendChild(this._testCanvas); } async initWebgpu() { @@ -246,7 +234,21 @@ export class GpuViewLayerRenderer { // Create texture atlas - const textureAtlas = this._textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); + if (!GpuViewLayerRenderer._textureAtlas) { + GpuViewLayerRenderer._textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); + + GpuViewLayerRenderer._testCanvas = document.createElement('canvas'); + GpuViewLayerRenderer._testCanvas.width = 2048; + GpuViewLayerRenderer._testCanvas.height = 2048; + GpuViewLayerRenderer._testCanvas.style.position = 'absolute'; + GpuViewLayerRenderer._testCanvas.style.top = '0'; + GpuViewLayerRenderer._testCanvas.style.left = '0'; + GpuViewLayerRenderer._testCanvas.style.zIndex = '10000'; + GpuViewLayerRenderer._testCanvas.style.pointerEvents = 'none'; + GpuViewLayerRenderer._testCtx = ensureNonNullable(GpuViewLayerRenderer._testCanvas.getContext('2d')); + getActiveDocument().body.appendChild(GpuViewLayerRenderer._testCanvas); + } + const textureAtlas = GpuViewLayerRenderer._textureAtlas; @@ -377,15 +379,15 @@ export class GpuViewLayerRenderer { } private _updateTextureAtlas() { - if (!this._textureAtlas.hasChanges) { + if (!GpuViewLayerRenderer._textureAtlas.hasChanges) { return; } - this._textureAtlas.hasChanges = false; + GpuViewLayerRenderer._textureAtlas.hasChanges = false; // TODO: Dynamically set buffer size const bufferSize = spriteInfoStorageBufferByteSize * 10000; const values = new Float32Array(bufferSize / 4); let entryOffset = 0; - for (const glyph of this._textureAtlas.glyphs) { + for (const glyph of GpuViewLayerRenderer._textureAtlas.glyphs) { values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition] = glyph.x; values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TexturePosition + 1] = glyph.y; values[entryOffset + SpriteInfoStorageBufferInfo.Offset_TextureSize] = glyph.w; @@ -397,13 +399,19 @@ export class GpuViewLayerRenderer { this._device.queue.writeBuffer(this._spriteInfoStorageBuffer, 0, values); this._device.queue.copyExternalImageToTexture( - { source: this._textureAtlas.source }, + { source: GpuViewLayerRenderer._textureAtlas.source }, { texture: this._textureAtlasGpuTexture }, - { width: this._textureAtlas.source.width, height: this._textureAtlas.source.height }, + { width: GpuViewLayerRenderer._textureAtlas.source.width, height: GpuViewLayerRenderer._textureAtlas.source.height }, ); - this._testCtx.drawImage(this._textureAtlas.source, 0, 0); + GpuViewLayerRenderer._drawToTextureAtlas(); + } + + @debounce(500) + private static _drawToTextureAtlas() { + GpuViewLayerRenderer._testCtx.clearRect(0, 0, GpuViewLayerRenderer._textureAtlas.source.width, GpuViewLayerRenderer._textureAtlas.source.height); + GpuViewLayerRenderer._testCtx.drawImage(GpuViewLayerRenderer._textureAtlas.source, 0, 0); } public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { @@ -483,7 +491,7 @@ export class GpuViewLayerRenderer { // TODO: Handle tab // chars = content[x]; - const glyph = this._textureAtlas.getGlyph(content, x); + const glyph = GpuViewLayerRenderer._textureAtlas.getGlyph(content, x); // TODO: Move math to gpu // TODO: Render using a line offset for partial line scrolling From 20d5126401cdc0bcbcaa93f0adcd112839030400 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 07:07:12 -0700 Subject: [PATCH 0020/2222] Create render strategy interface --- .../editor/browser/view/gpu/gpuViewLayer.ts | 298 ++++++++++-------- 1 file changed, 169 insertions(+), 129 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 66c05e2443c..1669d20f29b 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -38,79 +38,6 @@ const enum BindingId { TextureInfoUniform = 5, } -const wgsl = ` -struct Uniforms { - canvasDimensions: vec2f, -}; - -struct TextureInfoUniform { - spriteSheetSize: vec2f, -} - -struct SpriteInfo { - position: vec2f, - size: vec2f, - origin: vec2f, -}; - -struct Vertex { - @location(0) position: vec2f, -}; - -struct DynamicUnitInfo { - position: vec2f, - unused1: vec2f, - textureIndex: f32, - unused2: f32 -}; - -struct VSOutput { - @builtin(position) position: vec4f, - @location(0) texcoord: vec2f, -}; - -@group(0) @binding(${BindingId.Uniforms}) var uniforms: Uniforms; -@group(0) @binding(${BindingId.TextureInfoUniform}) var textureInfoUniform: TextureInfoUniform; - -@group(0) @binding(${BindingId.SpriteInfo}) var spriteInfo: array; -@group(0) @binding(${BindingId.DynamicUnitInfo}) var dynamicUnitInfoStructs: array; - -@vertex fn vs( - vert: Vertex, - @builtin(instance_index) instanceIndex: u32, - @builtin(vertex_index) vertexIndex : u32 -) -> VSOutput { - let dynamicUnitInfo = dynamicUnitInfoStructs[instanceIndex]; - let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureIndex)]; - - var vsOut: VSOutput; - // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 - vsOut.position = vec4f( - (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position - ((spriteInfo.origin * 2) / uniforms.canvasDimensions), - 0.0, - 1.0 - ); - - // Textures are flipped from natural direction on the y-axis, so flip it back - vsOut.texcoord = vert.position; - vsOut.texcoord = ( - // Sprite offset (0-1) - (spriteInfo.position / textureInfoUniform.spriteSheetSize) + - // Sprite coordinate (0-1) - (vsOut.texcoord * (spriteInfo.size / textureInfoUniform.spriteSheetSize)) - ); - - return vsOut; -} - -@group(0) @binding(${BindingId.TextureSampler}) var ourSampler: sampler; -@group(0) @binding(${BindingId.Texture}) var ourTexture: texture_2d; - -@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f { - return textureSample(ourTexture, ourSampler, vsOut.texcoord); -} -`; - export class GpuViewLayerRenderer { readonly domNode: HTMLCanvasElement; @@ -125,9 +52,6 @@ export class GpuViewLayerRenderer { private _bindGroup!: GPUBindGroup; private _pipeline!: GPURenderPipeline; - private _dataBindBuffer!: GPUBuffer; - private _dataValueBuffers!: ArrayBuffer[]; - private _dataValuesBufferActiveIndex: number = 0; private _vertexBuffer!: GPUBuffer; private _squareVertices!: { vertexData: Float32Array; numVertices: number }; @@ -143,6 +67,8 @@ export class GpuViewLayerRenderer { private static _testCanvas: HTMLCanvasElement; private static _testCtx: CanvasRenderingContext2D; + private _renderStrategy!: IRenderStrategy; + constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { this.domNode = domNode; @@ -171,9 +97,30 @@ export class GpuViewLayerRenderer { format: presentationFormat, }); + + // Create texture atlas + if (!GpuViewLayerRenderer._textureAtlas) { + GpuViewLayerRenderer._textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); + + GpuViewLayerRenderer._testCanvas = document.createElement('canvas'); + GpuViewLayerRenderer._testCanvas.width = 2048; + GpuViewLayerRenderer._testCanvas.height = 2048; + GpuViewLayerRenderer._testCanvas.style.position = 'absolute'; + GpuViewLayerRenderer._testCanvas.style.top = '0'; + GpuViewLayerRenderer._testCanvas.style.left = '0'; + GpuViewLayerRenderer._testCanvas.style.zIndex = '10000'; + GpuViewLayerRenderer._testCanvas.style.pointerEvents = 'none'; + GpuViewLayerRenderer._testCtx = ensureNonNullable(GpuViewLayerRenderer._testCanvas.getContext('2d')); + getActiveDocument().body.appendChild(GpuViewLayerRenderer._testCanvas); + } + const textureAtlas = GpuViewLayerRenderer._textureAtlas; + + + this._renderStrategy = new NaiveViewportRenderStrategy(this._device, this.domNode, this.viewportData, GpuViewLayerRenderer._textureAtlas); + const module = this._device.createShaderModule({ label: 'ViewLayer shader module', - code: wgsl, + code: this._renderStrategy.wgsl, }); this._pipeline = this._device.createRenderPipeline({ @@ -233,24 +180,6 @@ export class GpuViewLayerRenderer { } - // Create texture atlas - if (!GpuViewLayerRenderer._textureAtlas) { - GpuViewLayerRenderer._textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); - - GpuViewLayerRenderer._testCanvas = document.createElement('canvas'); - GpuViewLayerRenderer._testCanvas.width = 2048; - GpuViewLayerRenderer._testCanvas.height = 2048; - GpuViewLayerRenderer._testCanvas.style.position = 'absolute'; - GpuViewLayerRenderer._testCanvas.style.top = '0'; - GpuViewLayerRenderer._testCanvas.style.left = '0'; - GpuViewLayerRenderer._testCanvas.style.zIndex = '10000'; - GpuViewLayerRenderer._testCanvas.style.pointerEvents = 'none'; - GpuViewLayerRenderer._testCtx = ensureNonNullable(GpuViewLayerRenderer._testCanvas.getContext('2d')); - getActiveDocument().body.appendChild(GpuViewLayerRenderer._testCanvas); - } - const textureAtlas = GpuViewLayerRenderer._textureAtlas; - - const enum TextureInfoUniformBufferInfo { Size = 2, @@ -295,24 +224,8 @@ export class GpuViewLayerRenderer { + this._renderStrategy.initBuffers(); - - - - - - // TODO: Grow/shrink buffer size dynamically - const cellCount = 10000; - const bufferSize = cellCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; - this._dataBindBuffer = this._device.createBuffer({ - label: 'Entity dynamic info buffer', - size: bufferSize, - usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - }); - this._dataValueBuffers = [ - new ArrayBuffer(bufferSize), - new ArrayBuffer(bufferSize), - ]; this._updateSquareVertices(); @@ -326,11 +239,11 @@ export class GpuViewLayerRenderer { layout: this._pipeline.getBindGroupLayout(0), entries: [ { binding: BindingId.SpriteInfo, resource: { buffer: this._spriteInfoStorageBuffer } }, - { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._dataBindBuffer } }, { binding: BindingId.TextureSampler, resource: sampler }, { binding: BindingId.Texture, resource: this._textureAtlasGpuTexture.createView() }, { binding: BindingId.Uniforms, resource: { buffer: uniformBuffer } }, { binding: BindingId.TextureInfoUniform, resource: { buffer: textureInfoUniformBuffer } }, + ...this._renderStrategy.bindGroupEntries ], }); @@ -424,18 +337,11 @@ export class GpuViewLayerRenderer { if (!this._initialized) { return ctx; } - return this._renderWebgpu(ctx, startLineNumber, stopLineNumber, deltaTop); + return this._render(ctx, startLineNumber, stopLineNumber, deltaTop); } - private _renderWebgpu(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { - // TODO: Improve "data" name - const dataBuffer = new Float32Array(this._dataValueBuffers[this._dataValuesBufferActiveIndex]); - const visibleObjectCount = this._updateDataBuffer(dataBuffer, ctx, startLineNumber, stopLineNumber, deltaTop); - - // Write buffer and swap it out to unblock writes - this._device.queue.writeBuffer(this._dataBindBuffer, 0, dataBuffer, 0, visibleObjectCount * Constants.IndicesPerCell); - - this._dataValuesBufferActiveIndex = (this._dataValuesBufferActiveIndex + 1) % 2; + private _render(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { + const visibleObjectCount = this._renderStrategy.update(ctx, startLineNumber, stopLineNumber, deltaTop); // TODO: Only do this when needed this._updateTextureAtlas(); @@ -449,6 +355,7 @@ export class GpuViewLayerRenderer { pass.setBindGroup(0, this._bindGroup); // TODO: Draws could be split by chunk, this would help minimize moving data around in arrays + pass.draw(this._squareVertices.numVertices, visibleObjectCount); pass.end(); @@ -459,8 +366,139 @@ export class GpuViewLayerRenderer { return ctx; } +} + + +interface IRenderStrategy { + readonly wgsl: string; + readonly bindGroupEntries: GPUBindGroupEntry[]; + + initBuffers(): void; + update(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number; +} + +// #region Naive viewport render strategy + +const naiveViewportRenderStrategyWgsl = ` +struct Uniforms { + canvasDimensions: vec2f, +}; + +struct TextureInfoUniform { + spriteSheetSize: vec2f, +} + +struct SpriteInfo { + position: vec2f, + size: vec2f, + origin: vec2f, +}; + +struct Vertex { + @location(0) position: vec2f, +}; + +struct DynamicUnitInfo { + position: vec2f, + unused1: vec2f, + textureIndex: f32, + unused2: f32 +}; + +struct VSOutput { + @builtin(position) position: vec4f, + @location(0) texcoord: vec2f, +}; + +@group(0) @binding(${BindingId.Uniforms}) var uniforms: Uniforms; +@group(0) @binding(${BindingId.TextureInfoUniform}) var textureInfoUniform: TextureInfoUniform; + +@group(0) @binding(${BindingId.SpriteInfo}) var spriteInfo: array; +@group(0) @binding(${BindingId.DynamicUnitInfo}) var dynamicUnitInfoStructs: array; + +@vertex fn vs( + vert: Vertex, + @builtin(instance_index) instanceIndex: u32, + @builtin(vertex_index) vertexIndex : u32 +) -> VSOutput { + let dynamicUnitInfo = dynamicUnitInfoStructs[instanceIndex]; + let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureIndex)]; + + var vsOut: VSOutput; + // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 + vsOut.position = vec4f( + (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position - ((spriteInfo.origin * 2) / uniforms.canvasDimensions), + 0.0, + 1.0 + ); + + // Textures are flipped from natural direction on the y-axis, so flip it back + vsOut.texcoord = vert.position; + vsOut.texcoord = ( + // Sprite offset (0-1) + (spriteInfo.position / textureInfoUniform.spriteSheetSize) + + // Sprite coordinate (0-1) + (vsOut.texcoord * (spriteInfo.size / textureInfoUniform.spriteSheetSize)) + ); + + return vsOut; +} + +@group(0) @binding(${BindingId.TextureSampler}) var ourSampler: sampler; +@group(0) @binding(${BindingId.Texture}) var ourTexture: texture_2d; + +@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f { + return textureSample(ourTexture, ourSampler, vsOut.texcoord); +} +`; + +class NaiveViewportRenderStrategy implements IRenderStrategy { + readonly wgsl: string = naiveViewportRenderStrategyWgsl; + + private _cellBindBuffer!: GPUBuffer; + private _cellValueBuffers!: ArrayBuffer[]; + private _cellValuesBufferActiveIndex: number = 0; + + get bindGroupEntries(): GPUBindGroupEntry[] { + return [ + { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._cellBindBuffer } } + ]; + } + + constructor( + private readonly _device: GPUDevice, + private readonly _canvas: HTMLCanvasElement, + private readonly _viewportData: ViewportData, + private readonly _textureAtlas: TextureAtlas + ) { + } + + initBuffers(): void { + // TODO: Grow/shrink buffer size dynamically + const cellCount = 10000; + const bufferSize = cellCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; + this._cellBindBuffer = this._device.createBuffer({ + label: 'Entity dynamic info buffer', + size: bufferSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }); + this._cellValueBuffers = [ + new ArrayBuffer(bufferSize), + new ArrayBuffer(bufferSize), + ]; + } + + update(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { + const cellBuffer = new Float32Array(this._cellValueBuffers[this._cellValuesBufferActiveIndex]); + const visibleObjectCount = this._updateDataBuffer(cellBuffer, ctx, startLineNumber, stopLineNumber, deltaTop); + + // Write buffer and swap it out to unblock writes + this._device.queue.writeBuffer(this._cellBindBuffer, 0, cellBuffer, 0, visibleObjectCount * Constants.IndicesPerCell); + + this._cellValuesBufferActiveIndex = (this._cellValuesBufferActiveIndex + 1) % 2; + return visibleObjectCount; + } - // TODO: This update could be moved to an arbitrary task thread if expensive? private _updateDataBuffer(dataBuffer: Float32Array, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { // let chars: string = ''; let screenAbsoluteX: number = 0; @@ -472,7 +510,7 @@ export class GpuViewLayerRenderer { const activeWindow = getActiveWindow(); let charCount = 0; - let scrollTop = parseInt(this.domNode.parentElement!.getAttribute('data-adjusted-scroll-top')!); + let scrollTop = parseInt(this._canvas.parentElement!.getAttribute('data-adjusted-scroll-top')!); if (Number.isNaN(scrollTop)) { scrollTop = 0; } @@ -482,7 +520,7 @@ export class GpuViewLayerRenderer { if (y < 0) { continue; } - const content = this.viewportData.getViewLineRenderingData(lineNumber).content; + const content = this._viewportData.getViewLineRenderingData(lineNumber).content; // console.log(content, 0, y); for (let x = 0; x < content.length; x++) { if (content.charAt(x) === ' ') { @@ -491,7 +529,7 @@ export class GpuViewLayerRenderer { // TODO: Handle tab // chars = content[x]; - const glyph = GpuViewLayerRenderer._textureAtlas.getGlyph(content, x); + const glyph = this._textureAtlas.getGlyph(content, x); // TODO: Move math to gpu // TODO: Render using a line offset for partial line scrolling @@ -499,8 +537,8 @@ export class GpuViewLayerRenderer { screenAbsoluteX = x * 7 * activeWindow.devicePixelRatio; // TODO: This +10 is because the glyph is being rendered in the wrong position screenAbsoluteY = Math.round(y * activeWindow.devicePixelRatio); - zeroToOneX = screenAbsoluteX / this.domNode.width; - zeroToOneY = screenAbsoluteY / this.domNode.height; + zeroToOneX = screenAbsoluteX / this._canvas.width; + zeroToOneY = screenAbsoluteY / this._canvas.height; wgslX = zeroToOneX * 2 - 1; wgslY = zeroToOneY * 2 - 1; @@ -523,3 +561,5 @@ export class GpuViewLayerRenderer { return charCount; } } + +// #endregion Naive viewport render strategy From 2dd6a057414547e1ea74f6fb3544e5c7180cdcc8 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 07:48:56 -0700 Subject: [PATCH 0021/2222] Get rendering of full file data mostly working --- .../editor/browser/view/gpu/gpuViewLayer.ts | 216 +++++++++++++++++- 1 file changed, 214 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 1669d20f29b..960a3916ee0 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -116,7 +116,8 @@ export class GpuViewLayerRenderer { const textureAtlas = GpuViewLayerRenderer._textureAtlas; - this._renderStrategy = new NaiveViewportRenderStrategy(this._device, this.domNode, this.viewportData, GpuViewLayerRenderer._textureAtlas); + // this._renderStrategy = new NaiveViewportRenderStrategy(this._device, this.domNode, this.viewportData, GpuViewLayerRenderer._textureAtlas); + this._renderStrategy = new FullFileRenderStrategy(this._device, this.domNode, this.viewportData, GpuViewLayerRenderer._textureAtlas); const module = this._device.createShaderModule({ label: 'ViewLayer shader module', @@ -356,7 +357,11 @@ export class GpuViewLayerRenderer { pass.setBindGroup(0, this._bindGroup); // TODO: Draws could be split by chunk, this would help minimize moving data around in arrays - pass.draw(this._squareVertices.numVertices, visibleObjectCount); + if (this._renderStrategy?.draw) { + this._renderStrategy.draw(pass, ctx, startLineNumber, stopLineNumber, deltaTop); + } else { + pass.draw(this._squareVertices.numVertices, visibleObjectCount); + } pass.end(); @@ -375,6 +380,7 @@ interface IRenderStrategy { initBuffers(): void; update(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number; + draw?(pass: GPURenderPassEncoder, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): void; } // #region Naive viewport render strategy @@ -563,3 +569,209 @@ class NaiveViewportRenderStrategy implements IRenderStra } // #endregion Naive viewport render strategy + +// #region Full file render strategy + +const fullFileRenderStrategyWgsl = ` +struct Uniforms { + canvasDimensions: vec2f, +}; + +struct TextureInfoUniform { + spriteSheetSize: vec2f, +} + +struct SpriteInfo { + position: vec2f, + size: vec2f, + origin: vec2f, +}; + +struct Vertex { + @location(0) position: vec2f, +}; + +struct DynamicUnitInfo { + position: vec2f, + unused1: vec2f, + textureIndex: f32, + unused2: f32 +}; + +struct VSOutput { + @builtin(position) position: vec4f, + @location(0) texcoord: vec2f, +}; + +@group(0) @binding(${BindingId.Uniforms}) var uniforms: Uniforms; +@group(0) @binding(${BindingId.TextureInfoUniform}) var textureInfoUniform: TextureInfoUniform; + +@group(0) @binding(${BindingId.SpriteInfo}) var spriteInfo: array; +@group(0) @binding(${BindingId.DynamicUnitInfo}) var dynamicUnitInfoStructs: array; + +@vertex fn vs( + vert: Vertex, + @builtin(instance_index) instanceIndex: u32, + @builtin(vertex_index) vertexIndex : u32 +) -> VSOutput { + let dynamicUnitInfo = dynamicUnitInfoStructs[instanceIndex]; + let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureIndex)]; + + var vsOut: VSOutput; + // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 + vsOut.position = vec4f( + (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position - ((spriteInfo.origin * 2) / uniforms.canvasDimensions), + 0.0, + 1.0 + ); + + // Textures are flipped from natural direction on the y-axis, so flip it back + vsOut.texcoord = vert.position; + vsOut.texcoord = ( + // Sprite offset (0-1) + (spriteInfo.position / textureInfoUniform.spriteSheetSize) + + // Sprite coordinate (0-1) + (vsOut.texcoord * (spriteInfo.size / textureInfoUniform.spriteSheetSize)) + ); + + return vsOut; +} + +@group(0) @binding(${BindingId.TextureSampler}) var ourSampler: sampler; +@group(0) @binding(${BindingId.Texture}) var ourTexture: texture_2d; + +@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f { + return textureSample(ourTexture, ourSampler, vsOut.texcoord); +} +`; + +class FullFileRenderStrategy implements IRenderStrategy { + + private static _lineCount = 3000; + private static _columnCount = 200; + + readonly wgsl: string = fullFileRenderStrategyWgsl; + + private _cellBindBuffer!: GPUBuffer; + private _cellValueBuffers!: ArrayBuffer[]; + private _cellValuesBufferActiveIndex: number = 0; + + get bindGroupEntries(): GPUBindGroupEntry[] { + return [ + { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._cellBindBuffer } } + ]; + } + + constructor( + private readonly _device: GPUDevice, + private readonly _canvas: HTMLCanvasElement, + private readonly _viewportData: ViewportData, + private readonly _textureAtlas: TextureAtlas + ) { + } + + initBuffers(): void { + const bufferSize = FullFileRenderStrategy._lineCount * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; + this._cellBindBuffer = this._device.createBuffer({ + label: 'Full file cell buffer', + size: bufferSize, + usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, + }); + this._cellValueBuffers = [ + new ArrayBuffer(bufferSize), + new ArrayBuffer(bufferSize), + ]; + } + + update(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { + let y = 0; + let x = 0; + let screenAbsoluteX: number = 0; + let screenAbsoluteY: number = 0; + let zeroToOneX: number = 0; + let zeroToOneY: number = 0; + let wgslX: number = 0; + let wgslY: number = 0; + + const viewportData = this._viewportData; + const activeWindow = getActiveWindow(); + const cellBuffer = new Float32Array(this._cellValueBuffers[this._cellValuesBufferActiveIndex]); + const lineIndexCount = FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; + + let scrollTop = parseInt(this._canvas.parentElement!.getAttribute('data-adjusted-scroll-top')!); + if (Number.isNaN(scrollTop)) { + scrollTop = 0; + } + + for (y = startLineNumber; y <= stopLineNumber; y++) { + const viewLineRenderingData = viewportData.getViewLineRenderingData(y); + const content = viewLineRenderingData.content; + for (x = 0; x < FullFileRenderStrategy._columnCount; x++) { + const glyph = this._textureAtlas.getGlyph(content, x); + + screenAbsoluteX = x * 7 * activeWindow.devicePixelRatio; + // TODO: Send scroll offset instead of setting it here such that the cell data doesn't need to change when scrolling + screenAbsoluteY = Math.round(Math.round(-scrollTop + deltaTop[y - startLineNumber]) * activeWindow.devicePixelRatio); + zeroToOneX = screenAbsoluteX / this._canvas.width; + zeroToOneY = screenAbsoluteY / this._canvas.height; + wgslX = zeroToOneX * 2 - 1; + wgslY = zeroToOneY * 2 - 1; + + const cellIndex = y * lineIndexCount + x * Constants.IndicesPerCell; + cellBuffer[cellIndex + 0] = wgslX; // x + cellBuffer[cellIndex + 1] = -wgslY; // y + cellBuffer[cellIndex + 2] = 0; + cellBuffer[cellIndex + 3] = 0; + cellBuffer[cellIndex + 4] = glyph.index; // textureIndex + cellBuffer[cellIndex + 5] = 0; + } + } + + const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; + + // Write buffer and swap it out to unblock writes + // this._device.queue.writeBuffer( + // this._cellBindBuffer, + // (startLineNumber - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, + // // TODO: this cell buffer actually only needs to be the size of the viewport if we are only uploading a range + // // at the maximum each frame + // cellBuffer, + // (startLineNumber - 1) * lineIndexCount, + // (stopLineNumber - startLineNumber) * lineIndexCount + // ); + this._device.queue.writeBuffer( + this._cellBindBuffer, + 0, + cellBuffer + ); + + this._cellValuesBufferActiveIndex = (this._cellValuesBufferActiveIndex + 1) % 2; + + return visibleObjectCount; + } + + draw(pass: GPURenderPassEncoder, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): void { + const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; + + // console.log('draw', + // { + // ctx, + // startLineNumber, + // stopLineNumber, + // deltaTop + // }, + // 6, // square verticies + // visibleObjectCount, + // undefined, + // (startLineNumber - 1) * FullFileRenderStrategy._columnCount + // ); + pass.draw( + 6, // square verticies + visibleObjectCount, + undefined, + (startLineNumber - 1) * FullFileRenderStrategy._columnCount + ); + } +} + +// #endregion Full file render strategy From 4dd73e25c0f359fc1f90f196f13a0f7103a2a69b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 08:14:36 -0700 Subject: [PATCH 0022/2222] Progress --- .../editor/browser/view/gpu/gpuViewLayer.ts | 94 ++++++++++++------- 1 file changed, 60 insertions(+), 34 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 960a3916ee0..6987c31a869 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -36,6 +36,7 @@ const enum BindingId { Texture = 3, Uniforms = 4, TextureInfoUniform = 5, + ScrollOffset = 6, } export class GpuViewLayerRenderer { @@ -598,6 +599,10 @@ struct DynamicUnitInfo { unused2: f32 }; +struct ScrollOffset { + offset: vec2f +} + struct VSOutput { @builtin(position) position: vec4f, @location(0) texcoord: vec2f, @@ -608,6 +613,7 @@ struct VSOutput { @group(0) @binding(${BindingId.SpriteInfo}) var spriteInfo: array; @group(0) @binding(${BindingId.DynamicUnitInfo}) var dynamicUnitInfoStructs: array; +@group(0) @binding(${BindingId.ScrollOffset}) var scrollOffset: ScrollOffset; @vertex fn vs( vert: Vertex, @@ -620,7 +626,7 @@ struct VSOutput { var vsOut: VSOutput; // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 vsOut.position = vec4f( - (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position - ((spriteInfo.origin * 2) / uniforms.canvasDimensions), + (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position - ((spriteInfo.origin * 2) / uniforms.canvasDimensions) + ((scrollOffset.offset * 2) / uniforms.canvasDimensions), 0.0, 1.0 ); @@ -654,11 +660,15 @@ class FullFileRenderStrategy implements IRenderStrategy< private _cellBindBuffer!: GPUBuffer; private _cellValueBuffers!: ArrayBuffer[]; - private _cellValuesBufferActiveIndex: number = 0; + private _activeDoubleBufferIndex: number = 0; + + private _scrollOffsetBindBuffer!: GPUBuffer; + private _scrollOffsetValueBuffers!: Float32Array[]; get bindGroupEntries(): GPUBindGroupEntry[] { return [ - { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._cellBindBuffer } } + { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._cellBindBuffer } }, + { binding: BindingId.ScrollOffset, resource: { buffer: this._scrollOffsetBindBuffer } } ]; } @@ -681,6 +691,17 @@ class FullFileRenderStrategy implements IRenderStrategy< new ArrayBuffer(bufferSize), new ArrayBuffer(bufferSize), ]; + + const scrollOffsetBufferSize = 2; + this._scrollOffsetBindBuffer = this._device.createBuffer({ + label: 'Scroll offset buffer', + size: scrollOffsetBufferSize * Float32Array.BYTES_PER_ELEMENT, + usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, + }); + this._scrollOffsetValueBuffers = [ + new Float32Array(scrollOffsetBufferSize), + new Float32Array(scrollOffsetBufferSize), + ]; } update(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { @@ -693,15 +714,20 @@ class FullFileRenderStrategy implements IRenderStrategy< let wgslX: number = 0; let wgslY: number = 0; - const viewportData = this._viewportData; - const activeWindow = getActiveWindow(); - const cellBuffer = new Float32Array(this._cellValueBuffers[this._cellValuesBufferActiveIndex]); - const lineIndexCount = FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; - + // Update scroll offset let scrollTop = parseInt(this._canvas.parentElement!.getAttribute('data-adjusted-scroll-top')!); if (Number.isNaN(scrollTop)) { scrollTop = 0; } + const scrollOffsetBuffer = this._scrollOffsetValueBuffers[this._activeDoubleBufferIndex]; + scrollOffsetBuffer[1] = scrollTop; + this._device.queue.writeBuffer(this._scrollOffsetBindBuffer, 0, scrollOffsetBuffer); + + // Update cell data + const viewportData = this._viewportData; + const activeWindow = getActiveWindow(); + const cellBuffer = new Float32Array(this._cellValueBuffers[this._activeDoubleBufferIndex]); + const lineIndexCount = FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; for (y = startLineNumber; y <= stopLineNumber; y++) { const viewLineRenderingData = viewportData.getViewLineRenderingData(y); @@ -711,13 +737,13 @@ class FullFileRenderStrategy implements IRenderStrategy< screenAbsoluteX = x * 7 * activeWindow.devicePixelRatio; // TODO: Send scroll offset instead of setting it here such that the cell data doesn't need to change when scrolling - screenAbsoluteY = Math.round(Math.round(-scrollTop + deltaTop[y - startLineNumber]) * activeWindow.devicePixelRatio); + screenAbsoluteY = Math.round(deltaTop[y - startLineNumber] * activeWindow.devicePixelRatio); zeroToOneX = screenAbsoluteX / this._canvas.width; zeroToOneY = screenAbsoluteY / this._canvas.height; wgslX = zeroToOneX * 2 - 1; wgslY = zeroToOneY * 2 - 1; - const cellIndex = y * lineIndexCount + x * Constants.IndicesPerCell; + const cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x - 1)) * Constants.IndicesPerCell; cellBuffer[cellIndex + 0] = wgslX; // x cellBuffer[cellIndex + 1] = -wgslY; // y cellBuffer[cellIndex + 2] = 0; @@ -730,22 +756,22 @@ class FullFileRenderStrategy implements IRenderStrategy< const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; // Write buffer and swap it out to unblock writes - // this._device.queue.writeBuffer( - // this._cellBindBuffer, - // (startLineNumber - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, - // // TODO: this cell buffer actually only needs to be the size of the viewport if we are only uploading a range - // // at the maximum each frame - // cellBuffer, - // (startLineNumber - 1) * lineIndexCount, - // (stopLineNumber - startLineNumber) * lineIndexCount - // ); this._device.queue.writeBuffer( this._cellBindBuffer, - 0, - cellBuffer + (startLineNumber - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, + // TODO: this cell buffer actually only needs to be the size of the viewport if we are only uploading a range + // at the maximum each frame + cellBuffer.buffer, + (startLineNumber - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, + (stopLineNumber - startLineNumber) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT ); + // this._device.queue.writeBuffer( + // this._cellBindBuffer, + // 0, + // cellBuffer + // ); - this._cellValuesBufferActiveIndex = (this._cellValuesBufferActiveIndex + 1) % 2; + this._activeDoubleBufferIndex = (this._activeDoubleBufferIndex + 1) % 2; return visibleObjectCount; } @@ -753,18 +779,18 @@ class FullFileRenderStrategy implements IRenderStrategy< draw(pass: GPURenderPassEncoder, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): void { const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; - // console.log('draw', - // { - // ctx, - // startLineNumber, - // stopLineNumber, - // deltaTop - // }, - // 6, // square verticies - // visibleObjectCount, - // undefined, - // (startLineNumber - 1) * FullFileRenderStrategy._columnCount - // ); + console.log('draw', + { + ctx, + startLineNumber, + stopLineNumber, + deltaTop + }, + 6, // square verticies + visibleObjectCount, + undefined, + (startLineNumber - 1) * FullFileRenderStrategy._columnCount + ); pass.draw( 6, // square verticies visibleObjectCount, From a56c3cfab224f3e7efa62cbbdcb830778a32218a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:28:40 -0700 Subject: [PATCH 0023/2222] Fix scroll offset --- .../editor/browser/view/gpu/gpuViewLayer.ts | 22 ++++++------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 6987c31a869..adfdd5ade0a 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -660,7 +660,7 @@ class FullFileRenderStrategy implements IRenderStrategy< private _cellBindBuffer!: GPUBuffer; private _cellValueBuffers!: ArrayBuffer[]; - private _activeDoubleBufferIndex: number = 0; + private _activeDoubleBufferIndex: 0 | 1 = 0; private _scrollOffsetBindBuffer!: GPUBuffer; private _scrollOffsetValueBuffers!: Float32Array[]; @@ -714,10 +714,14 @@ class FullFileRenderStrategy implements IRenderStrategy< let wgslX: number = 0; let wgslY: number = 0; + const activeWindow = getActiveWindow(); + // Update scroll offset let scrollTop = parseInt(this._canvas.parentElement!.getAttribute('data-adjusted-scroll-top')!); if (Number.isNaN(scrollTop)) { scrollTop = 0; + } else { + scrollTop *= activeWindow.devicePixelRatio; } const scrollOffsetBuffer = this._scrollOffsetValueBuffers[this._activeDoubleBufferIndex]; scrollOffsetBuffer[1] = scrollTop; @@ -725,7 +729,6 @@ class FullFileRenderStrategy implements IRenderStrategy< // Update cell data const viewportData = this._viewportData; - const activeWindow = getActiveWindow(); const cellBuffer = new Float32Array(this._cellValueBuffers[this._activeDoubleBufferIndex]); const lineIndexCount = FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; @@ -765,13 +768,14 @@ class FullFileRenderStrategy implements IRenderStrategy< (startLineNumber - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, (stopLineNumber - startLineNumber) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT ); + // HACK: Replace entire buffer for testing purposes // this._device.queue.writeBuffer( // this._cellBindBuffer, // 0, // cellBuffer // ); - this._activeDoubleBufferIndex = (this._activeDoubleBufferIndex + 1) % 2; + this._activeDoubleBufferIndex = this._activeDoubleBufferIndex ? 0 : 1; return visibleObjectCount; } @@ -779,18 +783,6 @@ class FullFileRenderStrategy implements IRenderStrategy< draw(pass: GPURenderPassEncoder, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): void { const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; - console.log('draw', - { - ctx, - startLineNumber, - stopLineNumber, - deltaTop - }, - 6, // square verticies - visibleObjectCount, - undefined, - (startLineNumber - 1) * FullFileRenderStrategy._columnCount - ); pass.draw( 6, // square verticies visibleObjectCount, From 00fb56c91ef36c3db1e08673bbf25e5f50435f91 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:29:35 -0700 Subject: [PATCH 0024/2222] Improve types --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index adfdd5ade0a..4f0d678dc54 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -659,11 +659,11 @@ class FullFileRenderStrategy implements IRenderStrategy< readonly wgsl: string = fullFileRenderStrategyWgsl; private _cellBindBuffer!: GPUBuffer; - private _cellValueBuffers!: ArrayBuffer[]; + private _cellValueBuffers!: [ArrayBuffer, ArrayBuffer]; private _activeDoubleBufferIndex: 0 | 1 = 0; private _scrollOffsetBindBuffer!: GPUBuffer; - private _scrollOffsetValueBuffers!: Float32Array[]; + private _scrollOffsetValueBuffers!: [Float32Array, Float32Array]; get bindGroupEntries(): GPUBindGroupEntry[] { return [ From 07f647a197a684feb7895d96318c68a972927b37 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:35:24 -0700 Subject: [PATCH 0025/2222] Cache up to date lines in buffer --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 4f0d678dc54..cf04744a13e 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -662,6 +662,8 @@ class FullFileRenderStrategy implements IRenderStrategy< private _cellValueBuffers!: [ArrayBuffer, ArrayBuffer]; private _activeDoubleBufferIndex: 0 | 1 = 0; + private readonly _upToDateLines: [Set, Set] = [new Set(), new Set()]; + private _scrollOffsetBindBuffer!: GPUBuffer; private _scrollOffsetValueBuffers!: [Float32Array, Float32Array]; @@ -732,7 +734,12 @@ class FullFileRenderStrategy implements IRenderStrategy< const cellBuffer = new Float32Array(this._cellValueBuffers[this._activeDoubleBufferIndex]); const lineIndexCount = FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; + const upToDateLines = this._upToDateLines[this._activeDoubleBufferIndex]; + for (y = startLineNumber; y <= stopLineNumber; y++) { + if (upToDateLines.has(y)) { + continue; + } const viewLineRenderingData = viewportData.getViewLineRenderingData(y); const content = viewLineRenderingData.content; for (x = 0; x < FullFileRenderStrategy._columnCount; x++) { @@ -754,8 +761,10 @@ class FullFileRenderStrategy implements IRenderStrategy< cellBuffer[cellIndex + 4] = glyph.index; // textureIndex cellBuffer[cellIndex + 5] = 0; } + upToDateLines.add(y); } + // TODO: Write sub set of buffer const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; // Write buffer and swap it out to unblock writes From 702e78178e751e9057d551a93e68d444d706363f Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:38:44 -0700 Subject: [PATCH 0026/2222] Only write dirty lines --- .../editor/browser/view/gpu/gpuViewLayer.ts | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index cf04744a13e..0d2ff4ad067 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -735,11 +735,16 @@ class FullFileRenderStrategy implements IRenderStrategy< const lineIndexCount = FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; const upToDateLines = this._upToDateLines[this._activeDoubleBufferIndex]; + let dirtyLineStart = Number.MAX_SAFE_INTEGER; + let dirtyLineEnd = 0; for (y = startLineNumber; y <= stopLineNumber; y++) { if (upToDateLines.has(y)) { continue; } + dirtyLineStart = Math.min(dirtyLineStart, y); + dirtyLineEnd = Math.max(dirtyLineEnd, y); + const viewLineRenderingData = viewportData.getViewLineRenderingData(y); const content = viewLineRenderingData.content; for (x = 0; x < FullFileRenderStrategy._columnCount; x++) { @@ -767,16 +772,19 @@ class FullFileRenderStrategy implements IRenderStrategy< // TODO: Write sub set of buffer const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; - // Write buffer and swap it out to unblock writes - this._device.queue.writeBuffer( - this._cellBindBuffer, - (startLineNumber - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, - // TODO: this cell buffer actually only needs to be the size of the viewport if we are only uploading a range - // at the maximum each frame - cellBuffer.buffer, - (startLineNumber - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, - (stopLineNumber - startLineNumber) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT - ); + // Only write when there is changed data + if (dirtyLineStart <= dirtyLineEnd) { + // Write buffer and swap it out to unblock writes + this._device.queue.writeBuffer( + this._cellBindBuffer, + (dirtyLineStart - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, + // TODO: this cell buffer actually only needs to be the size of the viewport if we are only uploading a range + // at the maximum each frame + cellBuffer.buffer, + (dirtyLineStart - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, + (dirtyLineEnd - dirtyLineStart) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT + ); + } // HACK: Replace entire buffer for testing purposes // this._device.queue.writeBuffer( // this._cellBindBuffer, From 87d1332d9008e54bf2ba54bd940ec37f1df8847c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:46:10 -0700 Subject: [PATCH 0027/2222] Increase tab width --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 0d2ff4ad067..95b28bae02d 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -715,6 +715,8 @@ class FullFileRenderStrategy implements IRenderStrategy< let zeroToOneY: number = 0; let wgslX: number = 0; let wgslY: number = 0; + let chars: string = ''; + let xOffset: number = 0; const activeWindow = getActiveWindow(); @@ -747,10 +749,20 @@ class FullFileRenderStrategy implements IRenderStrategy< const viewLineRenderingData = viewportData.getViewLineRenderingData(y); const content = viewLineRenderingData.content; + xOffset = 0; for (x = 0; x < FullFileRenderStrategy._columnCount; x++) { const glyph = this._textureAtlas.getGlyph(content, x); + chars = content[x]; + switch (chars) { + case ' ': + continue; + case '\t': + // TODO: Pull actual tab size + xOffset += 3; + break; + } - screenAbsoluteX = x * 7 * activeWindow.devicePixelRatio; + screenAbsoluteX = (x + xOffset) * 7 * activeWindow.devicePixelRatio; // TODO: Send scroll offset instead of setting it here such that the cell data doesn't need to change when scrolling screenAbsoluteY = Math.round(deltaTop[y - startLineNumber] * activeWindow.devicePixelRatio); zeroToOneX = screenAbsoluteX / this._canvas.width; @@ -758,7 +770,7 @@ class FullFileRenderStrategy implements IRenderStrategy< wgslX = zeroToOneX * 2 - 1; wgslY = zeroToOneY * 2 - 1; - const cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x - 1)) * Constants.IndicesPerCell; + const cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x - 1 + xOffset)) * Constants.IndicesPerCell; cellBuffer[cellIndex + 0] = wgslX; // x cellBuffer[cellIndex + 1] = -wgslY; // y cellBuffer[cellIndex + 2] = 0; @@ -769,7 +781,6 @@ class FullFileRenderStrategy implements IRenderStrategy< upToDateLines.add(y); } - // TODO: Write sub set of buffer const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; // Only write when there is changed data From 394886f8de1bd67a94b58aa6786eeb01413f3c6e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:58:39 -0700 Subject: [PATCH 0028/2222] Fix alignment of chars --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 4 ++-- src/vs/editor/browser/view/gpu/textureAtlas.ts | 10 ++++++---- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 95b28bae02d..94c4fc8aa32 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -626,7 +626,7 @@ struct VSOutput { var vsOut: VSOutput; // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 vsOut.position = vec4f( - (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position - ((spriteInfo.origin * 2) / uniforms.canvasDimensions) + ((scrollOffset.offset * 2) / uniforms.canvasDimensions), + (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position + ((spriteInfo.origin * vec2f(2, -2)) / uniforms.canvasDimensions) + ((scrollOffset.offset * 2) / uniforms.canvasDimensions), 0.0, 1.0 ); @@ -764,7 +764,7 @@ class FullFileRenderStrategy implements IRenderStrategy< screenAbsoluteX = (x + xOffset) * 7 * activeWindow.devicePixelRatio; // TODO: Send scroll offset instead of setting it here such that the cell data doesn't need to change when scrolling - screenAbsoluteY = Math.round(deltaTop[y - startLineNumber] * activeWindow.devicePixelRatio); + screenAbsoluteY = (Math.round(deltaTop[y - startLineNumber] + viewportData.lineHeight) * activeWindow.devicePixelRatio); zeroToOneX = screenAbsoluteX / this._canvas.width; zeroToOneY = screenAbsoluteY / this._canvas.height; wgslX = zeroToOneX * 2 - 1; diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 8e00750242c..be742ab4202 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -85,7 +85,7 @@ class GlyphRasterizer extends Disposable { willReadFrequently: true })); this._ctx.font = `${this._fontSize}px ${fontFamily}`; - this._ctx.textBaseline = 'alphabetic'; + this._ctx.textBaseline = 'top'; this._ctx.fillStyle = '#FFFFFF'; } @@ -95,7 +95,9 @@ class GlyphRasterizer extends Disposable { this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); // TODO: Draw in middle using alphabetical baseline - this._ctx.fillText(chars, this._fontSize * 2, this._fontSize); + const originX = this._fontSize; + const originY = this._fontSize; + this._ctx.fillText(chars, originX, originY); const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); // TODO: Hot path: Reuse object @@ -104,8 +106,8 @@ class GlyphRasterizer extends Disposable { source: this._canvas, boundingBox, originOffset: { - x: boundingBox.left - this._fontSize, - y: boundingBox.top - this._fontSize + x: boundingBox.left - originX, + y: boundingBox.top - originY } }; return result; From 97b5f42a5eb42d49af58f18345493cf6c1084b34 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 16:59:39 -0700 Subject: [PATCH 0029/2222] Fix glyph dims --- src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index a5904eeb8d9..d4091d19320 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -37,8 +37,8 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { // TODO: Handle end of atlas page // Draw glyph - const glyphWidth = rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left; - const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top; + const glyphWidth = rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left + 1; + const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + 1; // TODO: Prefer putImageData as it doesn't do blending or scaling this._ctx.drawImage( rasterizedGlyph.source, From 8b571152c00b00a080cc5ac4870dc4105321a49b Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 30 Mar 2024 17:14:26 -0700 Subject: [PATCH 0030/2222] Print token todos --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 94c4fc8aa32..79ae8f6528a 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -750,6 +750,12 @@ class FullFileRenderStrategy implements IRenderStrategy< const viewLineRenderingData = viewportData.getViewLineRenderingData(y); const content = viewLineRenderingData.content; xOffset = 0; + // TODO: Handle colors via viewLineRenderingData.tokens + console.log(viewLineRenderingData.tokens); + console.log('fgs'); + for (let i = 0; i < viewLineRenderingData.tokens.getCount(); i++) { + console.log(` ${viewLineRenderingData.tokens.getForeground(i)}`); + } for (x = 0; x < FullFileRenderStrategy._columnCount; x++) { const glyph = this._textureAtlas.getGlyph(content, x); chars = content[x]; @@ -763,8 +769,7 @@ class FullFileRenderStrategy implements IRenderStrategy< } screenAbsoluteX = (x + xOffset) * 7 * activeWindow.devicePixelRatio; - // TODO: Send scroll offset instead of setting it here such that the cell data doesn't need to change when scrolling - screenAbsoluteY = (Math.round(deltaTop[y - startLineNumber] + viewportData.lineHeight) * activeWindow.devicePixelRatio); + screenAbsoluteY = Math.round(deltaTop[y - startLineNumber] * activeWindow.devicePixelRatio); zeroToOneX = screenAbsoluteX / this._canvas.width; zeroToOneY = screenAbsoluteY / this._canvas.height; wgslX = zeroToOneX * 2 - 1; From 717ec22ac7a0cacc1d5f984c661ae091477cb01d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 Mar 2024 07:15:57 -0700 Subject: [PATCH 0031/2222] Colors, atlas warm up, texture atlas preview scaling --- src/vs/editor/browser/view.ts | 6 +- .../editor/browser/view/gpu/gpuViewLayer.ts | 341 ++++++------------ src/vs/editor/browser/view/gpu/multiKeyMap.ts | 58 +++ src/vs/editor/browser/view/gpu/taskQueue.ts | 166 +++++++++ .../editor/browser/view/gpu/textureAtlas.ts | 57 ++- src/vs/editor/browser/view/viewLayer.ts | 8 +- src/vs/editor/browser/view/viewOverlays.ts | 22 +- .../browser/viewParts/lines/viewLines.ts | 9 +- 8 files changed, 414 insertions(+), 253 deletions(-) create mode 100644 src/vs/editor/browser/view/gpu/multiKeyMap.ts create mode 100644 src/vs/editor/browser/view/gpu/taskQueue.ts diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index a803234c4b4..fb7d9c09f24 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -147,7 +147,7 @@ export class View extends ViewEventHandler { this._viewParts.push(this._scrollbar); // View Lines - this._viewLines = new ViewLines(this._context, this._linesContent); + this._viewLines = this._instantiationService.createInstance(ViewLines, this._context, this._linesContent); // View Zones this._viewZones = new ViewZones(this._context); @@ -161,7 +161,7 @@ export class View extends ViewEventHandler { const scrollDecoration = new ScrollDecorationViewPart(this._context); this._viewParts.push(scrollDecoration); - const contentViewOverlays = new ContentViewOverlays(this._context); + const contentViewOverlays = this._instantiationService.createInstance(ContentViewOverlays, this._context); this._viewParts.push(contentViewOverlays); contentViewOverlays.addDynamicOverlay(new CurrentLineHighlightOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new SelectionsOverlay(this._context)); @@ -169,7 +169,7 @@ export class View extends ViewEventHandler { contentViewOverlays.addDynamicOverlay(new DecorationsOverlay(this._context)); contentViewOverlays.addDynamicOverlay(new WhitespaceOverlay(this._context)); - const marginViewOverlays = new MarginViewOverlays(this._context); + const marginViewOverlays = this._instantiationService.createInstance(MarginViewOverlays, this._context); this._viewParts.push(marginViewOverlays); marginViewOverlays.addDynamicOverlay(new CurrentLineMarginHighlightOverlay(this._context)); marginViewOverlays.addDynamicOverlay(new MarginViewLineDecorationsOverlay(this._context)); diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 79ae8f6528a..baa92b9e83b 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -8,7 +8,10 @@ import { debounce } from 'vs/base/common/decorators'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { TextureAtlas } from 'vs/editor/browser/view/gpu/textureAtlas'; import type { IVisibleLine, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; +import type { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; interface IRendererContext { rendLineNumberStart: number; @@ -71,7 +74,12 @@ export class GpuViewLayerRenderer { private _renderStrategy!: IRenderStrategy; - constructor(domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData) { + constructor( + domNode: HTMLCanvasElement, + host: IVisibleLinesHost, + viewportData: ViewportData, + @IInstantiationService private readonly _instantiationService: IInstantiationService + ) { this.domNode = domNode; this.host = host; this.viewportData = viewportData; @@ -101,11 +109,13 @@ export class GpuViewLayerRenderer { // Create texture atlas if (!GpuViewLayerRenderer._textureAtlas) { - GpuViewLayerRenderer._textureAtlas = new TextureAtlas(this.domNode, this._device.limits.maxTextureDimension2D); + GpuViewLayerRenderer._textureAtlas = this._instantiationService.createInstance(TextureAtlas, this.domNode, this._device.limits.maxTextureDimension2D); GpuViewLayerRenderer._testCanvas = document.createElement('canvas'); - GpuViewLayerRenderer._testCanvas.width = 2048; - GpuViewLayerRenderer._testCanvas.height = 2048; + GpuViewLayerRenderer._testCanvas.width = this._device.limits.maxTextureDimension2D; + GpuViewLayerRenderer._testCanvas.height = this._device.limits.maxTextureDimension2D; + GpuViewLayerRenderer._testCanvas.style.width = `${this._device.limits.maxTextureDimension2D / getActiveWindow().devicePixelRatio}px`; + GpuViewLayerRenderer._testCanvas.style.height = `${this._device.limits.maxTextureDimension2D / getActiveWindow().devicePixelRatio}px`; GpuViewLayerRenderer._testCanvas.style.position = 'absolute'; GpuViewLayerRenderer._testCanvas.style.top = '0'; GpuViewLayerRenderer._testCanvas.style.left = '0'; @@ -118,7 +128,7 @@ export class GpuViewLayerRenderer { // this._renderStrategy = new NaiveViewportRenderStrategy(this._device, this.domNode, this.viewportData, GpuViewLayerRenderer._textureAtlas); - this._renderStrategy = new FullFileRenderStrategy(this._device, this.domNode, this.viewportData, GpuViewLayerRenderer._textureAtlas); + this._renderStrategy = this._instantiationService.createInstance(FullFileRenderStrategy, this._device, this.domNode, this.viewportData, GpuViewLayerRenderer._textureAtlas); const module = this._device.createShaderModule({ label: 'ViewLayer shader module', @@ -384,193 +394,6 @@ interface IRenderStrategy { draw?(pass: GPURenderPassEncoder, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): void; } -// #region Naive viewport render strategy - -const naiveViewportRenderStrategyWgsl = ` -struct Uniforms { - canvasDimensions: vec2f, -}; - -struct TextureInfoUniform { - spriteSheetSize: vec2f, -} - -struct SpriteInfo { - position: vec2f, - size: vec2f, - origin: vec2f, -}; - -struct Vertex { - @location(0) position: vec2f, -}; - -struct DynamicUnitInfo { - position: vec2f, - unused1: vec2f, - textureIndex: f32, - unused2: f32 -}; - -struct VSOutput { - @builtin(position) position: vec4f, - @location(0) texcoord: vec2f, -}; - -@group(0) @binding(${BindingId.Uniforms}) var uniforms: Uniforms; -@group(0) @binding(${BindingId.TextureInfoUniform}) var textureInfoUniform: TextureInfoUniform; - -@group(0) @binding(${BindingId.SpriteInfo}) var spriteInfo: array; -@group(0) @binding(${BindingId.DynamicUnitInfo}) var dynamicUnitInfoStructs: array; - -@vertex fn vs( - vert: Vertex, - @builtin(instance_index) instanceIndex: u32, - @builtin(vertex_index) vertexIndex : u32 -) -> VSOutput { - let dynamicUnitInfo = dynamicUnitInfoStructs[instanceIndex]; - let spriteInfo = spriteInfo[u32(dynamicUnitInfo.textureIndex)]; - - var vsOut: VSOutput; - // Multiple vert.position by 2,-2 to get it into clipspace which ranged from -1 to 1 - vsOut.position = vec4f( - (((vert.position * vec2f(2, -2)) / uniforms.canvasDimensions)) * spriteInfo.size + dynamicUnitInfo.position - ((spriteInfo.origin * 2) / uniforms.canvasDimensions), - 0.0, - 1.0 - ); - - // Textures are flipped from natural direction on the y-axis, so flip it back - vsOut.texcoord = vert.position; - vsOut.texcoord = ( - // Sprite offset (0-1) - (spriteInfo.position / textureInfoUniform.spriteSheetSize) + - // Sprite coordinate (0-1) - (vsOut.texcoord * (spriteInfo.size / textureInfoUniform.spriteSheetSize)) - ); - - return vsOut; -} - -@group(0) @binding(${BindingId.TextureSampler}) var ourSampler: sampler; -@group(0) @binding(${BindingId.Texture}) var ourTexture: texture_2d; - -@fragment fn fs(vsOut: VSOutput) -> @location(0) vec4f { - return textureSample(ourTexture, ourSampler, vsOut.texcoord); -} -`; - -class NaiveViewportRenderStrategy implements IRenderStrategy { - readonly wgsl: string = naiveViewportRenderStrategyWgsl; - - private _cellBindBuffer!: GPUBuffer; - private _cellValueBuffers!: ArrayBuffer[]; - private _cellValuesBufferActiveIndex: number = 0; - - get bindGroupEntries(): GPUBindGroupEntry[] { - return [ - { binding: BindingId.DynamicUnitInfo, resource: { buffer: this._cellBindBuffer } } - ]; - } - - constructor( - private readonly _device: GPUDevice, - private readonly _canvas: HTMLCanvasElement, - private readonly _viewportData: ViewportData, - private readonly _textureAtlas: TextureAtlas - ) { - } - - initBuffers(): void { - // TODO: Grow/shrink buffer size dynamically - const cellCount = 10000; - const bufferSize = cellCount * Constants.IndicesPerCell * Float32Array.BYTES_PER_ELEMENT; - this._cellBindBuffer = this._device.createBuffer({ - label: 'Entity dynamic info buffer', - size: bufferSize, - usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST, - }); - this._cellValueBuffers = [ - new ArrayBuffer(bufferSize), - new ArrayBuffer(bufferSize), - ]; - } - - update(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { - const cellBuffer = new Float32Array(this._cellValueBuffers[this._cellValuesBufferActiveIndex]); - const visibleObjectCount = this._updateDataBuffer(cellBuffer, ctx, startLineNumber, stopLineNumber, deltaTop); - - // Write buffer and swap it out to unblock writes - this._device.queue.writeBuffer(this._cellBindBuffer, 0, cellBuffer, 0, visibleObjectCount * Constants.IndicesPerCell); - - this._cellValuesBufferActiveIndex = (this._cellValuesBufferActiveIndex + 1) % 2; - return visibleObjectCount; - } - - private _updateDataBuffer(dataBuffer: Float32Array, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { - // let chars: string = ''; - let screenAbsoluteX: number = 0; - let screenAbsoluteY: number = 0; - let zeroToOneX: number = 0; - let zeroToOneY: number = 0; - let wgslX: number = 0; - let wgslY: number = 0; - - const activeWindow = getActiveWindow(); - let charCount = 0; - let scrollTop = parseInt(this._canvas.parentElement!.getAttribute('data-adjusted-scroll-top')!); - if (Number.isNaN(scrollTop)) { - scrollTop = 0; - } - for (let lineNumber = startLineNumber; lineNumber <= stopLineNumber; lineNumber++) { - const y = Math.round(-scrollTop + deltaTop[lineNumber - startLineNumber]); - // Offscreen - if (y < 0) { - continue; - } - const content = this._viewportData.getViewLineRenderingData(lineNumber).content; - // console.log(content, 0, y); - for (let x = 0; x < content.length; x++) { - if (content.charAt(x) === ' ') { - continue; - } - // TODO: Handle tab - - // chars = content[x]; - const glyph = this._textureAtlas.getGlyph(content, x); - - // TODO: Move math to gpu - // TODO: Render using a line offset for partial line scrolling - // TODO: Sub-pixel rendering - screenAbsoluteX = x * 7 * activeWindow.devicePixelRatio; - // TODO: This +10 is because the glyph is being rendered in the wrong position - screenAbsoluteY = Math.round(y * activeWindow.devicePixelRatio); - zeroToOneX = screenAbsoluteX / this._canvas.width; - zeroToOneY = screenAbsoluteY / this._canvas.height; - wgslX = zeroToOneX * 2 - 1; - wgslY = zeroToOneY * 2 - 1; - - // TODO: We could upload the entire file as a grid, capping out lines at some reasonable amount (200?) - // Optimize for the common case and fallback to a slower path for long line files - // Doing the fast grid path would mean only the cell needs to change on data change, scrolling would simply change the start line index - // Even better would be a grid for standard sized lines (~120?) and then another buffer that handles larger lines in a slower but more dynamic way - - dataBuffer[charCount * Constants.IndicesPerCell + 0] = wgslX; // x - dataBuffer[charCount * Constants.IndicesPerCell + 1] = -wgslY; // y - dataBuffer[charCount * Constants.IndicesPerCell + 2] = 0; - dataBuffer[charCount * Constants.IndicesPerCell + 3] = 0; - dataBuffer[charCount * Constants.IndicesPerCell + 4] = glyph.index; // textureIndex - dataBuffer[charCount * Constants.IndicesPerCell + 5] = 0; - - charCount++; - } - } - // console.log('charCount: ' + charCount); - return charCount; - } -} - -// #endregion Naive viewport render strategy - // #region Full file render strategy const fullFileRenderStrategyWgsl = ` @@ -678,7 +501,8 @@ class FullFileRenderStrategy implements IRenderStrategy< private readonly _device: GPUDevice, private readonly _canvas: HTMLCanvasElement, private readonly _viewportData: ViewportData, - private readonly _textureAtlas: TextureAtlas + private readonly _textureAtlas: TextureAtlas, + @IThemeService private readonly _themeService: IThemeService, ) { } @@ -707,16 +531,18 @@ class FullFileRenderStrategy implements IRenderStrategy< } update(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { + let chars = ''; let y = 0; let x = 0; - let screenAbsoluteX: number = 0; - let screenAbsoluteY: number = 0; - let zeroToOneX: number = 0; - let zeroToOneY: number = 0; - let wgslX: number = 0; - let wgslY: number = 0; - let chars: string = ''; - let xOffset: number = 0; + let screenAbsoluteX = 0; + let screenAbsoluteY = 0; + let zeroToOneX = 0; + let zeroToOneY = 0; + let wgslX = 0; + let wgslY = 0; + let xOffset = 0; + + let tokens: IViewLineTokens; const activeWindow = getActiveWindow(); @@ -740,6 +566,11 @@ class FullFileRenderStrategy implements IRenderStrategy< let dirtyLineStart = Number.MAX_SAFE_INTEGER; let dirtyLineEnd = 0; + const colorMap = this._themeService.getColorTheme().tokenColorMap; + // const theme = this._themeService.getColorTheme() as ColorThemeData; + // const tokenStyle = theme.getTokenStyleMetadata(type, modifiers, defaultLanguage, true, definitions); + // console.log('colorMap', colorMap); + for (y = startLineNumber; y <= stopLineNumber; y++) { if (upToDateLines.has(y)) { continue; @@ -747,42 +578,90 @@ class FullFileRenderStrategy implements IRenderStrategy< dirtyLineStart = Math.min(dirtyLineStart, y); dirtyLineEnd = Math.max(dirtyLineEnd, y); - const viewLineRenderingData = viewportData.getViewLineRenderingData(y); - const content = viewLineRenderingData.content; + const lineData = viewportData.getViewLineRenderingData(y); + const content = lineData.content; xOffset = 0; + // TODO: Handle colors via viewLineRenderingData.tokens - console.log(viewLineRenderingData.tokens); - console.log('fgs'); - for (let i = 0; i < viewLineRenderingData.tokens.getCount(); i++) { - console.log(` ${viewLineRenderingData.tokens.getForeground(i)}`); - } - for (x = 0; x < FullFileRenderStrategy._columnCount; x++) { - const glyph = this._textureAtlas.getGlyph(content, x); - chars = content[x]; - switch (chars) { - case ' ': - continue; - case '\t': - // TODO: Pull actual tab size - xOffset += 3; + // console.log(lineData.tokens); + // console.log('fg'); + // for (let i = 0; i < lineData.tokens.getCount(); i++) { + // console.log(` ${lineData.tokens.getForeground(i)}`); + // } + + // See ViewLine#renderLine + // const renderLineInput = new RenderLineInput( + // options.useMonospaceOptimizations, + // options.canUseHalfwidthRightwardsArrow, + // lineData.content, + // lineData.continuesWithWrappedLine, + // lineData.isBasicASCII, + // lineData.containsRTL, + // lineData.minColumn - 1, + // lineData.tokens, + // actualInlineDecorations, + // lineData.tabSize, + // lineData.startVisibleColumn, + // options.spaceWidth, + // options.middotWidth, + // options.wsmiddotWidth, + // options.stopRenderingLineAfter, + // options.renderWhitespace, + // options.renderControlCharacters, + // options.fontLigatures !== EditorFontLigatures.OFF, + // selectionsOnLine + // ); + + tokens = lineData.tokens; + let tokenStartIndex = lineData.minColumn - 1; + let tokenFg: number; + for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) { + const tokenEndIndex = tokens.getEndOffset(tokenIndex); + if (tokenEndIndex <= tokenStartIndex) { + // The faux indent part of the line should have no token type + continue; + } + tokenFg = tokens.getForeground(tokenIndex); + // console.log(`token: start=${tokenStartIndex}, end=${tokenEndIndex}, fg=${colorMap[tokenFg]}`); + + + for (x = tokenStartIndex; x < tokenEndIndex; x++) { + // HACK: Prevent rendering past the end of the render buffer + // TODO: This needs to move to a dynamic long line rendering strategy + if (x > FullFileRenderStrategy._columnCount) { break; + } + chars = content.charAt(x); + switch (chars) { + case ' ': + continue; + case '\t': + // TODO: Pull actual tab size + xOffset += 3; + break; + } + + const glyph = this._textureAtlas.getGlyph(chars, tokenFg); + + screenAbsoluteX = (x + xOffset) * 7 * activeWindow.devicePixelRatio; + screenAbsoluteY = Math.round(deltaTop[y - startLineNumber] * activeWindow.devicePixelRatio); + zeroToOneX = screenAbsoluteX / this._canvas.width; + zeroToOneY = screenAbsoluteY / this._canvas.height; + wgslX = zeroToOneX * 2 - 1; + wgslY = zeroToOneY * 2 - 1; + + const cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x - 1 + xOffset)) * Constants.IndicesPerCell; + cellBuffer[cellIndex + 0] = wgslX; // x + cellBuffer[cellIndex + 1] = -wgslY; // y + cellBuffer[cellIndex + 2] = 0; + cellBuffer[cellIndex + 3] = 0; + cellBuffer[cellIndex + 4] = glyph.index; // textureIndex + cellBuffer[cellIndex + 5] = 0; } - screenAbsoluteX = (x + xOffset) * 7 * activeWindow.devicePixelRatio; - screenAbsoluteY = Math.round(deltaTop[y - startLineNumber] * activeWindow.devicePixelRatio); - zeroToOneX = screenAbsoluteX / this._canvas.width; - zeroToOneY = screenAbsoluteY / this._canvas.height; - wgslX = zeroToOneX * 2 - 1; - wgslY = zeroToOneY * 2 - 1; - - const cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x - 1 + xOffset)) * Constants.IndicesPerCell; - cellBuffer[cellIndex + 0] = wgslX; // x - cellBuffer[cellIndex + 1] = -wgslY; // y - cellBuffer[cellIndex + 2] = 0; - cellBuffer[cellIndex + 3] = 0; - cellBuffer[cellIndex + 4] = glyph.index; // textureIndex - cellBuffer[cellIndex + 5] = 0; + tokenStartIndex = tokenEndIndex; } + upToDateLines.add(y); } diff --git a/src/vs/editor/browser/view/gpu/multiKeyMap.ts b/src/vs/editor/browser/view/gpu/multiKeyMap.ts new file mode 100644 index 00000000000..52e49db234c --- /dev/null +++ b/src/vs/editor/browser/view/gpu/multiKeyMap.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Copyright (c) 2022 The xterm.js authors. All rights reserved. + * @license MIT + */ + +export class TwoKeyMap { + private _data: { [key: string | number]: { [key: string | number]: TValue | undefined } | undefined } = {}; + + public set(first: TFirst, second: TSecond, value: TValue): void { + if (!this._data[first]) { + this._data[first] = {}; + } + this._data[first as string | number]![second] = value; + } + + public get(first: TFirst, second: TSecond): TValue | undefined { + return this._data[first as string | number] ? this._data[first as string | number]![second] : undefined; + } + + public clear(): void { + this._data = {}; + } + + public *values() { + for (const first in this._data) { + for (const second in this._data[first]) { + const value = this._data[first]![second]; + if (value) { + yield value; + } + } + } + } +} + +export class FourKeyMap { + private _data: TwoKeyMap> = new TwoKeyMap(); + + public set(first: TFirst, second: TSecond, third: TThird, fourth: TFourth, value: TValue): void { + if (!this._data.get(first, second)) { + this._data.set(first, second, new TwoKeyMap()); + } + this._data.get(first, second)!.set(third, fourth, value); + } + + public get(first: TFirst, second: TSecond, third: TThird, fourth: TFourth): TValue | undefined { + return this._data.get(first, second)?.get(third, fourth); + } + + public clear(): void { + this._data.clear(); + } +} diff --git a/src/vs/editor/browser/view/gpu/taskQueue.ts b/src/vs/editor/browser/view/gpu/taskQueue.ts new file mode 100644 index 00000000000..5b96daf2567 --- /dev/null +++ b/src/vs/editor/browser/view/gpu/taskQueue.ts @@ -0,0 +1,166 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getActiveWindow } from 'vs/base/browser/dom'; + +/** + * Copyright (c) 2022 The xterm.js authors. All rights reserved. + * @license MIT + */ + +interface ITaskQueue { + /** + * Adds a task to the queue which will run in a future idle callback. + * To avoid perceivable stalls on the mainthread, tasks with heavy workload + * should split their work into smaller pieces and return `true` to get + * called again until the work is done (on falsy return value). + */ + enqueue(task: () => boolean | void): void; + + /** + * Flushes the queue, running all remaining tasks synchronously. + */ + flush(): void; + + /** + * Clears any remaining tasks from the queue, these will not be run. + */ + clear(): void; +} + +interface ITaskDeadline { + timeRemaining(): number; +} +type CallbackWithDeadline = (deadline: ITaskDeadline) => void; + +abstract class TaskQueue implements ITaskQueue { + private _tasks: (() => boolean | void)[] = []; + private _idleCallback?: number; + private _i = 0; + + protected abstract _requestCallback(callback: CallbackWithDeadline): number; + protected abstract _cancelCallback(identifier: number): void; + + public enqueue(task: () => boolean | void): void { + this._tasks.push(task); + this._start(); + } + + public flush(): void { + while (this._i < this._tasks.length) { + if (!this._tasks[this._i]()) { + this._i++; + } + } + this.clear(); + } + + public clear(): void { + if (this._idleCallback) { + this._cancelCallback(this._idleCallback); + this._idleCallback = undefined; + } + this._i = 0; + this._tasks.length = 0; + } + + private _start(): void { + if (!this._idleCallback) { + this._idleCallback = this._requestCallback(this._process.bind(this)); + } + } + + private _process(deadline: ITaskDeadline): void { + this._idleCallback = undefined; + let taskDuration = 0; + let longestTask = 0; + let lastDeadlineRemaining = deadline.timeRemaining(); + let deadlineRemaining = 0; + while (this._i < this._tasks.length) { + taskDuration = Date.now(); + if (!this._tasks[this._i]()) { + this._i++; + } + // other than performance.now, Date.now might not be stable (changes on wall clock changes), + // this is not an issue here as a clock change during a short running task is very unlikely + // in case it still happened and leads to negative duration, simply assume 1 msec + taskDuration = Math.max(1, Date.now() - taskDuration); + longestTask = Math.max(taskDuration, longestTask); + // Guess the following task will take a similar time to the longest task in this batch, allow + // additional room to try avoid exceeding the deadline + deadlineRemaining = deadline.timeRemaining(); + if (longestTask * 1.5 > deadlineRemaining) { + // Warn when the time exceeding the deadline is over 20ms, if this happens in practice the + // task should be split into sub-tasks to ensure the UI remains responsive. + if (lastDeadlineRemaining - taskDuration < -20) { + console.warn(`task queue exceeded allotted deadline by ${Math.abs(Math.round(lastDeadlineRemaining - taskDuration))}ms`); + } + this._start(); + return; + } + lastDeadlineRemaining = deadlineRemaining; + } + this.clear(); + } +} + +/** + * A queue of that runs tasks over several tasks via setTimeout, trying to maintain above 60 frames + * per second. The tasks will run in the order they are enqueued, but they will run some time later, + * and care should be taken to ensure they're non-urgent and will not introduce race conditions. + */ +export class PriorityTaskQueue extends TaskQueue { + protected _requestCallback(callback: CallbackWithDeadline): number { + return getActiveWindow().setTimeout(() => callback(this._createDeadline(16))); + } + + protected _cancelCallback(identifier: number): void { + getActiveWindow().clearTimeout(identifier); + } + + private _createDeadline(duration: number): ITaskDeadline { + const end = Date.now() + duration; + return { + timeRemaining: () => Math.max(0, end - Date.now()) + }; + } +} + +/** + * A queue of that runs tasks over several idle callbacks, trying to respect the idle callback's + * deadline given by the environment. The tasks will run in the order they are enqueued, but they + * will run some time later, and care should be taken to ensure they're non-urgent and will not + * introduce race conditions. + */ +export class IdleTaskQueue extends TaskQueue { + protected _requestCallback(callback: IdleRequestCallback): number { + return getActiveWindow().requestIdleCallback(callback); + } + + protected _cancelCallback(identifier: number): void { + getActiveWindow().cancelIdleCallback(identifier); + } +} + +/** + * An object that tracks a single debounced task that will run on the next idle frame. When called + * multiple times, only the last set task will run. + */ +export class DebouncedIdleTask { + private _queue: ITaskQueue; + + constructor() { + this._queue = new IdleTaskQueue(); + } + + public set(task: () => boolean | void): void { + this._queue.clear(); + this._queue.enqueue(task); + } + + public flush(): void { + this._queue.flush(); + } +} diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index be742ab4202..aa8851f4e04 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -4,22 +4,32 @@ *--------------------------------------------------------------------------------------------*/ import { getActiveWindow } from 'vs/base/browser/dom'; +import { Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; +import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; +import { IdleTaskQueue } from 'vs/editor/browser/view/gpu/taskQueue'; import { ITextureAtlasAllocator, TextureAtlasShelfAllocator } from 'vs/editor/browser/view/gpu/textureAtlasAllocator'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; export class TextureAtlas extends Disposable { private readonly _canvas: OffscreenCanvas; private readonly _ctx: OffscreenCanvasRenderingContext2D; - private readonly _glyphMap: Map = new Map(); + private readonly _glyphMap: TwoKeyMap = new TwoKeyMap(); + // HACK: This is an ordered set of glyphs to be passed to the GPU since currently the shader + // uses the index of the glyph. This should be improved to derive from _glyphMap + private readonly _glyphInOrderSet: Set = new Set(); public get glyphs(): IterableIterator { - return this._glyphMap.values(); + return this._glyphInOrderSet.values(); } private readonly _glyphRasterizer: GlyphRasterizer; private readonly _allocator: ITextureAtlasAllocator; + private _colorMap!: string[]; + private _warmUpTask?: IdleTaskQueue; + public get source(): OffscreenCanvas { return this._canvas; } @@ -27,7 +37,11 @@ export class TextureAtlas extends Disposable { public hasChanges = false; // TODO: Should pull in the font size from config instead of random dom node - constructor(parentDomNode: HTMLElement, maxTextureSize: number) { + constructor( + parentDomNode: HTMLElement, + maxTextureSize: number, + @IThemeService private readonly _themeService: IThemeService + ) { super(); this._canvas = new OffscreenCanvas(maxTextureSize, maxTextureSize); @@ -40,6 +54,12 @@ export class TextureAtlas extends Disposable { const fontSize = Math.ceil(parseInt(style.fontSize.replace('px', '')) * activeWindow.devicePixelRatio); this._ctx.font = `${fontSize}px ${style.fontFamily}`; + this._register(Event.runAndSubscribe(this._themeService.onDidColorThemeChange, () => { + // TODO: Clear entire atlas on theme change + this._colorMap = this._themeService.getColorTheme().tokenColorMap; + this._warmUpAtlas(); + })); + this._glyphRasterizer = new GlyphRasterizer(fontSize, style.fontFamily); this._allocator = new TextureAtlasShelfAllocator(this._canvas, this._ctx); @@ -51,25 +71,43 @@ export class TextureAtlas extends Disposable { } // TODO: Color, style etc. - public getGlyph(lineContent: string, glyphIndex: number): ITextureAtlasGlyph { - const chars = lineContent.charAt(glyphIndex); - let glyph: ITextureAtlasGlyph | undefined = this._glyphMap.get(chars); + public getGlyph(chars: string, tokenFg: number): ITextureAtlasGlyph { + let glyph: ITextureAtlasGlyph | undefined = this._glyphMap.get(chars, tokenFg); if (glyph) { return glyph; } - const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars); + const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars, this._colorMap[tokenFg]); glyph = this._allocator.allocate(rasterizedGlyph); - this._glyphMap.set(chars, glyph); + this._glyphMap.set(chars, tokenFg, glyph); + this._glyphInOrderSet.add(glyph); this.hasChanges = true; console.log('New glyph', { chars, + fg: this._colorMap[tokenFg], rasterizedGlyph, glyph }); return glyph; } + + /** + * Warms up the atlas by rasterizing all printable ASCII characters for each token color. This + * is distrubuted over multiple idle callbacks to avoid blocking the main thread. + */ + private _warmUpAtlas(): void { + // TODO: Clean up on dispose + this._warmUpTask?.clear(); + this._warmUpTask = new IdleTaskQueue(); + for (const tokenFg of this._colorMap.keys()) { + this._warmUpTask.enqueue(() => { + for (let code = 33; code < 126; code++) { + this.getGlyph(String.fromCharCode(code), tokenFg); + } + }); + } + } } class GlyphRasterizer extends Disposable { @@ -91,12 +129,13 @@ class GlyphRasterizer extends Disposable { // TODO: Support drawing multiple fonts and sizes // TODO: Should pull in the font size from config instead of random dom node - public rasterizeGlyph(chars: string): IRasterizedGlyph { + public rasterizeGlyph(chars: string, fg: string): IRasterizedGlyph { this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); // TODO: Draw in middle using alphabetical baseline const originX = this._fontSize; const originY = this._fontSize; + this._ctx.fillStyle = fg; this._ctx.fillText(chars, originX, originY); const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); diff --git a/src/vs/editor/browser/view/viewLayer.ts b/src/vs/editor/browser/view/viewLayer.ts index 948a739b4fd..c7befc25e51 100644 --- a/src/vs/editor/browser/view/viewLayer.ts +++ b/src/vs/editor/browser/view/viewLayer.ts @@ -13,6 +13,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { StringBuilder } from 'vs/editor/common/core/stringBuilder'; import * as viewEvents from 'vs/editor/common/viewEvents'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; /** * Represents a visible line @@ -260,7 +261,10 @@ export class VisibleLinesCollection { private readonly _canvas: HTMLCanvasElement; - constructor(host: IVisibleLinesHost) { + constructor( + host: IVisibleLinesHost, + @IInstantiationService private readonly _instantiationService: IInstantiationService + ) { this._host = host; this.domNode = this._createDomNode(); @@ -375,7 +379,7 @@ export class VisibleLinesCollection { } if (!this._gpuRenderer) { - this._gpuRenderer = new GpuViewLayerRenderer(this._canvas, this._host, viewportData); + this._gpuRenderer = this._instantiationService.createInstance(GpuViewLayerRenderer, this._canvas, this._host, viewportData); } renderer = this._gpuRenderer; renderer.update(viewportData); diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index 4d1b5a99581..b674d855fe4 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -14,6 +14,7 @@ import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; import * as viewEvents from 'vs/editor/common/viewEvents'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; export class ViewOverlays extends ViewPart implements IVisibleLinesHost { @@ -22,10 +23,13 @@ export class ViewOverlays extends ViewPart implements IVisibleLinesHost(this); + this._visibleLines = instantiationService.createInstance(VisibleLinesCollection, this); this.domNode = this._visibleLines.domNode; const options = this._context.configuration.options; @@ -207,8 +211,11 @@ export class ContentViewOverlays extends ViewOverlays { private _contentWidth: number; - constructor(context: ViewContext) { - super(context); + constructor( + context: ViewContext, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(context, instantiationService); const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); this._contentWidth = layoutInfo.contentWidth; @@ -241,8 +248,11 @@ export class MarginViewOverlays extends ViewOverlays { private _contentLeft: number; - constructor(context: ViewContext) { - super(context); + constructor( + context: ViewContext, + @IInstantiationService instantiationService: IInstantiationService + ) { + super(context, instantiationService); const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index 62928fb5ca3..d99b920e065 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -24,6 +24,7 @@ import * as viewEvents from 'vs/editor/common/viewEvents'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { Viewport } from 'vs/editor/common/viewModel'; import { ViewContext } from 'vs/editor/common/viewModel/viewContext'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; class LastRenderedData { @@ -120,11 +121,15 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, private _stickyScrollEnabled: boolean; private _maxNumberStickyLines: number; - constructor(context: ViewContext, linesContent: FastDomNode) { + constructor( + context: ViewContext, + linesContent: FastDomNode, + @IInstantiationService instantiationService: IInstantiationService + ) { super(context); this._linesContent = linesContent; this._textRangeRestingSpot = document.createElement('div'); - this._visibleLines = new VisibleLinesCollection(this); + this._visibleLines = instantiationService.createInstance(VisibleLinesCollection, this); this.domNode = this._visibleLines.domNode; const conf = this._context.configuration; From 71e5eee25c71c57238328b671008462b06a465fc Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 Mar 2024 07:20:38 -0700 Subject: [PATCH 0032/2222] Don't draw \t, warm tilde --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 18 +++++++++--------- src/vs/editor/browser/view/gpu/textureAtlas.ts | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index baa92b9e83b..8530b912a4a 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -117,8 +117,8 @@ export class GpuViewLayerRenderer { GpuViewLayerRenderer._testCanvas.style.width = `${this._device.limits.maxTextureDimension2D / getActiveWindow().devicePixelRatio}px`; GpuViewLayerRenderer._testCanvas.style.height = `${this._device.limits.maxTextureDimension2D / getActiveWindow().devicePixelRatio}px`; GpuViewLayerRenderer._testCanvas.style.position = 'absolute'; - GpuViewLayerRenderer._testCanvas.style.top = '0'; - GpuViewLayerRenderer._testCanvas.style.left = '0'; + GpuViewLayerRenderer._testCanvas.style.top = '2px'; + GpuViewLayerRenderer._testCanvas.style.left = '2px'; GpuViewLayerRenderer._testCanvas.style.zIndex = '10000'; GpuViewLayerRenderer._testCanvas.style.pointerEvents = 'none'; GpuViewLayerRenderer._testCtx = ensureNonNullable(GpuViewLayerRenderer._testCanvas.getContext('2d')); @@ -632,13 +632,13 @@ class FullFileRenderStrategy implements IRenderStrategy< break; } chars = content.charAt(x); - switch (chars) { - case ' ': - continue; - case '\t': - // TODO: Pull actual tab size - xOffset += 3; - break; + if (chars === ' ') { + continue; + } + if (chars === '\t') { + // TODO: Pull actual tab size + xOffset += 3; + continue; } const glyph = this._textureAtlas.getGlyph(chars, tokenFg); diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index aa8851f4e04..e1df0326722 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -102,7 +102,7 @@ export class TextureAtlas extends Disposable { this._warmUpTask = new IdleTaskQueue(); for (const tokenFg of this._colorMap.keys()) { this._warmUpTask.enqueue(() => { - for (let code = 33; code < 126; code++) { + for (let code = 33; code <= 126; code++) { this.getGlyph(String.fromCharCode(code), tokenFg); } }); From 8549f0ec7e42beb926bc79cc3e63bb53b7cdb4bd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 Mar 2024 07:29:02 -0700 Subject: [PATCH 0033/2222] Round x to render it properly --- .../editor/browser/view/gpu/gpuViewLayer.ts | 2 +- .../editor/browser/view/gpu/textureAtlas.ts | 37 +++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 8530b912a4a..e973036c15f 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -643,7 +643,7 @@ class FullFileRenderStrategy implements IRenderStrategy< const glyph = this._textureAtlas.getGlyph(chars, tokenFg); - screenAbsoluteX = (x + xOffset) * 7 * activeWindow.devicePixelRatio; + screenAbsoluteX = Math.round((x + xOffset) * 7 * activeWindow.devicePixelRatio); screenAbsoluteY = Math.round(deltaTop[y - startLineNumber] * activeWindow.devicePixelRatio); zeroToOneX = screenAbsoluteX / this._canvas.width; zeroToOneY = screenAbsoluteY / this._canvas.height; diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index e1df0326722..078bb58a883 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -12,6 +12,39 @@ import { IdleTaskQueue } from 'vs/editor/browser/view/gpu/taskQueue'; import { ITextureAtlasAllocator, TextureAtlasShelfAllocator } from 'vs/editor/browser/view/gpu/textureAtlasAllocator'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +// DEBUG: This helper can be used to draw image data to the console, it's commented out as we don't +// want to ship it, but this is very useful for investigating texture atlas issues. +// (console as any).image = (source: ImageData | HTMLCanvasElement, scale: number = 1) => { +// function getBox(width: number, height: number) { +// return { +// string: '+', +// style: 'font-size: 1px; padding: ' + Math.floor(height / 2) + 'px ' + Math.floor(width / 2) + 'px; line-height: ' + height + 'px;' +// }; +// } +// if (source instanceof HTMLCanvasElement) { +// source = source.getContext('2d')?.getImageData(0, 0, source.width, source.height)!; +// } +// const canvas = document.createElement('canvas'); +// canvas.width = source.width; +// canvas.height = source.height; +// const ctx = canvas.getContext('2d')!; +// ctx.putImageData(source, 0, 0); + +// const sw = source.width * scale; +// const sh = source.height * scale; +// const dim = getBox(sw, sh); +// console.log( +// `Image: ${source.width} x ${source.height}\n%c${dim.string}`, +// `${dim.style}background: url(${canvas.toDataURL()}); background-size: ${sw}px ${sh}px; background-repeat: no-repeat; color: transparent;` +// ); +// console.groupCollapsed('Zoomed'); +// console.log( +// `%c${dim.string}`, +// `${getBox(sw * 10, sh * 10).style}background: url(${canvas.toDataURL()}); background-size: ${sw * 10}px ${sh * 10}px; background-repeat: no-repeat; color: transparent; image-rendering: pixelated;-ms-interpolation-mode: nearest-neighbor;` +// ); +// console.groupEnd(); +// }; + export class TextureAtlas extends Disposable { private readonly _canvas: OffscreenCanvas; private readonly _ctx: OffscreenCanvasRenderingContext2D; @@ -149,6 +182,10 @@ class GlyphRasterizer extends Disposable { y: boundingBox.top - originY } }; + + // DEBUG: Show image data in console + // (console as any).image(imageData); + return result; } From 4d3544a80dd506ec00526392026f832cb0b40d57 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 Mar 2024 07:57:19 -0700 Subject: [PATCH 0034/2222] Clean up --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 1 + src/vs/editor/browser/view/gpu/textureAtlas.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index e973036c15f..df76dc099cd 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -504,6 +504,7 @@ class FullFileRenderStrategy implements IRenderStrategy< private readonly _textureAtlas: TextureAtlas, @IThemeService private readonly _themeService: IThemeService, ) { + // TODO: Detect when lines have been tokenized and clear _upToDateLines } initBuffers(): void { diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 078bb58a883..59c09432435 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getActiveWindow } from 'vs/base/browser/dom'; -import { Event } from 'vs/base/common/event'; +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; From 13f349743843340d748119cad069a3439597ca14 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:23:25 -0700 Subject: [PATCH 0035/2222] Fix draw buffer indexes --- .../editor/browser/view/gpu/gpuViewLayer.ts | 28 +++++++++++-------- .../editor/browser/view/gpu/textureAtlas.ts | 16 ++++++----- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index df76dc099cd..6639ee1d993 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -505,6 +505,8 @@ class FullFileRenderStrategy implements IRenderStrategy< @IThemeService private readonly _themeService: IThemeService, ) { // TODO: Detect when lines have been tokenized and clear _upToDateLines + const colorMap = this._themeService.getColorTheme().tokenColorMap; + console.log('colorMap', colorMap); } initBuffers(): void { @@ -567,10 +569,8 @@ class FullFileRenderStrategy implements IRenderStrategy< let dirtyLineStart = Number.MAX_SAFE_INTEGER; let dirtyLineEnd = 0; - const colorMap = this._themeService.getColorTheme().tokenColorMap; // const theme = this._themeService.getColorTheme() as ColorThemeData; // const tokenStyle = theme.getTokenStyleMetadata(type, modifiers, defaultLanguage, true, definitions); - // console.log('colorMap', colorMap); for (y = startLineNumber; y <= stopLineNumber; y++) { if (upToDateLines.has(y)) { @@ -651,7 +651,7 @@ class FullFileRenderStrategy implements IRenderStrategy< wgslX = zeroToOneX * 2 - 1; wgslY = zeroToOneY * 2 - 1; - const cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x - 1 + xOffset)) * Constants.IndicesPerCell; + const cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x + xOffset)) * Constants.IndicesPerCell; cellBuffer[cellIndex + 0] = wgslX; // x cellBuffer[cellIndex + 1] = -wgslY; // y cellBuffer[cellIndex + 2] = 0; @@ -666,7 +666,7 @@ class FullFileRenderStrategy implements IRenderStrategy< upToDateLines.add(y); } - const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; + const visibleObjectCount = (stopLineNumber - startLineNumber + 1) * lineIndexCount; // Only write when there is changed data if (dirtyLineStart <= dirtyLineEnd) { @@ -678,7 +678,7 @@ class FullFileRenderStrategy implements IRenderStrategy< // at the maximum each frame cellBuffer.buffer, (dirtyLineStart - 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT, - (dirtyLineEnd - dirtyLineStart) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT + (dirtyLineEnd - dirtyLineStart + 1) * lineIndexCount * Float32Array.BYTES_PER_ELEMENT ); } // HACK: Replace entire buffer for testing purposes @@ -694,14 +694,18 @@ class FullFileRenderStrategy implements IRenderStrategy< } draw(pass: GPURenderPassEncoder, ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): void { - const visibleObjectCount = (stopLineNumber - startLineNumber) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; + const visibleObjectCount = (stopLineNumber - startLineNumber + 1) * FullFileRenderStrategy._columnCount * Constants.IndicesPerCell; - pass.draw( - 6, // square verticies - visibleObjectCount, - undefined, - (startLineNumber - 1) * FullFileRenderStrategy._columnCount - ); + if (visibleObjectCount <= 0) { + console.error('Attempt to draw 0 objects'); + } else { + pass.draw( + 6, // square verticies + visibleObjectCount, + undefined, + (startLineNumber - 1) * FullFileRenderStrategy._columnCount + ); + } } } diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 59c09432435..ec68a482861 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { getActiveWindow } from 'vs/base/browser/dom'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Event } from 'vs/base/common/event'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; @@ -115,12 +115,14 @@ export class TextureAtlas extends Disposable { this._glyphInOrderSet.add(glyph); this.hasChanges = true; - console.log('New glyph', { - chars, - fg: this._colorMap[tokenFg], - rasterizedGlyph, - glyph - }); + if (!this._warmUpTask) { + console.debug('New glyph', { + chars, + fg: this._colorMap[tokenFg], + rasterizedGlyph, + glyph + }); + } return glyph; } From 9534f8f280fa5595ebb10ed7f08681174ce5cabd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:36:26 -0700 Subject: [PATCH 0036/2222] Get scroll top reliably --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 8 ++------ src/vs/editor/browser/viewParts/lines/viewLines.ts | 2 -- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 6639ee1d993..40cf07390f8 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -550,12 +550,8 @@ class FullFileRenderStrategy implements IRenderStrategy< const activeWindow = getActiveWindow(); // Update scroll offset - let scrollTop = parseInt(this._canvas.parentElement!.getAttribute('data-adjusted-scroll-top')!); - if (Number.isNaN(scrollTop)) { - scrollTop = 0; - } else { - scrollTop *= activeWindow.devicePixelRatio; - } + // TODO: Get at ViewModel in a safe way + const scrollTop = (this._viewportData as any)._model.viewLayout.getCurrentScrollTop() * activeWindow.devicePixelRatio; const scrollOffsetBuffer = this._scrollOffsetValueBuffers[this._activeDoubleBufferIndex]; scrollOffsetBuffer[1] = scrollTop; this._device.queue.writeBuffer(this._scrollOffsetBindBuffer, 0, scrollOffsetBuffer); diff --git a/src/vs/editor/browser/viewParts/lines/viewLines.ts b/src/vs/editor/browser/viewParts/lines/viewLines.ts index d99b920e065..0d387704a6f 100644 --- a/src/vs/editor/browser/viewParts/lines/viewLines.ts +++ b/src/vs/editor/browser/viewParts/lines/viewLines.ts @@ -667,8 +667,6 @@ export class ViewLines extends ViewPart implements IVisibleLinesHost, // (3) handle scrolling this._linesContent.setLayerHinting(this._canUseLayerHinting); this._linesContent.setContain('strict'); - const adjustedScrollTop = this._context.viewLayout.getCurrentScrollTop() - viewportData.bigNumbersDelta; - this.domNode.domNode.setAttribute('data-adjusted-scroll-top', adjustedScrollTop.toString()); // this._linesContent.setTop(-adjustedScrollTop); // this._linesContent.setLeft(-this._context.viewLayout.getCurrentScrollLeft()); } From 1ac620aeca2a8858d2c60e49e6c34ccc0dee04b9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sun, 31 Mar 2024 10:47:06 -0700 Subject: [PATCH 0037/2222] Clear end of line --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 40cf07390f8..33564d0ba1f 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -569,9 +569,10 @@ class FullFileRenderStrategy implements IRenderStrategy< // const tokenStyle = theme.getTokenStyleMetadata(type, modifiers, defaultLanguage, true, definitions); for (y = startLineNumber; y <= stopLineNumber; y++) { - if (upToDateLines.has(y)) { - continue; - } + // TODO: Update on dirty lines; is this known by line before rendering? + // if (upToDateLines.has(y)) { + // continue; + // } dirtyLineStart = Math.min(dirtyLineStart, y); dirtyLineEnd = Math.max(dirtyLineEnd, y); @@ -611,9 +612,10 @@ class FullFileRenderStrategy implements IRenderStrategy< tokens = lineData.tokens; let tokenStartIndex = lineData.minColumn - 1; + let tokenEndIndex = 0; let tokenFg: number; for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) { - const tokenEndIndex = tokens.getEndOffset(tokenIndex); + tokenEndIndex = tokens.getEndOffset(tokenIndex); if (tokenEndIndex <= tokenStartIndex) { // The faux indent part of the line should have no token type continue; @@ -659,6 +661,11 @@ class FullFileRenderStrategy implements IRenderStrategy< tokenStartIndex = tokenEndIndex; } + // Clear to end of line + const fillStartIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (tokenEndIndex + xOffset)) * Constants.IndicesPerCell; + const fillEndIndex = (y * FullFileRenderStrategy._columnCount) * Constants.IndicesPerCell; + cellBuffer.fill(0, fillStartIndex, fillEndIndex); + upToDateLines.add(y); } From bc2c9609c21a170a77df6e438203804417c4a9c7 Mon Sep 17 00:00:00 2001 From: Isabel Duan Date: Mon, 1 Apr 2024 19:24:48 +0000 Subject: [PATCH 0038/2222] add settings --- .../typescript-language-features/package.json | 98 +++++++++++++++++++ .../package.nls.json | 19 ++++ 2 files changed, 117 insertions(+) diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 34dad264553..d14576b338d 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -1068,6 +1068,104 @@ "description": "%typescript.preferences.renameMatchingJsxTags%", "scope": "language-overridable" }, + "typescript.preferences.organizeImports.presets": { + "type": "string", + "description": "%typescript.preferences.organizeImports.presets%", + "enum": [ + "auto", + "eslint sort-imports", + "eslint plugin-simple-import-sort", + "dprint" + ], + "enumDescriptions": [ + "%typescript.preferences.organizeImports.presets.auto%", + "%typescript.preferences.organizeImports.presets.eslintSortImports%", + "%typescript.preferences.organizeImports.presets.eslintPluginSimpleImportSort%", + "%typescript.preferences.organizeImports.presets.dprint%" + ], + "default": "auto" + }, + "typescript.preferences.organizeImports": { + "type": "object", + "description": "%typescript.preferences.organizeImports%", + "properties": { + "presets": { + "type": "string", + "description": "%typescript.preferences.organizeImports.presets%", + "enum": [ + "auto", + "eslint sort-imports", + "eslint plugin-simple-import-sort", + "dprint" + ], + "enumDescriptions": [ + "%typescript.preferences.organizeImports.presets.auto%", + "%typescript.preferences.organizeImports.presets.eslintSortImports%", + "%typescript.preferences.organizeImports.presets.eslintPluginSimpleImportSort%", + "%typescript.preferences.organizeImports.presets.dprint%" + ], + "default": "auto" + }, + "caseSensitivity": { + "type": "string", + "enum": [ + "auto", + "caseInsensitive", + "caseSensitive" + ], + "enumDescriptions": [ + "%typescript.preferences.organizeImports.caseSensitivity.auto%", + "%typescript.preferences.organizeImports.caseSensitivity.insensitive", + "%typescript.preferences.organizeImports.caseSensitivity.sensitive%" + ], + "default": "auto" + }, + "typeOrder": { + "type": "string", + "enum": [ + "auto", + "last", + "inline", + "first" + ], + "default": "auto" + }, + "unicodeCollation": { + "type": "string", + "enum": [ + "ordinal", + "unicode" + ], + "enumDescriptions": [ + "%typescript.preferences.organizeImports.unicodeCollation.ordinal%", + "%typescript.preferences.organizeImports.unicodeCollation.unicode%" + ], + "default": "ordinal" + }, + "locale": { + "type": "string", + "description": "%typescript.preferences.organizeImports.locale%" + }, + "numericCollation": { + "type": "boolean", + "description": "%typescript.preferences.organizeImports.numericCollation%" + }, + "accentCollation":{ + "type": "boolean", + "description": "%typescript.preferences.organizeImports.accentCollation%" + }, + "caseFirst": { + "type": "string", + "description": "%typescript.preferences.organizeImports.caseFirst%", + "enum": [ + "default", + "upper", + "lower" + ], + "default": "default" + } + } + }, "typescript.updateImportsOnFileMove.enabled": { "type": "string", "enum": [ diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 796b524a48c..df741e51802 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -185,6 +185,25 @@ "typescript.preferences.renameShorthandProperties.deprecationMessage": "The setting 'typescript.preferences.renameShorthandProperties' has been deprecated in favor of 'typescript.preferences.useAliasesForRenames'", "typescript.preferences.useAliasesForRenames": "Enable/disable introducing aliases for object shorthand properties during renames.", "typescript.preferences.renameMatchingJsxTags": "When on a JSX tag, try to rename the matching tag instead of renaming the symbol. Requires using TypeScript 5.1+ in the workspace.", + "typescript.preferences.organizeImports": "Preferences for organizing imports. Some presets are availible in `typescript.preferences.organizeImports.presets`", + "typescript.preferences.organizeImports.presets": "Some common presets for organizing imports. More specific options can be customized in `settings.json`", + "typescript.preferences.organizeImports.presets.auto": "Automatically detect the type order of named imports and the case sensitivity of existing imports in the file.", + "typescript.preferences.organizeImports.presets.eslintSortImports": "Eslint sort-imports default settings", + "typescript.preferences.organizeImports.presets.eslintPluginSimpleImportSort": "Eslint plugin-simple-import-sort default settings", + "typescript.preferences.organizeImports.presets.dprint": "dprint default settings", + "typescript.preferences.organizeImports.caseSensitivity.auto": "Detect case-sensitivity for import sorting", + "typescript.preferences.organizeImports.caseSensitivity.insensitive": "Sort imports case-insensitively", + "typescript.preferences.organizeImports.caseSensitivity.sensitive": "Sort imports case-sensitively", + "typescript.preferences.organizeImports.typeOrder.auto": "Detect where type-only named imports should be sorted", + "typescript.preferences.organizeImports.typeOrder.last": "Type only named imports are sorted to the end of the import list", + "typescript.preferences.organizeImports.typeOrder.inline": "Named imports are sorted by name only", + "typescript.preferences.organizeImports.typeOrder.first": "Type only named imports are sorted to the end of the import list", + "typescript.preferences.organizeImports.unicodeCollation.ordinal": "Sort imports using the numeric value of each code point", + "typescript.preferences.organizeImports.unicodeCollation.unicode": "Sort imports using the Unicode code collation", + "typescript.preferences.organizeImports.locale": "Overrides the locale used for collation. Specify 'auto' to use the UI locale. Only applies to organizeImportsCollation: 'unicode'", + "typescript.preferences.organizeImports.caseFirst": "Indicates whether upper-case comes before lower-case. Only applies to organizeImportsCollation: 'unicode'", + "typescript.preferences.organizeImports.numericCollation": "Sort numeric strings by integer value", + "typescript.preferences.organizeImports.accentCollation": "Compare characters with diacritical marks as unequal to base character", "typescript.workspaceSymbols.scope": "Controls which files are searched by [Go to Symbol in Workspace](https://code.visualstudio.com/docs/editor/editingevolved#_open-symbol-by-name).", "typescript.workspaceSymbols.scope.allOpenProjects": "Search all open JavaScript or TypeScript projects for symbols.", "typescript.workspaceSymbols.scope.currentProject": "Only search for symbols in the current JavaScript or TypeScript project.", From c3b777b5b942b079e147ebb17d5282e638169d06 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 13 Apr 2024 09:28:06 -0700 Subject: [PATCH 0039/2222] Add webgpu types back --- yarn.lock | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/yarn.lock b/yarn.lock index fe761f702c8..65aefb226bf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1890,17 +1890,6 @@ "@webassemblyjs/ast" "1.11.1" "@xtuc/long" "4.2.2" -<<<<<<< HEAD -"@webgpu/types@^0.1.40": - version "0.1.40" - resolved "https://registry.yarnpkg.com/@webgpu/types/-/types-0.1.40.tgz#cf72d1df6f9f8adc5d39556041f20ff2e8a58885" - integrity sha512-/BBkHLS6/eQjyWhY2H7Dx5DHcVrS2ICj9owvSRdgtQT6KcafLZA86tPze0xAOsd4FbsYKCUBUQyNi87q7gV7kw== - -"@webpack-cli/configtest@^2.0.1": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.0.1.tgz#a69720f6c9bad6aef54a8fa6ba9c3533e7ef4c7f" - integrity sha512-njsdJXJSiS2iNbQVS0eT8A/KPnmyH4pv1APj2K0d1wrZcBLw+yppxOy4CGqa0OxDJkzfL/XELDhD8rocnIwB5A== -======= "@webassemblyjs/wast-printer@1.12.1": version "1.12.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.12.1.tgz#bcecf661d7d1abdaf989d8341a4833e33e2b31ac" @@ -1908,7 +1897,11 @@ dependencies: "@webassemblyjs/ast" "1.12.1" "@xtuc/long" "4.2.2" ->>>>>>> origin/main + +"@webgpu/types@^0.1.40": + version "0.1.40" + resolved "https://registry.yarnpkg.com/@webgpu/types/-/types-0.1.40.tgz#cf72d1df6f9f8adc5d39556041f20ff2e8a58885" + integrity sha512-/BBkHLS6/eQjyWhY2H7Dx5DHcVrS2ICj9owvSRdgtQT6KcafLZA86tPze0xAOsd4FbsYKCUBUQyNi87q7gV7kw== "@webpack-cli/configtest@^2.1.1": version "2.1.1" From 604699c0436be9a4096fce212ea6f52016706ab9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 13 Apr 2024 09:47:10 -0700 Subject: [PATCH 0040/2222] Optimize update data --- .../editor/browser/view/gpu/gpuViewLayer.ts | 31 +++++++++++++------ .../editor/browser/view/gpu/textureAtlas.ts | 10 +++--- 2 files changed, 26 insertions(+), 15 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 33564d0ba1f..029ecf1c77a 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -6,10 +6,11 @@ import { getActiveDocument, getActiveWindow } from 'vs/base/browser/dom'; import { debounce } from 'vs/base/common/decorators'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; -import { TextureAtlas } from 'vs/editor/browser/view/gpu/textureAtlas'; +import { TextureAtlas, type ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; import type { IVisibleLine, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; import type { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; +import type { ViewLineRenderingData } from 'vs/editor/common/viewModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -534,6 +535,8 @@ class FullFileRenderStrategy implements IRenderStrategy< } update(ctx: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): number { + // Pre-allocate variables to be shared within the loop - don't trust the JIT compiler to do + // this optimization to avoid additional blocking time in garbage collector let chars = ''; let y = 0; let x = 0; @@ -544,6 +547,15 @@ class FullFileRenderStrategy implements IRenderStrategy< let wgslX = 0; let wgslY = 0; let xOffset = 0; + let glyph: ITextureAtlasGlyph; + let cellIndex = 0; + let tokenStartIndex = 0; + let tokenEndIndex = 0; + let tokenFg = 0; + let lineData: ViewLineRenderingData; + let content: string = ''; + let fillStartIndex = 0; + let fillEndIndex = 0; let tokens: IViewLineTokens; @@ -576,8 +588,8 @@ class FullFileRenderStrategy implements IRenderStrategy< dirtyLineStart = Math.min(dirtyLineStart, y); dirtyLineEnd = Math.max(dirtyLineEnd, y); - const lineData = viewportData.getViewLineRenderingData(y); - const content = lineData.content; + lineData = viewportData.getViewLineRenderingData(y); + content = lineData.content; xOffset = 0; // TODO: Handle colors via viewLineRenderingData.tokens @@ -611,9 +623,8 @@ class FullFileRenderStrategy implements IRenderStrategy< // ); tokens = lineData.tokens; - let tokenStartIndex = lineData.minColumn - 1; - let tokenEndIndex = 0; - let tokenFg: number; + tokenStartIndex = lineData.minColumn - 1; + tokenEndIndex = 0; for (let tokenIndex = 0, tokensLen = tokens.getCount(); tokenIndex < tokensLen; tokenIndex++) { tokenEndIndex = tokens.getEndOffset(tokenIndex); if (tokenEndIndex <= tokenStartIndex) { @@ -640,7 +651,7 @@ class FullFileRenderStrategy implements IRenderStrategy< continue; } - const glyph = this._textureAtlas.getGlyph(chars, tokenFg); + glyph = this._textureAtlas.getGlyph(chars, tokenFg); screenAbsoluteX = Math.round((x + xOffset) * 7 * activeWindow.devicePixelRatio); screenAbsoluteY = Math.round(deltaTop[y - startLineNumber] * activeWindow.devicePixelRatio); @@ -649,7 +660,7 @@ class FullFileRenderStrategy implements IRenderStrategy< wgslX = zeroToOneX * 2 - 1; wgslY = zeroToOneY * 2 - 1; - const cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x + xOffset)) * Constants.IndicesPerCell; + cellIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (x + xOffset)) * Constants.IndicesPerCell; cellBuffer[cellIndex + 0] = wgslX; // x cellBuffer[cellIndex + 1] = -wgslY; // y cellBuffer[cellIndex + 2] = 0; @@ -662,8 +673,8 @@ class FullFileRenderStrategy implements IRenderStrategy< } // Clear to end of line - const fillStartIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (tokenEndIndex + xOffset)) * Constants.IndicesPerCell; - const fillEndIndex = (y * FullFileRenderStrategy._columnCount) * Constants.IndicesPerCell; + fillStartIndex = ((y - 1) * FullFileRenderStrategy._columnCount + (tokenEndIndex + xOffset)) * Constants.IndicesPerCell; + fillEndIndex = (y * FullFileRenderStrategy._columnCount) * Constants.IndicesPerCell; cellBuffer.fill(0, fillStartIndex, fillEndIndex); upToDateLines.add(y); diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index ec68a482861..8ef168d2a0e 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -105,12 +105,12 @@ export class TextureAtlas extends Disposable { // TODO: Color, style etc. public getGlyph(chars: string, tokenFg: number): ITextureAtlasGlyph { - let glyph: ITextureAtlasGlyph | undefined = this._glyphMap.get(chars, tokenFg); - if (glyph) { - return glyph; - } + return this._glyphMap.get(chars, tokenFg) ?? this._createGlyph(chars, tokenFg); + } + + private _createGlyph(chars: string, tokenFg: number): ITextureAtlasGlyph { const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars, this._colorMap[tokenFg]); - glyph = this._allocator.allocate(rasterizedGlyph); + const glyph = this._allocator.allocate(rasterizedGlyph); this._glyphMap.set(chars, tokenFg, glyph); this._glyphInOrderSet.add(glyph); this.hasChanges = true; From 50ac6f5ec49fdacecb8eb99037bb315279003ccd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Sat, 13 Apr 2024 09:57:08 -0700 Subject: [PATCH 0041/2222] Add toggle to disable non-gpu rendering/layouts --- src/vs/editor/browser/view.ts | 7 +++++++ .../editor/browser/view/gpu/gpuViewLayer.ts | 2 ++ src/vs/editor/browser/view/viewOverlays.ts | 19 +++++++++++++++++++ .../browser/stickyScrollWidget.ts | 12 +++++++++--- 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index fb7d9c09f24..aaef0a1deb3 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -15,6 +15,7 @@ import { PointerHandlerLastRenderData } from 'vs/editor/browser/controller/mouse import { PointerHandler } from 'vs/editor/browser/controller/pointerHandler'; import { IVisibleRangeProvider, TextAreaHandler } from 'vs/editor/browser/controller/textAreaHandler'; import { IContentWidget, IContentWidgetPosition, IEditorAriaOptions, IGlyphMarginWidget, IGlyphMarginWidgetPosition, IMouseTarget, IOverlayWidget, IOverlayWidgetPosition, IViewZoneChangeAccessor } from 'vs/editor/browser/editorBrowser'; +import { disableNonGpuRendering } from 'vs/editor/browser/view/gpu/gpuViewLayer'; import { RenderingContext, RestrictedRenderingContext } from 'vs/editor/browser/view/renderingContext'; import { ICommandDelegate, ViewController } from 'vs/editor/browser/view/viewController'; import { ContentViewOverlays, MarginViewOverlays } from 'vs/editor/browser/view/viewOverlays'; @@ -429,12 +430,18 @@ export class View extends ViewEventHandler { if (this._store.isDisposed) { throw new BugIndicatingError(); } + if (disableNonGpuRendering) { + return; + } return rendering.prepareRender(viewParts, ctx); }, render: (viewParts: ViewPart[], ctx: RestrictedRenderingContext) => { if (this._store.isDisposed) { throw new BugIndicatingError(); } + if (disableNonGpuRendering) { + return; + } return rendering.render(viewParts, ctx); } }); diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 029ecf1c77a..59f8b367db8 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -14,6 +14,8 @@ import type { ViewLineRenderingData } from 'vs/editor/common/viewModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +export const disableNonGpuRendering = true; + interface IRendererContext { rendLineNumberStart: number; lines: T[]; diff --git a/src/vs/editor/browser/view/viewOverlays.ts b/src/vs/editor/browser/view/viewOverlays.ts index b674d855fe4..fe331743db6 100644 --- a/src/vs/editor/browser/view/viewOverlays.ts +++ b/src/vs/editor/browser/view/viewOverlays.ts @@ -15,6 +15,7 @@ import * as viewEvents from 'vs/editor/common/viewEvents'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { disableNonGpuRendering } from 'vs/editor/browser/view/gpu/gpuViewLayer'; export class ViewOverlays extends ViewPart implements IVisibleLinesHost { @@ -123,6 +124,9 @@ export class ViewOverlays extends ViewPart implements IVisibleLinesHost overlay.shouldRender()); for (let i = 0, len = toRender.length; i < len; i++) { @@ -133,6 +137,9 @@ export class ViewOverlays extends ViewPart implements IVisibleLinesHost { this._linesDomNode.style.left = this._editor.getOption(EditorOption.stickyScroll).scrollWithEditor ? `-${this._editor.getScrollLeft()}px` : '0px'; @@ -135,7 +138,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { const isWidgetHeightZero = this._isWidgetHeightZero(_state); const state = isWidgetHeightZero ? undefined : _state; const rebuildFromLine = isWidgetHeightZero ? 0 : this._findLineToRebuildWidgetFrom(_state, _rebuildFromLine); - this._renderRootNode(state, foldingModel, rebuildFromLine); + + if (!disableNonGpuRendering) { + this._renderRootNode(state, foldingModel, rebuildFromLine); + } this._previousState = _state; } From a821ce103292915a897d01a99224e574e7a8d37e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 8 May 2024 19:21:36 -0700 Subject: [PATCH 0042/2222] Reduce texture size --- src/vs/editor/browser/view/gpu/gpuViewLayer.ts | 11 ++++++----- src/vs/editor/browser/view/gpu/textureAtlas.ts | 4 +++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index 59f8b367db8..e9b9d9bcd06 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -112,13 +112,14 @@ export class GpuViewLayerRenderer { // Create texture atlas if (!GpuViewLayerRenderer._textureAtlas) { - GpuViewLayerRenderer._textureAtlas = this._instantiationService.createInstance(TextureAtlas, this.domNode, this._device.limits.maxTextureDimension2D); + const pageSize = 1024; // this._device.limits.maxTextureDimension2D; + GpuViewLayerRenderer._textureAtlas = this._instantiationService.createInstance(TextureAtlas, this.domNode, pageSize, this._device.limits.maxTextureDimension2D); GpuViewLayerRenderer._testCanvas = document.createElement('canvas'); - GpuViewLayerRenderer._testCanvas.width = this._device.limits.maxTextureDimension2D; - GpuViewLayerRenderer._testCanvas.height = this._device.limits.maxTextureDimension2D; - GpuViewLayerRenderer._testCanvas.style.width = `${this._device.limits.maxTextureDimension2D / getActiveWindow().devicePixelRatio}px`; - GpuViewLayerRenderer._testCanvas.style.height = `${this._device.limits.maxTextureDimension2D / getActiveWindow().devicePixelRatio}px`; + GpuViewLayerRenderer._testCanvas.width = pageSize; + GpuViewLayerRenderer._testCanvas.height = pageSize; + GpuViewLayerRenderer._testCanvas.style.width = `${GpuViewLayerRenderer._testCanvas.width / getActiveWindow().devicePixelRatio}px`; + GpuViewLayerRenderer._testCanvas.style.height = `${GpuViewLayerRenderer._testCanvas.height / getActiveWindow().devicePixelRatio}px`; GpuViewLayerRenderer._testCanvas.style.position = 'absolute'; GpuViewLayerRenderer._testCanvas.style.top = '2px'; GpuViewLayerRenderer._testCanvas.style.left = '2px'; diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 8ef168d2a0e..9323158ca29 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -72,12 +72,13 @@ export class TextureAtlas extends Disposable { // TODO: Should pull in the font size from config instead of random dom node constructor( parentDomNode: HTMLElement, + pageSize: number, maxTextureSize: number, @IThemeService private readonly _themeService: IThemeService ) { super(); - this._canvas = new OffscreenCanvas(maxTextureSize, maxTextureSize); + this._canvas = new OffscreenCanvas(pageSize, pageSize); this._ctx = ensureNonNullable(this._canvas.getContext('2d', { willReadFrequently: true })); @@ -191,6 +192,7 @@ class GlyphRasterizer extends Disposable { return result; } + // TODO: Does this even need to happen when measure text is used? // TODO: Pass back origin offset private _findGlyphBoundingBox(imageData: ImageData): IBoundingBox { // TODO: Hot path: Reuse object From 88e09b4c93a5ffb067c53bfe5b16a87d5fe2094a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 8 May 2024 19:23:58 -0700 Subject: [PATCH 0043/2222] Simplify parseInt --- src/vs/editor/browser/view/gpu/textureAtlas.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 9323158ca29..7509dd63ba0 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -85,7 +85,7 @@ export class TextureAtlas extends Disposable { const activeWindow = getActiveWindow(); const style = activeWindow.getComputedStyle(parentDomNode); - const fontSize = Math.ceil(parseInt(style.fontSize.replace('px', '')) * activeWindow.devicePixelRatio); + const fontSize = Math.ceil(parseInt(style.fontSize) * activeWindow.devicePixelRatio); this._ctx.font = `${fontSize}px ${style.fontFamily}`; this._register(Event.runAndSubscribe(this._themeService.onDidColorThemeChange, () => { From 0d70158daadb4322cb837ddfdbc0f7bad481ee58 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 8 May 2024 22:14:42 -0700 Subject: [PATCH 0044/2222] Split out TextureAtlasPage --- .../browser/view/gpu/glyphRasterizer.ts | 188 +++++++++++++++++ .../editor/browser/view/gpu/textureAtlas.ts | 196 ++---------------- .../browser/view/gpu/textureAtlasAllocator.ts | 3 +- .../browser/view/gpu/textureAtlasPage.ts | 132 ++++++++++++ 4 files changed, 342 insertions(+), 177 deletions(-) create mode 100644 src/vs/editor/browser/view/gpu/glyphRasterizer.ts create mode 100644 src/vs/editor/browser/view/gpu/textureAtlasPage.ts diff --git a/src/vs/editor/browser/view/gpu/glyphRasterizer.ts b/src/vs/editor/browser/view/gpu/glyphRasterizer.ts new file mode 100644 index 00000000000..76cc43f1638 --- /dev/null +++ b/src/vs/editor/browser/view/gpu/glyphRasterizer.ts @@ -0,0 +1,188 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; + +export class GlyphRasterizer extends Disposable { + private _canvas: OffscreenCanvas; + // A temporary context that glyphs are drawn to before being transfered to the atlas. + private _ctx: OffscreenCanvasRenderingContext2D; + + constructor(private readonly _fontSize: number, fontFamily: string) { + super(); + + this._canvas = new OffscreenCanvas(this._fontSize * 3, this._fontSize * 3); + this._ctx = ensureNonNullable(this._canvas.getContext('2d', { + willReadFrequently: true + })); + this._ctx.font = `${this._fontSize}px ${fontFamily}`; + this._ctx.textBaseline = 'top'; + this._ctx.fillStyle = '#FFFFFF'; + } + + // TODO: Support drawing multiple fonts and sizes + // TODO: Should pull in the font size from config instead of random dom node + public rasterizeGlyph(chars: string, fg: string): IRasterizedGlyph { + this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); + + // TODO: Draw in middle using alphabetical baseline + const originX = this._fontSize; + const originY = this._fontSize; + this._ctx.fillStyle = fg; + const textMetrics = this._ctx.measureText(chars); + this._ctx.fillText(chars, originX, originY); + + const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); + // TODO: Hot path: Reuse object + const boundingBox = this._findGlyphBoundingBox(imageData); + const offset = { + x: textMetrics.actualBoundingBoxLeft, + y: textMetrics.actualBoundingBoxAscent + }; + const size = { + w: textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft, + y: textMetrics.actualBoundingBoxDescent + textMetrics.actualBoundingBoxAscent, + wInt: Math.ceil(textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft), + yInt: Math.ceil(textMetrics.actualBoundingBoxDescent + textMetrics.actualBoundingBoxAscent), + }; + + console.log(`${chars}_${fg}`, textMetrics, boundingBox, originX, originY, { width: boundingBox.right - boundingBox.left, height: boundingBox.bottom - boundingBox.top }); + console.log('new', offset, size); + const result: IRasterizedGlyph = { + source: this._canvas, + boundingBox, + originOffset: { + x: boundingBox.left - originX, + y: boundingBox.top - originY + } + }; + const result2: IRasterizedGlyph = { + source: this._canvas, + boundingBox: { + left: Math.floor(originX - textMetrics.actualBoundingBoxLeft), + right: Math.ceil(originX + textMetrics.actualBoundingBoxRight), + top: Math.floor(originY - textMetrics.actualBoundingBoxAscent), + bottom: Math.ceil(originY + textMetrics.actualBoundingBoxDescent), + }, + originOffset: { + x: Math.floor(boundingBox.left - originX), + y: Math.floor(boundingBox.top - originY) + } + }; + + // DEBUG: Show image data in console + // (console as any).image(imageData); + + // TODO: Verify result 1 and 2 are the same + + // if (result2.boundingBox.left > result.boundingBox.left) { + // debugger; + // } + // if (result2.boundingBox.top > result.boundingBox.top) { + // debugger; + // } + // if (result2.boundingBox.right < result.boundingBox.right) { + // debugger; + // } + // if (result2.boundingBox.bottom < result.boundingBox.bottom) { + // debugger; + // } + if (JSON.stringify(result2.originOffset) !== JSON.stringify(result.originOffset)) { + debugger; + } + + return result2; + } + + // TODO: Does this even need to happen when measure text is used? + // TODO: Pass back origin offset + private _findGlyphBoundingBox(imageData: ImageData): IBoundingBox { + + // TODO: Hot path: Reuse object + const boundingBox = { + left: 0, + top: 0, + right: 0, + bottom: 0 + }; + // TODO: This could be optimized to be aware of the font size padding on all sides + const height = this._canvas.height; + const width = this._canvas.width; + let found = false; + for (let y = 0; y < height; y++) { + for (let x = 0; x < width; x++) { + const alphaOffset = y * width * 4 + x * 4 + 3; + if (imageData.data[alphaOffset] !== 0) { + boundingBox.top = y; + found = true; + break; + } + } + if (found) { + break; + } + } + boundingBox.left = 0; + found = false; + for (let x = 0; x < width; x++) { + for (let y = 0; y < height; y++) { + const alphaOffset = y * width * 4 + x * 4 + 3; + if (imageData.data[alphaOffset] !== 0) { + boundingBox.left = x; + found = true; + break; + } + } + if (found) { + break; + } + } + boundingBox.right = width; + found = false; + for (let x = width - 1; x >= boundingBox.left; x--) { + for (let y = 0; y < height; y++) { + const alphaOffset = y * width * 4 + x * 4 + 3; + if (imageData.data[alphaOffset] !== 0) { + boundingBox.right = x; + found = true; + break; + } + } + if (found) { + break; + } + } + boundingBox.bottom = boundingBox.top; + found = false; + for (let y = height - 1; y >= 0; y--) { + for (let x = 0; x < width; x++) { + const alphaOffset = y * width * 4 + x * 4 + 3; + if (imageData.data[alphaOffset] !== 0) { + boundingBox.bottom = y; + found = true; + break; + } + } + if (found) { + break; + } + } + return boundingBox; + } +} + +export interface IBoundingBox { + left: number; + top: number; + right: number; + bottom: number; +} + +export interface IRasterizedGlyph { + source: CanvasImageSource; + boundingBox: IBoundingBox; + originOffset: { x: number; y: number }; +} diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 7509dd63ba0..97ce622da72 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -5,11 +5,11 @@ import { getActiveWindow } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; -import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { GlyphRasterizer } from 'vs/editor/browser/view/gpu/glyphRasterizer'; import { IdleTaskQueue } from 'vs/editor/browser/view/gpu/taskQueue'; -import { ITextureAtlasAllocator, TextureAtlasShelfAllocator } from 'vs/editor/browser/view/gpu/textureAtlasAllocator'; +import { TextureAtlasPage } from 'vs/editor/browser/view/gpu/textureAtlasPage'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; // DEBUG: This helper can be used to draw image data to the console, it's commented out as we don't @@ -46,47 +46,42 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; // }; export class TextureAtlas extends Disposable { - private readonly _canvas: OffscreenCanvas; - private readonly _ctx: OffscreenCanvasRenderingContext2D; - - private readonly _glyphMap: TwoKeyMap = new TwoKeyMap(); - // HACK: This is an ordered set of glyphs to be passed to the GPU since currently the shader - // uses the index of the glyph. This should be improved to derive from _glyphMap - private readonly _glyphInOrderSet: Set = new Set(); public get glyphs(): IterableIterator { - return this._glyphInOrderSet.values(); + return this._page.glyphs; } private readonly _glyphRasterizer: GlyphRasterizer; - private readonly _allocator: ITextureAtlasAllocator; private _colorMap!: string[]; private _warmUpTask?: IdleTaskQueue; public get source(): OffscreenCanvas { - return this._canvas; + return this._page.source; } - public hasChanges = false; + public get hasChanges(): boolean { + return this._page.hasChanges; + } + public set hasChanges(value: boolean) { + this._page.hasChanges = value; + } + + private readonly _page: TextureAtlasPage; // TODO: Should pull in the font size from config instead of random dom node constructor( parentDomNode: HTMLElement, pageSize: number, maxTextureSize: number, - @IThemeService private readonly _themeService: IThemeService + @IThemeService private readonly _themeService: IThemeService, + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); - this._canvas = new OffscreenCanvas(pageSize, pageSize); - this._ctx = ensureNonNullable(this._canvas.getContext('2d', { - willReadFrequently: true - })); - const activeWindow = getActiveWindow(); const style = activeWindow.getComputedStyle(parentDomNode); const fontSize = Math.ceil(parseInt(style.fontSize) * activeWindow.devicePixelRatio); - this._ctx.font = `${fontSize}px ${style.fontFamily}`; + // this._ctx.font = `${fontSize}px ${style.fontFamily}`; this._register(Event.runAndSubscribe(this._themeService.onDidColorThemeChange, () => { // TODO: Clear entire atlas on theme change @@ -95,37 +90,14 @@ export class TextureAtlas extends Disposable { })); this._glyphRasterizer = new GlyphRasterizer(fontSize, style.fontFamily); - this._allocator = new TextureAtlasShelfAllocator(this._canvas, this._ctx); + // this._allocator = new TextureAtlasShelfAllocator(this._canvas, this._ctx); - // Reduce impact of a memory leak if this object is not released - this._register(toDisposable(() => { - this._canvas.width = 1; - this._canvas.height = 1; - })); + this._page = this._register(this._instantiationService.createInstance(TextureAtlasPage, parentDomNode, pageSize, maxTextureSize, this._glyphRasterizer)); } // TODO: Color, style etc. public getGlyph(chars: string, tokenFg: number): ITextureAtlasGlyph { - return this._glyphMap.get(chars, tokenFg) ?? this._createGlyph(chars, tokenFg); - } - - private _createGlyph(chars: string, tokenFg: number): ITextureAtlasGlyph { - const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars, this._colorMap[tokenFg]); - const glyph = this._allocator.allocate(rasterizedGlyph); - this._glyphMap.set(chars, tokenFg, glyph); - this._glyphInOrderSet.add(glyph); - this.hasChanges = true; - - if (!this._warmUpTask) { - console.debug('New glyph', { - chars, - fg: this._colorMap[tokenFg], - rasterizedGlyph, - glyph - }); - } - - return glyph; + return this._page.getGlyph(chars, tokenFg); } /** @@ -146,128 +118,6 @@ export class TextureAtlas extends Disposable { } } -class GlyphRasterizer extends Disposable { - private _canvas: OffscreenCanvas; - // A temporary context that glyphs are drawn to before being transfered to the atlas. - private _ctx: OffscreenCanvasRenderingContext2D; - - constructor(private readonly _fontSize: number, fontFamily: string) { - super(); - - this._canvas = new OffscreenCanvas(this._fontSize * 3, this._fontSize * 3); - this._ctx = ensureNonNullable(this._canvas.getContext('2d', { - willReadFrequently: true - })); - this._ctx.font = `${this._fontSize}px ${fontFamily}`; - this._ctx.textBaseline = 'top'; - this._ctx.fillStyle = '#FFFFFF'; - } - - // TODO: Support drawing multiple fonts and sizes - // TODO: Should pull in the font size from config instead of random dom node - public rasterizeGlyph(chars: string, fg: string): IRasterizedGlyph { - this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); - - // TODO: Draw in middle using alphabetical baseline - const originX = this._fontSize; - const originY = this._fontSize; - this._ctx.fillStyle = fg; - this._ctx.fillText(chars, originX, originY); - - const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); - // TODO: Hot path: Reuse object - const boundingBox = this._findGlyphBoundingBox(imageData); - const result: IRasterizedGlyph = { - source: this._canvas, - boundingBox, - originOffset: { - x: boundingBox.left - originX, - y: boundingBox.top - originY - } - }; - - // DEBUG: Show image data in console - // (console as any).image(imageData); - - return result; - } - - // TODO: Does this even need to happen when measure text is used? - // TODO: Pass back origin offset - private _findGlyphBoundingBox(imageData: ImageData): IBoundingBox { - // TODO: Hot path: Reuse object - const boundingBox = { - left: 0, - top: 0, - right: 0, - bottom: 0 - }; - // TODO: This could be optimized to be aware of the font size padding on all sides - const height = this._canvas.height; - const width = this._canvas.width; - let found = false; - for (let y = 0; y < height; y++) { - for (let x = 0; x < width; x++) { - const alphaOffset = y * width * 4 + x * 4 + 3; - if (imageData.data[alphaOffset] !== 0) { - boundingBox.top = y; - found = true; - break; - } - } - if (found) { - break; - } - } - boundingBox.left = 0; - found = false; - for (let x = 0; x < width; x++) { - for (let y = 0; y < height; y++) { - const alphaOffset = y * width * 4 + x * 4 + 3; - if (imageData.data[alphaOffset] !== 0) { - boundingBox.left = x; - found = true; - break; - } - } - if (found) { - break; - } - } - boundingBox.right = width; - found = false; - for (let x = width - 1; x >= boundingBox.left; x--) { - for (let y = 0; y < height; y++) { - const alphaOffset = y * width * 4 + x * 4 + 3; - if (imageData.data[alphaOffset] !== 0) { - boundingBox.right = x; - found = true; - break; - } - } - if (found) { - break; - } - } - boundingBox.bottom = boundingBox.top; - found = false; - for (let y = height - 1; y >= 0; y--) { - for (let x = 0; x < width; x++) { - const alphaOffset = y * width * 4 + x * 4 + 3; - if (imageData.data[alphaOffset] !== 0) { - boundingBox.bottom = y; - found = true; - break; - } - } - if (found) { - break; - } - } - return boundingBox; - } -} - export interface ITextureAtlasGlyph { index: number; x: number; @@ -284,9 +134,3 @@ export interface IBoundingBox { right: number; bottom: number; } - -export interface IRasterizedGlyph { - source: CanvasImageSource; - boundingBox: IBoundingBox; - originOffset: { x: number; y: number }; -} diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index d4091d19320..e013c52bf5f 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { IRasterizedGlyph, ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; +import type { IRasterizedGlyph } from 'vs/editor/browser/view/gpu/glyphRasterizer'; +import type { ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; export interface ITextureAtlasAllocator { allocate(rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph; diff --git a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts new file mode 100644 index 00000000000..f15d4b9c769 --- /dev/null +++ b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts @@ -0,0 +1,132 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getActiveWindow } from 'vs/base/browser/dom'; +import { Event } from 'vs/base/common/event'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import type { GlyphRasterizer } from 'vs/editor/browser/view/gpu/glyphRasterizer'; +import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; +import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; +import { IdleTaskQueue } from 'vs/editor/browser/view/gpu/taskQueue'; +import { ITextureAtlasAllocator, TextureAtlasShelfAllocator } from 'vs/editor/browser/view/gpu/textureAtlasAllocator'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; + +export class TextureAtlasPage extends Disposable { + + private readonly _canvas: OffscreenCanvas; + private readonly _ctx: OffscreenCanvasRenderingContext2D; + + private readonly _glyphMap: TwoKeyMap = new TwoKeyMap(); + // HACK: This is an ordered set of glyphs to be passed to the GPU since currently the shader + // uses the index of the glyph. This should be improved to derive from _glyphMap + private readonly _glyphInOrderSet: Set = new Set(); + public get glyphs(): IterableIterator { + return this._glyphInOrderSet.values(); + } + + private readonly _allocator: ITextureAtlasAllocator; + + private _colorMap!: string[]; + private _warmUpTask?: IdleTaskQueue; + + public get source(): OffscreenCanvas { + return this._canvas; + } + + public hasChanges = false; + + + // TODO: Should pull in the font size from config instead of random dom node + constructor( + parentDomNode: HTMLElement, + pageSize: number, + maxTextureSize: number, + private readonly _glyphRasterizer: GlyphRasterizer, + @IThemeService private readonly _themeService: IThemeService + ) { + super(); + + this._canvas = new OffscreenCanvas(pageSize, pageSize); + this._ctx = ensureNonNullable(this._canvas.getContext('2d', { + willReadFrequently: true + })); + + const activeWindow = getActiveWindow(); + const style = activeWindow.getComputedStyle(parentDomNode); + const fontSize = Math.ceil(parseInt(style.fontSize) * activeWindow.devicePixelRatio); + this._ctx.font = `${fontSize}px ${style.fontFamily}`; + + this._register(Event.runAndSubscribe(this._themeService.onDidColorThemeChange, () => { + // TODO: Clear entire atlas on theme change + this._colorMap = this._themeService.getColorTheme().tokenColorMap; + this._warmUpAtlas(); + })); + + this._allocator = new TextureAtlasShelfAllocator(this._canvas, this._ctx); + + // Reduce impact of a memory leak if this object is not released + this._register(toDisposable(() => { + this._canvas.width = 1; + this._canvas.height = 1; + })); + } + + // TODO: Color, style etc. + public getGlyph(chars: string, tokenFg: number): ITextureAtlasGlyph { + return this._glyphMap.get(chars, tokenFg) ?? this._createGlyph(chars, tokenFg); + } + + private _createGlyph(chars: string, tokenFg: number): ITextureAtlasGlyph { + const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars, this._colorMap[tokenFg]); + const glyph = this._allocator.allocate(rasterizedGlyph); + this._glyphMap.set(chars, tokenFg, glyph); + this._glyphInOrderSet.add(glyph); + this.hasChanges = true; + + if (!this._warmUpTask) { + console.debug('New glyph', { + chars, + fg: this._colorMap[tokenFg], + rasterizedGlyph, + glyph + }); + } + + return glyph; + } + + /** + * Warms up the atlas by rasterizing all printable ASCII characters for each token color. This + * is distrubuted over multiple idle callbacks to avoid blocking the main thread. + */ + private _warmUpAtlas(): void { + // TODO: Clean up on dispose + this._warmUpTask?.clear(); + this._warmUpTask = new IdleTaskQueue(); + for (const tokenFg of this._colorMap.keys()) { + this._warmUpTask.enqueue(() => { + for (let code = 33; code <= 126; code++) { + this.getGlyph(String.fromCharCode(code), tokenFg); + } + }); + } + } +} +export interface ITextureAtlasGlyph { + index: number; + x: number; + y: number; + w: number; + h: number; + originOffsetX: number; + originOffsetY: number; +} + +export interface IBoundingBox { + left: number; + top: number; + right: number; + bottom: number; +} From 216e138403e3ca56dd23c7bb907a9fe8dd41fb7c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 9 May 2024 00:31:03 -0700 Subject: [PATCH 0045/2222] Remove warm up from page --- .../browser/view/gpu/textureAtlasPage.ts | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts index f15d4b9c769..f3195dc4804 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts @@ -61,7 +61,6 @@ export class TextureAtlasPage extends Disposable { this._register(Event.runAndSubscribe(this._themeService.onDidColorThemeChange, () => { // TODO: Clear entire atlas on theme change this._colorMap = this._themeService.getColorTheme().tokenColorMap; - this._warmUpAtlas(); })); this._allocator = new TextureAtlasShelfAllocator(this._canvas, this._ctx); @@ -96,24 +95,8 @@ export class TextureAtlasPage extends Disposable { return glyph; } - - /** - * Warms up the atlas by rasterizing all printable ASCII characters for each token color. This - * is distrubuted over multiple idle callbacks to avoid blocking the main thread. - */ - private _warmUpAtlas(): void { - // TODO: Clean up on dispose - this._warmUpTask?.clear(); - this._warmUpTask = new IdleTaskQueue(); - for (const tokenFg of this._colorMap.keys()) { - this._warmUpTask.enqueue(() => { - for (let code = 33; code <= 126; code++) { - this.getGlyph(String.fromCharCode(code), tokenFg); - } - }); - } - } } + export interface ITextureAtlasGlyph { index: number; x: number; From 631ff57653e8db39f3c831189c5ca34000a8ce67 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 11:02:49 +0900 Subject: [PATCH 0046/2222] Atlas actual and usage files --- .../browser/view/gpu/glyphRasterizer.ts | 7 ++- .../editor/browser/view/gpu/gpuViewLayer.ts | 51 +++++++++---------- .../editor/browser/view/gpu/textureAtlas.ts | 15 ++++++ .../browser/view/gpu/textureAtlasPage.ts | 11 ++-- 4 files changed, 49 insertions(+), 35 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/glyphRasterizer.ts b/src/vs/editor/browser/view/gpu/glyphRasterizer.ts index 76cc43f1638..73637e24146 100644 --- a/src/vs/editor/browser/view/gpu/glyphRasterizer.ts +++ b/src/vs/editor/browser/view/gpu/glyphRasterizer.ts @@ -11,7 +11,10 @@ export class GlyphRasterizer extends Disposable { // A temporary context that glyphs are drawn to before being transfered to the atlas. private _ctx: OffscreenCanvasRenderingContext2D; - constructor(private readonly _fontSize: number, fontFamily: string) { + constructor( + private readonly _fontSize: number, + fontFamily: string, + ) { super(); this._canvas = new OffscreenCanvas(this._fontSize * 3, this._fontSize * 3); @@ -94,7 +97,7 @@ export class GlyphRasterizer extends Disposable { debugger; } - return result2; + return result; } // TODO: Does this even need to happen when measure text is used? diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index e9b9d9bcd06..eeb7227f00c 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -3,16 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getActiveDocument, getActiveWindow } from 'vs/base/browser/dom'; +import { getActiveWindow } from 'vs/base/browser/dom'; +import { VSBuffer } from 'vs/base/common/buffer'; import { debounce } from 'vs/base/common/decorators'; -import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; +import { URI } from 'vs/base/common/uri'; import { TextureAtlas, type ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; import type { IVisibleLine, IVisibleLinesHost } from 'vs/editor/browser/view/viewLayer'; import type { IViewLineTokens } from 'vs/editor/common/tokens/lineTokens'; import { ViewportData } from 'vs/editor/common/viewLayout/viewLinesViewportData'; import type { ViewLineRenderingData } from 'vs/editor/common/viewModel'; +import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; export const disableNonGpuRendering = true; @@ -69,19 +72,15 @@ export class GpuViewLayerRenderer { private _initialized = false; - - - private static _testCanvas: HTMLCanvasElement; - private static _testCtx: CanvasRenderingContext2D; - private _renderStrategy!: IRenderStrategy; - constructor( domNode: HTMLCanvasElement, host: IVisibleLinesHost, viewportData: ViewportData, - @IInstantiationService private readonly _instantiationService: IInstantiationService + @IFileService private readonly _fileService: IFileService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IWorkspaceContextService private readonly _workspaceContextService: IWorkspaceContextService, ) { this.domNode = domNode; this.host = host; @@ -114,19 +113,6 @@ export class GpuViewLayerRenderer { if (!GpuViewLayerRenderer._textureAtlas) { const pageSize = 1024; // this._device.limits.maxTextureDimension2D; GpuViewLayerRenderer._textureAtlas = this._instantiationService.createInstance(TextureAtlas, this.domNode, pageSize, this._device.limits.maxTextureDimension2D); - - GpuViewLayerRenderer._testCanvas = document.createElement('canvas'); - GpuViewLayerRenderer._testCanvas.width = pageSize; - GpuViewLayerRenderer._testCanvas.height = pageSize; - GpuViewLayerRenderer._testCanvas.style.width = `${GpuViewLayerRenderer._testCanvas.width / getActiveWindow().devicePixelRatio}px`; - GpuViewLayerRenderer._testCanvas.style.height = `${GpuViewLayerRenderer._testCanvas.height / getActiveWindow().devicePixelRatio}px`; - GpuViewLayerRenderer._testCanvas.style.position = 'absolute'; - GpuViewLayerRenderer._testCanvas.style.top = '2px'; - GpuViewLayerRenderer._testCanvas.style.left = '2px'; - GpuViewLayerRenderer._testCanvas.style.zIndex = '10000'; - GpuViewLayerRenderer._testCanvas.style.pointerEvents = 'none'; - GpuViewLayerRenderer._testCtx = ensureNonNullable(GpuViewLayerRenderer._testCanvas.getContext('2d')); - getActiveDocument().body.appendChild(GpuViewLayerRenderer._testCanvas); } const textureAtlas = GpuViewLayerRenderer._textureAtlas; @@ -333,14 +319,25 @@ export class GpuViewLayerRenderer { { width: GpuViewLayerRenderer._textureAtlas.source.width, height: GpuViewLayerRenderer._textureAtlas.source.height }, ); - - GpuViewLayerRenderer._drawToTextureAtlas(); + GpuViewLayerRenderer._drawToTextureAtlas(this._fileService, this._workspaceContextService); } @debounce(500) - private static _drawToTextureAtlas() { - GpuViewLayerRenderer._testCtx.clearRect(0, 0, GpuViewLayerRenderer._textureAtlas.source.width, GpuViewLayerRenderer._textureAtlas.source.height); - GpuViewLayerRenderer._testCtx.drawImage(GpuViewLayerRenderer._textureAtlas.source, 0, 0); + private static async _drawToTextureAtlas(fileService: IFileService, workspaceContextService: IWorkspaceContextService) { + const blob = await GpuViewLayerRenderer._textureAtlas.source.convertToBlob(); + const folders = workspaceContextService.getWorkspace().folders; + if (folders.length > 0) { + await Promise.all([ + fileService.writeFile( + URI.joinPath(folders[0].uri, 'atlas_scratch_actual.png'), + VSBuffer.wrap(new Uint8Array(await blob.arrayBuffer())) + ), + fileService.writeFile( + URI.joinPath(folders[0].uri, 'atlas_scratch_usage.png'), + VSBuffer.wrap(new Uint8Array(await ((await GpuViewLayerRenderer._textureAtlas.getUsagePreview()).arrayBuffer()))) + ) + ]); + } } public render(inContext: IRendererContext, startLineNumber: number, stopLineNumber: number, deltaTop: number[]): IRendererContext { diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 97ce622da72..9afef14c242 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -7,6 +7,7 @@ import { getActiveWindow } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { GlyphRasterizer } from 'vs/editor/browser/view/gpu/glyphRasterizer'; +import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { IdleTaskQueue } from 'vs/editor/browser/view/gpu/taskQueue'; import { TextureAtlasPage } from 'vs/editor/browser/view/gpu/textureAtlasPage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -100,6 +101,20 @@ export class TextureAtlas extends Disposable { return this._page.getGlyph(chars, tokenFg); } + public getUsagePreview(): Promise { + const w = this._page.source.width; + const h = this._page.source.height; + const canvas = new OffscreenCanvas(w, h); + const ctx = ensureNonNullable(canvas.getContext('2d')); + ctx.fillStyle = '#808080'; + ctx.fillRect(0, 0, w, h); + ctx.fillStyle = '#4040FF'; + for (const g of this.glyphs) { + ctx.fillRect(g.x, g.y, g.w, g.h); + } + return canvas.convertToBlob(); + } + /** * Warms up the atlas by rasterizing all printable ASCII characters for each token color. This * is distrubuted over multiple idle callbacks to avoid blocking the main thread. diff --git a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts index f3195dc4804..aba1af5464b 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts @@ -9,8 +9,8 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import type { GlyphRasterizer } from 'vs/editor/browser/view/gpu/glyphRasterizer'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; -import { IdleTaskQueue } from 'vs/editor/browser/view/gpu/taskQueue'; import { ITextureAtlasAllocator, TextureAtlasShelfAllocator } from 'vs/editor/browser/view/gpu/textureAtlasAllocator'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IThemeService } from 'vs/platform/theme/common/themeService'; export class TextureAtlasPage extends Disposable { @@ -29,7 +29,6 @@ export class TextureAtlasPage extends Disposable { private readonly _allocator: ITextureAtlasAllocator; private _colorMap!: string[]; - private _warmUpTask?: IdleTaskQueue; public get source(): OffscreenCanvas { return this._canvas; @@ -37,14 +36,14 @@ export class TextureAtlasPage extends Disposable { public hasChanges = false; - // TODO: Should pull in the font size from config instead of random dom node constructor( parentDomNode: HTMLElement, pageSize: number, maxTextureSize: number, private readonly _glyphRasterizer: GlyphRasterizer, - @IThemeService private readonly _themeService: IThemeService + @ILogService private readonly _logService: ILogService, + @IThemeService private readonly _themeService: IThemeService, ) { super(); @@ -84,8 +83,8 @@ export class TextureAtlasPage extends Disposable { this._glyphInOrderSet.add(glyph); this.hasChanges = true; - if (!this._warmUpTask) { - console.debug('New glyph', { + if (this._logService.getLevel() === LogLevel.Trace) { + this._logService.trace('New glyph', { chars, fg: this._colorMap[tokenFg], rasterizedGlyph, From 73d1a85e3eab7e90a9deaa4f77260fdadb683e7c Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 11:13:15 +0900 Subject: [PATCH 0047/2222] Display wasted space in usage preview --- src/vs/editor/browser/view/gpu/textureAtlas.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 9afef14c242..7687011749d 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -108,9 +108,25 @@ export class TextureAtlas extends Disposable { const ctx = ensureNonNullable(canvas.getContext('2d')); ctx.fillStyle = '#808080'; ctx.fillRect(0, 0, w, h); - ctx.fillStyle = '#4040FF'; + const rowHeight: Map = new Map(); // y -> h + const rowWidth: Map = new Map(); // y -> w + let lastY: number = 0; for (const g of this.glyphs) { + rowHeight.set(g.y, Math.max(rowHeight.get(g.y) ?? 0, g.h)); + rowWidth.set(g.y, Math.max(rowWidth.get(g.y) ?? 0, g.x + g.w)); + lastY = Math.max(lastY, g.y); + } + for (const g of this.glyphs) { + ctx.fillStyle = '#4040FF'; ctx.fillRect(g.x, g.y, g.w, g.h); + ctx.fillStyle = '#FF0000'; + ctx.fillRect(g.x, g.y + g.h, g.w, rowHeight.get(g.y)! - g.h); + } + for (const [rowY, rowW] of rowWidth.entries()) { + if (rowY !== lastY) { + ctx.fillStyle = '#FF0000'; + ctx.fillRect(rowW, rowY, w - rowW, rowHeight.get(rowY)!); + } } return canvas.convertToBlob(); } From 4e046518cb255f41b8f1a6889a5e99ad0b1397ce Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 11:20:48 +0900 Subject: [PATCH 0048/2222] Move usage preview into allocator --- .../editor/browser/view/gpu/textureAtlas.ts | 30 +------------ .../browser/view/gpu/textureAtlasAllocator.ts | 43 ++++++++++++++++++- .../browser/view/gpu/textureAtlasPage.ts | 6 ++- 3 files changed, 47 insertions(+), 32 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 7687011749d..13fd80ee962 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -7,7 +7,6 @@ import { getActiveWindow } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { GlyphRasterizer } from 'vs/editor/browser/view/gpu/glyphRasterizer'; -import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { IdleTaskQueue } from 'vs/editor/browser/view/gpu/taskQueue'; import { TextureAtlasPage } from 'vs/editor/browser/view/gpu/textureAtlasPage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -82,7 +81,6 @@ export class TextureAtlas extends Disposable { const activeWindow = getActiveWindow(); const style = activeWindow.getComputedStyle(parentDomNode); const fontSize = Math.ceil(parseInt(style.fontSize) * activeWindow.devicePixelRatio); - // this._ctx.font = `${fontSize}px ${style.fontFamily}`; this._register(Event.runAndSubscribe(this._themeService.onDidColorThemeChange, () => { // TODO: Clear entire atlas on theme change @@ -102,33 +100,7 @@ export class TextureAtlas extends Disposable { } public getUsagePreview(): Promise { - const w = this._page.source.width; - const h = this._page.source.height; - const canvas = new OffscreenCanvas(w, h); - const ctx = ensureNonNullable(canvas.getContext('2d')); - ctx.fillStyle = '#808080'; - ctx.fillRect(0, 0, w, h); - const rowHeight: Map = new Map(); // y -> h - const rowWidth: Map = new Map(); // y -> w - let lastY: number = 0; - for (const g of this.glyphs) { - rowHeight.set(g.y, Math.max(rowHeight.get(g.y) ?? 0, g.h)); - rowWidth.set(g.y, Math.max(rowWidth.get(g.y) ?? 0, g.x + g.w)); - lastY = Math.max(lastY, g.y); - } - for (const g of this.glyphs) { - ctx.fillStyle = '#4040FF'; - ctx.fillRect(g.x, g.y, g.w, g.h); - ctx.fillStyle = '#FF0000'; - ctx.fillRect(g.x, g.y + g.h, g.w, rowHeight.get(g.y)! - g.h); - } - for (const [rowY, rowW] of rowWidth.entries()) { - if (rowY !== lastY) { - ctx.fillStyle = '#FF0000'; - ctx.fillRect(rowW, rowY, w - rowW, rowHeight.get(rowY)!); - } - } - return canvas.convertToBlob(); + return this._page.getUsagePreview(); } /** diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index e013c52bf5f..bb7fea66f20 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -4,10 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import type { IRasterizedGlyph } from 'vs/editor/browser/view/gpu/glyphRasterizer'; +import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; +import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; import type { ITextureAtlasGlyph } from 'vs/editor/browser/view/gpu/textureAtlas'; export interface ITextureAtlasAllocator { - allocate(rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph; + readonly glyphMap: TwoKeyMap; + allocate(chars: string, tokenFg: number, rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph; + getUsagePreview(): Promise; } export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { @@ -16,9 +20,12 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { y: 0, h: 0 }; + // TODO: Allow for multiple active rows // public readonly fixedRows: ICharAtlasActiveRow[] = []; + readonly glyphMap: TwoKeyMap = new TwoKeyMap(); + private _nextIndex = 0; constructor( @@ -27,7 +34,7 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { ) { } - public allocate(rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph { + public allocate(chars: string, tokenFg: number, rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph { // Finalize row if it doesn't fix if (rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left > this._canvas.width - this._currentRow.x) { this._currentRow.x = 0; @@ -70,8 +77,40 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { this._currentRow.x += glyphWidth; this._currentRow.h = Math.max(this._currentRow.h, glyphHeight); + // Set the glyph + this.glyphMap.set(chars, tokenFg, glyph); + return glyph; } + + public getUsagePreview(): Promise { + // TODO: This is specific to the simple shelf allocator + const w = this._canvas.width; + const h = this._canvas.height; + const canvas = new OffscreenCanvas(w, h); + const ctx = ensureNonNullable(canvas.getContext('2d')); + ctx.fillStyle = '#808080'; + ctx.fillRect(0, 0, w, h); + const rowHeight: Map = new Map(); // y -> h + const rowWidth: Map = new Map(); // y -> w + for (const g of this.glyphMap.values()) { + rowHeight.set(g.y, Math.max(rowHeight.get(g.y) ?? 0, g.h)); + rowWidth.set(g.y, Math.max(rowWidth.get(g.y) ?? 0, g.x + g.w)); + } + for (const g of this.glyphMap.values()) { + ctx.fillStyle = '#4040FF'; + ctx.fillRect(g.x, g.y, g.w, g.h); + ctx.fillStyle = '#FF0000'; + ctx.fillRect(g.x, g.y + g.h, g.w, rowHeight.get(g.y)! - g.h); + } + for (const [rowY, rowW] of rowWidth.entries()) { + if (rowY !== this._currentRow.y) { + ctx.fillStyle = '#FF0000'; + ctx.fillRect(rowW, rowY, w - rowW, rowHeight.get(rowY)!); + } + } + return canvas.convertToBlob(); + } } interface ITextureAtlasShelf { diff --git a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts index aba1af5464b..57f154a6001 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts @@ -78,7 +78,7 @@ export class TextureAtlasPage extends Disposable { private _createGlyph(chars: string, tokenFg: number): ITextureAtlasGlyph { const rasterizedGlyph = this._glyphRasterizer.rasterizeGlyph(chars, this._colorMap[tokenFg]); - const glyph = this._allocator.allocate(rasterizedGlyph); + const glyph = this._allocator.allocate(chars, tokenFg, rasterizedGlyph); this._glyphMap.set(chars, tokenFg, glyph); this._glyphInOrderSet.add(glyph); this.hasChanges = true; @@ -94,6 +94,10 @@ export class TextureAtlasPage extends Disposable { return glyph; } + + getUsagePreview(): Promise { + return this._allocator.getUsagePreview(); + } } export interface ITextureAtlasGlyph { From 3338d40d83ee56c8c6daa98ca739f828dd9c865d Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 11:28:34 +0900 Subject: [PATCH 0049/2222] Log atlas stats to console --- .../browser/view/gpu/textureAtlasAllocator.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index bb7fea66f20..e122e5304f0 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -91,6 +91,11 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { const ctx = ensureNonNullable(canvas.getContext('2d')); ctx.fillStyle = '#808080'; ctx.fillRect(0, 0, w, h); + + let usedPixels = 0; + let wastedPixels = 0; + const totalPixels = w * h; + const rowHeight: Map = new Map(); // y -> h const rowWidth: Map = new Map(); // y -> w for (const g of this.glyphMap.values()) { @@ -98,6 +103,8 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { rowWidth.set(g.y, Math.max(rowWidth.get(g.y) ?? 0, g.x + g.w)); } for (const g of this.glyphMap.values()) { + usedPixels += g.w * g.h; + wastedPixels += g.w * (rowHeight.get(g.y)! - g.h); ctx.fillStyle = '#4040FF'; ctx.fillRect(g.x, g.y, g.w, g.h); ctx.fillStyle = '#FF0000'; @@ -107,8 +114,16 @@ export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { if (rowY !== this._currentRow.y) { ctx.fillStyle = '#FF0000'; ctx.fillRect(rowW, rowY, w - rowW, rowHeight.get(rowY)!); + wastedPixels += (w - rowW) * rowHeight.get(rowY)!; } } + console.log([ + `Texture atlas stats:`, + ` Total: ${totalPixels}`, + ` Used: ${usedPixels} (${((usedPixels / totalPixels) * 100).toPrecision(2)}%)`, + ` Wasted: ${wastedPixels} (${((wastedPixels / totalPixels) * 100).toPrecision(2)}%)`, + `Efficiency: ${((usedPixels / (usedPixels + wastedPixels)) * 100).toPrecision(2)}%`, + ].join('\n')); return canvas.convertToBlob(); } } From c8f4899ce94b9e0ccfc30b38a10f7fcd0b472d69 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 12:10:03 +0900 Subject: [PATCH 0050/2222] Slab allocator with number experiments --- .../browser/view/gpu/textureAtlasAllocator.ts | 131 ++++++++++++++++++ .../browser/view/gpu/textureAtlasPage.ts | 5 +- 2 files changed, 134 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index e122e5304f0..7ace5305ccf 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -14,6 +14,8 @@ export interface ITextureAtlasAllocator { getUsagePreview(): Promise; } +// #region Shelf allocator + export class TextureAtlasShelfAllocator implements ITextureAtlasAllocator { private _currentRow: ITextureAtlasShelf = { x: 0, @@ -133,3 +135,132 @@ interface ITextureAtlasShelf { y: number; h: number; } + +// #endregion + +// #region Slab allocator + +export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { + // TODO: Is there a better way to index slabs other than an unsorted list? + private _slabs: ITextureAtlasSlab[] = []; + private _activeSlabsByDims: TwoKeyMap = new TwoKeyMap(); + + readonly glyphMap: TwoKeyMap = new TwoKeyMap(); + + private _nextIndex = 0; + + constructor( + private readonly _canvas: OffscreenCanvas, + private readonly _ctx: OffscreenCanvasRenderingContext2D, + ) { + } + + public allocate(chars: string, tokenFg: number, rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph { + // Find ideal slab, creating it if there is none suitable + + // Slabs are sized: 1x1, 1x2, 1x4, 1x8, ... + // 2x1, 2x2, 2x4, 2x8, ... + // 4x1, 4x2, 4x4, 4x8, ... + // ... + const glyphWidth = rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left + 1; + const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + 1; + const desiredSlabSize = { + // TODO: This can probably be optimized + w: 1 << Math.ceil(Math.sqrt(glyphWidth)), + h: 1 << Math.ceil(Math.sqrt(glyphHeight)), + + // w: glyphWidth % 0 === 1 ? glyphWidth + 1 : glyphWidth, + // h: glyphHeight % 0 === 1 ? glyphHeight + 1 : glyphHeight, + + // w: glyphWidth, + // h: glyphHeight, + }; + + const slabW = 256; // this._canvas.width / 8; + const slabH = 256; // this._canvas.height / 8; + const slabsPerRow = Math.floor(this._canvas.width / slabW); + + let slab = this._activeSlabsByDims.get(desiredSlabSize.w, desiredSlabSize.h); + if (!slab) { + slab = { + x: Math.floor(this._slabs.length % slabsPerRow) * slabW, + y: Math.floor(this._slabs.length / slabsPerRow) * slabH, + entryW: desiredSlabSize.w, + entryH: desiredSlabSize.h, + count: 0 + }; + // console.log('new slab (full slab)', slab); + this._slabs.push(slab); + this._activeSlabsByDims.set(desiredSlabSize.w, desiredSlabSize.h, slab); + } + + // Create another slab if this one is full + const glyphsPerSlab = Math.floor(slabW / slab.entryW) * Math.floor(slabH / slab.entryH); + if (slab.count >= glyphsPerSlab) { + slab = { + x: Math.floor(this._slabs.length % slabsPerRow) * slabW, + y: Math.floor(this._slabs.length / slabsPerRow) * slabH, + entryW: desiredSlabSize.w, + entryH: desiredSlabSize.h, + count: 0 + }; + // console.log('new slab (full slab)', slab); + this._slabs.push(slab); + this._activeSlabsByDims.set(desiredSlabSize.w, desiredSlabSize.h, slab); + } + + // Draw glyph + // TODO: Prefer putImageData as it doesn't do blending or scaling + const glyphsPerRow = Math.floor(slabW / slab.entryW); + const dx = slab.x + Math.floor(slab.count % glyphsPerRow) * slab.entryW; + const dy = slab.y + Math.floor(slab.count / glyphsPerRow) * slab.entryH; + console.log('dx dy', dx, dy); + this._ctx.drawImage( + rasterizedGlyph.source, + // source + rasterizedGlyph.boundingBox.left, + rasterizedGlyph.boundingBox.top, + glyphWidth, + glyphHeight, + // destination + dx, + dy, + glyphWidth, + glyphHeight + ); + + // Create glyph object + const glyph: ITextureAtlasGlyph = { + index: this._nextIndex++, + x: dx, + y: dy, + w: glyphWidth, + h: glyphHeight, + originOffsetX: rasterizedGlyph.originOffset.x, + originOffsetY: rasterizedGlyph.originOffset.y + }; + + // Shift current row + slab.count++; + + // Set the glyph + this.glyphMap.set(chars, tokenFg, glyph); + + return glyph; + } + + public getUsagePreview(): Promise { + // TODO: Impl + return this._canvas.convertToBlob(); + } +} + +interface ITextureAtlasSlab { + x: number; + y: number; + entryH: number; + entryW: number; + count: number; +} + +// #endregion diff --git a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts index 57f154a6001..ac684b36ef8 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasPage.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasPage.ts @@ -9,7 +9,7 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import type { GlyphRasterizer } from 'vs/editor/browser/view/gpu/glyphRasterizer'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; -import { ITextureAtlasAllocator, TextureAtlasShelfAllocator } from 'vs/editor/browser/view/gpu/textureAtlasAllocator'; +import { ITextureAtlasAllocator, TextureAtlasSlabAllocator } from 'vs/editor/browser/view/gpu/textureAtlasAllocator'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IThemeService } from 'vs/platform/theme/common/themeService'; @@ -62,7 +62,8 @@ export class TextureAtlasPage extends Disposable { this._colorMap = this._themeService.getColorTheme().tokenColorMap; })); - this._allocator = new TextureAtlasShelfAllocator(this._canvas, this._ctx); + // this._allocator = new TextureAtlasShelfAllocator(this._canvas, this._ctx); + this._allocator = new TextureAtlasSlabAllocator(this._canvas, this._ctx); // Reduce impact of a memory leak if this object is not released this._register(toDisposable(() => { From bdfac3347798e0c8ccb9d48efcf39dfbc6327688 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 12:34:51 +0900 Subject: [PATCH 0051/2222] Slab size iteration --- .../browser/view/gpu/textureAtlasAllocator.ts | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index 7ace5305ccf..e05a89b4a08 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getActiveWindow } from 'vs/base/browser/dom'; import type { IRasterizedGlyph } from 'vs/editor/browser/view/gpu/glyphRasterizer'; import { ensureNonNullable } from 'vs/editor/browser/view/gpu/gpuUtils'; import { TwoKeyMap } from 'vs/editor/browser/view/gpu/multiKeyMap'; @@ -164,20 +165,34 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // ... const glyphWidth = rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left + 1; const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + 1; + const dpr = getActiveWindow().devicePixelRatio; + + // Round slab glyph dimensions to the nearest x pixels, where x scaled with device pixel ratio + const nearestXPixels = Math.max(1, Math.floor(dpr / 0.5)); const desiredSlabSize = { + // Nearest square number // TODO: This can probably be optimized - w: 1 << Math.ceil(Math.sqrt(glyphWidth)), - h: 1 << Math.ceil(Math.sqrt(glyphHeight)), + // w: 1 << Math.ceil(Math.sqrt(glyphWidth)), + // h: 1 << Math.ceil(Math.sqrt(glyphHeight)), + + // Nearest x px + w: Math.ceil(glyphWidth / nearestXPixels) * nearestXPixels, + h: Math.ceil(glyphHeight / nearestXPixels) * nearestXPixels, + // Round odd numbers up // w: glyphWidth % 0 === 1 ? glyphWidth + 1 : glyphWidth, // h: glyphHeight % 0 === 1 ? glyphHeight + 1 : glyphHeight, + // Exact number only // w: glyphWidth, // h: glyphHeight, }; - const slabW = 256; // this._canvas.width / 8; - const slabH = 256; // this._canvas.height / 8; + // TODO: Keeping track of the slab's x and y could allow variable sized slabs and less waste + // TODO: The unused rectangle at the bottom and side of a slab could house micro glyphs like `.` + + const slabW = 64 << (Math.floor(getActiveWindow().devicePixelRatio) - 1); // this._canvas.width / 8; + const slabH = slabW; // this._canvas.height / 8; const slabsPerRow = Math.floor(this._canvas.width / slabW); let slab = this._activeSlabsByDims.get(desiredSlabSize.w, desiredSlabSize.h); From 348a0584357264e536e080b12606e5a21ed6c501 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 13:11:36 +0900 Subject: [PATCH 0052/2222] Visualize slab allocator --- .../browser/view/gpu/textureAtlasAllocator.ts | 126 +++++++++++++++--- 1 file changed, 109 insertions(+), 17 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index e05a89b4a08..fee5520e90f 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -146,6 +146,8 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { private _slabs: ITextureAtlasSlab[] = []; private _activeSlabsByDims: TwoKeyMap = new TwoKeyMap(); + private _unusedRects: ITextureAtlasSlabUnusedRect[] = []; + readonly glyphMap: TwoKeyMap = new TwoKeyMap(); private _nextIndex = 0; @@ -195,23 +197,19 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { const slabH = slabW; // this._canvas.height / 8; const slabsPerRow = Math.floor(this._canvas.width / slabW); + // Get any existing slab let slab = this._activeSlabsByDims.get(desiredSlabSize.w, desiredSlabSize.h); - if (!slab) { - slab = { - x: Math.floor(this._slabs.length % slabsPerRow) * slabW, - y: Math.floor(this._slabs.length / slabsPerRow) * slabH, - entryW: desiredSlabSize.w, - entryH: desiredSlabSize.h, - count: 0 - }; - // console.log('new slab (full slab)', slab); - this._slabs.push(slab); - this._activeSlabsByDims.set(desiredSlabSize.w, desiredSlabSize.h, slab); + + // Check if the slab is full + if (slab) { + const glyphsPerSlab = Math.floor(slabW / slab.entryW) * Math.floor(slabH / slab.entryH); + if (slab.count >= glyphsPerSlab) { + slab = undefined; + } } - // Create another slab if this one is full - const glyphsPerSlab = Math.floor(slabW / slab.entryW) * Math.floor(slabH / slab.entryH); - if (slab.count >= glyphsPerSlab) { + // Create a new slab + if (!slab) { slab = { x: Math.floor(this._slabs.length % slabsPerRow) * slabW, y: Math.floor(this._slabs.length / slabsPerRow) * slabH, @@ -219,7 +217,34 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { entryH: desiredSlabSize.h, count: 0 }; - // console.log('new slab (full slab)', slab); + // Track unused regions to use for small glyphs + // +-------------+----+ + // | | | + // | | | <- Unused W region + // | | | + // |-------------+----+ + // | | <- Unused H region + // +------------------+ + const unusedW = slabW % slab.entryW; + const unusedH = slabH % slab.entryH; + if (unusedW) { + this._unusedRects.push({ + x: slab.x + slabW - unusedW, + w: unusedW, + y: slab.y, + h: slabH - (unusedH ?? 0) + }); + console.log('new unused (W)', this._unusedRects.at(-1)); + } + if (unusedH) { + this._unusedRects.push({ + x: slab.x, + w: slabW, + y: slab.y + slabH - unusedH, + h: unusedH + }); + console.log('new unused (H)', this._unusedRects.at(-1)); + } this._slabs.push(slab); this._activeSlabsByDims.set(desiredSlabSize.w, desiredSlabSize.h, slab); } @@ -265,8 +290,68 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { } public getUsagePreview(): Promise { - // TODO: Impl - return this._canvas.convertToBlob(); + // TODO: This is specific to the simple shelf allocator + const w = this._canvas.width; + const h = this._canvas.height; + const canvas = new OffscreenCanvas(w, h); + const ctx = ensureNonNullable(canvas.getContext('2d')); + + ctx.fillStyle = '#808080'; + ctx.fillRect(0, 0, w, h); + + let slabEntryPixels = 0; + let usedPixels = 0; + let wastedPixels = 0; + const totalPixels = w * h; + const slabW = 64 << (Math.floor(getActiveWindow().devicePixelRatio) - 1); + + // Draw wasted underneath glyphs first + for (const slab of this._slabs) { + let x = 0; + let y = 0; + for (let i = 0; i < slab.count; i++) { + if (x + slab.entryW > slabW) { + x = 0; + y += slab.entryH; + } + // TODO: This doesn't visualize wasted space between entries - draw glyphs on top? + ctx.fillStyle = '#FF0000'; + ctx.fillRect(slab.x + x, slab.y + y, slab.entryW, slab.entryH); + slabEntryPixels += slab.entryW * slab.entryH; + x += slab.entryW; + } + } + + // Draw glyphs + for (const g of this.glyphMap.values()) { + usedPixels += g.w * g.h; + ctx.fillStyle = '#4040FF'; + ctx.fillRect(g.x, g.y, g.w, g.h); + } + + // Draw unused space on side (currently wasted) + for (const r of this._unusedRects) { + ctx.fillStyle = '#FF0000'; + ctx.fillRect(r.x, r.y, r.w, r.h); + } + + // Overlay actual glyphs on top + ctx.globalAlpha = 0.5; + ctx.drawImage(this._canvas, 0, 0); + ctx.globalAlpha = 1; + + wastedPixels = slabEntryPixels - usedPixels; + + // Report stats + console.log([ + `Texture atlas stats:`, + ` Total: ${totalPixels}`, + ` Used: ${usedPixels} (${((usedPixels / totalPixels) * 100).toPrecision(2)}%)`, + ` Wasted: ${wastedPixels} (${((wastedPixels / totalPixels) * 100).toPrecision(2)}%)`, + `Efficiency: ${((usedPixels / (usedPixels + wastedPixels)) * 100).toPrecision(2)}%`, + ].join('\n')); + + return canvas.convertToBlob(); } } @@ -278,4 +363,11 @@ interface ITextureAtlasSlab { count: number; } +export interface ITextureAtlasSlabUnusedRect { + x: number; + y: number; + w: number; + h: number; +} + // #endregion From 86e5a9ac73d32f1be189d0f22d02039e245121c3 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 13:14:59 +0900 Subject: [PATCH 0053/2222] Fix efficiency log --- src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index fee5520e90f..b1e70627993 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -185,7 +185,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // w: glyphWidth % 0 === 1 ? glyphWidth + 1 : glyphWidth, // h: glyphHeight % 0 === 1 ? glyphHeight + 1 : glyphHeight, - // Exact number only + // Exact number only (100% efficiency) // w: glyphWidth, // h: glyphHeight, }; @@ -341,6 +341,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { ctx.globalAlpha = 1; wastedPixels = slabEntryPixels - usedPixels; + const efficiency = usedPixels / (usedPixels + wastedPixels); // Report stats console.log([ @@ -348,7 +349,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { ` Total: ${totalPixels}`, ` Used: ${usedPixels} (${((usedPixels / totalPixels) * 100).toPrecision(2)}%)`, ` Wasted: ${wastedPixels} (${((wastedPixels / totalPixels) * 100).toPrecision(2)}%)`, - `Efficiency: ${((usedPixels / (usedPixels + wastedPixels)) * 100).toPrecision(2)}%`, + `Efficiency: ${efficiency === 1 ? '100' : (efficiency * 100).toPrecision(2)}%`, ].join('\n')); return canvas.convertToBlob(); From e62af8fa1c8b961f7da7dbc52701712d475272a9 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 13:43:08 +0900 Subject: [PATCH 0054/2222] Use unused space on edge of slabs --- .../browser/view/gpu/textureAtlasAllocator.ts | 144 ++++++++++++------ 1 file changed, 96 insertions(+), 48 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index b1e70627993..fb7b3c39368 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -160,11 +160,6 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { public allocate(chars: string, tokenFg: number, rasterizedGlyph: IRasterizedGlyph): ITextureAtlasGlyph { // Find ideal slab, creating it if there is none suitable - - // Slabs are sized: 1x1, 1x2, 1x4, 1x8, ... - // 2x1, 2x2, 2x4, 2x8, ... - // 4x1, 4x2, 4x4, 4x8, ... - // ... const glyphWidth = rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left + 1; const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + 1; const dpr = getActiveWindow().devicePixelRatio; @@ -185,7 +180,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // w: glyphWidth % 0 === 1 ? glyphWidth + 1 : glyphWidth, // h: glyphHeight % 0 === 1 ? glyphHeight + 1 : glyphHeight, - // Exact number only (100% efficiency) + // Exact number only // w: glyphWidth, // h: glyphHeight, }; @@ -208,52 +203,107 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { } } - // Create a new slab + let dx: number | undefined; + let dy: number | undefined; + + // Search for suitable space in unused rectangles if (!slab) { - slab = { - x: Math.floor(this._slabs.length % slabsPerRow) * slabW, - y: Math.floor(this._slabs.length / slabsPerRow) * slabH, - entryW: desiredSlabSize.w, - entryH: desiredSlabSize.h, - count: 0 - }; - // Track unused regions to use for small glyphs - // +-------------+----+ - // | | | - // | | | <- Unused W region - // | | | - // |-------------+----+ - // | | <- Unused H region - // +------------------+ - const unusedW = slabW % slab.entryW; - const unusedH = slabH % slab.entryH; - if (unusedW) { - this._unusedRects.push({ - x: slab.x + slabW - unusedW, - w: unusedW, - y: slab.y, - h: slabH - (unusedH ?? 0) - }); - console.log('new unused (W)', this._unusedRects.at(-1)); + for (const [i, r] of this._unusedRects.entries()) { + if (r.w < r.h) { + if (r.w >= glyphWidth && r.h >= glyphHeight) { + dx = r.x; + dy = r.y; + if (glyphWidth < r.w) { + this._unusedRects.push({ + x: r.x + glyphWidth, + y: r.y, + w: r.w - glyphWidth, + h: glyphHeight + }); + } + r.y += glyphHeight; + r.h -= glyphHeight; + if (r.h === 0) { + // TODO: This is slow + this._unusedRects.splice(i, 1); + } + break; + } + } else { + if (r.w >= glyphWidth && r.h >= glyphHeight) { + dx = r.x; + dy = r.y; + if (glyphHeight < r.h) { + this._unusedRects.push({ + x: r.x, + y: r.y + glyphHeight, + w: glyphWidth, + h: r.h - glyphHeight + }); + } + r.x += glyphWidth; + r.w -= glyphWidth; + if (r.w === 0) { + // TODO: This is slow + this._unusedRects.splice(i, 1); + } + } + } } - if (unusedH) { - this._unusedRects.push({ - x: slab.x, - w: slabW, - y: slab.y + slabH - unusedH, - h: unusedH - }); - console.log('new unused (H)', this._unusedRects.at(-1)); + } + + // Create a new slab + if (dx === undefined || dy === undefined) { + if (!slab) { + slab = { + x: Math.floor(this._slabs.length % slabsPerRow) * slabW, + y: Math.floor(this._slabs.length / slabsPerRow) * slabH, + entryW: desiredSlabSize.w, + entryH: desiredSlabSize.h, + count: 0 + }; + // Track unused regions to use for small glyphs + // +-------------+----+ + // | | | + // | | | <- Unused W region + // | | | + // |-------------+----+ + // | | <- Unused H region + // +------------------+ + const unusedW = slabW % slab.entryW; + const unusedH = slabH % slab.entryH; + if (unusedW) { + this._unusedRects.push({ + x: slab.x + slabW - unusedW, + w: unusedW, + y: slab.y, + h: slabH - (unusedH ?? 0) + }); + console.log('new unused (W)', this._unusedRects.at(-1)); + } + if (unusedH) { + this._unusedRects.push({ + x: slab.x, + w: slabW, + y: slab.y + slabH - unusedH, + h: unusedH + }); + console.log('new unused (H)', this._unusedRects.at(-1)); + } + this._slabs.push(slab); + this._activeSlabsByDims.set(desiredSlabSize.w, desiredSlabSize.h, slab); } - this._slabs.push(slab); - this._activeSlabsByDims.set(desiredSlabSize.w, desiredSlabSize.h, slab); + + const glyphsPerRow = Math.floor(slabW / slab.entryW); + dx = slab.x + Math.floor(slab.count % glyphsPerRow) * slab.entryW; + dy = slab.y + Math.floor(slab.count / glyphsPerRow) * slab.entryH; + + // Shift current row + slab.count++; } // Draw glyph // TODO: Prefer putImageData as it doesn't do blending or scaling - const glyphsPerRow = Math.floor(slabW / slab.entryW); - const dx = slab.x + Math.floor(slab.count % glyphsPerRow) * slab.entryW; - const dy = slab.y + Math.floor(slab.count / glyphsPerRow) * slab.entryH; console.log('dx dy', dx, dy); this._ctx.drawImage( rasterizedGlyph.source, @@ -280,9 +330,6 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { originOffsetY: rasterizedGlyph.originOffset.y }; - // Shift current row - slab.count++; - // Set the glyph this.glyphMap.set(chars, tokenFg, glyph); @@ -331,6 +378,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // Draw unused space on side (currently wasted) for (const r of this._unusedRects) { + // TODO: Unused space claimed by unused rects isn't handled ctx.fillStyle = '#FF0000'; ctx.fillRect(r.x, r.y, r.w, r.h); } From 44f2c064cd8000e93aba21a2a8d5d2abd763c0a2 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 13 May 2024 14:22:11 +0900 Subject: [PATCH 0055/2222] Fix up usage stats --- .../browser/view/gpu/glyphRasterizer.ts | 24 ++++++------ .../browser/view/gpu/textureAtlasAllocator.ts | 39 ++++++++++++------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/glyphRasterizer.ts b/src/vs/editor/browser/view/gpu/glyphRasterizer.ts index 73637e24146..7a5fe1539d8 100644 --- a/src/vs/editor/browser/view/gpu/glyphRasterizer.ts +++ b/src/vs/editor/browser/view/gpu/glyphRasterizer.ts @@ -41,19 +41,17 @@ export class GlyphRasterizer extends Disposable { const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); // TODO: Hot path: Reuse object const boundingBox = this._findGlyphBoundingBox(imageData); - const offset = { - x: textMetrics.actualBoundingBoxLeft, - y: textMetrics.actualBoundingBoxAscent - }; - const size = { - w: textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft, - y: textMetrics.actualBoundingBoxDescent + textMetrics.actualBoundingBoxAscent, - wInt: Math.ceil(textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft), - yInt: Math.ceil(textMetrics.actualBoundingBoxDescent + textMetrics.actualBoundingBoxAscent), - }; - - console.log(`${chars}_${fg}`, textMetrics, boundingBox, originX, originY, { width: boundingBox.right - boundingBox.left, height: boundingBox.bottom - boundingBox.top }); - console.log('new', offset, size); + // const offset = { + // x: textMetrics.actualBoundingBoxLeft, + // y: textMetrics.actualBoundingBoxAscent + // }; + // const size = { + // w: textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft, + // y: textMetrics.actualBoundingBoxDescent + textMetrics.actualBoundingBoxAscent, + // wInt: Math.ceil(textMetrics.actualBoundingBoxRight + textMetrics.actualBoundingBoxLeft), + // yInt: Math.ceil(textMetrics.actualBoundingBoxDescent + textMetrics.actualBoundingBoxAscent), + // }; + // console.log(`${chars}_${fg}`, textMetrics, boundingBox, originX, originY, { width: boundingBox.right - boundingBox.left, height: boundingBox.bottom - boundingBox.top }); const result: IRasterizedGlyph = { source: this._canvas, boundingBox, diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index fb7b3c39368..29f3942dbcf 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -185,9 +185,6 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // h: glyphHeight, }; - // TODO: Keeping track of the slab's x and y could allow variable sized slabs and less waste - // TODO: The unused rectangle at the bottom and side of a slab could house micro glyphs like `.` - const slabW = 64 << (Math.floor(getActiveWindow().devicePixelRatio) - 1); // this._canvas.width / 8; const slabH = slabW; // this._canvas.height / 8; const slabsPerRow = Math.floor(this._canvas.width / slabW); @@ -279,7 +276,6 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { y: slab.y, h: slabH - (unusedH ?? 0) }); - console.log('new unused (W)', this._unusedRects.at(-1)); } if (unusedH) { this._unusedRects.push({ @@ -288,7 +284,6 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { y: slab.y + slabH - unusedH, h: unusedH }); - console.log('new unused (H)', this._unusedRects.at(-1)); } this._slabs.push(slab); this._activeSlabsByDims.set(desiredSlabSize.w, desiredSlabSize.h, slab); @@ -304,7 +299,6 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // Draw glyph // TODO: Prefer putImageData as it doesn't do blending or scaling - console.log('dx dy', dx, dy); this._ctx.drawImage( rasterizedGlyph.source, // source @@ -348,9 +342,12 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { let slabEntryPixels = 0; let usedPixels = 0; + let slabEdgePixels = 0; let wastedPixels = 0; + let restrictedPixels = 0; const totalPixels = w * h; const slabW = 64 << (Math.floor(getActiveWindow().devicePixelRatio) - 1); + const slabH = slabW; // Draw wasted underneath glyphs first for (const slab of this._slabs) { @@ -364,9 +361,14 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // TODO: This doesn't visualize wasted space between entries - draw glyphs on top? ctx.fillStyle = '#FF0000'; ctx.fillRect(slab.x + x, slab.y + y, slab.entryW, slab.entryH); + slabEntryPixels += slab.entryW * slab.entryH; x += slab.entryW; } + const entriesPerRow = Math.floor(slabW / slab.entryW); + const entriesPerCol = Math.floor(slabH / slab.entryH); + const thisSlabPixels = slab.entryW * entriesPerRow * slab.entryH * entriesPerCol; + slabEdgePixels += (slabW * slabH) - thisSlabPixels; } // Draw glyphs @@ -376,28 +378,35 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { ctx.fillRect(g.x, g.y, g.w, g.h); } - // Draw unused space on side (currently wasted) + // Draw unused space on side for (const r of this._unusedRects) { - // TODO: Unused space claimed by unused rects isn't handled - ctx.fillStyle = '#FF0000'; + ctx.fillStyle = '#FF000080'; ctx.fillRect(r.x, r.y, r.w, r.h); + restrictedPixels += r.w * r.h; } + const edgeUsedPixels = slabEdgePixels - restrictedPixels; + console.log({ edgeUsedPixels, slabEdgePixels, restrictedPixels }); + wastedPixels = slabEntryPixels - (usedPixels - edgeUsedPixels); + + // Overlay actual glyphs on top ctx.globalAlpha = 0.5; ctx.drawImage(this._canvas, 0, 0); ctx.globalAlpha = 1; - wastedPixels = slabEntryPixels - usedPixels; - const efficiency = usedPixels / (usedPixels + wastedPixels); + // usedPixels += slabEdgePixels - restrictedPixels; + const efficiency = usedPixels / (usedPixels + wastedPixels + restrictedPixels); // Report stats console.log([ `Texture atlas stats:`, - ` Total: ${totalPixels}`, - ` Used: ${usedPixels} (${((usedPixels / totalPixels) * 100).toPrecision(2)}%)`, - ` Wasted: ${wastedPixels} (${((wastedPixels / totalPixels) * 100).toPrecision(2)}%)`, - `Efficiency: ${efficiency === 1 ? '100' : (efficiency * 100).toPrecision(2)}%`, + ` Total: ${totalPixels}px`, + ` Used: ${usedPixels}px (${((usedPixels / totalPixels) * 100).toFixed(2)}%)`, + ` Wasted: ${wastedPixels}px (${((wastedPixels / totalPixels) * 100).toFixed(2)}%)`, + `Restricted: ${restrictedPixels}px (${((restrictedPixels / totalPixels) * 100).toFixed(2)}%) (hard to allocate)`, + `Efficiency: ${efficiency === 1 ? '100' : (efficiency * 100).toFixed(2)}%`, + ` Slabs: ${this._slabs.length} of ${Math.floor(this._canvas.width / slabW) * Math.floor(this._canvas.height / slabH)}` ].join('\n')); return canvas.convertToBlob(); From eeadf297a3694c415226aa089a403506b16dae84 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 15 May 2024 20:15:27 +1000 Subject: [PATCH 0056/2222] Tweak nearest x pixels --- src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index 29f3942dbcf..812b979d4e7 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -164,8 +164,11 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + 1; const dpr = getActiveWindow().devicePixelRatio; + // TODO: Include font size as well as DPR in nearestXPixels calculation + // Round slab glyph dimensions to the nearest x pixels, where x scaled with device pixel ratio - const nearestXPixels = Math.max(1, Math.floor(dpr / 0.5)); + // const nearestXPixels = Math.max(1, Math.floor(dpr / 0.5)); + const nearestXPixels = Math.max(1, Math.floor(dpr)); const desiredSlabSize = { // Nearest square number // TODO: This can probably be optimized From 3de59fcc49c151915196a246e375574c09d58532 Mon Sep 17 00:00:00 2001 From: Ritam Mukherjee Date: Wed, 22 May 2024 21:03:54 +0530 Subject: [PATCH 0057/2222] feat: allows cli to serve locally cached server when update service not available --- cli/src/commands/serve_web.rs | 24 ++++++++++++++++++++++-- cli/src/download_cache.rs | 25 ++++++++++++++++++++++++- 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/cli/src/commands/serve_web.rs b/cli/src/commands/serve_web.rs index fba92723426..313386a380c 100644 --- a/cli/src/commands/serve_web.rs +++ b/cli/src/commands/serve_web.rs @@ -516,7 +516,7 @@ impl ConnectionManager { platform, args, log: ctx.log.clone(), - cache: DownloadCache::new(ctx.paths.web_server_storage()), + cache: DownloadCache::load(ctx.paths.web_server_storage()), update_service: UpdateService::new( ctx.log.clone(), Arc::new(ReqwestSimpleHttp::with_client(ctx.http.clone())), @@ -544,6 +544,7 @@ impl ConnectionManager { pub async fn get_latest_release(&self) -> Result { let mut latest = self.latest_version.lock().await; let now = Instant::now(); + let target_kind = TargetKind::Web; if let Some((checked_at, release)) = &*latest { if checked_at.elapsed() < Duration::from_secs(RELEASE_CACHE_SECS) { return Ok(release.clone()); @@ -558,7 +559,7 @@ impl ConnectionManager { let release = self .update_service - .get_latest_commit(self.platform, TargetKind::Web, quality) + .get_latest_commit(self.platform, target_kind, quality) .await .map_err(|e| CodeError::UpdateCheckFailed(e.to_string())); @@ -568,6 +569,25 @@ impl ConnectionManager { return Ok(previous.clone()); } + // If the update service is unavailable and we have cached data, use that + if let Err(e) = &release { + warning!(self.log, "error getting latest release: {}", e); + if let Some(latest_commit) = self.cache.get().first() { + warning!(self.log, "using latest release available from cache"); + let release = Release { + name: String::from("0.0.0"), // Version information not stored on cache + commit: latest_commit.clone(), + platform: self.platform, + target: target_kind, + quality + }; + + *latest = Some((now, release.clone())); + + return Ok(release) + } + } + let release = release?; debug!(self.log, "refreshed latest release: {}", release); *latest = Some((now, release.clone())); diff --git a/cli/src/download_cache.rs b/cli/src/download_cache.rs index d3f05d2237f..5f245e810a1 100644 --- a/cli/src/download_cache.rs +++ b/cli/src/download_cache.rs @@ -20,6 +20,7 @@ const KEEP_LRU: usize = 5; const STAGING_SUFFIX: &str = ".staging"; const RENAME_ATTEMPTS: u32 = 20; const RENAME_DELAY: std::time::Duration = std::time::Duration::from_millis(200); +const PERSISTED_STATE_FILE_NAME: &str = "lru.json"; #[derive(Clone)] pub struct DownloadCache { @@ -30,11 +31,33 @@ pub struct DownloadCache { impl DownloadCache { pub fn new(path: PathBuf) -> DownloadCache { DownloadCache { - state: PersistedState::new(path.join("lru.json")), + state: PersistedState::new(path.join(PERSISTED_STATE_FILE_NAME)), path, } } + /// Gets an DownloadCache with previously persisted value if it exists + /// on the persistant storage, else returns a new DownloadCache. + pub fn load(path: PathBuf) -> DownloadCache { + let state = PersistedState::>::new(path.join(PERSISTED_STATE_FILE_NAME)); + match state.load().is_empty() { + true => DownloadCache { + state: PersistedState::new(path.join(PERSISTED_STATE_FILE_NAME)), + path, + }, + false => DownloadCache { + state, + path, + } + } + } + + /// Gets the value stored on the state + pub fn get(&self) -> Vec { + let state_value = self.state.load(); + state_value + } + /// Gets the download cache path. Names of cache entries can be formed by /// joining them to the path. pub fn path(&self) -> &Path { From 23f2247b7274c2a750d3e7fa778362bf16a33ddb Mon Sep 17 00:00:00 2001 From: Ritam Mukherjee Date: Wed, 22 May 2024 22:33:16 +0530 Subject: [PATCH 0058/2222] refactor: fix clippy let-and-return error in linting --- cli/src/download_cache.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/cli/src/download_cache.rs b/cli/src/download_cache.rs index 5f245e810a1..dae12a6ea62 100644 --- a/cli/src/download_cache.rs +++ b/cli/src/download_cache.rs @@ -54,8 +54,7 @@ impl DownloadCache { /// Gets the value stored on the state pub fn get(&self) -> Vec { - let state_value = self.state.load(); - state_value + self.state.load() } /// Gets the download cache path. Names of cache entries can be formed by From bc5e7b51a2d538c0d36da7e1a0dd0d9829707a82 Mon Sep 17 00:00:00 2001 From: Ritam Mukherjee Date: Wed, 22 May 2024 23:05:10 +0530 Subject: [PATCH 0059/2222] refactor: removed unnecessary check for empty vector in DownloadCache load --- cli/src/download_cache.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/cli/src/download_cache.rs b/cli/src/download_cache.rs index dae12a6ea62..5a343315d86 100644 --- a/cli/src/download_cache.rs +++ b/cli/src/download_cache.rs @@ -40,15 +40,10 @@ impl DownloadCache { /// on the persistant storage, else returns a new DownloadCache. pub fn load(path: PathBuf) -> DownloadCache { let state = PersistedState::>::new(path.join(PERSISTED_STATE_FILE_NAME)); - match state.load().is_empty() { - true => DownloadCache { - state: PersistedState::new(path.join(PERSISTED_STATE_FILE_NAME)), - path, - }, - false => DownloadCache { - state, - path, - } + state.load(); + DownloadCache { + state, + path, } } From a5b3f579dbd6c8aa89f94ac5612f79aac9a3c472 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Fri, 24 May 2024 08:12:54 +0900 Subject: [PATCH 0060/2222] progress --- .../browser/view/gpu/glyphRasterizer.ts | 35 ++-- .../editor/browser/view/gpu/gpuViewLayer.ts | 2 +- .../editor/browser/view/gpu/textureAtlas.ts | 22 ++- .../browser/view/gpu/textureAtlasAllocator.ts | 165 +++++++++++++----- 4 files changed, 160 insertions(+), 64 deletions(-) diff --git a/src/vs/editor/browser/view/gpu/glyphRasterizer.ts b/src/vs/editor/browser/view/gpu/glyphRasterizer.ts index 7a5fe1539d8..a4b9829b5df 100644 --- a/src/vs/editor/browser/view/gpu/glyphRasterizer.ts +++ b/src/vs/editor/browser/view/gpu/glyphRasterizer.ts @@ -35,7 +35,8 @@ export class GlyphRasterizer extends Disposable { const originX = this._fontSize; const originY = this._fontSize; this._ctx.fillStyle = fg; - const textMetrics = this._ctx.measureText(chars); + // TODO: This might actually be slower + // const textMetrics = this._ctx.measureText(chars); this._ctx.fillText(chars, originX, originY); const imageData = this._ctx.getImageData(0, 0, this._canvas.width, this._canvas.height); @@ -60,19 +61,19 @@ export class GlyphRasterizer extends Disposable { y: boundingBox.top - originY } }; - const result2: IRasterizedGlyph = { - source: this._canvas, - boundingBox: { - left: Math.floor(originX - textMetrics.actualBoundingBoxLeft), - right: Math.ceil(originX + textMetrics.actualBoundingBoxRight), - top: Math.floor(originY - textMetrics.actualBoundingBoxAscent), - bottom: Math.ceil(originY + textMetrics.actualBoundingBoxDescent), - }, - originOffset: { - x: Math.floor(boundingBox.left - originX), - y: Math.floor(boundingBox.top - originY) - } - }; + // const result2: IRasterizedGlyph = { + // source: this._canvas, + // boundingBox: { + // left: Math.floor(originX - textMetrics.actualBoundingBoxLeft), + // right: Math.ceil(originX + textMetrics.actualBoundingBoxRight), + // top: Math.floor(originY - textMetrics.actualBoundingBoxAscent), + // bottom: Math.ceil(originY + textMetrics.actualBoundingBoxDescent), + // }, + // originOffset: { + // x: Math.floor(boundingBox.left - originX), + // y: Math.floor(boundingBox.top - originY) + // } + // }; // DEBUG: Show image data in console // (console as any).image(imageData); @@ -91,9 +92,9 @@ export class GlyphRasterizer extends Disposable { // if (result2.boundingBox.bottom < result.boundingBox.bottom) { // debugger; // } - if (JSON.stringify(result2.originOffset) !== JSON.stringify(result.originOffset)) { - debugger; - } + // if (JSON.stringify(result2.originOffset) !== JSON.stringify(result.originOffset)) { + // debugger; + // } return result; } diff --git a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts index eeb7227f00c..0d62f45cfa0 100644 --- a/src/vs/editor/browser/view/gpu/gpuViewLayer.ts +++ b/src/vs/editor/browser/view/gpu/gpuViewLayer.ts @@ -111,7 +111,7 @@ export class GpuViewLayerRenderer { // Create texture atlas if (!GpuViewLayerRenderer._textureAtlas) { - const pageSize = 1024; // this._device.limits.maxTextureDimension2D; + const pageSize = this._device.limits.maxTextureDimension2D; GpuViewLayerRenderer._textureAtlas = this._instantiationService.createInstance(TextureAtlas, this.domNode, pageSize, this._device.limits.maxTextureDimension2D); } const textureAtlas = GpuViewLayerRenderer._textureAtlas; diff --git a/src/vs/editor/browser/view/gpu/textureAtlas.ts b/src/vs/editor/browser/view/gpu/textureAtlas.ts index 13fd80ee962..5d71fb53af2 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlas.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlas.ts @@ -111,9 +111,27 @@ export class TextureAtlas extends Disposable { // TODO: Clean up on dispose this._warmUpTask?.clear(); this._warmUpTask = new IdleTaskQueue(); - for (const tokenFg of this._colorMap.keys()) { + // Warm up using roughly the larger glyphs first to help optimize atlas allocation + // A-Z + for (let code = 65; code <= 90; code++) { this._warmUpTask.enqueue(() => { - for (let code = 33; code <= 126; code++) { + for (const tokenFg of this._colorMap.keys()) { + this.getGlyph(String.fromCharCode(code), tokenFg); + } + }); + } + // a-z + for (let code = 97; code <= 122; code++) { + this._warmUpTask.enqueue(() => { + for (const tokenFg of this._colorMap.keys()) { + this.getGlyph(String.fromCharCode(code), tokenFg); + } + }); + } + // Remaining ascii + for (let code = 33; code <= 126; code++) { + this._warmUpTask.enqueue(() => { + for (const tokenFg of this._colorMap.keys()) { this.getGlyph(String.fromCharCode(code), tokenFg); } }); diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index 812b979d4e7..e881337feb4 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -148,6 +148,9 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { private _unusedRects: ITextureAtlasSlabUnusedRect[] = []; + private _openRegionsByHeight: Map = new Map(); + private _openRegionsByWidth: Map = new Map(); + readonly glyphMap: TwoKeyMap = new TwoKeyMap(); private _nextIndex = 0; @@ -168,7 +171,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // Round slab glyph dimensions to the nearest x pixels, where x scaled with device pixel ratio // const nearestXPixels = Math.max(1, Math.floor(dpr / 0.5)); - const nearestXPixels = Math.max(1, Math.floor(dpr)); + // const nearestXPixels = Math.max(1, Math.floor(dpr)); const desiredSlabSize = { // Nearest square number // TODO: This can probably be optimized @@ -176,16 +179,16 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // h: 1 << Math.ceil(Math.sqrt(glyphHeight)), // Nearest x px - w: Math.ceil(glyphWidth / nearestXPixels) * nearestXPixels, - h: Math.ceil(glyphHeight / nearestXPixels) * nearestXPixels, + // w: Math.ceil(glyphWidth / nearestXPixels) * nearestXPixels, + // h: Math.ceil(glyphHeight / nearestXPixels) * nearestXPixels, // Round odd numbers up // w: glyphWidth % 0 === 1 ? glyphWidth + 1 : glyphWidth, // h: glyphHeight % 0 === 1 ? glyphHeight + 1 : glyphHeight, // Exact number only - // w: glyphWidth, - // h: glyphHeight, + w: glyphWidth, + h: glyphHeight, }; const slabW = 64 << (Math.floor(getActiveWindow().devicePixelRatio) - 1); // this._canvas.width / 8; @@ -208,48 +211,112 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // Search for suitable space in unused rectangles if (!slab) { - for (const [i, r] of this._unusedRects.entries()) { - if (r.w < r.h) { - if (r.w >= glyphWidth && r.h >= glyphHeight) { - dx = r.x; - dy = r.y; - if (glyphWidth < r.w) { - this._unusedRects.push({ - x: r.x + glyphWidth, - y: r.y, - w: r.w - glyphWidth, - h: glyphHeight - }); + // Only check availability for the smallest side + if (glyphWidth < glyphHeight) { + const openRegions = this._openRegionsByWidth.get(glyphWidth); + if (openRegions?.length) { + // TODO: Don't search everything? + // Search from the end so we can typically pop it off the stack + for (let i = openRegions.length - 1; i >= 0; i--) { + const r = openRegions[i]; + if (r.w >= glyphWidth && r.h >= glyphHeight) { + dx = r.x; + dy = r.y; + if (glyphWidth < r.w) { + this._unusedRects.push({ + x: r.x + glyphWidth, + y: r.y, + w: r.w - glyphWidth, + h: glyphHeight + }); + } + r.y += glyphHeight; + r.h -= glyphHeight; + if (r.h === 0) { + if (i === openRegions.length - 1) { + openRegions.pop(); + } else { + this._unusedRects.splice(i, 1); + } + } + break; } - r.y += glyphHeight; - r.h -= glyphHeight; - if (r.h === 0) { - // TODO: This is slow - this._unusedRects.splice(i, 1); - } - break; } - } else { - if (r.w >= glyphWidth && r.h >= glyphHeight) { - dx = r.x; - dy = r.y; - if (glyphHeight < r.h) { - this._unusedRects.push({ - x: r.x, - y: r.y + glyphHeight, - w: glyphWidth, - h: r.h - glyphHeight - }); - } - r.x += glyphWidth; - r.w -= glyphWidth; - if (r.w === 0) { - // TODO: This is slow - this._unusedRects.splice(i, 1); + } + } else { + const openRegions = this._openRegionsByHeight.get(glyphHeight); + if (openRegions?.length) { + // TODO: Don't search everything? + // Search from the end so we can typically pop it off the stack + for (let i = openRegions.length - 1; i >= 0; i--) { + const r = openRegions[i]; + if (r.w >= glyphWidth && r.h >= glyphHeight) { + dx = r.x; + dy = r.y; + if (glyphHeight < r.h) { + this._unusedRects.push({ + x: r.x, + y: r.y + glyphHeight, + w: glyphWidth, + h: r.h - glyphHeight + }); + } + r.x += glyphWidth; + r.w -= glyphWidth; + if (r.h === 0) { + if (i === openRegions.length - 1) { + openRegions.pop(); + } else { + this._unusedRects.splice(i, 1); + } + } + break; } } } } + // for (const [i, r] of this._unusedRects.entries()) { + // if (r.w < r.h) { + // if (r.w >= glyphWidth && r.h >= glyphHeight) { + // dx = r.x; + // dy = r.y; + // if (glyphWidth < r.w) { + // this._unusedRects.push({ + // x: r.x + glyphWidth, + // y: r.y, + // w: r.w - glyphWidth, + // h: glyphHeight + // }); + // } + // r.y += glyphHeight; + // r.h -= glyphHeight; + // if (r.h === 0) { + // // TODO: This is slow + // this._unusedRects.splice(i, 1); + // } + // break; + // } + // } else { + // if (r.w >= glyphWidth && r.h >= glyphHeight) { + // dx = r.x; + // dy = r.y; + // if (glyphHeight < r.h) { + // this._unusedRects.push({ + // x: r.x, + // y: r.y + glyphHeight, + // w: glyphWidth, + // h: r.h - glyphHeight + // }); + // } + // r.x += glyphWidth; + // r.w -= glyphWidth; + // if (r.w === 0) { + // // TODO: This is slow + // this._unusedRects.splice(i, 1); + // } + // } + // } + // } } // Create a new slab @@ -273,7 +340,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { const unusedW = slabW % slab.entryW; const unusedH = slabH % slab.entryH; if (unusedW) { - this._unusedRects.push({ + addEntryToMapArray(this._openRegionsByWidth, unusedW, { x: slab.x + slabW - unusedW, w: unusedW, y: slab.y, @@ -281,7 +348,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { }); } if (unusedH) { - this._unusedRects.push({ + addEntryToMapArray(this._openRegionsByHeight, unusedH, { x: slab.x, w: slabW, y: slab.y + slabH - unusedH, @@ -382,7 +449,8 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { } // Draw unused space on side - for (const r of this._unusedRects) { + const unusedRegions = Array.from(this._openRegionsByWidth.values()).flat().concat(Array.from(this._openRegionsByHeight.values()).flat()); + for (const r of unusedRegions) { ctx.fillStyle = '#FF000080'; ctx.fillRect(r.x, r.y, r.w, r.h); restrictedPixels += r.w * r.h; @@ -431,4 +499,13 @@ export interface ITextureAtlasSlabUnusedRect { h: number; } +function addEntryToMapArray(map: Map, key: K, entry: V) { + let list = map.get(key); + if (!list) { + list = []; + map.set(key, list); + } + list.push(entry); +} + // #endregion From 8c82ca149cd36c6a269ed3d765b7791bded87334 Mon Sep 17 00:00:00 2001 From: Ole Date: Fri, 7 Jun 2024 13:50:01 +0200 Subject: [PATCH 0061/2222] Fix leaking comment thread when CellComment is reused. Fixes #214585. --- .../comments/browser/commentThreadHeader.ts | 3 +- .../comments/browser/commentThreadWidget.ts | 3 +- .../browser/view/cellParts/cellComments.ts | 74 +++++++++---------- test/automation/package.json | 4 +- 4 files changed, 43 insertions(+), 41 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts b/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts index 2849c9afd76..8333654958e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadHeader.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { Action, ActionRunner } from 'vs/base/common/actions'; import { Codicon } from 'vs/base/common/codicons'; -import { Disposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import * as languages from 'vs/editor/common/languages'; import { IRange } from 'vs/editor/common/core/range'; @@ -46,6 +46,7 @@ export class CommentThreadHeader extends Disposable { super(); this._headElement = dom.$('.head'); container.appendChild(this._headElement); + this._register(toDisposable(() => this._headElement.remove())); this._fillHead(); } diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index a919fe262a9..521455bb77a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/review'; import * as dom from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; -import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, dispose, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import * as languages from 'vs/editor/common/languages'; import { IMarkdownRendererOptions } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; @@ -104,6 +104,7 @@ export class CommentThreadWidget extends const bodyElement = dom.$('.body'); container.appendChild(bodyElement); + this._register(toDisposable(() => bodyElement.remove())); const tracker = this._register(dom.trackFocus(bodyElement)); this._register(registerNavigableContainer({ diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts index b8ccbf07abf..7e4ad29842b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts @@ -20,10 +20,9 @@ import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; export class CellComments extends CellContentPart { - private _initialized: boolean = false; private _commentThreadWidget: CommentThreadWidget | null = null; private currentElement: CodeCellViewModel | undefined; - private readonly commentTheadDisposables = this._register(new DisposableStore()); + private readonly _commentThreadDisposables = this._register(new DisposableStore()); constructor( private readonly notebookEditor: INotebookEditorDelegate, @@ -45,21 +44,17 @@ export class CellComments extends CellContentPart { } private async initialize(element: ICellViewModel) { - if (this._initialized) { + if (this.currentElement === element) { return; } - this._initialized = true; - const info = await this._getCommentThreadForCell(element); - - if (info) { - await this._createCommentTheadWidget(info.owner, info.thread); - } + this.currentElement = element as CodeCellViewModel; + this._updateThread(); } private async _createCommentTheadWidget(owner: string, commentThread: languages.CommentThread) { this._commentThreadWidget?.dispose(); - this.commentTheadDisposables.clear(); + this._commentThreadDisposables.clear(); this._commentThreadWidget = this.instantiationService.createInstance( CommentThreadWidget, this.container, @@ -87,7 +82,7 @@ export class CellComments extends CellContentPart { await this._commentThreadWidget.display(layoutInfo.fontInfo.lineHeight, true); this._applyTheme(); - this.commentTheadDisposables.add(this._commentThreadWidget.onDidResize(() => { + this._commentThreadDisposables.add(this._commentThreadWidget.onDidResize(() => { if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget) { this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); } @@ -95,33 +90,38 @@ export class CellComments extends CellContentPart { } private _bindListeners() { - this.cellDisposables.add(this.commentService.onDidUpdateCommentThreads(async () => { - if (this.currentElement) { - const info = await this._getCommentThreadForCell(this.currentElement); - if (!this._commentThreadWidget && info) { - await this._createCommentTheadWidget(info.owner, info.thread); - const layoutInfo = (this.currentElement as CodeCellViewModel).layoutInfo; - this.container.style.top = `${layoutInfo.outputContainerOffset + layoutInfo.outputTotalHeight}px`; - this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget!.getDimensions().height); - return; - } - - if (this._commentThreadWidget) { - if (!info) { - this._commentThreadWidget.dispose(); - this.currentElement.commentHeight = 0; - return; - } - if (this._commentThreadWidget.commentThread === info.thread) { - this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); - return; - } - - await this._commentThreadWidget.updateCommentThread(info.thread); - this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); - } + this.cellDisposables.add(this.commentService.onDidUpdateCommentThreads(async () => this._updateThread())); + } + + private async _updateThread() { + if (!this.currentElement) { + return; + } + const info = await this._getCommentThreadForCell(this.currentElement); + if (!this._commentThreadWidget && info) { + await this._createCommentTheadWidget(info.owner, info.thread); + const layoutInfo = (this.currentElement as CodeCellViewModel).layoutInfo; + this.container.style.top = `${layoutInfo.outputContainerOffset + layoutInfo.outputTotalHeight}px`; + this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget!.getDimensions().height); + return; + } + + if (this._commentThreadWidget) { + if (!info) { + this._commentThreadDisposables.clear(); + this._commentThreadWidget.dispose(); + this._commentThreadWidget = null; + this.currentElement.commentHeight = 0; + return; } - })); + if (this._commentThreadWidget.commentThread === info.thread) { + this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); + return; + } + + await this._commentThreadWidget.updateCommentThread(info.thread); + this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); + } } private _calculateCommentThreadHeight(bodyHeight: number) { diff --git a/test/automation/package.json b/test/automation/package.json index b9dbbec4bb8..8172250a9a3 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -1,6 +1,6 @@ { "name": "vscode-automation", - "version": "1.71.0", + "version": "1.91.0", "description": "VS Code UI automation driver", "author": { "name": "Microsoft Corporation" @@ -33,4 +33,4 @@ "npm-run-all": "^4.1.5", "watch": "^1.0.2" } -} +} \ No newline at end of file From f68fa7e09f252a6121d110bdd0664a7f4e64e1ad Mon Sep 17 00:00:00 2001 From: Ole Date: Fri, 7 Jun 2024 14:01:44 +0200 Subject: [PATCH 0062/2222] Undo accidental change in test/automation/package.json. --- test/automation/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/automation/package.json b/test/automation/package.json index 8172250a9a3..b9dbbec4bb8 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -1,6 +1,6 @@ { "name": "vscode-automation", - "version": "1.91.0", + "version": "1.71.0", "description": "VS Code UI automation driver", "author": { "name": "Microsoft Corporation" @@ -33,4 +33,4 @@ "npm-run-all": "^4.1.5", "watch": "^1.0.2" } -} \ No newline at end of file +} From 825680cf03c8fae03fba14152475c0e5e2d541b5 Mon Sep 17 00:00:00 2001 From: Ole Date: Mon, 17 Jun 2024 17:04:53 +0200 Subject: [PATCH 0063/2222] Use MutableDisposable and await _updateThread. --- .../browser/view/cellParts/cellComments.ts | 40 +++++++++---------- test/automation/package.json | 4 +- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts index 7e4ad29842b..cbf886228ef 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce } from 'vs/base/common/arrays'; -import { DisposableStore } from 'vs/base/common/lifecycle'; +import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; import { EDITOR_FONT_DEFAULTS, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import * as languages from 'vs/editor/common/languages'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -20,7 +20,7 @@ import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; export class CellComments extends CellContentPart { - private _commentThreadWidget: CommentThreadWidget | null = null; + private readonly _commentThreadWidget = new MutableDisposable>; private currentElement: CodeCellViewModel | undefined; private readonly _commentThreadDisposables = this._register(new DisposableStore()); @@ -49,13 +49,12 @@ export class CellComments extends CellContentPart { } this.currentElement = element as CodeCellViewModel; - this._updateThread(); + await this._updateThread(); } private async _createCommentTheadWidget(owner: string, commentThread: languages.CommentThread) { - this._commentThreadWidget?.dispose(); this._commentThreadDisposables.clear(); - this._commentThreadWidget = this.instantiationService.createInstance( + this._commentThreadWidget.value = this.instantiationService.createInstance( CommentThreadWidget, this.container, this.notebookEditor, @@ -79,12 +78,12 @@ export class CellComments extends CellContentPart { const layoutInfo = this.notebookEditor.getLayoutInfo(); - await this._commentThreadWidget.display(layoutInfo.fontInfo.lineHeight, true); + await this._commentThreadWidget.value.display(layoutInfo.fontInfo.lineHeight, true); this._applyTheme(); - this._commentThreadDisposables.add(this._commentThreadWidget.onDidResize(() => { - if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget) { - this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); + this._commentThreadDisposables.add(this._commentThreadWidget.value.onDidResize(() => { + if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget.value) { + this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value.getDimensions().height); } })); } @@ -98,29 +97,28 @@ export class CellComments extends CellContentPart { return; } const info = await this._getCommentThreadForCell(this.currentElement); - if (!this._commentThreadWidget && info) { + if (!this._commentThreadWidget.value && info) { await this._createCommentTheadWidget(info.owner, info.thread); const layoutInfo = (this.currentElement as CodeCellViewModel).layoutInfo; this.container.style.top = `${layoutInfo.outputContainerOffset + layoutInfo.outputTotalHeight}px`; - this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget!.getDimensions().height); + this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value!.getDimensions().height); return; } - if (this._commentThreadWidget) { + if (this._commentThreadWidget.value) { if (!info) { this._commentThreadDisposables.clear(); this._commentThreadWidget.dispose(); - this._commentThreadWidget = null; this.currentElement.commentHeight = 0; return; } - if (this._commentThreadWidget.commentThread === info.thread) { - this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); + if (this._commentThreadWidget.value.commentThread === info.thread) { + this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value.getDimensions().height); return; } - await this._commentThreadWidget.updateCommentThread(info.thread); - this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); + await this._commentThreadWidget.value.updateCommentThread(info.thread); + this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value.getDimensions().height); } } @@ -151,7 +149,7 @@ export class CellComments extends CellContentPart { private _applyTheme() { const theme = this.themeService.getColorTheme(); const fontInfo = this.notebookEditor.getLayoutInfo().fontInfo; - this._commentThreadWidget?.applyTheme(theme, fontInfo); + this._commentThreadWidget.value?.applyTheme(theme, fontInfo); } override didRenderCell(element: ICellViewModel): void { @@ -164,13 +162,13 @@ export class CellComments extends CellContentPart { } override prepareLayout(): void { - if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget) { - this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.getDimensions().height); + if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget.value) { + this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value.getDimensions().height); } } override updateInternalLayoutNow(element: ICellViewModel): void { - if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget) { + if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget.value) { const layoutInfo = (element as CodeCellViewModel).layoutInfo; this.container.style.top = `${layoutInfo.outputContainerOffset + layoutInfo.outputTotalHeight}px`; } diff --git a/test/automation/package.json b/test/automation/package.json index b9dbbec4bb8..8172250a9a3 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -1,6 +1,6 @@ { "name": "vscode-automation", - "version": "1.71.0", + "version": "1.91.0", "description": "VS Code UI automation driver", "author": { "name": "Microsoft Corporation" @@ -33,4 +33,4 @@ "npm-run-all": "^4.1.5", "watch": "^1.0.2" } -} +} \ No newline at end of file From 8a6edc51da302ea3d011b90fd530209bab33f0f8 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Mon, 17 Jun 2024 12:20:24 -0700 Subject: [PATCH 0064/2222] add menu contribution for python manage config --- src/vs/platform/actions/common/actions.ts | 1 + .../testing/browser/testingExplorerView.ts | 24 +++++++++++++++---- .../actions/common/menusExtensionPoint.ts | 5 ++++ 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index fa9bd76ed8a..2338ee797c6 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -138,6 +138,7 @@ export class MenuId { static readonly StickyScrollContext = new MenuId('StickyScrollContext'); static readonly TestItem = new MenuId('TestItem'); static readonly TestItemGutter = new MenuId('TestItemGutter'); + static readonly TestPythonConfigMenu = new MenuId('TestPythonConfigMenu'); static readonly TestMessageContext = new MenuId('TestMessageContext'); static readonly TestMessageContent = new MenuId('TestMessageContent'); static readonly TestPeekElement = new MenuId('TestPeekElement'); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 5c5572d1dc0..6d122015ad8 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -30,7 +30,7 @@ import 'vs/css!./media/testing'; import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; import { localize } from 'vs/nls'; import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; -import { MenuEntryActionViewItem, createActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { MenuEntryActionViewItem, createActionViewItem, createAndFillInActionBarActions, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -115,6 +115,7 @@ export class TestingExplorerView extends ViewPane { @IHoverService hoverService: IHoverService, @ITestProfileService private readonly testProfileService: ITestProfileService, @ICommandService private readonly commandService: ICommandService, + @IMenuService private readonly menuService: IMenuService, ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); @@ -349,9 +350,19 @@ export class TestingExplorerView extends ViewPane { } } - // If there's only one group, don't add a heading for it in the dropdown. - if (participatingGroups === 1) { - profileActions.shift(); + const menuActions: IAction[] = []; + + const key = this.contextKeyService.createOverlay([]); + const menu = this.menuService.createMenu(MenuId.TestPythonConfigMenu, key); + const actions = menu.getActions({ renderShortTitle: true }).flatMap(entry => entry[1]); + if (actions.length > 0) { + // fill if there are any actions + try { + createAndFillInContextMenuActions(menu, undefined, menuActions); + } finally { + menu.dispose(); + } + } const postActions: IAction[] = []; @@ -375,7 +386,10 @@ export class TestingExplorerView extends ViewPane { )); } - return Separator.join(profileActions, postActions); + // show menu actions if there are any otherwise don't + return menuActions.length > 0 + ? Separator.join(profileActions, menuActions, postActions) + : Separator.join(profileActions, postActions); } /** diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 6791e00042c..a8cf20f1132 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -343,6 +343,11 @@ const apiMenus: IAPIMenu[] = [ id: MenuId.TestItemGutter, description: localize('testing.item.gutter.title', "The menu for a gutter decoration for a test item"), }, + { + key: 'testing/pythonConfig', + id: MenuId.TestPythonConfigMenu, + description: localize('testing.pythonConfig.title', "The menu for configuring Python tests"), + }, { key: 'testing/item/result', id: MenuId.TestPeekElement, From 52f062f709290eec516dbf5db8562ef33d5ef5b9 Mon Sep 17 00:00:00 2001 From: Ole Date: Tue, 18 Jun 2024 11:11:37 +0200 Subject: [PATCH 0065/2222] Revert again the unintentional change to test/automation/package.json. I wonder if VSCode or some extension I have keeps updating this? I am not doing it handishly. Anyways, gone again. --- test/automation/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/automation/package.json b/test/automation/package.json index 8172250a9a3..b9dbbec4bb8 100644 --- a/test/automation/package.json +++ b/test/automation/package.json @@ -1,6 +1,6 @@ { "name": "vscode-automation", - "version": "1.91.0", + "version": "1.71.0", "description": "VS Code UI automation driver", "author": { "name": "Microsoft Corporation" @@ -33,4 +33,4 @@ "npm-run-all": "^4.1.5", "watch": "^1.0.2" } -} \ No newline at end of file +} From c7a975543b03540d5770f30382754ca85f43bdcc Mon Sep 17 00:00:00 2001 From: Cody Beyer Date: Fri, 21 Jun 2024 15:43:18 -0700 Subject: [PATCH 0066/2222] adding auzre ai package tagging for js --- .../electron-sandbox/workspaceTagsService.ts | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index c77eadc1d0c..ff253ad3f84 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -208,6 +208,29 @@ const ModulesToLookFor = [ 'tika', 'weaviate-client', '@zilliz/milvus2-sdk-node', + //Azure AI + '@azure-rest/ai-anomaly-detector', + '@azure-rest/ai-content-safety', + '@azure-rest/ai-document-intelligence', + '@azure-rest/ai-document-translator', + '@azure-rest/ai-personalizer', + '@azure-rest/ai-translation-text', + '@azure-rest/ai-vision-image-analysis', + '@azure/ai-anomaly-detector', + '@azure/ai-form-recognizer', + '@azure/ai-language-conversations', + '@azure/ai-language-text', + '@azure/ai-text-analytics', + '@azure/arm-botservice', + '@azure/arm-cognitiveservices', + '@azure/arm-machinelearning', + '@azure/cognitiveservices-contentmoderator', + '@azure/cognitiveservices-customvision-prediction', + '@azure/cognitiveservices-customvision-training', + '@azure/cognitiveservices-face', + '@azure/cognitiveservices-translatortext', + 'microsoft-cognitiveservices-speech-sdk' + ]; const PyMetaModulesToLookFor = [ @@ -567,6 +590,27 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.npm.@azure/synapse-artifacts" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@azure/synapse-access-control" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@azure/ai-metrics-advisor" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure-rest/ai-anomaly-detector" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure-rest/ai-content-safety" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure-rest/ai-document-intelligence" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure-rest/ai-document-translator" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure-rest/ai-personalizer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure-rest/ai-translation-text" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure-rest/ai-vision-image-analysis" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/ai-anomaly-detector" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/ai-form-recognizer" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/ai-language-conversations" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/ai-language-text" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/ai-text-analytics" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/arm-botservice" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/arm-cognitiveservices" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/arm-machinelearning" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/cognitiveservices-contentmoderator" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/cognitiveservices-customvision-prediction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/cognitiveservices-customvision-training" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/cognitiveservices-face" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@azure/cognitiveservices-translatortext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.microsoft-cognitiveservices-speech-sdk" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@azure/service-bus" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@azure/keyvault-secrets" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.@azure/keyvault-keys" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, From 9d41b1cefcbacb7d3fa4b253ce56ac0cf01257c0 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 24 Jun 2024 02:30:46 -0700 Subject: [PATCH 0067/2222] Support Textual Selections in notebook find widget (#216840) * support textual selections in notebook find widget * add textual selection decorations * union type * fix passed ranges, clear find scope text range decs * organize imports pass * remove auto find explorations --- .../browser/contrib/find/findFilters.ts | 43 ++--- .../browser/contrib/find/findModel.ts | 24 ++- .../contrib/find/media/notebookFind.css | 4 + .../browser/contrib/find/notebookFind.ts | 13 +- .../contrib/find/notebookFindReplaceWidget.ts | 158 +++++++++++------- .../contrib/find/notebookFindWidget.ts | 5 +- .../notebook/browser/notebook.contribution.ts | 5 - .../notebook/browser/notebookBrowser.ts | 4 +- .../notebook/browser/notebookEditorWidget.ts | 16 +- .../browser/viewModel/baseCellViewModel.ts | 17 +- .../browser/viewModel/codeCellViewModel.ts | 18 +- .../browser/viewModel/markupCellViewModel.ts | 4 +- .../viewModel/notebookViewModelImpl.ts | 19 +-- .../contrib/notebook/common/notebookCommon.ts | 33 ++-- .../test/browser/testNotebookEditor.ts | 4 +- .../contrib/search/browser/searchWidget.ts | 6 +- 16 files changed, 201 insertions(+), 172 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts index 0901d295edd..cf98120913c 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findFilters.ts @@ -3,16 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Event, Emitter } from 'vs/base/common/event'; -import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; +import { INotebookFindScope, NotebookFindScopeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export interface INotebookFindChangeEvent { markupInput?: boolean; markupPreview?: boolean; codeInput?: boolean; codeOutput?: boolean; - searchInRanges?: boolean; + findScope?: boolean; } export class NotebookFindFilters extends Disposable { @@ -70,31 +70,19 @@ export class NotebookFindFilters extends Disposable { } } - private _searchInRanges: boolean = false; + private _findScope: INotebookFindScope = { findScopeType: NotebookFindScopeType.None }; - get searchInRanges(): boolean { - return this._searchInRanges; + get findScope(): INotebookFindScope { + return this._findScope; } - set searchInRanges(value: boolean) { - if (this._searchInRanges !== value) { - this._searchInRanges = value; - this._onDidChange.fire({ searchInRanges: value }); + set findScope(value: INotebookFindScope) { + if (this._findScope !== value) { + this._findScope = value; + this._onDidChange.fire({ findScope: true }); } } - private _selectedRanges: ICellRange[] = []; - - get selectedRanges(): ICellRange[] { - return this._selectedRanges; - } - - set selectedRanges(value: ICellRange[]) { - if (this._selectedRanges !== value) { - this._selectedRanges = value; - this._onDidChange.fire({ searchInRanges: this._searchInRanges }); - } - } private readonly _initialMarkupInput: boolean; private readonly _initialMarkupPreview: boolean; @@ -106,8 +94,7 @@ export class NotebookFindFilters extends Disposable { markupPreview: boolean, codeInput: boolean, codeOutput: boolean, - searchInRanges: boolean, - selectedRanges: ICellRange[] + findScope: INotebookFindScope ) { super(); @@ -115,8 +102,7 @@ export class NotebookFindFilters extends Disposable { this._markupPreview = markupPreview; this._codeInput = codeInput; this._codeOutput = codeOutput; - this._searchInRanges = searchInRanges; - this._selectedRanges = selectedRanges; + this._findScope = findScope; this._initialMarkupInput = markupInput; this._initialMarkupPreview = markupPreview; @@ -125,7 +111,7 @@ export class NotebookFindFilters extends Disposable { } isModified(): boolean { - // do not include searchInRanges or selectedRanges in the check. This will incorrectly mark the filter icon as modified + // do not include findInSelection or either selectedRanges in the check. This will incorrectly mark the filter icon as modified return ( this._markupInput !== this._initialMarkupInput || this._markupPreview !== this._initialMarkupPreview @@ -139,7 +125,6 @@ export class NotebookFindFilters extends Disposable { this._markupPreview = v.markupPreview; this._codeInput = v.codeInput; this._codeOutput = v.codeOutput; - this._searchInRanges = v.searchInRanges; - this._selectedRanges = v.selectedRanges; + this._findScope = v.findScope; } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts index de522b344a5..0ca2d422f53 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/findModel.ts @@ -3,21 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; import { CancelablePromise, createCancelablePromise, Delayer } from 'vs/base/common/async'; -import { INotebookEditor, CellEditState, CellFindMatchWithIndex, CellWebviewFindMatch, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Range } from 'vs/editor/common/core/range'; import { FindMatch } from 'vs/editor/common/model'; import { PrefixSumComputer } from 'vs/editor/common/model/prefixSumComputer'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/browser/findState'; -import { CellKind, INotebookSearchOptions, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { findFirstIdxMonotonousOrArrLen } from 'vs/base/common/arraysFind'; -import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; import { FindMatchDecorationModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findMatchDecorationModel'; +import { CellEditState, CellFindMatchWithIndex, CellWebviewFindMatch, ICellViewModel, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl'; +import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; +import { CellKind, INotebookFindOptions, NotebookCellsChangeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export class CellFindMatchModel implements CellFindMatchWithIndex { readonly cell: ICellViewModel; @@ -115,7 +115,7 @@ export class FindModel extends Disposable { } private _updateCellStates(e: FindReplaceStateChangedEvent) { - if (!this._state.filters?.markupInput || !this._state.filters?.markupPreview || !this._state.filters?.searchInRanges || !this._state.filters?.selectedRanges) { + if (!this._state.filters?.markupInput || !this._state.filters?.markupPreview || !this._state.filters?.findScope) { return; } @@ -127,7 +127,7 @@ export class FindModel extends Disposable { } // search markup sources first to decide if a markup cell should be in editing mode const wordSeparators = this._configurationService.inspect('editor.wordSeparators').value; - const options: INotebookSearchOptions = { + const options: INotebookFindOptions = { regex: this._state.isRegex, wholeWord: this._state.wholeWord, caseSensitive: this._state.matchCase, @@ -136,8 +136,7 @@ export class FindModel extends Disposable { includeCodeInput: false, includeMarkupPreview: false, includeOutput: false, - searchInRanges: this._state.filters?.searchInRanges, - selectedRanges: this._state.filters?.selectedRanges + findScope: this._state.filters?.findScope, }; const contentMatches = viewModel.find(this._state.searchString, options); @@ -476,7 +475,7 @@ export class FindModel extends Disposable { const val = this._state.searchString; const wordSeparators = this._configurationService.inspect('editor.wordSeparators').value; - const options: INotebookSearchOptions = { + const options: INotebookFindOptions = { regex: this._state.isRegex, wholeWord: this._state.wholeWord, caseSensitive: this._state.matchCase, @@ -485,8 +484,7 @@ export class FindModel extends Disposable { includeCodeInput: this._state.filters?.codeInput ?? true, includeMarkupPreview: !!this._state.filters?.markupPreview, includeOutput: !!this._state.filters?.codeOutput, - searchInRanges: this._state.filters?.searchInRanges, - selectedRanges: this._state.filters?.selectedRanges + findScope: this._state.filters?.findScope, }; ret = await this._notebookEditor.find(val, options, token); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css b/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css index fe115e23081..d61cc797497 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/media/notebookFind.css @@ -19,3 +19,7 @@ padding: 0 2px; box-sizing: border-box; } + +.monaco-workbench .nb-findScope { + background-color: var(--vscode-editor-findRangeHighlightBackground); +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts index fd39a06a3f7..fb3d93d9d5e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFind.ts @@ -10,6 +10,7 @@ import { isEqual } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ITextModel } from 'vs/editor/common/model'; import { FindStartFocusAction, getSelectionSearchString, IFindStartOptions, StartFindAction, StartFindReplaceAction } from 'vs/editor/contrib/find/browser/findController'; @@ -19,13 +20,12 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IShowNotebookFindWidgetOptions, NotebookFindContrib } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget'; +import { INotebookCommandContext, NotebookMultiCellAction } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { registerNotebookContribution } from 'vs/workbench/contrib/notebook/browser/notebookEditorExtensions'; -import { CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellUri, NotebookFindScopeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INTERACTIVE_WINDOW_IS_ACTIVE_EDITOR, KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { INotebookCommandContext, NotebookMultiCellAction } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; registerNotebookContribution(NotebookFindContrib.id, NotebookFindContrib); @@ -78,12 +78,7 @@ registerAction2(class extends NotebookMultiCellAction { } const controller = editor.getContribution(NotebookFindContrib.id); - - if (context.selectedCells.length > 1) { - controller.show(undefined, { searchInRanges: true, selectedRanges: editor.getSelections() }); - } else { - controller.show(undefined, { searchInRanges: false, selectedRanges: [] }); - } + controller.show(undefined, { findScope: { findScopeType: NotebookFindScopeType.None } }); } }); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index c8cc9eb2187..8c4b59a688e 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -3,46 +3,48 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; +import 'vs/css!./notebookFindReplaceWidget'; +import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; +import { AnchorAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; +import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; import { FindInput, IFindInputOptions } from 'vs/base/browser/ui/findinput/findInput'; import { ReplaceInput } from 'vs/base/browser/ui/findinput/replaceInput'; import { IMessage as InputBoxMessage } from 'vs/base/browser/ui/inputbox/inputBox'; import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; +import { ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; +import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle'; import { Widget } from 'vs/base/browser/ui/widget'; +import { Action, ActionRunner, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; import { Delayer } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; import { KeyCode } from 'vs/base/common/keyCodes'; -import 'vs/css!./notebookFindReplaceWidget'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { isSafari } from 'vs/base/common/platform'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { Range } from 'vs/editor/common/core/range'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/browser/findState'; import { findNextMatchIcon, findPreviousMatchIcon, findReplaceAllIcon, findReplaceIcon, findSelectionIcon, SimpleButton } from 'vs/editor/contrib/find/browser/findWidget'; -import * as nls from 'vs/nls'; -import { ContextScopedReplaceInput, registerAndCreateHistoryNavigationContext } from 'vs/platform/history/browser/contextScopedHistoryWidget'; +import { parseReplaceString, ReplacePattern } from 'vs/editor/contrib/find/browser/replacePattern'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenu } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; +import { ContextScopedReplaceInput, registerAndCreateHistoryNavigationContext } from 'vs/platform/history/browser/contextScopedHistoryWidget'; +import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { defaultInputBoxStyles, defaultProgressBarStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; +import { asCssVariable, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; import { registerIcon, widgetClose } from 'vs/platform/theme/common/iconRegistry'; import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { parseReplaceString, ReplacePattern } from 'vs/editor/contrib/find/browser/replacePattern'; -import { Codicon } from 'vs/base/common/codicons'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { Action, ActionRunner, IAction, IActionRunner, Separator } from 'vs/base/common/actions'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IMenu } from 'vs/platform/actions/common/actions'; -import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { AnchorAlignment, IContextViewProvider } from 'vs/base/browser/ui/contextview/contextview'; -import { DropdownMenuActionViewItem } from 'vs/base/browser/ui/dropdown/dropdownActionViewItem'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { filterIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contrib/find/findFilters'; -import { isSafari } from 'vs/base/common/platform'; -import { ISashEvent, Orientation, Sash } from 'vs/base/browser/ui/sash/sash'; -import { INotebookDeltaDecoration, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { defaultInputBoxStyles, defaultProgressBarStyles, defaultToggleStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { IToggleStyles, Toggle } from 'vs/base/browser/ui/toggle/toggle'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { IActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; -import { IHoverService } from 'vs/platform/hover/browser/hover'; -import { asCssVariable, inputActiveOptionBackground, inputActiveOptionBorder, inputActiveOptionForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IShowNotebookFindWidgetOptions } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget'; +import { ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, INotebookDeltaDecoration, INotebookEditor } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookFindScopeType, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -318,8 +320,8 @@ export abstract class SimpleFindReplaceWidget extends Widget { private _filters: NotebookFindFilters; private readonly inSelectionToggle: Toggle; - private searchInSelectionEnabled: boolean; - private selectionDecorationIds: string[] = []; + private cellSelectionDecorationIds: string[] = []; + private textSelectionDecorationIds: ICellModelDecorations[] = []; constructor( @IContextViewService private readonly _contextViewService: IContextViewService, @@ -340,7 +342,7 @@ export abstract class SimpleFindReplaceWidget extends Widget { codeOutput: boolean; }>(NotebookSetting.findFilters) ?? { markupSource: true, markupPreview: true, codeSource: true, codeOutput: true }; - this._filters = new NotebookFindFilters(findFilters.markupSource, findFilters.markupPreview, findFilters.codeSource, findFilters.codeOutput, false, []); + this._filters = new NotebookFindFilters(findFilters.markupSource, findFilters.markupPreview, findFilters.codeSource, findFilters.codeOutput, { findScopeType: NotebookFindScopeType.None }); this._state.change({ filters: this._filters }, false); this._filters.onDidChange(() => { @@ -468,16 +470,48 @@ export abstract class SimpleFindReplaceWidget extends Widget { inputActiveOptionBorder: asCssVariable(inputActiveOptionBorder), inputActiveOptionForeground: asCssVariable(inputActiveOptionForeground), })); + this.inSelectionToggle.domNode.style.display = 'inline'; this.inSelectionToggle.onChange(() => { const checked = this.inSelectionToggle.checked; - this._filters.searchInRanges = checked; if (checked) { - this._filters.selectedRanges = this._notebookEditor.getSelections(); - this.setCellSelectionDecorations(); + // selection logic: + // 1. if there are multiple cells, do that. + // 2. if there is only one cell, do the following: + // - if there is a multi-line range highlighted, textual in selection + // - if there is no range, cell in selection for that cell + + const cellSelection: ICellRange[] = this._notebookEditor.getSelections(); + const textSelection: Range[] = this._notebookEditor.getSelectionViewModels()[0].getSelections(); + + if (cellSelection.length > 1 || cellSelection.some(range => range.end - range.start > 1)) { + this._filters.findScope = { + findScopeType: NotebookFindScopeType.Cells, + selectedCellRanges: cellSelection + }; + this.setCellSelectionDecorations(); + + } else if (textSelection.length > 1 || textSelection.some(range => range.endLineNumber - range.startLineNumber >= 1)) { + this._filters.findScope = { + findScopeType: NotebookFindScopeType.Text, + selectedCellRanges: cellSelection, + selectedTextRanges: textSelection + }; + this.setTextSelectionDecorations(textSelection, this._notebookEditor.getSelectionViewModels()[0]); + + } else { + this._filters.findScope = { + findScopeType: NotebookFindScopeType.Cells, + selectedCellRanges: cellSelection + }; + this.setCellSelectionDecorations(); + } } else { - this._filters.selectedRanges = []; + this._filters.findScope = { + findScopeType: NotebookFindScopeType.None + }; this.clearCellSelectionDecorations(); + this.clearTextSelectionDecorations(); } }); @@ -496,22 +530,6 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._innerFindDomNode.appendChild(this.inSelectionToggle.domNode); this._innerFindDomNode.appendChild(closeBtn.domNode); - this.searchInSelectionEnabled = this._configurationService.getValue(NotebookSetting.findScope); - this.inSelectionToggle.domNode.style.display = this.searchInSelectionEnabled ? 'inline' : 'none'; - - this._configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration(NotebookSetting.findScope)) { - this.searchInSelectionEnabled = this._configurationService.getValue(NotebookSetting.findScope); - if (this.searchInSelectionEnabled) { - this.inSelectionToggle.domNode.style.display = 'inline'; - } else { - this.inSelectionToggle.domNode.style.display = 'none'; - this.inSelectionToggle.checked = false; - this.clearCellSelectionDecorations(); - } - } - }); - // _domNode wraps _innerDomNode, ensuring that this._domNode.appendChild(this._innerFindDomNode); @@ -704,11 +722,37 @@ export abstract class SimpleFindReplaceWidget extends Widget { options: { className: 'nb-multiCellHighlight', outputClassName: 'nb-multiCellHighlight' } } satisfies INotebookDeltaDecoration); } - this.selectionDecorationIds = this._notebookEditor.deltaCellDecorations([], decorations); + this.cellSelectionDecorationIds = this._notebookEditor.deltaCellDecorations([], decorations); } private clearCellSelectionDecorations() { - this._notebookEditor.deltaCellDecorations(this.selectionDecorationIds, []); + this._notebookEditor.deltaCellDecorations(this.cellSelectionDecorationIds, []); + } + + private setTextSelectionDecorations(textRanges: Range[], cell: ICellViewModel) { + this._notebookEditor.changeModelDecorations(changeAccessor => { + const decorations: ICellModelDeltaDecorations[] = []; + for (const range of textRanges) { + decorations.push({ + ownerId: cell.handle, + decorations: [{ + range: range, + options: { + description: 'text search range for notebook search scope', + isWholeLine: true, + className: 'nb-findScope' + } + }] + }); + } + this.textSelectionDecorationIds = changeAccessor.deltaDecorations([], decorations); + }); + } + + private clearTextSelectionDecorations() { + this._notebookEditor.changeModelDecorations(changeAccessor => { + changeAccessor.deltaDecorations(this.textSelectionDecorationIds, []); + }); } protected _updateMatchesCount(): void { @@ -748,20 +792,11 @@ export abstract class SimpleFindReplaceWidget extends Widget { this._findInput.focus(); } - public show(initialInput?: string, options?: { focus?: boolean; searchInRanges?: boolean; selectedRanges?: ICellRange[] }): void { + public show(initialInput?: string, options?: IShowNotebookFindWidgetOptions): void { if (initialInput) { this._findInput.setValue(initialInput); } - if (this.searchInSelectionEnabled && options?.searchInRanges !== undefined) { - this._filters.searchInRanges = options.searchInRanges; - this.inSelectionToggle.checked = options.searchInRanges; - if (options.searchInRanges && options.selectedRanges) { - this._filters.selectedRanges = options.selectedRanges; - this.setCellSelectionDecorations(); - } - } - this._isVisible = true; setTimeout(() => { @@ -810,7 +845,10 @@ export abstract class SimpleFindReplaceWidget extends Widget { public hide(): void { if (this._isVisible) { this.inSelectionToggle.checked = false; - this._notebookEditor.deltaCellDecorations(this.selectionDecorationIds, []); + this._notebookEditor.deltaCellDecorations(this.cellSelectionDecorationIds, []); + this._notebookEditor.changeModelDecorations(changeAccessor => { + changeAccessor.deltaDecorations(this.textSelectionDecorationIds, []); + }); this._domNode.classList.remove('visible-transition'); this._domNode.setAttribute('aria-hidden', 'true'); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts index 5e67785a00b..15954bc2b81 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindWidget.ts @@ -25,8 +25,8 @@ import { NotebookFindFilters } from 'vs/workbench/contrib/notebook/browser/contr import { FindModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findModel'; import { SimpleFindReplaceWidget } from 'vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget'; import { CellEditState, ICellViewModel, INotebookEditor, INotebookEditorContribution } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { INotebookFindScope } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { KEYBINDING_CONTEXT_NOTEBOOK_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; -import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; const FIND_HIDE_TRANSITION = 'find-hide-transition'; const FIND_SHOW_TRANSITION = 'find-show-transition'; @@ -40,8 +40,7 @@ export interface IShowNotebookFindWidgetOptions { matchIndex?: number; focus?: boolean; searchStringSeededFrom?: { cell: ICellViewModel; range: Range }; - searchInRanges?: boolean; - selectedRanges?: ICellRange[]; + findScope?: INotebookFindScope; } export class NotebookFindContrib extends Disposable implements INotebookEditorContribution { diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index 423c6165f73..4e174318c11 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -1055,11 +1055,6 @@ configurationRegistry.registerConfiguration({ }, tags: ['notebookLayout'] }, - [NotebookSetting.findScope]: { - markdownDescription: nls.localize('notebook.experimental.find.scope.enabled', "Enables the user to search within a selection of cells in the notebook. When enabled, the user will see a \"Find in Cell Selection\" icon in the notebook find widget."), - type: 'boolean', - default: false, - }, [NotebookSetting.remoteSaving]: { markdownDescription: nls.localize('notebook.remoteSaving', "Enables the incremental saving of notebooks between processes and across Remote connections. When enabled, only the changes to the notebook are sent to the extension host, improving performance for large notebooks and slow network connections."), type: 'boolean', diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index dce4811e235..18e44f2a663 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -22,7 +22,7 @@ import { IEditorPane, IEditorPaneWithSelection } from 'vs/workbench/common/edito import { CellViewModelStateChangeEvent, NotebookCellStateChangedEvent, NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookSearchOptions, IOrderedMimeType, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID, REPL_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, ICellOutput, INotebookCellStatusBarItem, INotebookRendererInfo, INotebookFindOptions, IOrderedMimeType, NotebookCellInternalMetadata, NotebookCellMetadata, NOTEBOOK_EDITOR_ID, REPL_EDITOR_ID } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { isCompositeNotebookEditorInput } from 'vs/workbench/contrib/notebook/common/notebookEditorInput'; import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; @@ -738,7 +738,7 @@ export interface INotebookEditor { getCellIndex(cell: ICellViewModel): number | undefined; getNextVisibleCellIndex(index: number): number | undefined; getPreviousVisibleCellIndex(index: number): number | undefined; - find(query: string, options: INotebookSearchOptions, token: CancellationToken, skipWarmup?: boolean, shouldGetSearchPreviewInfo?: boolean, ownerID?: string): Promise; + find(query: string, options: INotebookFindOptions, token: CancellationToken, skipWarmup?: boolean, shouldGetSearchPreviewInfo?: boolean, ownerID?: string): Promise; findHighlightCurrent(matchIndex: number, ownerID?: string): Promise; findUnHighlightCurrent(matchIndex: number, ownerID?: string): Promise; findStop(ownerID?: string): void; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index ea03755d75d..1e59c4cc138 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -74,7 +74,7 @@ import { NotebookEditorContextKeys } from 'vs/workbench/contrib/notebook/browser import { NotebookOverviewRuler } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookOverviewRuler'; import { ListTopCellToolbar } from 'vs/workbench/contrib/notebook/browser/viewParts/notebookTopCellToolbar'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellEditType, CellKind, INotebookSearchOptions, RENDERER_NOT_AVAILABLE, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellEditType, CellKind, INotebookFindOptions, NotebookFindScopeType, RENDERER_NOT_AVAILABLE, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { NOTEBOOK_CURSOR_NAVIGATION_MODE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_OUTPUT_INPUT_FOCUSED } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionService } from 'vs/workbench/contrib/notebook/common/notebookExecutionService'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; @@ -2567,7 +2567,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return Promise.all(requests); } - async find(query: string, options: INotebookSearchOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false, ownerID?: string): Promise { + async find(query: string, options: INotebookFindOptions, token: CancellationToken, skipWarmup: boolean = false, shouldGetSearchPreviewInfo = false, ownerID?: string): Promise { if (!this._notebookViewModel) { return []; } @@ -2578,7 +2578,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD const findMatches = this._notebookViewModel.find(query, options).filter(match => match.length > 0); - if (!options.includeMarkupPreview && !options.includeOutput) { + if ((!options.includeMarkupPreview && !options.includeOutput) || options.findScope?.findScopeType === NotebookFindScopeType.Text) { this._webview?.findStop(ownerID); return findMatches; } @@ -2602,11 +2602,13 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return []; } - const selectedRanges = options.selectedRanges?.map(range => this._notebookViewModel?.validateRange(range)).filter(range => !!range); - const selectedIndexes = cellRangesToIndexes(selectedRanges ?? []); - const findIds: string[] = selectedIndexes.map(index => this._notebookViewModel?.viewCells[index].id ?? ''); + let findIds: string[] = []; + if (options.findScope && options.findScope.findScopeType === NotebookFindScopeType.Cells && options.findScope.selectedCellRanges) { + const selectedIndexes = cellRangesToIndexes(options.findScope.selectedCellRanges); + findIds = selectedIndexes.map(index => this._notebookViewModel?.viewCells[index].id ?? ''); + } - const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID, findIds: options.searchInRanges ? findIds : [] }); + const webviewMatches = await this._webview.find(query, { caseSensitive: options.caseSensitive, wholeWord: options.wholeWord, includeMarkup: !!options.includeMarkupPreview, includeOutput: !!options.includeOutput, shouldGetSearchPreviewInfo, ownerID, findIds: findIds }); if (token.isCancellationRequested) { return []; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index f1c1ab7b7c8..b0751fc5b96 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -9,7 +9,7 @@ import { Mimes } from 'vs/base/common/mime'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IPosition } from 'vs/editor/common/core/position'; -import { Range } from 'vs/editor/common/core/range'; +import { IRange, Range } from 'vs/editor/common/core/range'; import { Selection } from 'vs/editor/common/core/selection'; import * as editorCommon from 'vs/editor/common/editorCommon'; import * as model from 'vs/editor/common/model'; @@ -24,7 +24,7 @@ import { NotebookOptionsChangeEvent } from 'vs/workbench/contrib/notebook/browse import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, INotebookCellStatusBarItem, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, INotebookCellStatusBarItem, INotebookFindOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; export abstract class BaseCellViewModel extends Disposable { @@ -650,20 +650,21 @@ export abstract class BaseCellViewModel extends Disposable { protected abstract onDidChangeTextModelContent(): void; - protected cellStartFind(value: string, options: INotebookSearchOptions): model.FindMatch[] | null { + protected cellStartFind(value: string, options: INotebookFindOptions): model.FindMatch[] | null { let cellMatches: model.FindMatch[] = []; + const lineCount = this.textBuffer.getLineCount(); + const findRange: IRange[] = options.findScope?.selectedTextRanges ?? [new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1)]; + if (this.assertTextModelAttached()) { cellMatches = this.textModel!.findMatches( value, - false, + findRange, options.regex || false, options.caseSensitive || false, options.wholeWord ? options.wordSeparators || null : null, options.regex || false); } else { - const lineCount = this.textBuffer.getLineCount(); - const fullRange = new Range(1, 1, lineCount, this.textBuffer.getLineLength(lineCount) + 1); const searchParams = new SearchParams(value, options.regex || false, options.caseSensitive || false, options.wholeWord ? options.wordSeparators || null : null,); const searchData = searchParams.parseSearchRequest(); @@ -671,7 +672,9 @@ export abstract class BaseCellViewModel extends Disposable { return null; } - cellMatches = this.textBuffer.findMatchesLineByLine(fullRange, searchData, options.regex || false, 1000); + findRange.forEach(range => { + cellMatches.push(...this.textBuffer.findMatchesLineByLine(new Range(range.startLineNumber, range.startColumn, range.endLineNumber, range.endColumn), searchData, options.regex || false, 1000)); + }); } return cellMatches; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 3f5a6c3e68d..06c8c851f3b 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -5,25 +5,25 @@ import { Emitter, Event, PauseableEmitter } from 'vs/base/common/event'; import { dispose } from 'vs/base/common/lifecycle'; +import { observableValue } from 'vs/base/common/observable'; import * as UUID from 'vs/base/common/uuid'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import * as editorCommon from 'vs/editor/common/editorCommon'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { PrefixSumComputer } from 'vs/editor/common/model/prefixSumComputer'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { CellEditState, CellFindMatch, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, CellLayoutState, ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFindMatch, CellLayoutState, CodeCellLayoutChangeEvent, CodeCellLayoutInfo, ICellOutputViewModel, ICellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookOptionsChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; +import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { CellOutputViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, INotebookSearchOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NotebookOptionsChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; +import { CellKind, INotebookFindOptions, NotebookCellOutputsSplice } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { ICellExecutionError, ICellExecutionStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { BaseCellViewModel } from './baseCellViewModel'; -import { NotebookLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; -import { ICellExecutionError, ICellExecutionStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { observableValue } from 'vs/base/common/observable'; export const outputDisplayLimit = 500; @@ -518,7 +518,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod private readonly _hasFindResult = this._register(new Emitter()); public readonly hasFindResult: Event = this._hasFindResult.event; - startFind(value: string, options: INotebookSearchOptions): CellFindMatch | null { + startFind(value: string, options: INotebookFindOptions): CellFindMatch | null { const matches = super.cellStartFind(value, options); if (matches === null) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts index 0f255cc20db..ee1b0d37d9f 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts @@ -10,7 +10,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { CellEditState, CellFindMatch, CellFoldingState, CellLayoutContext, CellLayoutState, EditorFoldingStateDelegate, ICellOutputViewModel, ICellViewModel, MarkupCellLayoutChangeEvent, MarkupCellLayoutInfo } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { BaseCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; -import { CellKind, INotebookSearchOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, INotebookFindOptions } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; @@ -295,7 +295,7 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM private readonly _hasFindResult = this._register(new Emitter()); public readonly hasFindResult: Event = this._hasFindResult.event; - startFind(value: string, options: INotebookSearchOptions): CellFindMatch | null { + startFind(value: string, options: INotebookFindOptions): CellFindMatch | null { const matches = super.cellStartFind(value, options); if (matches === null) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts index 7ea020f145c..60a3626484f 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/notebookViewModelImpl.ts @@ -13,27 +13,27 @@ import { URI } from 'vs/base/common/uri'; import { IBulkEditService, ResourceTextEdit } from 'vs/editor/browser/services/bulkEditService'; import { Range } from 'vs/editor/common/core/range'; import * as editorCommon from 'vs/editor/common/editorCommon'; +import { IWorkspaceTextEdit } from 'vs/editor/common/languages'; import { FindMatch, IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness } from 'vs/editor/common/model'; import { MultiModelEditStackElement, SingleModelEditStackElement } from 'vs/editor/common/model/editStack'; import { IntervalNode, IntervalTree } from 'vs/editor/common/model/intervalTree'; import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; -import { IWorkspaceTextEdit } from 'vs/editor/common/languages'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { FoldingRegions } from 'vs/editor/contrib/folding/browser/foldingRanges'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; -import { CellEditState, CellFindMatchWithIndex, CellFoldingState, EditorFoldingStateDelegate, ICellViewModel, INotebookDeltaCellStatusBarItems, INotebookDeltaDecoration, ICellModelDecorations, ICellModelDeltaDecorations, IModelDecorationsChangeAccessor, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellFindMatchModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findModel'; +import { CellEditState, CellFindMatchWithIndex, CellFoldingState, EditorFoldingStateDelegate, ICellModelDecorations, ICellModelDeltaDecorations, ICellViewModel, IModelDecorationsChangeAccessor, INotebookDeltaCellStatusBarItems, INotebookDeltaDecoration, INotebookEditorViewState, INotebookViewCellsUpdateEvent, INotebookViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { NotebookLayoutInfo, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { NotebookCellSelectionCollection } from 'vs/workbench/contrib/notebook/browser/viewModel/cellSelectionCollection'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { MarkupCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { CellKind, ICell, INotebookSearchOptions, ISelectionState, NotebookCellsChangeType, NotebookCellTextModelSplice, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { cellIndexesToRanges, cellRangesToIndexes, ICellRange, reduceCellRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { NotebookLayoutInfo, NotebookMetadataChangedEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; -import { CellFindMatchModel } from 'vs/workbench/contrib/notebook/browser/contrib/find/findModel'; +import { CellKind, ICell, INotebookFindOptions, ISelectionState, NotebookCellsChangeType, NotebookCellTextModelSplice, NotebookFindScopeType, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService, NotebookExecutionType } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; +import { cellIndexesToRanges, cellRangesToIndexes, ICellRange, reduceCellRanges } from 'vs/workbench/contrib/notebook/common/notebookRange'; const invalidFunc = () => { throw new Error(`Invalid change accessor`); }; @@ -910,13 +910,12 @@ export class NotebookViewModel extends Disposable implements EditorFoldingStateD } //#region Find - find(value: string, options: INotebookSearchOptions): CellFindMatchWithIndex[] { + find(value: string, options: INotebookFindOptions): CellFindMatchWithIndex[] { const matches: CellFindMatchWithIndex[] = []; let findCells: CellViewModel[] = []; - const selectedRanges = options.selectedRanges?.map(range => this.validateRange(range)).filter(range => !!range); - - if (options.searchInRanges && selectedRanges) { + if (options.findScope && (options.findScope.findScopeType === NotebookFindScopeType.Cells || options.findScope.findScopeType === NotebookFindScopeType.Text)) { + const selectedRanges = options.findScope.selectedCellRanges?.map(range => this.validateRange(range)).filter(range => !!range) ?? []; const selectedIndexes = cellRangesToIndexes(selectedRanges); findCells = selectedIndexes.map(index => this._viewCells[index]); } else { diff --git a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts index bb08370538b..aea2596da48 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookCommon.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookCommon.ts @@ -8,33 +8,34 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { IDiffResult } from 'vs/base/common/diff/diff'; import { Event } from 'vs/base/common/event'; import * as glob from 'vs/base/common/glob'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Iterable } from 'vs/base/common/iterator'; +import { IDisposable } from 'vs/base/common/lifecycle'; import { Mimes } from 'vs/base/common/mime'; import { Schemas } from 'vs/base/common/network'; import { basename } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { ISplice } from 'vs/base/common/sequence'; +import { ThemeColor } from 'vs/base/common/themables'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; import { ILineChange } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import * as editorCommon from 'vs/editor/common/editorCommon'; import { Command, WorkspaceEditMetadata } from 'vs/editor/common/languages'; import { IReadonlyTextBuffer } from 'vs/editor/common/model'; import { IAccessibilityInformation } from 'vs/platform/accessibility/common/accessibility'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IDisposable } from 'vs/base/common/lifecycle'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { ThemeColor } from 'vs/base/common/themables'; +import { IFileReadLimits } from 'vs/platform/files/common/files'; import { UndoRedoGroup } from 'vs/platform/undoRedo/common/undoRedo'; import { IRevertOptions, ISaveOptions, IUntypedEditorInput } from 'vs/workbench/common/editor'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; -import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; -import { IWorkingCopyBackupMeta, IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; -import { IFileReadLimits } from 'vs/platform/files/common/files'; -import { parse as parseUri, generate as generateUri } from 'vs/workbench/services/notebook/common/notebookDocumentService'; import { ICellExecutionError } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookTextModelLike } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; +import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; +import { generate as generateUri, parse as parseUri } from 'vs/workbench/services/notebook/common/notebookDocumentService'; +import { IWorkingCopyBackupMeta, IWorkingCopySaveEvent } from 'vs/workbench/services/workingCopy/common/workingCopy'; export const NOTEBOOK_EDITOR_ID = 'workbench.editor.notebook'; export const NOTEBOOK_DIFF_EDITOR_ID = 'workbench.editor.notebookTextDiffEditor'; @@ -828,7 +829,7 @@ export enum NotebookEditorPriority { option = 'option', } -export interface INotebookSearchOptions { +export interface INotebookFindOptions { regex?: boolean; wholeWord?: boolean; caseSensitive?: boolean; @@ -837,8 +838,19 @@ export interface INotebookSearchOptions { includeMarkupPreview?: boolean; includeCodeInput?: boolean; includeOutput?: boolean; - searchInRanges?: boolean; - selectedRanges?: ICellRange[]; + findScope?: INotebookFindScope; +} + +export interface INotebookFindScope { + findScopeType: NotebookFindScopeType; + selectedCellRanges?: ICellRange[]; + selectedTextRanges?: Range[]; +} + +export enum NotebookFindScopeType { + Cells = 'cells', + Text = 'text', + None = 'none' } export interface INotebookExclusiveDocumentFilter { @@ -964,7 +976,6 @@ export const NotebookSetting = { outputFontFamilyDeprecated: 'notebook.outputFontFamily', outputFontFamily: 'notebook.output.fontFamily', findFilters: 'notebook.find.filters', - findScope: 'notebook.experimental.find.scope.enabled', logging: 'notebook.logging', confirmDeleteRunningCell: 'notebook.confirmDeleteRunningCell', remoteSaving: 'notebook.experimental.remoteSave', diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index ef524745b9d..830ac41e6a8 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -52,7 +52,7 @@ import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/vie import { NotebookCellTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookCellTextModel'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { INotebookCellStatusBarService } from 'vs/workbench/contrib/notebook/common/notebookCellStatusBarService'; -import { CellKind, CellUri, ICellDto2, INotebookDiffEditorModel, INotebookEditorModel, INotebookSearchOptions, IOutputDto, IResolvedNotebookEditorModel, NotebookCellExecutionState, NotebookCellMetadata, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri, ICellDto2, INotebookDiffEditorModel, INotebookEditorModel, INotebookFindOptions, IOutputDto, IResolvedNotebookEditorModel, NotebookCellExecutionState, NotebookCellMetadata, SelectionStateType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellExecuteUpdate, ICellExecutionComplete, ICellExecutionStateChangedEvent, IExecutionStateChangedEvent, INotebookCellExecution, INotebookExecution, INotebookExecutionStateService, INotebookFailStateChangedEvent } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { NotebookOptions } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -311,7 +311,7 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic override get onDidChangeSelection() { return viewModel.onDidChangeSelection as Event; } override get onDidChangeOptions() { return viewModel.onDidChangeOptions; } override get onDidChangeViewCells() { return viewModel.onDidChangeViewCells; } - override async find(query: string, options: INotebookSearchOptions): Promise { + override async find(query: string, options: INotebookFindOptions): Promise { const findMatches = viewModel.find(query, options).filter(match => match.length > 0); return findMatches; } diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 905ea80a71c..f8beab04b54 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as nls from 'vs/nls'; import * as dom from 'vs/base/browser/dom'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -16,7 +17,6 @@ import { Delayer } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { CONTEXT_FIND_WIDGET_NOT_VISIBLE } from 'vs/editor/contrib/find/browser/findModel'; -import * as nls from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -44,6 +44,7 @@ import { GroupModelChangeKind } from 'vs/workbench/common/editor'; import { SearchFindInput } from 'vs/workbench/contrib/search/browser/searchFindInput'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { IDisposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { NotebookFindScopeType } from 'vs/workbench/contrib/notebook/common/notebookCommon'; /** Specified in searchview.css */ const SingleLineInputHeight = 26; @@ -205,8 +206,7 @@ export class SearchWidget extends Widget { notebookOptions.isInNotebookMarkdownPreview, notebookOptions.isInNotebookCellInput, notebookOptions.isInNotebookCellOutput, - false, - [] + { findScopeType: NotebookFindScopeType.None } )); this._register( From e2c6c2b9e83cff13eab63e7e7ec9c7ab1185e45b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:21:00 +0200 Subject: [PATCH 0068/2222] SCM - avoid opening multiple multi-file diff editors for the same history item (#216979) --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 6d579939f68..988cbda1404 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -3229,7 +3229,13 @@ export class SCMViewPane extends ViewPane { const historyItemChanges = await historyProvider?.provideHistoryItemChanges(historyItem.id, historyItemParentId); if (historyItemChanges) { const title = `${historyItem.id.substring(0, 8)} - ${historyItem.message}`; - await this.commandService.executeCommand('_workbench.openMultiDiffEditor', { title, resources: historyItemChanges }); + + const rootUri = e.element.repository.provider.rootUri; + const multiDiffSourceUri = rootUri ? + rootUri.with({ scheme: 'scm-history-item', path: `${rootUri.path}/${historyItem.id}` }) : + { scheme: 'scm-history-item', path: `${e.element.repository.provider.label}/${historyItem.id}` }; + + await this.commandService.executeCommand('_workbench.openMultiDiffEditor', { title, multiDiffSourceUri, resources: historyItemChanges }); } this.scmViewService.focus(e.element.repository); From 6d903b443cf644e8823b5f850fd34be35543f387 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 24 Jun 2024 12:59:30 +0200 Subject: [PATCH 0069/2222] Respect command enablement in tree view message buttons (#216995) --- .../workbench/browser/parts/views/treeView.ts | 36 +++++++++++++++---- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 155c5d6728b..42a54190f0b 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -36,7 +36,7 @@ import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/plat import { Action2, IMenuService, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, ContextKeyExpression, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { FileKind } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -75,6 +75,7 @@ import { parseLinkedText } from 'vs/base/common/linkedText'; import { Button } from 'vs/base/browser/ui/button/button'; import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; import { IAccessibleViewInformationService } from 'vs/workbench/services/accessibility/common/accessibleViewInformationService'; +import { Command } from 'vs/editor/common/languages'; export class TreeViewPane extends ViewPane { @@ -175,15 +176,22 @@ class Root implements ITreeItem { children: ITreeItem[] | undefined = undefined; } -function isTreeCommandEnabled(treeCommand: TreeCommand, contextKeyService: IContextKeyService): boolean { - const command = CommandsRegistry.getCommand(treeCommand.originalId ? treeCommand.originalId : treeCommand.id); +function commandPreconditions(commandId: string): ContextKeyExpression | undefined { + const command = CommandsRegistry.getCommand(commandId); if (command) { const commandAction = MenuRegistry.getCommand(command.id); - const precondition = commandAction && commandAction.precondition; - if (precondition) { - return contextKeyService.contextMatchesRules(precondition); - } + return commandAction && commandAction.precondition; } + return undefined; +} + +function isTreeCommandEnabled(treeCommand: TreeCommand | Command, contextKeyService: IContextKeyService): boolean { + const commandId: string = (treeCommand as TreeCommand).originalId ? (treeCommand as TreeCommand).originalId! : treeCommand.id; + const precondition = commandPreconditions(commandId); + if (precondition) { + return contextKeyService.contextMatchesRules(precondition); + } + return true; } @@ -878,6 +886,20 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { button.onDidClick(_ => { this.openerService.open(node.href, { allowCommands: true }); }, null, disposables); + + const href = URI.parse(node.href); + if (href.scheme === Schemas.command) { + const preConditions = commandPreconditions(href.path); + if (preConditions) { + button.enabled = this.contextKeyService.contextMatchesRules(preConditions); + disposables.add(this.contextKeyService.onDidChangeContext(e => { + if (e.affectsSome(new Set(preConditions.keys()))) { + button.enabled = this.contextKeyService.contextMatchesRules(preConditions); + } + })); + } + } + disposables.add(button); hasFoundButton = true; result.push(buttonContainer); From 53c0b5ac530b55356d3487612fce4abe9db0b1b1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Jun 2024 13:06:53 +0200 Subject: [PATCH 0070/2222] Joh/mental-koi (#217004) * hightlight first button only when inline chat has a prompt * rename "Cancel Request" to "Stop Request" * mark `inlineChat.experimental.textButtons` as experimental * show all "input" command when not using text btns * When cancelling, undo untill before the request --- .../chatTextEditContentPart.ts | 6 ++- .../browser/inlineChat.contribution.ts | 16 +++---- .../browser/inlineChatContentWidget.ts | 10 +++-- .../browser/inlineChatController.ts | 41 +++++++---------- .../inlineChat/browser/inlineChatSession.ts | 34 ++++++++++---- .../browser/inlineChatStrategies.ts | 8 ++-- .../inlineChat/browser/media/inlineChat.css | 14 +++--- .../contrib/inlineChat/common/inlineChat.ts | 3 +- .../test/browser/inlineChatController.test.ts | 45 ++++++++++++++++++- 9 files changed, 119 insertions(+), 58 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTextEditContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTextEditContentPart.ts index 5232437f966..5f4e6556c5c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTextEditContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTextEditContentPart.ts @@ -55,7 +55,11 @@ export class ChatTextEditContentPart extends Disposable implements IChatContentP // TODO@jrieken move this into the CompareCodeBlock and properly say what kind of changes happen if (rendererOptions.renderTextEditsAsSummary?.(chatTextEdit.uri)) { if (isResponseVM(element) && element.response.value.every(item => item.kind === 'textEditGroup')) { - this.domNode = $('.interactive-edits-summary', undefined, !element.isComplete ? localize('editsSummary1', "Making changes...") : localize('editsSummary', "Made changes.")); + this.domNode = $('.interactive-edits-summary', undefined, !element.isComplete + ? localize('editsSummary1', "Making changes...") + : element.isCanceled + ? localize('edits0', "Making changes was aborted.") + : localize('editsSummary', "Made changes.")); } else { this.domNode = $('div'); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index d5d84180b57..21f02771f96 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -58,8 +58,8 @@ const cancelActionMenuItem: IMenuItem = { order: 0, command: { id: CancelAction.ID, - title: localize('cancel', "Cancel Request"), - shortTitle: localize('cancelShort', "Cancel"), + title: localize('cancel', "Stop Request"), + shortTitle: localize('cancelShort', "Stop"), }, when: ContextKeyExpr.and( CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, @@ -111,14 +111,12 @@ class MenuCopier implements IDisposable { const store = new DisposableStore(); function updateMenu() { - if (configService.getValue(InlineChatConfigKeys.ExpTextButtons)) { - store.clear(); - for (const item of MenuRegistry.getMenuItems(MenuId.ChatExecute)) { - if (isIMenuItem(item) && (item.command.id === SubmitAction.ID || item.command.id === CancelAction.ID)) { - continue; - } - store.add(MenuRegistry.appendMenuItem(MENU_INLINE_CHAT_EXECUTE, item)); + store.clear(); + for (const item of MenuRegistry.getMenuItems(MenuId.ChatExecute)) { + if (configService.getValue(InlineChatConfigKeys.ExpTextButtons) && isIMenuItem(item) && (item.command.id === SubmitAction.ID || item.command.id === CancelAction.ID)) { + continue; } + store.add(MenuRegistry.appendMenuItem(MENU_INLINE_CHAT_EXECUTE, item)); } } updateMenu(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts index a10f96a8be0..e22cab4975c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts @@ -107,18 +107,20 @@ export class InlineChatContentWidget implements IContentWidget { } this._domNode.appendChild(this._toolbarContainer); - this._store.add(scopedInstaService.createInstance(MenuWorkbenchToolBar, this._toolbarContainer, MENU_INLINE_CHAT_CONTENT_STATUS, { + const toolbar = this._store.add(scopedInstaService.createInstance(MenuWorkbenchToolBar, this._toolbarContainer, MENU_INLINE_CHAT_CONTENT_STATUS, { actionViewItemProvider: action => action instanceof MenuItemAction ? instaService.createInstance(TextOnlyMenuEntryActionViewItem, action, { conversational: true }) : undefined, toolbarOptions: { primaryGroup: '0_main' }, icon: false, label: true, })); + this._store.add(toolbar.onDidChangeMenuItems(() => { + this._domNode.classList.toggle('contents', toolbar.getItemsLength() > 1); + })); + const tracker = dom.trackFocus(this._domNode); this._store.add(tracker.onDidBlur(() => { - if (this._visible && this._widget.inputEditor.getModel()?.getValueLength() === 0 - // && !"ON" - ) { + if (this._visible && this._widget.inputEditor.getModel()?.getValueLength() === 0) { this._onDidBlur.fire(); } })); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 2bc8215c4de..8fdf2669f59 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -428,21 +428,7 @@ export class InlineChatController implements IEditorContribution { // TODO@jrieken there is still some work left for when a request "in the middle" // is removed. We will undo all changes till that point but not remove those // later request - const exchange = this._session!.exchanges.find(candidate => candidate.prompt.request.id === e.requestId); - if (exchange && this._editor.hasModel()) { - // undo till this point - this._session!.hunkData.ignoreTextModelNChanges = true; - try { - - const model = this._editor.getModel(); - const targetAltVersion = exchange.prompt.modelAltVersionId; - while (targetAltVersion < model.getAlternativeVersionId() && model.canUndo()) { - await model.undo(); - } - } finally { - this._session!.hunkData.ignoreTextModelNChanges = false; - } - } + await this._session!.undoChangesUntil(e.requestId); } })); @@ -734,6 +720,12 @@ export class InlineChatController implements IEditorContribution { await responsePromise.p; await progressiveEditsQueue.whenIdle(); + + if (response.isCanceled) { + // + await this._session.undoChangesUntil(response.requestId); + } + store.dispose(); const diff = await this._editorWorkerService.computeDiff(this._session.textModel0.uri, this._session.textModelN.uri, { computeMoves: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, ignoreTrimWhitespace: false }, 'advanced'); @@ -771,7 +763,7 @@ export class InlineChatController implements IEditorContribution { // real response -> complex... this._ui.value.zone.widget.updateStatus(''); - const position = await this._strategy.renderChanges(response); + const position = await this._strategy.renderChanges(); if (position) { // if the selection doesn't start far off we keep the widget at its current position // because it makes reading this nicer @@ -827,7 +819,7 @@ export class InlineChatController implements IEditorContribution { this._sessionStore.clear(); // only stash sessions that were not unstashed, not "empty", and not interacted with - const shouldStash = !this._session.isUnstashed && !!this._session.lastExchange && this._session.hunkData.size === this._session.hunkData.pending; + const shouldStash = !this._session.isUnstashed && this._session.chatModel.hasRequests && this._session.hunkData.size === this._session.hunkData.pending; let undoCancelEdits: IValidEditOperation[] = []; try { undoCancelEdits = this._strategy.cancel(); @@ -874,7 +866,7 @@ export class InlineChatController implements IEditorContribution { widgetPosition = this._editor.getSelection().getStartPosition().delta(-1); } - if (this._session && !position && (this._session.hasChangedText || this._session.lastExchange)) { + if (this._session && !position && (this._session.hasChangedText || this._session.chatModel.hasRequests)) { widgetPosition = this._session.wholeRange.value.getStartPosition().delta(-1); } @@ -1063,10 +1055,10 @@ export class InlineChatController implements IEditorContribution { } acceptSession(): void { - if (this._session?.lastExchange?.response instanceof ReplyResponse && this._session?.lastExchange?.response.chatResponse) { - const response = this._session?.lastExchange?.response.chatResponse; + const response = this._session?.chatModel.getRequests().at(-1)?.response; + if (response) { this._chatService.notifyUserAction({ - sessionId: this._session.chatModel.sessionId, + sessionId: response.session.sessionId, requestId: response.requestId, agentId: response.agent?.id, result: response.result, @@ -1088,11 +1080,10 @@ export class InlineChatController implements IEditorContribution { } async cancelSession() { - - if (this._session?.lastExchange?.response instanceof ReplyResponse && this._session?.lastExchange?.response.chatResponse) { - const response = this._session?.lastExchange?.response.chatResponse; + const response = this._session?.chatModel.getRequests().at(-1)?.response; + if (response) { this._chatService.notifyUserAction({ - sessionId: this._session.chatModel.sessionId, + sessionId: response.session.sessionId, requestId: response.requestId, agentId: response.agent?.id, result: response.result, diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index 1ac39ac4b40..4d18fe88fce 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -130,7 +130,7 @@ export class Session { private _lastInput: SessionPrompt | undefined; private _isUnstashed: boolean = false; - private readonly _exchange: SessionExchange[] = []; + private readonly _exchanges: SessionExchange[] = []; private readonly _startTime = new Date(); private readonly _teldata: TelemetryData; @@ -147,7 +147,7 @@ export class Session { */ readonly textModel0: ITextModel, /** - * The document into which AI edits went, when live this is `targetUri` otherwise it is a temporary document + * The model of the editor */ readonly textModelN: ITextModel, readonly agent: IChatAgent, @@ -191,17 +191,35 @@ export class Session { addExchange(exchange: SessionExchange): void { this._isUnstashed = false; - const newLen = this._exchange.push(exchange); + const newLen = this._exchanges.push(exchange); this._teldata.rounds += `${newLen}|`; // this._teldata.responseTypes += `${exchange.response instanceof ReplyResponse ? exchange.response.responseType : InlineChatResponseTypes.Empty}|`; } - get exchanges(): readonly SessionExchange[] { - return this._exchange; + get lastExchange(): SessionExchange | undefined { + return this._exchanges[this._exchanges.length - 1]; } - get lastExchange(): SessionExchange | undefined { - return this._exchange[this._exchange.length - 1]; + async undoChangesUntil(requestId: string): Promise { + const idx = this._exchanges.findIndex(candidate => candidate.prompt.request.id === requestId); + if (idx < 0) { + return false; + } + // undo till this point + this.hunkData.ignoreTextModelNChanges = true; + try { + const targetAltVersion = this._exchanges[idx].prompt.modelAltVersionId; + while (targetAltVersion < this.textModelN.getAlternativeVersionId() && this.textModelN.canUndo()) { + await this.textModelN.undo(); + } + } finally { + this.hunkData.ignoreTextModelNChanges = false; + } + // TODO@jrieken cannot do this yet because some parts still rely on + // exchanges being around... + // // remove this and following exchanges + // this._exchanges.length = idx; + return true; } get hasChangedText(): boolean { @@ -251,7 +269,7 @@ export class Session { when: this._startTime, exchanges: [] }; - for (const exchange of this._exchange) { + for (const exchange of this._exchanges) { const response = exchange.response; if (response instanceof ReplyResponse) { result.exchanges.push({ prompt: exchange.prompt.value, res: response.chatResponse }); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts index 05f3fde5e88..2e2f692cc88 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatStrategies.ts @@ -26,7 +26,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { Progress } from 'vs/platform/progress/common/progress'; import { SaveReason } from 'vs/workbench/common/editor'; import { countWords } from 'vs/workbench/contrib/chat/common/chatWordCounter'; -import { HunkInformation, ReplyResponse, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; +import { HunkInformation, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { InlineChatZoneWidget } from './inlineChatZoneWidget'; import { CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, CTX_INLINE_CHAT_DOCUMENT_CHANGED, InlineChatConfigKeys, minimapInlineChatDiffInserted, overviewRulerInlineChatDiffInserted } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { HunkState } from './inlineChatSession'; @@ -136,7 +136,7 @@ export abstract class EditModeStrategy { abstract makeChanges(edits: ISingleEditOperation[], obs: IEditObserver, undoStopBefore: boolean): Promise; - abstract renderChanges(response: ReplyResponse): Promise; + abstract renderChanges(): Promise; move?(next: boolean): void; @@ -190,7 +190,7 @@ export class PreviewStrategy extends EditModeStrategy { override async makeProgressiveChanges(): Promise { } - override async renderChanges(response: ReplyResponse): Promise { } + override async renderChanges(): Promise { } hasFocus(): boolean { return this._zone.widget.hasFocus(); @@ -364,7 +364,7 @@ export class LiveStrategy extends EditModeStrategy { private readonly _hunkDisplayData = new Map(); - override async renderChanges(response: ReplyResponse) { + override async renderChanges() { this._progressiveEditingDecorations.clear(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index b2efaecda49..aa6f14f0ae4 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -160,11 +160,6 @@ border-radius: 3px; } - .monaco-action-bar .action-item.menu-entry.text-only:first-of-type .action-label{ - color: var(--vscode-button-foreground); - background-color: var(--vscode-button-background); - } - .monaco-action-bar .action-item.menu-entry.text-only + .action-item:not(.text-only) > .monaco-dropdown .action-label { font-size: 12px; line-height: 16px; @@ -173,6 +168,15 @@ } } +.monaco-workbench .inline-chat .status .actions, +.monaco-workbench .inline-chat-content-widget.contents .toolbar { + + .monaco-action-bar .action-item.menu-entry.text-only:first-of-type .action-label{ + color: var(--vscode-button-foreground); + background-color: var(--vscode-button-background); + } +} + .monaco-workbench .inline-chat .status .actions > .monaco-button, .monaco-workbench .inline-chat .status .actions > .monaco-button-dropdown { margin-right: 4px; diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 4039811d60d..3ab26fb4335 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -69,7 +69,8 @@ Registry.as(Extensions.Configuration).registerConfigurat [InlineChatConfigKeys.ExpTextButtons]: { description: localize('txtButtons', "Whether to use textual buttons (Requires restart)."), default: false, - type: 'boolean' + type: 'boolean', + tags: ['experimental'] }, } }); diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 00e75f65816..cc4ddad6e7a 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -42,7 +42,7 @@ import { IInlineChatSessionService } from '../../browser/inlineChatSessionServic import { InlineChatSessionServiceImpl } from '../../browser/inlineChatSessionServiceImpl'; import { TestWorkerService } from './testWorkerService'; import { IExtensionService, nullExtensionDescription } from 'vs/workbench/services/extensions/common/extensions'; -import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatProgress, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { ChatService } from 'vs/workbench/contrib/chat/common/chatServiceImpl'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; @@ -778,4 +778,47 @@ suite('InteractiveChatController', function () { assert.strictEqual(model.getValue(), 'TRY:1\ntwo\none\n'); }); + + test('Stopping/cancelling a request should undo its changes', async function () { + + model.setValue('World'); + + const deferred = new DeferredPromise(); + let progress: ((part: IChatProgress) => void) | undefined; + + store.add(chatAgentService.registerDynamicAgent({ + id: 'testEditorAgent2', + ...agentData + }, { + async invoke(request, _progress, history, token) { + + progress = _progress; + await deferred.p; + return {}; + }, + })); + + ctrl = instaService.createInstance(TestController, editor); + + // REQUEST 1 + const p = ctrl.awaitStates([...TestController.INIT_SEQUENCE, State.SHOW_REQUEST]); + ctrl.run({ message: 'Hello', autoSend: true }); + assert.strictEqual(await p, undefined); + + assertType(progress); + + const modelChange = new Promise(resolve => model.onDidChangeContent(() => resolve())); + + progress({ kind: 'textEdit', uri: model.uri, edits: [{ range: new Range(1, 1, 1, 1), text: 'Hello-Hello' }] }); + + await modelChange; + assert.strictEqual(model.getValue(), 'HelloWorld'); // first word has been streamed + + const p2 = ctrl.awaitStates([State.SHOW_RESPONSE, State.WAIT_FOR_INPUT]); + chatService.cancelCurrentRequestForSession(ctrl.chatWidget.viewModel!.model.sessionId); + assert.strictEqual(await p2, undefined); + + assert.strictEqual(model.getValue(), 'World'); + + }); }); From 74a5de9113836d14a531b982070230379ec59005 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 24 Jun 2024 13:18:58 +0200 Subject: [PATCH 0071/2222] fix #216574 (#217023) --- .../extensionRecommendationNotificationService.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts index 4152bf2f351..1f510c15b17 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationNotificationService.ts @@ -14,6 +14,7 @@ import { isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IExtensionRecommendationNotificationService, IExtensionRecommendations, RecommendationsNotificationResult, RecommendationSource, RecommendationSourceToString } from 'vs/platform/extensionRecommendations/common/extensionRecommendations'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -283,9 +284,18 @@ export class ExtensionRecommendationNotificationService extends Disposable imple const installExtensions = async (isMachineScoped: boolean) => { this.runAction(this.instantiationService.createInstance(SearchExtensionsAction, searchValue)); onDidInstallRecommendedExtensions(extensions); + const galleryExtensions: IGalleryExtension[] = [], resourceExtensions: IExtension[] = []; + for (const extension of extensions) { + if (extension.gallery) { + galleryExtensions.push(extension.gallery); + } else if (extension.resourceExtension) { + resourceExtensions.push(extension); + } + } await Promises.settled([ Promises.settled(extensions.map(extension => this.extensionsWorkbenchService.open(extension, { pinned: true }))), - this.extensionManagementService.installGalleryExtensions(extensions.map(e => ({ extension: e.gallery!, options: { isMachineScoped } }))) + galleryExtensions.length ? this.extensionManagementService.installGalleryExtensions(galleryExtensions.map(e => ({ extension: e, options: { isMachineScoped } }))) : Promise.resolve(), + resourceExtensions.length ? Promise.allSettled(resourceExtensions.map(r => this.extensionsWorkbenchService.install(r))) : Promise.resolve() ]); }; choices.push({ From cd5f306825446b4301eb90bb63cc6f59641443c2 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 24 Jun 2024 15:19:09 +0200 Subject: [PATCH 0072/2222] fixes: install pre-release action & disablement (#217157) --- .../browser/extensions.contribution.ts | 30 +++++++++++++++++-- .../extensions/browser/extensionsActions.ts | 1 + .../extensions/browser/extensionsList.ts | 2 +- 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 89da697bf19..55dfb610a32 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -1493,7 +1493,31 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menu: { id: MenuId.ExtensionContext, group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), CONTEXT_SYNC_ENABLEMENT) + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), CONTEXT_SYNC_ENABLEMENT), + order: 1 + }, + run: async (accessor: ServicesAccessor, extensionId: string) => { + const instantiationService = accessor.get(IInstantiationService); + const extension = this.extensionsWorkbenchService.local.filter(e => areSameExtensions(e.identifier, { id: extensionId }))[0] + || (await this.extensionsWorkbenchService.getExtensions([{ id: extensionId }], CancellationToken.None))[0]; + if (extension) { + const action = instantiationService.createInstance(InstallAction, { + isMachineScoped: true, + }); + action.extension = extension; + return action.run(); + } + } + }); + + this.registerExtensionAction({ + id: 'workbench.extensions.action.installPrereleaseAndDonotSync', + title: localize('installPrereleaseAndDonotSync', "Install Pre-Release (Do not Sync)"), + menu: { + id: MenuId.ExtensionContext, + group: '0_install', + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), CONTEXT_SYNC_ENABLEMENT), + order: 2 }, run: async (accessor: ServicesAccessor, extensionId: string) => { const instantiationService = accessor.get(IInstantiationService); @@ -1502,6 +1526,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi if (extension) { const action = instantiationService.createInstance(InstallAction, { isMachineScoped: true, + preRelease: true }); action.extension = extension; return action.run(); @@ -1515,7 +1540,8 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menu: { id: MenuId.ExtensionContext, group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension')) + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension')), + order: 3 }, run: async (accessor: ServicesAccessor, extensionId: string) => { const instantiationService = accessor.get(IInstantiationService); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 34c1862a061..7141e8effe4 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1168,6 +1168,7 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n cksOverlay.push(['installedExtensionIsOptedToPreRelease', !!extension.local?.preRelease]); cksOverlay.push(['galleryExtensionIsPreReleaseVersion', !!extension.gallery?.properties.isPreReleaseVersion]); cksOverlay.push(['galleryExtensionHasPreReleaseVersion', extension.gallery?.hasPreReleaseVersion]); + cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]); cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]); const [colorThemes, fileIconThemes, productIconThemes] = await Promise.all([workbenchThemeService.getColorThemes(), workbenchThemeService.getFileIconThemes(), workbenchThemeService.getProductIconThemes()]); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 8a443609adc..d74439e6c52 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -194,7 +194,7 @@ export class Renderer implements IPagedRenderer { data.extensionDisposables = dispose(data.extensionDisposables); const updateEnablement = () => { - const disabled = extension.local && !this.extensionEnablementService.isEnabled(extension.local); + const disabled = extension.state === ExtensionState.Installed && extension.local && !this.extensionEnablementService.isEnabled(extension.local); const deprecated = !!extension.deprecationInfo; data.element.classList.toggle('deprecated', deprecated); data.root.classList.toggle('disabled', disabled); From d38b24bf7ab6318bf69a5898857013f4c277a998 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Mon, 24 Jun 2024 15:27:01 +0200 Subject: [PATCH 0073/2222] Developing the accessible hover view/help (#216710) * adding code so thatcontent is shown appropriately * adding changes * adding changes * removing the fetching of the markdown hover content * adding changes * adding status bar into the rendered hover parts * polishing the code * adding code * polishing code * fixing the text for accessible help * combining the text * modifying the chat input editor hover * adding polish * adding localization * merging main * polishing after merging * returning the elemenet and the specific hover part * polishing the code * adding polish * polishing * merging main * wip * cleaning up the code, introducing new disposable types * polishing the code * adding code * Adding polish code * polishing the code * polishing code * adding code * polsih code * polishing the method getAccessibleContent * removing the sticky controller hover * disposing onClose --- .../browser/colorHoverParticipant.ts | 35 ++- .../colorPicker/browser/colorPickerWidget.ts | 13 +- .../browser/standaloneColorPickerWidget.ts | 7 +- .../hover/browser/contentHoverController.ts | 66 ++--- .../hover/browser/contentHoverRendered.ts | 263 +++++++++++++++-- .../hover/browser/hoverAccessibleViews.ts | 100 +++---- .../contrib/hover/browser/hoverActions.ts | 14 +- .../contrib/hover/browser/hoverController.ts | 24 +- .../contrib/hover/browser/hoverTypes.ts | 35 ++- .../hover/browser/markdownHoverParticipant.ts | 270 ++++++++++-------- .../hover/browser/markerHoverParticipant.ts | 32 ++- .../inlayHints/browser/inlayHintsHover.ts | 4 +- .../browser/hoverParticipant.ts | 35 ++- .../inlineEdit/browser/hoverParticipant.ts | 23 +- .../browser/unicodeHighlighter.ts | 10 +- .../browser/contrib/chatInputEditorHover.ts | 32 ++- 16 files changed, 655 insertions(+), 308 deletions(-) diff --git a/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts b/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts index 1bbdde4c746..32956ec08da 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorHoverParticipant.ts @@ -6,7 +6,7 @@ import { AsyncIterableObject } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Color, RGBA } from 'vs/base/common/color'; -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; @@ -16,11 +16,12 @@ import { getColorPresentations, getColors } from 'vs/editor/contrib/colorPicker/ import { ColorDetector } from 'vs/editor/contrib/colorPicker/browser/colorDetector'; import { ColorPickerModel } from 'vs/editor/contrib/colorPicker/browser/colorPickerModel'; import { ColorPickerWidget } from 'vs/editor/contrib/colorPicker/browser/colorPickerWidget'; -import { HoverAnchor, HoverAnchorType, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { HoverAnchor, HoverAnchorType, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { Dimension } from 'vs/base/browser/dom'; +import * as nls from 'vs/nls'; export class ColorHover implements IHoverPart { @@ -88,10 +89,22 @@ export class ColorHoverParticipant implements IEditorHoverParticipant { + const renderedPart = renderHoverParts(this, this._editor, this._themeService, hoverParts, context); + if (!renderedPart) { + return new RenderedHoverParts([]); + } + this._colorPicker = renderedPart.colorPicker; + const renderedHoverPart: IRenderedHoverPart = { + hoverPart: renderedPart.hoverPart, + hoverElement: this._colorPicker.domNode, + dispose() { renderedPart.disposables.dispose(); } + }; + return new RenderedHoverParts([renderedHoverPart]); + } + + public getAccessibleContent(hoverPart: ColorHover): string { + return nls.localize('hoverAccessibilityColorParticipant', 'There is a color picker here.'); } public handleResize(): void { @@ -158,7 +171,7 @@ export class StandaloneColorPickerParticipant { } } - public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: ColorHover[] | StandaloneColorPickerHover[]): { disposables: IDisposable; colorPicker: ColorPickerWidget | undefined } { + public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: StandaloneColorPickerHover[]): { disposables: IDisposable; hoverPart: StandaloneColorPickerHover; colorPicker: ColorPickerWidget } | undefined { return renderHoverParts(this, this._editor, this._themeService, hoverParts, context); } @@ -190,9 +203,9 @@ async function _createColorHover(participant: ColorHoverParticipant | Standalone } } -function renderHoverParts(participant: ColorHoverParticipant | StandaloneColorPickerParticipant, editor: ICodeEditor, themeService: IThemeService, hoverParts: ColorHover[] | StandaloneColorPickerHover[], context: IEditorHoverRenderContext): { disposables: IDisposable; colorPicker: ColorPickerWidget | undefined } { +function renderHoverParts(participant: ColorHoverParticipant | StandaloneColorPickerParticipant, editor: ICodeEditor, themeService: IThemeService, hoverParts: T[], context: IEditorHoverRenderContext): { hoverPart: T; colorPicker: ColorPickerWidget; disposables: DisposableStore } | undefined { if (hoverParts.length === 0 || !editor.hasModel()) { - return { disposables: Disposable.None, colorPicker: undefined }; + return undefined; } if (context.setMinimumDimensions) { const minimumHeight = editor.getOption(EditorOption.lineHeight) + 8; @@ -208,7 +221,7 @@ function renderHoverParts(participant: ColorHoverParticipant | StandaloneColorPi let editorUpdatedByColorPicker = false; let range = new Range(colorHover.range.startLineNumber, colorHover.range.startColumn, colorHover.range.endLineNumber, colorHover.range.endColumn); if (participant instanceof StandaloneColorPickerParticipant) { - const color = hoverParts[0].model.color; + const color = colorHover.model.color; participant.color = color; _updateColorPresentations(editorModel, model, color, range, colorHover); disposables.add(model.onColorFlushed((color: Color) => { @@ -232,7 +245,7 @@ function renderHoverParts(participant: ColorHoverParticipant | StandaloneColorPi editor.focus(); } })); - return { disposables, colorPicker }; + return { hoverPart: colorHover, colorPicker, disposables }; } function _updateEditorModel(editor: IActiveCodeEditor, range: Range, model: ColorPickerModel): Range { diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts index 7688defca5f..b911fe5b0f5 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerWidget.ts @@ -468,6 +468,7 @@ export class InsertButton extends Disposable { export class ColorPickerWidget extends Widget implements IEditorHoverColorPickerWidget { private static readonly ID = 'editor.contrib.colorPickerWidget'; + private readonly _domNode: HTMLElement; body: ColorPickerBody; header: ColorPickerHeader; @@ -477,11 +478,11 @@ export class ColorPickerWidget extends Widget implements IEditorHoverColorPicker this._register(PixelRatio.getInstance(dom.getWindow(container)).onDidChange(() => this.layout())); - const element = $('.colorpicker-widget'); - container.appendChild(element); + this._domNode = $('.colorpicker-widget'); + container.appendChild(this._domNode); - this.header = this._register(new ColorPickerHeader(element, this.model, themeService, standaloneColorPicker)); - this.body = this._register(new ColorPickerBody(element, this.model, this.pixelRatio, standaloneColorPicker)); + this.header = this._register(new ColorPickerHeader(this._domNode, this.model, themeService, standaloneColorPicker)); + this.body = this._register(new ColorPickerBody(this._domNode, this.model, this.pixelRatio, standaloneColorPicker)); } getId(): string { @@ -491,4 +492,8 @@ export class ColorPickerWidget extends Widget implements IEditorHoverColorPicker layout(): void { this.body.layout(); } + + get domNode(): HTMLElement { + return this._domNode; + } } diff --git a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPickerWidget.ts b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPickerWidget.ts index 7f9cad0d757..424448611b1 100644 --- a/src/vs/editor/contrib/colorPicker/browser/standaloneColorPickerWidget.ts +++ b/src/vs/editor/contrib/colorPicker/browser/standaloneColorPickerWidget.ts @@ -224,11 +224,12 @@ export class StandaloneColorPickerWidget extends Disposable implements IContentW }; this._colorHover = colorHover; - const { disposables, colorPicker } = this._standaloneColorPickerParticipant.renderHoverParts(context, [colorHover]); - this._register(disposables); - if (colorPicker === undefined) { + const renderedHoverPart = this._standaloneColorPickerParticipant.renderHoverParts(context, [colorHover]); + if (!renderedHoverPart) { return; } + this._register(renderedHoverPart.disposables); + const colorPicker = renderedHoverPart.colorPicker; this._body.classList.add('standalone-colorpicker-body'); this._body.style.maxHeight = Math.max(this._editor.getLayoutInfo().height / 4, 250) + 'px'; this._body.style.maxWidth = Math.max(this._editor.getLayoutInfo().width * 0.66, 500) + 'px'; diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index 7f49ad50a41..5ae7773bd1d 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -14,26 +14,21 @@ import { HoverOperation, HoverStartMode, HoverStartSource } from 'vs/editor/cont import { HoverAnchor, HoverParticipantRegistry, HoverRangeAnchor, IEditorHoverContext, IEditorHoverParticipant, IHoverPart, IHoverWidget } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { MarkdownHoverParticipant } from 'vs/editor/contrib/hover/browser/markdownHoverParticipant'; -import { InlayHintsHover } from 'vs/editor/contrib/inlayHints/browser/inlayHintsHover'; import { HoverVerbosityAction } from 'vs/editor/common/standalone/standaloneEnums'; import { ContentHoverWidget } from 'vs/editor/contrib/hover/browser/contentHoverWidget'; import { ContentHoverComputer } from 'vs/editor/contrib/hover/browser/contentHoverComputer'; import { HoverResult } from 'vs/editor/contrib/hover/browser/contentHoverTypes'; import { Emitter } from 'vs/base/common/event'; -import { ColorHoverParticipant } from 'vs/editor/contrib/colorPicker/browser/colorHoverParticipant'; import { RenderedContentHover } from 'vs/editor/contrib/hover/browser/contentHoverRendered'; export class ContentHoverController extends Disposable implements IHoverWidget { private _currentResult: HoverResult | null = null; + private _renderedContentHover: RenderedContentHover | undefined; private readonly _computer: ContentHoverComputer; private readonly _contentHoverWidget: ContentHoverWidget; private readonly _participants: IEditorHoverParticipant[]; - // TODO@aiday-mar make array of participants, dispatch between them - private readonly _markdownHoverParticipant: MarkdownHoverParticipant | undefined; - private readonly _colorHoverParticipant: ColorHoverParticipant | undefined; private readonly _hoverOperation: HoverOperation; private readonly _onContentsChanged = this._register(new Emitter()); @@ -46,34 +41,23 @@ export class ContentHoverController extends Disposable implements IHoverWidget { ) { super(); this._contentHoverWidget = this._register(this._instantiationService.createInstance(ContentHoverWidget, this._editor)); - const initializedParticipants = this._initializeHoverParticipants(); - this._participants = initializedParticipants.participants; - this._markdownHoverParticipant = initializedParticipants.markdownHoverParticipant; - this._colorHoverParticipant = initializedParticipants.colorHoverParticipant; + this._participants = this._initializeHoverParticipants(); this._computer = new ContentHoverComputer(this._editor, this._participants); this._hoverOperation = this._register(new HoverOperation(this._editor, this._computer)); this._registerListeners(); } - private _initializeHoverParticipants(): { participants: IEditorHoverParticipant[]; markdownHoverParticipant: MarkdownHoverParticipant | undefined; colorHoverParticipant: ColorHoverParticipant | undefined } { + private _initializeHoverParticipants(): IEditorHoverParticipant[] { const participants: IEditorHoverParticipant[] = []; - let markdownHoverParticipant: MarkdownHoverParticipant | undefined; - let colorHoverParticipant: ColorHoverParticipant | undefined; for (const participant of HoverParticipantRegistry.getAll()) { const participantInstance = this._instantiationService.createInstance(participant, this._editor); - if (participantInstance instanceof MarkdownHoverParticipant && !(participantInstance instanceof InlayHintsHover)) { - markdownHoverParticipant = participantInstance; - } - if (participantInstance instanceof ColorHoverParticipant) { - colorHoverParticipant = participantInstance; - } participants.push(participantInstance); } participants.sort((p1, p2) => p1.hoverOrdinal - p2.hoverOrdinal); this._register(this._contentHoverWidget.onDidResize(() => { this._participants.forEach(participant => participant.handleResize?.()); })); - return { participants, markdownHoverParticipant, colorHoverParticipant }; + return participants; } private _registerListeners(): void { @@ -221,11 +205,11 @@ export class ContentHoverController extends Disposable implements IHoverWidget { private _showHover(hoverResult: HoverResult): void { const context = this._getHoverContext(); - const renderedHover = new RenderedContentHover(this._editor, hoverResult, this._participants, this._computer, context, this._keybindingService); - if (renderedHover.domNodeHasChildren) { - this._contentHoverWidget.show(renderedHover); + this._renderedContentHover = new RenderedContentHover(this._editor, hoverResult, this._participants, this._computer, context, this._keybindingService); + if (this._renderedContentHover.domNodeHasChildren) { + this._contentHoverWidget.show(this._renderedContentHover); } else { - renderedHover.dispose(); + this._renderedContentHover.dispose(); } } @@ -301,28 +285,32 @@ export class ContentHoverController extends Disposable implements IHoverWidget { this._startShowingOrUpdateHover(new HoverRangeAnchor(0, range, undefined, undefined), mode, source, focus, null); } - public async updateMarkdownHoverVerbosityLevel(action: HoverVerbosityAction, index?: number, focus?: boolean): Promise { - this._markdownHoverParticipant?.updateMarkdownHoverVerbosityLevel(action, index, focus); + public getWidgetContent(): string | undefined { + const node = this._contentHoverWidget.getDomNode(); + if (!node.textContent) { + return undefined; + } + return node.textContent; + } + + public async updateHoverVerbosityLevel(action: HoverVerbosityAction, index: number, focus?: boolean): Promise { + this._renderedContentHover?.updateHoverVerbosityLevel(action, index, focus); } - public focusedMarkdownHoverIndex(): number { - return this._markdownHoverParticipant?.focusedMarkdownHoverIndex() ?? -1; + public doesHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { + return this._renderedContentHover?.doesHoverAtIndexSupportVerbosityAction(index, action) ?? false; } - public markdownHoverContentAtIndex(index: number): string { - return this._markdownHoverParticipant?.markdownHoverContentAtIndex(index) ?? ''; + public getAccessibleWidgetContent(): string | undefined { + return this._renderedContentHover?.getAccessibleWidgetContent(); } - public doesMarkdownHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { - return this._markdownHoverParticipant?.doesMarkdownHoverAtIndexSupportVerbosityAction(index, action) ?? false; + public getAccessibleWidgetContentAtIndex(index: number): string | undefined { + return this._renderedContentHover?.getAccessibleWidgetContentAtIndex(index); } - public getWidgetContent(): string | undefined { - const node = this._contentHoverWidget.getDomNode(); - if (!node.textContent) { - return undefined; - } - return node.textContent; + public focusedHoverPartIndex(): number { + return this._renderedContentHover?.focusedHoverPartIndex ?? -1; } public containsNode(node: Node | null | undefined): boolean { @@ -372,7 +360,7 @@ export class ContentHoverController extends Disposable implements IHoverWidget { } public get isColorPickerVisible(): boolean { - return this._colorHoverParticipant?.isColorPickerVisible() ?? false; + return this._renderedContentHover?.isColorPickerVisible() ?? false; } public get isVisibleFromKeyboard(): boolean { diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index 5d9c6f52c8c..6384984b529 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEditorHoverContext, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { IEditorHoverContext, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverParts, RenderedHoverParts } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ContentHoverComputer } from 'vs/editor/contrib/hover/browser/contentHoverComputer'; import { EditorHoverStatusBar } from 'vs/editor/contrib/hover/browser/contentHoverStatusBar'; @@ -14,6 +14,13 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { HoverResult } from 'vs/editor/contrib/hover/browser/contentHoverTypes'; +import * as dom from 'vs/base/browser/dom'; +import { HoverVerbosityAction } from 'vs/editor/common/languages'; +import { MarkdownHoverParticipant } from 'vs/editor/contrib/hover/browser/markdownHoverParticipant'; +import { ColorHoverParticipant } from 'vs/editor/contrib/colorPicker/browser/colorHoverParticipant'; +import { localize } from 'vs/nls'; +import { InlayHintsHover } from 'vs/editor/contrib/inlayHints/browser/inlayHintsHover'; +import { BugIndicatingError } from 'vs/base/common/errors'; export class RenderedContentHover extends Disposable { @@ -44,8 +51,8 @@ export class RenderedContentHover extends Disposable { editor, participants, parts, - context, - keybindingService + keybindingService, + context )); const { showAtPosition, showAtSecondaryPosition } = RenderedContentHover.computeHoverPositions(editor, anchor.range, parts); this.shouldAppearBeforeContent = parts.some(m => m.isBeforeContent); @@ -65,6 +72,30 @@ export class RenderedContentHover extends Disposable { return this._renderedHoverParts.domNodeHasChildren; } + public get focusedHoverPartIndex(): number { + return this._renderedHoverParts.focusedHoverPartIndex; + } + + public getAccessibleWidgetContent(): string { + return this._renderedHoverParts.getAccessibleContent(); + } + + public getAccessibleWidgetContentAtIndex(index: number): string { + return this._renderedHoverParts.getAccessibleHoverContentAtIndex(index); + } + + public async updateHoverVerbosityLevel(action: HoverVerbosityAction, index: number, focus?: boolean): Promise { + this._renderedHoverParts.updateHoverVerbosityLevel(action, index, focus); + } + + public doesHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { + return this._renderedHoverParts.doesHoverAtIndexSupportVerbosityAction(index, action); + } + + public isColorPickerVisible(): boolean { + return this._renderedHoverParts.isColorPickerVisible(); + } + public static computeHoverPositions(editor: ICodeEditor, anchorRange: Range, hoverParts: IHoverPart[]): { showAtPosition: Position; showAtSecondaryPosition: Position } { let startColumnBoundary = 1; @@ -116,6 +147,53 @@ export class RenderedContentHover extends Disposable { } } +interface IRenderedContentHoverPart { + /** + * Type of rendered part + */ + type: 'hoverPart'; + /** + * Participant of the rendered hover part + */ + participant: IEditorHoverParticipant; + /** + * The rendered hover part + */ + hoverPart: IHoverPart; + /** + * The HTML element containing the hover status bar. + */ + hoverElement: HTMLElement; +} + +interface IRenderedContentStatusBar { + /** + * Type of rendered part + */ + type: 'statusBar'; + /** + * The HTML element containing the hover status bar. + */ + hoverElement: HTMLElement; +} + +type IRenderedContentHoverPartOrStatusBar = IRenderedContentHoverPart | IRenderedContentStatusBar; + +class RenderedStatusBar implements IDisposable { + + constructor(fragment: DocumentFragment, private readonly _statusBar: EditorHoverStatusBar) { + fragment.appendChild(this._statusBar.hoverElement); + } + + get hoverElement(): HTMLElement { + return this._statusBar.hoverElement; + } + + dispose() { + this._statusBar.dispose(); + } +} + class RenderedContentHoverParts extends Disposable { private static readonly _DECORATION_OPTIONS = ModelDecorationOptions.register({ @@ -123,27 +201,31 @@ class RenderedContentHoverParts extends Disposable { className: 'hoverHighlight' }); - private readonly _participants: IEditorHoverParticipant[]; + private readonly _renderedParts: IRenderedContentHoverPartOrStatusBar[] = []; private readonly _fragment: DocumentFragment; + private readonly _context: IEditorHoverContext; + + private _markdownHoverParticipant: MarkdownHoverParticipant | undefined; + private _colorHoverParticipant: ColorHoverParticipant | undefined; + private _focusedHoverPartIndex: number = -1; constructor( editor: ICodeEditor, participants: IEditorHoverParticipant[], hoverParts: IHoverPart[], - context: IEditorHoverContext, - keybindingService: IKeybindingService + keybindingService: IKeybindingService, + context: IEditorHoverContext ) { super(); - this._participants = participants; + this._context = context; this._fragment = document.createDocumentFragment(); - const statusBar = new EditorHoverStatusBar(keybindingService); - const hoverContext: IEditorHoverRenderContext = { fragment: this._fragment, statusBar, ...context }; - this._register(this._renderHoverParts(hoverContext, hoverParts)); - this._register(this._renderStatusBar(this._fragment, statusBar)); - this._register(this._addEditorDecorations(editor, hoverParts)); + this._register(this._renderParts(participants, hoverParts, context, keybindingService)); + this._register(this._registerListenersOnRenderedParts()); + this._register(this._createEditorDecorations(editor, hoverParts)); + this._updateMarkdownAndColorParticipantInfo(participants); } - private _addEditorDecorations(editor: ICodeEditor, hoverParts: IHoverPart[]): IDisposable { + private _createEditorDecorations(editor: ICodeEditor, hoverParts: IHoverPart[]): IDisposable { if (hoverParts.length === 0) { return Disposable.None; } @@ -162,25 +244,152 @@ class RenderedContentHoverParts extends Disposable { }); } - private _renderHoverParts(context: IEditorHoverRenderContext, hoverParts: IHoverPart[]): IDisposable { + private _renderParts(participants: IEditorHoverParticipant[], hoverParts: IHoverPart[], hoverContext: IEditorHoverContext, keybindingService: IKeybindingService): IDisposable { + const statusBar = new EditorHoverStatusBar(keybindingService); + const hoverRenderingContext: IEditorHoverRenderContext = { + fragment: this._fragment, + statusBar, + ...hoverContext + }; const disposables = new DisposableStore(); - for (const participant of this._participants) { - const hoverPartsForParticipant = hoverParts.filter(hoverPart => hoverPart.owner === participant); - const hasHoverPartsForParticipant = hoverPartsForParticipant.length > 0; - if (!hasHoverPartsForParticipant) { - continue; + for (const participant of participants) { + const renderedHoverParts = this._renderHoverPartsForParticipant(hoverParts, participant, hoverRenderingContext); + disposables.add(renderedHoverParts); + for (const renderedHoverPart of renderedHoverParts.renderedHoverParts) { + this._renderedParts.push({ + type: 'hoverPart', + participant, + hoverPart: renderedHoverPart.hoverPart, + hoverElement: renderedHoverPart.hoverElement, + }); } - disposables.add(participant.renderHoverParts(context, hoverPartsForParticipant)); } - return disposables; + const renderedStatusBar = this._renderStatusBar(this._fragment, statusBar); + if (renderedStatusBar) { + disposables.add(renderedStatusBar); + this._renderedParts.push({ + type: 'statusBar', + hoverElement: renderedStatusBar.hoverElement, + }); + } + return toDisposable(() => { disposables.dispose(); }); + } + + private _renderHoverPartsForParticipant(hoverParts: IHoverPart[], participant: IEditorHoverParticipant, hoverRenderingContext: IEditorHoverRenderContext): IRenderedHoverParts { + const hoverPartsForParticipant = hoverParts.filter(hoverPart => hoverPart.owner === participant); + const hasHoverPartsForParticipant = hoverPartsForParticipant.length > 0; + if (!hasHoverPartsForParticipant) { + return new RenderedHoverParts([]); + } + return participant.renderHoverParts(hoverRenderingContext, hoverPartsForParticipant); } - private _renderStatusBar(fragment: DocumentFragment, statusBar: EditorHoverStatusBar): IDisposable { + private _renderStatusBar(fragment: DocumentFragment, statusBar: EditorHoverStatusBar): RenderedStatusBar | undefined { if (!statusBar.hasContent) { - return Disposable.None; + return undefined; + } + return new RenderedStatusBar(fragment, statusBar); + } + + private _registerListenersOnRenderedParts(): IDisposable { + const disposables = new DisposableStore(); + this._renderedParts.forEach((renderedPart: IRenderedContentHoverPartOrStatusBar, index: number) => { + const element = renderedPart.hoverElement; + element.tabIndex = 0; + disposables.add(dom.addDisposableListener(element, dom.EventType.FOCUS_IN, (event: Event) => { + event.stopPropagation(); + this._focusedHoverPartIndex = index; + })); + disposables.add(dom.addDisposableListener(element, dom.EventType.FOCUS_OUT, (event: Event) => { + event.stopPropagation(); + this._focusedHoverPartIndex = -1; + })); + }); + return disposables; + } + + private _updateMarkdownAndColorParticipantInfo(participants: IEditorHoverParticipant[]) { + const markdownHoverParticipant = participants.find(p => { + return (p instanceof MarkdownHoverParticipant) && !(p instanceof InlayHintsHover); + }); + if (markdownHoverParticipant) { + this._markdownHoverParticipant = markdownHoverParticipant as MarkdownHoverParticipant; + } + this._colorHoverParticipant = participants.find(p => p instanceof ColorHoverParticipant); + } + + public getAccessibleContent(): string { + const content: string[] = []; + for (let i = 0; i < this._renderedParts.length; i++) { + content.push(this.getAccessibleHoverContentAtIndex(i)); + } + return content.join('\n\n'); + } + + public getAccessibleHoverContentAtIndex(index: number): string { + const renderedPart = this._renderedParts[index]; + if (!renderedPart) { + return ''; + } + if (renderedPart.type === 'statusBar') { + return localize('hoverAccessibilityStatusBar', "This is a hover status bar."); + } + return renderedPart.participant.getAccessibleContent(renderedPart.hoverPart); + } + + public async updateHoverVerbosityLevel(action: HoverVerbosityAction, index: number, focus?: boolean): Promise { + if (!this._markdownHoverParticipant) { + return; + } + const normalizedMarkdownHoverIndex = this._normalizedIndexToMarkdownHoverIndexRange(this._markdownHoverParticipant, index); + if (normalizedMarkdownHoverIndex === undefined) { + return; + } + const renderedPart = await this._markdownHoverParticipant.updateMarkdownHoverVerbosityLevel(action, normalizedMarkdownHoverIndex, focus); + if (!renderedPart) { + return; + } + this._renderedParts[index] = { + type: 'hoverPart', + participant: this._markdownHoverParticipant, + hoverPart: renderedPart.hoverPart, + hoverElement: renderedPart.hoverElement, + }; + this._context.onContentsChanged(); + } + + public doesHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { + if (!this._markdownHoverParticipant) { + return false; + } + const normalizedMarkdownHoverIndex = this._normalizedIndexToMarkdownHoverIndexRange(this._markdownHoverParticipant, index); + if (normalizedMarkdownHoverIndex === undefined) { + return false; } - fragment.appendChild(statusBar.hoverElement); - return statusBar; + return this._markdownHoverParticipant.doesMarkdownHoverAtIndexSupportVerbosityAction(normalizedMarkdownHoverIndex, action); + } + + public isColorPickerVisible(): boolean { + return this._colorHoverParticipant?.isColorPickerVisible() ?? false; + } + + private _normalizedIndexToMarkdownHoverIndexRange(markdownHoverParticipant: MarkdownHoverParticipant, index: number): number | undefined { + const renderedPart = this._renderedParts[index]; + if (!renderedPart || renderedPart.type !== 'hoverPart') { + return undefined; + } + const isHoverPartMarkdownHover = renderedPart.participant === markdownHoverParticipant; + if (!isHoverPartMarkdownHover) { + return undefined; + } + const firstIndexOfMarkdownHovers = this._renderedParts.findIndex(renderedPart => + renderedPart.type === 'hoverPart' + && renderedPart.participant === markdownHoverParticipant + ); + if (firstIndexOfMarkdownHovers === -1) { + throw new BugIndicatingError(); + } + return index - firstIndexOfMarkdownHovers; } public get domNode(): DocumentFragment { @@ -190,4 +399,8 @@ class RenderedContentHoverParts extends Disposable { public get domNodeHasChildren(): boolean { return this._fragment.hasChildNodes(); } + + public get focusedHoverPartIndex(): number { + return this._focusedHoverPartIndex; + } } diff --git a/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts b/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts index 2998c0e3e30..462df2e9d60 100644 --- a/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts +++ b/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts @@ -18,15 +18,15 @@ import { Action, IAction } from 'vs/base/common/actions'; import { ThemeIcon } from 'vs/base/common/themables'; import { Codicon } from 'vs/base/common/codicons'; import { Emitter, Event } from 'vs/base/common/event'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { labelForHoverVerbosityAction } from 'vs/editor/contrib/hover/browser/markdownHoverParticipant'; namespace HoverAccessibilityHelpNLS { - export const intro = localize('intro', "Focus on the hover widget to cycle through the hover parts with the Tab key."); - export const increaseVerbosity = localize('increaseVerbosity', "- The focused hover part verbosity level can be increased with the Increase Hover Verbosity command.", INCREASE_HOVER_VERBOSITY_ACTION_ID); - export const decreaseVerbosity = localize('decreaseVerbosity', "- The focused hover part verbosity level can be decreased with the Decrease Hover Verbosity command.", DECREASE_HOVER_VERBOSITY_ACTION_ID); - export const hoverContent = localize('contentHover', "The last focused hover content is the following."); + export const introHoverPart = localize('introHoverPart', 'The focused hover part content is the following:'); + export const introHoverFull = localize('introHoverFull', 'The full focused hover content is the following:'); + export const increaseVerbosity = localize('increaseVerbosity', '- The focused hover part verbosity level can be increased with the Increase Hover Verbosity command.', INCREASE_HOVER_VERBOSITY_ACTION_ID); + export const decreaseVerbosity = localize('decreaseVerbosity', '- The focused hover part verbosity level can be decreased with the Decrease Hover Verbosity command.', DECREASE_HOVER_VERBOSITY_ACTION_ID); } export class HoverAccessibleView implements IAccessibleViewImplentation { @@ -85,7 +85,6 @@ export class HoverAccessibilityHelp implements IAccessibleViewImplentation { } } - abstract class BaseHoverAccessibleViewProvider extends Disposable implements IAccessibleViewContentProvider { abstract provideContent(): string; @@ -97,12 +96,9 @@ abstract class BaseHoverAccessibleViewProvider extends Disposable implements IAc private readonly _onDidChangeContent: Emitter = this._register(new Emitter()); public readonly onDidChangeContent: Event = this._onDidChangeContent.event; - protected _markdownHoverFocusedIndex: number = -1; - private _onHoverContentsChanged: IDisposable | undefined; + protected _focusedHoverPartIndex: number = -1; - constructor( - protected readonly _hoverController: HoverController, - ) { + constructor(protected readonly _hoverController: HoverController) { super(); } @@ -111,8 +107,8 @@ abstract class BaseHoverAccessibleViewProvider extends Disposable implements IAc return; } this._hoverController.shouldKeepOpenOnEditorMouseMoveOrLeave = true; - this._markdownHoverFocusedIndex = this._hoverController.focusedMarkdownHoverIndex(); - this._onHoverContentsChanged = this._register(this._hoverController.onHoverContentsChanged(() => { + this._focusedHoverPartIndex = this._hoverController.focusedHoverPartIndex(); + this._register(this._hoverController.onHoverContentsChanged(() => { this._onDidChangeContent.fire(); })); } @@ -121,33 +117,35 @@ abstract class BaseHoverAccessibleViewProvider extends Disposable implements IAc if (!this._hoverController) { return; } - this._markdownHoverFocusedIndex = -1; + this._focusedHoverPartIndex = -1; this._hoverController.focus(); this._hoverController.shouldKeepOpenOnEditorMouseMoveOrLeave = false; - this._onHoverContentsChanged?.dispose(); - } -} - -export class HoverAccessibilityHelpProvider extends BaseHoverAccessibleViewProvider implements IAccessibleViewContentProvider { - - public readonly options: IAccessibleViewOptions = { type: AccessibleViewType.Help }; - - constructor( - hoverController: HoverController, - ) { - super(hoverController); + this.dispose(); } - provideContent(): string { - return this.provideContentAtIndex(this._markdownHoverFocusedIndex); - } - - provideContentAtIndex(index: number): string { - const content: string[] = []; - content.push(HoverAccessibilityHelpNLS.intro); - content.push(...this._descriptionsOfVerbosityActionsForIndex(index)); - content.push(...this._descriptionOfFocusedMarkdownHoverAtIndex(index)); - return content.join('\n'); + provideContentAtIndex(focusedHoverIndex: number, includeVerbosityActions: boolean): string { + if (focusedHoverIndex !== -1) { + const accessibleContent = this._hoverController.getAccessibleWidgetContentAtIndex(focusedHoverIndex); + if (accessibleContent === undefined) { + return ''; + } + const contents: string[] = []; + if (includeVerbosityActions) { + contents.push(...this._descriptionsOfVerbosityActionsForIndex(focusedHoverIndex)); + } + contents.push(HoverAccessibilityHelpNLS.introHoverPart); + contents.push(accessibleContent); + return contents.join('\n\n'); + } else { + const accessibleContent = this._hoverController.getAccessibleWidgetContent(); + if (accessibleContent === undefined) { + return ''; + } + const contents: string[] = []; + contents.push(HoverAccessibilityHelpNLS.introHoverFull); + contents.push(accessibleContent); + return contents.join('\n\n'); + } } private _descriptionsOfVerbosityActionsForIndex(index: number): string[] { @@ -164,7 +162,7 @@ export class HoverAccessibilityHelpProvider extends BaseHoverAccessibleViewProvi } private _descriptionOfVerbosityActionForIndex(action: HoverVerbosityAction, index: number): string | undefined { - const isActionSupported = this._hoverController.doesMarkdownHoverAtIndexSupportVerbosityAction(index, action); + const isActionSupported = this._hoverController.doesHoverAtIndexSupportVerbosityAction(index, action); if (!isActionSupported) { return; } @@ -175,15 +173,18 @@ export class HoverAccessibilityHelpProvider extends BaseHoverAccessibleViewProvi return HoverAccessibilityHelpNLS.decreaseVerbosity; } } +} - protected _descriptionOfFocusedMarkdownHoverAtIndex(index: number): string[] { - const content: string[] = []; - const hoverContent = this._hoverController.markdownHoverContentAtIndex(index); - if (hoverContent) { - content.push('\n' + HoverAccessibilityHelpNLS.hoverContent); - content.push('\n' + hoverContent); - } - return content; +export class HoverAccessibilityHelpProvider extends BaseHoverAccessibleViewProvider implements IAccessibleViewContentProvider { + + public readonly options: IAccessibleViewOptions = { type: AccessibleViewType.Help }; + + constructor(hoverController: HoverController) { + super(hoverController); + } + + provideContent(): string { + return this.provideContentAtIndex(this._focusedHoverPartIndex, true); } } @@ -201,8 +202,7 @@ export class HoverAccessibleViewProvider extends BaseHoverAccessibleViewProvider } public provideContent(): string { - const hoverContent = this._hoverController.markdownHoverContentAtIndex(this._markdownHoverFocusedIndex); - return hoverContent.length > 0 ? hoverContent : this._hoverController.getWidgetContent() || HoverAccessibilityHelpNLS.intro; + return this.provideContentAtIndex(this._focusedHoverPartIndex, false); } public get actions(): IAction[] { @@ -229,16 +229,16 @@ export class HoverAccessibleViewProvider extends BaseHoverAccessibleViewProvider break; } const actionLabel = labelForHoverVerbosityAction(this._keybindingService, action); - const actionEnabled = this._hoverController.doesMarkdownHoverAtIndexSupportVerbosityAction(this._markdownHoverFocusedIndex, action); + const actionEnabled = this._hoverController.doesHoverAtIndexSupportVerbosityAction(this._focusedHoverPartIndex, action); return new Action(accessibleActionId, actionLabel, ThemeIcon.asClassName(actionCodicon), actionEnabled, () => { - editor.getAction(actionId)?.run({ index: this._markdownHoverFocusedIndex, focus: false }); + editor.getAction(actionId)?.run({ index: this._focusedHoverPartIndex, focus: false }); }); } private _initializeOptions(editor: ICodeEditor, hoverController: HoverController): void { const helpProvider = this._register(new HoverAccessibilityHelpProvider(hoverController)); this.options.language = editor.getModel()?.getLanguageId(); - this.options.customHelp = () => { return helpProvider.provideContentAtIndex(this._markdownHoverFocusedIndex); }; + this.options.customHelp = () => { return helpProvider.provideContentAtIndex(this._focusedHoverPartIndex, true); }; } } diff --git a/src/vs/editor/contrib/hover/browser/hoverActions.ts b/src/vs/editor/contrib/hover/browser/hoverActions.ts index 20a5148fd74..37eea40441a 100644 --- a/src/vs/editor/contrib/hover/browser/hoverActions.ts +++ b/src/vs/editor/contrib/hover/browser/hoverActions.ts @@ -432,7 +432,12 @@ export class IncreaseHoverVerbosityLevel extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor, args?: { index: number; focus: boolean }): void { - HoverController.get(editor)?.updateMarkdownHoverVerbosityLevel(HoverVerbosityAction.Increase, args?.index, args?.focus); + const hoverController = HoverController.get(editor); + if (!hoverController) { + return; + } + const index = args?.index !== undefined ? args.index : hoverController.focusedHoverPartIndex(); + hoverController.updateHoverVerbosityLevel(HoverVerbosityAction.Increase, index, args?.focus); } } @@ -448,6 +453,11 @@ export class DecreaseHoverVerbosityLevel extends EditorAction { } public run(accessor: ServicesAccessor, editor: ICodeEditor, args?: { index: number; focus: boolean }): void { - HoverController.get(editor)?.updateMarkdownHoverVerbosityLevel(HoverVerbosityAction.Decrease, args?.index, args?.focus); + const hoverController = HoverController.get(editor); + if (!hoverController) { + return; + } + const index = args?.index !== undefined ? args.index : hoverController.focusedHoverPartIndex(); + HoverController.get(editor)?.updateHoverVerbosityLevel(HoverVerbosityAction.Decrease, index, args?.focus); } } diff --git a/src/vs/editor/contrib/hover/browser/hoverController.ts b/src/vs/editor/contrib/hover/browser/hoverController.ts index 293902db46d..9dbae0ce643 100644 --- a/src/vs/editor/contrib/hover/browser/hoverController.ts +++ b/src/vs/editor/contrib/hover/browser/hoverController.ts @@ -417,20 +417,16 @@ export class HoverController extends Disposable implements IEditorContribution { return this._contentWidget?.widget.isResizing || false; } - public focusedMarkdownHoverIndex(): number { - return this._getOrCreateContentWidget().focusedMarkdownHoverIndex(); + public focusedHoverPartIndex(): number { + return this._getOrCreateContentWidget().focusedHoverPartIndex(); } - public markdownHoverContentAtIndex(index: number): string { - return this._getOrCreateContentWidget().markdownHoverContentAtIndex(index); + public doesHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { + return this._getOrCreateContentWidget().doesHoverAtIndexSupportVerbosityAction(index, action); } - public doesMarkdownHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { - return this._getOrCreateContentWidget().doesMarkdownHoverAtIndexSupportVerbosityAction(index, action); - } - - public updateMarkdownHoverVerbosityLevel(action: HoverVerbosityAction, index?: number, focus?: boolean): void { - this._getOrCreateContentWidget().updateMarkdownHoverVerbosityLevel(action, index, focus); + public updateHoverVerbosityLevel(action: HoverVerbosityAction, index: number, focus?: boolean): void { + this._getOrCreateContentWidget().updateHoverVerbosityLevel(action, index, focus); } public focus(): void { @@ -473,6 +469,14 @@ export class HoverController extends Disposable implements IEditorContribution { return this._contentWidget?.getWidgetContent(); } + public getAccessibleWidgetContent(): string | undefined { + return this._contentWidget?.getAccessibleWidgetContent(); + } + + public getAccessibleWidgetContentAtIndex(index: number): string | undefined { + return this._contentWidget?.getAccessibleWidgetContentAtIndex(index); + } + public get isColorPickerVisible(): boolean | undefined { return this._contentWidget?.isColorPickerVisible; } diff --git a/src/vs/editor/contrib/hover/browser/hoverTypes.ts b/src/vs/editor/contrib/hover/browser/hoverTypes.ts index 0caaba7f030..4b23a838478 100644 --- a/src/vs/editor/contrib/hover/browser/hoverTypes.ts +++ b/src/vs/editor/contrib/hover/browser/hoverTypes.ts @@ -120,13 +120,46 @@ export interface IEditorHoverRenderContext extends IEditorHoverContext { readonly statusBar: IEditorHoverStatusBar; } +export interface IRenderedHoverPart extends IDisposable { + /** + * The rendered hover part. + */ + hoverPart: T; + /** + * The HTML element containing the hover part. + */ + hoverElement: HTMLElement; +} + +export interface IRenderedHoverParts extends IDisposable { + /** + * Array of rendered hover parts. + */ + renderedHoverParts: IRenderedHoverPart[]; +} + +/** + * Default implementation of IRenderedHoverParts. + */ +export class RenderedHoverParts implements IRenderedHoverParts { + + constructor(public readonly renderedHoverParts: IRenderedHoverPart[]) { } + + dispose() { + for (const part of this.renderedHoverParts) { + part.dispose(); + } + } +} + export interface IEditorHoverParticipant { readonly hoverOrdinal: number; suggestHoverAnchor?(mouseEvent: IEditorMouseEvent): HoverAnchor | null; computeSync(anchor: HoverAnchor, lineDecorations: IModelDecoration[]): T[]; computeAsync?(anchor: HoverAnchor, lineDecorations: IModelDecoration[], token: CancellationToken): AsyncIterableObject; createLoadingMessage?(anchor: HoverAnchor): T | null; - renderHoverParts(context: IEditorHoverRenderContext, hoverParts: T[]): IDisposable; + renderHoverParts(context: IEditorHoverRenderContext, hoverParts: T[]): IRenderedHoverParts; + getAccessibleContent(hoverPart: T): string; handleResize?(): void; } diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index 7bc9ae9985e..86626b4bf69 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { asArray, compareBy, numberComparator } from 'vs/base/common/arrays'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IMarkdownString, isEmptyMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; import { DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID } from 'vs/editor/contrib/hover/browser/hoverActionIds'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -15,7 +15,7 @@ import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IModelDecoration, ITextModel } from 'vs/editor/common/model'; import { ILanguageService } from 'vs/editor/common/languages/language'; -import { HoverAnchor, HoverAnchorType, HoverRangeAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { HoverAnchor, HoverAnchorType, HoverRangeAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from 'vs/editor/contrib/hover/browser/hoverTypes'; import * as nls from 'vs/nls'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -33,6 +33,7 @@ import { IHoverService, WorkbenchHoverDelegate } from 'vs/platform/hover/browser import { AsyncIterableObject } from 'vs/base/common/async'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; import { getHoverProviderResultsAsAsyncIterable } from 'vs/editor/contrib/hover/browser/getHover'; +import { ICommandService } from 'vs/platform/commands/common/commands'; const $ = dom.$; const increaseHoverVerbosityIcon = registerIcon('hover-increase-verbosity', Codicon.add, nls.localize('increaseHoverVerbosity', 'Icon for increaseing hover verbosity.')); @@ -90,6 +91,7 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant { this._renderedHoverParts = new MarkdownRenderedHoverParts( hoverParts, context.fragment, + this, this._editor, this._languageService, this._openerService, + this._commandService, this._keybindingService, this._hoverService, this._configurationService, @@ -191,55 +195,65 @@ export class MarkdownHoverParticipant implements IEditorHoverParticipant { + return Promise.resolve(this._renderedHoverParts?.updateMarkdownHoverPartVerbosityLevel(action, index, focus)); } } -interface RenderedHoverPart { - renderedMarkdown: HTMLElement; - disposables: DisposableStore; - hoverSource?: HoverSource; +class RenderedMarkdownHoverPart implements IRenderedHoverPart { + + constructor( + public readonly hoverPart: MarkdownHover, + public readonly hoverElement: HTMLElement, + public readonly disposables: DisposableStore, + ) { } + + get hoverAccessibleContent(): string { + return this.hoverElement.innerText.trim(); + } + + dispose(): void { + this.disposables.dispose(); + } } -class MarkdownRenderedHoverParts extends Disposable { +class MarkdownRenderedHoverParts implements IRenderedHoverParts { + + public renderedHoverParts: RenderedMarkdownHoverPart[]; - private _renderedHoverParts: RenderedHoverPart[]; - private _focusedHoverPartIndex: number = -1; private _ongoingHoverOperations: Map = new Map(); + private readonly _disposables = new DisposableStore(); + constructor( - hoverParts: MarkdownHover[], // we own! + hoverParts: MarkdownHover[], hoverPartsContainer: DocumentFragment, + private readonly _hoverParticipant: MarkdownHoverParticipant, private readonly _editor: ICodeEditor, private readonly _languageService: ILanguageService, private readonly _openerService: IOpenerService, + private readonly _commandService: ICommandService, private readonly _keybindingService: IKeybindingService, private readonly _hoverService: IHoverService, private readonly _configurationService: IConfigurationService, private readonly _onFinishedRendering: () => void, ) { - super(); - this._renderedHoverParts = this._renderHoverParts(hoverParts, hoverPartsContainer, this._onFinishedRendering); - this._register(toDisposable(() => { - this._renderedHoverParts.forEach(renderedHoverPart => { - renderedHoverPart.disposables.dispose(); + this.renderedHoverParts = this._renderHoverParts(hoverParts, hoverPartsContainer, this._onFinishedRendering); + this._disposables.add(toDisposable(() => { + this.renderedHoverParts.forEach(renderedHoverPart => { + renderedHoverPart.dispose(); + }); + this._ongoingHoverOperations.forEach(operation => { + operation.tokenSource.dispose(true); }); - })); - this._register(toDisposable(() => { - this._ongoingHoverOperations.forEach(operation => { operation.tokenSource.dispose(true); }); })); } @@ -247,75 +261,57 @@ class MarkdownRenderedHoverParts extends Disposable { hoverParts: MarkdownHover[], hoverPartsContainer: DocumentFragment, onFinishedRendering: () => void, - ): RenderedHoverPart[] { + ): RenderedMarkdownHoverPart[] { hoverParts.sort(compareBy(hover => hover.ordinal, numberComparator)); - return hoverParts.map((hoverPart, hoverIndex) => { - const renderedHoverPart = this._renderHoverPart( - hoverIndex, - hoverPart.contents, - hoverPart.source, - onFinishedRendering - ); - hoverPartsContainer.appendChild(renderedHoverPart.renderedMarkdown); + return hoverParts.map(hoverPart => { + const renderedHoverPart = this._renderHoverPart(hoverPart, onFinishedRendering); + hoverPartsContainer.appendChild(renderedHoverPart.hoverElement); return renderedHoverPart; }); } private _renderHoverPart( - hoverPartIndex: number, - hoverContents: IMarkdownString[], - hoverSource: HoverSource | undefined, + hoverPart: MarkdownHover, onFinishedRendering: () => void - ): RenderedHoverPart { + ): RenderedMarkdownHoverPart { - const { renderedMarkdown, disposables } = this._renderMarkdownContent(hoverContents, onFinishedRendering); + const renderedMarkdownPart = this._renderMarkdownHover(hoverPart, onFinishedRendering); + const renderedMarkdownElement = renderedMarkdownPart.hoverElement; + const hoverSource = hoverPart.source; + const disposables = new DisposableStore(); + disposables.add(renderedMarkdownPart); if (!hoverSource) { - return { renderedMarkdown, disposables }; + return new RenderedMarkdownHoverPart(hoverPart, renderedMarkdownElement, disposables); } const canIncreaseVerbosity = hoverSource.supportsVerbosityAction(HoverVerbosityAction.Increase); const canDecreaseVerbosity = hoverSource.supportsVerbosityAction(HoverVerbosityAction.Decrease); if (!canIncreaseVerbosity && !canDecreaseVerbosity) { - return { renderedMarkdown, disposables, hoverSource }; + return new RenderedMarkdownHoverPart(hoverPart, renderedMarkdownElement, disposables); } const actionsContainer = $('div.verbosity-actions'); - renderedMarkdown.prepend(actionsContainer); + renderedMarkdownElement.prepend(actionsContainer); disposables.add(this._renderHoverExpansionAction(actionsContainer, HoverVerbosityAction.Increase, canIncreaseVerbosity)); disposables.add(this._renderHoverExpansionAction(actionsContainer, HoverVerbosityAction.Decrease, canDecreaseVerbosity)); - - this._register(dom.addDisposableListener(renderedMarkdown, dom.EventType.FOCUS_IN, (event: Event) => { - event.stopPropagation(); - this._focusedHoverPartIndex = hoverPartIndex; - })); - this._register(dom.addDisposableListener(renderedMarkdown, dom.EventType.FOCUS_OUT, (event: Event) => { - event.stopPropagation(); - this._focusedHoverPartIndex = -1; - })); - return { renderedMarkdown, disposables, hoverSource }; + return new RenderedMarkdownHoverPart(hoverPart, renderedMarkdownElement, disposables); } - private _renderMarkdownContent( - markdownContent: IMarkdownString[], + private _renderMarkdownHover( + markdownHover: MarkdownHover, onFinishedRendering: () => void - ): RenderedHoverPart { - const renderedMarkdown = $('div.hover-row'); - renderedMarkdown.tabIndex = 0; - const renderedMarkdownContents = $('div.hover-row-contents'); - renderedMarkdown.appendChild(renderedMarkdownContents); - const disposables = new DisposableStore(); - disposables.add(renderMarkdownInContainer( + ): IRenderedHoverPart { + const renderedMarkdownHover = renderMarkdownInContainer( this._editor, - renderedMarkdownContents, - markdownContent, + markdownHover, this._languageService, this._openerService, onFinishedRendering, - )); - return { renderedMarkdown, disposables }; + ); + return renderedMarkdownHover; } private _renderHoverExpansionAction(container: HTMLElement, action: HoverVerbosityAction, actionEnabled: boolean): DisposableStore { @@ -330,53 +326,67 @@ class MarkdownRenderedHoverParts extends Disposable { return store; } actionElement.classList.add('enabled'); - const actionFunction = () => this.updateMarkdownHoverPartVerbosityLevel(action); + const actionFunction = () => this._commandService.executeCommand(action === HoverVerbosityAction.Increase ? INCREASE_HOVER_VERBOSITY_ACTION_ID : DECREASE_HOVER_VERBOSITY_ACTION_ID); store.add(new ClickAction(actionElement, actionFunction)); store.add(new KeyDownAction(actionElement, actionFunction, [KeyCode.Enter, KeyCode.Space])); return store; } - public async updateMarkdownHoverPartVerbosityLevel(action: HoverVerbosityAction, index: number = -1, focus: boolean = true): Promise { + public async updateMarkdownHoverPartVerbosityLevel(action: HoverVerbosityAction, index: number, focus: boolean = true): Promise<{ hoverPart: MarkdownHover; hoverElement: HTMLElement } | undefined> { const model = this._editor.getModel(); if (!model) { - return; + return undefined; } - const indexOfInterest = index !== -1 ? index : this._focusedHoverPartIndex; - const hoverRenderedPart = this._getRenderedHoverPartAtIndex(indexOfInterest); - if (!hoverRenderedPart || !hoverRenderedPart.hoverSource?.supportsVerbosityAction(action)) { - return; + const hoverRenderedPart = this._getRenderedHoverPartAtIndex(index); + const hoverSource = hoverRenderedPart?.hoverPart.source; + if (!hoverRenderedPart || !hoverSource?.supportsVerbosityAction(action)) { + return undefined; } - const hoverSource = hoverRenderedPart.hoverSource; const newHover = await this._fetchHover(hoverSource, model, action); if (!newHover) { - return; + return undefined; } const newHoverSource = new HoverSource(newHover, hoverSource.hoverProvider, hoverSource.hoverPosition); - const newHoverRenderedPart = this._renderHoverPart( - indexOfInterest, + const initialHoverPart = hoverRenderedPart.hoverPart; + const newHoverPart = new MarkdownHover( + this._hoverParticipant, + initialHoverPart.range, newHover.contents, - newHoverSource, + initialHoverPart.isBeforeContent, + initialHoverPart.ordinal, + newHoverSource + ); + const newHoverRenderedPart = this._renderHoverPart( + newHoverPart, this._onFinishedRendering ); - this._replaceRenderedHoverPartAtIndex(indexOfInterest, newHoverRenderedPart); + this._replaceRenderedHoverPartAtIndex(index, newHoverRenderedPart, newHoverPart); if (focus) { - this._focusOnHoverPartWithIndex(indexOfInterest); + this._focusOnHoverPartWithIndex(index); } - this._onFinishedRendering(); - } - - public markdownHoverContentAtIndex(index: number): string { - const hoverRenderedPart = this._getRenderedHoverPartAtIndex(index); - return hoverRenderedPart?.renderedMarkdown.innerText ?? ''; + return { + hoverPart: newHoverPart, + hoverElement: newHoverRenderedPart.hoverElement + }; } - public focusedMarkdownHoverIndex(): number { - return this._focusedHoverPartIndex; + public getAccessibleContent(hoverPart: MarkdownHover): string | undefined { + const renderedHoverPartIndex = this.renderedHoverParts.findIndex(renderedHoverPart => renderedHoverPart.hoverPart === hoverPart); + if (renderedHoverPartIndex === -1) { + return undefined; + } + const renderedHoverPart = this._getRenderedHoverPartAtIndex(renderedHoverPartIndex); + if (!renderedHoverPart) { + return undefined; + } + const accessibleContent = renderedHoverPart.hoverElement.innerText.trim(); + return accessibleContent; } public doesMarkdownHoverAtIndexSupportVerbosityAction(index: number, action: HoverVerbosityAction): boolean { const hoverRenderedPart = this._getRenderedHoverPartAtIndex(index); - if (!hoverRenderedPart || !hoverRenderedPart.hoverSource?.supportsVerbosityAction(action)) { + const hoverSource = hoverRenderedPart?.hoverPart.source; + if (!hoverRenderedPart || !hoverSource?.supportsVerbosityAction(action)) { return false; } return true; @@ -404,76 +414,94 @@ class MarkdownRenderedHoverParts extends Disposable { return hover; } - private _replaceRenderedHoverPartAtIndex(index: number, renderedHoverPart: RenderedHoverPart): void { - if (index >= this._renderHoverParts.length || index < 0) { + private _replaceRenderedHoverPartAtIndex(index: number, renderedHoverPart: RenderedMarkdownHoverPart, hoverPart: MarkdownHover): void { + if (index >= this.renderedHoverParts.length || index < 0) { return; } - const currentRenderedHoverPart = this._renderedHoverParts[index]; - const currentRenderedMarkdown = currentRenderedHoverPart.renderedMarkdown; - currentRenderedMarkdown.replaceWith(renderedHoverPart.renderedMarkdown); - currentRenderedHoverPart.disposables.dispose(); - this._renderedHoverParts[index] = renderedHoverPart; + const currentRenderedHoverPart = this.renderedHoverParts[index]; + const currentRenderedMarkdown = currentRenderedHoverPart.hoverElement; + const renderedMarkdown = renderedHoverPart.hoverElement; + const renderedChildrenElements = Array.from(renderedMarkdown.children); + currentRenderedMarkdown.replaceChildren(...renderedChildrenElements); + const newRenderedHoverPart = new RenderedMarkdownHoverPart( + hoverPart, + currentRenderedMarkdown, + renderedHoverPart.disposables + ); + currentRenderedMarkdown.focus(); + currentRenderedHoverPart.dispose(); + this.renderedHoverParts[index] = newRenderedHoverPart; } private _focusOnHoverPartWithIndex(index: number): void { - this._renderedHoverParts[index].renderedMarkdown.focus(); + this.renderedHoverParts[index].hoverElement.focus(); + } + + private _getRenderedHoverPartAtIndex(index: number): RenderedMarkdownHoverPart | undefined { + return this.renderedHoverParts[index]; } - private _getRenderedHoverPartAtIndex(index: number): RenderedHoverPart | undefined { - return this._renderedHoverParts[index]; + public dispose(): void { + this._disposables.dispose(); } } export function renderMarkdownHovers( context: IEditorHoverRenderContext, - hoverParts: MarkdownHover[], + markdownHovers: MarkdownHover[], editor: ICodeEditor, languageService: ILanguageService, openerService: IOpenerService, -): IDisposable { +): IRenderedHoverParts { // Sort hover parts to keep them stable since they might come in async, out-of-order - hoverParts.sort(compareBy(hover => hover.ordinal, numberComparator)); - - const disposables = new DisposableStore(); - for (const hoverPart of hoverParts) { - disposables.add(renderMarkdownInContainer( + markdownHovers.sort(compareBy(hover => hover.ordinal, numberComparator)); + const renderedHoverParts: IRenderedHoverPart[] = []; + for (const markdownHover of markdownHovers) { + renderedHoverParts.push(renderMarkdownInContainer( editor, - context.fragment, - hoverPart.contents, + markdownHover, languageService, openerService, context.onContentsChanged, )); } - return disposables; + return new RenderedHoverParts(renderedHoverParts); } function renderMarkdownInContainer( editor: ICodeEditor, - container: DocumentFragment | HTMLElement, - markdownStrings: IMarkdownString[], + markdownHover: MarkdownHover, languageService: ILanguageService, openerService: IOpenerService, onFinishedRendering: () => void, -): IDisposable { - const store = new DisposableStore(); - for (const contents of markdownStrings) { - if (isEmptyMarkdownString(contents)) { +): IRenderedHoverPart { + const disposables = new DisposableStore(); + const renderedMarkdown = $('div.hover-row'); + const renderedMarkdownContents = $('div.hover-row-contents'); + renderedMarkdown.appendChild(renderedMarkdownContents); + const markdownStrings = markdownHover.contents; + for (const markdownString of markdownStrings) { + if (isEmptyMarkdownString(markdownString)) { continue; } const markdownHoverElement = $('div.markdown-hover'); const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents')); - const renderer = store.add(new MarkdownRenderer({ editor }, languageService, openerService)); - store.add(renderer.onDidRenderAsync(() => { + const renderer = disposables.add(new MarkdownRenderer({ editor }, languageService, openerService)); + disposables.add(renderer.onDidRenderAsync(() => { hoverContentsElement.className = 'hover-contents code-hover-contents'; onFinishedRendering(); })); - const renderedContents = store.add(renderer.render(contents)); + const renderedContents = disposables.add(renderer.render(markdownString)); hoverContentsElement.appendChild(renderedContents.element); - container.appendChild(markdownHoverElement); + renderedMarkdownContents.appendChild(markdownHoverElement); } - return store; + const renderedHoverPart: IRenderedHoverPart = { + hoverPart: markdownHover, + hoverElement: renderedMarkdown, + dispose() { disposables.dispose(); } + }; + return renderedHoverPart; } export function labelForHoverVerbosityAction(keybindingService: IKeybindingService, action: HoverVerbosityAction): string { diff --git a/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts index 3ed0b3fab14..86dba6a80e3 100644 --- a/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markerHoverParticipant.ts @@ -7,7 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { CancelablePromise, createCancelablePromise, disposableTimeout } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { basename } from 'vs/base/common/resources'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; @@ -20,7 +20,7 @@ import { getCodeActions, quickFixCommandId } from 'vs/editor/contrib/codeAction/ import { CodeActionController } from 'vs/editor/contrib/codeAction/browser/codeActionController'; import { CodeActionKind, CodeActionSet, CodeActionTrigger, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/common/types'; import { MarkerController, NextMarkerAction } from 'vs/editor/contrib/gotoError/browser/gotoError'; -import { HoverAnchor, HoverAnchorType, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { HoverAnchor, HoverAnchorType, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from 'vs/editor/contrib/hover/browser/hoverTypes'; import * as nls from 'vs/nls'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IMarker, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers'; @@ -90,20 +90,29 @@ export class MarkerHoverParticipant implements IEditorHoverParticipant { if (!hoverParts.length) { - return Disposable.None; + return new RenderedHoverParts([]); } const disposables = new DisposableStore(); - hoverParts.forEach(msg => context.fragment.appendChild(this.renderMarkerHover(msg, disposables))); + const renderedHoverParts: IRenderedHoverPart[] = []; + hoverParts.forEach(hoverPart => { + const renderedMarkerHover = this._renderMarkerHover(hoverPart); + context.fragment.appendChild(renderedMarkerHover.hoverElement); + renderedHoverParts.push(renderedMarkerHover); + }); const markerHoverForStatusbar = hoverParts.length === 1 ? hoverParts[0] : hoverParts.sort((a, b) => MarkerSeverity.compare(a.marker.severity, b.marker.severity))[0]; this.renderMarkerStatusbar(context, markerHoverForStatusbar, disposables); - return disposables; + return new RenderedHoverParts(renderedHoverParts); + } + + public getAccessibleContent(hoverPart: MarkerHover): string { + return hoverPart.marker.message; } - private renderMarkerHover(markerHover: MarkerHover, disposables: DisposableStore): HTMLElement { + private _renderMarkerHover(markerHover: MarkerHover): IRenderedHoverPart { + const disposables: DisposableStore = new DisposableStore(); const hoverElement = $('div.hover-row'); - hoverElement.tabIndex = 0; const markerElement = dom.append(hoverElement, $('div.marker.hover-contents')); const { source, message, code, relatedInformation } = markerHover.marker; @@ -166,7 +175,12 @@ export class MarkerHoverParticipant implements IEditorHoverParticipant = { + hoverPart: markerHover, + hoverElement, + dispose: () => disposables.dispose() + }; + return renderedHoverPart; } private renderMarkerStatusbar(context: IEditorHoverRenderContext, markerHover: MarkerHover, disposables: DisposableStore): void { diff --git a/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts b/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts index d49a4bb781f..05703e108d1 100644 --- a/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts +++ b/src/vs/editor/contrib/inlayHints/browser/inlayHintsHover.ts @@ -26,6 +26,7 @@ import { asCommandLink } from 'vs/editor/contrib/inlayHints/browser/inlayHints'; import { isNonEmptyArray } from 'vs/base/common/arrays'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { ICommandService } from 'vs/platform/commands/common/commands'; class InlayHintsHoverAnchor extends HoverForeignElementAnchor { constructor( @@ -51,8 +52,9 @@ export class InlayHintsHover extends MarkdownHoverParticipant implements IEditor @IConfigurationService configurationService: IConfigurationService, @ITextModelService private readonly _resolverService: ITextModelService, @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, + @ICommandService commandService: ICommandService ) { - super(editor, languageService, openerService, configurationService, languageFeaturesService, keybindingService, hoverService); + super(editor, languageService, openerService, configurationService, languageFeaturesService, keybindingService, hoverService, commandService); } suggestHoverAnchor(mouseEvent: IEditorMouseEvent): HoverAnchor | null { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/hoverParticipant.ts b/src/vs/editor/contrib/inlineCompletions/browser/hoverParticipant.ts index 90d4ffcba0c..7bcf0ea59f1 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/hoverParticipant.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/hoverParticipant.ts @@ -12,7 +12,7 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { IModelDecoration } from 'vs/editor/common/model'; -import { HoverAnchor, HoverAnchorType, HoverForeignElementAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { HoverAnchor, HoverAnchorType, HoverForeignElementAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; import { InlineSuggestionHintsContentWidget } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsHintsWidget'; import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; @@ -94,8 +94,8 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan return []; } - renderHoverParts(context: IEditorHoverRenderContext, hoverParts: InlineCompletionsHover[]): IDisposable { - const disposableStore = new DisposableStore(); + renderHoverParts(context: IEditorHoverRenderContext, hoverParts: InlineCompletionsHover[]): IRenderedHoverParts { + const disposables = new DisposableStore(); const part = hoverParts[0]; this._telemetryService.publicLog2<{}, { @@ -104,7 +104,7 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan }>('inlineCompletionHover.shown'); if (this.accessibilityService.isScreenReaderOptimized() && !this._editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) { - this.renderScreenReaderText(context, part, disposableStore); + disposables.add(this.renderScreenReaderText(context, part)); } const model = part.controller.model.get()!; @@ -115,32 +115,42 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan model.inlineCompletionsCount, model.activeCommands, ); - context.fragment.appendChild(w.getDomNode()); + const widgetNode: HTMLElement = w.getDomNode(); + context.fragment.appendChild(widgetNode); model.triggerExplicitly(); - disposableStore.add(w); + disposables.add(w); + const renderedHoverPart: IRenderedHoverPart = { + hoverPart: part, + hoverElement: widgetNode, + dispose() { disposables.dispose(); } + }; + return new RenderedHoverParts([renderedHoverPart]); + } - return disposableStore; + getAccessibleContent(hoverPart: InlineCompletionsHover): string { + return nls.localize('hoverAccessibilityStatusBar', 'There are inline completions here'); } - private renderScreenReaderText(context: IEditorHoverRenderContext, part: InlineCompletionsHover, disposableStore: DisposableStore) { + private renderScreenReaderText(context: IEditorHoverRenderContext, part: InlineCompletionsHover): IDisposable { + const disposables = new DisposableStore(); const $ = dom.$; const markdownHoverElement = $('div.hover-row.markdown-hover'); const hoverContentsElement = dom.append(markdownHoverElement, $('div.hover-contents', { ['aria-live']: 'assertive' })); - const renderer = disposableStore.add(new MarkdownRenderer({ editor: this._editor }, this._languageService, this._openerService)); + const renderer = disposables.add(new MarkdownRenderer({ editor: this._editor }, this._languageService, this._openerService)); const render = (code: string) => { - disposableStore.add(renderer.onDidRenderAsync(() => { + disposables.add(renderer.onDidRenderAsync(() => { hoverContentsElement.className = 'hover-contents code-hover-contents'; context.onContentsChanged(); })); const inlineSuggestionAvailable = nls.localize('inlineSuggestionFollows', "Suggestion:"); - const renderedContents = disposableStore.add(renderer.render(new MarkdownString().appendText(inlineSuggestionAvailable).appendCodeblock('text', code))); + const renderedContents = disposables.add(renderer.render(new MarkdownString().appendText(inlineSuggestionAvailable).appendCodeblock('text', code))); hoverContentsElement.replaceChildren(renderedContents.element); }; - disposableStore.add(autorun(reader => { + disposables.add(autorun(reader => { /** @description update hover */ const ghostText = part.controller.model.read(reader)?.primaryGhostText.read(reader); if (ghostText) { @@ -152,5 +162,6 @@ export class InlineCompletionsHoverParticipant implements IEditorHoverParticipan })); context.fragment.appendChild(markdownHoverElement); + return disposables; } } diff --git a/src/vs/editor/contrib/inlineEdit/browser/hoverParticipant.ts b/src/vs/editor/contrib/inlineEdit/browser/hoverParticipant.ts index 6c1c7337f7f..cfacc77329a 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/hoverParticipant.ts +++ b/src/vs/editor/contrib/inlineEdit/browser/hoverParticipant.ts @@ -3,17 +3,18 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { constObservable } from 'vs/base/common/observable'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { IModelDecoration } from 'vs/editor/common/model'; -import { HoverAnchor, HoverAnchorType, HoverForeignElementAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { HoverAnchor, HoverAnchorType, HoverForeignElementAnchor, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { InlineEditController } from 'vs/editor/contrib/inlineEdit/browser/inlineEditController'; import { InlineEditHintsContentWidget } from 'vs/editor/contrib/inlineEdit/browser/inlineEditHintsWidget'; +import * as nls from 'vs/nls'; export class InlineEditHover implements IHoverPart { constructor( @@ -86,8 +87,8 @@ export class InlineEditHoverParticipant implements IEditorHoverParticipant { + const disposables = new DisposableStore(); this._telemetryService.publicLog2<{}, { owner: 'hediet'; @@ -97,9 +98,17 @@ export class InlineEditHoverParticipant implements IEditorHoverParticipant = { + hoverPart: hoverParts[0], + hoverElement: widgetNode, + dispose: () => disposables.dispose() + }; + return new RenderedHoverParts([renderedHoverPart]); + } - return disposableStore; + getAccessibleContent(hoverPart: InlineEditHover): string { + return nls.localize('hoverAccessibilityInlineEdits', 'There are inline edits here.'); } } diff --git a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts index f44fc76e0bd..eae47ac2d71 100644 --- a/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts +++ b/src/vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts @@ -7,7 +7,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { CharCode } from 'vs/base/common/charCode'; import { Codicon } from 'vs/base/common/codicons'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; import { InvisibleCharacters, isBasicASCII } from 'vs/base/common/strings'; import 'vs/css!./unicodeHighlighter'; @@ -22,7 +22,7 @@ import { UnicodeHighlighterOptions, UnicodeHighlighterReason, UnicodeHighlighter import { IEditorWorkerService, IUnicodeHighlightsResult } from 'vs/editor/common/services/editorWorker'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { isModelDecorationInComment, isModelDecorationInString, isModelDecorationVisible } from 'vs/editor/common/viewModel/viewModelDecorations'; -import { HoverAnchor, HoverAnchorType, HoverParticipantRegistry, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { HoverAnchor, HoverAnchorType, HoverParticipantRegistry, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverParts } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { MarkdownHover, renderMarkdownHovers } from 'vs/editor/contrib/hover/browser/markdownHoverParticipant'; import { BannerController } from 'vs/editor/contrib/unicodeHighlighter/browser/bannerController'; import * as nls from 'vs/nls'; @@ -506,9 +506,13 @@ export class UnicodeHighlighterHoverParticipant implements IEditorHoverParticipa return result; } - public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: MarkdownHover[]): IDisposable { + public renderHoverParts(context: IEditorHoverRenderContext, hoverParts: MarkdownHover[]): IRenderedHoverParts { return renderMarkdownHovers(context, hoverParts, this._editor, this._languageService, this._openerService); } + + public getAccessibleContent(hoverPart: MarkdownHover): string { + return hoverPart.contents.map(c => c.value).join('\n'); + } } function codePointToHex(codePoint: number): string { diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts index 2ad7122eb92..893353b52f1 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorHover.ts @@ -3,11 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, DisposableStore, IDisposable } from 'vs/base/common/lifecycle'; +import { DisposableStore } from 'vs/base/common/lifecycle'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { Range } from 'vs/editor/common/core/range'; import { IModelDecoration } from 'vs/editor/common/model'; -import { HoverAnchor, HoverAnchorType, HoverParticipantRegistry, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart } from 'vs/editor/contrib/hover/browser/hoverTypes'; +import { HoverAnchor, HoverAnchorType, HoverParticipantRegistry, IEditorHoverParticipant, IEditorHoverRenderContext, IHoverPart, IRenderedHoverPart, IRenderedHoverParts, RenderedHoverParts } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; @@ -15,6 +15,7 @@ import { ChatAgentHover, getChatAgentHoverOptions } from 'vs/workbench/contrib/c import { ChatEditorHoverWrapper } from 'vs/workbench/contrib/chat/browser/contrib/editorHoverWrapper'; import { IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents'; import { extractAgentAndCommand } from 'vs/workbench/contrib/chat/common/chatParserTypes'; +import * as nls from 'vs/nls'; export class ChatAgentHoverParticipant implements IEditorHoverParticipant { @@ -49,22 +50,33 @@ export class ChatAgentHoverParticipant implements IEditorHoverParticipant { if (!hoverParts.length) { - return Disposable.None; + return new RenderedHoverParts([]); } - const store = new DisposableStore(); - const hover = store.add(this.instantiationService.createInstance(ChatAgentHover)); - store.add(hover.onDidChangeContents(() => context.onContentsChanged())); - const agent = hoverParts[0].agent; + const disposables = new DisposableStore(); + const hover = disposables.add(this.instantiationService.createInstance(ChatAgentHover)); + disposables.add(hover.onDidChangeContents(() => context.onContentsChanged())); + const hoverPart = hoverParts[0]; + const agent = hoverPart.agent; hover.setAgent(agent.id); const actions = getChatAgentHoverOptions(() => agent, this.commandService).actions; const wrapper = this.instantiationService.createInstance(ChatEditorHoverWrapper, hover.domNode, actions); - context.fragment.appendChild(wrapper.domNode); + const wrapperNode = wrapper.domNode; + context.fragment.appendChild(wrapperNode); + const renderedHoverPart: IRenderedHoverPart = { + hoverPart, + hoverElement: wrapperNode, + dispose() { disposables.dispose(); } + }; + return new RenderedHoverParts([renderedHoverPart]); + } + + public getAccessibleContent(hoverPart: ChatAgentHoverPart): string { + return nls.localize('hoverAccessibilityChatAgent', 'There is a chat agent hover part here.'); - return store; } } From c1bf420dbaa4f325b524a71092d0d20eadb2d27e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 24 Jun 2024 15:48:49 +0200 Subject: [PATCH 0074/2222] fix #213840 (#217189) --- .../browser/preferences.contribution.ts | 1 + .../services/views/browser/viewsService.ts | 20 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 66ca47c40e5..ccf4663736c 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -1246,6 +1246,7 @@ class SettingsEditorTitleContribution extends Disposable implements IWorkbenchCo ResourceContextKey.Resource.isEqualTo(this.userDataProfilesService.defaultProfile.settingsResource.toString())), ContextKeyExpr.not('isInDiffEditor')); const registerOpenUserSettingsEditorFromJsonAction = () => { + registerOpenUserSettingsEditorFromJsonActionDisposables.value = undefined; registerOpenUserSettingsEditorFromJsonActionDisposables.value = registerAction2(class extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/services/views/browser/viewsService.ts b/src/vs/workbench/services/views/browser/viewsService.ts index c342e91da15..0a7d3a2f760 100644 --- a/src/vs/workbench/services/views/browser/viewsService.ts +++ b/src/vs/workbench/services/views/browser/viewsService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, IDisposable, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, IDisposable, toDisposable, DisposableStore, DisposableMap } from 'vs/base/common/lifecycle'; import { IViewDescriptorService, ViewContainer, IViewDescriptor, IView, ViewContainerLocation, IViewPaneContainer } from 'vs/workbench/common/views'; import { FocusedViewContext, getVisbileViewContextKey } from 'vs/workbench/common/contextkeys'; import { Registry } from 'vs/platform/registry/common/platform'; @@ -51,6 +51,7 @@ export class ViewsService extends Disposable implements IViewsService { private readonly _onDidChangeFocusedView = this._register(new Emitter()); readonly onDidChangeFocusedView = this._onDidChangeFocusedView.event; + private readonly viewContainerDisposables = this._register(new DisposableMap()); private readonly enabledViewContainersContextKeys: Map>; private readonly visibleViewContextKeys: Map>; private readonly focusedViewContextKey: IContextKey; @@ -114,7 +115,7 @@ export class ViewsService extends Disposable implements IViewsService { private onDidChangeContainers(added: ReadonlyArray<{ container: ViewContainer; location: ViewContainerLocation }>, removed: ReadonlyArray<{ container: ViewContainer; location: ViewContainerLocation }>): void { for (const { container, location } of removed) { - this.deregisterPaneComposite(container, location); + this.onDidDeregisterViewContainer(container, location); } for (const { container, location } of added) { this.onDidRegisterViewContainer(container, location); @@ -123,15 +124,24 @@ export class ViewsService extends Disposable implements IViewsService { private onDidRegisterViewContainer(viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation): void { this.registerPaneComposite(viewContainer, viewContainerLocation); + const disposables = new DisposableStore(); + const viewContainerModel = this.viewDescriptorService.getViewContainerModel(viewContainer); this.onViewDescriptorsAdded(viewContainerModel.allViewDescriptors, viewContainer); - this._register(viewContainerModel.onDidChangeAllViewDescriptors(({ added, removed }) => { + disposables.add(viewContainerModel.onDidChangeAllViewDescriptors(({ added, removed }) => { this.onViewDescriptorsAdded(added, viewContainer); this.onViewDescriptorsRemoved(removed); })); this.updateViewContainerEnablementContextKey(viewContainer); - this._register(viewContainerModel.onDidChangeActiveViewDescriptors(() => this.updateViewContainerEnablementContextKey(viewContainer))); - this._register(this.registerOpenViewContainerAction(viewContainer)); + disposables.add(viewContainerModel.onDidChangeActiveViewDescriptors(() => this.updateViewContainerEnablementContextKey(viewContainer))); + disposables.add(this.registerOpenViewContainerAction(viewContainer)); + + this.viewContainerDisposables.set(viewContainer.id, disposables); + } + + private onDidDeregisterViewContainer(viewContainer: ViewContainer, viewContainerLocation: ViewContainerLocation): void { + this.deregisterPaneComposite(viewContainer, viewContainerLocation); + this.viewContainerDisposables.deleteAndDispose(viewContainer.id); } private onDidChangeContainerLocation(viewContainer: ViewContainer, from: ViewContainerLocation, to: ViewContainerLocation): void { From 65e55083107e40639a9b36916667603616988a4a Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 24 Jun 2024 16:11:05 +0200 Subject: [PATCH 0075/2222] Revert change to not listen on tree menus (#217170) Revert "Do not listen on tree menus (#216446)" This reverts commit ce7105088c927a86acf16e8fa378f349e5fee759. --- src/vs/base/browser/ui/tree/asyncDataTree.ts | 1 - .../workbench/browser/parts/views/treeView.ts | 30 +++++++------------ 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index ccfd84591a6..debb1a04685 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -345,7 +345,6 @@ export class AsyncDataTree implements IDisposable get onDidChangeSelection(): Event> { return Event.map(this.tree.onDidChangeSelection, asTreeEvent); } get onKeyDown(): Event { return this.tree.onKeyDown; } - get onMouseOver(): Event> { return Event.map(this.tree.onMouseOver, asTreeMouseEvent); } get onMouseClick(): Event> { return Event.map(this.tree.onMouseClick, asTreeMouseEvent); } get onMouseDblClick(): Event> { return Event.map(this.tree.onMouseDblClick, asTreeMouseEvent); } get onContextMenu(): Event> { return Event.map(this.tree.onContextMenu, asTreeContextMenuEvent); } diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 42a54190f0b..7c1b3619944 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -717,21 +717,6 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { dnd: this.treeViewDnd, overrideStyles: getLocationBasedViewColors(this.viewLocation).listOverrideStyles }) as WorkbenchAsyncDataTree); - this.treeDisposables.add(this.tree.onMouseOver(e => { - if (e.element) { - this.tree?.rerender(e.element); - } - })); - this.treeDisposables.add(this.tree.contextKeyService.onDidChangeContext(e => { - const selection = this.tree?.getSelection(); - if (selection?.length) { - selection.map(item => this.tree?.rerender(item)); - } - const focus = this.tree?.getFocus(); - if (focus?.length) { - focus.map(item => this.tree?.rerender(item)); - } - })); this.treeDisposables.add(this.tree); treeMenus.setContextKeyService(this.tree.contextKeyService); aligner.tree = this.tree; @@ -1354,7 +1339,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer this._onDidChange.fire(element))); + listen.add(menu); + } else { + menu.dispose(); + } } return { primary: this.buildMenu(primaryGroups), secondary: this.buildMenu(secondaryGroups) }; From 64e01374b338c95820f0547cec1eca78fc710363 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 24 Jun 2024 16:18:37 +0200 Subject: [PATCH 0076/2222] Add event handling for Home and End keys in FilterWidget (#217200) chore: Add event handling for Home and End keys in FilterWidget --- src/vs/workbench/browser/parts/views/viewFilter.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/browser/parts/views/viewFilter.ts b/src/vs/workbench/browser/parts/views/viewFilter.ts index 3331c892d0c..724a67b931a 100644 --- a/src/vs/workbench/browser/parts/views/viewFilter.ts +++ b/src/vs/workbench/browser/parts/views/viewFilter.ts @@ -230,6 +230,8 @@ export class FilterWidget extends Widget { if (event.equals(KeyCode.Space) || event.equals(KeyCode.LeftArrow) || event.equals(KeyCode.RightArrow) + || event.equals(KeyCode.Home) + || event.equals(KeyCode.End) ) { event.stopPropagation(); } From b94320d5a128d030992f2361430b19539a986b88 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 24 Jun 2024 16:31:33 +0200 Subject: [PATCH 0077/2222] add API proposal tracking link (#217201) --- src/vscode-dts/vscode.proposed.lmTools.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vscode-dts/vscode.proposed.lmTools.d.ts b/src/vscode-dts/vscode.proposed.lmTools.d.ts index 589d6d8fef1..facbeb20c4d 100644 --- a/src/vscode-dts/vscode.proposed.lmTools.d.ts +++ b/src/vscode-dts/vscode.proposed.lmTools.d.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ // version: 2 +// https://github.com/microsoft/vscode/issues/213274 declare module 'vscode' { From 93a7382ecd63439a5bc507ef60e57610845ec05d Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 24 Jun 2024 17:13:10 +0200 Subject: [PATCH 0078/2222] report file operation result code (#217211) --- .../node/extensionManagementService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 1444a63134c..29613dba832 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -488,7 +488,7 @@ type UpdateMetadataErrorClassification = { owner: 'sandy081'; comment: 'Update metadata error'; extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'extension identifier' }; - code?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'result code of the verification' }; + code?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'error code' }; }; type UpdateMetadataErrorEvent = { extensionId: string; @@ -594,7 +594,7 @@ export class ExtensionsScanner extends Disposable { try { await this.extensionsScannerService.updateMetadata(extensionLocation, metadata); } catch (error) { - this.telemetryService.publicLog2('extension:extract', { extensionId: extensionKey.id, code: error.code }); + this.telemetryService.publicLog2('extension:extract', { extensionId: extensionKey.id, code: `${toFileOperationResult(error)}` }); throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateExistingMetadata); } } else { @@ -615,7 +615,7 @@ export class ExtensionsScanner extends Disposable { try { await this.extensionsScannerService.updateMetadata(tempLocation, metadata); } catch (error) { - this.telemetryService.publicLog2('extension:extract', { extensionId: extensionKey.id, code: error.code }); + this.telemetryService.publicLog2('extension:extract', { extensionId: extensionKey.id, code: `${toFileOperationResult(error)}` }); throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata); } @@ -671,7 +671,7 @@ export class ExtensionsScanner extends Disposable { await this.extensionsScannerService.updateMetadata(local.location, metadata); } } catch (error) { - this.telemetryService.publicLog2('extension:extract', { extensionId: local.identifier.id, code: error.code }); + this.telemetryService.publicLog2('extension:extract', { extensionId: local.identifier.id, code: `${toFileOperationResult(error)}` }); throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata); } return this.scanLocalExtension(local.location, local.type, profileLocation); From 10961c6eec00f18aab98306a725f1e74c84bb726 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Mon, 24 Jun 2024 10:09:25 -0700 Subject: [PATCH 0079/2222] Minor inline text button polish --- .../contrib/inlineChat/browser/media/inlineChat.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index aa6f14f0ae4..a345f1cc0c9 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -31,7 +31,7 @@ } .monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list { - padding: 2px 0 0 0; + padding: 4px 0 0 0; } .monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list .interactive-item-container.interactive-item-compact { @@ -156,8 +156,8 @@ .action-item.text-only .action-label { font-size: 12px; line-height: 16px; - padding: 1px 2px; - border-radius: 3px; + padding: 0 4px; + border-radius: 2px; } .monaco-action-bar .action-item.menu-entry.text-only + .action-item:not(.text-only) > .monaco-dropdown .action-label { From 1b883b238ed87888a19d2a4b88fb536e5a66fb9e Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Mon, 24 Jun 2024 10:31:17 -0700 Subject: [PATCH 0080/2222] Fix #209158. Add Copy Output and Open Output in Text Editor to Scrollable text output context menu. (#216920) * Fix #209158. Add Copy Output and Open Output in Text Editor to Scrollable text output context menu. * Context menu should be triggered on container * fix naming --- extensions/ipynb/package.json | 17 +++++++ extensions/ipynb/package.nls.json | 1 + .../notebook-renderers/src/textHelper.ts | 10 +++++ .../browser/controller/cellOutputActions.ts | 45 ++++++++++++++++++- 4 files changed, 72 insertions(+), 1 deletion(-) diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index b97bd42e6c8..d881eb8ca22 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -62,6 +62,11 @@ "command": "notebook.cellOutput.copy", "title": "%copyCellOutput.title%", "category": "Notebook" + }, + { + "command": "notebook.cellOutput.openInTextEditor", + "title": "%openCellOutput.title%", + "category": "Notebook" } ], "notebooks": [ @@ -108,12 +113,24 @@ { "command": "notebook.cellOutput.copy", "when": "notebookCellHasOutputs" + }, + { + "command": "notebook.cellOutput.openInTextEditor", + "when": "false" } ], "webview/context": [ { "command": "notebook.cellOutput.copy", "when": "webviewId == 'notebook.output' && webviewSection == 'image'" + }, + { + "command": "notebook.cellOutput.copy", + "when": "webviewId == 'notebook.output' && webviewSection == 'text'" + }, + { + "command": "notebook.cellOutput.openInTextEditor", + "when": "webviewId == 'notebook.output' && webviewSection == 'text'" } ] } diff --git a/extensions/ipynb/package.nls.json b/extensions/ipynb/package.nls.json index af7d8f4ab47..7a3d95181cf 100644 --- a/extensions/ipynb/package.nls.json +++ b/extensions/ipynb/package.nls.json @@ -7,6 +7,7 @@ "openIpynbInNotebookEditor.title": "Open IPYNB File In Notebook Editor", "cleanInvalidImageAttachment.title": "Clean Invalid Image Attachment Reference", "copyCellOutput.title": "Copy Cell Output", + "openCellOutput.title": "Open Cell Output in Text Editor", "markdownAttachmentRenderer.displayName": { "message": "Markdown-It ipynb Cell Attachment renderer", "comment": [ diff --git a/extensions/notebook-renderers/src/textHelper.ts b/extensions/notebook-renderers/src/textHelper.ts index b49dbb6ad8d..9c080c7f9e4 100644 --- a/extensions/notebook-renderers/src/textHelper.ts +++ b/extensions/notebook-renderers/src/textHelper.ts @@ -71,6 +71,11 @@ function generateNestedViewAllElement(outputId: string) { function truncatedArrayOfString(id: string, buffer: string[], linesLimit: number, linkOptions: LinkOptions) { const container = document.createElement('div'); + container.setAttribute('data-vscode-context', JSON.stringify({ + webviewSection: 'text', + outputId: id, + 'preventDefaultContextMenuItems': true + })); const lineCount = buffer.length; if (lineCount <= linesLimit) { @@ -95,6 +100,11 @@ function truncatedArrayOfString(id: string, buffer: string[], linesLimit: number function scrollableArrayOfString(id: string, buffer: string[], linkOptions: LinkOptions) { const element = document.createElement('div'); + element.setAttribute('data-vscode-context', JSON.stringify({ + webviewSection: 'text', + outputId: id, + 'preventDefaultContextMenuItems': true + })); if (buffer.length > softScrollableLineLimit) { element.appendChild(generateNestedViewAllElement(id)); } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index 0db8e6dd2b4..db449afafdb 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -7,6 +7,7 @@ import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; import { localize } from 'vs/nls'; import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/actions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; import { INotebookOutputActionContext, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; import { NOTEBOOK_CELL_HAS_OUTPUTS } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; @@ -14,7 +15,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { copyCellOutput } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ICellOutputViewModel, ICellViewModel, INotebookEditor, getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { CellKind, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copy'; @@ -104,3 +105,45 @@ function getOutputViewModelFromId(outputId: string, notebookEditor: INotebookEdi return undefined; } + +export const OPEN_OUTPUT_COMMAND_ID = 'notebook.cellOutput.openInTextEditor'; + +registerAction2(class OpenCellOutputInEditorAction extends Action2 { + constructor() { + super({ + id: OPEN_OUTPUT_COMMAND_ID, + title: localize('notebookActions.openOutputInEditor', "Open Cell Output in Text Editor"), + f1: false, + category: NOTEBOOK_ACTIONS_CATEGORY, + icon: icons.copyIcon, + }); + } + + private getNoteboookEditor(editorService: IEditorService, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): INotebookEditor | undefined { + if (outputContext && 'notebookEditor' in outputContext) { + return outputContext.notebookEditor; + } + return getNotebookEditorFromEditorPane(editorService.activeEditorPane); + } + + async run(accessor: ServicesAccessor, outputContext: INotebookOutputActionContext | { outputViewModel: ICellOutputViewModel } | undefined): Promise { + const notebookEditor = this.getNoteboookEditor(accessor.get(IEditorService), outputContext); + + if (!notebookEditor) { + return; + } + + let outputViewModel: ICellOutputViewModel | undefined; + if (outputContext && 'outputId' in outputContext && typeof outputContext.outputId === 'string') { + outputViewModel = getOutputViewModelFromId(outputContext.outputId, notebookEditor); + } else if (outputContext && 'outputViewModel' in outputContext) { + outputViewModel = outputContext.outputViewModel; + } + + const openerService = accessor.get(IOpenerService); + + if (outputViewModel?.model.outputId && notebookEditor.textModel?.uri) { + openerService.open(CellUri.generateCellOutputUri(notebookEditor.textModel.uri, outputViewModel.model.outputId)); + } + } +}); From 3cdb16526254722b567d23fd830800cca8e0947f Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 24 Jun 2024 14:11:39 +0200 Subject: [PATCH 0081/2222] Update to @vscode/proxy-agent 0.21.0 --- .../vscode-api-tests/src/singlefolder-tests/proxy.test.ts | 8 ++++---- package.json | 4 ++-- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- src/vs/workbench/api/node/proxyResolver.ts | 8 ++++---- yarn.lock | 8 ++++---- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts index 60f100c7c1f..74fa5d181a5 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts @@ -8,7 +8,7 @@ import 'mocha'; import { assertNoRpc } from '../utils'; import { pki } from 'node-forge'; import { AddressInfo } from 'net'; -import { resetCaches } from '@vscode/proxy-agent'; +import { resetCaches, testCertificates } from '@vscode/proxy-agent'; suite('vscode API - network proxy support', () => { @@ -52,8 +52,8 @@ suite('vscode API - network proxy support', () => { rejectPort(err); }); - // Using https.globalAgent because it is shared with proxyResolver.ts and mutable. - (https.globalAgent as any).testCertificates = [certPEM]; + // Using `testCertificates` shared between proxyResolver.ts and proxy.test.ts. + testCertificates.splice(0, testCertificates.length, certPEM); resetCaches(); try { @@ -69,7 +69,7 @@ suite('vscode API - network proxy support', () => { .on('error', reject); }); } finally { - delete (https.globalAgent as any).testCertificates; + testCertificates.splice(0, testCertificates.length); resetCaches(); server.close(); } diff --git a/package.json b/package.json index 4bc1bf13857..7bd20339726 100644 --- a/package.json +++ b/package.json @@ -73,7 +73,7 @@ "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/policy-watcher": "^1.1.4", - "@vscode/proxy-agent": "^0.20.0", + "@vscode/proxy-agent": "^0.21.0", "@vscode/ripgrep": "^1.15.9", "@vscode/spdlog": "^0.15.0", "@vscode/sqlite3": "5.1.6-vscode", @@ -229,4 +229,4 @@ "optionalDependencies": { "windows-foreground-love": "0.5.0" } -} \ No newline at end of file +} diff --git a/remote/package.json b/remote/package.json index c9b813f2b65..5b0e80182ea 100644 --- a/remote/package.json +++ b/remote/package.json @@ -8,7 +8,7 @@ "@parcel/watcher": "2.1.0", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.20.0", + "@vscode/proxy-agent": "^0.21.0", "@vscode/ripgrep": "^1.15.9", "@vscode/spdlog": "^0.15.0", "@vscode/vscode-languagedetection": "1.0.21", diff --git a/remote/yarn.lock b/remote/yarn.lock index f57d42bd604..c8e84fc81e1 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -66,10 +66,10 @@ resolved "https://registry.yarnpkg.com/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz#d2f1e0664ee6036408f9743fee264ea0699b0e48" integrity sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg== -"@vscode/proxy-agent@^0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.20.0.tgz#48cbfd15e76da674bb1ba6e09a05b736d5677df0" - integrity sha512-FskCQwrFiXI+KiGgvEvUENNDffu5cDg3Cz1zWsafqvuPoPpTC88SOJl9kbeu3Y2YvSMcHx/r3tkK6ICoMaEWNQ== +"@vscode/proxy-agent@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.21.0.tgz#93c818b863ad20b42679032ecc1e3ecdc6306f12" + integrity sha512-9YcpBq+ZhMr3EQY/5ScyHc9kIIU/AcYOQn3DXq0N9tl81ViVsUvii3Fh+FAtD0YQ/qWtDfGxt8VCWZtuyh2D0g== dependencies: "@tootallnate/once" "^3.0.0" agent-base "^7.0.1" diff --git a/src/vs/workbench/api/node/proxyResolver.ts b/src/vs/workbench/api/node/proxyResolver.ts index f2854500083..6cb6349d172 100644 --- a/src/vs/workbench/api/node/proxyResolver.ts +++ b/src/vs/workbench/api/node/proxyResolver.ts @@ -16,7 +16,7 @@ import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionS import { URI } from 'vs/base/common/uri'; import { ILogService, LogLevel as LogServiceLevel } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch, loadSystemCertificates } from '@vscode/proxy-agent'; +import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch, loadSystemCertificates, testCertificates } from '@vscode/proxy-agent'; const systemCertificatesV2Default = false; @@ -67,10 +67,10 @@ export function connectProxyResolver( certs.then(certs => extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loaded certificates from main process', certs.length)); promises.push(certs); } - // Using https.globalAgent because it is shared with proxy.test.ts and mutable. - if (initData.environment.extensionTestsLocationURI && (https.globalAgent as any).testCertificates?.length) { + // Using `testCertificates` shared between proxyResolver.ts and proxy.test.ts. + if (initData.environment.extensionTestsLocationURI && testCertificates.length) { extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loading test certificates'); - promises.push(Promise.resolve((https.globalAgent as any).testCertificates as string[])); + promises.push(Promise.resolve(testCertificates)); } return (await Promise.all(promises)).flat(); }, diff --git a/yarn.lock b/yarn.lock index 05de6b41206..71aef4295fa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1570,10 +1570,10 @@ bindings "^1.5.0" node-addon-api "^6.0.0" -"@vscode/proxy-agent@^0.20.0": - version "0.20.0" - resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.20.0.tgz#48cbfd15e76da674bb1ba6e09a05b736d5677df0" - integrity sha512-FskCQwrFiXI+KiGgvEvUENNDffu5cDg3Cz1zWsafqvuPoPpTC88SOJl9kbeu3Y2YvSMcHx/r3tkK6ICoMaEWNQ== +"@vscode/proxy-agent@^0.21.0": + version "0.21.0" + resolved "https://registry.yarnpkg.com/@vscode/proxy-agent/-/proxy-agent-0.21.0.tgz#93c818b863ad20b42679032ecc1e3ecdc6306f12" + integrity sha512-9YcpBq+ZhMr3EQY/5ScyHc9kIIU/AcYOQn3DXq0N9tl81ViVsUvii3Fh+FAtD0YQ/qWtDfGxt8VCWZtuyh2D0g== dependencies: "@tootallnate/once" "^3.0.0" agent-base "^7.0.1" From 298c72219bcc76aa0ff825b77f307013b568fcc5 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 24 Jun 2024 15:08:31 +0200 Subject: [PATCH 0082/2222] Module not shared on Windows? --- .../vscode-api-tests/src/singlefolder-tests/proxy.test.ts | 8 ++++---- src/vs/workbench/api/node/proxyResolver.ts | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts index 74fa5d181a5..60f100c7c1f 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts @@ -8,7 +8,7 @@ import 'mocha'; import { assertNoRpc } from '../utils'; import { pki } from 'node-forge'; import { AddressInfo } from 'net'; -import { resetCaches, testCertificates } from '@vscode/proxy-agent'; +import { resetCaches } from '@vscode/proxy-agent'; suite('vscode API - network proxy support', () => { @@ -52,8 +52,8 @@ suite('vscode API - network proxy support', () => { rejectPort(err); }); - // Using `testCertificates` shared between proxyResolver.ts and proxy.test.ts. - testCertificates.splice(0, testCertificates.length, certPEM); + // Using https.globalAgent because it is shared with proxyResolver.ts and mutable. + (https.globalAgent as any).testCertificates = [certPEM]; resetCaches(); try { @@ -69,7 +69,7 @@ suite('vscode API - network proxy support', () => { .on('error', reject); }); } finally { - testCertificates.splice(0, testCertificates.length); + delete (https.globalAgent as any).testCertificates; resetCaches(); server.close(); } diff --git a/src/vs/workbench/api/node/proxyResolver.ts b/src/vs/workbench/api/node/proxyResolver.ts index 6cb6349d172..f2854500083 100644 --- a/src/vs/workbench/api/node/proxyResolver.ts +++ b/src/vs/workbench/api/node/proxyResolver.ts @@ -16,7 +16,7 @@ import { ExtHostExtensionService } from 'vs/workbench/api/node/extHostExtensionS import { URI } from 'vs/base/common/uri'; import { ILogService, LogLevel as LogServiceLevel } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; -import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch, loadSystemCertificates, testCertificates } from '@vscode/proxy-agent'; +import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch, loadSystemCertificates } from '@vscode/proxy-agent'; const systemCertificatesV2Default = false; @@ -67,10 +67,10 @@ export function connectProxyResolver( certs.then(certs => extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loaded certificates from main process', certs.length)); promises.push(certs); } - // Using `testCertificates` shared between proxyResolver.ts and proxy.test.ts. - if (initData.environment.extensionTestsLocationURI && testCertificates.length) { + // Using https.globalAgent because it is shared with proxy.test.ts and mutable. + if (initData.environment.extensionTestsLocationURI && (https.globalAgent as any).testCertificates?.length) { extHostLogService.trace('ProxyResolver#loadAdditionalCertificates: Loading test certificates'); - promises.push(Promise.resolve(testCertificates)); + promises.push(Promise.resolve((https.globalAgent as any).testCertificates as string[])); } return (await Promise.all(promises)).flat(); }, From 7b12ac06093d41d9293c580bbbcda3a4e4819ff5 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 24 Jun 2024 11:05:36 -0700 Subject: [PATCH 0083/2222] debug: fix shadowed variables duplicating inlay values (#217330) I noticed when working on another thing that if a variable appears in multiple scopes with different values debug would duplicate the inline value. This was because we created the decorations for each scope and then deduplicated them by content. Now we keep a map of all inline values we want to apply and add variables to it from narrowest to widest scope, skipping any duplicates. --- .../debug/browser/debugEditorContribution.ts | 50 ++++++++++++------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 1612646a1e9..7ef1947ea2c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -7,7 +7,6 @@ import { addDisposableListener, isKeyboardEvent } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { IKeyboardEvent, StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { IMouseEvent } from 'vs/base/browser/mouseEvent'; -import { distinct } from 'vs/base/common/arrays'; import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { memoize } from 'vs/base/common/decorators'; @@ -127,7 +126,7 @@ function replaceWsWithNoBreakWs(str: string): string { return str.replace(/[ \t]/g, strings.noBreakWhitespace); } -function createInlineValueDecorationsInsideRange(expressions: ReadonlyArray, ranges: Range[], model: ITextModel, wordToLineNumbersMap: Map): IModelDeltaDecoration[] { +function createInlineValueDecorationsInsideRange(expressions: ReadonlyArray, ranges: Range[], model: ITextModel, wordToLineNumbersMap: Map) { const nameValueMap = new Map(); for (const expr of expressions) { nameValueMap.set(expr.name, expr.value); @@ -157,17 +156,14 @@ function createInlineValueDecorationsInsideRange(expressions: ReadonlyArray { - const contentText = names.sort((first, second) => { + return [...lineToNamesMap].map(([line, names]) => ({ + line, + variables: names.sort((first, second) => { const content = model.getLineContent(line); return content.indexOf(first) - content.indexOf(second); - }).map(name => `${name} = ${nameValueMap.get(name)}`).join(', '); - decorations.push(...createInlineValueDecoration(line, contentText)); - }); - - return decorations; + }).map(name => ({ name, value: nameValueMap.get(name)! })) + })); } function getWordToLineNumbersMap(model: ITextModel, lineNumber: number, result: Map) { @@ -783,10 +779,15 @@ export class DebugEditorContribution implements IDebugEditorContribution { // old "one-size-fits-all" strategy const scopes = await stackFrame.getMostSpecificScopes(stackFrame.range); - // Get all top level variables in the scope chain - const decorationsPerScope = await Promise.all(scopes.map(async scope => { - const variables = await scope.getChildren(); + const scopesWithVariables = await Promise.all(scopes.map(async scope => + ({ scope, variables: await scope.getChildren() }))); + + // Map of inline values per line that's populated in scope order, from + // narrowest to widest. This is done to avoid duplicating values if + // they appear in multiple scopes or are shadowed (#129770, #217326) + const valuesPerLine = new Map>(); + for (const { scope, variables } of scopesWithVariables) { let scopeRange = new Range(0, 0, stackFrame.range.startLineNumber, stackFrame.range.startColumn); if (scope.range) { scopeRange = scopeRange.setStartPosition(scope.range.startLineNumber, scope.range.startColumn); @@ -798,12 +799,25 @@ export class DebugEditorContribution implements IDebugEditorContribution { this._wordToLineNumbersMap.ensureRangePopulated(range); } - return createInlineValueDecorationsInsideRange(variables, ownRanges, model, this._wordToLineNumbersMap.value); - })); + const mapped = createInlineValueDecorationsInsideRange(variables, ownRanges, model, this._wordToLineNumbersMap.value); + for (const { line, variables } of mapped) { + let values = valuesPerLine.get(line); + if (!values) { + values = new Map(); + valuesPerLine.set(line, values); + } + + for (const { name, value } of variables) { + if (!values.has(name)) { + values.set(name, value); + } + } + } + } - allDecorations = distinct(decorationsPerScope.flat(), - // Deduplicate decorations since same variable can appear in multiple scopes, leading to duplicated decorations #129770 - decoration => `${decoration.range.startLineNumber}:${decoration?.options.after?.content}`); + allDecorations = [...valuesPerLine.entries()].flatMap(([line, values]) => + createInlineValueDecoration(line, [...values].map(([n, v]) => `${n} = ${v}`).join(', ')) + ); } if (cts.token.isCancellationRequested) { From e3e15e15e82df2390b0c9e1ad4bc8add01752610 Mon Sep 17 00:00:00 2001 From: mohankumarelec Date: Mon, 24 Jun 2024 23:35:55 +0530 Subject: [PATCH 0084/2222] Add http.noProxy setting (fixes #211956) --- src/vs/platform/request/common/request.ts | 6 ++++++ src/vs/platform/windows/electron-main/windowImpl.ts | 5 +++-- src/vs/workbench/api/node/proxyResolver.ts | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 289ad6740e0..06265628b94 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -155,6 +155,12 @@ function registerProxyConfigurations(scope: ConfigurationScope): void { markdownDescription: localize('proxyKerberosServicePrincipal', "Overrides the principal service name for Kerberos authentication with the HTTP proxy. A default based on the proxy hostname is used when this is not set."), restricted: true }, + 'http.noProxy': { + type: 'array', + items: { type: 'string' }, + markdownDescription: localize('noProxy', "Specifies domain names for which proxy settings should be ignored for HTTP/HTTPS requests."), + restricted: true + }, 'http.proxyAuthorization': { type: ['null', 'string'], default: null, diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index dcb3a1776ec..dcb1cc9210d 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -957,7 +957,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { } // Proxy - if (!e || e.affectsConfiguration('http.proxy')) { + if (!e || e.affectsConfiguration('http.proxy') || e.affectsConfiguration('http.noProxy')) { let newHttpProxy = (this.configurationService.getValue('http.proxy') || '').trim() || (process.env['https_proxy'] || process.env['HTTPS_PROXY'] || process.env['http_proxy'] || process.env['HTTP_PROXY'] || '').trim() // Not standardized. || undefined; @@ -966,7 +966,8 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { newHttpProxy = newHttpProxy.substr(0, newHttpProxy.length - 1); } - const newNoProxy = (process.env['no_proxy'] || process.env['NO_PROXY'] || '').trim() || undefined; // Not standardized. + const newNoProxy = (this.configurationService.getValue('http.noProxy') || []).map((item) => item.trim()).join(',') + || (process.env['no_proxy'] || process.env['NO_PROXY'] || '').trim() || undefined; // Not standardized. if ((newHttpProxy || '').indexOf('@') === -1 && (newHttpProxy !== this.currentHttpProxy || newNoProxy !== this.currentNoProxy)) { this.currentHttpProxy = newHttpProxy; this.currentNoProxy = newNoProxy; diff --git a/src/vs/workbench/api/node/proxyResolver.ts b/src/vs/workbench/api/node/proxyResolver.ts index f2854500083..ac373f9739b 100644 --- a/src/vs/workbench/api/node/proxyResolver.ts +++ b/src/vs/workbench/api/node/proxyResolver.ts @@ -35,6 +35,7 @@ export function connectProxyResolver( lookupProxyAuthorization: lookupProxyAuthorization.bind(undefined, extHostLogService, mainThreadTelemetry, configProvider, {}, initData.remote.isRemote), getProxyURL: () => configProvider.getConfiguration('http').get('proxy'), getProxySupport: () => configProvider.getConfiguration('http').get('proxySupport') || 'off', + getNoProxyConfig: () => configProvider.getConfiguration('http').get('noProxy') || [], addCertificatesV1: () => certSettingV1(configProvider), addCertificatesV2: () => certSettingV2(configProvider), log: extHostLogService, From b8f8a1b4b239459f1f44f105fccb0241c3249bec Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 24 Jun 2024 11:10:37 -0700 Subject: [PATCH 0085/2222] Cap number of code blocks models we keep around (#216753) --- .../chat/common/codeBlockModelCollection.ts | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts index a857b64cb1d..d5ed699cd9b 100644 --- a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts +++ b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts @@ -22,6 +22,13 @@ export class CodeBlockModelCollection extends Disposable { vulns: readonly IMarkdownVulnerability[]; }>(); + /** + * Max number of models to keep in memory. + * + * Currently always maintains the most recently created models. + */ + private readonly maxModelCount = 100; + constructor( @ILanguageService private readonly languageService: ILanguageService, @ITextModelService private readonly textModelService: ITextModelService @@ -52,9 +59,28 @@ export class CodeBlockModelCollection extends Disposable { const uri = this.getUri(sessionId, chat, codeBlockIndex); const ref = this.textModelService.createModelReference(uri); this._models.set(uri, { model: ref, vulns: [] }); + + while (this._models.size > this.maxModelCount) { + const first = Array.from(this._models.keys()).at(0); + if (!first) { + break; + } + this.delete(first); + } + return { model: ref.then(ref => ref.object), vulns: [] }; } + private delete(codeBlockUri: URI) { + const entry = this._models.get(codeBlockUri); + if (!entry) { + return; + } + + entry.model.then(ref => ref.dispose()); + this._models.delete(codeBlockUri); + } + clear(): void { this._models.forEach(async entry => (await entry.model).dispose()); this._models.clear(); From 4a59a68f3a928101f44694345f07ad543d84dd86 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 24 Jun 2024 11:11:05 -0700 Subject: [PATCH 0086/2222] Revert "Remove check for validity" (#217334) Revert "Remove check for validity (#216675)" This reverts commit a3017010e9846f36d4e2da88c5e782290ca77399. --- .github/workflows/on-open.yml | 5 +++++ .github/workflows/on-reopen.yml | 22 ++++++++++++++++++++++ 2 files changed, 27 insertions(+) create mode 100644 .github/workflows/on-reopen.yml diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index 361ac11b946..679c1c65db8 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -16,6 +16,11 @@ jobs: - name: Install Actions run: npm install --production --prefix ./actions + - name: Check for Validity + uses: ./actions/validity-checker + with: + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} + - name: Run CopyCat (VSCodeTriageBot/testissues) uses: ./actions/copycat with: diff --git a/.github/workflows/on-reopen.yml b/.github/workflows/on-reopen.yml new file mode 100644 index 00000000000..d29de326c53 --- /dev/null +++ b/.github/workflows/on-reopen.yml @@ -0,0 +1,22 @@ +name: On Reopen +on: + issues: + types: [reopened] + +jobs: + main: + runs-on: ubuntu-latest + steps: + - name: Checkout Actions + uses: actions/checkout@v4 + with: + repository: "microsoft/vscode-github-triage-actions" + ref: stable + path: ./actions + - name: Install Actions + run: npm install --production --prefix ./actions + + - name: Check for Validity + uses: ./actions/validity-checker + with: + token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} From 2a106ad4b531fa1a6571cddf9e50995268d0ec87 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Mon, 24 Jun 2024 11:40:51 -0700 Subject: [PATCH 0087/2222] fix: some settings still localize link markdown (#217375) --- src/vs/platform/list/browser/listService.ts | 2 +- src/vs/workbench/electron-sandbox/desktop.contribution.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/list/browser/listService.ts b/src/vs/platform/list/browser/listService.ts index 6857531cb66..976b9a85dc3 100644 --- a/src/vs/platform/list/browser/listService.ts +++ b/src/vs/platform/list/browser/listService.ts @@ -1490,7 +1490,7 @@ configurationRegistry.registerConfiguration({ type: 'number', minimum: 1, default: 7, - markdownDescription: localize('sticky scroll maximum items', "Controls the number of sticky elements displayed in the tree when `#workbench.tree.enableStickyScroll#` is enabled."), + markdownDescription: localize('sticky scroll maximum items', "Controls the number of sticky elements displayed in the tree when {0} is enabled.", '`#workbench.tree.enableStickyScroll#`'), }, [typeNavigationModeSettingKey]: { type: 'string', diff --git a/src/vs/workbench/electron-sandbox/desktop.contribution.ts b/src/vs/workbench/electron-sandbox/desktop.contribution.ts index 6fd2279a320..4ae3a9f94ed 100644 --- a/src/vs/workbench/electron-sandbox/desktop.contribution.ts +++ b/src/vs/workbench/electron-sandbox/desktop.contribution.ts @@ -226,7 +226,7 @@ import { MAX_ZOOM_LEVEL, MIN_ZOOM_LEVEL } from 'vs/platform/window/electron-sand 'type': 'boolean', 'default': false, 'scope': ConfigurationScope.APPLICATION, - 'markdownDescription': localize('window.doubleClickIconToClose', "If enabled, this setting will close the window when the application icon in the title bar is double-clicked. The window will not be able to be dragged by the icon. This setting is effective only if `#window.titleBarStyle#` is set to `custom`.") + 'markdownDescription': localize('window.doubleClickIconToClose', "If enabled, this setting will close the window when the application icon in the title bar is double-clicked. The window will not be able to be dragged by the icon. This setting is effective only if {0} is set to `custom`.", '`#window.titleBarStyle#`') }, 'window.titleBarStyle': { 'type': 'string', From 06bf1d5fcf5268ecd7a2f7b348e09c6dc5561df8 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 24 Jun 2024 20:43:08 +0200 Subject: [PATCH 0088/2222] rename 'insert at cursor' to 'apply in editor' (#217376) --- src/vs/editor/common/languages.ts | 2 +- src/vs/monaco.d.ts | 2 +- .../contrib/chat/browser/actions/chatCodeblockActions.ts | 2 +- src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 1d5c2901178..3159cdc6ae5 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -2262,7 +2262,7 @@ export interface MappedEditsProvider { * * @param document The document to provide mapped edits for. * @param codeBlocks Code blocks that come from an LLM's reply. - * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. + * "Apply in Editor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. * @param context The context for providing mapped edits. * @param token A cancellation token. * @returns A provider result of text edits. diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index d12441499ea..c7e413801cc 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -8065,7 +8065,7 @@ declare namespace monaco.languages { * * @param document The document to provide mapped edits for. * @param codeBlocks Code blocks that come from an LLM's reply. - * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. + * "Apply in Editor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. * @param context The context for providing mapped edits. * @param token A cancellation token. * @returns A provider result of text edits. diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index 01f026310de..b94a9434cc5 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -184,7 +184,7 @@ export function registerChatCodeBlockActions() { constructor() { super({ id: 'workbench.action.chat.insertCodeBlock', - title: localize2('interactive.insertCodeBlock.label', "Insert at Cursor"), + title: localize2('interactive.insertCodeBlock.label', "Apply in Editor"), precondition: CONTEXT_CHAT_ENABLED, f1: true, category: CHAT_CATEGORY, diff --git a/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts b/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts index 653f6a2d50d..6ed185785d8 100644 --- a/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts +++ b/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts @@ -23,7 +23,7 @@ declare module 'vscode' { * Provide mapped edits for a given document. * @param document The document to provide mapped edits for. * @param codeBlocks Code blocks that come from an LLM's reply. - * "Insert at cursor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. + * "Apply in Editor" in the panel chat only sends one edit that the user clicks on, but inline chat can send multiple blocks and let the lang server decide what to do with them. * @param context The context for providing mapped edits. * @param token A cancellation token. * @returns A provider result of text edits. From 45be8b8f75907203e9f9ec52c6ece3e9fa24132d Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 24 Jun 2024 20:47:01 +0200 Subject: [PATCH 0089/2222] [css/json/html] update services (#217364) --- extensions/css-language-features/package.json | 2 +- .../css-language-features/server/package.json | 4 +- .../css-language-features/server/yarn.lock | 46 ++++++++-------- extensions/css-language-features/yarn.lock | 38 ++++++------- .../html-language-features/package.json | 2 +- .../server/package.json | 6 +-- .../html-language-features/server/yarn.lock | 54 +++++++++---------- extensions/html-language-features/yarn.lock | 38 ++++++------- .../json-language-features/package.json | 2 +- .../server/package.json | 4 +- .../json-language-features/server/yarn.lock | 53 +++++++++--------- extensions/json-language-features/yarn.lock | 38 ++++++------- 12 files changed, 146 insertions(+), 141 deletions(-) diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index f4f6adfb7f4..4ddfe8fce0d 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -994,7 +994,7 @@ ] }, "dependencies": { - "vscode-languageclient": "^10.0.0-next.5", + "vscode-languageclient": "^10.0.0-next.8", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 0f1750e800a..fe4f64d7c01 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -11,8 +11,8 @@ "browser": "./dist/browser/cssServerMain", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.2.14", - "vscode-languageserver": "^10.0.0-next.3", + "vscode-css-languageservice": "^6.3.0", + "vscode-languageserver": "^10.0.0-next.6", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/css-language-features/server/yarn.lock b/extensions/css-language-features/server/yarn.lock index 8d4c46d641e..59033f770c1 100644 --- a/extensions/css-language-features/server/yarn.lock +++ b/extensions/css-language-features/server/yarn.lock @@ -24,28 +24,28 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -vscode-css-languageservice@^6.2.14: - version "6.2.14" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.14.tgz#d44fe75c03942d865a9c1a5ff5fb4e8dec1f89d0" - integrity sha512-5UPQ9Y1sUTnuMyaMBpO7LrBkqjhEJb5eAwdUlDp+Uez8lry+Tspnk3+3p2qWS4LlNsr4p3v9WkZxUf1ltgFpgw== +vscode-css-languageservice@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.3.0.tgz#51724d193d19b1a9075b1cef5cfeea6a555d2aa4" + integrity sha512-nU92imtkgzpCL0xikrIb8WvedV553F2BENzgz23wFuok/HLN5BeQmroMy26pUwFxV2eV8oNRmYCUv8iO7kSMhw== dependencies: "@vscode/l10n" "^0.0.18" vscode-languageserver-textdocument "^1.0.11" vscode-languageserver-types "3.17.5" vscode-uri "^3.0.8" -vscode-jsonrpc@9.0.0-next.2: - version "9.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.2.tgz#29e9741c742c80329bba1c60ce38fd014651ba80" - integrity sha512-meIaXAgChCHzWy45QGU8YpCNyqnZQ/sYeCj32OLDDbUYsCF7AvgpdXx3nnZn9yzr8ed0Od9bW+NGphEmXsqvIQ== +vscode-jsonrpc@9.0.0-next.4: + version "9.0.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz#ba403ddb3b82ca578179963dbe08e120a935f50d" + integrity sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ== -vscode-languageserver-protocol@3.17.6-next.4: - version "3.17.6-next.4" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.4.tgz#3c56f6eb588bb42fccc0ac54a0d5daf2d02f0a1b" - integrity sha512-/2bleKBxZLyRObS4mkpaWlVI9xGiUqMVmh/ztZ2vL4uP2XyIpraT45JBpn9AtXr0alqKJPKLuKr+/qcYULvm/w== +vscode-languageserver-protocol@3.17.6-next.6: + version "3.17.6-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz#8863a4dc8b395a8c31106ffdc945a00f9163b68b" + integrity sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw== dependencies: - vscode-jsonrpc "9.0.0-next.2" - vscode-languageserver-types "3.17.6-next.3" + vscode-jsonrpc "9.0.0-next.4" + vscode-languageserver-types "3.17.6-next.4" vscode-languageserver-textdocument@^1.0.11: version "1.0.11" @@ -57,17 +57,17 @@ vscode-languageserver-types@3.17.5: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== -vscode-languageserver-types@3.17.6-next.3: - version "3.17.6-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.3.tgz#f71d6c57f18d921346cfe0c227aabd72eb8cd2f0" - integrity sha512-l5kNFXFRQGuzriXpuBqFpRmkf6f6A4VoU3h95OsVkqIOoi1k7KbwSo600cIdsKSJWrPg/+vX+QMPcMw1oI7ItA== +vscode-languageserver-types@3.17.6-next.4: + version "3.17.6-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz#6670939eb98f00aa7b05021dc3dd7fe9aa4453ea" + integrity sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q== -vscode-languageserver@^10.0.0-next.3: - version "10.0.0-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-10.0.0-next.3.tgz#a63c5ea9fab1be93d7732ab0fdc18c9b37956e07" - integrity sha512-4x1qHImf6ePji4+8PX43lnBCBfBNdi2jneGX2k5FswJhx/cxaYYmusShmmtO/clyL1iurxJacrQoXfw9+ikhvg== +vscode-languageserver@^10.0.0-next.6: + version "10.0.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-10.0.0-next.6.tgz#0db118a93fe010c6b40cd04e91a15d09e7b60b60" + integrity sha512-0Lh1nhQfSxo5Ob+ayYO1QTIsDix2/Lc72Urm1KZrCFxK5zIFYaEh3QFeM9oZih4Rzs0ZkQPXXnoHtpvs5GT+Zw== dependencies: - vscode-languageserver-protocol "3.17.6-next.4" + vscode-languageserver-protocol "3.17.6-next.6" vscode-uri@^3.0.8: version "3.0.8" diff --git a/extensions/css-language-features/yarn.lock b/extensions/css-language-features/yarn.lock index 25a22d07ca6..eef1c9ab57d 100644 --- a/extensions/css-language-features/yarn.lock +++ b/extensions/css-language-features/yarn.lock @@ -47,32 +47,32 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -vscode-jsonrpc@9.0.0-next.2: - version "9.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.2.tgz#29e9741c742c80329bba1c60ce38fd014651ba80" - integrity sha512-meIaXAgChCHzWy45QGU8YpCNyqnZQ/sYeCj32OLDDbUYsCF7AvgpdXx3nnZn9yzr8ed0Od9bW+NGphEmXsqvIQ== +vscode-jsonrpc@9.0.0-next.4: + version "9.0.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz#ba403ddb3b82ca578179963dbe08e120a935f50d" + integrity sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ== -vscode-languageclient@^10.0.0-next.5: - version "10.0.0-next.5" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-10.0.0-next.5.tgz#7431d88255a5fd99e9423659ac484b1f968200f3" - integrity sha512-JIf1WE7fvV0RElFM062bAummI433vcxuFwqoYAp+1zTVhta/jznxkTz1zs3Hbj2tiDfclf0TZ0qCxflAP1mY2Q== +vscode-languageclient@^10.0.0-next.8: + version "10.0.0-next.8" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-10.0.0-next.8.tgz#5afa0ced3b2ac68d31cc1c48edc4f289744542a0" + integrity sha512-D9inIHgqKayO9Tv0MeLb3XIL76yTuWmKdHqcGZKzjtQrMGJgASJDYWTapu+yAjEpDp0gmVOaCYyIlLB86ncDoQ== dependencies: minimatch "^9.0.3" semver "^7.6.0" - vscode-languageserver-protocol "3.17.6-next.4" + vscode-languageserver-protocol "3.17.6-next.6" -vscode-languageserver-protocol@3.17.6-next.4: - version "3.17.6-next.4" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.4.tgz#3c56f6eb588bb42fccc0ac54a0d5daf2d02f0a1b" - integrity sha512-/2bleKBxZLyRObS4mkpaWlVI9xGiUqMVmh/ztZ2vL4uP2XyIpraT45JBpn9AtXr0alqKJPKLuKr+/qcYULvm/w== +vscode-languageserver-protocol@3.17.6-next.6: + version "3.17.6-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz#8863a4dc8b395a8c31106ffdc945a00f9163b68b" + integrity sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw== dependencies: - vscode-jsonrpc "9.0.0-next.2" - vscode-languageserver-types "3.17.6-next.3" + vscode-jsonrpc "9.0.0-next.4" + vscode-languageserver-types "3.17.6-next.4" -vscode-languageserver-types@3.17.6-next.3: - version "3.17.6-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.3.tgz#f71d6c57f18d921346cfe0c227aabd72eb8cd2f0" - integrity sha512-l5kNFXFRQGuzriXpuBqFpRmkf6f6A4VoU3h95OsVkqIOoi1k7KbwSo600cIdsKSJWrPg/+vX+QMPcMw1oI7ItA== +vscode-languageserver-types@3.17.6-next.4: + version "3.17.6-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz#6670939eb98f00aa7b05021dc3dd7fe9aa4453ea" + integrity sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q== vscode-uri@^3.0.8: version "3.0.8" diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index 49489ff20df..ac026b973eb 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -259,7 +259,7 @@ }, "dependencies": { "@vscode/extension-telemetry": "^0.9.0", - "vscode-languageclient": "^10.0.0-next.3", + "vscode-languageclient": "^10.0.0-next.8", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 75bfa00de11..c1ddc242fa4 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,9 +10,9 @@ "main": "./out/node/htmlServerMain", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.2.13", - "vscode-html-languageservice": "^5.2.0", - "vscode-languageserver": "^10.0.0-next.2", + "vscode-css-languageservice": "^6.3.0", + "vscode-html-languageservice": "^5.3.0", + "vscode-languageserver": "^10.0.0-next.6", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, diff --git a/extensions/html-language-features/server/yarn.lock b/extensions/html-language-features/server/yarn.lock index f327f1f352f..caaf929d895 100644 --- a/extensions/html-language-features/server/yarn.lock +++ b/extensions/html-language-features/server/yarn.lock @@ -24,38 +24,38 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -vscode-css-languageservice@^6.2.13: - version "6.2.13" - resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.2.13.tgz#c7c2dc7a081a203048d60157c65536767d6d96f8" - integrity sha512-2rKWXfH++Kxd9Z4QuEgd1IF7WmblWWU7DScuyf1YumoGLkY9DW6wF/OTlhOyO2rN63sWHX2dehIpKBbho4ZwvA== +vscode-css-languageservice@^6.3.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/vscode-css-languageservice/-/vscode-css-languageservice-6.3.0.tgz#51724d193d19b1a9075b1cef5cfeea6a555d2aa4" + integrity sha512-nU92imtkgzpCL0xikrIb8WvedV553F2BENzgz23wFuok/HLN5BeQmroMy26pUwFxV2eV8oNRmYCUv8iO7kSMhw== dependencies: "@vscode/l10n" "^0.0.18" vscode-languageserver-textdocument "^1.0.11" vscode-languageserver-types "3.17.5" vscode-uri "^3.0.8" -vscode-html-languageservice@^5.2.0: - version "5.2.0" - resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.2.0.tgz#5b36f9131acc073cebaa2074dc8ff53e84c80f31" - integrity sha512-cdNMhyw57/SQzgUUGSIMQ66jikqEN6nBNyhx5YuOyj9310+eY9zw8Q0cXpiKzDX8aHYFewQEXRnigl06j/TVwQ== +vscode-html-languageservice@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/vscode-html-languageservice/-/vscode-html-languageservice-5.3.0.tgz#298ae5600c6749cbb95838975d07f449c44cb478" + integrity sha512-C4Z3KsP5Ih+fjHpiBc5jxmvCl+4iEwvXegIrzu2F5pktbWvQaBT3YkVPk8N+QlSSMk8oCG6PKtZ/Sq2YHb5e8g== dependencies: "@vscode/l10n" "^0.0.18" vscode-languageserver-textdocument "^1.0.11" vscode-languageserver-types "^3.17.5" vscode-uri "^3.0.8" -vscode-jsonrpc@9.0.0-next.2: - version "9.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.2.tgz#29e9741c742c80329bba1c60ce38fd014651ba80" - integrity sha512-meIaXAgChCHzWy45QGU8YpCNyqnZQ/sYeCj32OLDDbUYsCF7AvgpdXx3nnZn9yzr8ed0Od9bW+NGphEmXsqvIQ== +vscode-jsonrpc@9.0.0-next.4: + version "9.0.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz#ba403ddb3b82ca578179963dbe08e120a935f50d" + integrity sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ== -vscode-languageserver-protocol@3.17.6-next.3: - version "3.17.6-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.3.tgz#09d3e28e9ad12270233d07fa0b69cf1d51d7dfe4" - integrity sha512-H8ATH5SAvc3JzttS+AL6g681PiBOZM/l34WP2JZk4akY3y7NqTP+f9cJ+MhrVBbD3aDS8bdAKewZgbFLW6M8Pg== +vscode-languageserver-protocol@3.17.6-next.6: + version "3.17.6-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz#8863a4dc8b395a8c31106ffdc945a00f9163b68b" + integrity sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw== dependencies: - vscode-jsonrpc "9.0.0-next.2" - vscode-languageserver-types "3.17.6-next.3" + vscode-jsonrpc "9.0.0-next.4" + vscode-languageserver-types "3.17.6-next.4" vscode-languageserver-textdocument@^1.0.11: version "1.0.11" @@ -67,17 +67,17 @@ vscode-languageserver-types@3.17.5, vscode-languageserver-types@^3.17.5: resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== -vscode-languageserver-types@3.17.6-next.3: - version "3.17.6-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.3.tgz#f71d6c57f18d921346cfe0c227aabd72eb8cd2f0" - integrity sha512-l5kNFXFRQGuzriXpuBqFpRmkf6f6A4VoU3h95OsVkqIOoi1k7KbwSo600cIdsKSJWrPg/+vX+QMPcMw1oI7ItA== +vscode-languageserver-types@3.17.6-next.4: + version "3.17.6-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz#6670939eb98f00aa7b05021dc3dd7fe9aa4453ea" + integrity sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q== -vscode-languageserver@^10.0.0-next.2: - version "10.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-10.0.0-next.2.tgz#9a8ac58f72979961497c4fd7f6097561d4134d5f" - integrity sha512-WZdK/XO6EkNU6foYck49NpS35sahWhYFs4hwCGalH/6lhPmdUKABTnWioK/RLZKWqH8E5HdlAHQMfSBIxKBV9Q== +vscode-languageserver@^10.0.0-next.6: + version "10.0.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-10.0.0-next.6.tgz#0db118a93fe010c6b40cd04e91a15d09e7b60b60" + integrity sha512-0Lh1nhQfSxo5Ob+ayYO1QTIsDix2/Lc72Urm1KZrCFxK5zIFYaEh3QFeM9oZih4Rzs0ZkQPXXnoHtpvs5GT+Zw== dependencies: - vscode-languageserver-protocol "3.17.6-next.3" + vscode-languageserver-protocol "3.17.6-next.6" vscode-uri@^3.0.8: version "3.0.8" diff --git a/extensions/html-language-features/yarn.lock b/extensions/html-language-features/yarn.lock index d1d73407809..aa2ea1c6840 100644 --- a/extensions/html-language-features/yarn.lock +++ b/extensions/html-language-features/yarn.lock @@ -149,32 +149,32 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -vscode-jsonrpc@9.0.0-next.2: - version "9.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.2.tgz#29e9741c742c80329bba1c60ce38fd014651ba80" - integrity sha512-meIaXAgChCHzWy45QGU8YpCNyqnZQ/sYeCj32OLDDbUYsCF7AvgpdXx3nnZn9yzr8ed0Od9bW+NGphEmXsqvIQ== +vscode-jsonrpc@9.0.0-next.4: + version "9.0.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz#ba403ddb3b82ca578179963dbe08e120a935f50d" + integrity sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ== -vscode-languageclient@^10.0.0-next.3: - version "10.0.0-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-10.0.0-next.3.tgz#d7336bafafb37569ac1d8e931d20ba2a6385cc64" - integrity sha512-jJhPdZaiELpPRnCUt8kQcF2HJuvzLgeW4HOGc6dp8Je+p08ndueVT4fpSsbly6KiEHr/Ri73tNz0CSfsOye6MA== +vscode-languageclient@^10.0.0-next.8: + version "10.0.0-next.8" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-10.0.0-next.8.tgz#5afa0ced3b2ac68d31cc1c48edc4f289744542a0" + integrity sha512-D9inIHgqKayO9Tv0MeLb3XIL76yTuWmKdHqcGZKzjtQrMGJgASJDYWTapu+yAjEpDp0gmVOaCYyIlLB86ncDoQ== dependencies: minimatch "^9.0.3" semver "^7.6.0" - vscode-languageserver-protocol "3.17.6-next.4" + vscode-languageserver-protocol "3.17.6-next.6" -vscode-languageserver-protocol@3.17.6-next.4: - version "3.17.6-next.4" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.4.tgz#3c56f6eb588bb42fccc0ac54a0d5daf2d02f0a1b" - integrity sha512-/2bleKBxZLyRObS4mkpaWlVI9xGiUqMVmh/ztZ2vL4uP2XyIpraT45JBpn9AtXr0alqKJPKLuKr+/qcYULvm/w== +vscode-languageserver-protocol@3.17.6-next.6: + version "3.17.6-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz#8863a4dc8b395a8c31106ffdc945a00f9163b68b" + integrity sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw== dependencies: - vscode-jsonrpc "9.0.0-next.2" - vscode-languageserver-types "3.17.6-next.3" + vscode-jsonrpc "9.0.0-next.4" + vscode-languageserver-types "3.17.6-next.4" -vscode-languageserver-types@3.17.6-next.3: - version "3.17.6-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.3.tgz#f71d6c57f18d921346cfe0c227aabd72eb8cd2f0" - integrity sha512-l5kNFXFRQGuzriXpuBqFpRmkf6f6A4VoU3h95OsVkqIOoi1k7KbwSo600cIdsKSJWrPg/+vX+QMPcMw1oI7ItA== +vscode-languageserver-types@3.17.6-next.4: + version "3.17.6-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz#6670939eb98f00aa7b05021dc3dd7fe9aa4453ea" + integrity sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q== vscode-uri@^3.0.8: version "3.0.8" diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index f86470429a4..fa8004e2b02 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -163,7 +163,7 @@ "dependencies": { "@vscode/extension-telemetry": "^0.9.0", "request-light": "^0.7.0", - "vscode-languageclient": "^10.0.0-next.5" + "vscode-languageclient": "^10.0.0-next.8" }, "devDependencies": { "@types/node": "20.x" diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 6134fb4224d..8472ca618a4 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -15,8 +15,8 @@ "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.2.1", "request-light": "^0.7.0", - "vscode-json-languageservice": "^5.3.11", - "vscode-languageserver": "^10.0.0-next.3", + "vscode-json-languageservice": "^5.4.0", + "vscode-languageserver": "^10.0.0-next.6", "vscode-uri": "^3.0.8" }, "devDependencies": { diff --git a/extensions/json-language-features/server/yarn.lock b/extensions/json-language-features/server/yarn.lock index 669e823497d..608619637e4 100644 --- a/extensions/json-language-features/server/yarn.lock +++ b/extensions/json-language-features/server/yarn.lock @@ -24,6 +24,11 @@ jsonc-parser@^3.2.1: resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.1.tgz#031904571ccf929d7670ee8c547545081cb37f1a" integrity sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA== +jsonc-parser@^3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.3.0.tgz#030d182672c8ffc2805db95467c83ffc0b033d9d" + integrity sha512-RK1Xb5alM78sdXpB2hqqK7jxAE5jTRH05GvUiLWqh7Vbp6OPHuJYlsAMRUDYNYJTAQgkmhHgkdwOEknxwP4ojQ== + request-light@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/request-light/-/request-light-0.7.0.tgz#885628bb2f8040c26401ebf258ec51c4ae98ac2a" @@ -34,51 +39,51 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -vscode-json-languageservice@^5.3.11: - version "5.3.11" - resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.3.11.tgz#71dbc56e9b1d07a57aa6a3d5569c8b7f2c05ca05" - integrity sha512-WYS72Ymria3dn8ZbjtBbt5K71m05wY1Q6hpXV5JxUT0q75Ts0ljLmnZJAVpx8DjPgYbFD+Z8KHpWh2laKLUCtQ== +vscode-json-languageservice@^5.4.0: + version "5.4.0" + resolved "https://registry.yarnpkg.com/vscode-json-languageservice/-/vscode-json-languageservice-5.4.0.tgz#caf1aabc81b1df9faf6a97e4c34e13a2d10a8cdf" + integrity sha512-NCkkCr63OHVkE4lcb0xlUAaix6vE5gHQW4NrswbLEh3ArXj81lrGuFTsGEYEUXlNHdnc53vWPcjeSy/nMTrfXg== dependencies: "@vscode/l10n" "^0.0.18" - jsonc-parser "^3.2.1" + jsonc-parser "^3.3.0" vscode-languageserver-textdocument "^1.0.11" vscode-languageserver-types "^3.17.5" vscode-uri "^3.0.8" -vscode-jsonrpc@9.0.0-next.2: - version "9.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.2.tgz#29e9741c742c80329bba1c60ce38fd014651ba80" - integrity sha512-meIaXAgChCHzWy45QGU8YpCNyqnZQ/sYeCj32OLDDbUYsCF7AvgpdXx3nnZn9yzr8ed0Od9bW+NGphEmXsqvIQ== +vscode-jsonrpc@9.0.0-next.4: + version "9.0.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz#ba403ddb3b82ca578179963dbe08e120a935f50d" + integrity sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ== -vscode-languageserver-protocol@3.17.6-next.4: - version "3.17.6-next.4" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.4.tgz#3c56f6eb588bb42fccc0ac54a0d5daf2d02f0a1b" - integrity sha512-/2bleKBxZLyRObS4mkpaWlVI9xGiUqMVmh/ztZ2vL4uP2XyIpraT45JBpn9AtXr0alqKJPKLuKr+/qcYULvm/w== +vscode-languageserver-protocol@3.17.6-next.6: + version "3.17.6-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz#8863a4dc8b395a8c31106ffdc945a00f9163b68b" + integrity sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw== dependencies: - vscode-jsonrpc "9.0.0-next.2" - vscode-languageserver-types "3.17.6-next.3" + vscode-jsonrpc "9.0.0-next.4" + vscode-languageserver-types "3.17.6-next.4" vscode-languageserver-textdocument@^1.0.11: version "1.0.11" resolved "https://registry.yarnpkg.com/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz#0822a000e7d4dc083312580d7575fe9e3ba2e2bf" integrity sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA== -vscode-languageserver-types@3.17.6-next.3: - version "3.17.6-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.3.tgz#f71d6c57f18d921346cfe0c227aabd72eb8cd2f0" - integrity sha512-l5kNFXFRQGuzriXpuBqFpRmkf6f6A4VoU3h95OsVkqIOoi1k7KbwSo600cIdsKSJWrPg/+vX+QMPcMw1oI7ItA== +vscode-languageserver-types@3.17.6-next.4: + version "3.17.6-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz#6670939eb98f00aa7b05021dc3dd7fe9aa4453ea" + integrity sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q== vscode-languageserver-types@^3.17.5: version "3.17.5" resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz#3273676f0cf2eab40b3f44d085acbb7f08a39d8a" integrity sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg== -vscode-languageserver@^10.0.0-next.3: - version "10.0.0-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-10.0.0-next.3.tgz#a63c5ea9fab1be93d7732ab0fdc18c9b37956e07" - integrity sha512-4x1qHImf6ePji4+8PX43lnBCBfBNdi2jneGX2k5FswJhx/cxaYYmusShmmtO/clyL1iurxJacrQoXfw9+ikhvg== +vscode-languageserver@^10.0.0-next.6: + version "10.0.0-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver/-/vscode-languageserver-10.0.0-next.6.tgz#0db118a93fe010c6b40cd04e91a15d09e7b60b60" + integrity sha512-0Lh1nhQfSxo5Ob+ayYO1QTIsDix2/Lc72Urm1KZrCFxK5zIFYaEh3QFeM9oZih4Rzs0ZkQPXXnoHtpvs5GT+Zw== dependencies: - vscode-languageserver-protocol "3.17.6-next.4" + vscode-languageserver-protocol "3.17.6-next.6" vscode-uri@^3.0.8: version "3.0.8" diff --git a/extensions/json-language-features/yarn.lock b/extensions/json-language-features/yarn.lock index b7ca937103a..c825de07683 100644 --- a/extensions/json-language-features/yarn.lock +++ b/extensions/json-language-features/yarn.lock @@ -154,32 +154,32 @@ undici-types@~5.26.4: resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== -vscode-jsonrpc@9.0.0-next.2: - version "9.0.0-next.2" - resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.2.tgz#29e9741c742c80329bba1c60ce38fd014651ba80" - integrity sha512-meIaXAgChCHzWy45QGU8YpCNyqnZQ/sYeCj32OLDDbUYsCF7AvgpdXx3nnZn9yzr8ed0Od9bW+NGphEmXsqvIQ== +vscode-jsonrpc@9.0.0-next.4: + version "9.0.0-next.4" + resolved "https://registry.yarnpkg.com/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.4.tgz#ba403ddb3b82ca578179963dbe08e120a935f50d" + integrity sha512-zSVIr58lJSMYKIsZ5P7GtBbv1eEx25eNyOf0NmEzxmn1GhUNJAVAb5hkA1poKUwj1FRMwN6CeyWxZypmr8SsQQ== -vscode-languageclient@^10.0.0-next.5: - version "10.0.0-next.5" - resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-10.0.0-next.5.tgz#7431d88255a5fd99e9423659ac484b1f968200f3" - integrity sha512-JIf1WE7fvV0RElFM062bAummI433vcxuFwqoYAp+1zTVhta/jznxkTz1zs3Hbj2tiDfclf0TZ0qCxflAP1mY2Q== +vscode-languageclient@^10.0.0-next.8: + version "10.0.0-next.8" + resolved "https://registry.yarnpkg.com/vscode-languageclient/-/vscode-languageclient-10.0.0-next.8.tgz#5afa0ced3b2ac68d31cc1c48edc4f289744542a0" + integrity sha512-D9inIHgqKayO9Tv0MeLb3XIL76yTuWmKdHqcGZKzjtQrMGJgASJDYWTapu+yAjEpDp0gmVOaCYyIlLB86ncDoQ== dependencies: minimatch "^9.0.3" semver "^7.6.0" - vscode-languageserver-protocol "3.17.6-next.4" + vscode-languageserver-protocol "3.17.6-next.6" -vscode-languageserver-protocol@3.17.6-next.4: - version "3.17.6-next.4" - resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.4.tgz#3c56f6eb588bb42fccc0ac54a0d5daf2d02f0a1b" - integrity sha512-/2bleKBxZLyRObS4mkpaWlVI9xGiUqMVmh/ztZ2vL4uP2XyIpraT45JBpn9AtXr0alqKJPKLuKr+/qcYULvm/w== +vscode-languageserver-protocol@3.17.6-next.6: + version "3.17.6-next.6" + resolved "https://registry.yarnpkg.com/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.6.tgz#8863a4dc8b395a8c31106ffdc945a00f9163b68b" + integrity sha512-naxM9kc/phpl0kAFNVPejMUWUtzFXdPYY/BtQTYtfbBbHf8sceHOrKkmf6yynZRu1A4oFtRZNqV3wyFRTWqUHw== dependencies: - vscode-jsonrpc "9.0.0-next.2" - vscode-languageserver-types "3.17.6-next.3" + vscode-jsonrpc "9.0.0-next.4" + vscode-languageserver-types "3.17.6-next.4" -vscode-languageserver-types@3.17.6-next.3: - version "3.17.6-next.3" - resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.3.tgz#f71d6c57f18d921346cfe0c227aabd72eb8cd2f0" - integrity sha512-l5kNFXFRQGuzriXpuBqFpRmkf6f6A4VoU3h95OsVkqIOoi1k7KbwSo600cIdsKSJWrPg/+vX+QMPcMw1oI7ItA== +vscode-languageserver-types@3.17.6-next.4: + version "3.17.6-next.4" + resolved "https://registry.yarnpkg.com/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.4.tgz#6670939eb98f00aa7b05021dc3dd7fe9aa4453ea" + integrity sha512-SeJTpH/S14EbxOAVaOUoGVqPToqpRTld5QO5Ghig3AlbFJTFF9Wu7srHMfa85L0SX1RYAuuCSFKJVVCxDIk1/Q== yallist@^4.0.0: version "4.0.0" From b66a2b091ed79a67f7679428316fb4e49de71777 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 24 Jun 2024 11:53:17 -0700 Subject: [PATCH 0090/2222] Bump distro (#217382) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7bd20339726..3d8b1cbb209 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.91.0", - "distro": "951ec3be152d660cf34920fc3d734d32691bd880", + "distro": "44256034a3b5a2bbd6b12530ab050a2a3c2bd285", "author": { "name": "Microsoft Corporation" }, From 8d0ba61f01742f8cfc1c01b6b2ba0ae3289485a0 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Mon, 24 Jun 2024 12:24:44 -0700 Subject: [PATCH 0091/2222] removes remnants of lightbulb telemetry (#217410) remove telemetry --- .../browser/codeActionController.ts | 50 +------------------ .../codeAction/browser/lightBulbWidget.ts | 4 +- 2 files changed, 2 insertions(+), 52 deletions(-) diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index 65dc839da2c..612fd4d2923 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -39,8 +39,6 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic import { CodeActionAutoApply, CodeActionFilter, CodeActionItem, CodeActionKind, CodeActionSet, CodeActionTrigger, CodeActionTriggerSource } from 'vs/editor/contrib/codeAction/common/types'; import { CodeActionModel, CodeActionsState } from 'vs/editor/contrib/codeAction/browser/codeActionModel'; import { HierarchicalKind } from 'vs/base/common/hierarchicalKind'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; - interface IActionShowOptions { readonly includeDisabledActions?: boolean; @@ -79,8 +77,7 @@ export class CodeActionController extends Disposable implements IEditorContribut @ICommandService private readonly _commandService: ICommandService, @IConfigurationService private readonly _configurationService: IConfigurationService, @IActionWidgetService private readonly _actionWidgetService: IActionWidgetService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ITelemetryService private readonly _telemetryService: ITelemetryService + @IInstantiationService private readonly _instantiationService: IInstantiationService ) { super(); @@ -107,29 +104,6 @@ export class CodeActionController extends Disposable implements IEditorContribut } private async showCodeActionsFromLightbulb(actions: CodeActionSet, at: IAnchor | IPosition): Promise { - - // Telemetry for showing code actions from lightbulb. Shows us how often it was clicked. - type ShowCodeActionListEvent = { - codeActionListLength: number; - codeActions: string[]; - codeActionProviders: string[]; - }; - - type ShowListEventClassification = { - codeActionListLength: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The length of the code action list from the lightbulb widget.' }; - codeActions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The title of code actions in this menu.' }; - codeActionProviders: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The provider of code actions in this menu.' }; - owner: 'justschen'; - comment: 'Event used to gain insights into what code actions are being shown'; - }; - - this._telemetryService.publicLog2('codeAction.showCodeActionsFromLightbulb', { - codeActionListLength: actions.validActions.length, - codeActions: actions.validActions.map(action => action.action.title), - codeActionProviders: actions.validActions.map(action => action.provider?.displayName ?? ''), - }); - - if (actions.allAIFixes && actions.validActions.length === 1) { const actionItem = actions.validActions[0]; const command = actionItem.action.command; @@ -312,28 +286,6 @@ export class CodeActionController extends Disposable implements IEditorContribut onHide: (didCancel?) => { this._editor?.focus(); currentDecorations.clear(); - // Telemetry for showing code actions here. only log on `showLightbulb`. Logs when code action list is quit out. - if (options.fromLightbulb && didCancel !== undefined) { - type ShowCodeActionListEvent = { - codeActionListLength: number; - didCancel: boolean; - codeActions: string[]; - }; - - type ShowListEventClassification = { - codeActionListLength: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The length of the code action list when quit out. Can be from any code action menu.' }; - didCancel: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the code action was cancelled or selected.' }; - codeActions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'What code actions were available when cancelled.' }; - owner: 'justschen'; - comment: 'Event used to gain insights into how many valid code actions are being shown'; - }; - - this._telemetryService.publicLog2('codeAction.showCodeActionList.onHide', { - codeActionListLength: actions.validActions.length, - didCancel: didCancel, - codeActions: actions.validActions.map(action => action.action.title), - }); - } }, onHover: async (action: CodeActionItem, token: CancellationToken) => { if (token.isCancellationRequested) { diff --git a/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts b/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts index bd391744ab3..45fc9aa9cb7 100644 --- a/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts +++ b/src/vs/editor/contrib/codeAction/browser/lightBulbWidget.ts @@ -17,7 +17,6 @@ import { computeIndentLevel } from 'vs/editor/common/model/utils'; import { autoFixCommandId, quickFixCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; import { CodeActionSet, CodeActionTrigger } from 'vs/editor/contrib/codeAction/common/types'; import * as nls from 'vs/nls'; -import { ICommandService } from 'vs/platform/commands/common/commands'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; namespace LightBulbState { @@ -62,8 +61,7 @@ export class LightBulbWidget extends Disposable implements IContentWidget { constructor( private readonly _editor: ICodeEditor, - @IKeybindingService private readonly _keybindingService: IKeybindingService, - @ICommandService commandService: ICommandService + @IKeybindingService private readonly _keybindingService: IKeybindingService ) { super(); From 53e6a2324a3803d46a4c74d864afefc5213827d1 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 24 Jun 2024 13:19:27 -0700 Subject: [PATCH 0092/2222] Update endgame ghinb -> June Milestone (#217475) update endgame ghinb --- .vscode/notebooks/endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index d7836922bad..e1c0a9fce02 100644 --- a/.vscode/notebooks/endgame.github-issues +++ b/.vscode/notebooks/endgame.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"May 2024\"" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n$MILESTONE=milestone:\"June 2024\"" }, { "kind": 1, From aef28862c491e0d57b10f70e703932504afbcf63 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 24 Jun 2024 23:19:37 +0200 Subject: [PATCH 0093/2222] Allow multiple extensions to provide default values for object settings (#217179) * Allow multiple extensions to provide default values for object settings * Fix merge conflict * type check * fix tests --- .../common/configurationRegistry.ts | 154 ++++++++++++-- .../test/common/configurationRegistry.test.ts | 32 ++- .../test/common/configurations.test.ts | 61 +++++- .../browser/workbench.contribution.ts | 2 +- .../settingsEditorSettingIndicators.ts | 50 ++++- .../preferences/browser/settingsTree.ts | 157 +++++++++------ .../preferences/browser/settingsTreeModels.ts | 24 ++- .../preferences/browser/settingsWidgets.ts | 190 ++++++++++++------ .../preferences/common/preferences.ts | 4 +- .../preferences/common/preferencesModels.ts | 4 +- 10 files changed, 516 insertions(+), 162 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index ed8e56d50c5..d7e9ae576b0 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -233,21 +233,24 @@ export interface IConfigurationNode { restrictedProperties?: string[]; } +export type ConfigurationDefaultSource = IExtensionInfo | string; +export type ConfigurationDefaultValueSource = ConfigurationDefaultSource | Map; + export interface IConfigurationDefaults { overrides: IStringDictionary; - source?: IExtensionInfo | string; + source?: ConfigurationDefaultSource; } export type IRegisteredConfigurationPropertySchema = IConfigurationPropertySchema & { defaultDefaultValue?: any; source?: IExtensionInfo; // Source of the Property - defaultValueSource?: IExtensionInfo | string; // Source of the Default Value + defaultValueSource?: ConfigurationDefaultValueSource; // Source of the Default Value }; export type IConfigurationDefaultOverride = { readonly value: any; - readonly source?: IExtensionInfo | string; // Source of the default override - readonly valuesSources?: Map; // Source of each value in default language overrides + readonly source?: ConfigurationDefaultValueSource; // Source of the default override + readonly valuesSources?: Map; // Source of each value in default language overrides }; export const allSettings: { properties: IStringDictionary; patternProperties: IStringDictionary } = { properties: {}, patternProperties: {} }; @@ -351,13 +354,42 @@ class ConfigurationRegistry implements IConfigurationRegistry { if (OVERRIDE_PROPERTY_REGEX.test(key)) { const configurationDefaultOverride = this.configurationDefaultsOverrides.get(key); - const valuesSources = configurationDefaultOverride?.valuesSources ?? new Map(); - if (source) { - for (const configuration of Object.keys(overrides[key])) { - valuesSources.set(configuration, source); + const valuesSources = configurationDefaultOverride?.valuesSources ?? new Map(); + + const defaultValue = configurationDefaultOverride?.value || {}; + for (const configuration of Object.keys(overrides[key])) { + const overrideValue = overrides[key][configuration]; + + const isObjectSetting = types.isObject(overrideValue) && (types.isUndefined(defaultValue[configuration]) || types.isObject(defaultValue[configuration])); + if (isObjectSetting) { + // Objects are merged instead of overridden + defaultValue[configuration] = { ...(defaultValue[configuration] ?? {}), ...overrideValue }; + + // Track the source of each value in the object + if (source) { + let objectConfigurationSources = valuesSources.get(configuration); + if (!objectConfigurationSources) { + objectConfigurationSources = new Map(); + valuesSources.set(configuration, objectConfigurationSources); + } + if (!(objectConfigurationSources instanceof Map)) { + console.error('objectConfigurationSources is not a Map'); + continue; + } + + for (const objectKey in overrideValue) { + objectConfigurationSources.set(objectKey, source); + } + } + } else { + // Primitive values are overridden + defaultValue[configuration] = overrideValue; + if (source) { + valuesSources.set(configuration, source); + } } } - const defaultValue = { ...(configurationDefaultOverride?.value || {}), ...overrides[key] }; + this.configurationDefaultsOverrides.set(key, { source, value: defaultValue, valuesSources }); const plainKey = getLanguageTagSettingPlainKey(key); const property: IRegisteredConfigurationPropertySchema = { @@ -373,8 +405,33 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.configurationProperties[key] = property; this.defaultLanguageConfigurationOverridesNode.properties![key] = property; } else { - this.configurationDefaultsOverrides.set(key, { value: overrides[key], source }); const property = this.configurationProperties[key]; + let defaultValue = overrides[key]; + let defaultValueSource: ConfigurationDefaultValueSource | undefined = source; + + // If the default value is an object, merge the objects and store the source of each keys + if (property.type === 'object' && types.isObject(overrides[key])) { + const objectDefaults = this.configurationDefaultsOverrides.get(key); + const existingDefaultValue = objectDefaults?.value ?? property.defaultDefaultValue ?? {}; + defaultValue = { ...existingDefaultValue, ...overrides[key] }; + + defaultValueSource = objectDefaults?.source ?? new Map(); + if (!(defaultValueSource instanceof Map)) { + console.error('defaultValueSource is not a Map'); + continue; + } + + for (const objectKey in overrides[key]) { + if (source) { + defaultValueSource.set(objectKey, source); + } else { + defaultValueSource.delete(objectKey); + } + } + } + + this.configurationDefaultsOverrides.set(key, { value: defaultValue, source: defaultValueSource }); + if (property) { this.updatePropertyDefaultValue(key, property); this.updateSchema(key, property); @@ -397,24 +454,87 @@ class ConfigurationRegistry implements IConfigurationRegistry { for (const { overrides, source } of defaultConfigurations) { for (const key in overrides) { - const configurationDefaultsOverride = this.configurationDefaultsOverrides.get(key); const id = types.isString(source) ? source : source?.id; - const configurationDefaultsOverrideSourceId = types.isString(configurationDefaultsOverride?.source) ? configurationDefaultsOverride?.source : configurationDefaultsOverride?.source?.id; - if (id !== configurationDefaultsOverrideSourceId) { + + const configurationDefaultsOverride = this.configurationDefaultsOverrides.get(key); + if (!configurationDefaultsOverride) { continue; } - bucket.add(key); - this.configurationDefaultsOverrides.delete(key); + if (OVERRIDE_PROPERTY_REGEX.test(key)) { - delete this.configurationProperties[key]; - delete this.defaultLanguageConfigurationOverridesNode.properties![key]; + for (const configuration of Object.keys(overrides[key])) { + const overrideValue = overrides[key][configuration]; + + if (types.isObject(overrideValue)) { + const configurationSource = configurationDefaultsOverride.valuesSources?.get(configuration) as Map | undefined; + + for (const overrideObjectKey of Object.keys(overrideValue)) { + const keySource = configurationSource?.get(overrideObjectKey); + const keySourceId = types.isString(keySource) ? keySource : keySource?.id; + if (keySourceId === id) { + configurationSource?.delete(overrideObjectKey); + delete configurationDefaultsOverride.value[configuration][overrideObjectKey]; + } + } + + if (Object.keys(configurationDefaultsOverride.value[configuration]).length === 0) { + delete configurationDefaultsOverride.value[configuration]; + configurationDefaultsOverride.valuesSources?.delete(configuration); + } + } else { + const configurationSource = configurationDefaultsOverride.valuesSources?.get(configuration) as string | IExtensionInfo | undefined; + + const keySourceId = types.isString(configurationSource) ? configurationSource : configurationSource?.id; + if (keySourceId === id) { + configurationDefaultsOverride.valuesSources?.delete(configuration); + delete configurationDefaultsOverride.value[configuration]; + } + } + } + // Remove language configuration if empty ({[css]: {}} => {}) + const languageValues = this.configurationDefaultsOverrides.get(key); + if (languageValues && Object.keys(languageValues.value).length === 0) { + this.configurationDefaultsOverrides.delete(key); + delete this.configurationProperties[key]; + delete this.defaultLanguageConfigurationOverridesNode.properties![key]; + } } else { + // If the default value is an object, remove the source of each key + if (configurationDefaultsOverride.source instanceof Map) { + + const keySources = configurationDefaultsOverride.source; + for (const objectKey in overrides[key]) { + const keySource = keySources.get(objectKey); + const keySourceId = types.isString(keySource) ? keySource : keySource?.id; + + if (keySourceId === id) { + keySources.delete(objectKey); + delete configurationDefaultsOverride.value[objectKey]; + } + } + + if (keySources.size === 0) { + this.configurationDefaultsOverrides.delete(key); + } + } + // Otherwise, remove the default value if the source matches + else { + const configurationDefaultsOverrideSourceId = types.isString(configurationDefaultsOverride.source) ? configurationDefaultsOverride.source : configurationDefaultsOverride.source?.id; + if (id !== configurationDefaultsOverrideSourceId) { + continue; // Another source is overriding this default value + } + + this.configurationDefaultsOverrides.delete(key); + + } const property = this.configurationProperties[key]; if (property) { this.updatePropertyDefaultValue(key, property); this.updateSchema(key, property); } } + + bucket.add(key); } } diff --git a/src/vs/platform/configuration/test/common/configurationRegistry.test.ts b/src/vs/platform/configuration/test/common/configurationRegistry.test.ts index e2cf8972a2c..24dc735cd11 100644 --- a/src/vs/platform/configuration/test/common/configurationRegistry.test.ts +++ b/src/vs/platform/configuration/test/common/configurationRegistry.test.ts @@ -38,7 +38,7 @@ suite('ConfigurationRegistry', () => { assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, b: 2, c: 3 }); }); - test('configuration defaults - overrides defaults', async () => { + test('configuration defaults - merge object default overrides', async () => { configurationRegistry.registerConfiguration({ 'id': '_test_default', 'type': 'object', @@ -51,7 +51,7 @@ suite('ConfigurationRegistry', () => { configurationRegistry.registerDefaultConfigurations([{ overrides: { 'config': { a: 1, b: 2 } } }]); configurationRegistry.registerDefaultConfigurations([{ overrides: { 'config': { a: 2, c: 3 } } }]); - assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, c: 3 }); + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, b: 2, c: 3 }); }); test('registering multiple settings with same policy', async () => { @@ -79,4 +79,32 @@ suite('ConfigurationRegistry', () => { assert.ok(actual['policy1'] !== undefined); assert.ok(actual['policy2'] === undefined); }); + + test('configuration defaults - deregister merged object default override', async () => { + configurationRegistry.registerConfiguration({ + 'id': '_test_default', + 'type': 'object', + 'properties': { + 'config': { + 'type': 'object', + } + } + }); + + const overrides1 = [{ overrides: { 'config': { a: 1, b: 2 } }, source: 'source1' }]; + const overrides2 = [{ overrides: { 'config': { a: 2, c: 3 } }, source: 'source2' }]; + + configurationRegistry.registerDefaultConfigurations(overrides1); + configurationRegistry.registerDefaultConfigurations(overrides2); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, b: 2, c: 3 }); + + configurationRegistry.deregisterDefaultConfigurations(overrides2); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { b: 2 }); // TODO this should actualy equal overrides1 + + configurationRegistry.deregisterDefaultConfigurations(overrides1); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, {}); + }); }); diff --git a/src/vs/platform/configuration/test/common/configurations.test.ts b/src/vs/platform/configuration/test/common/configurations.test.ts index f86e6f66249..eb02aec4f9a 100644 --- a/src/vs/platform/configuration/test/common/configurations.test.ts +++ b/src/vs/platform/configuration/test/common/configurations.test.ts @@ -110,7 +110,7 @@ suite('DefaultConfiguration', () => { assert.ok(equals(actual.getValue('a'), { b: { c: '2' } })); assert.ok(equals(actual.contents, { 'a': { b: { c: '2' } } })); - assert.deepStrictEqual(actual.keys, ['a.b', 'a.b.c']); + assert.deepStrictEqual(actual.keys.sort(), ['a.b', 'a.b.c']); }); test('Test registering the same property again', async () => { @@ -158,7 +158,7 @@ suite('DefaultConfiguration', () => { assert.ok(equals(actual.getValue('[a]'), { 'b': true })); assert.ok(equals(actual.contents, { '[a]': { 'b': true } })); assert.ok(equals(actual.overrides, [{ contents: { 'b': true }, identifiers: ['a'], keys: ['b'] }])); - assert.deepStrictEqual(actual.keys, ['[a]']); + assert.deepStrictEqual(actual.keys.sort(), ['[a]']); assert.strictEqual(actual.getOverrideValue('b', 'a'), true); }); @@ -191,7 +191,7 @@ suite('DefaultConfiguration', () => { assert.ok(equals(actual.getValue('[a]'), { 'b': true })); assert.ok(equals(actual.contents, { 'b': false, '[a]': { 'b': true } })); assert.ok(equals(actual.overrides, [{ contents: { 'b': true }, identifiers: ['a'], keys: ['b'] }])); - assert.deepStrictEqual(actual.keys, ['b', '[a]']); + assert.deepStrictEqual(actual.keys.sort(), ['[a]', 'b']); assert.strictEqual(actual.getOverrideValue('b', 'a'), true); }); @@ -227,7 +227,7 @@ suite('DefaultConfiguration', () => { assert.ok(equals(actual.getValue('[a]'), { 'b': true })); assert.ok(equals(actual.contents, { 'b': false, '[a]': { 'b': true } })); assert.ok(equals(actual.overrides, [{ contents: { 'b': true }, identifiers: ['a'], keys: ['b'] }])); - assert.deepStrictEqual(actual.keys, ['[a]', 'b']); + assert.deepStrictEqual(actual.keys.sort(), ['[a]', 'b']); assert.strictEqual(actual.getOverrideValue('b', 'a'), true); assert.deepStrictEqual(properties, ['b']); }); @@ -263,7 +263,7 @@ suite('DefaultConfiguration', () => { assert.ok(equals(actual.getValue('[a]'), { 'b': true })); assert.ok(equals(actual.contents, { 'b': false, '[a]': { 'b': true } })); assert.ok(equals(actual.overrides, [{ contents: { 'b': true }, identifiers: ['a'], keys: ['b'] }])); - assert.deepStrictEqual(actual.keys, ['b', '[a]']); + assert.deepStrictEqual(actual.keys.sort(), ['[a]', 'b']); assert.strictEqual(actual.getOverrideValue('b', 'a'), true); assert.deepStrictEqual(properties, ['[a]']); }); @@ -299,7 +299,7 @@ suite('DefaultConfiguration', () => { assert.ok(equals(actual.getValue('[a]'), { 'b': true })); assert.ok(equals(actual.contents, { 'b': false, '[a]': { 'b': true } })); assert.ok(equals(actual.overrides, [{ contents: { 'b': true }, identifiers: ['a'], keys: ['b'] }])); - assert.deepStrictEqual(actual.keys, ['b', '[a]']); + assert.deepStrictEqual(actual.keys.sort(), ['[a]', 'b']); assert.strictEqual(actual.getOverrideValue('b', 'a'), true); }); @@ -361,4 +361,53 @@ suite('DefaultConfiguration', () => { assert.deepStrictEqual(testObject.configurationModel.keys, ['b']); assert.strictEqual(testObject.configurationModel.getOverrideValue('b', 'a'), undefined); }); + + test('Test deregistering a merged language object setting', async () => { + const testObject = disposables.add(new DefaultConfiguration(new NullLogService())); + configurationRegistry.registerConfiguration({ + 'id': 'b', + 'order': 1, + 'title': 'b', + 'type': 'object', + 'properties': { + 'b': { + 'description': 'b', + 'type': 'object', + 'default': {}, + } + } + }); + const node1 = { + overrides: { + '[a]': { + 'b': { + 'aa': '1', + 'bb': '2' + } + } + }, + source: 'source1' + }; + + const node2 = { + overrides: { + '[a]': { + 'b': { + 'bb': '20', + 'cc': '30' + } + } + }, + source: 'source2' + }; + configurationRegistry.registerDefaultConfigurations([node1]); + configurationRegistry.registerDefaultConfigurations([node2]); + await testObject.initialize(); + configurationRegistry.deregisterDefaultConfigurations([node1]); + assert.ok(equals(testObject.configurationModel.getValue('[a]'), { 'b': { 'bb': '20', 'cc': '30' } })); + assert.ok(equals(testObject.configurationModel.contents, { '[a]': { 'b': { 'bb': '20', 'cc': '30' } }, 'b': {} })); + //assert.ok(equals(testObject.configurationModel.overrides, [{ '[a]': { 'b': { 'bb': '20', 'cc': '30' } } }])); TODO: Check this later + //assert.deepStrictEqual(testObject.configurationModel.keys.sort(), ['[a]', 'b']); + assert.ok(equals(testObject.configurationModel.getOverrideValue('b', 'a'), { 'bb': '20', 'cc': '30' })); + }); }); diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index a5a3f44d17c..ae4d22c9eaa 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -113,7 +113,7 @@ const registry = Registry.as(ConfigurationExtensions.Con })(), additionalProperties: { - type: 'string', + type: ['string', 'null'], markdownDescription: localize('workbench.editor.label.template', "The template which should be rendered when the pattern matches. May include the variables ${dirname}, ${filename} and ${extname}."), minLength: 1, pattern: '.*[a-zA-Z0-9].*' diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts index 6db2e7883d9..59b31491a53 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators.ts @@ -9,7 +9,7 @@ import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; import { SimpleIconLabel } from 'vs/base/browser/ui/iconLabel/simpleIconLabel'; import { RunOnceScheduler } from 'vs/base/common/async'; import { Emitter } from 'vs/base/common/event'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { ILanguageService } from 'vs/editor/common/languages/language'; @@ -448,15 +448,27 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { updateDefaultOverrideIndicator(element: SettingsTreeSettingElement) { this.defaultOverrideIndicator.element.style.display = 'none'; - const sourceToDisplay = getDefaultValueSourceToDisplay(element); + let sourceToDisplay = getDefaultValueSourceToDisplay(element); if (sourceToDisplay !== undefined) { this.defaultOverrideIndicator.element.style.display = 'inline'; this.defaultOverrideIndicator.disposables.clear(); - const defaultOverrideHoverContent = localize('defaultOverriddenDetails', "Default setting value overridden by {0}", sourceToDisplay); + // Show source of default value when hovered + if (Array.isArray(sourceToDisplay) && sourceToDisplay.length === 1) { + sourceToDisplay = sourceToDisplay[0]; + } + + let defaultOverrideHoverContent; + if (!Array.isArray(sourceToDisplay)) { + defaultOverrideHoverContent = localize('defaultOverriddenDetails', "Default setting value overridden by `{0}`", sourceToDisplay); + } else { + sourceToDisplay = sourceToDisplay.map(source => `\`${source}\``); + defaultOverrideHoverContent = localize('multipledefaultOverriddenDetails', "A default values has been set by {0}", sourceToDisplay.slice(0, -1).join(', ') + ' & ' + sourceToDisplay.slice(-1)); + } + const showHover = (focus: boolean) => { return this.hoverService.showHover({ - content: defaultOverrideHoverContent, + content: new MarkdownString().appendMarkdown(defaultOverrideHoverContent), target: this.defaultOverrideIndicator.element, position: { hoverPosition: HoverPosition.BELOW, @@ -473,14 +485,22 @@ export class SettingsTreeIndicatorsLabel implements IDisposable { } } -function getDefaultValueSourceToDisplay(element: SettingsTreeSettingElement): string | undefined { - let sourceToDisplay: string | undefined; +function getDefaultValueSourceToDisplay(element: SettingsTreeSettingElement): string | undefined | string[] { + let sourceToDisplay: string | undefined | string[]; const defaultValueSource = element.defaultValueSource; if (defaultValueSource) { - if (typeof defaultValueSource !== 'string') { - sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; + if (defaultValueSource instanceof Map) { + sourceToDisplay = []; + for (const [, value] of defaultValueSource) { + const newValue = typeof value !== 'string' ? value.displayName ?? value.id : value; + if (!sourceToDisplay.includes(newValue)) { + sourceToDisplay.push(newValue); + } + } } else if (typeof defaultValueSource === 'string') { sourceToDisplay = defaultValueSource; + } else { + sourceToDisplay = defaultValueSource.displayName ?? defaultValueSource.id; } } return sourceToDisplay; @@ -538,9 +558,19 @@ export function getIndicatorsLabelAriaLabel(element: SettingsTreeSettingElement, } // Add default override indicator text - const sourceToDisplay = getDefaultValueSourceToDisplay(element); + let sourceToDisplay = getDefaultValueSourceToDisplay(element); if (sourceToDisplay !== undefined) { - ariaLabelSections.push(localize('defaultOverriddenDetailsAriaLabel', "{0} overrides the default value", sourceToDisplay)); + if (Array.isArray(sourceToDisplay) && sourceToDisplay.length === 1) { + sourceToDisplay = sourceToDisplay[0]; + } + + let overriddenDetailsText; + if (!Array.isArray(sourceToDisplay)) { + overriddenDetailsText = localize('defaultOverriddenDetailsAriaLabel', "{0} overrides the default value", sourceToDisplay); + } else { + overriddenDetailsText = localize('multipleDefaultOverriddenDetailsAriaLabel', "{0} override the default value", sourceToDisplay.slice(0, -1).join(', ') + ' & ' + sourceToDisplay.slice(-1)); + } + ariaLabelSections.push(overriddenDetailsText); } // Add text about default values being overridden in other languages diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 79dadc81822..06efb414415 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -60,8 +60,8 @@ import { settingsMoreActionIcon } from 'vs/workbench/contrib/preferences/browser import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/preferencesWidgets'; import { ISettingOverrideClickEvent, SettingsTreeIndicatorsLabel, getIndicatorsLabelAriaLabel } from 'vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; -import { ISettingsEditorViewState, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement, inspectSetting, settingKeyToDisplayFormat } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; -import { ExcludeSettingWidget, IListDataItem, IObjectDataItem, IObjectEnumOption, IObjectKeySuggester, IObjectValueSuggester, ISettingListChangeEvent, IncludeSettingWidget, ListSettingWidget, ObjectSettingCheckboxWidget, ObjectSettingDropdownWidget, ObjectValue } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; +import { ISettingsEditorViewState, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement, inspectSetting, objectSettingSupportsRemoveDefaultValue, settingKeyToDisplayFormat } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; +import { ExcludeSettingWidget, IIncludeExcludeDataItem, IListDataItem, IObjectDataItem, IObjectEnumOption, IObjectKeySuggester, IObjectValueSuggester, ISettingListChangeEvent, IncludeSettingWidget, ListSettingWidget, ObjectSettingCheckboxWidget, ObjectSettingDropdownWidget, ObjectValue, SettingListEvent } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { LANGUAGE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, compareTwoNullableNumbers } from 'vs/workbench/contrib/preferences/common/preferences'; import { settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { APPLY_ALL_PROFILES_SETTING, IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; @@ -74,14 +74,27 @@ import { IHoverService } from 'vs/platform/hover/browser/hover'; const $ = DOM.$; -function getIncludeExcludeDisplayValue(element: SettingsTreeSettingElement): IListDataItem[] { +function getIncludeExcludeDisplayValue(element: SettingsTreeSettingElement): IIncludeExcludeDataItem[] { + const elementDefaultValue: Record = typeof element.defaultValue === 'object' + ? element.defaultValue ?? {} + : {}; + const data = element.isConfigured ? - { ...element.defaultValue, ...element.scopeValue } : - element.defaultValue; + { ...elementDefaultValue, ...element.scopeValue } : + elementDefaultValue; return Object.keys(data) .filter(key => !!data[key]) .map(key => { + const defaultValue = elementDefaultValue[key]; + + // Get source if it's a default value + let source: string | undefined; + if (defaultValue === data[key] && element.setting.type === 'object' && element.defaultValueSource instanceof Map) { + const defaultSource = element.defaultValueSource.get(key); + source = typeof defaultSource === 'string' ? defaultSource : defaultSource?.displayName; + } + const value = data[key]; const sibling = typeof value === 'boolean' ? undefined : value.when; return { @@ -90,7 +103,8 @@ function getIncludeExcludeDisplayValue(element: SettingsTreeSettingElement): ILi data: key }, sibling, - elementType: element.valueType + elementType: element.valueType, + source }; }); } @@ -162,6 +176,14 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData return Object.keys(data).map(key => { const defaultValue = elementDefaultValue[key]; + + // Get source if it's a default value + let source: string | undefined; + if (defaultValue === data[key] && element.setting.type === 'object' && element.defaultValueSource instanceof Map) { + const defaultSource = element.defaultValueSource.get(key); + source = typeof defaultSource === 'string' ? defaultSource : defaultSource?.displayName; + } + if (isDefined(objectProperties) && key in objectProperties) { if (element.setting.allKeysAreBoolean) { return { @@ -174,7 +196,9 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData data: data[key] }, keyDescription: objectProperties[key].description, - removable: false + removable: false, + resetable: true, + source } as IObjectDataItem; } @@ -192,12 +216,15 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData }, keyDescription: objectProperties[key].description, removable: isUndefinedOrNull(defaultValue), + resetable: !isUndefinedOrNull(defaultValue), + source } as IObjectDataItem; } - // The row is removable if it doesn't have a default value assigned. - // Otherwise, it is not removable, but its value can be reset to the default. - const removable = !defaultValue; + // The row is removable if it doesn't have a default value assigned or the setting supports removing the default value. + // If a default value is assigned and the user modified the default, it can be reset back to the default. + const removable = defaultValue === undefined || objectSettingSupportsRemoveDefaultValue(element.setting.key); + const resetable = defaultValue && defaultValue !== data[key]; const schema = patternsAndSchemas.find(({ pattern }) => pattern.test(key))?.schema; if (schema) { const valueEnumOptions = getEnumOptionsFromSchema(schema); @@ -210,6 +237,8 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData }, keyDescription: schema.description, removable, + resetable, + source } as IObjectDataItem; } @@ -228,6 +257,8 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData }, keyDescription: typeof objectAdditionalProperties === 'object' ? objectAdditionalProperties.description : undefined, removable, + resetable, + source } as IObjectDataItem; }).filter(item => !isUndefinedOrNull(item.value.data)); } @@ -629,12 +660,12 @@ interface ISettingComplexItemTemplate extends ISettingItemTemplate { } interface ISettingListItemTemplate extends ISettingItemTemplate { - listWidget: ListSettingWidget; + listWidget: ListSettingWidget; validationErrorMessageElement: HTMLElement; } interface ISettingIncludeExcludeItemTemplate extends ISettingItemTemplate { - includeExcludeWidget: ListSettingWidget; + includeExcludeWidget: ListSettingWidget; } interface ISettingObjectItemTemplate extends ISettingItemTemplate | undefined> { @@ -1174,7 +1205,7 @@ class SettingArrayRenderer extends AbstractSettingRenderer implements ITreeRende return template; } - private computeNewList(template: ISettingListItemTemplate, e: ISettingListChangeEvent): string[] | undefined { + private computeNewList(template: ISettingListItemTemplate, e: SettingListEvent): string[] | undefined { if (template.context) { let newValue: string[] = []; if (Array.isArray(template.context.scopeValue)) { @@ -1183,33 +1214,28 @@ class SettingArrayRenderer extends AbstractSettingRenderer implements ITreeRende newValue = [...template.context.value]; } - if (e.sourceIndex !== undefined) { + if (e.type === 'move') { // A drag and drop occurred const sourceIndex = e.sourceIndex; - const targetIndex = e.targetIndex!; + const targetIndex = e.targetIndex; const splicedElem = newValue.splice(sourceIndex, 1)[0]; newValue.splice(targetIndex, 0, splicedElem); - } else if (e.targetIndex !== undefined) { - const itemValueData = e.item?.value.data.toString() ?? ''; - // Delete value - if (!e.item?.value.data && e.originalItem.value.data && e.targetIndex > -1) { - newValue.splice(e.targetIndex, 1); - } + } else if (e.type === 'remove' || e.type === 'reset') { + newValue.splice(e.targetIndex, 1); + } else if (e.type === 'change') { + const itemValueData = e.newItem.value.data.toString(); + // Update value - else if (e.item?.value.data && e.originalItem.value.data) { - if (e.targetIndex > -1) { - newValue[e.targetIndex] = itemValueData; - } - // For some reason, we are updating and cannot find original value - // Just append the value in this case - else { - newValue.push(itemValueData); - } + if (e.targetIndex > -1) { + newValue[e.targetIndex] = itemValueData; } - // Add value - else if (e.item?.value.data && !e.originalItem.value.data && e.targetIndex >= newValue.length) { + // For some reason, we are updating and cannot find original value + // Just append the value in this case + else { newValue.push(itemValueData); } + } else if (e.type === 'add') { + newValue.push(e.newItem.value.data.toString()); } if ( @@ -1288,9 +1314,10 @@ abstract class AbstractSettingObjectRenderer extends AbstractSettingRenderer imp return template; } - protected onDidChangeObject(template: ISettingObjectItemTemplate, e: ISettingListChangeEvent): void { + protected onDidChangeObject(template: ISettingObjectItemTemplate, e: SettingListEvent): void { const widget = (template.objectCheckboxWidget ?? template.objectDropdownWidget)!; if (template.context) { + const settingSupportsRemoveDefault = objectSettingSupportsRemoveDefaultValue(template.context.setting.key); const defaultValue: Record = typeof template.context.defaultValue === 'object' ? template.context.defaultValue ?? {} : {}; @@ -1299,45 +1326,55 @@ abstract class AbstractSettingObjectRenderer extends AbstractSettingRenderer imp ? template.context.scopeValue ?? {} : {}; - const newValue: Record = {}; + const newValue: Record = { ...template.context.scopeValue }; // Initialize with scoped values as removed default values are not rendered const newItems: IObjectDataItem[] = []; widget.items.forEach((item, idx) => { // Item was updated - if (isDefined(e.item) && e.targetIndex === idx) { - newValue[e.item.key.data] = e.item.value.data; - newItems.push(e.item); + if ((e.type === 'change' || e.type === 'move') && e.targetIndex === idx) { + // If the key of the default value is changed, remove the default value + if (e.originalItem.key.data !== e.newItem.key.data && settingSupportsRemoveDefault && e.originalItem.key.data in defaultValue) { + newValue[e.originalItem.key.data] = null; + } + newValue[e.newItem.key.data] = e.newItem.value.data; + newItems.push(e.newItem); } // All remaining items, but skip the one that we just updated - else if (isUndefinedOrNull(e.item) || e.item.key.data !== item.key.data) { + else if ((e.type !== 'change' && e.type !== 'move') || e.newItem.key.data !== item.key.data) { newValue[item.key.data] = item.value.data; newItems.push(item); } }); // Item was deleted - if (isUndefinedOrNull(e.item)) { - delete newValue[e.originalItem.key.data]; + if (e.type === 'remove' || e.type === 'reset') { + const objectKey = e.originalItem.key.data; + const removingDefaultValue = e.type === 'remove' && settingSupportsRemoveDefault && defaultValue[objectKey] === e.originalItem.value.data; + if (removingDefaultValue) { + newValue[objectKey] = null; + } else { + delete newValue[objectKey]; + } - const itemToDelete = newItems.findIndex(item => item.key.data === e.originalItem.key.data); - const defaultItemValue = defaultValue[e.originalItem.key.data] as string | boolean; + const itemToDelete = newItems.findIndex(item => item.key.data === objectKey); + const defaultItemValue = defaultValue[objectKey] as string | boolean; - // Item does not have a default - if (isUndefinedOrNull(defaultValue[e.originalItem.key.data]) && itemToDelete > -1) { + // Item does not have a default or default is bing removed + if (removingDefaultValue || isUndefinedOrNull(defaultValue[objectKey]) && itemToDelete > -1) { newItems.splice(itemToDelete, 1); - } else if (itemToDelete > -1) { + } else if (!removingDefaultValue && itemToDelete > -1) { newItems[itemToDelete].value.data = defaultItemValue; } } // New item was added - else if (widget.isItemNew(e.originalItem) && e.item.key.data !== '') { - newValue[e.item.key.data] = e.item.value.data; - newItems.push(e.item); + else if (e.type === 'add') { + newValue[e.newItem.key.data] = e.newItem.value.data; + newItems.push(e.newItem); } Object.entries(newValue).forEach(([key, value]) => { // value from the scope has changed back to the default - if (scopeValue[key] !== value && defaultValue[key] === value) { + if (scopeValue[key] !== value && defaultValue[key] === value && !(settingSupportsRemoveDefault && value === null)) { delete newValue[key]; } }); @@ -1462,25 +1499,27 @@ abstract class SettingIncludeExcludeRenderer extends AbstractSettingRenderer imp return template; } - private onDidChangeIncludeExclude(template: ISettingIncludeExcludeItemTemplate, e: ISettingListChangeEvent): void { + private onDidChangeIncludeExclude(template: ISettingIncludeExcludeItemTemplate, e: SettingListEvent): void { if (template.context) { const newValue = { ...template.context.scopeValue }; // first delete the existing entry, if present - if (e.originalItem.value.data.toString() in template.context.defaultValue) { - // delete a default by overriding it - newValue[e.originalItem.value.data.toString()] = false; - } else { - delete newValue[e.originalItem.value.data.toString()]; + if (e.type !== 'add') { + if (e.originalItem.value.data.toString() in template.context.defaultValue) { + // delete a default by overriding it + newValue[e.originalItem.value.data.toString()] = false; + } else { + delete newValue[e.originalItem.value.data.toString()]; + } } // then add the new or updated entry, if present - if (e.item?.value) { - if (e.item.value.data.toString() in template.context.defaultValue && !e.item.sibling) { + if (e.type === 'change' || e.type === 'add' || e.type === 'move') { + if (e.newItem.value.data.toString() in template.context.defaultValue && !e.newItem.sibling) { // add a default by deleting its override - delete newValue[e.item.value.data.toString()]; + delete newValue[e.newItem.value.data.toString()]; } else { - newValue[e.item.value.data.toString()] = e.item.sibling ? { when: e.item.sibling } : true; + newValue[e.newItem.value.data.toString()] = e.newItem.sibling ? { when: e.newItem.sibling } : true; } } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index e30c3bb549a..e651ffdccf7 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -17,7 +17,7 @@ import { FOLDER_SCOPES, WORKSPACE_SCOPES, REMOTE_MACHINE_SCOPES, LOCAL_MACHINE_S import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { Disposable } from 'vs/base/common/lifecycle'; import { Emitter } from 'vs/base/common/event'; -import { ConfigurationScope, EditPresentationTypes, Extensions, IConfigurationRegistry, IExtensionInfo } from 'vs/platform/configuration/common/configurationRegistry'; +import { ConfigurationDefaultValueSource, ConfigurationScope, EditPresentationTypes, Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; import { ILanguageService } from 'vs/editor/common/languages/language'; import { Registry } from 'vs/platform/registry/common/platform'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; @@ -135,7 +135,7 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { * The source of the default value to display. * This value also accounts for extension-contributed language-specific default value overrides. */ - defaultValueSource: string | IExtensionInfo | undefined; + defaultValueSource: ConfigurationDefaultValueSource | undefined; /** * Whether the setting is configured in the selected scope. @@ -792,11 +792,25 @@ function isIncludeSetting(setting: ISetting): boolean { return setting.key === 'files.readonlyInclude'; } -function isObjectRenderableSchema({ type }: IJSONSchema): boolean { - return type === 'string' || type === 'boolean' || type === 'integer' || type === 'number'; +// The values of the following settings when a default values has been removed +export function objectSettingSupportsRemoveDefaultValue(key: string): boolean { + return key === 'workbench.editor.customLabels.patterns'; +} + +function isObjectRenderableSchema({ type }: IJSONSchema, key: string): boolean { + if (type === 'string' || type === 'boolean' || type === 'integer' || type === 'number') { + return true; + } + + if (objectSettingSupportsRemoveDefaultValue(key) && Array.isArray(type) && type.length === 2) { + return type.includes('null') && (type.includes('string') || type.includes('boolean') || type.includes('integer') || type.includes('number')); + } + + return false; } function isObjectSetting({ + key, type, objectProperties, objectPatternProperties, @@ -838,7 +852,7 @@ function isObjectSetting({ return [schema]; }).flat(); - return flatSchemas.every(isObjectRenderableSchema); + return flatSchemas.every((schema) => isObjectRenderableSchema(schema, key)); } function settingTypeEnumRenderable(_type: string | string[]) { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index 5021036167b..9e9dcc17e86 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -29,6 +29,9 @@ import { settingsSelectBackground, settingsSelectBorder, settingsSelectForegroun import { defaultButtonStyles, getInputBoxStyle, getSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { IManagedHoverTooltipMarkdownString } from 'vs/base/browser/ui/hover/hover'; +import { SettingValueType } from 'vs/workbench/services/preferences/common/preferences'; const $ = DOM.$; @@ -110,21 +113,49 @@ export class ListSettingListModel { } export interface ISettingListChangeEvent { + type: 'change'; originalItem: TDataItem; - item?: TDataItem; - targetIndex?: number; - sourceIndex?: number; + newItem: TDataItem; + targetIndex: number; } +export interface ISettingListAddEvent { + type: 'add'; + newItem: TDataItem; + targetIndex: number; +} + +export interface ISettingListMoveEvent { + type: 'move'; + originalItem: TDataItem; + newItem: TDataItem; + targetIndex: number; + sourceIndex: number; +} + +export interface ISettingListRemoveEvent { + type: 'remove'; + originalItem: TDataItem; + targetIndex: number; +} + +export interface ISettingListResetEvent { + type: 'reset'; + originalItem: TDataItem; + targetIndex: number; +} + +export type SettingListEvent = ISettingListChangeEvent | ISettingListAddEvent | ISettingListMoveEvent | ISettingListRemoveEvent | ISettingListResetEvent; + export abstract class AbstractListSettingWidget extends Disposable { private listElement: HTMLElement; private rowElements: HTMLElement[] = []; - protected readonly _onDidChangeList = this._register(new Emitter>()); + protected readonly _onDidChangeList = this._register(new Emitter>()); protected readonly model = new ListSettingListModel(this.getEmptyItem()); protected readonly listDisposables = this._register(new DisposableStore()); - readonly onDidChangeList: Event> = this._onDidChangeList.event; + readonly onDidChangeList: Event> = this._onDidChangeList.event; get domNode(): HTMLElement { return this.listElement; @@ -250,11 +281,20 @@ export abstract class AbstractListSettingWidget extend protected handleItemChange(originalItem: TDataItem, changedItem: TDataItem, idx: number) { this.model.setEditKey('none'); - this._onDidChangeList.fire({ - originalItem, - item: changedItem, - targetIndex: idx, - }); + if (this.isItemNew(originalItem)) { + this._onDidChangeList.fire({ + type: 'add', + newItem: changedItem, + targetIndex: idx, + }); + } else { + this._onDidChangeList.fire({ + type: 'change', + originalItem, + newItem: changedItem, + targetIndex: idx, + }); + } this.renderList(); } @@ -396,17 +436,17 @@ export interface IListDataItem { sibling?: string; } -interface ListSettingWidgetDragDetails { +interface ListSettingWidgetDragDetails { element: HTMLElement; - item: IListDataItem; + item: TListDataItem; itemIndex: number; } -export class ListSettingWidget extends AbstractListSettingWidget { +export class ListSettingWidget extends AbstractListSettingWidget { private keyValueSuggester: IObjectKeySuggester | undefined; private showAddButton: boolean = true; - override setValue(listData: IListDataItem[], options?: IListSetValueOptions) { + override setValue(listData: TListDataItem[], options?: IListSetValueOptions) { this.keyValueSuggester = options?.keySuggester; this.showAddButton = options?.showAddButton ?? true; super.setValue(listData); @@ -421,13 +461,13 @@ export class ListSettingWidget extends AbstractListSettingWidget super(container, themeService, contextViewService); } - protected getEmptyItem(): IListDataItem { + protected getEmptyItem(): TListDataItem { return { value: { type: 'string', data: '' } - }; + } as TListDataItem; } protected override isAddButtonVisible(): boolean { @@ -438,7 +478,7 @@ export class ListSettingWidget extends AbstractListSettingWidget return ['setting-list-widget']; } - protected getActionsForItem(item: IListDataItem, idx: number): IAction[] { + protected getActionsForItem(item: TListDataItem, idx: number): IAction[] { return [ { class: ThemeIcon.asClassName(settingsEditIcon), @@ -452,20 +492,20 @@ export class ListSettingWidget extends AbstractListSettingWidget enabled: true, id: 'workbench.action.removeListItem', tooltip: this.getLocalizedStrings().deleteActionTooltip, - run: () => this._onDidChangeList.fire({ originalItem: item, item: undefined, targetIndex: idx }) + run: () => this._onDidChangeList.fire({ type: 'remove', originalItem: item, targetIndex: idx }) } ] as IAction[]; } - private dragDetails: ListSettingWidgetDragDetails | undefined; + private dragDetails: ListSettingWidgetDragDetails | undefined; - private getDragImage(item: IListDataItem): HTMLElement { + private getDragImage(item: TListDataItem): HTMLElement { const dragImage = $('.monaco-drag-image'); dragImage.textContent = item.value.data; return dragImage; } - protected renderItem(item: IListDataItem, idx: number): RowElementGroup { + protected renderItem(item: TListDataItem, idx: number): RowElementGroup { const rowElement = $('.setting-list-row'); const valueElement = DOM.append(rowElement, $('.setting-list-value')); const siblingElement = DOM.append(rowElement, $('.setting-list-sibling')); @@ -477,7 +517,7 @@ export class ListSettingWidget extends AbstractListSettingWidget return { rowElement, keyElement: valueElement, valueElement: siblingElement }; } - protected addDragAndDrop(rowElement: HTMLElement, item: IListDataItem, idx: number) { + protected addDragAndDrop(rowElement: HTMLElement, item: TListDataItem, idx: number) { if (this.inReadMode) { rowElement.draggable = true; rowElement.classList.add('draggable'); @@ -530,9 +570,10 @@ export class ListSettingWidget extends AbstractListSettingWidget counter = 0; if (this.dragDetails.element !== rowElement) { this._onDidChangeList.fire({ + type: 'move', originalItem: this.dragDetails.item, sourceIndex: this.dragDetails.itemIndex, - item, + newItem: item, targetIndex: idx }); } @@ -548,7 +589,7 @@ export class ListSettingWidget extends AbstractListSettingWidget })); } - protected renderEdit(item: IListDataItem, idx: number): HTMLElement { + protected renderEdit(item: TListDataItem, idx: number): HTMLElement { const rowElement = $('.setting-list-edit-row'); let valueInput: InputBox | SelectBox; let currentDisplayValue: string; @@ -580,7 +621,7 @@ export class ListSettingWidget extends AbstractListSettingWidget break; } - const updatedInputBoxItem = (): IListDataItem => { + const updatedInputBoxItem = (): TListDataItem => { const inputBox = valueInput as InputBox; return { value: { @@ -588,16 +629,16 @@ export class ListSettingWidget extends AbstractListSettingWidget data: inputBox.value }, sibling: siblingInput?.value - }; + } as TListDataItem; }; - const updatedSelectBoxItem = (selectedValue: string): IListDataItem => { + const updatedSelectBoxItem = (selectedValue: string): TListDataItem => { return { value: { type: 'enum', data: selectedValue, options: currentEnumOptions ?? [] } - }; + } as TListDataItem; }; const onKeyDown = (e: StandardKeyboardEvent) => { if (e.equals(KeyCode.Enter)) { @@ -674,11 +715,11 @@ export class ListSettingWidget extends AbstractListSettingWidget return rowElement; } - override isItemNew(item: IListDataItem): boolean { + override isItemNew(item: TListDataItem): boolean { return item.value.data === ''; } - protected addTooltipsToRow(rowElementGroup: RowElementGroup, { value, sibling }: IListDataItem) { + protected addTooltipsToRow(rowElementGroup: RowElementGroup, { value, sibling }: TListDataItem) { const title = isUndefinedOrNull(sibling) ? localize('listValueHintLabel', "List item `{0}`", value.data) : localize('listSiblingHintLabel', "List item `{0}` with sibling `${1}`", value.data, sibling); @@ -729,22 +770,28 @@ export class ListSettingWidget extends AbstractListSettingWidget } } -export class ExcludeSettingWidget extends ListSettingWidget { +export class ExcludeSettingWidget extends ListSettingWidget { protected override getContainerClasses() { return ['setting-list-include-exclude-widget']; } - protected override addDragAndDrop(rowElement: HTMLElement, item: IListDataItem, idx: number) { + protected override addDragAndDrop(rowElement: HTMLElement, item: IIncludeExcludeDataItem, idx: number) { return; } - protected override addTooltipsToRow(rowElementGroup: RowElementGroup, { value, sibling }: IListDataItem): void { - const title = isUndefinedOrNull(sibling) - ? localize('excludePatternHintLabel', "Exclude files matching `{0}`", value.data) - : localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", value.data, sibling); + protected override addTooltipsToRow(rowElementGroup: RowElementGroup, item: IIncludeExcludeDataItem): void { + let title = isUndefinedOrNull(item.sibling) + ? localize('excludePatternHintLabel', "Exclude files matching `{0}`", item.value.data) + : localize('excludeSiblingHintLabel', "Exclude files matching `{0}`, only when a file matching `{1}` is present", item.value.data, item.sibling); + + if (item.source) { + title += localize('excludeIncludeSource', ". Default value provided by `{0}`", item.source); + } + + const markdownTitle = new MarkdownString().appendMarkdown(title); const { rowElement } = rowElementGroup; - this.listDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), rowElement, title)); + this.listDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), rowElement, { markdown: markdownTitle, markdownNotSupportedFallback: title })); rowElement.setAttribute('aria-label', title); } @@ -759,22 +806,28 @@ export class ExcludeSettingWidget extends ListSettingWidget { } } -export class IncludeSettingWidget extends ListSettingWidget { +export class IncludeSettingWidget extends ListSettingWidget { protected override getContainerClasses() { return ['setting-list-include-exclude-widget']; } - protected override addDragAndDrop(rowElement: HTMLElement, item: IListDataItem, idx: number) { + protected override addDragAndDrop(rowElement: HTMLElement, item: IIncludeExcludeDataItem, idx: number) { return; } - protected override addTooltipsToRow(rowElementGroup: RowElementGroup, { value, sibling }: IListDataItem): void { - const title = isUndefinedOrNull(sibling) - ? localize('includePatternHintLabel', "Include files matching `{0}`", value.data) - : localize('includeSiblingHintLabel', "Include files matching `{0}`, only when a file matching `{1}` is present", value.data, sibling); + protected override addTooltipsToRow(rowElementGroup: RowElementGroup, item: IIncludeExcludeDataItem): void { + let title = isUndefinedOrNull(item.sibling) + ? localize('includePatternHintLabel', "Include files matching `{0}`", item.value.data) + : localize('includeSiblingHintLabel', "Include files matching `{0}`, only when a file matching `{1}` is present", item.value.data, item.sibling); + + if (item.source) { + title += localize('excludeIncludeSource', ". Default value provided by `{0}`", item.source); + } + + const markdownTitle = new MarkdownString().appendMarkdown(title); const { rowElement } = rowElementGroup; - this.listDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), rowElement, title)); + this.listDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), rowElement, { markdown: markdownTitle, markdownNotSupportedFallback: title })); rowElement.setAttribute('aria-label', title); } @@ -818,7 +871,16 @@ export interface IObjectDataItem { key: ObjectKey; value: ObjectValue; keyDescription?: string; + source?: string; removable: boolean; + resetable: boolean; +} + +export interface IIncludeExcludeDataItem { + value: ObjectKey; + elementType: SettingValueType; + sibling?: string; + source?: string; } export interface IObjectValueSuggester { @@ -886,6 +948,7 @@ export class ObjectSettingDropdownWidget extends AbstractListSettingWidget this._onDidChangeList.fire({ originalItem: item, item: undefined, targetIndex: idx }) + tooltip: this.getLocalizedStrings().resetActionTooltip, + run: () => this._onDidChangeList.fire({ type: 'reset', originalItem: item, targetIndex: idx }) }); - } else { + } + + if (item.removable) { actions.push({ - class: ThemeIcon.asClassName(settingsDiscardIcon), + class: ThemeIcon.asClassName(settingsRemoveIcon), enabled: true, - id: 'workbench.action.resetListItem', + id: 'workbench.action.removeListItem', label: '', - tooltip: this.getLocalizedStrings().resetActionTooltip, - run: () => this._onDidChangeList.fire({ originalItem: item, item: undefined, targetIndex: idx }) + tooltip: this.getLocalizedStrings().deleteActionTooltip, + run: () => this._onDidChangeList.fire({ type: 'remove', originalItem: item, targetIndex: idx }) }); } @@ -1181,12 +1246,20 @@ export class ObjectSettingDropdownWidget extends AbstractListSettingWidget Date: Tue, 25 Jun 2024 00:32:45 +0300 Subject: [PATCH 0094/2222] fix: Quotes with headings on markdown not rendering properly (#205227) --- extensions/markdown-language-features/media/markdown.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/markdown-language-features/media/markdown.css b/extensions/markdown-language-features/media/markdown.css index 168f6a8a862..800be985a43 100644 --- a/extensions/markdown-language-features/media/markdown.css +++ b/extensions/markdown-language-features/media/markdown.css @@ -205,7 +205,7 @@ table > tbody > tr + tr > td { blockquote { margin: 0; - padding: 2px 16px 0 10px; + padding: 0px 16px 0 10px; border-left-width: 5px; border-left-style: solid; border-radius: 2px; From 445a0cfca30ae89c8e9c52a1fa6b4dbcb2c0df50 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 24 Jun 2024 14:49:42 -0700 Subject: [PATCH 0095/2222] Run steps for valid issues only (#217596) --- .github/workflows/on-open.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/on-open.yml b/.github/workflows/on-open.yml index 679c1c65db8..2a26794c6b0 100644 --- a/.github/workflows/on-open.yml +++ b/.github/workflows/on-open.yml @@ -22,6 +22,7 @@ jobs: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} - name: Run CopyCat (VSCodeTriageBot/testissues) + if: github.event.issue.user.login != 'ghost' uses: ./actions/copycat with: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} @@ -30,6 +31,7 @@ jobs: repo: testissues - name: Run New Release + if: github.event.issue.user.login != 'ghost' uses: ./actions/new-release with: label: new release @@ -41,6 +43,7 @@ jobs: days: 5 - name: Run Clipboard Labeler + if: github.event.issue.user.login != 'ghost' uses: ./actions/regex-labeler with: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} @@ -49,6 +52,7 @@ jobs: comment: "It looks like you're using the VS Code Issue Reporter but did not paste the text generated into the created issue. We've closed this issue, please open a new one containing the text we placed in your clipboard.\n\nHappy Coding!" - name: Run Clipboard Labeler (Chinese) + if: github.event.issue.user.login != 'ghost' uses: ./actions/regex-labeler with: appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} @@ -58,6 +62,7 @@ jobs: # source of truth in ./english-please.yml - name: Run English Please + if: github.event.issue.user.login != 'ghost' uses: ./actions/english-please with: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} @@ -69,6 +74,7 @@ jobs: translatorRequestedLabelColor: "c29cff" # source of truth in ./test-plan-item-validator.yml - name: Run Test Plan Item Validator + if: github.event.issue.user.login != 'ghost' uses: ./actions/test-plan-item-validator with: token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} From b7ff4c513d93af9405989c624110692759c2cb73 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Mon, 24 Jun 2024 15:11:44 -0700 Subject: [PATCH 0096/2222] intellisense for code actions on save (#215475) * language registry and code actions on save new filter * fixes logic * more clean up * remove static contribution --- .../editor/common/languageFeatureRegistry.ts | 4 ++ .../browser/codeActionsContribution.ts | 48 +++++++++++++++---- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/vs/editor/common/languageFeatureRegistry.ts b/src/vs/editor/common/languageFeatureRegistry.ts index 53c14ac57b9..47679f308f4 100644 --- a/src/vs/editor/common/languageFeatureRegistry.ts +++ b/src/vs/editor/common/languageFeatureRegistry.ts @@ -109,6 +109,10 @@ export class LanguageFeatureRegistry { return result; } + allNoModel(): T[] { + return this._entries.map(entry => entry.provider); + } + ordered(model: ITextModel): T[] { const result: T[] = []; this._orderedForEach(model, entry => result.push(entry.provider)); diff --git a/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts b/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts index 38fd5502ae2..17e6217a6a9 100644 --- a/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts +++ b/src/vs/workbench/contrib/codeActions/browser/codeActionsContribution.ts @@ -8,6 +8,7 @@ import { HierarchicalKind } from 'vs/base/common/hierarchicalKind'; import { IJSONSchema, IJSONSchemaMap } from 'vs/base/common/jsonSchema'; import { Disposable } from 'vs/base/common/lifecycle'; import { editorConfigurationBaseNode } from 'vs/editor/common/config/editorConfigurationSchema'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { codeActionCommandId, refactorCommandId, sourceActionCommandId } from 'vs/editor/contrib/codeAction/browser/codeAction'; import { CodeActionKind } from 'vs/editor/contrib/codeAction/common/types'; import * as nls from 'vs/nls'; @@ -34,15 +35,11 @@ const createCodeActionsAutoSave = (description: string): IJSONSchema => { }; }; -const codeActionsOnSaveDefaultProperties = Object.freeze({ - 'source.fixAll': createCodeActionsAutoSave(nls.localize('codeActionsOnSave.fixAll', "Controls whether auto fix action should be run on file save.")), -}); const codeActionsOnSaveSchema: IConfigurationPropertySchema = { oneOf: [ { type: 'object', - properties: codeActionsOnSaveDefaultProperties, additionalProperties: { type: 'string' }, @@ -72,15 +69,24 @@ export const editorConfiguration = Object.freeze({ export class CodeActionsContribution extends Disposable implements IWorkbenchContribution { private _contributedCodeActions: CodeActionsExtensionPoint[] = []; + private settings: Set = new Set(); private readonly _onDidChangeContributions = this._register(new Emitter()); constructor( codeActionsExtensionPoint: IExtensionPoint, @IKeybindingService keybindingService: IKeybindingService, + @ILanguageFeaturesService private readonly languageFeatures: ILanguageFeaturesService ) { super(); + // TODO: @justschen caching of code actions based on extensions loaded: https://github.com/microsoft/vscode/issues/216019 + + languageFeatures.codeActionProvider.onDidChange(() => { + this.updateSettingsFromCodeActionProviders(); + this.updateConfigurationSchemaFromContribs(); + }, 2000); + codeActionsExtensionPoint.setHandler(extensionPoints => { this._contributedCodeActions = extensionPoints.flatMap(x => x.value).filter(x => Array.isArray(x.actions)); this.updateConfigurationSchema(this._contributedCodeActions); @@ -93,9 +99,23 @@ export class CodeActionsContribution extends Disposable implements IWorkbenchCon }); } + private updateSettingsFromCodeActionProviders(): void { + const providers = this.languageFeatures.codeActionProvider.allNoModel(); + providers.forEach(provider => { + if (provider.providedCodeActionKinds) { + provider.providedCodeActionKinds.forEach(kind => { + if (!this.settings.has(kind) && CodeActionKind.Source.contains(new HierarchicalKind(kind))) { + this.settings.add(kind); + } + }); + } + }); + } + private updateConfigurationSchema(codeActionContributions: readonly CodeActionsExtensionPoint[]) { - const newProperties: IJSONSchemaMap = { ...codeActionsOnSaveDefaultProperties }; + const newProperties: IJSONSchemaMap = {}; for (const [sourceAction, props] of this.getSourceActions(codeActionContributions)) { + this.settings.add(sourceAction); newProperties[sourceAction] = createCodeActionsAutoSave(nls.localize('codeActionsOnSave.generic', "Controls whether '{0}' actions should be run on file save.", props.title)); } codeActionsOnSaveSchema.properties = newProperties; @@ -103,16 +123,24 @@ export class CodeActionsContribution extends Disposable implements IWorkbenchCon .notifyConfigurationSchemaUpdated(editorConfiguration); } + private updateConfigurationSchemaFromContribs() { + const properties: IJSONSchemaMap = { ...codeActionsOnSaveSchema.properties }; + for (const codeActionKind of this.settings) { + if (!properties[codeActionKind]) { + properties[codeActionKind] = createCodeActionsAutoSave(nls.localize('codeActionsOnSave.generic', "Controls whether '{0}' actions should be run on file save.", codeActionKind)); + } + } + codeActionsOnSaveSchema.properties = properties; + Registry.as(Extensions.Configuration) + .notifyConfigurationSchemaUpdated(editorConfiguration); + } + private getSourceActions(contributions: readonly CodeActionsExtensionPoint[]) { - const defaultKinds = Object.keys(codeActionsOnSaveDefaultProperties).map(value => new HierarchicalKind(value)); const sourceActions = new Map(); for (const contribution of contributions) { for (const action of contribution.actions) { const kind = new HierarchicalKind(action.kind); - if (CodeActionKind.Source.contains(kind) - // Exclude any we already included by default - && !defaultKinds.some(defaultKind => defaultKind.contains(kind)) - ) { + if (CodeActionKind.Source.contains(kind)) { sourceActions.set(kind.value, action); } } From 278b6a37a7c58b32a80a3bbaa661894791afe23a Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 24 Jun 2024 15:25:24 -0700 Subject: [PATCH 0097/2222] Fix static fn call for nb codeaction (#217605) fix static fn call --- .../browser/contrib/saveParticipants/saveParticipants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts b/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts index eb2d57e62fe..d5899e31986 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/saveParticipants/saveParticipants.ts @@ -499,7 +499,7 @@ export class CodeActionParticipantUtils { }; for (const codeActionKind of codeActionsOnSave) { - const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, languageFeaturesService, getActionProgress, token); + const actionsToRun = await CodeActionParticipantUtils.getActionsToRun(model, codeActionKind, excludes, languageFeaturesService, getActionProgress, token); if (token.isCancellationRequested) { actionsToRun.dispose(); return; From d681a19ddc2bd4d3a9ac0a3dfdcd42895d7f1d7b Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 24 Jun 2024 15:44:17 -0700 Subject: [PATCH 0098/2222] Make ExtHostSecrets disposable (#217619) Also register a few disposables. I don't have too much time to figure out how to register these properly (hence the TODO), and maybe this is more in @jrieken's territory... but wanted to have _something_ in place for now. --- src/vs/workbench/api/common/extHostExtensionService.ts | 7 ++++--- src/vs/workbench/api/common/extHostSecrets.ts | 9 ++++++++- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 6947479c2b8..97935e89d08 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -498,9 +498,10 @@ export abstract class AbstractExtHostExtensionService extends Disposable impleme private _loadExtensionContext(extensionDescription: IExtensionDescription): Promise { const lanuageModelAccessInformation = this._extHostLanguageModels.createLanguageModelAccessInformation(extensionDescription); - const globalState = new ExtensionGlobalMemento(extensionDescription, this._storage); - const workspaceState = new ExtensionMemento(extensionDescription.identifier.value, false, this._storage); - const secrets = new ExtensionSecrets(extensionDescription, this._secretState); + // TODO: These should probably be disposed when the extension deactivates + const globalState = this._register(new ExtensionGlobalMemento(extensionDescription, this._storage)); + const workspaceState = this._register(new ExtensionMemento(extensionDescription.identifier.value, false, this._storage)); + const secrets = this._register(new ExtensionSecrets(extensionDescription, this._secretState)); const extensionMode = extensionDescription.isUnderDevelopment ? (this._initData.environment.extensionTestsLocationURI ? ExtensionMode.Test : ExtensionMode.Development) : ExtensionMode.Production; diff --git a/src/vs/workbench/api/common/extHostSecrets.ts b/src/vs/workbench/api/common/extHostSecrets.ts index 4b1e284b604..13fb3293a35 100644 --- a/src/vs/workbench/api/common/extHostSecrets.ts +++ b/src/vs/workbench/api/common/extHostSecrets.ts @@ -10,6 +10,7 @@ import type * as vscode from 'vscode'; import { ExtHostSecretState } from 'vs/workbench/api/common/extHostSecretState'; import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { Event } from 'vs/base/common/event'; +import { DisposableStore } from 'vs/base/common/lifecycle'; export class ExtensionSecrets implements vscode.SecretStorage { @@ -17,6 +18,7 @@ export class ExtensionSecrets implements vscode.SecretStorage { readonly #secretState: ExtHostSecretState; readonly onDidChange: Event; + readonly disposables = new DisposableStore(); constructor(extensionDescription: IExtensionDescription, secretState: ExtHostSecretState) { this._id = ExtensionIdentifier.toKey(extensionDescription.identifier); @@ -24,10 +26,15 @@ export class ExtensionSecrets implements vscode.SecretStorage { this.onDidChange = Event.map( Event.filter(this.#secretState.onDidChangePassword, e => e.extensionId === this._id), - e => ({ key: e.key }) + e => ({ key: e.key }), + this.disposables ); } + dispose() { + this.disposables.dispose(); + } + get(key: string): Promise { return this.#secretState.get(this._id, key); } From 4f396935a4ef0af47d586ba1c203a420649f7ee2 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 24 Jun 2024 15:50:16 -0700 Subject: [PATCH 0099/2222] Cleanup getting started video experiment (#217620) --- .../browser/gettingStarted.ts | 120 +----------------- .../browser/media/gettingStarted.css | 10 +- 2 files changed, 5 insertions(+), 125 deletions(-) diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts index 46bac27a420..664c55b7172 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStarted.ts @@ -69,7 +69,6 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { GettingStartedIndexList } from './gettingStartedList'; -import { IWorkbenchAssignmentService } from 'vs/workbench/services/assignment/common/assignmentService'; const SLIDE_TRANSITION_TIME_MS = 250; const configurationKey = 'workbench.startupEditor'; @@ -148,7 +147,6 @@ export class GettingStartedPage extends EditorPane { private recentlyOpenedList?: GettingStartedIndexList; private startList?: GettingStartedIndexList; private gettingStartedList?: GettingStartedIndexList; - private videoList?: GettingStartedIndexList; private stepsSlide!: HTMLElement; private categoriesSlide!: HTMLElement; @@ -187,8 +185,7 @@ export class GettingStartedPage extends EditorPane { @IHostService private readonly hostService: IHostService, @IWebviewService private readonly webviewService: IWebviewService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, - @IAccessibilityService private readonly accessibilityService: IAccessibilityService, - @IWorkbenchAssignmentService private readonly tasExperimentService: IWorkbenchAssignmentService + @IAccessibilityService private readonly accessibilityService: IAccessibilityService ) { super(GettingStartedPage.ID, group, telemetryService, themeService, storageService); @@ -443,10 +440,6 @@ export class GettingStartedPage extends EditorPane { } break; } - case 'hideVideos': { - this.hideVideos(); - break; - } case 'openLink': { this.openerService.open(argument); break; @@ -465,11 +458,6 @@ export class GettingStartedPage extends EditorPane { this.gettingStartedList?.rerender(); } - private hideVideos() { - this.setHiddenCategories([...this.getHiddenCategories().add('getting-started-videos')]); - this.videoList?.setEntries(undefined); - } - private markAllStepsComplete() { if (this.currentWalkthrough) { this.currentWalkthrough?.steps.forEach(step => { @@ -821,29 +809,6 @@ export class GettingStartedPage extends EditorPane { const startList = this.buildStartList(); const recentList = this.buildRecentlyOpenedList(); - - const showVideoTutorials = await Promise.race([ - this.tasExperimentService?.getTreatment('gettingStarted.showVideoTutorials'), - new Promise(resolve => setTimeout(() => resolve(false), 200)) - ]); - - let videoList: GettingStartedIndexList; - if (showVideoTutorials === true) { - this.showFeaturedWalkthrough = false; - videoList = this.buildVideosList(); - const layoutVideos = () => { - if (videoList?.itemCount > 0) { - reset(rightColumn, videoList?.getDomElement(), gettingStartedList.getDomElement()); - } - else { - reset(rightColumn, gettingStartedList.getDomElement()); - } - setTimeout(() => this.categoriesPageScrollbar?.scanDomNode(), 50); - layoutRecentList(); - }; - videoList.onDidChange(layoutVideos); - } - const gettingStartedList = this.buildGettingStartedWalkthroughsList(); const footer = $('.footer', {}, @@ -855,31 +820,18 @@ export class GettingStartedPage extends EditorPane { const layoutLists = () => { if (gettingStartedList.itemCount) { this.container.classList.remove('noWalkthroughs'); - if (videoList?.itemCount > 0) { - this.container.classList.remove('noVideos'); - reset(rightColumn, videoList?.getDomElement(), gettingStartedList.getDomElement()); - } else { - this.container.classList.add('noVideos'); - reset(rightColumn, gettingStartedList.getDomElement()); - } + reset(rightColumn, gettingStartedList.getDomElement()); } else { this.container.classList.add('noWalkthroughs'); - if (videoList?.itemCount > 0) { - this.container.classList.remove('noVideos'); - reset(rightColumn, videoList?.getDomElement()); - } - else { - this.container.classList.add('noVideos'); - reset(rightColumn); - } + reset(rightColumn); } setTimeout(() => this.categoriesPageScrollbar?.scanDomNode(), 50); layoutRecentList(); }; const layoutRecentList = () => { - if (this.container.classList.contains('noWalkthroughs') && this.container.classList.contains('noVideos')) { + if (this.container.classList.contains('noWalkthroughs')) { recentList.setLimit(10); reset(leftColumn, startList.getDomElement()); reset(rightColumn, recentList.getDomElement()); @@ -1139,69 +1091,6 @@ export class GettingStartedPage extends EditorPane { return gettingStartedList; } - private buildVideosList(): GettingStartedIndexList { - - const renderFeaturedExtensions = (entry: IWelcomePageStartEntry): HTMLElement => { - - const featuredBadge = $('.featured-badge', {}); - const descriptionContent = $('.description-content', {},); - - reset(featuredBadge, $('.featured', {}, $('span.featured-icon.codicon.codicon-star-full'))); - reset(descriptionContent, ...renderLabelWithIcons(entry.description)); - - const titleContent = $('h3.category-title.max-lines-3', { 'x-category-title-for': entry.id }); - reset(titleContent, ...renderLabelWithIcons(entry.title)); - - return $('button.getting-started-category' + '.featured', - { - 'x-dispatch': 'openLink:' + entry.command, - 'title': entry.title - }, - featuredBadge, - $('.main-content', {}, - this.iconWidgetFor(entry), - titleContent, - $('a.codicon.codicon-close.hide-category-button', { - 'tabindex': 0, - 'x-dispatch': 'hideVideos', - 'title': localize('close', "Hide"), - 'role': 'button', - 'aria-label': localize('closeAriaLabel', "Hide"), - }), - ), - descriptionContent); - }; - - if (this.videoList) { - this.videoList.dispose(); - } - const videoList = this.videoList = new GettingStartedIndexList( - { - title: localize('videos', "Videos"), - klass: 'getting-started-videos', - limit: 1, - renderElement: renderFeaturedExtensions, - contextService: this.contextService, - }); - - if (this.getHiddenCategories().has('getting-started-videos')) { - return videoList; - } - - videoList.setEntries([{ - id: 'getting-started-videos', - title: localize('videos-title', 'Watch Getting Started Tutorials'), - description: localize('videos-description', 'Learn VS Code\'s must-have features in short and practical videos'), - command: 'https://aka.ms/vscode-getting-started-tutorials', - order: 0, - icon: { type: 'icon', icon: Codicon.deviceCameraVideo }, - when: ContextKeyExpr.true(), - }]); - videoList.onDidChange(() => this.registerDispatchListeners()); - - return videoList; - } - layout(size: Dimension) { this.detailsScrollbar?.scanDomNode(); @@ -1211,7 +1100,6 @@ export class GettingStartedPage extends EditorPane { this.startList?.layout(size); this.gettingStartedList?.layout(size); this.recentlyOpenedList?.layout(size); - this.videoList?.layout(size); if (this.editorInput?.selectedStep && this.currentMediaType) { this.mediaDisposables.clear(); diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css index 492fd698fb2..80cddd5958b 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css @@ -228,7 +228,6 @@ } .monaco-workbench .part.editor > .content .gettingStartedContainer .icon-widget, -.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlideCategories .icon-widget:not(.codicon-device-camera-video), .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlideCategories .featured-icon { font-size: 20px; padding-right: 8px; @@ -236,13 +235,6 @@ top: 3px; } -.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlideCategories .icon-widget.codicon-device-camera-video { - font-size: 20px; - padding-right: 8px; - position: relative; - transform: translateY(+100%); -} - .monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlideCategories .codicon:not(.icon-widget, .featured-icon, .hide-category-button) { margin: 0 2px; } @@ -348,7 +340,7 @@ right: 8px; } -.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .getting-started-category.featured .icon-widget:not(.codicon-device-camera-video) { +.monaco-workbench .part.editor > .content .gettingStartedContainer .gettingStartedSlide .getting-started-category.featured .icon-widget { visibility: hidden; } From 1435b366b2d8e4a1d446d7969e9c246a572027d5 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Mon, 24 Jun 2024 18:13:26 -0700 Subject: [PATCH 0100/2222] Chore: OSS tool (#217642) * oss tool * update distro hash --- ThirdPartyNotices.txt | 4 +- cli/ThirdPartyNotices.txt | 175 +++----------------------------------- package.json | 2 +- 3 files changed, 13 insertions(+), 168 deletions(-) diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 2e3f2610f46..fe3cf5c4a9b 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -517,7 +517,7 @@ to the base-name name of the original file, and an extension of txt, html, or si --------------------------------------------------------- -go-syntax 0.6.6 - MIT +go-syntax 0.6.8 - MIT https://github.com/worlpaker/go-syntax MIT License @@ -833,7 +833,7 @@ SOFTWARE. --------------------------------------------------------- -jlelong/vscode-latex-basics 1.7.0 - MIT +jlelong/vscode-latex-basics 1.9.0 - MIT https://github.com/jlelong/vscode-latex-basics Copyright (c) vscode-latex-basics authors diff --git a/cli/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt index 7a74fb503c5..a9630c12022 100644 --- a/cli/ThirdPartyNotices.txt +++ b/cli/ThirdPartyNotices.txt @@ -1341,7 +1341,7 @@ SOFTWARE. --------------------------------------------------------- clap_derive 4.5.4 - MIT OR Apache-2.0 -https://github.com/clap-rs/clap/tree/master/clap_derive +https://github.com/clap-rs/clap Copyright (c) Individual contributors @@ -1367,7 +1367,7 @@ SOFTWARE. --------------------------------------------------------- clap_lex 0.7.0 - MIT OR Apache-2.0 -https://github.com/clap-rs/clap/tree/master/clap_lex +https://github.com/clap-rs/clap Copyright (c) Individual contributors @@ -3428,7 +3428,7 @@ DEALINGS IN THE SOFTWARE. httparse 1.8.0 - MIT/Apache-2.0 https://github.com/seanmonstar/httparse -Copyright (c) 2015-2021 Sean McArthur +Copyright (c) 2015-2024 Sean McArthur Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -8507,6 +8507,7 @@ subtle 2.5.0 - BSD-3-Clause https://github.com/dalek-cryptography/subtle Copyright (c) 2016-2017 Isis Agora Lovecruft, Henry de Valence. All rights reserved. +Copyright (c) 2016-2024 Isis Agora Lovecruft. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -10790,33 +10791,7 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI zbus 3.15.2 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10824,33 +10799,7 @@ DEALINGS IN THE SOFTWARE. zbus_macros 3.15.2 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10858,33 +10807,7 @@ DEALINGS IN THE SOFTWARE. zbus_names 2.6.1 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -10977,33 +10900,7 @@ licences; see files named LICENSE.*.txt for details. zvariant 3.15.2 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -11011,33 +10908,7 @@ DEALINGS IN THE SOFTWARE. zvariant_derive 3.15.2 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- --------------------------------------------------------- @@ -11045,31 +10916,5 @@ DEALINGS IN THE SOFTWARE. zvariant_utils 1.0.1 - MIT https://github.com/dbus2/zbus/ -The MIT License (MIT) - -Copyright (c) 2024 Zeeshan Ali Khan & zbus contributors - -Permission is hereby granted, free of charge, to any -person obtaining a copy of this software and associated -documentation files (the "Software"), to deal in the -Software without restriction, including without -limitation the rights to use, copy, modify, merge, -publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software -is furnished to do so, subject to the following -conditions: - -The above copyright notice and this permission notice -shall be included in all copies or substantial portions -of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED -TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT -SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR -IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER -DEALINGS IN THE SOFTWARE. +LICENSE-MIT --------------------------------------------------------- \ No newline at end of file diff --git a/package.json b/package.json index 3d8b1cbb209..49c83a768b7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.91.0", - "distro": "44256034a3b5a2bbd6b12530ab050a2a3c2bd285", + "distro": "9cef7f933867933892cb3b591231ed071fe861a8", "author": { "name": "Microsoft Corporation" }, From 6f3598b9a1692036664d1550e05a39b105740fdb Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 24 Jun 2024 21:18:41 -0700 Subject: [PATCH 0101/2222] Ensure that the first line of markdown in a 'supportHtml' MarkdownString is parsed as markdown (#217641) * Ensure that the first line of markdown in a 'supportHtml' MarkdownString is parsed as markdown Fix microsoft/vscode-copilot-release#1337 * Update tests --- .../contrib/chat/browser/chatMarkdownRenderer.ts | 3 ++- .../ChatMarkdownRenderer_invalid_HTML.0.snap | 2 +- ...kdownRenderer_invalid_HTML_with_attributes.0.snap | 2 +- .../ChatMarkdownRenderer_remote_images.0.snap | 2 +- ...ChatMarkdownRenderer_self-closing_elements.0.snap | 2 +- ...enderer_supportHtml_with_one-line_markdown.0.snap | 1 + ...enderer_supportHtml_with_one-line_markdown.1.snap | 4 ++++ .../chat/test/browser/chatMarkdownRenderer.test.ts | 12 ++++++++++++ 8 files changed, 23 insertions(+), 5 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_supportHtml_with_one-line_markdown.0.snap create mode 100644 src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_supportHtml_with_one-line_markdown.1.snap diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts index 3e4b6a9349f..50c6029a9e8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts @@ -73,7 +73,8 @@ export class ChatMarkdownRenderer extends MarkdownRenderer { ...markdown, // dompurify uses DOMParser, which strips leading comments. Wrapping it all in 'body' prevents this. - value: `${markdown.value}`, + // The \n\n prevents marked.js from parsing the body contents as just text in an 'html' token, instead of actual markdown. + value: `\n\n${markdown.value}`, } : markdown; return super.render(mdWithBody, options, markedOptions); diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_invalid_HTML.0.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_invalid_HTML.0.snap index 0d3458d76b7..9bfd3b945e8 100644 --- a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_invalid_HTML.0.snap +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_invalid_HTML.0.snap @@ -1 +1 @@ -
1<canvas>2<details>3</details></canvas>4
\ No newline at end of file +

1<canvas>2</canvas>

<details>3</details>4

\ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_invalid_HTML_with_attributes.0.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_invalid_HTML_with_attributes.0.snap index 3bb96899c11..c0b5a277aac 100644 --- a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_invalid_HTML_with_attributes.0.snap +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_invalid_HTML_with_attributes.0.snap @@ -1 +1 @@ -
1<details id="id1" style="display: none">2<details id="my id 2">3</details></details>4
\ No newline at end of file +

1

<details id="id1" style="display: none">2<details id="my id 2">3</details></details>4

\ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_remote_images.0.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_remote_images.0.snap index 34a719b0613..1241ef62b5f 100644 --- a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_remote_images.0.snap +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_remote_images.0.snap @@ -1 +1 @@ -
<img src="http://disallowed.com/image.jpg">
\ No newline at end of file +

<img src="http://disallowed.com/image.jpg">

\ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_self-closing_elements.0.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_self-closing_elements.0.snap index 023b2e6a846..5b482726d3a 100644 --- a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_self-closing_elements.0.snap +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_self-closing_elements.0.snap @@ -1 +1 @@ -
<area>

<input type="text" value="test">
\ No newline at end of file +

<area>



<input type="text" value="test">

\ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_supportHtml_with_one-line_markdown.0.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_supportHtml_with_one-line_markdown.0.snap new file mode 100644 index 00000000000..89991e7676e --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_supportHtml_with_one-line_markdown.0.snap @@ -0,0 +1 @@ +

hello

\ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_supportHtml_with_one-line_markdown.1.snap b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_supportHtml_with_one-line_markdown.1.snap new file mode 100644 index 00000000000..d704b7b322d --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/__snapshots__/ChatMarkdownRenderer_supportHtml_with_one-line_markdown.1.snap @@ -0,0 +1,4 @@ +
    +
  1. hello test text
  2. +
+
\ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/browser/chatMarkdownRenderer.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatMarkdownRenderer.test.ts index e9975f25283..657ed1c961c 100644 --- a/src/vs/workbench/contrib/chat/test/browser/chatMarkdownRenderer.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/chatMarkdownRenderer.test.ts @@ -27,6 +27,18 @@ suite('ChatMarkdownRenderer', () => { await assertSnapshot(result.element.textContent); }); + test('supportHtml with one-line markdown', async () => { + const md = new MarkdownString('**hello**'); + md.supportHtml = true; + const result = store.add(testRenderer.render(md)); + await assertSnapshot(result.element.outerHTML); + + const md2 = new MarkdownString('1. [_hello_](https://example.com) test **text**'); + md2.supportHtml = true; + const result2 = store.add(testRenderer.render(md2)); + await assertSnapshot(result2.element.outerHTML); + }); + test('invalid HTML', async () => { const md = new MarkdownString('12
3
4'); md.supportHtml = true; From 3e9cab05385c88a3174175e47085952bd3caf8a3 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 24 Jun 2024 21:18:58 -0700 Subject: [PATCH 0102/2222] Fix error from undefined 'isSettled' (#217646) Workaround for #217645 --- .../chat/browser/chatContentParts/chatTaskContentPart.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTaskContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTaskContentPart.ts index ad493c4afc7..a1a03e1cae1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTaskContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatTaskContentPart.ts @@ -34,7 +34,9 @@ export class ChatTaskContentPart extends Disposable implements IChatContentPart this.domNode.appendChild(refsPart.domNode); this.onDidChangeHeight = refsPart.onDidChangeHeight; } else { - const progressPart = this._register(instantiationService.createInstance(ChatProgressContentPart, task, renderer, context, !task.isSettled(), true)); + // #217645 + const isSettled = task.isSettled?.() ?? true; + const progressPart = this._register(instantiationService.createInstance(ChatProgressContentPart, task, renderer, context, !isSettled, true)); this.domNode = progressPart.domNode; this.onDidChangeHeight = Event.None; } From ca8cb6f637fc5ac820b581eb4115bc3917ba7548 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 25 Jun 2024 09:03:25 +0200 Subject: [PATCH 0103/2222] registerColor: pass in a single value for defaults, improve further (#214468) --- .../diffEditor/registrations.contribution.ts | 4 +- .../browser/widget/multiDiffEditor/colors.ts | 2 +- .../editor/common/core/editorColorRegistry.ts | 84 +-- .../browser/bracketMatching.ts | 2 +- .../folding/browser/foldingDecorations.ts | 2 +- .../gotoError/browser/gotoErrorWidget.ts | 2 +- .../browser/parameterHintsWidget.ts | 2 +- .../contrib/peekView/browser/peekView.ts | 4 +- .../contrib/suggest/browser/suggestWidget.ts | 18 +- .../symbolIcons/browser/symbolIcons.ts | 191 ++---- .../browser/highlightDecorations.ts | 10 +- .../actionWidget/browser/actionWidget.ts | 2 +- src/vs/platform/theme/common/colorUtils.ts | 27 +- .../theme/common/colors/baseColors.ts | 2 +- .../theme/common/colors/chartsColors.ts | 12 +- .../theme/common/colors/editorColors.ts | 70 +-- .../theme/common/colors/inputColors.ts | 18 +- .../theme/common/colors/listColors.ts | 22 +- .../theme/common/colors/menuColors.ts | 8 +- .../theme/common/colors/minimapColors.ts | 10 +- .../theme/common/colors/miscColors.ts | 2 +- .../theme/common/colors/quickpickColors.ts | 10 +- src/vs/workbench/common/theme.ts | 579 +++--------------- .../contrib/chat/common/chatColors.ts | 2 +- .../contrib/comments/browser/commentColors.ts | 6 +- .../comments/browser/commentGlyphWidget.ts | 6 +- .../browser/breakpointEditorContribution.ts | 8 +- .../contrib/debug/browser/debugColors.ts | 19 +- .../debug/browser/debugEditorContribution.ts | 7 +- .../contrib/debug/browser/exceptionWidget.ts | 2 +- .../debug/browser/statusbarColorProvider.ts | 16 +- .../extensions/browser/extensionsActions.ts | 7 +- .../extensions/browser/extensionsWidgets.ts | 2 +- .../contrib/inlineChat/common/inlineChat.ts | 22 +- .../mergeEditor/browser/view/colors.ts | 18 +- .../notebook/browser/notebookEditorWidget.ts | 114 +--- .../preferences/browser/keybindingsEditor.ts | 4 +- .../common/settingsEditorColorRegistry.ts | 39 +- .../contrib/remote/browser/tunnelView.ts | 7 +- .../contrib/scm/browser/dirtydiffDecorator.ts | 42 +- .../contrib/scm/browser/scmViewPane.ts | 34 +- .../searchEditor/browser/searchEditor.ts | 2 +- .../terminal/common/terminalColorRegistry.ts | 42 +- .../terminalStickyScrollColorRegistry.ts | 7 +- .../contrib/testing/browser/theme.ts | 104 +--- .../browser/userDataProfilesEditor.ts | 2 +- .../browser/gettingStartedColors.ts | 6 +- 47 files changed, 429 insertions(+), 1172 deletions(-) diff --git a/src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts b/src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts index 36bd4d465ff..80b553bcb30 100644 --- a/src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts +++ b/src/vs/editor/browser/widget/diffEditor/registrations.contribution.ts @@ -12,13 +12,13 @@ import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; export const diffMoveBorder = registerColor( 'diffEditor.move.border', - { dark: '#8b8b8b9c', light: '#8b8b8b9c', hcDark: '#8b8b8b9c', hcLight: '#8b8b8b9c', }, + '#8b8b8b9c', localize('diffEditor.move.border', 'The border color for text that got moved in the diff editor.') ); export const diffMoveBorderActive = registerColor( 'diffEditor.moveActive.border', - { dark: '#FFA500', light: '#FFA500', hcDark: '#FFA500', hcLight: '#FFA500', }, + '#FFA500', localize('diffEditor.moveActive.border', 'The active border color for text that got moved in the diff editor.') ); diff --git a/src/vs/editor/browser/widget/multiDiffEditor/colors.ts b/src/vs/editor/browser/widget/multiDiffEditor/colors.ts index d58781aabfe..297e5e86465 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/colors.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/colors.ts @@ -14,7 +14,7 @@ export const multiDiffEditorHeaderBackground = registerColor( export const multiDiffEditorBackground = registerColor( 'multiDiffEditor.background', - { dark: 'editorBackground', light: 'editorBackground', hcDark: 'editorBackground', hcLight: 'editorBackground', }, + 'editorBackground', localize('multiDiffEditor.background', 'The background color of the multi file diff editor') ); diff --git a/src/vs/editor/common/core/editorColorRegistry.ts b/src/vs/editor/common/core/editorColorRegistry.ts index f7ad150e80c..b7c71f99721 100644 --- a/src/vs/editor/common/core/editorColorRegistry.ts +++ b/src/vs/editor/common/core/editorColorRegistry.ts @@ -11,7 +11,7 @@ import { registerThemingParticipant } from 'vs/platform/theme/common/themeServic /** * Definition of the editor colors */ -export const editorLineHighlight = registerColor('editor.lineHighlightBackground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('lineHighlight', 'Background color for the highlight of line at the cursor position.')); +export const editorLineHighlight = registerColor('editor.lineHighlightBackground', null, nls.localize('lineHighlight', 'Background color for the highlight of line at the cursor position.')); export const editorLineHighlightBorder = registerColor('editor.lineHighlightBorder', { dark: '#282828', light: '#eeeeee', hcDark: '#f38518', hcLight: contrastBorder }, nls.localize('lineHighlightBorderBox', 'Background color for the border around the line at the cursor position.')); export const editorRangeHighlight = registerColor('editor.rangeHighlightBackground', { dark: '#ffffff0b', light: '#fdff0033', hcDark: null, hcLight: null }, nls.localize('rangeHighlight', 'Background color of highlighted ranges, like by quick open and find features. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorRangeHighlightBorder = registerColor('editor.rangeHighlightBorder', { dark: null, light: null, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('rangeHighlightBorder', 'Background color of the border around highlighted ranges.')); @@ -21,32 +21,32 @@ export const editorSymbolHighlightBorder = registerColor('editor.symbolHighlight export const editorCursorForeground = registerColor('editorCursor.foreground', { dark: '#AEAFAD', light: Color.black, hcDark: Color.white, hcLight: '#0F4A85' }, nls.localize('caret', 'Color of the editor cursor.')); export const editorCursorBackground = registerColor('editorCursor.background', null, nls.localize('editorCursorBackground', 'The background color of the editor cursor. Allows customizing the color of a character overlapped by a block cursor.')); export const editorMultiCursorPrimaryForeground = registerColor('editorMultiCursor.primary.foreground', editorCursorForeground, nls.localize('editorMultiCursorPrimaryForeground', 'Color of the primary editor cursor when multiple cursors are present.')); -export const editorMultiCursorPrimaryBackground = registerColor('editorMultiCursor.primary.background', { dark: editorCursorBackground, light: editorCursorBackground, hcDark: editorCursorBackground, hcLight: editorCursorBackground }, nls.localize('editorMultiCursorPrimaryBackground', 'The background color of the primary editor cursor when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.')); -export const editorMultiCursorSecondaryForeground = registerColor('editorMultiCursor.secondary.foreground', { dark: editorCursorForeground, light: editorCursorForeground, hcDark: editorCursorForeground, hcLight: editorCursorForeground }, nls.localize('editorMultiCursorSecondaryForeground', 'Color of secondary editor cursors when multiple cursors are present.')); -export const editorMultiCursorSecondaryBackground = registerColor('editorMultiCursor.secondary.background', { dark: editorCursorBackground, light: editorCursorBackground, hcDark: editorCursorBackground, hcLight: editorCursorBackground }, nls.localize('editorMultiCursorSecondaryBackground', 'The background color of secondary editor cursors when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.')); +export const editorMultiCursorPrimaryBackground = registerColor('editorMultiCursor.primary.background', editorCursorBackground, nls.localize('editorMultiCursorPrimaryBackground', 'The background color of the primary editor cursor when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.')); +export const editorMultiCursorSecondaryForeground = registerColor('editorMultiCursor.secondary.foreground', editorCursorForeground, nls.localize('editorMultiCursorSecondaryForeground', 'Color of secondary editor cursors when multiple cursors are present.')); +export const editorMultiCursorSecondaryBackground = registerColor('editorMultiCursor.secondary.background', editorCursorBackground, nls.localize('editorMultiCursorSecondaryBackground', 'The background color of secondary editor cursors when multiple cursors are present. Allows customizing the color of a character overlapped by a block cursor.')); export const editorWhitespaces = registerColor('editorWhitespace.foreground', { dark: '#e3e4e229', light: '#33333333', hcDark: '#e3e4e229', hcLight: '#CCCCCC' }, nls.localize('editorWhitespaces', 'Color of whitespace characters in the editor.')); export const editorLineNumbers = registerColor('editorLineNumber.foreground', { dark: '#858585', light: '#237893', hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorLineNumbers', 'Color of editor line numbers.')); -export const deprecatedEditorIndentGuides = registerColor('editorIndentGuide.background', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.'), false, nls.localize('deprecatedEditorIndentGuides', '\'editorIndentGuide.background\' is deprecated. Use \'editorIndentGuide.background1\' instead.')); -export const deprecatedEditorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', { dark: editorWhitespaces, light: editorWhitespaces, hcDark: editorWhitespaces, hcLight: editorWhitespaces }, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.'), false, nls.localize('deprecatedEditorActiveIndentGuide', '\'editorIndentGuide.activeBackground\' is deprecated. Use \'editorIndentGuide.activeBackground1\' instead.')); +export const deprecatedEditorIndentGuides = registerColor('editorIndentGuide.background', editorWhitespaces, nls.localize('editorIndentGuides', 'Color of the editor indentation guides.'), false, nls.localize('deprecatedEditorIndentGuides', '\'editorIndentGuide.background\' is deprecated. Use \'editorIndentGuide.background1\' instead.')); +export const deprecatedEditorActiveIndentGuides = registerColor('editorIndentGuide.activeBackground', editorWhitespaces, nls.localize('editorActiveIndentGuide', 'Color of the active editor indentation guides.'), false, nls.localize('deprecatedEditorActiveIndentGuide', '\'editorIndentGuide.activeBackground\' is deprecated. Use \'editorIndentGuide.activeBackground1\' instead.')); -export const editorIndentGuide1 = registerColor('editorIndentGuide.background1', { dark: deprecatedEditorIndentGuides, light: deprecatedEditorIndentGuides, hcDark: deprecatedEditorIndentGuides, hcLight: deprecatedEditorIndentGuides }, nls.localize('editorIndentGuides1', 'Color of the editor indentation guides (1).')); -export const editorIndentGuide2 = registerColor('editorIndentGuide.background2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides2', 'Color of the editor indentation guides (2).')); -export const editorIndentGuide3 = registerColor('editorIndentGuide.background3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides3', 'Color of the editor indentation guides (3).')); -export const editorIndentGuide4 = registerColor('editorIndentGuide.background4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides4', 'Color of the editor indentation guides (4).')); -export const editorIndentGuide5 = registerColor('editorIndentGuide.background5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides5', 'Color of the editor indentation guides (5).')); -export const editorIndentGuide6 = registerColor('editorIndentGuide.background6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorIndentGuides6', 'Color of the editor indentation guides (6).')); +export const editorIndentGuide1 = registerColor('editorIndentGuide.background1', deprecatedEditorIndentGuides, nls.localize('editorIndentGuides1', 'Color of the editor indentation guides (1).')); +export const editorIndentGuide2 = registerColor('editorIndentGuide.background2', '#00000000', nls.localize('editorIndentGuides2', 'Color of the editor indentation guides (2).')); +export const editorIndentGuide3 = registerColor('editorIndentGuide.background3', '#00000000', nls.localize('editorIndentGuides3', 'Color of the editor indentation guides (3).')); +export const editorIndentGuide4 = registerColor('editorIndentGuide.background4', '#00000000', nls.localize('editorIndentGuides4', 'Color of the editor indentation guides (4).')); +export const editorIndentGuide5 = registerColor('editorIndentGuide.background5', '#00000000', nls.localize('editorIndentGuides5', 'Color of the editor indentation guides (5).')); +export const editorIndentGuide6 = registerColor('editorIndentGuide.background6', '#00000000', nls.localize('editorIndentGuides6', 'Color of the editor indentation guides (6).')); -export const editorActiveIndentGuide1 = registerColor('editorIndentGuide.activeBackground1', { dark: deprecatedEditorActiveIndentGuides, light: deprecatedEditorActiveIndentGuides, hcDark: deprecatedEditorActiveIndentGuides, hcLight: deprecatedEditorActiveIndentGuides }, nls.localize('editorActiveIndentGuide1', 'Color of the active editor indentation guides (1).')); -export const editorActiveIndentGuide2 = registerColor('editorIndentGuide.activeBackground2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide2', 'Color of the active editor indentation guides (2).')); -export const editorActiveIndentGuide3 = registerColor('editorIndentGuide.activeBackground3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide3', 'Color of the active editor indentation guides (3).')); -export const editorActiveIndentGuide4 = registerColor('editorIndentGuide.activeBackground4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide4', 'Color of the active editor indentation guides (4).')); -export const editorActiveIndentGuide5 = registerColor('editorIndentGuide.activeBackground5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide5', 'Color of the active editor indentation guides (5).')); -export const editorActiveIndentGuide6 = registerColor('editorIndentGuide.activeBackground6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorActiveIndentGuide6', 'Color of the active editor indentation guides (6).')); +export const editorActiveIndentGuide1 = registerColor('editorIndentGuide.activeBackground1', deprecatedEditorActiveIndentGuides, nls.localize('editorActiveIndentGuide1', 'Color of the active editor indentation guides (1).')); +export const editorActiveIndentGuide2 = registerColor('editorIndentGuide.activeBackground2', '#00000000', nls.localize('editorActiveIndentGuide2', 'Color of the active editor indentation guides (2).')); +export const editorActiveIndentGuide3 = registerColor('editorIndentGuide.activeBackground3', '#00000000', nls.localize('editorActiveIndentGuide3', 'Color of the active editor indentation guides (3).')); +export const editorActiveIndentGuide4 = registerColor('editorIndentGuide.activeBackground4', '#00000000', nls.localize('editorActiveIndentGuide4', 'Color of the active editor indentation guides (4).')); +export const editorActiveIndentGuide5 = registerColor('editorIndentGuide.activeBackground5', '#00000000', nls.localize('editorActiveIndentGuide5', 'Color of the active editor indentation guides (5).')); +export const editorActiveIndentGuide6 = registerColor('editorIndentGuide.activeBackground6', '#00000000', nls.localize('editorActiveIndentGuide6', 'Color of the active editor indentation guides (6).')); const deprecatedEditorActiveLineNumber = registerColor('editorActiveLineNumber.foreground', { dark: '#c6c6c6', light: '#0B216F', hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('editorActiveLineNumber', 'Color of editor active line number'), false, nls.localize('deprecatedEditorActiveLineNumber', 'Id is deprecated. Use \'editorLineNumber.activeForeground\' instead.')); -export const editorActiveLineNumber = registerColor('editorLineNumber.activeForeground', { dark: deprecatedEditorActiveLineNumber, light: deprecatedEditorActiveLineNumber, hcDark: deprecatedEditorActiveLineNumber, hcLight: deprecatedEditorActiveLineNumber }, nls.localize('editorActiveLineNumber', 'Color of editor active line number')); -export const editorDimmedLineNumber = registerColor('editorLineNumber.dimmedForeground', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('editorDimmedLineNumber', 'Color of the final editor line when editor.renderFinalNewline is set to dimmed.')); +export const editorActiveLineNumber = registerColor('editorLineNumber.activeForeground', deprecatedEditorActiveLineNumber, nls.localize('editorActiveLineNumber', 'Color of editor active line number')); +export const editorDimmedLineNumber = registerColor('editorLineNumber.dimmedForeground', null, nls.localize('editorDimmedLineNumber', 'Color of the final editor line when editor.renderFinalNewline is set to dimmed.')); export const editorRuler = registerColor('editorRuler.foreground', { dark: '#5A5A5A', light: Color.lightgrey, hcDark: Color.white, hcLight: '#292929' }, nls.localize('editorRuler', 'Color of the editor rulers.')); @@ -58,17 +58,17 @@ export const editorBracketMatchBorder = registerColor('editorBracketMatch.border export const editorOverviewRulerBorder = registerColor('editorOverviewRuler.border', { dark: '#7f7f7f4d', light: '#7f7f7f4d', hcDark: '#7f7f7f4d', hcLight: '#666666' }, nls.localize('editorOverviewRulerBorder', 'Color of the overview ruler border.')); export const editorOverviewRulerBackground = registerColor('editorOverviewRuler.background', null, nls.localize('editorOverviewRulerBackground', 'Background color of the editor overview ruler.')); -export const editorGutter = registerColor('editorGutter.background', { dark: editorBackground, light: editorBackground, hcDark: editorBackground, hcLight: editorBackground }, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.')); +export const editorGutter = registerColor('editorGutter.background', editorBackground, nls.localize('editorGutter', 'Background color of the editor gutter. The gutter contains the glyph margins and the line numbers.')); export const editorUnnecessaryCodeBorder = registerColor('editorUnnecessaryCode.border', { dark: null, light: null, hcDark: Color.fromHex('#fff').transparent(0.8), hcLight: contrastBorder }, nls.localize('unnecessaryCodeBorder', 'Border color of unnecessary (unused) source code in the editor.')); export const editorUnnecessaryCodeOpacity = registerColor('editorUnnecessaryCode.opacity', { dark: Color.fromHex('#000a'), light: Color.fromHex('#0007'), hcDark: null, hcLight: null }, nls.localize('unnecessaryCodeOpacity', 'Opacity of unnecessary (unused) source code in the editor. For example, "#000000c0" will render the code with 75% opacity. For high contrast themes, use the \'editorUnnecessaryCode.border\' theme color to underline unnecessary code instead of fading it out.')); export const ghostTextBorder = registerColor('editorGhostText.border', { dark: null, light: null, hcDark: Color.fromHex('#fff').transparent(0.8), hcLight: Color.fromHex('#292929').transparent(0.8) }, nls.localize('editorGhostTextBorder', 'Border color of ghost text in the editor.')); export const ghostTextForeground = registerColor('editorGhostText.foreground', { dark: Color.fromHex('#ffffff56'), light: Color.fromHex('#0007'), hcDark: null, hcLight: null }, nls.localize('editorGhostTextForeground', 'Foreground color of the ghost text in the editor.')); -export const ghostTextBackground = registerColor('editorGhostText.background', { dark: null, light: null, hcDark: null, hcLight: null }, nls.localize('editorGhostTextBackground', 'Background color of the ghost text in the editor.')); +export const ghostTextBackground = registerColor('editorGhostText.background', null, nls.localize('editorGhostTextBackground', 'Background color of the ghost text in the editor.')); const rulerRangeDefault = new Color(new RGBA(0, 122, 204, 0.6)); -export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', { dark: rulerRangeDefault, light: rulerRangeDefault, hcDark: rulerRangeDefault, hcLight: rulerRangeDefault }, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true); +export const overviewRulerRangeHighlight = registerColor('editorOverviewRuler.rangeHighlightForeground', rulerRangeDefault, nls.localize('overviewRulerRangeHighlight', 'Overview ruler marker color for range highlights. The color must not be opaque so as not to hide underlying decorations.'), true); export const overviewRulerError = registerColor('editorOverviewRuler.errorForeground', { dark: new Color(new RGBA(255, 18, 18, 0.7)), light: new Color(new RGBA(255, 18, 18, 0.7)), hcDark: new Color(new RGBA(255, 50, 50, 1)), hcLight: '#B5200D' }, nls.localize('overviewRuleError', 'Overview ruler marker color for errors.')); export const overviewRulerWarning = registerColor('editorOverviewRuler.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hcDark: editorWarningBorder, hcLight: editorWarningBorder }, nls.localize('overviewRuleWarning', 'Overview ruler marker color for warnings.')); export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hcDark: editorInfoBorder, hcLight: editorInfoBorder }, nls.localize('overviewRuleInfo', 'Overview ruler marker color for infos.')); @@ -76,28 +76,28 @@ export const overviewRulerInfo = registerColor('editorOverviewRuler.infoForegrou export const editorBracketHighlightingForeground1 = registerColor('editorBracketHighlight.foreground1', { dark: '#FFD700', light: '#0431FAFF', hcDark: '#FFD700', hcLight: '#0431FAFF' }, nls.localize('editorBracketHighlightForeground1', 'Foreground color of brackets (1). Requires enabling bracket pair colorization.')); export const editorBracketHighlightingForeground2 = registerColor('editorBracketHighlight.foreground2', { dark: '#DA70D6', light: '#319331FF', hcDark: '#DA70D6', hcLight: '#319331FF' }, nls.localize('editorBracketHighlightForeground2', 'Foreground color of brackets (2). Requires enabling bracket pair colorization.')); export const editorBracketHighlightingForeground3 = registerColor('editorBracketHighlight.foreground3', { dark: '#179FFF', light: '#7B3814FF', hcDark: '#87CEFA', hcLight: '#7B3814FF' }, nls.localize('editorBracketHighlightForeground3', 'Foreground color of brackets (3). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingForeground4 = registerColor('editorBracketHighlight.foreground4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketHighlightForeground4', 'Foreground color of brackets (4). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingForeground5 = registerColor('editorBracketHighlight.foreground5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketHighlightForeground5', 'Foreground color of brackets (5). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingForeground6 = registerColor('editorBracketHighlight.foreground6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketHighlightForeground6', 'Foreground color of brackets (6). Requires enabling bracket pair colorization.')); +export const editorBracketHighlightingForeground4 = registerColor('editorBracketHighlight.foreground4', '#00000000', nls.localize('editorBracketHighlightForeground4', 'Foreground color of brackets (4). Requires enabling bracket pair colorization.')); +export const editorBracketHighlightingForeground5 = registerColor('editorBracketHighlight.foreground5', '#00000000', nls.localize('editorBracketHighlightForeground5', 'Foreground color of brackets (5). Requires enabling bracket pair colorization.')); +export const editorBracketHighlightingForeground6 = registerColor('editorBracketHighlight.foreground6', '#00000000', nls.localize('editorBracketHighlightForeground6', 'Foreground color of brackets (6). Requires enabling bracket pair colorization.')); export const editorBracketHighlightingUnexpectedBracketForeground = registerColor('editorBracketHighlight.unexpectedBracket.foreground', { dark: new Color(new RGBA(255, 18, 18, 0.8)), light: new Color(new RGBA(255, 18, 18, 0.8)), hcDark: new Color(new RGBA(255, 50, 50, 1)), hcLight: '' }, nls.localize('editorBracketHighlightUnexpectedBracketForeground', 'Foreground color of unexpected brackets.')); -export const editorBracketPairGuideBackground1 = registerColor('editorBracketPairGuide.background1', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.background1', 'Background color of inactive bracket pair guides (1). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground2 = registerColor('editorBracketPairGuide.background2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.background2', 'Background color of inactive bracket pair guides (2). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground3 = registerColor('editorBracketPairGuide.background3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.background3', 'Background color of inactive bracket pair guides (3). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground4 = registerColor('editorBracketPairGuide.background4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.background4', 'Background color of inactive bracket pair guides (4). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground5 = registerColor('editorBracketPairGuide.background5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.background5', 'Background color of inactive bracket pair guides (5). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideBackground6 = registerColor('editorBracketPairGuide.background6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.background6', 'Background color of inactive bracket pair guides (6). Requires enabling bracket pair guides.')); - -export const editorBracketPairGuideActiveBackground1 = registerColor('editorBracketPairGuide.activeBackground1', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.activeBackground1', 'Background color of active bracket pair guides (1). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground2 = registerColor('editorBracketPairGuide.activeBackground2', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.activeBackground2', 'Background color of active bracket pair guides (2). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground3 = registerColor('editorBracketPairGuide.activeBackground3', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.activeBackground3', 'Background color of active bracket pair guides (3). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground4 = registerColor('editorBracketPairGuide.activeBackground4', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.activeBackground4', 'Background color of active bracket pair guides (4). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground5 = registerColor('editorBracketPairGuide.activeBackground5', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.activeBackground5', 'Background color of active bracket pair guides (5). Requires enabling bracket pair guides.')); -export const editorBracketPairGuideActiveBackground6 = registerColor('editorBracketPairGuide.activeBackground6', { dark: '#00000000', light: '#00000000', hcDark: '#00000000', hcLight: '#00000000' }, nls.localize('editorBracketPairGuide.activeBackground6', 'Background color of active bracket pair guides (6). Requires enabling bracket pair guides.')); - -export const editorUnicodeHighlightBorder = registerColor('editorUnicodeHighlight.border', { dark: editorWarningForeground, light: editorWarningForeground, hcDark: editorWarningForeground, hcLight: editorWarningForeground }, nls.localize('editorUnicodeHighlight.border', 'Border color used to highlight unicode characters.')); -export const editorUnicodeHighlightBackground = registerColor('editorUnicodeHighlight.background', { dark: editorWarningBackground, light: editorWarningBackground, hcDark: editorWarningBackground, hcLight: editorWarningBackground }, nls.localize('editorUnicodeHighlight.background', 'Background color used to highlight unicode characters.')); +export const editorBracketPairGuideBackground1 = registerColor('editorBracketPairGuide.background1', '#00000000', nls.localize('editorBracketPairGuide.background1', 'Background color of inactive bracket pair guides (1). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground2 = registerColor('editorBracketPairGuide.background2', '#00000000', nls.localize('editorBracketPairGuide.background2', 'Background color of inactive bracket pair guides (2). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground3 = registerColor('editorBracketPairGuide.background3', '#00000000', nls.localize('editorBracketPairGuide.background3', 'Background color of inactive bracket pair guides (3). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground4 = registerColor('editorBracketPairGuide.background4', '#00000000', nls.localize('editorBracketPairGuide.background4', 'Background color of inactive bracket pair guides (4). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground5 = registerColor('editorBracketPairGuide.background5', '#00000000', nls.localize('editorBracketPairGuide.background5', 'Background color of inactive bracket pair guides (5). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideBackground6 = registerColor('editorBracketPairGuide.background6', '#00000000', nls.localize('editorBracketPairGuide.background6', 'Background color of inactive bracket pair guides (6). Requires enabling bracket pair guides.')); + +export const editorBracketPairGuideActiveBackground1 = registerColor('editorBracketPairGuide.activeBackground1', '#00000000', nls.localize('editorBracketPairGuide.activeBackground1', 'Background color of active bracket pair guides (1). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground2 = registerColor('editorBracketPairGuide.activeBackground2', '#00000000', nls.localize('editorBracketPairGuide.activeBackground2', 'Background color of active bracket pair guides (2). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground3 = registerColor('editorBracketPairGuide.activeBackground3', '#00000000', nls.localize('editorBracketPairGuide.activeBackground3', 'Background color of active bracket pair guides (3). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground4 = registerColor('editorBracketPairGuide.activeBackground4', '#00000000', nls.localize('editorBracketPairGuide.activeBackground4', 'Background color of active bracket pair guides (4). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground5 = registerColor('editorBracketPairGuide.activeBackground5', '#00000000', nls.localize('editorBracketPairGuide.activeBackground5', 'Background color of active bracket pair guides (5). Requires enabling bracket pair guides.')); +export const editorBracketPairGuideActiveBackground6 = registerColor('editorBracketPairGuide.activeBackground6', '#00000000', nls.localize('editorBracketPairGuide.activeBackground6', 'Background color of active bracket pair guides (6). Requires enabling bracket pair guides.')); + +export const editorUnicodeHighlightBorder = registerColor('editorUnicodeHighlight.border', editorWarningForeground, nls.localize('editorUnicodeHighlight.border', 'Border color used to highlight unicode characters.')); +export const editorUnicodeHighlightBackground = registerColor('editorUnicodeHighlight.background', editorWarningBackground, nls.localize('editorUnicodeHighlight.background', 'Background color used to highlight unicode characters.')); // contains all color rules that used to defined in editor/browser/widget/editor.css diff --git a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts index ffd9e3240dd..b3530eaa888 100644 --- a/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts +++ b/src/vs/editor/contrib/bracketMatching/browser/bracketMatching.ts @@ -23,7 +23,7 @@ import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegis import { registerColor } from 'vs/platform/theme/common/colorRegistry'; import { themeColorFromId } from 'vs/platform/theme/common/themeService'; -const overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', { dark: '#A0A0A0', light: '#A0A0A0', hcDark: '#A0A0A0', hcLight: '#A0A0A0' }, nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.')); +const overviewRulerBracketMatchForeground = registerColor('editorOverviewRuler.bracketMatchForeground', '#A0A0A0', nls.localize('overviewRulerBracketMatchForeground', 'Overview ruler marker color for matching brackets.')); class JumpToBracketAction extends EditorAction { constructor() { diff --git a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts index 03a9b4a402c..b6ae9cd31e2 100644 --- a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts @@ -15,7 +15,7 @@ import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/base/common/themables'; const foldBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hcDark: null, hcLight: null }, localize('foldBackgroundBackground', "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations."), true); -registerColor('editorGutter.foldingControlForeground', { dark: iconForeground, light: iconForeground, hcDark: iconForeground, hcLight: iconForeground }, localize('editorGutter.foldingControlForeground', 'Color of the folding control in the editor gutter.')); +registerColor('editorGutter.foldingControlForeground', iconForeground, localize('editorGutter.foldingControlForeground', 'Color of the folding control in the editor gutter.')); export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown, localize('foldingExpandedIcon', 'Icon for expanded ranges in the editor glyph margin.')); export const foldingCollapsedIcon = registerIcon('folding-collapsed', Codicon.chevronRight, localize('foldingCollapsedIcon', 'Icon for collapsed ranges in the editor glyph margin.')); diff --git a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts index 24ffac212bd..00f716bb45f 100644 --- a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts @@ -410,4 +410,4 @@ const editorMarkerNavigationWarningHeader = registerColor('editorMarkerNavigatio const editorMarkerNavigationInfo = registerColor('editorMarkerNavigationInfo.background', { dark: infoDefault, light: infoDefault, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('editorMarkerNavigationInfo', 'Editor marker navigation widget info color.')); const editorMarkerNavigationInfoHeader = registerColor('editorMarkerNavigationInfo.headerBackground', { dark: transparent(editorMarkerNavigationInfo, .1), light: transparent(editorMarkerNavigationInfo, .1), hcDark: null, hcLight: null }, nls.localize('editorMarkerNavigationInfoHeaderBackground', 'Editor marker navigation widget info heading background.')); -const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', { dark: editorBackground, light: editorBackground, hcDark: editorBackground, hcLight: editorBackground }, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.')); +const editorMarkerNavigationBackground = registerColor('editorMarkerNavigation.background', editorBackground, nls.localize('editorMarkerNavigationBackground', 'Editor marker navigation widget background.')); diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index f5b8af528e3..3036277c448 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -387,4 +387,4 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } } -registerColor('editorHoverWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('editorHoverWidgetHighlightForeground', 'Foreground color of the active item in the parameter hint.')); +registerColor('editorHoverWidget.highlightForeground', listHighlightForeground, nls.localize('editorHoverWidgetHighlightForeground', 'Foreground color of the active item in the parameter hint.')); diff --git a/src/vs/editor/contrib/peekView/browser/peekView.ts b/src/vs/editor/contrib/peekView/browser/peekView.ts index a0f2dfd914e..86f6cb1d47f 100644 --- a/src/vs/editor/contrib/peekView/browser/peekView.ts +++ b/src/vs/editor/contrib/peekView/browser/peekView.ts @@ -289,8 +289,8 @@ export const peekViewResultsFileForeground = registerColor('peekViewResult.fileF export const peekViewResultsSelectionBackground = registerColor('peekViewResult.selectionBackground', { dark: '#3399ff33', light: '#3399ff33', hcDark: null, hcLight: null }, nls.localize('peekViewResultsSelectionBackground', 'Background color of the selected entry in the peek view result list.')); export const peekViewResultsSelectionForeground = registerColor('peekViewResult.selectionForeground', { dark: Color.white, light: '#6C6C6C', hcDark: Color.white, hcLight: editorForeground }, nls.localize('peekViewResultsSelectionForeground', 'Foreground color of the selected entry in the peek view result list.')); export const peekViewEditorBackground = registerColor('peekViewEditor.background', { dark: '#001F33', light: '#F2F8FC', hcDark: Color.black, hcLight: Color.white }, nls.localize('peekViewEditorBackground', 'Background color of the peek view editor.')); -export const peekViewEditorGutterBackground = registerColor('peekViewEditorGutter.background', { dark: peekViewEditorBackground, light: peekViewEditorBackground, hcDark: peekViewEditorBackground, hcLight: peekViewEditorBackground }, nls.localize('peekViewEditorGutterBackground', 'Background color of the gutter in the peek view editor.')); -export const peekViewEditorStickyScrollBackground = registerColor('peekViewEditorStickyScroll.background', { dark: peekViewEditorBackground, light: peekViewEditorBackground, hcDark: peekViewEditorBackground, hcLight: peekViewEditorBackground }, nls.localize('peekViewEditorStickScrollBackground', 'Background color of sticky scroll in the peek view editor.')); +export const peekViewEditorGutterBackground = registerColor('peekViewEditorGutter.background', peekViewEditorBackground, nls.localize('peekViewEditorGutterBackground', 'Background color of the gutter in the peek view editor.')); +export const peekViewEditorStickyScrollBackground = registerColor('peekViewEditorStickyScroll.background', peekViewEditorBackground, nls.localize('peekViewEditorStickScrollBackground', 'Background color of sticky scroll in the peek view editor.')); export const peekViewResultsMatchHighlight = registerColor('peekViewResult.matchHighlightBackground', { dark: '#ea5c004d', light: '#ea5c004d', hcDark: null, hcLight: null }, nls.localize('peekViewResultsMatchHighlight', 'Match highlight color in the peek view result list.')); export const peekViewEditorMatchHighlight = registerColor('peekViewEditor.matchHighlightBackground', { dark: '#ff8f0099', light: '#f5d802de', hcDark: null, hcLight: null }, nls.localize('peekViewEditorMatchHighlight', 'Match highlight color in the peek view editor.')); diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 2eeb94d99b6..2d30e5925ab 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -39,15 +39,15 @@ import { status } from 'vs/base/browser/ui/aria/aria'; /** * Suggest widget colors */ -registerColor('editorSuggestWidget.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.')); -registerColor('editorSuggestWidget.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.')); -const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', { dark: editorForeground, light: editorForeground, hcDark: editorForeground, hcLight: editorForeground }, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.')); -registerColor('editorSuggestWidget.selectedForeground', { dark: quickInputListFocusForeground, light: quickInputListFocusForeground, hcDark: quickInputListFocusForeground, hcLight: quickInputListFocusForeground }, nls.localize('editorSuggestWidgetSelectedForeground', 'Foreground color of the selected entry in the suggest widget.')); -registerColor('editorSuggestWidget.selectedIconForeground', { dark: quickInputListFocusIconForeground, light: quickInputListFocusIconForeground, hcDark: quickInputListFocusIconForeground, hcLight: quickInputListFocusIconForeground }, nls.localize('editorSuggestWidgetSelectedIconForeground', 'Icon foreground color of the selected entry in the suggest widget.')); -export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', { dark: quickInputListFocusBackground, light: quickInputListFocusBackground, hcDark: quickInputListFocusBackground, hcLight: quickInputListFocusBackground }, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); -registerColor('editorSuggestWidget.highlightForeground', { dark: listHighlightForeground, light: listHighlightForeground, hcDark: listHighlightForeground, hcLight: listHighlightForeground }, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.')); -registerColor('editorSuggestWidget.focusHighlightForeground', { dark: listFocusHighlightForeground, light: listFocusHighlightForeground, hcDark: listFocusHighlightForeground, hcLight: listFocusHighlightForeground }, nls.localize('editorSuggestWidgetFocusHighlightForeground', 'Color of the match highlights in the suggest widget when an item is focused.')); -registerColor('editorSuggestWidgetStatus.foreground', { dark: transparent(editorSuggestWidgetForeground, .5), light: transparent(editorSuggestWidgetForeground, .5), hcDark: transparent(editorSuggestWidgetForeground, .5), hcLight: transparent(editorSuggestWidgetForeground, .5) }, nls.localize('editorSuggestWidgetStatusForeground', 'Foreground color of the suggest widget status.')); +registerColor('editorSuggestWidget.background', editorWidgetBackground, nls.localize('editorSuggestWidgetBackground', 'Background color of the suggest widget.')); +registerColor('editorSuggestWidget.border', editorWidgetBorder, nls.localize('editorSuggestWidgetBorder', 'Border color of the suggest widget.')); +const editorSuggestWidgetForeground = registerColor('editorSuggestWidget.foreground', editorForeground, nls.localize('editorSuggestWidgetForeground', 'Foreground color of the suggest widget.')); +registerColor('editorSuggestWidget.selectedForeground', quickInputListFocusForeground, nls.localize('editorSuggestWidgetSelectedForeground', 'Foreground color of the selected entry in the suggest widget.')); +registerColor('editorSuggestWidget.selectedIconForeground', quickInputListFocusIconForeground, nls.localize('editorSuggestWidgetSelectedIconForeground', 'Icon foreground color of the selected entry in the suggest widget.')); +export const editorSuggestWidgetSelectedBackground = registerColor('editorSuggestWidget.selectedBackground', quickInputListFocusBackground, nls.localize('editorSuggestWidgetSelectedBackground', 'Background color of the selected entry in the suggest widget.')); +registerColor('editorSuggestWidget.highlightForeground', listHighlightForeground, nls.localize('editorSuggestWidgetHighlightForeground', 'Color of the match highlights in the suggest widget.')); +registerColor('editorSuggestWidget.focusHighlightForeground', listFocusHighlightForeground, nls.localize('editorSuggestWidgetFocusHighlightForeground', 'Color of the match highlights in the suggest widget when an item is focused.')); +registerColor('editorSuggestWidgetStatus.foreground', transparent(editorSuggestWidgetForeground, .5), nls.localize('editorSuggestWidgetStatusForeground', 'Foreground color of the suggest widget status.')); const enum State { Hidden, diff --git a/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.ts b/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.ts index 64ccb1bc618..7d6fe4b73f6 100644 --- a/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.ts +++ b/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.ts @@ -7,19 +7,9 @@ import 'vs/css!./symbolIcons'; import { localize } from 'vs/nls'; import { foreground, registerColor } from 'vs/platform/theme/common/colorRegistry'; -export const SYMBOL_ICON_ARRAY_FOREGROUND = registerColor('symbolIcon.arrayForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground, -}, localize('symbolIcon.arrayForeground', 'The foreground color for array symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_BOOLEAN_FOREGROUND = registerColor('symbolIcon.booleanForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground, -}, localize('symbolIcon.booleanForeground', 'The foreground color for boolean symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); +export const SYMBOL_ICON_ARRAY_FOREGROUND = registerColor('symbolIcon.arrayForeground', foreground, localize('symbolIcon.arrayForeground', 'The foreground color for array symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_BOOLEAN_FOREGROUND = registerColor('symbolIcon.booleanForeground', foreground, localize('symbolIcon.booleanForeground', 'The foreground color for boolean symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); export const SYMBOL_ICON_CLASS_FOREGROUND = registerColor('symbolIcon.classForeground', { dark: '#EE9D28', @@ -28,19 +18,9 @@ export const SYMBOL_ICON_CLASS_FOREGROUND = registerColor('symbolIcon.classForeg hcLight: '#D67E00' }, localize('symbolIcon.classForeground', 'The foreground color for class symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -export const SYMBOL_ICON_COLOR_FOREGROUND = registerColor('symbolIcon.colorForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.colorForeground', 'The foreground color for color symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); +export const SYMBOL_ICON_COLOR_FOREGROUND = registerColor('symbolIcon.colorForeground', foreground, localize('symbolIcon.colorForeground', 'The foreground color for color symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -export const SYMBOL_ICON_CONSTANT_FOREGROUND = registerColor('symbolIcon.constantForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.constantForeground', 'The foreground color for constant symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); +export const SYMBOL_ICON_CONSTANT_FOREGROUND = registerColor('symbolIcon.constantForeground', foreground, localize('symbolIcon.constantForeground', 'The foreground color for constant symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); export const SYMBOL_ICON_CONSTRUCTOR_FOREGROUND = registerColor('symbolIcon.constructorForeground', { dark: '#B180D7', @@ -77,19 +57,9 @@ export const SYMBOL_ICON_FIELD_FOREGROUND = registerColor('symbolIcon.fieldForeg hcLight: '#007ACC' }, localize('symbolIcon.fieldForeground', 'The foreground color for field symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -export const SYMBOL_ICON_FILE_FOREGROUND = registerColor('symbolIcon.fileForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.fileForeground', 'The foreground color for file symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); +export const SYMBOL_ICON_FILE_FOREGROUND = registerColor('symbolIcon.fileForeground', foreground, localize('symbolIcon.fileForeground', 'The foreground color for file symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -export const SYMBOL_ICON_FOLDER_FOREGROUND = registerColor('symbolIcon.folderForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.folderForeground', 'The foreground color for folder symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); +export const SYMBOL_ICON_FOLDER_FOREGROUND = registerColor('symbolIcon.folderForeground', foreground, localize('symbolIcon.folderForeground', 'The foreground color for folder symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); export const SYMBOL_ICON_FUNCTION_FOREGROUND = registerColor('symbolIcon.functionForeground', { dark: '#B180D7', @@ -105,19 +75,9 @@ export const SYMBOL_ICON_INTERFACE_FOREGROUND = registerColor('symbolIcon.interf hcLight: '#007ACC' }, localize('symbolIcon.interfaceForeground', 'The foreground color for interface symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -export const SYMBOL_ICON_KEY_FOREGROUND = registerColor('symbolIcon.keyForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.keyForeground', 'The foreground color for key symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); +export const SYMBOL_ICON_KEY_FOREGROUND = registerColor('symbolIcon.keyForeground', foreground, localize('symbolIcon.keyForeground', 'The foreground color for key symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -export const SYMBOL_ICON_KEYWORD_FOREGROUND = registerColor('symbolIcon.keywordForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.keywordForeground', 'The foreground color for keyword symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); +export const SYMBOL_ICON_KEYWORD_FOREGROUND = registerColor('symbolIcon.keywordForeground', foreground, localize('symbolIcon.keywordForeground', 'The foreground color for keyword symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); export const SYMBOL_ICON_METHOD_FOREGROUND = registerColor('symbolIcon.methodForeground', { dark: '#B180D7', @@ -126,110 +86,35 @@ export const SYMBOL_ICON_METHOD_FOREGROUND = registerColor('symbolIcon.methodFor hcLight: '#652D90' }, localize('symbolIcon.methodForeground', 'The foreground color for method symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -export const SYMBOL_ICON_MODULE_FOREGROUND = registerColor('symbolIcon.moduleForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.moduleForeground', 'The foreground color for module symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_NAMESPACE_FOREGROUND = registerColor('symbolIcon.namespaceForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.namespaceForeground', 'The foreground color for namespace symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_NULL_FOREGROUND = registerColor('symbolIcon.nullForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.nullForeground', 'The foreground color for null symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_NUMBER_FOREGROUND = registerColor('symbolIcon.numberForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.numberForeground', 'The foreground color for number symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_OBJECT_FOREGROUND = registerColor('symbolIcon.objectForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.objectForeground', 'The foreground color for object symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_OPERATOR_FOREGROUND = registerColor('symbolIcon.operatorForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.operatorForeground', 'The foreground color for operator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_PACKAGE_FOREGROUND = registerColor('symbolIcon.packageForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.packageForeground', 'The foreground color for package symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_PROPERTY_FOREGROUND = registerColor('symbolIcon.propertyForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.propertyForeground', 'The foreground color for property symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_REFERENCE_FOREGROUND = registerColor('symbolIcon.referenceForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.referenceForeground', 'The foreground color for reference symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_SNIPPET_FOREGROUND = registerColor('symbolIcon.snippetForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.snippetForeground', 'The foreground color for snippet symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_STRING_FOREGROUND = registerColor('symbolIcon.stringForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.stringForeground', 'The foreground color for string symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_STRUCT_FOREGROUND = registerColor('symbolIcon.structForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground, -}, localize('symbolIcon.structForeground', 'The foreground color for struct symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_TEXT_FOREGROUND = registerColor('symbolIcon.textForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.textForeground', 'The foreground color for text symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_TYPEPARAMETER_FOREGROUND = registerColor('symbolIcon.typeParameterForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.typeParameterForeground', 'The foreground color for type parameter symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_UNIT_FOREGROUND = registerColor('symbolIcon.unitForeground', { - dark: foreground, - light: foreground, - hcDark: foreground, - hcLight: foreground -}, localize('symbolIcon.unitForeground', 'The foreground color for unit symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); +export const SYMBOL_ICON_MODULE_FOREGROUND = registerColor('symbolIcon.moduleForeground', foreground, localize('symbolIcon.moduleForeground', 'The foreground color for module symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_NAMESPACE_FOREGROUND = registerColor('symbolIcon.namespaceForeground', foreground, localize('symbolIcon.namespaceForeground', 'The foreground color for namespace symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_NULL_FOREGROUND = registerColor('symbolIcon.nullForeground', foreground, localize('symbolIcon.nullForeground', 'The foreground color for null symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_NUMBER_FOREGROUND = registerColor('symbolIcon.numberForeground', foreground, localize('symbolIcon.numberForeground', 'The foreground color for number symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_OBJECT_FOREGROUND = registerColor('symbolIcon.objectForeground', foreground, localize('symbolIcon.objectForeground', 'The foreground color for object symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_OPERATOR_FOREGROUND = registerColor('symbolIcon.operatorForeground', foreground, localize('symbolIcon.operatorForeground', 'The foreground color for operator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_PACKAGE_FOREGROUND = registerColor('symbolIcon.packageForeground', foreground, localize('symbolIcon.packageForeground', 'The foreground color for package symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_PROPERTY_FOREGROUND = registerColor('symbolIcon.propertyForeground', foreground, localize('symbolIcon.propertyForeground', 'The foreground color for property symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_REFERENCE_FOREGROUND = registerColor('symbolIcon.referenceForeground', foreground, localize('symbolIcon.referenceForeground', 'The foreground color for reference symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_SNIPPET_FOREGROUND = registerColor('symbolIcon.snippetForeground', foreground, localize('symbolIcon.snippetForeground', 'The foreground color for snippet symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_STRING_FOREGROUND = registerColor('symbolIcon.stringForeground', foreground, localize('symbolIcon.stringForeground', 'The foreground color for string symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_STRUCT_FOREGROUND = registerColor('symbolIcon.structForeground', foreground, localize('symbolIcon.structForeground', 'The foreground color for struct symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_TEXT_FOREGROUND = registerColor('symbolIcon.textForeground', foreground, localize('symbolIcon.textForeground', 'The foreground color for text symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_TYPEPARAMETER_FOREGROUND = registerColor('symbolIcon.typeParameterForeground', foreground, localize('symbolIcon.typeParameterForeground', 'The foreground color for type parameter symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); + +export const SYMBOL_ICON_UNIT_FOREGROUND = registerColor('symbolIcon.unitForeground', foreground, localize('symbolIcon.unitForeground', 'The foreground color for unit symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); export const SYMBOL_ICON_VARIABLE_FOREGROUND = registerColor('symbolIcon.variableForeground', { dark: '#75BEFF', diff --git a/src/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.ts b/src/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.ts index b3825b0f05d..79ec7ad02e6 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/highlightDecorations.ts @@ -13,13 +13,13 @@ import { registerThemingParticipant, themeColorFromId } from 'vs/platform/theme/ const wordHighlightBackground = registerColor('editor.wordHighlightBackground', { dark: '#575757B8', light: '#57575740', hcDark: null, hcLight: null }, nls.localize('wordHighlight', 'Background color of a symbol during read-access, like reading a variable. The color must not be opaque so as not to hide underlying decorations.'), true); registerColor('editor.wordHighlightStrongBackground', { dark: '#004972B8', light: '#0e639c40', hcDark: null, hcLight: null }, nls.localize('wordHighlightStrong', 'Background color of a symbol during write-access, like writing to a variable. The color must not be opaque so as not to hide underlying decorations.'), true); -registerColor('editor.wordHighlightTextBackground', { light: wordHighlightBackground, dark: wordHighlightBackground, hcDark: wordHighlightBackground, hcLight: wordHighlightBackground }, nls.localize('wordHighlightText', 'Background color of a textual occurrence for a symbol. The color must not be opaque so as not to hide underlying decorations.'), true); +registerColor('editor.wordHighlightTextBackground', wordHighlightBackground, nls.localize('wordHighlightText', 'Background color of a textual occurrence for a symbol. The color must not be opaque so as not to hide underlying decorations.'), true); const wordHighlightBorder = registerColor('editor.wordHighlightBorder', { light: null, dark: null, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('wordHighlightBorder', 'Border color of a symbol during read-access, like reading a variable.')); registerColor('editor.wordHighlightStrongBorder', { light: null, dark: null, hcDark: activeContrastBorder, hcLight: activeContrastBorder }, nls.localize('wordHighlightStrongBorder', 'Border color of a symbol during write-access, like writing to a variable.')); -registerColor('editor.wordHighlightTextBorder', { light: wordHighlightBorder, dark: wordHighlightBorder, hcDark: wordHighlightBorder, hcLight: wordHighlightBorder }, nls.localize('wordHighlightTextBorder', "Border color of a textual occurrence for a symbol.")); -const overviewRulerWordHighlightForeground = registerColor('editorOverviewRuler.wordHighlightForeground', { dark: '#A0A0A0CC', light: '#A0A0A0CC', hcDark: '#A0A0A0CC', hcLight: '#A0A0A0CC' }, nls.localize('overviewRulerWordHighlightForeground', 'Overview ruler marker color for symbol highlights. The color must not be opaque so as not to hide underlying decorations.'), true); -const overviewRulerWordHighlightStrongForeground = registerColor('editorOverviewRuler.wordHighlightStrongForeground', { dark: '#C0A0C0CC', light: '#C0A0C0CC', hcDark: '#C0A0C0CC', hcLight: '#C0A0C0CC' }, nls.localize('overviewRulerWordHighlightStrongForeground', 'Overview ruler marker color for write-access symbol highlights. The color must not be opaque so as not to hide underlying decorations.'), true); -const overviewRulerWordHighlightTextForeground = registerColor('editorOverviewRuler.wordHighlightTextForeground', { dark: overviewRulerSelectionHighlightForeground, light: overviewRulerSelectionHighlightForeground, hcDark: overviewRulerSelectionHighlightForeground, hcLight: overviewRulerSelectionHighlightForeground }, nls.localize('overviewRulerWordHighlightTextForeground', 'Overview ruler marker color of a textual occurrence for a symbol. The color must not be opaque so as not to hide underlying decorations.'), true); +registerColor('editor.wordHighlightTextBorder', wordHighlightBorder, nls.localize('wordHighlightTextBorder', "Border color of a textual occurrence for a symbol.")); +const overviewRulerWordHighlightForeground = registerColor('editorOverviewRuler.wordHighlightForeground', '#A0A0A0CC', nls.localize('overviewRulerWordHighlightForeground', 'Overview ruler marker color for symbol highlights. The color must not be opaque so as not to hide underlying decorations.'), true); +const overviewRulerWordHighlightStrongForeground = registerColor('editorOverviewRuler.wordHighlightStrongForeground', '#C0A0C0CC', nls.localize('overviewRulerWordHighlightStrongForeground', 'Overview ruler marker color for write-access symbol highlights. The color must not be opaque so as not to hide underlying decorations.'), true); +const overviewRulerWordHighlightTextForeground = registerColor('editorOverviewRuler.wordHighlightTextForeground', overviewRulerSelectionHighlightForeground, nls.localize('overviewRulerWordHighlightTextForeground', 'Overview ruler marker color of a textual occurrence for a symbol. The color must not be opaque so as not to hide underlying decorations.'), true); const _WRITE_OPTIONS = ModelDecorationOptions.register({ description: 'word-highlight-strong', diff --git a/src/vs/platform/actionWidget/browser/actionWidget.ts b/src/vs/platform/actionWidget/browser/actionWidget.ts index e7b26382eae..20a030b67ab 100644 --- a/src/vs/platform/actionWidget/browser/actionWidget.ts +++ b/src/vs/platform/actionWidget/browser/actionWidget.ts @@ -21,7 +21,7 @@ import { inputActiveOptionBackground, registerColor } from 'vs/platform/theme/co registerColor( 'actionBar.toggledBackground', - { dark: inputActiveOptionBackground, light: inputActiveOptionBackground, hcDark: inputActiveOptionBackground, hcLight: inputActiveOptionBackground, }, + inputActiveOptionBackground, localize('actionBar.toggledBackground', 'Background color for toggled action items in action bar.') ); diff --git a/src/vs/platform/theme/common/colorUtils.ts b/src/vs/platform/theme/common/colorUtils.ts index 0f21708a437..14ceea8847b 100644 --- a/src/vs/platform/theme/common/colorUtils.ts +++ b/src/vs/platform/theme/common/colorUtils.ts @@ -20,7 +20,7 @@ export type ColorIdentifier = string; export interface ColorContribution { readonly id: ColorIdentifier; readonly description: string; - readonly defaults: ColorDefaults | null; + readonly defaults: ColorDefaults | ColorValue | null; readonly needsTransparency: boolean; readonly deprecationMessage: string | undefined; } @@ -62,10 +62,6 @@ export type ColorTransform = | { op: ColorTransformType.LessProminent; value: ColorValue; background: ColorValue; factor: number; transparency: number } | { op: ColorTransformType.IfDefinedThenElse; if: ColorIdentifier; then: ColorValue; else: ColorValue }; -export function isColorTransform(value: unknown): value is ColorTransform { - return typeof value === 'object' && !!value && 'op' in value && typeof value.op === 'number'; -} - export interface ColorDefaults { light: ColorValue | null; dark: ColorValue | null; @@ -73,16 +69,15 @@ export interface ColorDefaults { hcLight: ColorValue | null; } +export function isColorDefaults(value: unknown): value is ColorDefaults { + return value !== null && typeof value === 'object' && 'light' in value && 'dark' in value; +} /** * A Color Value is either a color literal, a reference to an other color or a derived color */ export type ColorValue = Color | string | ColorIdentifier | ColorTransform; -export function isColorValue(value: unknown): value is ColorValue { - return typeof value === 'string' || value instanceof Color || isColorTransform(value); -} - // color registry export const Extensions = { ColorContribution: 'base.contributions.colors' @@ -161,7 +156,7 @@ class ColorRegistry implements IColorRegistry { this._onDidChangeSchema.fire(); } - public registerColor(id: string, defaults: ColorDefaults | null, description: string, needsTransparency = false, deprecationMessage?: string): ColorIdentifier { + public registerColor(id: string, defaults: ColorDefaults | ColorValue | null, description: string, needsTransparency = false, deprecationMessage?: string): ColorIdentifier { const colorContribution: ColorContribution = { id, description, defaults, needsTransparency, deprecationMessage }; this.colorsById[id] = colorContribution; const propertySchema: IJSONSchemaWithSnippets = { type: 'string', description, format: 'color-hex', defaultSnippets: [{ body: '${1:#ff0000}' }] }; @@ -203,8 +198,8 @@ class ColorRegistry implements IColorRegistry { public resolveDefaultColor(id: ColorIdentifier, theme: IColorTheme): Color | undefined { const colorDesc = this.colorsById[id]; - if (colorDesc && colorDesc.defaults) { - const colorValue = colorDesc.defaults[theme.type]; + if (colorDesc?.defaults) { + const colorValue = isColorDefaults(colorDesc.defaults) ? colorDesc.defaults[theme.type] : colorDesc.defaults; return resolveColorValue(colorValue, theme); } return undefined; @@ -238,14 +233,6 @@ platform.Registry.add(Extensions.ColorContribution, colorRegistry); export function registerColor(id: string, defaults: ColorDefaults | ColorValue | null, description: string, needsTransparency?: boolean, deprecationMessage?: string): ColorIdentifier { - if (isColorValue(defaults)) { - defaults = { - dark: defaults, - light: defaults, - hcDark: defaults, - hcLight: defaults - } satisfies ColorDefaults; - } return colorRegistry.registerColor(id, defaults, description, needsTransparency, deprecationMessage); } diff --git a/src/vs/platform/theme/common/colors/baseColors.ts b/src/vs/platform/theme/common/colors/baseColors.ts index 1d19b3adc1f..baf6b86f27f 100644 --- a/src/vs/platform/theme/common/colors/baseColors.ts +++ b/src/vs/platform/theme/common/colors/baseColors.ts @@ -43,7 +43,7 @@ export const activeContrastBorder = registerColor('contrastActiveBorder', nls.localize('activeContrastBorder', "An extra border around active elements to separate them from others for greater contrast.")); export const selectionBackground = registerColor('selection.background', - { light: null, dark: null, hcDark: null, hcLight: null }, + null, nls.localize('selectionBackground', "The background color of text selections in the workbench (e.g. for input fields or text areas). Note that this does not apply to selections within the editor.")); diff --git a/src/vs/platform/theme/common/colors/chartsColors.ts b/src/vs/platform/theme/common/colors/chartsColors.ts index eb63b602234..a35e296d2ad 100644 --- a/src/vs/platform/theme/common/colors/chartsColors.ts +++ b/src/vs/platform/theme/common/colors/chartsColors.ts @@ -12,27 +12,27 @@ import { minimapFindMatch } from 'vs/platform/theme/common/colors/minimapColors' export const chartsForeground = registerColor('charts.foreground', - { dark: foreground, light: foreground, hcDark: foreground, hcLight: foreground }, + foreground, nls.localize('chartsForeground', "The foreground color used in charts.")); export const chartsLines = registerColor('charts.lines', - { dark: transparent(foreground, .5), light: transparent(foreground, .5), hcDark: transparent(foreground, .5), hcLight: transparent(foreground, .5) }, + transparent(foreground, .5), nls.localize('chartsLines', "The color used for horizontal lines in charts.")); export const chartsRed = registerColor('charts.red', - { dark: editorErrorForeground, light: editorErrorForeground, hcDark: editorErrorForeground, hcLight: editorErrorForeground }, + editorErrorForeground, nls.localize('chartsRed', "The red color used in chart visualizations.")); export const chartsBlue = registerColor('charts.blue', - { dark: editorInfoForeground, light: editorInfoForeground, hcDark: editorInfoForeground, hcLight: editorInfoForeground }, + editorInfoForeground, nls.localize('chartsBlue', "The blue color used in chart visualizations.")); export const chartsYellow = registerColor('charts.yellow', - { dark: editorWarningForeground, light: editorWarningForeground, hcDark: editorWarningForeground, hcLight: editorWarningForeground }, + editorWarningForeground, nls.localize('chartsYellow', "The yellow color used in chart visualizations.")); export const chartsOrange = registerColor('charts.orange', - { dark: minimapFindMatch, light: minimapFindMatch, hcDark: minimapFindMatch, hcLight: minimapFindMatch }, + minimapFindMatch, nls.localize('chartsOrange', "The orange color used in chart visualizations.")); export const chartsGreen = registerColor('charts.green', diff --git a/src/vs/platform/theme/common/colors/editorColors.ts b/src/vs/platform/theme/common/colors/editorColors.ts index a57b85e2c29..cac6dea162c 100644 --- a/src/vs/platform/theme/common/colors/editorColors.ts +++ b/src/vs/platform/theme/common/colors/editorColors.ts @@ -26,7 +26,7 @@ export const editorForeground = registerColor('editor.foreground', export const editorStickyScrollBackground = registerColor('editorStickyScroll.background', - { light: editorBackground, dark: editorBackground, hcDark: editorBackground, hcLight: editorBackground }, + editorBackground, nls.localize('editorStickyScrollBackground', "Background color of sticky scroll in the editor")); export const editorStickyScrollHoverBackground = registerColor('editorStickyScrollHover.background', @@ -38,7 +38,7 @@ export const editorStickyScrollBorder = registerColor('editorStickyScroll.border nls.localize('editorStickyScrollBorder', "Border color of sticky scroll in the editor")); export const editorStickyScrollShadow = registerColor('editorStickyScroll.shadow', - { dark: scrollbarShadow, light: scrollbarShadow, hcDark: scrollbarShadow, hcLight: scrollbarShadow }, + scrollbarShadow, nls.localize('editorStickyScrollShadow', " Shadow color of sticky scroll in the editor")); @@ -47,7 +47,7 @@ export const editorWidgetBackground = registerColor('editorWidget.background', nls.localize('editorWidgetBackground', 'Background color of editor widgets, such as find/replace.')); export const editorWidgetForeground = registerColor('editorWidget.foreground', - { dark: foreground, light: foreground, hcDark: foreground, hcLight: foreground }, + foreground, nls.localize('editorWidgetForeground', 'Foreground color of editor widgets, such as find/replace.')); export const editorWidgetBorder = registerColor('editorWidget.border', @@ -55,12 +55,12 @@ export const editorWidgetBorder = registerColor('editorWidget.border', nls.localize('editorWidgetBorder', 'Border color of editor widgets. The color is only used if the widget chooses to have a border and if the color is not overridden by a widget.')); export const editorWidgetResizeBorder = registerColor('editorWidget.resizeBorder', - { light: null, dark: null, hcDark: null, hcLight: null }, + null, nls.localize('editorWidgetResizeBorder', "Border color of the resize bar of editor widgets. The color is only used if the widget chooses to have a resize border and if the color is not overridden by a widget.")); export const editorErrorBackground = registerColor('editorError.background', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('editorError.background', 'Background color of error text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorErrorForeground = registerColor('editorError.foreground', @@ -73,7 +73,7 @@ export const editorErrorBorder = registerColor('editorError.border', export const editorWarningBackground = registerColor('editorWarning.background', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('editorWarning.background', 'Background color of warning text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorWarningForeground = registerColor('editorWarning.foreground', @@ -86,7 +86,7 @@ export const editorWarningBorder = registerColor('editorWarning.border', export const editorInfoBackground = registerColor('editorInfo.background', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('editorInfo.background', 'Background color of info text in the editor. The color must not be opaque so as not to hide underlying decorations.'), true); export const editorInfoForeground = registerColor('editorInfo.foreground', @@ -142,7 +142,7 @@ export const editorFindMatch = registerColor('editor.findMatchBackground', nls.localize('editorFindMatch', "Color of the current search match.")); export const editorFindMatchForeground = registerColor('editor.findMatchForeground', - { light: null, dark: null, hcDark: null, hcLight: null }, + null, nls.localize('editorFindMatchForeground', "Text color of the current search match.")); export const editorFindMatchHighlight = registerColor('editor.findMatchHighlightBackground', @@ -150,7 +150,7 @@ export const editorFindMatchHighlight = registerColor('editor.findMatchHighlight nls.localize('findMatchHighlight', "Color of the other search matches. The color must not be opaque so as not to hide underlying decorations."), true); export const editorFindMatchHighlightForeground = registerColor('editor.findMatchHighlightForeground', - { light: null, dark: null, hcDark: null, hcLight: null }, + null, nls.localize('findMatchHighlightForeground', "Foreground color of the other search matches."), true); export const editorFindRangeHighlight = registerColor('editor.findRangeHighlightBackground', @@ -177,15 +177,15 @@ export const editorHoverHighlight = registerColor('editor.hoverHighlightBackgrou 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, hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, + editorWidgetBackground, nls.localize('hoverBackground', 'Background color of the editor hover.')); export const editorHoverForeground = registerColor('editorHoverWidget.foreground', - { light: editorWidgetForeground, dark: editorWidgetForeground, hcDark: editorWidgetForeground, hcLight: editorWidgetForeground }, + editorWidgetForeground, nls.localize('hoverForeground', 'Foreground color of the editor hover.')); export const editorHoverBorder = registerColor('editorHoverWidget.border', - { light: editorWidgetBorder, dark: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, + editorWidgetBorder, nls.localize('hoverBorder', 'Border color of the editor hover.')); export const editorHoverStatusBarBackground = registerColor('editorHoverWidget.statusBarBackground', @@ -204,19 +204,19 @@ export const editorInlayHintBackground = registerColor('editorInlayHint.backgrou nls.localize('editorInlayHintBackground', 'Background color of inline hints')); export const editorInlayHintTypeForeground = registerColor('editorInlayHint.typeForeground', - { dark: editorInlayHintForeground, light: editorInlayHintForeground, hcDark: editorInlayHintForeground, hcLight: editorInlayHintForeground }, + editorInlayHintForeground, nls.localize('editorInlayHintForegroundTypes', 'Foreground color of inline hints for types')); export const editorInlayHintTypeBackground = registerColor('editorInlayHint.typeBackground', - { dark: editorInlayHintBackground, light: editorInlayHintBackground, hcDark: editorInlayHintBackground, hcLight: editorInlayHintBackground }, + editorInlayHintBackground, nls.localize('editorInlayHintBackgroundTypes', 'Background color of inline hints for types')); export const editorInlayHintParameterForeground = registerColor('editorInlayHint.parameterForeground', - { dark: editorInlayHintForeground, light: editorInlayHintForeground, hcDark: editorInlayHintForeground, hcLight: editorInlayHintForeground }, + editorInlayHintForeground, nls.localize('editorInlayHintForegroundParameter', 'Foreground color of inline hints for parameters')); export const editorInlayHintParameterBackground = registerColor('editorInlayHint.parameterBackground', - { dark: editorInlayHintBackground, light: editorInlayHintBackground, hcDark: editorInlayHintBackground, hcLight: editorInlayHintBackground }, + editorInlayHintBackground, nls.localize('editorInlayHintBackgroundParameter', 'Background color of inline hints for parameters')); @@ -231,7 +231,7 @@ export const editorLightBulbAutoFixForeground = registerColor('editorLightBulbAu nls.localize('editorLightBulbAutoFixForeground', "The color used for the lightbulb auto fix actions icon.")); export const editorLightBulbAiForeground = registerColor('editorLightBulbAi.foreground', - { dark: editorLightBulbForeground, light: editorLightBulbForeground, hcDark: editorLightBulbForeground, hcLight: editorLightBulbForeground }, + editorLightBulbForeground, nls.localize('editorLightBulbAiForeground', "The color used for the lightbulb AI icon.")); @@ -242,11 +242,11 @@ export const snippetTabstopHighlightBackground = registerColor('editor.snippetTa nls.localize('snippetTabstopHighlightBackground', "Highlight background color of a snippet tabstop.")); export const snippetTabstopHighlightBorder = registerColor('editor.snippetTabstopHighlightBorder', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('snippetTabstopHighlightBorder', "Highlight border color of a snippet tabstop.")); export const snippetFinalTabstopHighlightBackground = registerColor('editor.snippetFinalTabstopHighlightBackground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('snippetFinalTabstopHighlightBackground', "Highlight background color of the final tabstop of a snippet.")); export const snippetFinalTabstopHighlightBorder = registerColor('editor.snippetFinalTabstopHighlightBorder', @@ -278,20 +278,20 @@ export const diffRemovedLine = registerColor('diffEditor.removedLineBackground', export const diffInsertedLineGutter = registerColor('diffEditorGutter.insertedLineBackground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('diffEditorInsertedLineGutter', 'Background color for the margin where lines got inserted.')); export const diffRemovedLineGutter = registerColor('diffEditorGutter.removedLineBackground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('diffEditorRemovedLineGutter', 'Background color for the margin where lines got removed.')); export const diffOverviewRulerInserted = registerColor('diffEditorOverview.insertedForeground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('diffEditorOverviewInserted', 'Diff overview ruler foreground for inserted content.')); export const diffOverviewRulerRemoved = registerColor('diffEditorOverview.removedForeground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('diffEditorOverviewRemoved', 'Diff overview ruler foreground for removed content.')); @@ -314,11 +314,11 @@ export const diffDiagonalFill = registerColor('diffEditor.diagonalFill', export const diffUnchangedRegionBackground = registerColor('diffEditor.unchangedRegionBackground', - { dark: 'sideBar.background', light: 'sideBar.background', hcDark: 'sideBar.background', hcLight: 'sideBar.background' }, + 'sideBar.background', nls.localize('diffEditor.unchangedRegionBackground', "The background color of unchanged blocks in the diff editor.")); export const diffUnchangedRegionForeground = registerColor('diffEditor.unchangedRegionForeground', - { dark: 'foreground', light: 'foreground', hcDark: 'foreground', hcLight: 'foreground' }, + 'foreground', nls.localize('diffEditor.unchangedRegionForeground', "The foreground color of unchanged blocks in the diff editor.")); export const diffUnchangedTextBackground = registerColor('diffEditor.unchangedCodeBackground', @@ -355,11 +355,11 @@ export const toolbarActiveBackground = registerColor('toolbar.activeBackground', // ----- breadcumbs export const breadcrumbsForeground = registerColor('breadcrumb.foreground', - { light: transparent(foreground, 0.8), dark: transparent(foreground, 0.8), hcDark: transparent(foreground, 0.8), hcLight: transparent(foreground, 0.8) }, + transparent(foreground, 0.8), nls.localize('breadcrumbsFocusForeground', "Color of focused breadcrumb items.")); export const breadcrumbsBackground = registerColor('breadcrumb.background', - { light: editorBackground, dark: editorBackground, hcDark: editorBackground, hcLight: editorBackground }, + editorBackground, nls.localize('breadcrumbsBackground', "Background color of breadcrumb items.")); export const breadcrumbsFocusForeground = registerColor('breadcrumb.focusForeground', @@ -371,7 +371,7 @@ export const breadcrumbsActiveSelectionForeground = registerColor('breadcrumb.ac nls.localize('breadcrumbsSelectedForeground', "Color of selected breadcrumb items.")); export const breadcrumbsPickerBackground = registerColor('breadcrumbPicker.background', - { light: editorWidgetBackground, dark: editorWidgetBackground, hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, + editorWidgetBackground, nls.localize('breadcrumbsSelectedBackground', "Background color of breadcrumb item picker.")); @@ -389,7 +389,7 @@ export const mergeCurrentHeaderBackground = registerColor('merge.currentHeaderBa nls.localize('mergeCurrentHeaderBackground', 'Current header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); export const mergeCurrentContentBackground = registerColor('merge.currentContentBackground', - { dark: transparent(mergeCurrentHeaderBackground, contentTransparency), light: transparent(mergeCurrentHeaderBackground, contentTransparency), hcDark: transparent(mergeCurrentHeaderBackground, contentTransparency), hcLight: transparent(mergeCurrentHeaderBackground, contentTransparency) }, + transparent(mergeCurrentHeaderBackground, contentTransparency), nls.localize('mergeCurrentContentBackground', 'Current content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); export const mergeIncomingHeaderBackground = registerColor('merge.incomingHeaderBackground', @@ -397,7 +397,7 @@ export const mergeIncomingHeaderBackground = registerColor('merge.incomingHeader nls.localize('mergeIncomingHeaderBackground', 'Incoming header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); export const mergeIncomingContentBackground = registerColor('merge.incomingContentBackground', - { dark: transparent(mergeIncomingHeaderBackground, contentTransparency), light: transparent(mergeIncomingHeaderBackground, contentTransparency), hcDark: transparent(mergeIncomingHeaderBackground, contentTransparency), hcLight: transparent(mergeIncomingHeaderBackground, contentTransparency) }, + transparent(mergeIncomingHeaderBackground, contentTransparency), nls.localize('mergeIncomingContentBackground', 'Incoming content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); export const mergeCommonHeaderBackground = registerColor('merge.commonHeaderBackground', @@ -405,7 +405,7 @@ export const mergeCommonHeaderBackground = registerColor('merge.commonHeaderBack nls.localize('mergeCommonHeaderBackground', 'Common ancestor header background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); export const mergeCommonContentBackground = registerColor('merge.commonContentBackground', - { dark: transparent(mergeCommonHeaderBackground, contentTransparency), light: transparent(mergeCommonHeaderBackground, contentTransparency), hcDark: transparent(mergeCommonHeaderBackground, contentTransparency), hcLight: transparent(mergeCommonHeaderBackground, contentTransparency) }, + transparent(mergeCommonHeaderBackground, contentTransparency), nls.localize('mergeCommonContentBackground', 'Common ancestor content background in inline merge-conflicts. The color must not be opaque so as not to hide underlying decorations.'), true); export const mergeBorder = registerColor('merge.border', @@ -430,20 +430,20 @@ export const overviewRulerFindMatchForeground = registerColor('editorOverviewRul nls.localize('overviewRulerFindMatchForeground', 'Overview ruler marker color for find matches. The color must not be opaque so as not to hide underlying decorations.'), true); export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', - { dark: '#A0A0A0CC', light: '#A0A0A0CC', hcDark: '#A0A0A0CC', hcLight: '#A0A0A0CC' }, + '#A0A0A0CC', nls.localize('overviewRulerSelectionHighlightForeground', 'Overview ruler marker color for selection highlights. The color must not be opaque so as not to hide underlying decorations.'), true); // ----- problems export const problemsErrorIconForeground = registerColor('problemsErrorIcon.foreground', - { dark: editorErrorForeground, light: editorErrorForeground, hcDark: editorErrorForeground, hcLight: editorErrorForeground }, + editorErrorForeground, nls.localize('problemsErrorIconForeground', "The color used for the problems error icon.")); export const problemsWarningIconForeground = registerColor('problemsWarningIcon.foreground', - { dark: editorWarningForeground, light: editorWarningForeground, hcDark: editorWarningForeground, hcLight: editorWarningForeground }, + editorWarningForeground, nls.localize('problemsWarningIconForeground', "The color used for the problems warning icon.")); export const problemsInfoIconForeground = registerColor('problemsInfoIcon.foreground', - { dark: editorInfoForeground, light: editorInfoForeground, hcDark: editorInfoForeground, hcLight: editorInfoForeground }, + editorInfoForeground, nls.localize('problemsInfoIconForeground', "The color used for the problems info icon.")); diff --git a/src/vs/platform/theme/common/colors/inputColors.ts b/src/vs/platform/theme/common/colors/inputColors.ts index dc38222d402..c79c1d2840b 100644 --- a/src/vs/platform/theme/common/colors/inputColors.ts +++ b/src/vs/platform/theme/common/colors/inputColors.ts @@ -21,7 +21,7 @@ export const inputBackground = registerColor('input.background', nls.localize('inputBoxBackground', "Input box background.")); export const inputForeground = registerColor('input.foreground', - { dark: foreground, light: foreground, hcDark: foreground, hcLight: foreground }, + foreground, nls.localize('inputBoxForeground', "Input box foreground.")); export const inputBorder = registerColor('input.border', @@ -110,11 +110,11 @@ export const selectBorder = registerColor('dropdown.border', // ------ button export const buttonForeground = registerColor('button.foreground', - { dark: Color.white, light: Color.white, hcDark: Color.white, hcLight: Color.white }, + Color.white, nls.localize('buttonForeground', "Button foreground color.")); export const buttonSeparator = registerColor('button.separator', - { dark: transparent(buttonForeground, .4), light: transparent(buttonForeground, .4), hcDark: transparent(buttonForeground, .4), hcLight: transparent(buttonForeground, .4) }, + transparent(buttonForeground, .4), nls.localize('buttonSeparator', "Button separator color.")); export const buttonBackground = registerColor('button.background', @@ -126,7 +126,7 @@ export const buttonHoverBackground = registerColor('button.hoverBackground', nls.localize('buttonHoverBackground', "Button background color when hovering.")); export const buttonBorder = registerColor('button.border', - { dark: contrastBorder, light: contrastBorder, hcDark: contrastBorder, hcLight: contrastBorder }, + contrastBorder, nls.localize('buttonBorder', "Button border color.")); export const buttonSecondaryForeground = registerColor('button.secondaryForeground', @@ -145,23 +145,23 @@ export const buttonSecondaryHoverBackground = registerColor('button.secondaryHov // ------ checkbox export const checkboxBackground = registerColor('checkbox.background', - { dark: selectBackground, light: selectBackground, hcDark: selectBackground, hcLight: selectBackground }, + selectBackground, nls.localize('checkbox.background', "Background color of checkbox widget.")); export const checkboxSelectBackground = registerColor('checkbox.selectBackground', - { dark: editorWidgetBackground, light: editorWidgetBackground, hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, + editorWidgetBackground, nls.localize('checkbox.select.background', "Background color of checkbox widget when the element it's in is selected.")); export const checkboxForeground = registerColor('checkbox.foreground', - { dark: selectForeground, light: selectForeground, hcDark: selectForeground, hcLight: selectForeground }, + selectForeground, nls.localize('checkbox.foreground', "Foreground color of checkbox widget.")); export const checkboxBorder = registerColor('checkbox.border', - { dark: selectBorder, light: selectBorder, hcDark: selectBorder, hcLight: selectBorder }, + selectBorder, nls.localize('checkbox.border', "Border color of checkbox widget.")); export const checkboxSelectBorder = registerColor('checkbox.selectBorder', - { dark: iconForeground, light: iconForeground, hcDark: iconForeground, hcLight: iconForeground }, + iconForeground, nls.localize('checkbox.select.border', "Border color of checkbox widget when the element it's in is selected.")); diff --git a/src/vs/platform/theme/common/colors/listColors.ts b/src/vs/platform/theme/common/colors/listColors.ts index b6f51e3696b..dd5c405199c 100644 --- a/src/vs/platform/theme/common/colors/listColors.ts +++ b/src/vs/platform/theme/common/colors/listColors.ts @@ -15,11 +15,11 @@ import { editorWidgetBackground, editorFindMatchHighlightBorder, editorFindMatch export const listFocusBackground = registerColor('list.focusBackground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listFocusBackground', "List/Tree background color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusForeground = registerColor('list.focusForeground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listFocusForeground', "List/Tree foreground color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusOutline = registerColor('list.focusOutline', @@ -27,7 +27,7 @@ export const listFocusOutline = registerColor('list.focusOutline', nls.localize('listFocusOutline', "List/Tree outline color for the focused item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listFocusAndSelectionOutline = registerColor('list.focusAndSelectionOutline', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listFocusAndSelectionOutline', "List/Tree outline color for the focused item when the list/tree is active and selected. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionBackground = registerColor('list.activeSelectionBackground', @@ -39,7 +39,7 @@ export const listActiveSelectionForeground = registerColor('list.activeSelection nls.localize('listActiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listActiveSelectionIconForeground = registerColor('list.activeSelectionIconForeground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listActiveSelectionIconForeground', "List/Tree icon foreground color for the selected item when the list/tree is active. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionBackground = registerColor('list.inactiveSelectionBackground', @@ -47,19 +47,19 @@ export const listInactiveSelectionBackground = registerColor('list.inactiveSelec nls.localize('listInactiveSelectionBackground', "List/Tree background color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionForeground = registerColor('list.inactiveSelectionForeground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listInactiveSelectionForeground', "List/Tree foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveSelectionIconForeground = registerColor('list.inactiveSelectionIconForeground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listInactiveSelectionIconForeground', "List/Tree icon foreground color for the selected item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveFocusBackground = registerColor('list.inactiveFocusBackground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listInactiveFocusBackground', "List/Tree background color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listInactiveFocusOutline = registerColor('list.inactiveFocusOutline', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listInactiveFocusOutline', "List/Tree outline color for the focused item when the list/tree is inactive. An active list/tree has keyboard focus, an inactive does not.")); export const listHoverBackground = registerColor('list.hoverBackground', @@ -67,7 +67,7 @@ export const listHoverBackground = registerColor('list.hoverBackground', nls.localize('listHoverBackground', "List/Tree background when hovering over items using the mouse.")); export const listHoverForeground = registerColor('list.hoverForeground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('listHoverForeground', "List/Tree foreground when hovering over items using the mouse.")); export const listDropOverBackground = registerColor('list.dropBackground', @@ -109,7 +109,7 @@ export const listFilterWidgetNoMatchesOutline = registerColor('listFilterWidget. nls.localize('listFilterWidgetNoMatchesOutline', 'Outline color of the type filter widget in lists and trees, when there are no matches.')); export const listFilterWidgetShadow = registerColor('listFilterWidget.shadow', - { dark: widgetShadow, light: widgetShadow, hcDark: widgetShadow, hcLight: widgetShadow }, + widgetShadow, nls.localize('listFilterWidgetShadow', 'Shadow color of the type filter widget in lists and trees.')); export const listFilterMatchHighlight = registerColor('list.filterMatchBackground', @@ -132,7 +132,7 @@ export const treeIndentGuidesStroke = registerColor('tree.indentGuidesStroke', nls.localize('treeIndentGuidesStroke', "Tree stroke color for the indentation guides.")); export const treeInactiveIndentGuidesStroke = registerColor('tree.inactiveIndentGuidesStroke', - { dark: transparent(treeIndentGuidesStroke, 0.4), light: transparent(treeIndentGuidesStroke, 0.4), hcDark: transparent(treeIndentGuidesStroke, 0.4), hcLight: transparent(treeIndentGuidesStroke, 0.4) }, + transparent(treeIndentGuidesStroke, 0.4), nls.localize('treeInactiveIndentGuidesStroke', "Tree stroke color for the indentation guides that are not active.")); diff --git a/src/vs/platform/theme/common/colors/menuColors.ts b/src/vs/platform/theme/common/colors/menuColors.ts index 6fa9a0ec326..05bf5491952 100644 --- a/src/vs/platform/theme/common/colors/menuColors.ts +++ b/src/vs/platform/theme/common/colors/menuColors.ts @@ -19,19 +19,19 @@ export const menuBorder = registerColor('menu.border', nls.localize('menuBorder', "Border color of menus.")); export const menuForeground = registerColor('menu.foreground', - { dark: selectForeground, light: selectForeground, hcDark: selectForeground, hcLight: selectForeground }, + selectForeground, nls.localize('menuForeground', "Foreground color of menu items.")); export const menuBackground = registerColor('menu.background', - { dark: selectBackground, light: selectBackground, hcDark: selectBackground, hcLight: selectBackground }, + selectBackground, nls.localize('menuBackground', "Background color of menu items.")); export const menuSelectionForeground = registerColor('menu.selectionForeground', - { dark: listActiveSelectionForeground, light: listActiveSelectionForeground, hcDark: listActiveSelectionForeground, hcLight: listActiveSelectionForeground }, + listActiveSelectionForeground, nls.localize('menuSelectionForeground', "Foreground color of the selected menu item in menus.")); export const menuSelectionBackground = registerColor('menu.selectionBackground', - { dark: listActiveSelectionBackground, light: listActiveSelectionBackground, hcDark: listActiveSelectionBackground, hcLight: listActiveSelectionBackground }, + listActiveSelectionBackground, nls.localize('menuSelectionBackground', "Background color of the selected menu item in menus.")); export const menuSelectionBorder = registerColor('menu.selectionBorder', diff --git a/src/vs/platform/theme/common/colors/minimapColors.ts b/src/vs/platform/theme/common/colors/minimapColors.ts index 0b051994d09..ade38578c28 100644 --- a/src/vs/platform/theme/common/colors/minimapColors.ts +++ b/src/vs/platform/theme/common/colors/minimapColors.ts @@ -39,21 +39,21 @@ export const minimapError = registerColor('minimap.errorHighlight', nls.localize('minimapError', 'Minimap marker color for errors.')); export const minimapBackground = registerColor('minimap.background', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, nls.localize('minimapBackground', "Minimap background color.")); export const minimapForegroundOpacity = registerColor('minimap.foregroundOpacity', - { dark: Color.fromHex('#000f'), light: Color.fromHex('#000f'), hcDark: Color.fromHex('#000f'), hcLight: Color.fromHex('#000f') }, + Color.fromHex('#000f'), nls.localize('minimapForegroundOpacity', 'Opacity of foreground elements rendered in the minimap. For example, "#000000c0" will render the elements with 75% opacity.')); export const minimapSliderBackground = registerColor('minimapSlider.background', - { light: transparent(scrollbarSliderBackground, 0.5), dark: transparent(scrollbarSliderBackground, 0.5), hcDark: transparent(scrollbarSliderBackground, 0.5), hcLight: transparent(scrollbarSliderBackground, 0.5) }, + transparent(scrollbarSliderBackground, 0.5), nls.localize('minimapSliderBackground', "Minimap slider background color.")); export const minimapSliderHoverBackground = registerColor('minimapSlider.hoverBackground', - { light: transparent(scrollbarSliderHoverBackground, 0.5), dark: transparent(scrollbarSliderHoverBackground, 0.5), hcDark: transparent(scrollbarSliderHoverBackground, 0.5), hcLight: transparent(scrollbarSliderHoverBackground, 0.5) }, + transparent(scrollbarSliderHoverBackground, 0.5), nls.localize('minimapSliderHoverBackground', "Minimap slider background color when hovering.")); export const minimapSliderActiveBackground = registerColor('minimapSlider.activeBackground', - { light: transparent(scrollbarSliderActiveBackground, 0.5), dark: transparent(scrollbarSliderActiveBackground, 0.5), hcDark: transparent(scrollbarSliderActiveBackground, 0.5), hcLight: transparent(scrollbarSliderActiveBackground, 0.5) }, + transparent(scrollbarSliderActiveBackground, 0.5), nls.localize('minimapSliderActiveBackground', "Minimap slider background color when clicked on.")); diff --git a/src/vs/platform/theme/common/colors/miscColors.ts b/src/vs/platform/theme/common/colors/miscColors.ts index 5a2ea49b702..42a00e23e6a 100644 --- a/src/vs/platform/theme/common/colors/miscColors.ts +++ b/src/vs/platform/theme/common/colors/miscColors.ts @@ -16,7 +16,7 @@ import { contrastBorder, focusBorder } from 'vs/platform/theme/common/colors/bas // ----- sash export const sashHoverBorder = registerColor('sash.hoverBorder', - { dark: focusBorder, light: focusBorder, hcDark: focusBorder, hcLight: focusBorder }, + focusBorder, nls.localize('sashActiveBorder', "Border color of active sashes.")); diff --git a/src/vs/platform/theme/common/colors/quickpickColors.ts b/src/vs/platform/theme/common/colors/quickpickColors.ts index 7f8fc271a6e..3b109a21872 100644 --- a/src/vs/platform/theme/common/colors/quickpickColors.ts +++ b/src/vs/platform/theme/common/colors/quickpickColors.ts @@ -15,11 +15,11 @@ import { listActiveSelectionBackground, listActiveSelectionForeground, listActiv export const quickInputBackground = registerColor('quickInput.background', - { dark: editorWidgetBackground, light: editorWidgetBackground, hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, + editorWidgetBackground, nls.localize('pickerBackground', "Quick picker background color. The quick picker widget is the container for pickers like the command palette.")); export const quickInputForeground = registerColor('quickInput.foreground', - { dark: editorWidgetForeground, light: editorWidgetForeground, hcDark: editorWidgetForeground, hcLight: editorWidgetForeground }, + editorWidgetForeground, nls.localize('pickerForeground', "Quick picker foreground color. The quick picker widget is the container for pickers like the command palette.")); export const quickInputTitleBackground = registerColor('quickInputTitle.background', @@ -35,15 +35,15 @@ export const pickerGroupBorder = registerColor('pickerGroup.border', nls.localize('pickerGroupBorder', "Quick picker color for grouping borders.")); export const _deprecatedQuickInputListFocusBackground = registerColor('quickInput.list.focusBackground', - { dark: null, light: null, hcDark: null, hcLight: null }, '', undefined, + null, '', undefined, nls.localize('quickInput.list.focusBackground deprecation', "Please use quickInputList.focusBackground instead")); export const quickInputListFocusForeground = registerColor('quickInputList.focusForeground', - { dark: listActiveSelectionForeground, light: listActiveSelectionForeground, hcDark: listActiveSelectionForeground, hcLight: listActiveSelectionForeground }, + listActiveSelectionForeground, nls.localize('quickInput.listFocusForeground', "Quick picker foreground color for the focused item.")); export const quickInputListFocusIconForeground = registerColor('quickInputList.focusIconForeground', - { dark: listActiveSelectionIconForeground, light: listActiveSelectionIconForeground, hcDark: listActiveSelectionIconForeground, hcLight: listActiveSelectionIconForeground }, + listActiveSelectionIconForeground, nls.localize('quickInput.listFocusIconForeground', "Quick picker icon foreground color for the focused item.")); export const quickInputListFocusBackground = registerColor('quickInputList.focusBackground', diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 3412f81c3bf..1e6aba052fd 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -28,19 +28,9 @@ export function WORKBENCH_BACKGROUND(theme: IColorTheme): Color { //#region Tab Background -export const TAB_ACTIVE_BACKGROUND = registerColor('tab.activeBackground', { - dark: editorBackground, - light: editorBackground, - hcDark: editorBackground, - hcLight: editorBackground -}, localize('tabActiveBackground', "Active tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); - -export const TAB_UNFOCUSED_ACTIVE_BACKGROUND = registerColor('tab.unfocusedActiveBackground', { - dark: TAB_ACTIVE_BACKGROUND, - light: TAB_ACTIVE_BACKGROUND, - hcDark: TAB_ACTIVE_BACKGROUND, - hcLight: TAB_ACTIVE_BACKGROUND, -}, localize('tabUnfocusedActiveBackground', "Active tab background color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +export const TAB_ACTIVE_BACKGROUND = registerColor('tab.activeBackground', editorBackground, localize('tabActiveBackground', "Active tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); + +export const TAB_UNFOCUSED_ACTIVE_BACKGROUND = registerColor('tab.unfocusedActiveBackground', TAB_ACTIVE_BACKGROUND, localize('tabUnfocusedActiveBackground', "Active tab background color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_INACTIVE_BACKGROUND = registerColor('tab.inactiveBackground', { dark: '#2D2D2D', @@ -49,12 +39,7 @@ export const TAB_INACTIVE_BACKGROUND = registerColor('tab.inactiveBackground', { hcLight: null, }, localize('tabInactiveBackground', "Inactive tab background color in an active group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); -export const TAB_UNFOCUSED_INACTIVE_BACKGROUND = registerColor('tab.unfocusedInactiveBackground', { - dark: TAB_INACTIVE_BACKGROUND, - light: TAB_INACTIVE_BACKGROUND, - hcDark: TAB_INACTIVE_BACKGROUND, - hcLight: TAB_INACTIVE_BACKGROUND -}, localize('tabUnfocusedInactiveBackground', "Inactive tab background color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +export const TAB_UNFOCUSED_INACTIVE_BACKGROUND = registerColor('tab.unfocusedInactiveBackground', TAB_INACTIVE_BACKGROUND, localize('tabUnfocusedInactiveBackground', "Inactive tab background color in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); //#endregion @@ -92,12 +77,7 @@ export const TAB_UNFOCUSED_INACTIVE_FOREGROUND = registerColor('tab.unfocusedIna //#region Tab Hover Foreground/Background -export const TAB_HOVER_BACKGROUND = registerColor('tab.hoverBackground', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('tabHoverBackground', "Tab background color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +export const TAB_HOVER_BACKGROUND = registerColor('tab.hoverBackground', null, localize('tabHoverBackground', "Tab background color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_HOVER_BACKGROUND = registerColor('tab.unfocusedHoverBackground', { dark: transparent(TAB_HOVER_BACKGROUND, 0.5), @@ -106,12 +86,7 @@ export const TAB_UNFOCUSED_HOVER_BACKGROUND = registerColor('tab.unfocusedHoverB hcLight: null }, localize('tabUnfocusedHoverBackground', "Tab background color in an unfocused group when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); -export const TAB_HOVER_FOREGROUND = registerColor('tab.hoverForeground', { - dark: null, - light: null, - hcDark: null, - hcLight: null, -}, localize('tabHoverForeground', "Tab foreground color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +export const TAB_HOVER_FOREGROUND = registerColor('tab.hoverForeground', null, localize('tabHoverForeground', "Tab foreground color when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_HOVER_FOREGROUND = registerColor('tab.unfocusedHoverForeground', { dark: transparent(TAB_HOVER_FOREGROUND, 0.5), @@ -138,12 +113,7 @@ export const TAB_LAST_PINNED_BORDER = registerColor('tab.lastPinnedBorder', { hcLight: contrastBorder }, localize('lastPinnedTabBorder', "Border to separate pinned tabs from other tabs. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); -export const TAB_ACTIVE_BORDER = registerColor('tab.activeBorder', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('tabActiveBorder', "Border on the bottom of an active tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +export const TAB_ACTIVE_BORDER = registerColor('tab.activeBorder', null, localize('tabActiveBorder', "Border on the bottom of an active tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_ACTIVE_BORDER = registerColor('tab.unfocusedActiveBorder', { dark: transparent(TAB_ACTIVE_BORDER, 0.5), @@ -166,34 +136,14 @@ export const TAB_UNFOCUSED_ACTIVE_BORDER_TOP = registerColor('tab.unfocusedActiv hcLight: '#B5200D' }, localize('tabActiveUnfocusedBorderTop', "Border to the top of an active tab in an unfocused group. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); -export const TAB_SELECTED_BORDER_TOP = registerColor('tab.selectedBorderTop', { - dark: TAB_ACTIVE_BORDER_TOP, - light: TAB_ACTIVE_BORDER_TOP, - hcDark: TAB_ACTIVE_BORDER_TOP, - hcLight: TAB_ACTIVE_BORDER_TOP -}, localize('tabSelectedBorderTop', "Border to the top of a selected tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); - -export const TAB_SELECTED_BACKGROUND = registerColor('tab.selectedBackground', { - dark: TAB_ACTIVE_BACKGROUND, - light: TAB_ACTIVE_BACKGROUND, - hcDark: TAB_ACTIVE_BACKGROUND, - hcLight: TAB_ACTIVE_BACKGROUND -}, localize('tabSelectedBackground', "Background of a selected tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); - -export const TAB_SELECTED_FOREGROUND = registerColor('tab.selectedForeground', { - dark: TAB_ACTIVE_FOREGROUND, - light: TAB_ACTIVE_FOREGROUND, - hcDark: TAB_ACTIVE_FOREGROUND, - hcLight: TAB_ACTIVE_FOREGROUND -}, localize('tabSelectedForeground', "Foreground of a selected tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +export const TAB_SELECTED_BORDER_TOP = registerColor('tab.selectedBorderTop', TAB_ACTIVE_BORDER_TOP, localize('tabSelectedBorderTop', "Border to the top of a selected tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +export const TAB_SELECTED_BACKGROUND = registerColor('tab.selectedBackground', TAB_ACTIVE_BACKGROUND, localize('tabSelectedBackground', "Background of a selected tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); -export const TAB_HOVER_BORDER = registerColor('tab.hoverBorder', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('tabHoverBorder', "Border to highlight tabs when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); +export const TAB_SELECTED_FOREGROUND = registerColor('tab.selectedForeground', TAB_ACTIVE_FOREGROUND, localize('tabSelectedForeground', "Foreground of a selected tab. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); + + +export const TAB_HOVER_BORDER = registerColor('tab.hoverBorder', null, localize('tabHoverBorder', "Border to highlight tabs when hovering. Tabs are the containers for editors in the editor area. Multiple tabs can be opened in one editor group. There can be multiple editor groups.")); export const TAB_UNFOCUSED_HOVER_BORDER = registerColor('tab.unfocusedHoverBorder', { dark: transparent(TAB_HOVER_BORDER, 0.5), @@ -249,19 +199,9 @@ export const TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER = registerColor('tab.unfocus // < --- Editors --- > -export const EDITOR_PANE_BACKGROUND = registerColor('editorPane.background', { - dark: editorBackground, - light: editorBackground, - hcDark: editorBackground, - hcLight: editorBackground -}, localize('editorPaneBackground', "Background color of the editor pane visible on the left and right side of the centered editor layout.")); +export const EDITOR_PANE_BACKGROUND = registerColor('editorPane.background', editorBackground, localize('editorPaneBackground', "Background color of the editor pane visible on the left and right side of the centered editor layout.")); -export const EDITOR_GROUP_EMPTY_BACKGROUND = registerColor('editorGroup.emptyBackground', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('editorGroupEmptyBackground', "Background color of an empty editor group. Editor groups are the containers of editors.")); +export const EDITOR_GROUP_EMPTY_BACKGROUND = registerColor('editorGroup.emptyBackground', null, localize('editorGroupEmptyBackground', "Background color of an empty editor group. Editor groups are the containers of editors.")); export const EDITOR_GROUP_FOCUSED_EMPTY_BORDER = registerColor('editorGroup.focusedEmptyBorder', { dark: null, @@ -277,19 +217,9 @@ export const EDITOR_GROUP_HEADER_TABS_BACKGROUND = registerColor('editorGroupHea hcLight: null }, localize('tabsContainerBackground', "Background color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.")); -export const EDITOR_GROUP_HEADER_TABS_BORDER = registerColor('editorGroupHeader.tabsBorder', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('tabsContainerBorder', "Border color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.")); +export const EDITOR_GROUP_HEADER_TABS_BORDER = registerColor('editorGroupHeader.tabsBorder', null, localize('tabsContainerBorder', "Border color of the editor group title header when tabs are enabled. Editor groups are the containers of editors.")); -export const EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND = registerColor('editorGroupHeader.noTabsBackground', { - dark: editorBackground, - light: editorBackground, - hcDark: editorBackground, - hcLight: editorBackground -}, localize('editorGroupHeaderBackground', "Background color of the editor group title header when (`\"workbench.editor.showTabs\": \"single\"`). Editor groups are the containers of editors.")); +export const EDITOR_GROUP_HEADER_NO_TABS_BACKGROUND = registerColor('editorGroupHeader.noTabsBackground', editorBackground, localize('editorGroupHeaderBackground', "Background color of the editor group title header when (`\"workbench.editor.showTabs\": \"single\"`). Editor groups are the containers of editors.")); export const EDITOR_GROUP_HEADER_BORDER = registerColor('editorGroupHeader.border', { dark: null, @@ -312,19 +242,9 @@ export const EDITOR_DRAG_AND_DROP_BACKGROUND = registerColor('editorGroup.dropBa hcLight: Color.fromHex('#0F4A85').transparent(0.50) }, localize('editorDragAndDropBackground', "Background color when dragging editors around. The color should have transparency so that the editor contents can still shine through.")); -export const EDITOR_DROP_INTO_PROMPT_FOREGROUND = registerColor('editorGroup.dropIntoPromptForeground', { - dark: editorWidgetForeground, - light: editorWidgetForeground, - hcDark: editorWidgetForeground, - hcLight: editorWidgetForeground -}, localize('editorDropIntoPromptForeground', "Foreground color of text shown over editors when dragging files. This text informs the user that they can hold shift to drop into the editor.")); +export const EDITOR_DROP_INTO_PROMPT_FOREGROUND = registerColor('editorGroup.dropIntoPromptForeground', editorWidgetForeground, localize('editorDropIntoPromptForeground', "Foreground color of text shown over editors when dragging files. This text informs the user that they can hold shift to drop into the editor.")); -export const EDITOR_DROP_INTO_PROMPT_BACKGROUND = registerColor('editorGroup.dropIntoPromptBackground', { - dark: editorWidgetBackground, - light: editorWidgetBackground, - hcDark: editorWidgetBackground, - hcLight: editorWidgetBackground -}, localize('editorDropIntoPromptBackground', "Background color of text shown over editors when dragging files. This text informs the user that they can hold shift to drop into the editor.")); +export const EDITOR_DROP_INTO_PROMPT_BACKGROUND = registerColor('editorGroup.dropIntoPromptBackground', editorWidgetBackground, localize('editorDropIntoPromptBackground', "Background color of text shown over editors when dragging files. This text informs the user that they can hold shift to drop into the editor.")); export const EDITOR_DROP_INTO_PROMPT_BORDER = registerColor('editorGroup.dropIntoPromptBorder', { dark: null, @@ -333,28 +253,13 @@ export const EDITOR_DROP_INTO_PROMPT_BORDER = registerColor('editorGroup.dropInt hcLight: contrastBorder }, localize('editorDropIntoPromptBorder', "Border color of text shown over editors when dragging files. This text informs the user that they can hold shift to drop into the editor.")); -export const SIDE_BY_SIDE_EDITOR_HORIZONTAL_BORDER = registerColor('sideBySideEditor.horizontalBorder', { - dark: EDITOR_GROUP_BORDER, - light: EDITOR_GROUP_BORDER, - hcDark: EDITOR_GROUP_BORDER, - hcLight: EDITOR_GROUP_BORDER -}, localize('sideBySideEditor.horizontalBorder', "Color to separate two editors from each other when shown side by side in an editor group from top to bottom.")); +export const SIDE_BY_SIDE_EDITOR_HORIZONTAL_BORDER = registerColor('sideBySideEditor.horizontalBorder', EDITOR_GROUP_BORDER, localize('sideBySideEditor.horizontalBorder', "Color to separate two editors from each other when shown side by side in an editor group from top to bottom.")); -export const SIDE_BY_SIDE_EDITOR_VERTICAL_BORDER = registerColor('sideBySideEditor.verticalBorder', { - dark: EDITOR_GROUP_BORDER, - light: EDITOR_GROUP_BORDER, - hcDark: EDITOR_GROUP_BORDER, - hcLight: EDITOR_GROUP_BORDER -}, localize('sideBySideEditor.verticalBorder', "Color to separate two editors from each other when shown side by side in an editor group from left to right.")); +export const SIDE_BY_SIDE_EDITOR_VERTICAL_BORDER = registerColor('sideBySideEditor.verticalBorder', EDITOR_GROUP_BORDER, localize('sideBySideEditor.verticalBorder', "Color to separate two editors from each other when shown side by side in an editor group from left to right.")); // < --- Panels --- > -export const PANEL_BACKGROUND = registerColor('panel.background', { - dark: editorBackground, - light: editorBackground, - hcDark: editorBackground, - hcLight: editorBackground -}, localize('panelBackground', "Panel background color. Panels are shown below the editor area and contain views like output and integrated terminal.")); +export const PANEL_BACKGROUND = registerColor('panel.background', editorBackground, localize('panelBackground', "Panel background color. Panels are shown below the editor area and contain views like output and integrated terminal.")); export const PANEL_BORDER = registerColor('panel.border', { dark: Color.fromHex('#808080').transparent(0.35), @@ -391,19 +296,9 @@ export const PANEL_INPUT_BORDER = registerColor('panelInput.border', { hcLight: inputBorder }, localize('panelInputBorder', "Input box border for inputs in the panel.")); -export const PANEL_DRAG_AND_DROP_BORDER = registerColor('panel.dropBorder', { - dark: PANEL_ACTIVE_TITLE_FOREGROUND, - light: PANEL_ACTIVE_TITLE_FOREGROUND, - hcDark: PANEL_ACTIVE_TITLE_FOREGROUND, - hcLight: PANEL_ACTIVE_TITLE_FOREGROUND -}, localize('panelDragAndDropBorder', "Drag and drop feedback color for the panel titles. Panels are shown below the editor area and contain views like output and integrated terminal.")); +export const PANEL_DRAG_AND_DROP_BORDER = registerColor('panel.dropBorder', PANEL_ACTIVE_TITLE_FOREGROUND, localize('panelDragAndDropBorder', "Drag and drop feedback color for the panel titles. Panels are shown below the editor area and contain views like output and integrated terminal.")); -export const PANEL_SECTION_DRAG_AND_DROP_BACKGROUND = registerColor('panelSection.dropBackground', { - dark: EDITOR_DRAG_AND_DROP_BACKGROUND, - light: EDITOR_DRAG_AND_DROP_BACKGROUND, - hcDark: EDITOR_DRAG_AND_DROP_BACKGROUND, - hcLight: EDITOR_DRAG_AND_DROP_BACKGROUND -}, localize('panelSectionDragAndDropBackground', "Drag and drop feedback color for the panel sections. The color should have transparency so that the panel sections can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); +export const PANEL_SECTION_DRAG_AND_DROP_BACKGROUND = registerColor('panelSection.dropBackground', EDITOR_DRAG_AND_DROP_BACKGROUND, localize('panelSectionDragAndDropBackground', "Drag and drop feedback color for the panel sections. The color should have transparency so that the panel sections can still shine through. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); export const PANEL_SECTION_HEADER_BACKGROUND = registerColor('panelSectionHeader.background', { dark: Color.fromHex('#808080').transparent(0.2), @@ -412,64 +307,24 @@ export const PANEL_SECTION_HEADER_BACKGROUND = registerColor('panelSectionHeader hcLight: null, }, localize('panelSectionHeaderBackground', "Panel section header background color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); -export const PANEL_SECTION_HEADER_FOREGROUND = registerColor('panelSectionHeader.foreground', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('panelSectionHeaderForeground', "Panel section header foreground color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); +export const PANEL_SECTION_HEADER_FOREGROUND = registerColor('panelSectionHeader.foreground', null, localize('panelSectionHeaderForeground', "Panel section header foreground color. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); -export const PANEL_SECTION_HEADER_BORDER = registerColor('panelSectionHeader.border', { - dark: contrastBorder, - light: contrastBorder, - hcDark: contrastBorder, - hcLight: contrastBorder -}, localize('panelSectionHeaderBorder', "Panel section header border color used when multiple views are stacked vertically in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); - -export const PANEL_SECTION_BORDER = registerColor('panelSection.border', { - dark: PANEL_BORDER, - light: PANEL_BORDER, - hcDark: PANEL_BORDER, - hcLight: PANEL_BORDER -}, localize('panelSectionBorder', "Panel section border color used when multiple views are stacked horizontally in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); - -export const PANEL_STICKY_SCROLL_BACKGROUND = registerColor('panelStickyScroll.background', { - dark: PANEL_BACKGROUND, - light: PANEL_BACKGROUND, - hcDark: PANEL_BACKGROUND, - hcLight: PANEL_BACKGROUND -}, localize('panelStickyScrollBackground', "Background color of sticky scroll in the panel.")); - -export const PANEL_STICKY_SCROLL_BORDER = registerColor('panelStickyScroll.border', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('panelStickyScrollBorder', "Border color of sticky scroll in the panel.")); +export const PANEL_SECTION_HEADER_BORDER = registerColor('panelSectionHeader.border', contrastBorder, localize('panelSectionHeaderBorder', "Panel section header border color used when multiple views are stacked vertically in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); + +export const PANEL_SECTION_BORDER = registerColor('panelSection.border', PANEL_BORDER, localize('panelSectionBorder', "Panel section border color used when multiple views are stacked horizontally in the panel. Panels are shown below the editor area and contain views like output and integrated terminal. Panel sections are views nested within the panels.")); -export const PANEL_STICKY_SCROLL_SHADOW = registerColor('panelStickyScroll.shadow', { - dark: scrollbarShadow, - light: scrollbarShadow, - hcDark: scrollbarShadow, - hcLight: scrollbarShadow -}, localize('panelStickyScrollShadow', "Shadow color of sticky scroll in the panel.")); +export const PANEL_STICKY_SCROLL_BACKGROUND = registerColor('panelStickyScroll.background', PANEL_BACKGROUND, localize('panelStickyScrollBackground', "Background color of sticky scroll in the panel.")); + +export const PANEL_STICKY_SCROLL_BORDER = registerColor('panelStickyScroll.border', null, localize('panelStickyScrollBorder', "Border color of sticky scroll in the panel.")); + +export const PANEL_STICKY_SCROLL_SHADOW = registerColor('panelStickyScroll.shadow', scrollbarShadow, localize('panelStickyScrollShadow', "Shadow color of sticky scroll in the panel.")); // < --- Output Editor --> -const OUTPUT_VIEW_BACKGROUND = registerColor('outputView.background', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('outputViewBackground', "Output view background color.")); +const OUTPUT_VIEW_BACKGROUND = registerColor('outputView.background', null, localize('outputViewBackground', "Output view background color.")); -registerColor('outputViewStickyScroll.background', { - dark: OUTPUT_VIEW_BACKGROUND, - light: OUTPUT_VIEW_BACKGROUND, - hcDark: OUTPUT_VIEW_BACKGROUND, - hcLight: OUTPUT_VIEW_BACKGROUND -}, localize('outputViewStickyScrollBackground', "Output view sticky scroll background color.")); +registerColor('outputViewStickyScroll.background', OUTPUT_VIEW_BACKGROUND, localize('outputViewStickyScrollBackground', "Output view sticky scroll background color.")); // < --- Banner --- > @@ -481,19 +336,9 @@ export const BANNER_BACKGROUND = registerColor('banner.background', { hcLight: listActiveSelectionBackground }, localize('banner.background', "Banner background color. The banner is shown under the title bar of the window.")); -export const BANNER_FOREGROUND = registerColor('banner.foreground', { - dark: listActiveSelectionForeground, - light: listActiveSelectionForeground, - hcDark: listActiveSelectionForeground, - hcLight: listActiveSelectionForeground -}, localize('banner.foreground', "Banner foreground color. The banner is shown under the title bar of the window.")); +export const BANNER_FOREGROUND = registerColor('banner.foreground', listActiveSelectionForeground, localize('banner.foreground', "Banner foreground color. The banner is shown under the title bar of the window.")); -export const BANNER_ICON_FOREGROUND = registerColor('banner.iconForeground', { - dark: editorInfoForeground, - light: editorInfoForeground, - hcDark: editorInfoForeground, - hcLight: editorInfoForeground -}, localize('banner.iconForeground', "Banner icon color. The banner is shown under the title bar of the window.")); +export const BANNER_ICON_FOREGROUND = registerColor('banner.iconForeground', editorInfoForeground, localize('banner.iconForeground', "Banner icon color. The banner is shown under the title bar of the window.")); // < --- Status --- > @@ -504,12 +349,7 @@ export const STATUS_BAR_FOREGROUND = registerColor('statusBar.foreground', { hcLight: editorForeground }, localize('statusBarForeground', "Status bar foreground color when a workspace or folder is opened. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_NO_FOLDER_FOREGROUND = registerColor('statusBar.noFolderForeground', { - dark: STATUS_BAR_FOREGROUND, - light: STATUS_BAR_FOREGROUND, - hcDark: STATUS_BAR_FOREGROUND, - hcLight: STATUS_BAR_FOREGROUND -}, localize('statusBarNoFolderForeground', "Status bar foreground color when no folder is opened. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_NO_FOLDER_FOREGROUND = registerColor('statusBar.noFolderForeground', STATUS_BAR_FOREGROUND, localize('statusBarNoFolderForeground', "Status bar foreground color when no folder is opened. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_BACKGROUND = registerColor('statusBar.background', { dark: '#007ACC', @@ -539,12 +379,7 @@ export const STATUS_BAR_FOCUS_BORDER = registerColor('statusBar.focusBorder', { hcLight: STATUS_BAR_FOREGROUND }, localize('statusBarFocusBorder', "Status bar border color when focused on keyboard navigation. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_NO_FOLDER_BORDER = registerColor('statusBar.noFolderBorder', { - dark: STATUS_BAR_BORDER, - light: STATUS_BAR_BORDER, - hcDark: STATUS_BAR_BORDER, - hcLight: STATUS_BAR_BORDER -}, localize('statusBarNoFolderBorder', "Status bar border color separating to the sidebar and editor when no folder is opened. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_NO_FOLDER_BORDER = registerColor('statusBar.noFolderBorder', STATUS_BAR_BORDER, localize('statusBarNoFolderBorder', "Status bar border color separating to the sidebar and editor when no folder is opened. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_ITEM_ACTIVE_BACKGROUND = registerColor('statusBarItem.activeBackground', { dark: Color.white.transparent(0.18), @@ -567,12 +402,7 @@ export const STATUS_BAR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.hov hcLight: Color.black.transparent(0.12) }, localize('statusBarItemHoverBackground', "Status bar item background color when hovering. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.hoverForeground', { - dark: STATUS_BAR_FOREGROUND, - light: STATUS_BAR_FOREGROUND, - hcDark: STATUS_BAR_FOREGROUND, - hcLight: STATUS_BAR_FOREGROUND -}, localize('statusBarItemHoverForeground', "Status bar item foreground color when hovering. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.hoverForeground', STATUS_BAR_FOREGROUND, localize('statusBarItemHoverForeground', "Status bar item foreground color when hovering. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_ITEM_COMPACT_HOVER_BACKGROUND = registerColor('statusBarItem.compactHoverBackground', { dark: Color.white.transparent(0.20), @@ -581,26 +411,11 @@ export const STATUS_BAR_ITEM_COMPACT_HOVER_BACKGROUND = registerColor('statusBar hcLight: Color.black.transparent(0.20) }, localize('statusBarItemCompactHoverBackground', "Status bar item background color when hovering an item that contains two hovers. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_PROMINENT_ITEM_FOREGROUND = registerColor('statusBarItem.prominentForeground', { - dark: STATUS_BAR_FOREGROUND, - light: STATUS_BAR_FOREGROUND, - hcDark: STATUS_BAR_FOREGROUND, - hcLight: STATUS_BAR_FOREGROUND -}, localize('statusBarProminentItemForeground', "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); - -export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem.prominentBackground', { - dark: Color.black.transparent(0.5), - light: Color.black.transparent(0.5), - hcDark: Color.black.transparent(0.5), - hcLight: Color.black.transparent(0.5), -}, localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); - -export const STATUS_BAR_PROMINENT_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.prominentHoverForeground', { - dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - light: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND -}, localize('statusBarProminentItemHoverForeground', "Status bar prominent items foreground color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_PROMINENT_ITEM_FOREGROUND = registerColor('statusBarItem.prominentForeground', STATUS_BAR_FOREGROUND, localize('statusBarProminentItemForeground', "Status bar prominent items foreground color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); + +export const STATUS_BAR_PROMINENT_ITEM_BACKGROUND = registerColor('statusBarItem.prominentBackground', Color.black.transparent(0.5), localize('statusBarProminentItemBackground', "Status bar prominent items background color. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); + +export const STATUS_BAR_PROMINENT_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.prominentHoverForeground', STATUS_BAR_ITEM_HOVER_FOREGROUND, localize('statusBarProminentItemHoverForeground', "Status bar prominent items foreground color when hovering. Prominent items stand out from other status bar entries to indicate importance. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_PROMINENT_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.prominentHoverBackground', { dark: Color.black.transparent(0.3), @@ -616,26 +431,11 @@ export const STATUS_BAR_ERROR_ITEM_BACKGROUND = registerColor('statusBarItem.err hcLight: '#B5200D' }, localize('statusBarErrorItemBackground', "Status bar error items background color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_ERROR_ITEM_FOREGROUND = registerColor('statusBarItem.errorForeground', { - dark: Color.white, - light: Color.white, - hcDark: Color.white, - hcLight: Color.white -}, localize('statusBarErrorItemForeground', "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_ERROR_ITEM_FOREGROUND = registerColor('statusBarItem.errorForeground', Color.white, localize('statusBarErrorItemForeground', "Status bar error items foreground color. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_ERROR_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.errorHoverForeground', { - dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - light: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND -}, localize('statusBarErrorItemHoverForeground', "Status bar error items foreground color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_ERROR_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.errorHoverForeground', STATUS_BAR_ITEM_HOVER_FOREGROUND, localize('statusBarErrorItemHoverForeground', "Status bar error items foreground color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_ERROR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.errorHoverBackground', { - dark: STATUS_BAR_ITEM_HOVER_BACKGROUND, - light: STATUS_BAR_ITEM_HOVER_BACKGROUND, - hcDark: STATUS_BAR_ITEM_HOVER_BACKGROUND, - hcLight: STATUS_BAR_ITEM_HOVER_BACKGROUND -}, localize('statusBarErrorItemHoverBackground', "Status bar error items background color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_ERROR_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.errorHoverBackground', STATUS_BAR_ITEM_HOVER_BACKGROUND, localize('statusBarErrorItemHoverBackground', "Status bar error items background color when hovering. Error items stand out from other status bar entries to indicate error conditions. The status bar is shown in the bottom of the window.")); export const STATUS_BAR_WARNING_ITEM_BACKGROUND = registerColor('statusBarItem.warningBackground', { dark: darken(editorWarningForeground, .4), @@ -644,26 +444,11 @@ export const STATUS_BAR_WARNING_ITEM_BACKGROUND = registerColor('statusBarItem.w hcLight: '#895503' }, localize('statusBarWarningItemBackground', "Status bar warning items background color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_WARNING_ITEM_FOREGROUND = registerColor('statusBarItem.warningForeground', { - dark: Color.white, - light: Color.white, - hcDark: Color.white, - hcLight: Color.white -}, localize('statusBarWarningItemForeground', "Status bar warning items foreground color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_WARNING_ITEM_FOREGROUND = registerColor('statusBarItem.warningForeground', Color.white, localize('statusBarWarningItemForeground', "Status bar warning items foreground color. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_WARNING_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.warningHoverForeground', { - dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - light: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND -}, localize('statusBarWarningItemHoverForeground', "Status bar warning items foreground color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_WARNING_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.warningHoverForeground', STATUS_BAR_ITEM_HOVER_FOREGROUND, localize('statusBarWarningItemHoverForeground', "Status bar warning items foreground color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); -export const STATUS_BAR_WARNING_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.warningHoverBackground', { - dark: STATUS_BAR_ITEM_HOVER_BACKGROUND, - light: STATUS_BAR_ITEM_HOVER_BACKGROUND, - hcDark: STATUS_BAR_ITEM_HOVER_BACKGROUND, - hcLight: STATUS_BAR_ITEM_HOVER_BACKGROUND -}, localize('statusBarWarningItemHoverBackground', "Status bar warning items background color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); +export const STATUS_BAR_WARNING_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.warningHoverBackground', STATUS_BAR_ITEM_HOVER_BACKGROUND, localize('statusBarWarningItemHoverBackground', "Status bar warning items background color when hovering. Warning items stand out from other status bar entries to indicate warning conditions. The status bar is shown in the bottom of the window.")); // < --- Activity Bar --- > @@ -710,12 +495,7 @@ export const ACTIVITY_BAR_ACTIVE_FOCUS_BORDER = registerColor('activityBar.activ hcLight: '#B5200D' }, localize('activityBarActiveFocusBorder', "Activity bar focus border color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); -export const ACTIVITY_BAR_ACTIVE_BACKGROUND = registerColor('activityBar.activeBackground', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('activityBarActiveBackground', "Activity bar background color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +export const ACTIVITY_BAR_ACTIVE_BACKGROUND = registerColor('activityBar.activeBackground', null, localize('activityBarActiveBackground', "Activity bar background color for the active item. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_DRAG_AND_DROP_BORDER = registerColor('activityBar.dropBorder', { dark: ACTIVITY_BAR_FOREGROUND, @@ -731,12 +511,7 @@ export const ACTIVITY_BAR_BADGE_BACKGROUND = registerColor('activityBarBadge.bac hcLight: '#0F4A85' }, localize('activityBarBadgeBackground', "Activity notification badge background color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); -export const ACTIVITY_BAR_BADGE_FOREGROUND = registerColor('activityBarBadge.foreground', { - dark: Color.white, - light: Color.white, - hcDark: Color.white, - hcLight: Color.white -}, localize('activityBarBadgeForeground', "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); +export const ACTIVITY_BAR_BADGE_FOREGROUND = registerColor('activityBarBadge.foreground', Color.white, localize('activityBarBadgeForeground', "Activity notification badge foreground color. The activity bar is showing on the far left or right and allows to switch between views of the side bar.")); export const ACTIVITY_BAR_TOP_FOREGROUND = registerColor('activityBarTop.foreground', { dark: '#E7E7E7', @@ -752,12 +527,7 @@ export const ACTIVITY_BAR_TOP_ACTIVE_BORDER = registerColor('activityBarTop.acti hcLight: '#B5200D' }, localize('activityBarTopActiveFocusBorder', "Focus border color for the active item in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.")); -export const ACTIVITY_BAR_TOP_ACTIVE_BACKGROUND = registerColor('activityBarTop.activeBackground', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('activityBarTopActiveBackground', "Background color for the active item in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.")); +export const ACTIVITY_BAR_TOP_ACTIVE_BACKGROUND = registerColor('activityBarTop.activeBackground', null, localize('activityBarTopActiveBackground', "Background color for the active item in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.")); export const ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND = registerColor('activityBarTop.inactiveForeground', { dark: transparent(ACTIVITY_BAR_TOP_FOREGROUND, 0.6), @@ -766,19 +536,9 @@ export const ACTIVITY_BAR_TOP_INACTIVE_FOREGROUND = registerColor('activityBarTo hcLight: editorForeground }, localize('activityBarTopInActiveForeground', "Inactive foreground color of the item in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.")); -export const ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER = registerColor('activityBarTop.dropBorder', { - dark: ACTIVITY_BAR_TOP_FOREGROUND, - light: ACTIVITY_BAR_TOP_FOREGROUND, - hcDark: ACTIVITY_BAR_TOP_FOREGROUND, - hcLight: ACTIVITY_BAR_TOP_FOREGROUND -}, localize('activityBarTopDragAndDropBorder', "Drag and drop feedback color for the items in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.")); +export const ACTIVITY_BAR_TOP_DRAG_AND_DROP_BORDER = registerColor('activityBarTop.dropBorder', ACTIVITY_BAR_TOP_FOREGROUND, localize('activityBarTopDragAndDropBorder', "Drag and drop feedback color for the items in the Activity bar when it is on top / bottom. The activity allows to switch between views of the side bar.")); -export const ACTIVITY_BAR_TOP_BACKGROUND = registerColor('activityBarTop.background', { - dark: null, - light: null, - hcDark: null, - hcLight: null, -}, localize('activityBarTopBackground', "Background color of the activity bar when set to top / bottom.")); +export const ACTIVITY_BAR_TOP_BACKGROUND = registerColor('activityBarTop.background', null, localize('activityBarTopBackground', "Background color of the activity bar when set to top / bottom.")); // < --- Profiles --- > @@ -799,26 +559,11 @@ export const PROFILE_BADGE_FOREGROUND = registerColor('profileBadge.foreground', // < --- Remote --- > -export const STATUS_BAR_REMOTE_ITEM_BACKGROUND = registerColor('statusBarItem.remoteBackground', { - dark: ACTIVITY_BAR_BADGE_BACKGROUND, - light: ACTIVITY_BAR_BADGE_BACKGROUND, - hcDark: ACTIVITY_BAR_BADGE_BACKGROUND, - hcLight: ACTIVITY_BAR_BADGE_BACKGROUND -}, localize('statusBarItemHostBackground', "Background color for the remote indicator on the status bar.")); - -export const STATUS_BAR_REMOTE_ITEM_FOREGROUND = registerColor('statusBarItem.remoteForeground', { - dark: ACTIVITY_BAR_BADGE_FOREGROUND, - light: ACTIVITY_BAR_BADGE_FOREGROUND, - hcDark: ACTIVITY_BAR_BADGE_FOREGROUND, - hcLight: ACTIVITY_BAR_BADGE_FOREGROUND -}, localize('statusBarItemHostForeground', "Foreground color for the remote indicator on the status bar.")); - -export const STATUS_BAR_REMOTE_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.remoteHoverForeground', { - dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - light: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND -}, localize('statusBarRemoteItemHoverForeground', "Foreground color for the remote indicator on the status bar when hovering.")); +export const STATUS_BAR_REMOTE_ITEM_BACKGROUND = registerColor('statusBarItem.remoteBackground', ACTIVITY_BAR_BADGE_BACKGROUND, localize('statusBarItemHostBackground', "Background color for the remote indicator on the status bar.")); + +export const STATUS_BAR_REMOTE_ITEM_FOREGROUND = registerColor('statusBarItem.remoteForeground', ACTIVITY_BAR_BADGE_FOREGROUND, localize('statusBarItemHostForeground', "Foreground color for the remote indicator on the status bar.")); + +export const STATUS_BAR_REMOTE_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.remoteHoverForeground', STATUS_BAR_ITEM_HOVER_FOREGROUND, localize('statusBarRemoteItemHoverForeground', "Foreground color for the remote indicator on the status bar when hovering.")); export const STATUS_BAR_REMOTE_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.remoteHoverBackground', { dark: STATUS_BAR_ITEM_HOVER_BACKGROUND, @@ -827,26 +572,11 @@ export const STATUS_BAR_REMOTE_ITEM_HOVER_BACKGROUND = registerColor('statusBarI hcLight: null }, localize('statusBarRemoteItemHoverBackground', "Background color for the remote indicator on the status bar when hovering.")); -export const STATUS_BAR_OFFLINE_ITEM_BACKGROUND = registerColor('statusBarItem.offlineBackground', { - dark: '#6c1717', - light: '#6c1717', - hcDark: '#6c1717', - hcLight: '#6c1717' -}, localize('statusBarItemOfflineBackground', "Status bar item background color when the workbench is offline.")); - -export const STATUS_BAR_OFFLINE_ITEM_FOREGROUND = registerColor('statusBarItem.offlineForeground', { - dark: STATUS_BAR_REMOTE_ITEM_FOREGROUND, - light: STATUS_BAR_REMOTE_ITEM_FOREGROUND, - hcDark: STATUS_BAR_REMOTE_ITEM_FOREGROUND, - hcLight: STATUS_BAR_REMOTE_ITEM_FOREGROUND -}, localize('statusBarItemOfflineForeground', "Status bar item foreground color when the workbench is offline.")); - -export const STATUS_BAR_OFFLINE_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.offlineHoverForeground', { - dark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - light: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcDark: STATUS_BAR_ITEM_HOVER_FOREGROUND, - hcLight: STATUS_BAR_ITEM_HOVER_FOREGROUND -}, localize('statusBarOfflineItemHoverForeground', "Status bar item foreground hover color when the workbench is offline.")); +export const STATUS_BAR_OFFLINE_ITEM_BACKGROUND = registerColor('statusBarItem.offlineBackground', '#6c1717', localize('statusBarItemOfflineBackground', "Status bar item background color when the workbench is offline.")); + +export const STATUS_BAR_OFFLINE_ITEM_FOREGROUND = registerColor('statusBarItem.offlineForeground', STATUS_BAR_REMOTE_ITEM_FOREGROUND, localize('statusBarItemOfflineForeground', "Status bar item foreground color when the workbench is offline.")); + +export const STATUS_BAR_OFFLINE_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.offlineHoverForeground', STATUS_BAR_ITEM_HOVER_FOREGROUND, localize('statusBarOfflineItemHoverForeground', "Status bar item foreground hover color when the workbench is offline.")); export const STATUS_BAR_OFFLINE_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.offlineHoverBackground', { dark: STATUS_BAR_ITEM_HOVER_BACKGROUND, @@ -855,19 +585,9 @@ export const STATUS_BAR_OFFLINE_ITEM_HOVER_BACKGROUND = registerColor('statusBar hcLight: null }, localize('statusBarOfflineItemHoverBackground', "Status bar item background hover color when the workbench is offline.")); -export const EXTENSION_BADGE_REMOTE_BACKGROUND = registerColor('extensionBadge.remoteBackground', { - dark: ACTIVITY_BAR_BADGE_BACKGROUND, - light: ACTIVITY_BAR_BADGE_BACKGROUND, - hcDark: ACTIVITY_BAR_BADGE_BACKGROUND, - hcLight: ACTIVITY_BAR_BADGE_BACKGROUND -}, localize('extensionBadge.remoteBackground', "Background color for the remote badge in the extensions view.")); +export const EXTENSION_BADGE_REMOTE_BACKGROUND = registerColor('extensionBadge.remoteBackground', ACTIVITY_BAR_BADGE_BACKGROUND, localize('extensionBadge.remoteBackground', "Background color for the remote badge in the extensions view.")); -export const EXTENSION_BADGE_REMOTE_FOREGROUND = registerColor('extensionBadge.remoteForeground', { - dark: ACTIVITY_BAR_BADGE_FOREGROUND, - light: ACTIVITY_BAR_BADGE_FOREGROUND, - hcDark: ACTIVITY_BAR_BADGE_FOREGROUND, - hcLight: ACTIVITY_BAR_BADGE_FOREGROUND -}, localize('extensionBadge.remoteForeground', "Foreground color for the remote badge in the extensions view.")); +export const EXTENSION_BADGE_REMOTE_FOREGROUND = registerColor('extensionBadge.remoteForeground', ACTIVITY_BAR_BADGE_FOREGROUND, localize('extensionBadge.remoteForeground', "Foreground color for the remote badge in the extensions view.")); // < --- Side Bar --- > @@ -879,12 +599,7 @@ export const SIDE_BAR_BACKGROUND = registerColor('sideBar.background', { hcLight: '#FFFFFF' }, localize('sideBarBackground', "Side bar background color. The side bar is the container for views like explorer and search.")); -export const SIDE_BAR_FOREGROUND = registerColor('sideBar.foreground', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('sideBarForeground', "Side bar foreground color. The side bar is the container for views like explorer and search.")); +export const SIDE_BAR_FOREGROUND = registerColor('sideBar.foreground', null, localize('sideBarForeground', "Side bar foreground color. The side bar is the container for views like explorer and search.")); export const SIDE_BAR_BORDER = registerColor('sideBar.border', { dark: null, @@ -893,26 +608,11 @@ export const SIDE_BAR_BORDER = registerColor('sideBar.border', { hcLight: contrastBorder }, localize('sideBarBorder', "Side bar border color on the side separating to the editor. The side bar is the container for views like explorer and search.")); -export const SIDE_BAR_TITLE_BACKGROUND = registerColor('sideBarTitle.background', { - dark: SIDE_BAR_BACKGROUND, - light: SIDE_BAR_BACKGROUND, - hcDark: SIDE_BAR_BACKGROUND, - hcLight: SIDE_BAR_BACKGROUND -}, localize('sideBarTitleBackground', "Side bar title background color. The side bar is the container for views like explorer and search.")); - -export const SIDE_BAR_TITLE_FOREGROUND = registerColor('sideBarTitle.foreground', { - dark: SIDE_BAR_FOREGROUND, - light: SIDE_BAR_FOREGROUND, - hcDark: SIDE_BAR_FOREGROUND, - hcLight: SIDE_BAR_FOREGROUND -}, localize('sideBarTitleForeground', "Side bar title foreground color. The side bar is the container for views like explorer and search.")); - -export const SIDE_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('sideBar.dropBackground', { - dark: EDITOR_DRAG_AND_DROP_BACKGROUND, - light: EDITOR_DRAG_AND_DROP_BACKGROUND, - hcDark: EDITOR_DRAG_AND_DROP_BACKGROUND, - hcLight: EDITOR_DRAG_AND_DROP_BACKGROUND -}, localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); +export const SIDE_BAR_TITLE_BACKGROUND = registerColor('sideBarTitle.background', SIDE_BAR_BACKGROUND, localize('sideBarTitleBackground', "Side bar title background color. The side bar is the container for views like explorer and search.")); + +export const SIDE_BAR_TITLE_FOREGROUND = registerColor('sideBarTitle.foreground', SIDE_BAR_FOREGROUND, localize('sideBarTitleForeground', "Side bar title foreground color. The side bar is the container for views like explorer and search.")); + +export const SIDE_BAR_DRAG_AND_DROP_BACKGROUND = registerColor('sideBar.dropBackground', EDITOR_DRAG_AND_DROP_BACKGROUND, localize('sideBarDragAndDropBackground', "Drag and drop feedback color for the side bar sections. The color should have transparency so that the side bar sections can still shine through. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); export const SIDE_BAR_SECTION_HEADER_BACKGROUND = registerColor('sideBarSectionHeader.background', { dark: Color.fromHex('#808080').transparent(0.2), @@ -921,47 +621,17 @@ export const SIDE_BAR_SECTION_HEADER_BACKGROUND = registerColor('sideBarSectionH hcLight: null }, localize('sideBarSectionHeaderBackground', "Side bar section header background color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); -export const SIDE_BAR_SECTION_HEADER_FOREGROUND = registerColor('sideBarSectionHeader.foreground', { - dark: SIDE_BAR_FOREGROUND, - light: SIDE_BAR_FOREGROUND, - hcDark: SIDE_BAR_FOREGROUND, - hcLight: SIDE_BAR_FOREGROUND -}, localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); +export const SIDE_BAR_SECTION_HEADER_FOREGROUND = registerColor('sideBarSectionHeader.foreground', SIDE_BAR_FOREGROUND, localize('sideBarSectionHeaderForeground', "Side bar section header foreground color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); -export const SIDE_BAR_SECTION_HEADER_BORDER = registerColor('sideBarSectionHeader.border', { - dark: contrastBorder, - light: contrastBorder, - hcDark: contrastBorder, - hcLight: contrastBorder -}, localize('sideBarSectionHeaderBorder', "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); - -export const ACTIVITY_BAR_TOP_BORDER = registerColor('sideBarActivityBarTop.border', { - dark: SIDE_BAR_SECTION_HEADER_BORDER, - light: SIDE_BAR_SECTION_HEADER_BORDER, - hcDark: SIDE_BAR_SECTION_HEADER_BORDER, - hcLight: SIDE_BAR_SECTION_HEADER_BORDER -}, localize('sideBarActivityBarTopBorder', "Border color between the activity bar at the top/bottom and the views.")); - -export const SIDE_BAR_STICKY_SCROLL_BACKGROUND = registerColor('sideBarStickyScroll.background', { - dark: SIDE_BAR_BACKGROUND, - light: SIDE_BAR_BACKGROUND, - hcDark: SIDE_BAR_BACKGROUND, - hcLight: SIDE_BAR_BACKGROUND -}, localize('sideBarStickyScrollBackground', "Background color of sticky scroll in the side bar.")); - -export const SIDE_BAR_STICKY_SCROLL_BORDER = registerColor('sideBarStickyScroll.border', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('sideBarStickyScrollBorder', "Border color of sticky scroll in the side bar.")); +export const SIDE_BAR_SECTION_HEADER_BORDER = registerColor('sideBarSectionHeader.border', contrastBorder, localize('sideBarSectionHeaderBorder', "Side bar section header border color. The side bar is the container for views like explorer and search. Side bar sections are views nested within the side bar.")); + +export const ACTIVITY_BAR_TOP_BORDER = registerColor('sideBarActivityBarTop.border', SIDE_BAR_SECTION_HEADER_BORDER, localize('sideBarActivityBarTopBorder', "Border color between the activity bar at the top/bottom and the views.")); + +export const SIDE_BAR_STICKY_SCROLL_BACKGROUND = registerColor('sideBarStickyScroll.background', SIDE_BAR_BACKGROUND, localize('sideBarStickyScrollBackground', "Background color of sticky scroll in the side bar.")); -export const SIDE_BAR_STICKY_SCROLL_SHADOW = registerColor('sideBarStickyScroll.shadow', { - dark: scrollbarShadow, - light: scrollbarShadow, - hcDark: scrollbarShadow, - hcLight: scrollbarShadow -}, localize('sideBarStickyScrollShadow', "Shadow color of sticky scroll in the side bar.")); +export const SIDE_BAR_STICKY_SCROLL_BORDER = registerColor('sideBarStickyScroll.border', null, localize('sideBarStickyScrollBorder', "Border color of sticky scroll in the side bar.")); + +export const SIDE_BAR_STICKY_SCROLL_SHADOW = registerColor('sideBarStickyScroll.shadow', scrollbarShadow, localize('sideBarStickyScrollShadow', "Shadow color of sticky scroll in the side bar.")); // < --- Title Bar --- > @@ -1002,12 +672,7 @@ export const TITLE_BAR_BORDER = registerColor('titleBar.border', { // < --- Menubar --- > -export const MENUBAR_SELECTION_FOREGROUND = registerColor('menubar.selectionForeground', { - dark: TITLE_BAR_ACTIVE_FOREGROUND, - light: TITLE_BAR_ACTIVE_FOREGROUND, - hcDark: TITLE_BAR_ACTIVE_FOREGROUND, - hcLight: TITLE_BAR_ACTIVE_FOREGROUND, -}, localize('menubarSelectionForeground', "Foreground color of the selected menu item in the menubar.")); +export const MENUBAR_SELECTION_FOREGROUND = registerColor('menubar.selectionForeground', TITLE_BAR_ACTIVE_FOREGROUND, localize('menubarSelectionForeground', "Foreground color of the selected menu item in the menubar.")); export const MENUBAR_SELECTION_BACKGROUND = registerColor('menubar.selectionBackground', { dark: toolbarHoverBackground, @@ -1028,19 +693,19 @@ export const MENUBAR_SELECTION_BORDER = registerColor('menubar.selectionBorder', // foreground (inactive and active) export const COMMAND_CENTER_FOREGROUND = registerColor( 'commandCenter.foreground', - { dark: TITLE_BAR_ACTIVE_FOREGROUND, hcDark: TITLE_BAR_ACTIVE_FOREGROUND, light: TITLE_BAR_ACTIVE_FOREGROUND, hcLight: TITLE_BAR_ACTIVE_FOREGROUND }, + TITLE_BAR_ACTIVE_FOREGROUND, localize('commandCenter-foreground', "Foreground color of the command center"), false ); export const COMMAND_CENTER_ACTIVEFOREGROUND = registerColor( 'commandCenter.activeForeground', - { dark: MENUBAR_SELECTION_FOREGROUND, hcDark: MENUBAR_SELECTION_FOREGROUND, light: MENUBAR_SELECTION_FOREGROUND, hcLight: MENUBAR_SELECTION_FOREGROUND }, + MENUBAR_SELECTION_FOREGROUND, localize('commandCenter-activeForeground', "Active foreground color of the command center"), false ); export const COMMAND_CENTER_INACTIVEFOREGROUND = registerColor( 'commandCenter.inactiveForeground', - { dark: TITLE_BAR_INACTIVE_FOREGROUND, hcDark: TITLE_BAR_INACTIVE_FOREGROUND, light: TITLE_BAR_INACTIVE_FOREGROUND, hcLight: TITLE_BAR_INACTIVE_FOREGROUND }, + TITLE_BAR_INACTIVE_FOREGROUND, localize('commandCenter-inactiveForeground', "Foreground color of the command center when the window is inactive"), false ); @@ -1070,7 +735,7 @@ export const COMMAND_CENTER_ACTIVEBORDER = registerColor( ); // border: defaults to active background export const COMMAND_CENTER_INACTIVEBORDER = registerColor( - 'commandCenter.inactiveBorder', { dark: transparent(TITLE_BAR_INACTIVE_FOREGROUND, .25), hcDark: transparent(TITLE_BAR_INACTIVE_FOREGROUND, .25), light: transparent(TITLE_BAR_INACTIVE_FOREGROUND, .25), hcLight: transparent(TITLE_BAR_INACTIVE_FOREGROUND, .25) }, + 'commandCenter.inactiveBorder', transparent(TITLE_BAR_INACTIVE_FOREGROUND, .25), localize('commandCenter-inactiveBorder', "Border color of the command center when the window is inactive"), false ); @@ -1092,33 +757,13 @@ export const NOTIFICATIONS_TOAST_BORDER = registerColor('notificationToast.borde hcLight: contrastBorder }, localize('notificationToastBorder', "Notification toast border color. Notifications slide in from the bottom right of the window.")); -export const NOTIFICATIONS_FOREGROUND = registerColor('notifications.foreground', { - dark: editorWidgetForeground, - light: editorWidgetForeground, - hcDark: editorWidgetForeground, - hcLight: editorWidgetForeground -}, localize('notificationsForeground', "Notifications foreground color. Notifications slide in from the bottom right of the window.")); - -export const NOTIFICATIONS_BACKGROUND = registerColor('notifications.background', { - dark: editorWidgetBackground, - light: editorWidgetBackground, - hcDark: editorWidgetBackground, - hcLight: editorWidgetBackground -}, localize('notificationsBackground', "Notifications background color. Notifications slide in from the bottom right of the window.")); - -export const NOTIFICATIONS_LINKS = registerColor('notificationLink.foreground', { - dark: textLinkForeground, - light: textLinkForeground, - hcDark: textLinkForeground, - hcLight: textLinkForeground -}, localize('notificationsLink', "Notification links foreground color. Notifications slide in from the bottom right of the window.")); - -export const NOTIFICATIONS_CENTER_HEADER_FOREGROUND = registerColor('notificationCenterHeader.foreground', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('notificationCenterHeaderForeground', "Notifications center header foreground color. Notifications slide in from the bottom right of the window.")); +export const NOTIFICATIONS_FOREGROUND = registerColor('notifications.foreground', editorWidgetForeground, localize('notificationsForeground', "Notifications foreground color. Notifications slide in from the bottom right of the window.")); + +export const NOTIFICATIONS_BACKGROUND = registerColor('notifications.background', editorWidgetBackground, localize('notificationsBackground', "Notifications background color. Notifications slide in from the bottom right of the window.")); + +export const NOTIFICATIONS_LINKS = registerColor('notificationLink.foreground', textLinkForeground, localize('notificationsLink', "Notification links foreground color. Notifications slide in from the bottom right of the window.")); + +export const NOTIFICATIONS_CENTER_HEADER_FOREGROUND = registerColor('notificationCenterHeader.foreground', null, localize('notificationCenterHeaderForeground', "Notifications center header foreground color. Notifications slide in from the bottom right of the window.")); export const NOTIFICATIONS_CENTER_HEADER_BACKGROUND = registerColor('notificationCenterHeader.background', { dark: lighten(NOTIFICATIONS_BACKGROUND, 0.3), @@ -1127,33 +772,13 @@ export const NOTIFICATIONS_CENTER_HEADER_BACKGROUND = registerColor('notificatio hcLight: NOTIFICATIONS_BACKGROUND }, localize('notificationCenterHeaderBackground', "Notifications center header background color. Notifications slide in from the bottom right of the window.")); -export const NOTIFICATIONS_BORDER = registerColor('notifications.border', { - dark: NOTIFICATIONS_CENTER_HEADER_BACKGROUND, - light: NOTIFICATIONS_CENTER_HEADER_BACKGROUND, - hcDark: NOTIFICATIONS_CENTER_HEADER_BACKGROUND, - hcLight: NOTIFICATIONS_CENTER_HEADER_BACKGROUND -}, localize('notificationsBorder', "Notifications border color separating from other notifications in the notifications center. Notifications slide in from the bottom right of the window.")); - -export const NOTIFICATIONS_ERROR_ICON_FOREGROUND = registerColor('notificationsErrorIcon.foreground', { - dark: editorErrorForeground, - light: editorErrorForeground, - hcDark: editorErrorForeground, - hcLight: editorErrorForeground -}, localize('notificationsErrorIconForeground', "The color used for the icon of error notifications. Notifications slide in from the bottom right of the window.")); - -export const NOTIFICATIONS_WARNING_ICON_FOREGROUND = registerColor('notificationsWarningIcon.foreground', { - dark: editorWarningForeground, - light: editorWarningForeground, - hcDark: editorWarningForeground, - hcLight: editorWarningForeground -}, localize('notificationsWarningIconForeground', "The color used for the icon of warning notifications. Notifications slide in from the bottom right of the window.")); - -export const NOTIFICATIONS_INFO_ICON_FOREGROUND = registerColor('notificationsInfoIcon.foreground', { - dark: editorInfoForeground, - light: editorInfoForeground, - hcDark: editorInfoForeground, - hcLight: editorInfoForeground -}, localize('notificationsInfoIconForeground', "The color used for the icon of info notifications. Notifications slide in from the bottom right of the window.")); +export const NOTIFICATIONS_BORDER = registerColor('notifications.border', NOTIFICATIONS_CENTER_HEADER_BACKGROUND, localize('notificationsBorder', "Notifications border color separating from other notifications in the notifications center. Notifications slide in from the bottom right of the window.")); + +export const NOTIFICATIONS_ERROR_ICON_FOREGROUND = registerColor('notificationsErrorIcon.foreground', editorErrorForeground, localize('notificationsErrorIconForeground', "The color used for the icon of error notifications. Notifications slide in from the bottom right of the window.")); + +export const NOTIFICATIONS_WARNING_ICON_FOREGROUND = registerColor('notificationsWarningIcon.foreground', editorWarningForeground, localize('notificationsWarningIconForeground', "The color used for the icon of warning notifications. Notifications slide in from the bottom right of the window.")); + +export const NOTIFICATIONS_INFO_ICON_FOREGROUND = registerColor('notificationsInfoIcon.foreground', editorInfoForeground, localize('notificationsInfoIconForeground', "The color used for the icon of info notifications. Notifications slide in from the bottom right of the window.")); export const WINDOW_ACTIVE_BORDER = registerColor('window.activeBorder', { dark: null, diff --git a/src/vs/workbench/contrib/chat/common/chatColors.ts b/src/vs/workbench/contrib/chat/common/chatColors.ts index 3c4cce05e01..15451f0de58 100644 --- a/src/vs/workbench/contrib/chat/common/chatColors.ts +++ b/src/vs/workbench/contrib/chat/common/chatColors.ts @@ -39,6 +39,6 @@ export const chatAvatarBackground = registerColor( export const chatAvatarForeground = registerColor( 'chat.avatarForeground', - { dark: foreground, light: foreground, hcDark: foreground, hcLight: foreground, }, + foreground, localize('chat.avatarForeground', 'The foreground color of a chat avatar.') ); diff --git a/src/vs/workbench/contrib/comments/browser/commentColors.ts b/src/vs/workbench/contrib/comments/browser/commentColors.ts index 08d44a3224b..b66b9590f76 100644 --- a/src/vs/workbench/contrib/comments/browser/commentColors.ts +++ b/src/vs/workbench/contrib/comments/browser/commentColors.ts @@ -13,11 +13,11 @@ import { IColorTheme } from 'vs/platform/theme/common/themeService'; const resolvedCommentViewIcon = registerColor('commentsView.resolvedIcon', { dark: disabledForeground, light: disabledForeground, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('resolvedCommentIcon', 'Icon color for resolved comments.')); const unresolvedCommentViewIcon = registerColor('commentsView.unresolvedIcon', { dark: listFocusOutline, light: listFocusOutline, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('unresolvedCommentIcon', 'Icon color for unresolved comments.')); -registerColor('editorCommentsWidget.replyInputBackground', { dark: peekViewTitleBackground, light: peekViewTitleBackground, hcDark: peekViewTitleBackground, hcLight: peekViewTitleBackground }, nls.localize('commentReplyInputBackground', 'Background color for comment reply input box.')); +registerColor('editorCommentsWidget.replyInputBackground', peekViewTitleBackground, nls.localize('commentReplyInputBackground', 'Background color for comment reply input box.')); const resolvedCommentBorder = registerColor('editorCommentsWidget.resolvedBorder', { dark: resolvedCommentViewIcon, light: resolvedCommentViewIcon, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('resolvedCommentBorder', 'Color of borders and arrow for resolved comments.')); const unresolvedCommentBorder = registerColor('editorCommentsWidget.unresolvedBorder', { dark: unresolvedCommentViewIcon, light: unresolvedCommentViewIcon, hcDark: contrastBorder, hcLight: contrastBorder }, nls.localize('unresolvedCommentBorder', 'Color of borders and arrow for unresolved comments.')); -export const commentThreadRangeBackground = registerColor('editorCommentsWidget.rangeBackground', { dark: transparent(unresolvedCommentBorder, .1), light: transparent(unresolvedCommentBorder, .1), hcDark: transparent(unresolvedCommentBorder, .1), hcLight: transparent(unresolvedCommentBorder, .1) }, nls.localize('commentThreadRangeBackground', 'Color of background for comment ranges.')); -export const commentThreadRangeActiveBackground = registerColor('editorCommentsWidget.rangeActiveBackground', { dark: transparent(unresolvedCommentBorder, .1), light: transparent(unresolvedCommentBorder, .1), hcDark: transparent(unresolvedCommentBorder, .1), hcLight: transparent(unresolvedCommentBorder, .1) }, nls.localize('commentThreadActiveRangeBackground', 'Color of background for currently selected or hovered comment range.')); +export const commentThreadRangeBackground = registerColor('editorCommentsWidget.rangeBackground', transparent(unresolvedCommentBorder, .1), nls.localize('commentThreadRangeBackground', 'Color of background for comment ranges.')); +export const commentThreadRangeActiveBackground = registerColor('editorCommentsWidget.rangeActiveBackground', transparent(unresolvedCommentBorder, .1), nls.localize('commentThreadActiveRangeBackground', 'Color of background for currently selected or hovered comment range.')); const commentThreadStateBorderColors = new Map([ [languages.CommentThreadState.Unresolved, unresolvedCommentBorder], diff --git a/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts b/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts index 92b52ac5402..725b522f956 100644 --- a/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentGlyphWidget.ts @@ -14,11 +14,11 @@ import { IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { CommentThreadState } from 'vs/editor/common/languages'; export const overviewRulerCommentingRangeForeground = registerColor('editorGutter.commentRangeForeground', { dark: opaque(listInactiveSelectionBackground, editorBackground), light: darken(opaque(listInactiveSelectionBackground, editorBackground), .05), hcDark: Color.white, hcLight: Color.black }, nls.localize('editorGutterCommentRangeForeground', 'Editor gutter decoration color for commenting ranges. This color should be opaque.')); -const overviewRulerCommentForeground = registerColor('editorOverviewRuler.commentForeground', { dark: overviewRulerCommentingRangeForeground, light: overviewRulerCommentingRangeForeground, hcDark: overviewRulerCommentingRangeForeground, hcLight: overviewRulerCommentingRangeForeground }, nls.localize('editorOverviewRuler.commentForeground', 'Editor overview ruler decoration color for resolved comments. This color should be opaque.')); -const overviewRulerCommentUnresolvedForeground = registerColor('editorOverviewRuler.commentUnresolvedForeground', { dark: overviewRulerCommentForeground, light: overviewRulerCommentForeground, hcDark: overviewRulerCommentForeground, hcLight: overviewRulerCommentForeground }, nls.localize('editorOverviewRuler.commentUnresolvedForeground', 'Editor overview ruler decoration color for unresolved comments. This color should be opaque.')); +const overviewRulerCommentForeground = registerColor('editorOverviewRuler.commentForeground', overviewRulerCommentingRangeForeground, nls.localize('editorOverviewRuler.commentForeground', 'Editor overview ruler decoration color for resolved comments. This color should be opaque.')); +const overviewRulerCommentUnresolvedForeground = registerColor('editorOverviewRuler.commentUnresolvedForeground', overviewRulerCommentForeground, nls.localize('editorOverviewRuler.commentUnresolvedForeground', 'Editor overview ruler decoration color for unresolved comments. This color should be opaque.')); const editorGutterCommentGlyphForeground = registerColor('editorGutter.commentGlyphForeground', { dark: editorForeground, light: editorForeground, hcDark: Color.black, hcLight: Color.white }, nls.localize('editorGutterCommentGlyphForeground', 'Editor gutter decoration color for commenting glyphs.')); -registerColor('editorGutter.commentUnresolvedGlyphForeground', { dark: editorGutterCommentGlyphForeground, light: editorGutterCommentGlyphForeground, hcDark: editorGutterCommentGlyphForeground, hcLight: editorGutterCommentGlyphForeground }, nls.localize('editorGutterCommentUnresolvedGlyphForeground', 'Editor gutter decoration color for commenting glyphs for unresolved comment threads.')); +registerColor('editorGutter.commentUnresolvedGlyphForeground', editorGutterCommentGlyphForeground, nls.localize('editorGutterCommentUnresolvedGlyphForeground', 'Editor gutter decoration color for commenting glyphs for unresolved comment threads.')); export class CommentGlyphWidget { public static description = 'comment-glyph-widget'; diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts index 1b53d40047f..d29256d9072 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -869,8 +869,8 @@ registerThemingParticipant((theme, collector) => { } }); -export const debugIconBreakpointForeground = registerColor('debugIcon.breakpointForeground', { dark: '#E51400', light: '#E51400', hcDark: '#E51400', hcLight: '#E51400' }, nls.localize('debugIcon.breakpointForeground', 'Icon color for breakpoints.')); -const debugIconBreakpointDisabledForeground = registerColor('debugIcon.breakpointDisabledForeground', { dark: '#848484', light: '#848484', hcDark: '#848484', hcLight: '#848484' }, nls.localize('debugIcon.breakpointDisabledForeground', 'Icon color for disabled breakpoints.')); -const debugIconBreakpointUnverifiedForeground = registerColor('debugIcon.breakpointUnverifiedForeground', { dark: '#848484', light: '#848484', hcDark: '#848484', hcLight: '#848484' }, nls.localize('debugIcon.breakpointUnverifiedForeground', 'Icon color for unverified breakpoints.')); +export const debugIconBreakpointForeground = registerColor('debugIcon.breakpointForeground', '#E51400', nls.localize('debugIcon.breakpointForeground', 'Icon color for breakpoints.')); +const debugIconBreakpointDisabledForeground = registerColor('debugIcon.breakpointDisabledForeground', '#848484', nls.localize('debugIcon.breakpointDisabledForeground', 'Icon color for disabled breakpoints.')); +const debugIconBreakpointUnverifiedForeground = registerColor('debugIcon.breakpointUnverifiedForeground', '#848484', nls.localize('debugIcon.breakpointUnverifiedForeground', 'Icon color for unverified breakpoints.')); const debugIconBreakpointCurrentStackframeForeground = registerColor('debugIcon.breakpointCurrentStackframeForeground', { dark: '#FFCC00', light: '#BE8700', hcDark: '#FFCC00', hcLight: '#BE8700' }, nls.localize('debugIcon.breakpointCurrentStackframeForeground', 'Icon color for the current breakpoint stack frame.')); -const debugIconBreakpointStackframeForeground = registerColor('debugIcon.breakpointStackframeForeground', { dark: '#89D185', light: '#89D185', hcDark: '#89D185', hcLight: '#89D185' }, nls.localize('debugIcon.breakpointStackframeForeground', 'Icon color for all breakpoint stack frames.')); +const debugIconBreakpointStackframeForeground = registerColor('debugIcon.breakpointStackframeForeground', '#89D185', nls.localize('debugIcon.breakpointStackframeForeground', 'Icon color for all breakpoint stack frames.')); diff --git a/src/vs/workbench/contrib/debug/browser/debugColors.ts b/src/vs/workbench/contrib/debug/browser/debugColors.ts index 654a9b983b6..19b47280524 100644 --- a/src/vs/workbench/contrib/debug/browser/debugColors.ts +++ b/src/vs/workbench/contrib/debug/browser/debugColors.ts @@ -18,12 +18,7 @@ export const debugToolBarBackground = registerColor('debugToolBar.background', { hcLight: '#FFFFFF' }, localize('debugToolBarBackground', "Debug toolbar background color.")); -export const debugToolBarBorder = registerColor('debugToolBar.border', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, localize('debugToolBarBorder', "Debug toolbar border color.")); +export const debugToolBarBorder = registerColor('debugToolBar.border', null, localize('debugToolBarBorder', "Debug toolbar border color.")); export const debugIconStartForeground = registerColor('debugIcon.startForeground', { dark: '#89D185', @@ -44,15 +39,15 @@ export function registerColors() { const debugViewExceptionLabelForeground = registerColor('debugView.exceptionLabelForeground', { dark: foreground, light: '#FFF', hcDark: foreground, hcLight: foreground }, 'Foreground color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); const debugViewExceptionLabelBackground = registerColor('debugView.exceptionLabelBackground', { dark: '#6C2022', light: '#A31515', hcDark: '#6C2022', hcLight: '#A31515' }, 'Background color for a label shown in the CALL STACK view when the debugger breaks on an exception.'); - const debugViewStateLabelForeground = registerColor('debugView.stateLabelForeground', { dark: foreground, light: foreground, hcDark: foreground, hcLight: foreground }, 'Foreground color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); - const debugViewStateLabelBackground = registerColor('debugView.stateLabelBackground', { dark: '#88888844', light: '#88888844', hcDark: '#88888844', hcLight: '#88888844' }, 'Background color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); - const debugViewValueChangedHighlight = registerColor('debugView.valueChangedHighlight', { dark: '#569CD6', light: '#569CD6', hcDark: '#569CD6', hcLight: '#569CD6' }, 'Color used to highlight value changes in the debug views (ie. in the Variables view).'); + const debugViewStateLabelForeground = registerColor('debugView.stateLabelForeground', foreground, 'Foreground color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); + const debugViewStateLabelBackground = registerColor('debugView.stateLabelBackground', '#88888844', 'Background color for a label in the CALL STACK view showing the current session\'s or thread\'s state.'); + const debugViewValueChangedHighlight = registerColor('debugView.valueChangedHighlight', '#569CD6', 'Color used to highlight value changes in the debug views (ie. in the Variables view).'); const debugConsoleInfoForeground = registerColor('debugConsole.infoForeground', { dark: editorInfoForeground, light: editorInfoForeground, hcDark: foreground, hcLight: foreground }, 'Foreground color for info messages in debug REPL console.'); const debugConsoleWarningForeground = registerColor('debugConsole.warningForeground', { dark: editorWarningForeground, light: editorWarningForeground, hcDark: '#008000', hcLight: editorWarningForeground }, 'Foreground color for warning messages in debug REPL console.'); - const debugConsoleErrorForeground = registerColor('debugConsole.errorForeground', { dark: errorForeground, light: errorForeground, hcDark: errorForeground, hcLight: errorForeground }, 'Foreground color for error messages in debug REPL console.'); - const debugConsoleSourceForeground = registerColor('debugConsole.sourceForeground', { dark: foreground, light: foreground, hcDark: foreground, hcLight: foreground }, 'Foreground color for source filenames in debug REPL console.'); - const debugConsoleInputIconForeground = registerColor('debugConsoleInputIcon.foreground', { dark: foreground, light: foreground, hcDark: foreground, hcLight: foreground }, 'Foreground color for debug console input marker icon.'); + const debugConsoleErrorForeground = registerColor('debugConsole.errorForeground', errorForeground, 'Foreground color for error messages in debug REPL console.'); + const debugConsoleSourceForeground = registerColor('debugConsole.sourceForeground', foreground, 'Foreground color for source filenames in debug REPL console.'); + const debugConsoleInputIconForeground = registerColor('debugConsoleInputIcon.foreground', foreground, 'Foreground color for debug console input marker icon.'); const debugIconPauseForeground = registerColor('debugIcon.pauseForeground', { dark: '#75BEFF', diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 7ef1947ea2c..5e3d5e9d583 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -66,12 +66,7 @@ export const debugInlineForeground = registerColor('editor.inlineValuesForegroun hcLight: '#00000080' }, nls.localize('editor.inlineValuesForeground', "Color for the debug inline value text.")); -export const debugInlineBackground = registerColor('editor.inlineValuesBackground', { - dark: '#ffc80033', - light: '#ffc80033', - hcDark: '#ffc80033', - hcLight: '#ffc80033' -}, nls.localize('editor.inlineValuesBackground', "Color for the debug inline value background.")); +export const debugInlineBackground = registerColor('editor.inlineValuesBackground', '#ffc80033', nls.localize('editor.inlineValuesBackground', "Color for the debug inline value background.")); class InlineSegment { constructor(public column: number, public text: string) { diff --git a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts index 1b920801dec..7c9b48503a2 100644 --- a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts @@ -24,7 +24,7 @@ const $ = dom.$; // theming -const debugExceptionWidgetBorder = registerColor('debugExceptionWidget.border', { dark: '#a31515', light: '#a31515', hcDark: '#a31515', hcLight: '#a31515' }, nls.localize('debugExceptionWidgetBorder', 'Exception widget border color.')); +const debugExceptionWidgetBorder = registerColor('debugExceptionWidget.border', '#a31515', nls.localize('debugExceptionWidgetBorder', 'Exception widget border color.')); const debugExceptionWidgetBackground = registerColor('debugExceptionWidget.background', { dark: '#420b0d', light: '#f1dfde', hcDark: '#420b0d', hcLight: '#f1dfde' }, nls.localize('debugExceptionWidgetBackground', 'Exception widget background color.')); export class ExceptionWidget extends ZoneWidget { diff --git a/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts b/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts index 1968c0ccdeb..54c881dfb53 100644 --- a/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts +++ b/src/vs/workbench/contrib/debug/browser/statusbarColorProvider.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { ColorTransformType, asCssVariable, asCssVariableName, registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { asCssVariable, asCssVariableName, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IDebugService, State, IDebugSession, IDebugConfiguration } from 'vs/workbench/contrib/debug/common/debug'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -31,21 +31,11 @@ export const STATUS_BAR_DEBUGGING_FOREGROUND = registerColor('statusBar.debuggin hcLight: '#FFFFFF' }, localize('statusBarDebuggingForeground', "Status bar foreground color when a program is being debugged. The status bar is shown in the bottom of the window")); -export const STATUS_BAR_DEBUGGING_BORDER = registerColor('statusBar.debuggingBorder', { - dark: STATUS_BAR_BORDER, - light: STATUS_BAR_BORDER, - hcDark: STATUS_BAR_BORDER, - hcLight: STATUS_BAR_BORDER -}, localize('statusBarDebuggingBorder', "Status bar border color separating to the sidebar and editor when a program is being debugged. The status bar is shown in the bottom of the window")); +export const STATUS_BAR_DEBUGGING_BORDER = registerColor('statusBar.debuggingBorder', STATUS_BAR_BORDER, localize('statusBarDebuggingBorder', "Status bar border color separating to the sidebar and editor when a program is being debugged. The status bar is shown in the bottom of the window")); export const COMMAND_CENTER_DEBUGGING_BACKGROUND = registerColor( 'commandCenter.debuggingBackground', - { - dark: { value: STATUS_BAR_DEBUGGING_BACKGROUND, op: ColorTransformType.Transparent, factor: 0.258 }, - hcDark: { value: STATUS_BAR_DEBUGGING_BACKGROUND, op: ColorTransformType.Transparent, factor: 0.258 }, - light: { value: STATUS_BAR_DEBUGGING_BACKGROUND, op: ColorTransformType.Transparent, factor: 0.258 }, - hcLight: { value: STATUS_BAR_DEBUGGING_BACKGROUND, op: ColorTransformType.Transparent, factor: 0.258 } - }, + transparent(STATUS_BAR_DEBUGGING_BACKGROUND, 0.258), localize('commandCenter-activeBackground', "Command center background color when a program is being debugged"), true ); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 7141e8effe4..0b5323a75a0 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -3092,12 +3092,7 @@ registerColor('extensionButton.hoverBackground', { hcLight: null }, localize('extensionButtonHoverBackground', "Button background hover color for extension actions.")); -registerColor('extensionButton.separator', { - dark: buttonSeparator, - light: buttonSeparator, - hcDark: buttonSeparator, - hcLight: buttonSeparator -}, localize('extensionButtonSeparator', "Button separator color for extension actions")); +registerColor('extensionButton.separator', buttonSeparator, localize('extensionButtonSeparator', "Button separator color for extension actions")); export const extensionButtonProminentBackground = registerColor('extensionButton.prominentBackground', { dark: buttonBackground, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 3a92bc9aa69..4b53d91cbc5 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -824,7 +824,7 @@ export class ExtensionRecommendationWidget extends ExtensionWidget { } export const extensionRatingIconColor = registerColor('extensionIcon.starForeground', { light: '#DF6100', dark: '#FF8E00', hcDark: '#FF8E00', hcLight: textLinkForeground }, localize('extensionIconStarForeground', "The icon color for extension ratings."), true); -export const extensionVerifiedPublisherIconColor = registerColor('extensionIcon.verifiedForeground', { dark: textLinkForeground, light: textLinkForeground, hcDark: textLinkForeground, hcLight: textLinkForeground }, localize('extensionIconVerifiedForeground', "The icon color for extension verified publisher."), true); +export const extensionVerifiedPublisherIconColor = registerColor('extensionIcon.verifiedForeground', textLinkForeground, localize('extensionIconVerifiedForeground', "The icon color for extension verified publisher."), true); export const extensionPreReleaseIconColor = registerColor('extensionIcon.preReleaseForeground', { dark: '#1d9271', light: '#1d9271', hcDark: '#1d9271', hcLight: textLinkForeground }, localize('extensionPreReleaseForeground', "The icon color for pre-release extension."), true); export const extensionSponsorIconColor = registerColor('extensionIcon.sponsorForeground', { light: '#B51E78', dark: '#D758B3', hcDark: null, hcLight: '#B51E78' }, localize('extensionIcon.sponsorForeground', "The icon color for extension sponsor."), true); diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index 3ab26fb4335..ee60b223f56 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -124,18 +124,18 @@ export const MENU_INLINE_CHAT_WIDGET_STATUS = MenuId.for('inlineChatWidget.statu // --- colors -export const inlineChatForeground = registerColor('inlineChat.foreground', { dark: editorWidgetForeground, light: editorWidgetForeground, hcDark: editorWidgetForeground, hcLight: editorWidgetForeground }, localize('inlineChat.foreground', "Foreground color of the interactive editor widget")); -export const inlineChatBackground = registerColor('inlineChat.background', { dark: editorWidgetBackground, light: editorWidgetBackground, hcDark: editorWidgetBackground, hcLight: editorWidgetBackground }, localize('inlineChat.background', "Background color of the interactive editor widget")); -export const inlineChatBorder = registerColor('inlineChat.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, localize('inlineChat.border', "Border color of the interactive editor widget")); -export const inlineChatShadow = registerColor('inlineChat.shadow', { dark: widgetShadow, light: widgetShadow, hcDark: widgetShadow, hcLight: widgetShadow }, localize('inlineChat.shadow', "Shadow color of the interactive editor widget")); -export const inlineChatInputBorder = registerColor('inlineChatInput.border', { dark: editorWidgetBorder, light: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, localize('inlineChatInput.border', "Border color of the interactive editor input")); -export const inlineChatInputFocusBorder = registerColor('inlineChatInput.focusBorder', { dark: focusBorder, light: focusBorder, hcDark: focusBorder, hcLight: focusBorder }, localize('inlineChatInput.focusBorder', "Border color of the interactive editor input when focused")); -export const inlineChatInputPlaceholderForeground = registerColor('inlineChatInput.placeholderForeground', { dark: inputPlaceholderForeground, light: inputPlaceholderForeground, hcDark: inputPlaceholderForeground, hcLight: inputPlaceholderForeground }, localize('inlineChatInput.placeholderForeground', "Foreground color of the interactive editor input placeholder")); -export const inlineChatInputBackground = registerColor('inlineChatInput.background', { dark: inputBackground, light: inputBackground, hcDark: inputBackground, hcLight: inputBackground }, localize('inlineChatInput.background', "Background color of the interactive editor input")); - -export const inlineChatDiffInserted = registerColor('inlineChatDiff.inserted', { dark: transparent(diffInserted, .5), light: transparent(diffInserted, .5), hcDark: transparent(diffInserted, .5), hcLight: transparent(diffInserted, .5) }, localize('inlineChatDiff.inserted', "Background color of inserted text in the interactive editor input")); +export const inlineChatForeground = registerColor('inlineChat.foreground', editorWidgetForeground, localize('inlineChat.foreground', "Foreground color of the interactive editor widget")); +export const inlineChatBackground = registerColor('inlineChat.background', editorWidgetBackground, localize('inlineChat.background', "Background color of the interactive editor widget")); +export const inlineChatBorder = registerColor('inlineChat.border', editorWidgetBorder, localize('inlineChat.border', "Border color of the interactive editor widget")); +export const inlineChatShadow = registerColor('inlineChat.shadow', widgetShadow, localize('inlineChat.shadow', "Shadow color of the interactive editor widget")); +export const inlineChatInputBorder = registerColor('inlineChatInput.border', editorWidgetBorder, localize('inlineChatInput.border', "Border color of the interactive editor input")); +export const inlineChatInputFocusBorder = registerColor('inlineChatInput.focusBorder', focusBorder, localize('inlineChatInput.focusBorder', "Border color of the interactive editor input when focused")); +export const inlineChatInputPlaceholderForeground = registerColor('inlineChatInput.placeholderForeground', inputPlaceholderForeground, localize('inlineChatInput.placeholderForeground', "Foreground color of the interactive editor input placeholder")); +export const inlineChatInputBackground = registerColor('inlineChatInput.background', inputBackground, localize('inlineChatInput.background', "Background color of the interactive editor input")); + +export const inlineChatDiffInserted = registerColor('inlineChatDiff.inserted', transparent(diffInserted, .5), localize('inlineChatDiff.inserted', "Background color of inserted text in the interactive editor input")); export const overviewRulerInlineChatDiffInserted = registerColor('editorOverviewRuler.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorOverviewRuler.inlineChatInserted', 'Overview ruler marker color for inline chat inserted content.')); export const minimapInlineChatDiffInserted = registerColor('editorOverviewRuler.inlineChatInserted', { dark: transparent(diffInserted, 0.6), light: transparent(diffInserted, 0.8), hcDark: transparent(diffInserted, 0.6), hcLight: transparent(diffInserted, 0.8) }, localize('editorOverviewRuler.inlineChatInserted', 'Overview ruler marker color for inline chat inserted content.')); -export const inlineChatDiffRemoved = registerColor('inlineChatDiff.removed', { dark: transparent(diffRemoved, .5), light: transparent(diffRemoved, .5), hcDark: transparent(diffRemoved, .5), hcLight: transparent(diffRemoved, .5) }, localize('inlineChatDiff.removed', "Background color of removed text in the interactive editor input")); +export const inlineChatDiffRemoved = registerColor('inlineChatDiff.removed', transparent(diffRemoved, .5), localize('inlineChatDiff.removed', "Background color of removed text in the interactive editor input")); export const overviewRulerInlineChatDiffRemoved = registerColor('editorOverviewRuler.inlineChatRemoved', { dark: transparent(diffRemoved, 0.6), light: transparent(diffRemoved, 0.8), hcDark: transparent(diffRemoved, 0.6), hcLight: transparent(diffRemoved, 0.8) }, localize('editorOverviewRuler.inlineChatRemoved', 'Overview ruler marker color for inline chat removed content.')); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts index cc6e5700812..f8d1d8b6fcd 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/colors.ts @@ -8,7 +8,7 @@ import { mergeCurrentHeaderBackground, mergeIncomingHeaderBackground, registerCo export const diff = registerColor( 'mergeEditor.change.background', - { dark: '#9bb95533', light: '#9bb95533', hcDark: '#9bb95533', hcLight: '#9bb95533', }, + '#9bb95533', localize('mergeEditor.change.background', 'The background color for changes.') ); @@ -38,49 +38,49 @@ export const conflictBorderUnhandledUnfocused = registerColor( export const conflictBorderUnhandledFocused = registerColor( 'mergeEditor.conflict.unhandledFocused.border', - { dark: '#ffa600', light: '#ffa600', hcDark: '#ffa600', hcLight: '#ffa600', }, + '#ffa600', localize('mergeEditor.conflict.unhandledFocused.border', 'The border color of unhandled focused conflicts.') ); export const conflictBorderHandledUnfocused = registerColor( 'mergeEditor.conflict.handledUnfocused.border', - { dark: '#86868649', light: '#86868649', hcDark: '#86868649', hcLight: '#86868649', }, + '#86868649', localize('mergeEditor.conflict.handledUnfocused.border', 'The border color of handled unfocused conflicts.') ); export const conflictBorderHandledFocused = registerColor( 'mergeEditor.conflict.handledFocused.border', - { dark: '#c1c1c1cc', light: '#c1c1c1cc', hcDark: '#c1c1c1cc', hcLight: '#c1c1c1cc', }, + '#c1c1c1cc', localize('mergeEditor.conflict.handledFocused.border', 'The border color of handled focused conflicts.') ); export const handledConflictMinimapOverViewRulerColor = registerColor( 'mergeEditor.conflict.handled.minimapOverViewRuler', - { dark: '#adaca8ee', light: '#adaca8ee', hcDark: '#adaca8ee', hcLight: '#adaca8ee', }, + '#adaca8ee', localize('mergeEditor.conflict.handled.minimapOverViewRuler', 'The foreground color for changes in input 1.') ); export const unhandledConflictMinimapOverViewRulerColor = registerColor( 'mergeEditor.conflict.unhandled.minimapOverViewRuler', - { dark: '#fcba03FF', light: '#fcba03FF', hcDark: '#fcba03FF', hcLight: '#fcba03FF', }, + '#fcba03FF', localize('mergeEditor.conflict.unhandled.minimapOverViewRuler', 'The foreground color for changes in input 1.') ); export const conflictingLinesBackground = registerColor( 'mergeEditor.conflictingLines.background', - { dark: '#ffea0047', light: '#ffea0047', hcDark: '#ffea0047', hcLight: '#ffea0047', }, + '#ffea0047', localize('mergeEditor.conflictingLines.background', 'The background of the "Conflicting Lines" text.') ); const contentTransparency = 0.4; export const conflictInput1Background = registerColor( 'mergeEditor.conflict.input1.background', - { dark: transparent(mergeCurrentHeaderBackground, contentTransparency), light: transparent(mergeCurrentHeaderBackground, contentTransparency), hcDark: transparent(mergeCurrentHeaderBackground, contentTransparency), hcLight: transparent(mergeCurrentHeaderBackground, contentTransparency) }, + transparent(mergeCurrentHeaderBackground, contentTransparency), localize('mergeEditor.conflict.input1.background', 'The background color of decorations in input 1.') ); export const conflictInput2Background = registerColor( 'mergeEditor.conflict.input2.background', - { dark: transparent(mergeIncomingHeaderBackground, contentTransparency), light: transparent(mergeIncomingHeaderBackground, contentTransparency), hcDark: transparent(mergeIncomingHeaderBackground, contentTransparency), hcLight: transparent(mergeIncomingHeaderBackground, contentTransparency) }, + transparent(mergeIncomingHeaderBackground, contentTransparency), localize('mergeEditor.conflict.input2.background', 'The background color of decorations in input 2.') ); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 1e59c4cc138..072a2a70593 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -3227,54 +3227,19 @@ export const notebookCellBorder = registerColor('notebook.cellBorderColor', { hcLight: PANEL_BORDER }, nls.localize('notebook.cellBorderColor', "The border color for notebook cells.")); -export const focusedEditorBorderColor = registerColor('notebook.focusedEditorBorder', { - light: focusBorder, - dark: focusBorder, - hcDark: focusBorder, - hcLight: focusBorder -}, nls.localize('notebook.focusedEditorBorder', "The color of the notebook cell editor border.")); - -export const cellStatusIconSuccess = registerColor('notebookStatusSuccessIcon.foreground', { - light: debugIconStartForeground, - dark: debugIconStartForeground, - hcDark: debugIconStartForeground, - hcLight: debugIconStartForeground -}, nls.localize('notebookStatusSuccessIcon.foreground', "The error icon color of notebook cells in the cell status bar.")); - -export const runningCellRulerDecorationColor = registerColor('notebookEditorOverviewRuler.runningCellForeground', { - light: debugIconStartForeground, - dark: debugIconStartForeground, - hcDark: debugIconStartForeground, - hcLight: debugIconStartForeground -}, nls.localize('notebookEditorOverviewRuler.runningCellForeground', "The color of the running cell decoration in the notebook editor overview ruler.")); - -export const cellStatusIconError = registerColor('notebookStatusErrorIcon.foreground', { - light: errorForeground, - dark: errorForeground, - hcDark: errorForeground, - hcLight: errorForeground -}, nls.localize('notebookStatusErrorIcon.foreground', "The error icon color of notebook cells in the cell status bar.")); - -export const cellStatusIconRunning = registerColor('notebookStatusRunningIcon.foreground', { - light: foreground, - dark: foreground, - hcDark: foreground, - hcLight: foreground -}, nls.localize('notebookStatusRunningIcon.foreground', "The running icon color of notebook cells in the cell status bar.")); - -export const notebookOutputContainerBorderColor = registerColor('notebook.outputContainerBorderColor', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, nls.localize('notebook.outputContainerBorderColor', "The border color of the notebook output container.")); +export const focusedEditorBorderColor = registerColor('notebook.focusedEditorBorder', focusBorder, nls.localize('notebook.focusedEditorBorder', "The color of the notebook cell editor border.")); -export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, nls.localize('notebook.outputContainerBackgroundColor', "The color of the notebook output container background.")); +export const cellStatusIconSuccess = registerColor('notebookStatusSuccessIcon.foreground', debugIconStartForeground, nls.localize('notebookStatusSuccessIcon.foreground', "The error icon color of notebook cells in the cell status bar.")); + +export const runningCellRulerDecorationColor = registerColor('notebookEditorOverviewRuler.runningCellForeground', debugIconStartForeground, nls.localize('notebookEditorOverviewRuler.runningCellForeground', "The color of the running cell decoration in the notebook editor overview ruler.")); + +export const cellStatusIconError = registerColor('notebookStatusErrorIcon.foreground', errorForeground, nls.localize('notebookStatusErrorIcon.foreground', "The error icon color of notebook cells in the cell status bar.")); + +export const cellStatusIconRunning = registerColor('notebookStatusRunningIcon.foreground', foreground, nls.localize('notebookStatusRunningIcon.foreground', "The running icon color of notebook cells in the cell status bar.")); + +export const notebookOutputContainerBorderColor = registerColor('notebook.outputContainerBorderColor', null, nls.localize('notebook.outputContainerBorderColor', "The border color of the notebook output container.")); + +export const notebookOutputContainerColor = registerColor('notebook.outputContainerBackgroundColor', null, nls.localize('notebook.outputContainerBackgroundColor', "The color of the notebook output container background.")); // TODO@rebornix currently also used for toolbar border, if we keep all of this, pick a generic name export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparator', { @@ -3284,12 +3249,7 @@ export const CELL_TOOLBAR_SEPERATOR = registerColor('notebook.cellToolbarSeparat hcLight: contrastBorder }, nls.localize('notebook.cellToolbarSeparator', "The color of the separator in the cell bottom toolbar")); -export const focusedCellBackground = registerColor('notebook.focusedCellBackground', { - dark: null, - light: null, - hcDark: null, - hcLight: null -}, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused.")); +export const focusedCellBackground = registerColor('notebook.focusedCellBackground', null, nls.localize('focusedCellBackground', "The background color of a cell when the cell is focused.")); export const selectedCellBackground = registerColor('notebook.selectedCellBackground', { dark: listInactiveSelectionBackground, @@ -3320,19 +3280,9 @@ export const inactiveSelectedCellBorder = registerColor('notebook.inactiveSelect hcLight: focusBorder }, nls.localize('notebook.inactiveSelectedCellBorder', "The color of the cell's borders when multiple cells are selected.")); -export const focusedCellBorder = registerColor('notebook.focusedCellBorder', { - dark: focusBorder, - light: focusBorder, - hcDark: focusBorder, - hcLight: focusBorder -}, nls.localize('notebook.focusedCellBorder', "The color of the cell's focus indicator borders when the cell is focused.")); +export const focusedCellBorder = registerColor('notebook.focusedCellBorder', focusBorder, nls.localize('notebook.focusedCellBorder', "The color of the cell's focus indicator borders when the cell is focused.")); -export const inactiveFocusedCellBorder = registerColor('notebook.inactiveFocusedCellBorder', { - dark: notebookCellBorder, - light: notebookCellBorder, - hcDark: notebookCellBorder, - hcLight: notebookCellBorder -}, nls.localize('notebook.inactiveFocusedCellBorder', "The color of the cell's top and bottom border when a cell is focused while the primary focus is outside of the editor.")); +export const inactiveFocusedCellBorder = registerColor('notebook.inactiveFocusedCellBorder', notebookCellBorder, nls.localize('notebook.inactiveFocusedCellBorder', "The color of the cell's top and bottom border when a cell is focused while the primary focus is outside of the editor.")); export const cellStatusBarItemHover = registerColor('notebook.cellStatusBarItemHoverBackground', { light: new Color(new RGBA(0, 0, 0, 0.08)), @@ -3341,33 +3291,13 @@ export const cellStatusBarItemHover = registerColor('notebook.cellStatusBarItemH hcLight: new Color(new RGBA(0, 0, 0, 0.08)), }, nls.localize('notebook.cellStatusBarItemHoverBackground', "The background color of notebook cell status bar items.")); -export const cellInsertionIndicator = registerColor('notebook.cellInsertionIndicator', { - light: focusBorder, - dark: focusBorder, - hcDark: focusBorder, - hcLight: focusBorder -}, nls.localize('notebook.cellInsertionIndicator', "The color of the notebook cell insertion indicator.")); - -export const listScrollbarSliderBackground = registerColor('notebookScrollbarSlider.background', { - dark: scrollbarSliderBackground, - light: scrollbarSliderBackground, - hcDark: scrollbarSliderBackground, - hcLight: scrollbarSliderBackground -}, nls.localize('notebookScrollbarSliderBackground', "Notebook scrollbar slider background color.")); - -export const listScrollbarSliderHoverBackground = registerColor('notebookScrollbarSlider.hoverBackground', { - dark: scrollbarSliderHoverBackground, - light: scrollbarSliderHoverBackground, - hcDark: scrollbarSliderHoverBackground, - hcLight: scrollbarSliderHoverBackground -}, nls.localize('notebookScrollbarSliderHoverBackground', "Notebook scrollbar slider background color when hovering.")); - -export const listScrollbarSliderActiveBackground = registerColor('notebookScrollbarSlider.activeBackground', { - dark: scrollbarSliderActiveBackground, - light: scrollbarSliderActiveBackground, - hcDark: scrollbarSliderActiveBackground, - hcLight: scrollbarSliderActiveBackground -}, nls.localize('notebookScrollbarSliderActiveBackground', "Notebook scrollbar slider background color when clicked on.")); +export const cellInsertionIndicator = registerColor('notebook.cellInsertionIndicator', focusBorder, nls.localize('notebook.cellInsertionIndicator', "The color of the notebook cell insertion indicator.")); + +export const listScrollbarSliderBackground = registerColor('notebookScrollbarSlider.background', scrollbarSliderBackground, nls.localize('notebookScrollbarSliderBackground', "Notebook scrollbar slider background color.")); + +export const listScrollbarSliderHoverBackground = registerColor('notebookScrollbarSlider.hoverBackground', scrollbarSliderHoverBackground, nls.localize('notebookScrollbarSliderHoverBackground', "Notebook scrollbar slider background color when hovering.")); + +export const listScrollbarSliderActiveBackground = registerColor('notebookScrollbarSlider.activeBackground', scrollbarSliderActiveBackground, nls.localize('notebookScrollbarSliderActiveBackground', "Notebook scrollbar slider background color when clicked on.")); export const cellSymbolHighlight = registerColor('notebook.symbolHighlightBackground', { dark: Color.fromHex('#ffffff0b'), diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index d1bbf48e3e8..6f3c5086186 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -1248,8 +1248,8 @@ class AccessibilityProvider implements IListAccessibilityProvider { const foregroundColor = theme.getColor(foreground); diff --git a/src/vs/workbench/contrib/preferences/common/settingsEditorColorRegistry.ts b/src/vs/workbench/contrib/preferences/common/settingsEditorColorRegistry.ts index 29841a28248..bd5e4ef29c9 100644 --- a/src/vs/workbench/contrib/preferences/common/settingsEditorColorRegistry.ts +++ b/src/vs/workbench/contrib/preferences/common/settingsEditorColorRegistry.ts @@ -10,36 +10,36 @@ import { PANEL_BORDER } from 'vs/workbench/common/theme'; // General setting colors export const settingsHeaderForeground = registerColor('settings.headerForeground', { light: '#444444', dark: '#e7e7e7', hcDark: '#ffffff', hcLight: '#292929' }, localize('headerForeground', "The foreground color for a section header or active title.")); -export const settingsHeaderHoverForeground = registerColor('settings.settingsHeaderHoverForeground', { light: transparent(settingsHeaderForeground, 0.7), dark: transparent(settingsHeaderForeground, 0.7), hcDark: transparent(settingsHeaderForeground, 0.7), hcLight: transparent(settingsHeaderForeground, 0.7) }, localize('settingsHeaderHoverForeground', "The foreground color for a section header or hovered title.")); +export const settingsHeaderHoverForeground = registerColor('settings.settingsHeaderHoverForeground', transparent(settingsHeaderForeground, 0.7), localize('settingsHeaderHoverForeground', "The foreground color for a section header or hovered title.")); export const modifiedItemIndicator = registerColor('settings.modifiedItemIndicator', { light: new Color(new RGBA(102, 175, 224)), dark: new Color(new RGBA(12, 125, 157)), hcDark: new Color(new RGBA(0, 73, 122)), hcLight: new Color(new RGBA(102, 175, 224)), }, localize('modifiedItemForeground', "The color of the modified setting indicator.")); -export const settingsHeaderBorder = registerColor('settings.headerBorder', { dark: PANEL_BORDER, light: PANEL_BORDER, hcDark: PANEL_BORDER, hcLight: PANEL_BORDER }, localize('settingsHeaderBorder', "The color of the header container border.")); -export const settingsSashBorder = registerColor('settings.sashBorder', { dark: PANEL_BORDER, light: PANEL_BORDER, hcDark: PANEL_BORDER, hcLight: PANEL_BORDER }, localize('settingsSashBorder', "The color of the Settings editor splitview sash border.")); +export const settingsHeaderBorder = registerColor('settings.headerBorder', PANEL_BORDER, localize('settingsHeaderBorder', "The color of the header container border.")); +export const settingsSashBorder = registerColor('settings.sashBorder', PANEL_BORDER, localize('settingsSashBorder', "The color of the Settings editor splitview sash border.")); // Enum control colors -export const settingsSelectBackground = registerColor(`settings.dropdownBackground`, { dark: selectBackground, light: selectBackground, hcDark: selectBackground, hcLight: selectBackground }, localize('settingsDropdownBackground', "Settings editor dropdown background.")); -export const settingsSelectForeground = registerColor('settings.dropdownForeground', { dark: selectForeground, light: selectForeground, hcDark: selectForeground, hcLight: selectForeground }, localize('settingsDropdownForeground', "Settings editor dropdown foreground.")); -export const settingsSelectBorder = registerColor('settings.dropdownBorder', { dark: selectBorder, light: selectBorder, hcDark: selectBorder, hcLight: selectBorder }, localize('settingsDropdownBorder', "Settings editor dropdown border.")); -export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', { dark: editorWidgetBorder, light: editorWidgetBorder, hcDark: editorWidgetBorder, hcLight: editorWidgetBorder }, localize('settingsDropdownListBorder', "Settings editor dropdown list border. This surrounds the options and separates the options from the description.")); +export const settingsSelectBackground = registerColor(`settings.dropdownBackground`, selectBackground, localize('settingsDropdownBackground', "Settings editor dropdown background.")); +export const settingsSelectForeground = registerColor('settings.dropdownForeground', selectForeground, localize('settingsDropdownForeground', "Settings editor dropdown foreground.")); +export const settingsSelectBorder = registerColor('settings.dropdownBorder', selectBorder, localize('settingsDropdownBorder', "Settings editor dropdown border.")); +export const settingsSelectListBorder = registerColor('settings.dropdownListBorder', editorWidgetBorder, localize('settingsDropdownListBorder', "Settings editor dropdown list border. This surrounds the options and separates the options from the description.")); // Bool control colors -export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', { dark: checkboxBackground, light: checkboxBackground, hcDark: checkboxBackground, hcLight: checkboxBackground }, localize('settingsCheckboxBackground', "Settings editor checkbox background.")); -export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', { dark: checkboxForeground, light: checkboxForeground, hcDark: checkboxForeground, hcLight: checkboxForeground }, localize('settingsCheckboxForeground', "Settings editor checkbox foreground.")); -export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', { dark: checkboxBorder, light: checkboxBorder, hcDark: checkboxBorder, hcLight: checkboxBorder }, localize('settingsCheckboxBorder', "Settings editor checkbox border.")); +export const settingsCheckboxBackground = registerColor('settings.checkboxBackground', checkboxBackground, localize('settingsCheckboxBackground', "Settings editor checkbox background.")); +export const settingsCheckboxForeground = registerColor('settings.checkboxForeground', checkboxForeground, localize('settingsCheckboxForeground', "Settings editor checkbox foreground.")); +export const settingsCheckboxBorder = registerColor('settings.checkboxBorder', checkboxBorder, localize('settingsCheckboxBorder', "Settings editor checkbox border.")); // Text control colors -export const settingsTextInputBackground = registerColor('settings.textInputBackground', { dark: inputBackground, light: inputBackground, hcDark: inputBackground, hcLight: inputBackground }, localize('textInputBoxBackground', "Settings editor text input box background.")); -export const settingsTextInputForeground = registerColor('settings.textInputForeground', { dark: inputForeground, light: inputForeground, hcDark: inputForeground, hcLight: inputForeground }, localize('textInputBoxForeground', "Settings editor text input box foreground.")); -export const settingsTextInputBorder = registerColor('settings.textInputBorder', { dark: inputBorder, light: inputBorder, hcDark: inputBorder, hcLight: inputBorder }, localize('textInputBoxBorder', "Settings editor text input box border.")); +export const settingsTextInputBackground = registerColor('settings.textInputBackground', inputBackground, localize('textInputBoxBackground', "Settings editor text input box background.")); +export const settingsTextInputForeground = registerColor('settings.textInputForeground', inputForeground, localize('textInputBoxForeground', "Settings editor text input box foreground.")); +export const settingsTextInputBorder = registerColor('settings.textInputBorder', inputBorder, localize('textInputBoxBorder', "Settings editor text input box border.")); // Number control colors -export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', { dark: inputBackground, light: inputBackground, hcDark: inputBackground, hcLight: inputBackground }, localize('numberInputBoxBackground', "Settings editor number input box background.")); -export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', { dark: inputForeground, light: inputForeground, hcDark: inputForeground, hcLight: inputForeground }, localize('numberInputBoxForeground', "Settings editor number input box foreground.")); -export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', { dark: inputBorder, light: inputBorder, hcDark: inputBorder, hcLight: inputBorder }, localize('numberInputBoxBorder', "Settings editor number input box border.")); +export const settingsNumberInputBackground = registerColor('settings.numberInputBackground', inputBackground, localize('numberInputBoxBackground', "Settings editor number input box background.")); +export const settingsNumberInputForeground = registerColor('settings.numberInputForeground', inputForeground, localize('numberInputBoxForeground', "Settings editor number input box foreground.")); +export const settingsNumberInputBorder = registerColor('settings.numberInputBorder', inputBorder, localize('numberInputBoxBorder', "Settings editor number input box border.")); export const focusedRowBackground = registerColor('settings.focusedRowBackground', { dark: transparent(listHoverBackground, .6), @@ -55,9 +55,4 @@ export const rowHoverBackground = registerColor('settings.rowHoverBackground', { hcLight: null }, localize('settings.rowHoverBackground', "The background color of a settings row when hovered.")); -export const focusedRowBorder = registerColor('settings.focusedRowBorder', { - dark: focusBorder, - light: focusBorder, - hcDark: focusBorder, - hcLight: focusBorder -}, localize('settings.focusedRowBorder', "The color of the row's top and bottom border when the row is focused.")); +export const focusedRowBorder = registerColor('settings.focusedRowBorder', focusBorder, localize('settings.focusedRowBorder', "The color of the row's top and bottom border when the row is focused.")); diff --git a/src/vs/workbench/contrib/remote/browser/tunnelView.ts b/src/vs/workbench/contrib/remote/browser/tunnelView.ts index aa8ce0ff514..a1b6e3ae2be 100644 --- a/src/vs/workbench/contrib/remote/browser/tunnelView.ts +++ b/src/vs/workbench/contrib/remote/browser/tunnelView.ts @@ -1818,10 +1818,5 @@ MenuRegistry.appendMenuItem(MenuId.TunnelLocalAddressInline, ({ when: isForwardedOrDetectedExpr })); -registerColor('ports.iconRunningProcessForeground', { - light: STATUS_BAR_REMOTE_ITEM_BACKGROUND, - dark: STATUS_BAR_REMOTE_ITEM_BACKGROUND, - hcDark: STATUS_BAR_REMOTE_ITEM_BACKGROUND, - hcLight: STATUS_BAR_REMOTE_ITEM_BACKGROUND -}, nls.localize('portWithRunningProcess.foreground', "The color of the icon for a port that has an associated running process.")); +registerColor('ports.iconRunningProcessForeground', STATUS_BAR_REMOTE_ITEM_BACKGROUND, nls.localize('portWithRunningProcess.foreground', "The color of the icon for a port that has an associated running process.")); diff --git a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts index 7537f80f534..173cdb04fd0 100644 --- a/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts +++ b/src/vs/workbench/contrib/scm/browser/dirtydiffDecorator.ts @@ -1013,37 +1013,17 @@ const editorGutterAddedBackground = registerColor('editorGutter.addedBackground' hcLight: '#48985D' }, nls.localize('editorGutterAddedBackground', "Editor gutter background color for lines that are added.")); -const editorGutterDeletedBackground = registerColor('editorGutter.deletedBackground', { - dark: editorErrorForeground, - light: editorErrorForeground, - hcDark: editorErrorForeground, - hcLight: editorErrorForeground -}, nls.localize('editorGutterDeletedBackground', "Editor gutter background color for lines that are deleted.")); - -const minimapGutterModifiedBackground = registerColor('minimapGutter.modifiedBackground', { - dark: editorGutterModifiedBackground, - light: editorGutterModifiedBackground, - hcDark: editorGutterModifiedBackground, - hcLight: editorGutterModifiedBackground -}, nls.localize('minimapGutterModifiedBackground', "Minimap gutter background color for lines that are modified.")); - -const minimapGutterAddedBackground = registerColor('minimapGutter.addedBackground', { - dark: editorGutterAddedBackground, - light: editorGutterAddedBackground, - hcDark: editorGutterAddedBackground, - hcLight: editorGutterAddedBackground -}, nls.localize('minimapGutterAddedBackground', "Minimap gutter background color for lines that are added.")); - -const minimapGutterDeletedBackground = registerColor('minimapGutter.deletedBackground', { - dark: editorGutterDeletedBackground, - light: editorGutterDeletedBackground, - hcDark: editorGutterDeletedBackground, - hcLight: editorGutterDeletedBackground -}, nls.localize('minimapGutterDeletedBackground', "Minimap gutter background color for lines that are deleted.")); - -const overviewRulerModifiedForeground = registerColor('editorOverviewRuler.modifiedForeground', { dark: transparent(editorGutterModifiedBackground, 0.6), light: transparent(editorGutterModifiedBackground, 0.6), hcDark: transparent(editorGutterModifiedBackground, 0.6), hcLight: transparent(editorGutterModifiedBackground, 0.6) }, nls.localize('overviewRulerModifiedForeground', 'Overview ruler marker color for modified content.')); -const overviewRulerAddedForeground = registerColor('editorOverviewRuler.addedForeground', { dark: transparent(editorGutterAddedBackground, 0.6), light: transparent(editorGutterAddedBackground, 0.6), hcDark: transparent(editorGutterAddedBackground, 0.6), hcLight: transparent(editorGutterAddedBackground, 0.6) }, nls.localize('overviewRulerAddedForeground', 'Overview ruler marker color for added content.')); -const overviewRulerDeletedForeground = registerColor('editorOverviewRuler.deletedForeground', { dark: transparent(editorGutterDeletedBackground, 0.6), light: transparent(editorGutterDeletedBackground, 0.6), hcDark: transparent(editorGutterDeletedBackground, 0.6), hcLight: transparent(editorGutterDeletedBackground, 0.6) }, nls.localize('overviewRulerDeletedForeground', 'Overview ruler marker color for deleted content.')); +const editorGutterDeletedBackground = registerColor('editorGutter.deletedBackground', editorErrorForeground, nls.localize('editorGutterDeletedBackground', "Editor gutter background color for lines that are deleted.")); + +const minimapGutterModifiedBackground = registerColor('minimapGutter.modifiedBackground', editorGutterModifiedBackground, nls.localize('minimapGutterModifiedBackground', "Minimap gutter background color for lines that are modified.")); + +const minimapGutterAddedBackground = registerColor('minimapGutter.addedBackground', editorGutterAddedBackground, nls.localize('minimapGutterAddedBackground', "Minimap gutter background color for lines that are added.")); + +const minimapGutterDeletedBackground = registerColor('minimapGutter.deletedBackground', editorGutterDeletedBackground, nls.localize('minimapGutterDeletedBackground', "Minimap gutter background color for lines that are deleted.")); + +const overviewRulerModifiedForeground = registerColor('editorOverviewRuler.modifiedForeground', transparent(editorGutterModifiedBackground, 0.6), nls.localize('overviewRulerModifiedForeground', 'Overview ruler marker color for modified content.')); +const overviewRulerAddedForeground = registerColor('editorOverviewRuler.addedForeground', transparent(editorGutterAddedBackground, 0.6), nls.localize('overviewRulerAddedForeground', 'Overview ruler marker color for added content.')); +const overviewRulerDeletedForeground = registerColor('editorOverviewRuler.deletedForeground', transparent(editorGutterDeletedBackground, 0.6), nls.localize('overviewRulerDeletedForeground', 'Overview ruler marker color for deleted content.')); class DirtyDiffDecorator extends Disposable { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 988cbda1404..0961bec2627 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -130,33 +130,13 @@ type TreeElement = type ShowChangesSetting = 'always' | 'never' | 'auto'; -registerColor('scm.historyItemAdditionsForeground', { - dark: 'gitDecoration.addedResourceForeground', - light: 'gitDecoration.addedResourceForeground', - hcDark: 'gitDecoration.addedResourceForeground', - hcLight: 'gitDecoration.addedResourceForeground' -}, localize('scm.historyItemAdditionsForeground', "History item additions foreground color.")); - -registerColor('scm.historyItemDeletionsForeground', { - dark: 'gitDecoration.deletedResourceForeground', - light: 'gitDecoration.deletedResourceForeground', - hcDark: 'gitDecoration.deletedResourceForeground', - hcLight: 'gitDecoration.deletedResourceForeground' -}, localize('scm.historyItemDeletionsForeground', "History item deletions foreground color.")); - -registerColor('scm.historyItemStatisticsBorder', { - dark: transparent(foreground, 0.2), - light: transparent(foreground, 0.2), - hcDark: transparent(foreground, 0.2), - hcLight: transparent(foreground, 0.2) -}, localize('scm.historyItemStatisticsBorder', "History item statistics border color.")); - -registerColor('scm.historyItemSelectedStatisticsBorder', { - dark: transparent(listActiveSelectionForeground, 0.2), - light: transparent(listActiveSelectionForeground, 0.2), - hcDark: transparent(listActiveSelectionForeground, 0.2), - hcLight: transparent(listActiveSelectionForeground, 0.2) -}, localize('scm.historyItemSelectedStatisticsBorder', "History item selected statistics border color.")); +registerColor('scm.historyItemAdditionsForeground', 'gitDecoration.addedResourceForeground', localize('scm.historyItemAdditionsForeground', "History item additions foreground color.")); + +registerColor('scm.historyItemDeletionsForeground', 'gitDecoration.deletedResourceForeground', localize('scm.historyItemDeletionsForeground', "History item deletions foreground color.")); + +registerColor('scm.historyItemStatisticsBorder', transparent(foreground, 0.2), localize('scm.historyItemStatisticsBorder', "History item statistics border color.")); + +registerColor('scm.historyItemSelectedStatisticsBorder', transparent(listActiveSelectionForeground, 0.2), localize('scm.historyItemSelectedStatisticsBorder', "History item selected statistics border color.")); function processResourceFilterData(uri: URI, filterData: FuzzyScore | LabelFuzzyScore | undefined): [IMatch[] | undefined, IMatch[] | undefined] { if (!filterData) { diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts index 5bf122311af..c3577b4e873 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditor.ts @@ -776,7 +776,7 @@ export class SearchEditor extends AbstractTextCodeEditor } } -const searchEditorTextInputBorder = registerColor('searchEditor.textInputBorder', { dark: inputBorder, light: inputBorder, hcDark: inputBorder, hcLight: inputBorder }, localize('textInputBoxBorder', "Search editor text input box border.")); +const searchEditorTextInputBorder = registerColor('searchEditor.textInputBorder', inputBorder, localize('textInputBoxBorder', "Search editor text input box border.")); function findNextRange(matchRanges: Range[], currentPosition: Position) { for (const matchRange of matchRanges) { diff --git a/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts b/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts index 0750923f614..86a5a037b4d 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalColorRegistry.ts @@ -23,12 +23,7 @@ export const TERMINAL_FOREGROUND_COLOR = registerColor('terminal.foreground', { }, nls.localize('terminal.foreground', 'The foreground color of the terminal.')); export const TERMINAL_CURSOR_FOREGROUND_COLOR = registerColor('terminalCursor.foreground', null, nls.localize('terminalCursor.foreground', 'The foreground color of the terminal cursor.')); export const TERMINAL_CURSOR_BACKGROUND_COLOR = registerColor('terminalCursor.background', null, nls.localize('terminalCursor.background', 'The background color of the terminal cursor. Allows customizing the color of a character overlapped by a block cursor.')); -export const TERMINAL_SELECTION_BACKGROUND_COLOR = registerColor('terminal.selectionBackground', { - light: editorSelectionBackground, - dark: editorSelectionBackground, - hcDark: editorSelectionBackground, - hcLight: editorSelectionBackground -}, nls.localize('terminal.selectionBackground', 'The selection background color of the terminal.')); +export const TERMINAL_SELECTION_BACKGROUND_COLOR = registerColor('terminal.selectionBackground', editorSelectionBackground, nls.localize('terminal.selectionBackground', 'The selection background color of the terminal.')); export const TERMINAL_INACTIVE_SELECTION_BACKGROUND_COLOR = registerColor('terminal.inactiveSelectionBackground', { light: transparent(TERMINAL_SELECTION_BACKGROUND_COLOR, 0.5), dark: transparent(TERMINAL_SELECTION_BACKGROUND_COLOR, 0.5), @@ -59,18 +54,8 @@ export const TERMINAL_COMMAND_DECORATION_ERROR_BACKGROUND_COLOR = registerColor( hcDark: '#F14C4C', hcLight: '#B5200D' }, nls.localize('terminalCommandDecoration.errorBackground', 'The terminal command decoration background color for error commands.')); -export const TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR = registerColor('terminalOverviewRuler.cursorForeground', { - dark: '#A0A0A0CC', - light: '#A0A0A0CC', - hcDark: '#A0A0A0CC', - hcLight: '#A0A0A0CC' -}, nls.localize('terminalOverviewRuler.cursorForeground', 'The overview ruler cursor color.')); -export const TERMINAL_BORDER_COLOR = registerColor('terminal.border', { - dark: PANEL_BORDER, - light: PANEL_BORDER, - hcDark: PANEL_BORDER, - hcLight: PANEL_BORDER -}, nls.localize('terminal.border', 'The color of the border that separates split panes within the terminal. This defaults to panel.border.')); +export const TERMINAL_OVERVIEW_RULER_CURSOR_FOREGROUND_COLOR = registerColor('terminalOverviewRuler.cursorForeground', '#A0A0A0CC', nls.localize('terminalOverviewRuler.cursorForeground', 'The overview ruler cursor color.')); +export const TERMINAL_BORDER_COLOR = registerColor('terminal.border', PANEL_BORDER, nls.localize('terminal.border', 'The color of the border that separates split panes within the terminal. This defaults to panel.border.')); export const TERMINAL_FIND_MATCH_BACKGROUND_COLOR = registerColor('terminal.findMatchBackground', { dark: editorFindMatch, light: editorFindMatch, @@ -78,12 +63,7 @@ export const TERMINAL_FIND_MATCH_BACKGROUND_COLOR = registerColor('terminal.find hcDark: null, hcLight: '#0F4A85' }, nls.localize('terminal.findMatchBackground', 'Color of the current search match in the terminal. The color must not be opaque so as not to hide underlying terminal content.'), true); -export const TERMINAL_HOVER_HIGHLIGHT_BACKGROUND_COLOR = registerColor('terminal.hoverHighlightBackground', { - dark: transparent(editorHoverHighlight, 0.5), - light: transparent(editorHoverHighlight, 0.5), - hcDark: transparent(editorHoverHighlight, 0.5), - hcLight: transparent(editorHoverHighlight, 0.5) -}, nls.localize('terminal.findMatchHighlightBorder', 'Border color of the other search matches in the terminal.')); +export const TERMINAL_HOVER_HIGHLIGHT_BACKGROUND_COLOR = registerColor('terminal.hoverHighlightBackground', transparent(editorHoverHighlight, 0.5), nls.localize('terminal.findMatchHighlightBorder', 'Border color of the other search matches in the terminal.')); export const TERMINAL_FIND_MATCH_BORDER_COLOR = registerColor('terminal.findMatchBorder', { dark: null, light: null, @@ -108,18 +88,8 @@ export const TERMINAL_OVERVIEW_RULER_FIND_MATCH_FOREGROUND_COLOR = registerColor hcDark: '#f38518', hcLight: '#0F4A85' }, nls.localize('terminalOverviewRuler.findMatchHighlightForeground', 'Overview ruler marker color for find matches in the terminal.')); -export const TERMINAL_DRAG_AND_DROP_BACKGROUND = registerColor('terminal.dropBackground', { - dark: EDITOR_DRAG_AND_DROP_BACKGROUND, - light: EDITOR_DRAG_AND_DROP_BACKGROUND, - hcDark: EDITOR_DRAG_AND_DROP_BACKGROUND, - hcLight: EDITOR_DRAG_AND_DROP_BACKGROUND -}, nls.localize('terminal.dragAndDropBackground', "Background color when dragging on top of terminals. The color should have transparency so that the terminal contents can still shine through."), true); -export const TERMINAL_TAB_ACTIVE_BORDER = registerColor('terminal.tab.activeBorder', { - dark: TAB_ACTIVE_BORDER, - light: TAB_ACTIVE_BORDER, - hcDark: TAB_ACTIVE_BORDER, - hcLight: TAB_ACTIVE_BORDER -}, nls.localize('terminal.tab.activeBorder', 'Border on the side of the terminal tab in the panel. This defaults to tab.activeBorder.')); +export const TERMINAL_DRAG_AND_DROP_BACKGROUND = registerColor('terminal.dropBackground', EDITOR_DRAG_AND_DROP_BACKGROUND, nls.localize('terminal.dragAndDropBackground', "Background color when dragging on top of terminals. The color should have transparency so that the terminal contents can still shine through."), true); +export const TERMINAL_TAB_ACTIVE_BORDER = registerColor('terminal.tab.activeBorder', TAB_ACTIVE_BORDER, nls.localize('terminal.tab.activeBorder', 'Border on the side of the terminal tab in the panel. This defaults to tab.activeBorder.')); export const TERMINAL_INITIAL_HINT_FOREGROUND = registerColor('terminal.initialHintForeground', { dark: '#ffffff56', light: '#0007', diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry.ts index bc18c7db093..ed805826706 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollColorRegistry.ts @@ -6,12 +6,7 @@ import { localize } from 'vs/nls'; import { registerColor } from 'vs/platform/theme/common/colorUtils'; -export const terminalStickyScrollBackground = registerColor('terminalStickyScroll.background', { - light: null, - dark: null, - hcDark: null, - hcLight: null -}, localize('terminalStickyScroll.background', 'The background color of the sticky scroll overlay in the terminal.')); +export const terminalStickyScrollBackground = registerColor('terminalStickyScroll.background', null, localize('terminalStickyScroll.background', 'The background color of the sticky scroll overlay in the terminal.')); export const terminalStickyScrollHoverBackground = registerColor('terminalStickyScrollHover.background', { dark: '#2A2D2E', diff --git a/src/vs/workbench/contrib/testing/browser/theme.ts b/src/vs/workbench/contrib/testing/browser/theme.ts index 536c03da5f9..0c088a787c3 100644 --- a/src/vs/workbench/contrib/testing/browser/theme.ts +++ b/src/vs/workbench/contrib/testing/browser/theme.ts @@ -30,33 +30,13 @@ export const testingColorIconPassed = registerColor('testing.iconPassed', { hcLight: '#007100' }, localize('testing.iconPassed', "Color for the 'passed' icon in the test explorer.")); -export const testingColorRunAction = registerColor('testing.runAction', { - dark: testingColorIconPassed, - light: testingColorIconPassed, - hcDark: testingColorIconPassed, - hcLight: testingColorIconPassed -}, localize('testing.runAction', "Color for 'run' icons in the editor.")); - -export const testingColorIconQueued = registerColor('testing.iconQueued', { - dark: '#cca700', - light: '#cca700', - hcDark: '#cca700', - hcLight: '#cca700' -}, localize('testing.iconQueued', "Color for the 'Queued' icon in the test explorer.")); - -export const testingColorIconUnset = registerColor('testing.iconUnset', { - dark: '#848484', - light: '#848484', - hcDark: '#848484', - hcLight: '#848484' -}, localize('testing.iconUnset', "Color for the 'Unset' icon in the test explorer.")); - -export const testingColorIconSkipped = registerColor('testing.iconSkipped', { - dark: '#848484', - light: '#848484', - hcDark: '#848484', - hcLight: '#848484' -}, localize('testing.iconSkipped', "Color for the 'Skipped' icon in the test explorer.")); +export const testingColorRunAction = registerColor('testing.runAction', testingColorIconPassed, localize('testing.runAction', "Color for 'run' icons in the editor.")); + +export const testingColorIconQueued = registerColor('testing.iconQueued', '#cca700', localize('testing.iconQueued', "Color for the 'Queued' icon in the test explorer.")); + +export const testingColorIconUnset = registerColor('testing.iconUnset', '#848484', localize('testing.iconUnset', "Color for the 'Unset' icon in the test explorer.")); + +export const testingColorIconSkipped = registerColor('testing.iconSkipped', '#848484', localize('testing.iconSkipped', "Color for the 'Skipped' icon in the test explorer.")); export const testingPeekBorder = registerColor('testing.peekBorder', { dark: editorErrorForeground, @@ -135,19 +115,9 @@ export const testingUncoveredGutterBackground = registerColor('testing.uncovered hcLight: chartsRed }, localize('testing.uncoveredGutterBackground', 'Gutter color of regions where code not covered.')); -export const testingCoverCountBadgeBackground = registerColor('testing.coverCountBadgeBackground', { - dark: badgeBackground, - light: badgeBackground, - hcDark: badgeBackground, - hcLight: badgeBackground -}, localize('testing.coverCountBadgeBackground', 'Background for the badge indicating execution count')); +export const testingCoverCountBadgeBackground = registerColor('testing.coverCountBadgeBackground', badgeBackground, localize('testing.coverCountBadgeBackground', 'Background for the badge indicating execution count')); -export const testingCoverCountBadgeForeground = registerColor('testing.coverCountBadgeForeground', { - dark: badgeForeground, - light: badgeForeground, - hcDark: badgeForeground, - hcLight: badgeForeground -}, localize('testing.coverCountBadgeForeground', 'Foreground for the badge indicating execution count')); +export const testingCoverCountBadgeForeground = registerColor('testing.coverCountBadgeForeground', badgeForeground, localize('testing.coverCountBadgeForeground', 'Foreground for the badge indicating execution count')); export const testMessageSeverityColors: { [K in TestMessageType]: { @@ -170,12 +140,12 @@ export const testMessageSeverityColors: { [TestMessageType.Output]: { decorationForeground: registerColor( 'testing.message.info.decorationForeground', - { dark: transparent(editorForeground, 0.5), light: transparent(editorForeground, 0.5), hcDark: transparent(editorForeground, 0.5), hcLight: transparent(editorForeground, 0.5) }, + transparent(editorForeground, 0.5), localize('testing.message.info.decorationForeground', 'Text color of test info messages shown inline in the editor.') ), marginBackground: registerColor( 'testing.message.info.lineBackground', - { dark: null, light: null, hcDark: null, hcLight: null }, + null, localize('testing.message.info.marginBackground', 'Margin color beside info messages shown inline in the editor.') ), }, @@ -190,47 +160,17 @@ export const testStatesToIconColors: { [K in TestResultState]?: string } = { [TestResultState.Skipped]: testingColorIconSkipped, }; -export const testingRetiredColorIconErrored = registerColor('testing.iconErrored.retired', { - dark: transparent(testingColorIconErrored, 0.7), - light: transparent(testingColorIconErrored, 0.7), - hcDark: transparent(testingColorIconErrored, 0.7), - hcLight: transparent(testingColorIconErrored, 0.7) -}, localize('testing.iconErrored.retired', "Retired color for the 'Errored' icon in the test explorer.")); - -export const testingRetiredColorIconFailed = registerColor('testing.iconFailed.retired', { - dark: transparent(testingColorIconFailed, 0.7), - light: transparent(testingColorIconFailed, 0.7), - hcDark: transparent(testingColorIconFailed, 0.7), - hcLight: transparent(testingColorIconFailed, 0.7) -}, localize('testing.iconFailed.retired', "Retired color for the 'failed' icon in the test explorer.")); - -export const testingRetiredColorIconPassed = registerColor('testing.iconPassed.retired', { - dark: transparent(testingColorIconPassed, 0.7), - light: transparent(testingColorIconPassed, 0.7), - hcDark: transparent(testingColorIconPassed, 0.7), - hcLight: transparent(testingColorIconPassed, 0.7) -}, localize('testing.iconPassed.retired', "Retired color for the 'passed' icon in the test explorer.")); - -export const testingRetiredColorIconQueued = registerColor('testing.iconQueued.retired', { - dark: transparent(testingColorIconQueued, 0.7), - light: transparent(testingColorIconQueued, 0.7), - hcDark: transparent(testingColorIconQueued, 0.7), - hcLight: transparent(testingColorIconQueued, 0.7) -}, localize('testing.iconQueued.retired', "Retired color for the 'Queued' icon in the test explorer.")); - -export const testingRetiredColorIconUnset = registerColor('testing.iconUnset.retired', { - dark: transparent(testingColorIconUnset, 0.7), - light: transparent(testingColorIconUnset, 0.7), - hcDark: transparent(testingColorIconUnset, 0.7), - hcLight: transparent(testingColorIconUnset, 0.7) -}, localize('testing.iconUnset.retired', "Retired color for the 'Unset' icon in the test explorer.")); - -export const testingRetiredColorIconSkipped = registerColor('testing.iconSkipped.retired', { - dark: transparent(testingColorIconSkipped, 0.7), - light: transparent(testingColorIconSkipped, 0.7), - hcDark: transparent(testingColorIconSkipped, 0.7), - hcLight: transparent(testingColorIconSkipped, 0.7) -}, localize('testing.iconSkipped.retired', "Retired color for the 'Skipped' icon in the test explorer.")); +export const testingRetiredColorIconErrored = registerColor('testing.iconErrored.retired', transparent(testingColorIconErrored, 0.7), localize('testing.iconErrored.retired', "Retired color for the 'Errored' icon in the test explorer.")); + +export const testingRetiredColorIconFailed = registerColor('testing.iconFailed.retired', transparent(testingColorIconFailed, 0.7), localize('testing.iconFailed.retired', "Retired color for the 'failed' icon in the test explorer.")); + +export const testingRetiredColorIconPassed = registerColor('testing.iconPassed.retired', transparent(testingColorIconPassed, 0.7), localize('testing.iconPassed.retired', "Retired color for the 'passed' icon in the test explorer.")); + +export const testingRetiredColorIconQueued = registerColor('testing.iconQueued.retired', transparent(testingColorIconQueued, 0.7), localize('testing.iconQueued.retired', "Retired color for the 'Queued' icon in the test explorer.")); + +export const testingRetiredColorIconUnset = registerColor('testing.iconUnset.retired', transparent(testingColorIconUnset, 0.7), localize('testing.iconUnset.retired', "Retired color for the 'Unset' icon in the test explorer.")); + +export const testingRetiredColorIconSkipped = registerColor('testing.iconSkipped.retired', transparent(testingColorIconSkipped, 0.7), localize('testing.iconSkipped.retired', "Retired color for the 'Skipped' icon in the test explorer.")); export const testStatesToRetiredIconColors: { [K in TestResultState]?: string } = { [TestResultState.Errored]: testingRetiredColorIconErrored, diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts index 6c47eb9557c..8ddcc3527b5 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts @@ -56,7 +56,7 @@ import { Codicon } from 'vs/base/common/codicons'; import { WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { createInstantHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; -export const profilesSashBorder = registerColor('profiles.sashBorder', { dark: PANEL_BORDER, light: PANEL_BORDER, hcDark: PANEL_BORDER, hcLight: PANEL_BORDER }, localize('profilesSashBorder', "The color of the Profiles editor splitview sash border.")); +export const profilesSashBorder = registerColor('profiles.sashBorder', PANEL_BORDER, localize('profilesSashBorder', "The color of the Profiles editor splitview sash border.")); export class UserDataProfilesEditor extends EditorPane implements IUserDataProfilesEditor { diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors.ts b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors.ts index 8e934b891a3..e3227a80af4 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors.ts +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/gettingStartedColors.ts @@ -7,14 +7,14 @@ import { darken, inputBackground, editorWidgetBackground, lighten, registerColor import { localize } from 'vs/nls'; // Seprate from main module to break dependency cycles between welcomePage and gettingStarted. -export const welcomePageBackground = registerColor('welcomePage.background', { light: null, dark: null, hcDark: null, hcLight: null }, localize('welcomePage.background', 'Background color for the Welcome page.')); +export const welcomePageBackground = registerColor('welcomePage.background', null, localize('welcomePage.background', 'Background color for the Welcome page.')); export const welcomePageTileBackground = registerColor('welcomePage.tileBackground', { dark: editorWidgetBackground, light: editorWidgetBackground, hcDark: '#000', hcLight: editorWidgetBackground }, localize('welcomePage.tileBackground', 'Background color for the tiles on the Welcome page.')); export const welcomePageTileHoverBackground = registerColor('welcomePage.tileHoverBackground', { dark: lighten(editorWidgetBackground, .2), light: darken(editorWidgetBackground, .1), hcDark: null, hcLight: null }, localize('welcomePage.tileHoverBackground', 'Hover background color for the tiles on the Welcome.')); export const welcomePageTileBorder = registerColor('welcomePage.tileBorder', { dark: '#ffffff1a', light: '#0000001a', hcDark: contrastBorder, hcLight: contrastBorder }, localize('welcomePage.tileBorder', 'Border color for the tiles on the Welcome page.')); -export const welcomePageProgressBackground = registerColor('welcomePage.progress.background', { light: inputBackground, dark: inputBackground, hcDark: inputBackground, hcLight: inputBackground }, localize('welcomePage.progress.background', 'Foreground color for the Welcome page progress bars.')); -export const welcomePageProgressForeground = registerColor('welcomePage.progress.foreground', { light: textLinkForeground, dark: textLinkForeground, hcDark: textLinkForeground, hcLight: textLinkForeground }, localize('welcomePage.progress.foreground', 'Background color for the Welcome page progress bars.')); +export const welcomePageProgressBackground = registerColor('welcomePage.progress.background', inputBackground, localize('welcomePage.progress.background', 'Foreground color for the Welcome page progress bars.')); +export const welcomePageProgressForeground = registerColor('welcomePage.progress.foreground', textLinkForeground, localize('welcomePage.progress.foreground', 'Background color for the Welcome page progress bars.')); export const walkthroughStepTitleForeground = registerColor('walkthrough.stepTitle.foreground', { light: '#000000', dark: '#ffffff', hcDark: null, hcLight: null }, localize('walkthrough.stepTitle.foreground', 'Foreground color of the heading of each walkthrough step')); From b36286db80c68b7fdc6bfee2f55e0208c8833dfc Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 25 Jun 2024 10:16:20 +0200 Subject: [PATCH 0104/2222] Fix property undefined bug (#217800) * fix property undefined bug * only set source of overrides --- .../common/configurationRegistry.ts | 34 ++++++++++++------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index d7e9ae576b0..322e807f0a7 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -406,31 +406,41 @@ class ConfigurationRegistry implements IConfigurationRegistry { this.defaultLanguageConfigurationOverridesNode.properties![key] = property; } else { const property = this.configurationProperties[key]; - let defaultValue = overrides[key]; - let defaultValueSource: ConfigurationDefaultValueSource | undefined = source; + + const existingDefaultOverride = this.configurationDefaultsOverrides.get(key); + let existingDefaultValue = existingDefaultOverride?.value ?? property?.defaultDefaultValue; + + let newDefaultValue = overrides[key]; + let newDefaultValueSource: ConfigurationDefaultValueSource | undefined = source; + + const isObjectSetting = types.isObject(newDefaultValue) && ( + property !== undefined && property.type === 'object' || + property === undefined && (types.isUndefined(existingDefaultValue) || types.isObject(existingDefaultValue))); // If the default value is an object, merge the objects and store the source of each keys - if (property.type === 'object' && types.isObject(overrides[key])) { - const objectDefaults = this.configurationDefaultsOverrides.get(key); - const existingDefaultValue = objectDefaults?.value ?? property.defaultDefaultValue ?? {}; - defaultValue = { ...existingDefaultValue, ...overrides[key] }; + if (isObjectSetting) { + if (!types.isObject(existingDefaultValue)) { + existingDefaultValue = {}; + } + + newDefaultValue = { ...existingDefaultValue, ...newDefaultValue }; - defaultValueSource = objectDefaults?.source ?? new Map(); - if (!(defaultValueSource instanceof Map)) { + newDefaultValueSource = existingDefaultOverride?.source ?? new Map(); + if (!(newDefaultValueSource instanceof Map)) { console.error('defaultValueSource is not a Map'); continue; } - for (const objectKey in overrides[key]) { + for (const overrideObjectKey in overrides[key]) { if (source) { - defaultValueSource.set(objectKey, source); + newDefaultValueSource.set(overrideObjectKey, source); } else { - defaultValueSource.delete(objectKey); + newDefaultValueSource.delete(overrideObjectKey); } } } - this.configurationDefaultsOverrides.set(key, { value: defaultValue, source: defaultValueSource }); + this.configurationDefaultsOverrides.set(key, { value: newDefaultValue, source: newDefaultValueSource }); if (property) { this.updatePropertyDefaultValue(key, property); From 1b27e602ed26c3aa7d232788c5ca32edc41b9bba Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 25 Jun 2024 13:45:49 +0200 Subject: [PATCH 0105/2222] Bump ws from 8.13.0 to 8.17.1 in /extensions/notebook-renderers (#216556) Bumps [ws](https://github.com/websockets/ws) from 8.13.0 to 8.17.1. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/compare/8.13.0...8.17.1) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- extensions/notebook-renderers/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/notebook-renderers/yarn.lock b/extensions/notebook-renderers/yarn.lock index 3cbe531e0fd..00c3e704dba 100644 --- a/extensions/notebook-renderers/yarn.lock +++ b/extensions/notebook-renderers/yarn.lock @@ -408,9 +408,9 @@ word-wrap@~1.2.3: integrity sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA== ws@^8.13.0: - version "8.13.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.13.0.tgz#9a9fb92f93cf41512a0735c8f4dd09b8a1211cd0" - integrity sha512-x9vcZYTrFPC7aSIbj7sRCYo7L/Xb8Iy+pW0ng0wt2vCJv7M9HOMy0UoN3rr+IFC7hb7vXoqS+P9ktyLLLhO+LA== + version "8.17.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.17.1.tgz#9293da530bb548febc95371d90f9c878727d919b" + integrity sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ== xml-name-validator@^4.0.0: version "4.0.0" From a28cbc207a08017f8f020bfb0a008defc927eb73 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Tue, 25 Jun 2024 16:26:48 +0200 Subject: [PATCH 0106/2222] css/json/html web: adopt LanguageClient API change (#218060) --- .../client/src/browser/cssClientMain.ts | 9 +-------- extensions/css-language-features/client/tsconfig.json | 5 ++++- .../client/src/browser/htmlClientMain.ts | 9 +-------- extensions/html-language-features/client/tsconfig.json | 5 ++++- .../client/src/browser/jsonClientMain.ts | 8 +------- extensions/json-language-features/client/tsconfig.json | 5 ++++- 6 files changed, 15 insertions(+), 26 deletions(-) diff --git a/extensions/css-language-features/client/src/browser/cssClientMain.ts b/extensions/css-language-features/client/src/browser/cssClientMain.ts index 6522c786389..c89997ffaa0 100644 --- a/extensions/css-language-features/client/src/browser/cssClientMain.ts +++ b/extensions/css-language-features/client/src/browser/cssClientMain.ts @@ -8,13 +8,6 @@ import { BaseLanguageClient, LanguageClientOptions } from 'vscode-languageclient import { startClient, LanguageClientConstructor } from '../cssClient'; import { LanguageClient } from 'vscode-languageclient/browser'; -declare const Worker: { - new(stringUrl: string): any; -}; -declare const TextDecoder: { - new(encoding?: string): { decode(buffer: ArrayBuffer): string }; -}; - let client: BaseLanguageClient | undefined; // this method is called when vs code is activated @@ -25,7 +18,7 @@ export async function activate(context: ExtensionContext) { worker.postMessage({ i10lLocation: l10n.uri?.toString(false) ?? '' }); const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { - return new LanguageClient(id, name, clientOptions, worker); + return new LanguageClient(id, name, worker, clientOptions); }; client = await startClient(context, newLanguageClient, { TextDecoder }); diff --git a/extensions/css-language-features/client/tsconfig.json b/extensions/css-language-features/client/tsconfig.json index 573b24b4aa6..44b77895c10 100644 --- a/extensions/css-language-features/client/tsconfig.json +++ b/extensions/css-language-features/client/tsconfig.json @@ -1,7 +1,10 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "lib": [ + "webworker" + ] }, "include": [ "src/**/*", diff --git a/extensions/html-language-features/client/src/browser/htmlClientMain.ts b/extensions/html-language-features/client/src/browser/htmlClientMain.ts index 3f10e6d131f..06997d39fb0 100644 --- a/extensions/html-language-features/client/src/browser/htmlClientMain.ts +++ b/extensions/html-language-features/client/src/browser/htmlClientMain.ts @@ -8,13 +8,6 @@ import { LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor, AsyncDisposable } from '../htmlClient'; import { LanguageClient } from 'vscode-languageclient/browser'; -declare const Worker: { - new(stringUrl: string): any; -}; -declare const TextDecoder: { - new(encoding?: string): { decode(buffer: ArrayBuffer): string }; -}; - let client: AsyncDisposable | undefined; // this method is called when vs code is activated @@ -25,7 +18,7 @@ export async function activate(context: ExtensionContext) { worker.postMessage({ i10lLocation: l10n.uri?.toString(false) ?? '' }); const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { - return new LanguageClient(id, name, clientOptions, worker); + return new LanguageClient(id, name, worker, clientOptions); }; const timer = { diff --git a/extensions/html-language-features/client/tsconfig.json b/extensions/html-language-features/client/tsconfig.json index 8f5cef74fd3..349af163eea 100644 --- a/extensions/html-language-features/client/tsconfig.json +++ b/extensions/html-language-features/client/tsconfig.json @@ -1,7 +1,10 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "lib": [ + "webworker" + ] }, "include": [ "src/**/*", diff --git a/extensions/json-language-features/client/src/browser/jsonClientMain.ts b/extensions/json-language-features/client/src/browser/jsonClientMain.ts index f78f494d727..91ed937fe6f 100644 --- a/extensions/json-language-features/client/src/browser/jsonClientMain.ts +++ b/extensions/json-language-features/client/src/browser/jsonClientMain.ts @@ -8,12 +8,6 @@ import { LanguageClientOptions } from 'vscode-languageclient'; import { startClient, LanguageClientConstructor, SchemaRequestService, AsyncDisposable, languageServerDescription } from '../jsonClient'; import { LanguageClient } from 'vscode-languageclient/browser'; -declare const Worker: { - new(stringUrl: string): any; -}; - -declare function fetch(uri: string, options: any): any; - let client: AsyncDisposable | undefined; // this method is called when vs code is activated @@ -24,7 +18,7 @@ export async function activate(context: ExtensionContext) { worker.postMessage({ i10lLocation: l10n.uri?.toString(false) ?? '' }); const newLanguageClient: LanguageClientConstructor = (id: string, name: string, clientOptions: LanguageClientOptions) => { - return new LanguageClient(id, name, clientOptions, worker); + return new LanguageClient(id, name, worker, clientOptions); }; const schemaRequests: SchemaRequestService = { diff --git a/extensions/json-language-features/client/tsconfig.json b/extensions/json-language-features/client/tsconfig.json index aa51e4d0157..89e6a6c12b7 100644 --- a/extensions/json-language-features/client/tsconfig.json +++ b/extensions/json-language-features/client/tsconfig.json @@ -1,7 +1,10 @@ { "extends": "../../tsconfig.base.json", "compilerOptions": { - "outDir": "./out" + "outDir": "./out", + "lib": [ + "webworker" + ] }, "include": [ "src/**/*", From 4580ba51fe1914ca29916f829adb35930089b013 Mon Sep 17 00:00:00 2001 From: Mohammad Baqer Date: Tue, 25 Jun 2024 09:28:39 -0500 Subject: [PATCH 0107/2222] make collapsedText theme-able (#173203) Co-authored-by: Martin Aeschlimann --- build/lib/stylelint/vscode-known-variables.json | 1 + src/vs/editor/contrib/folding/browser/folding.css | 3 +-- src/vs/editor/contrib/folding/browser/foldingDecorations.ts | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 97be6f79cef..272347c6c99 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -128,6 +128,7 @@ "--vscode-dropdown-foreground", "--vscode-dropdown-listBackground", "--vscode-editor-background", + "--vscode-editor-collapsedText", "--vscode-editor-findMatchBackground", "--vscode-editor-findMatchBorder", "--vscode-editor-findMatchForeground", diff --git a/src/vs/editor/contrib/folding/browser/folding.css b/src/vs/editor/contrib/folding/browser/folding.css index f973d5f7a30..9a7c91ced43 100644 --- a/src/vs/editor/contrib/folding/browser/folding.css +++ b/src/vs/editor/contrib/folding/browser/folding.css @@ -31,7 +31,7 @@ } .monaco-editor .inline-folded:after { - color: grey; + color: var(--vscode-editor-collapsedText); margin: 0.1em 0.2em 0 0.2em; content: "\22EF"; /* ellipses unicode character */ display: inline; @@ -49,4 +49,3 @@ .monaco-editor .cldr.codicon.codicon-folding-manual-collapsed { color: var(--vscode-editorGutter-foldingControlForeground) !important; } - diff --git a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts index b6ae9cd31e2..d30d019ca98 100644 --- a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts @@ -15,6 +15,7 @@ import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/base/common/themables'; const foldBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hcDark: null, hcLight: null }, localize('foldBackgroundBackground', "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations."), true); +registerColor('editor.collapsedText', { light: '#808080', dark: '#808080', hcDark: null, hcLight: null }, localize('collapsedTextColor', "Color of the collapsed text after the first line of a folded range.")); registerColor('editorGutter.foldingControlForeground', iconForeground, localize('editorGutter.foldingControlForeground', 'Color of the folding control in the editor gutter.')); export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown, localize('foldingExpandedIcon', 'Icon for expanded ranges in the editor glyph margin.')); From 682fa68122e75d59ba7f522b1ac38292aa5325e3 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 25 Jun 2024 08:52:49 -0700 Subject: [PATCH 0108/2222] Adopt link decoration var (#216859) * Adopt link decoration var * Ensure links in walkthrough markdown content get underlines and react to setting change --- .../contrib/markdown/browser/markdownDocumentRenderer.ts | 2 +- src/vs/workbench/contrib/webview/browser/pre/index.html | 6 +++++- src/vs/workbench/contrib/webview/browser/themeing.ts | 4 +++- .../welcomeGettingStarted/browser/media/gettingStarted.css | 1 + .../welcomeWalkthrough/browser/media/walkThroughPart.css | 2 +- 5 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts index 68dfb0f3350..b1c6b962fab 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownDocumentRenderer.ts @@ -32,7 +32,7 @@ img { } a { - text-decoration: none; + text-decoration: var(--text-link-decoration); } a:hover { diff --git a/src/vs/workbench/contrib/webview/browser/pre/index.html b/src/vs/workbench/contrib/webview/browser/pre/index.html index 0c6829c08f8..f46e1240428 100644 --- a/src/vs/workbench/contrib/webview/browser/pre/index.html +++ b/src/vs/workbench/contrib/webview/browser/pre/index.html @@ -6,7 +6,7 @@ + content="default-src 'none'; script-src 'sha256-ikaxwm2UFoiIKkEZTEU4mnSxpYf3lmsrhy5KqqJZfek=' 'self'; frame-src 'self'; style-src 'unsafe-inline';"> @@ -112,6 +112,10 @@ color: var(--vscode-textLink-foreground); } + p > a { + text-decoration: var(--text-link-decoration); + } + a:hover { color: var(--vscode-textLink-activeForeground); } diff --git a/src/vs/workbench/contrib/webview/browser/themeing.ts b/src/vs/workbench/contrib/webview/browser/themeing.ts index eda7179665f..75ee4b73061 100644 --- a/src/vs/workbench/contrib/webview/browser/themeing.ts +++ b/src/vs/workbench/contrib/webview/browser/themeing.ts @@ -37,7 +37,7 @@ export class WebviewThemeDataProvider extends Disposable { this._reset(); })); - const webviewConfigurationKeys = ['editor.fontFamily', 'editor.fontWeight', 'editor.fontSize']; + const webviewConfigurationKeys = ['editor.fontFamily', 'editor.fontWeight', 'editor.fontSize', 'accessibility.underlineLinks']; this._register(this._configurationService.onDidChangeConfiguration(e => { if (webviewConfigurationKeys.some(key => e.affectsConfiguration(key))) { this._reset(); @@ -55,6 +55,7 @@ export class WebviewThemeDataProvider extends Disposable { const editorFontFamily = configuration.fontFamily || EDITOR_FONT_DEFAULTS.fontFamily; const editorFontWeight = configuration.fontWeight || EDITOR_FONT_DEFAULTS.fontWeight; const editorFontSize = configuration.fontSize || EDITOR_FONT_DEFAULTS.fontSize; + const linkUnderlines = this._configurationService.getValue('accessibility.underlineLinks'); const theme = this._themeService.getColorTheme(); const exportedColors = colorRegistry.getColorRegistry().getColors().reduce>((colors, entry) => { @@ -72,6 +73,7 @@ export class WebviewThemeDataProvider extends Disposable { 'vscode-editor-font-family': editorFontFamily, 'vscode-editor-font-weight': editorFontWeight, 'vscode-editor-font-size': editorFontSize + 'px', + 'text-link-decoration': linkUnderlines ? 'underline' : 'none', ...exportedColors }; diff --git a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css index 80cddd5958b..f979b739601 100644 --- a/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css +++ b/src/vs/workbench/contrib/welcomeGettingStarted/browser/media/gettingStarted.css @@ -923,6 +923,7 @@ .monaco-workbench .part.editor > .content .gettingStartedContainer .button-link { color: var(--vscode-textLink-foreground); + text-decoration: var(--text-link-decoration); } .monaco-workbench .part.editor > .content .gettingStartedContainer .button-link .codicon { diff --git a/src/vs/workbench/contrib/welcomeWalkthrough/browser/media/walkThroughPart.css b/src/vs/workbench/contrib/welcomeWalkthrough/browser/media/walkThroughPart.css index 9622dd1b858..7ab127eaab4 100644 --- a/src/vs/workbench/contrib/welcomeWalkthrough/browser/media/walkThroughPart.css +++ b/src/vs/workbench/contrib/welcomeWalkthrough/browser/media/walkThroughPart.css @@ -18,7 +18,7 @@ } .monaco-workbench .part.editor > .content .walkThroughContent a { - text-decoration: none; + text-decoration: var(--text-link-decoration); } .monaco-workbench .part.editor > .content .walkThroughContent a:focus, From e9b254e731c3e849a6e8b06521f1b07432ab73b4 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Tue, 25 Jun 2024 11:30:26 -0700 Subject: [PATCH 0109/2222] use chord instead of label (#218107) --- .../browser/replInputHintContentWidget.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts b/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts index e7f72559e62..b42ea604df8 100644 --- a/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts +++ b/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts @@ -7,6 +7,7 @@ import * as dom from 'vs/base/browser/dom'; import { status } from 'vs/base/browser/ui/aria/aria'; import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel'; import { Event } from 'vs/base/common/event'; +import { ResolvedKeybinding } from 'vs/base/common/keybindings'; import { Disposable } from 'vs/base/common/lifecycle'; import { OS } from 'vs/base/common/platform'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from 'vs/editor/browser/editorBrowser'; @@ -121,25 +122,29 @@ export class ReplInputHintContentWidget extends Disposable implements IContentWi private getKeybinding() { const keybindings = this.keybindingService.lookupKeybindings('interactive.execute'); const shiftEnterConfig = this.configurationService.getValue(InteractiveWindowSetting.executeWithShiftEnter); + const hasChord = (chord: string, kb: ResolvedKeybinding) => { + const chords = kb.getDispatchChords(); + return chords.length === 1 && chords[0] === chord; + }; if (shiftEnterConfig) { - const keybinding = keybindings.find(kb => kb.getLabel() === 'Shift+Enter'); + const keybinding = keybindings.find(kb => hasChord('shift+Enter', kb)); if (keybinding) { return keybinding; } } else { - let keybinding = keybindings.find(kb => kb.getLabel() === 'Enter'); + let keybinding = keybindings.find(kb => hasChord('Enter', kb)); if (keybinding) { return keybinding; } keybinding = this.keybindingService.lookupKeybindings('python.execInREPLEnter') - .find(kb => kb.getLabel() === 'Enter'); + .find(kb => hasChord('Enter', kb)); if (keybinding) { return keybinding; } } - return undefined; + return keybindings?.[0]; } override dispose(): void { From 3106eec8ecdd4905e98af8b62b0dee2aea92190b Mon Sep 17 00:00:00 2001 From: Eleanor Boyd Date: Tue, 25 Jun 2024 11:37:44 -0700 Subject: [PATCH 0110/2222] add anthony to endgame notebook --- .vscode/notebooks/my-endgame.github-issues | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index ce02ecfa577..0b260270ed7 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -157,7 +157,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:*out-of-scope -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen -author:benibenj -author:luabud" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed sort:updated-asc label:bug -label:unreleased -label:verified -label:z-author-verified -label:on-testplan -label:*duplicate -label:duplicate -label:invalid -label:*as-designed -label:*out-of-scope -label:error-telemetry -label:verification-steps-needed -label:verification-found -author:aeschli -author:alexdima -author:alexr00 -author:AmandaSilver -author:andreamah -author:bamurtaugh -author:bpasero -author:chrisdias -author:chrmarti -author:Chuxel -author:claudiaregio -author:connor4312 -author:dbaeumer -author:deepak1556 -author:devinvalenciano -author:digitarald -author:DonJayamanne -author:egamma -author:fiveisprime -author:gregvanl -author:hediet -author:isidorn -author:joaomoreno -author:joyceerhl -author:jrieken -author:kieferrm -author:lramos15 -author:lszomoru -author:meganrogge -author:misolori -author:mjbvz -author:rebornix -author:roblourens -author:rzhao271 -author:sandy081 -author:sbatten -author:stevencl -author:tanhakabir -author:TylerLeonhardt -author:Tyriar -author:weinand -author:amunger -author:karthiknadig -author:eleanorjboyd -author:Yoyokrazy -author:paulacamargo25 -author:ulugbekna -author:aiday-mar -author:daviddossett -author:bhavyaus -author:justschen -author:benibenj -author:luabud -author:anthonykim1" }, { "kind": 1, From 01ef3f75267ede77ea95f2f59702b54f680a3873 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jun 2024 21:02:05 +0200 Subject: [PATCH 0111/2222] Fix #218058 (#218182) --- .../extensionManagement/common/extensionsScannerService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 74c8eeaeb1f..a65142d2c65 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -657,7 +657,7 @@ class ExtensionsScanner extends Disposable { const type = metadata?.isSystem ? ExtensionType.System : input.type; const isBuiltin = type === ExtensionType.System || !!metadata?.isBuiltin; manifest = await this.translateManifest(input.location, manifest, ExtensionScannerInput.createNlsConfiguration(input)); - if (manifest.enabledApiProposals && !this.extensionsEnabledWithApiProposalVersion?.includes(id.toLowerCase())) { + if (manifest.enabledApiProposals && this.extensionsEnabledWithApiProposalVersion?.includes(id.toLowerCase())) { manifest.enabledApiProposals = parseEnabledApiProposalNames([...manifest.enabledApiProposals]); } const extension: IRelaxedScannedExtension = { From 393e1222caaea0b1c1d21138bd493d7a2b9bc1ac Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Tue, 25 Jun 2024 21:52:08 +0200 Subject: [PATCH 0112/2222] proper fix for #218058 (#218200) --- .../common/extensionsScannerService.ts | 16 ++++++++++------ .../extensions/common/extensionValidator.ts | 4 ++-- .../browser/webExtensionsScannerService.ts | 15 +++++++++++---- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index a65142d2c65..1b6d8f3dab9 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -657,10 +657,7 @@ class ExtensionsScanner extends Disposable { const type = metadata?.isSystem ? ExtensionType.System : input.type; const isBuiltin = type === ExtensionType.System || !!metadata?.isBuiltin; manifest = await this.translateManifest(input.location, manifest, ExtensionScannerInput.createNlsConfiguration(input)); - if (manifest.enabledApiProposals && this.extensionsEnabledWithApiProposalVersion?.includes(id.toLowerCase())) { - manifest.enabledApiProposals = parseEnabledApiProposalNames([...manifest.enabledApiProposals]); - } - const extension: IRelaxedScannedExtension = { + let extension: IRelaxedScannedExtension = { type, identifier, manifest, @@ -672,7 +669,13 @@ class ExtensionsScanner extends Disposable { isValid: true, validations: [] }; - return input.validate ? this.validate(extension, input) : extension; + if (input.validate) { + extension = this.validate(extension, input); + } + if (manifest.enabledApiProposals && this.extensionsEnabledWithApiProposalVersion.includes(id.toLowerCase())) { + manifest.enabledApiProposals = parseEnabledApiProposalNames([...manifest.enabledApiProposals]); + } + return extension; } } catch (e) { if (input.type !== ExtensionType.System) { @@ -684,7 +687,8 @@ class ExtensionsScanner extends Disposable { validate(extension: IRelaxedScannedExtension, input: ExtensionScannerInput): IRelaxedScannedExtension { let isValid = true; - const validations = validateExtensionManifest(input.productVersion, input.productDate, input.location, extension.manifest, extension.isBuiltin); + const validateApiVersion = this.extensionsEnabledWithApiProposalVersion.includes(extension.identifier.id.toLowerCase()); + const validations = validateExtensionManifest(input.productVersion, input.productDate, input.location, extension.manifest, extension.isBuiltin, validateApiVersion); for (const [severity, message] of validations) { if (severity === Severity.Error) { isValid = false; diff --git a/src/vs/platform/extensions/common/extensionValidator.ts b/src/vs/platform/extensions/common/extensionValidator.ts index 08cb360de78..5fae8b4b1e2 100644 --- a/src/vs/platform/extensions/common/extensionValidator.ts +++ b/src/vs/platform/extensions/common/extensionValidator.ts @@ -240,7 +240,7 @@ export function isValidVersion(_inputVersion: string | INormalizedVersion, _inpu type ProductDate = string | Date | undefined; -export function validateExtensionManifest(productVersion: string, productDate: ProductDate, extensionLocation: URI, extensionManifest: IExtensionManifest, extensionIsBuiltin: boolean): readonly [Severity, string][] { +export function validateExtensionManifest(productVersion: string, productDate: ProductDate, extensionLocation: URI, extensionManifest: IExtensionManifest, extensionIsBuiltin: boolean, validateApiVersion: boolean): readonly [Severity, string][] { const validations: [Severity, string][] = []; if (typeof extensionManifest.publisher !== 'undefined' && typeof extensionManifest.publisher !== 'string') { validations.push([Severity.Error, nls.localize('extensionDescription.publisher', "property publisher must be of type `string`.")]); @@ -322,7 +322,7 @@ export function validateExtensionManifest(productVersion: string, productDate: P } } - if (extensionManifest.enabledApiProposals?.length) { + if (validateApiVersion && extensionManifest.enabledApiProposals?.length) { const incompatibleNotices: string[] = []; if (!areApiProposalsCompatible([...extensionManifest.enabledApiProposals], incompatibleNotices)) { for (const notice of incompatibleNotices) { diff --git a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts index 17c6ac2cb10..525a2961875 100644 --- a/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts +++ b/src/vs/workbench/services/extensionManagement/browser/webExtensionsScannerService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionIdentifier, IExtension, IExtensionManifest, TargetPlatform } from 'vs/platform/extensions/common/extensions'; +import { IBuiltinExtensionsScannerService, ExtensionType, IExtensionIdentifier, IExtension, IExtensionManifest, TargetPlatform, IRelaxedExtensionManifest, parseEnabledApiProposalNames } from 'vs/platform/extensions/common/extensions'; import { IBrowserWorkbenchEnvironmentService } from 'vs/workbench/services/environment/browser/environmentService'; import { IScannedExtension, IWebExtensionsScannerService, ScanOptions } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { isWeb, Language } from 'vs/base/common/platform'; @@ -99,6 +99,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten private readonly systemExtensionsCacheResource: URI | undefined = undefined; private readonly customBuiltinExtensionsCacheResource: URI | undefined = undefined; private readonly resourcesAccessQueueMap = new ResourceMap>(); + private readonly extensionsEnabledWithApiProposalVersion: string[]; constructor( @IBrowserWorkbenchEnvironmentService private readonly environmentService: IBrowserWorkbenchEnvironmentService, @@ -123,6 +124,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten // Eventually update caches lifecycleService.when(LifecyclePhase.Eventually).then(() => this.updateCaches()); } + this.extensionsEnabledWithApiProposalVersion = productService.extensionsEnabledWithApiProposalVersion?.map(id => id.toLowerCase()) ?? []; } private _customBuiltinExtensionsInfoPromise: Promise<{ extensions: ExtensionInfo[]; extensionsToMigrate: [string, string][]; extensionLocations: URI[]; extensionGalleryResources: URI[] }> | undefined; @@ -735,7 +737,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten private async toScannedExtension(webExtension: IWebExtension, isBuiltin: boolean, type: ExtensionType = ExtensionType.User): Promise { const validations: [Severity, string][] = []; - let manifest: IExtensionManifest | undefined = webExtension.manifest; + let manifest: IRelaxedExtensionManifest | undefined = webExtension.manifest; if (!manifest) { try { @@ -766,7 +768,8 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten const uuid = (webExtension.metadata)?.id; - validations.push(...validateExtensionManifest(this.productService.version, this.productService.date, webExtension.location, manifest, false)); + const validateApiVersion = this.extensionsEnabledWithApiProposalVersion.includes(webExtension.identifier.id.toLowerCase()); + validations.push(...validateExtensionManifest(this.productService.version, this.productService.date, webExtension.location, manifest, false, validateApiVersion)); let isValid = true; for (const [severity, message] of validations) { if (severity === Severity.Error) { @@ -775,6 +778,10 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten } } + if (manifest.enabledApiProposals && validateApiVersion) { + manifest.enabledApiProposals = parseEnabledApiProposalNames([...manifest.enabledApiProposals]); + } + return { identifier: { id: webExtension.identifier.id, uuid: webExtension.identifier.uuid || uuid }, location: webExtension.location, @@ -800,7 +807,7 @@ export class WebExtensionsScannerService extends Disposable implements IWebExten return []; } - private async translateManifest(manifest: IExtensionManifest, nlsURL: ITranslations | URI, fallbackNLS?: ITranslations | URI): Promise { + private async translateManifest(manifest: IExtensionManifest, nlsURL: ITranslations | URI, fallbackNLS?: ITranslations | URI): Promise { try { const translations = URI.isUri(nlsURL) ? await this.getTranslations(nlsURL) : nlsURL; const fallbackTranslations = URI.isUri(fallbackNLS) ? await this.getTranslations(fallbackNLS) : fallbackNLS; From 477babf454bc990c6dcc746ea281be4623d357e3 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 25 Jun 2024 13:57:52 -0700 Subject: [PATCH 0113/2222] Opt in to link underlines for settings commands --- .../contrib/preferences/browser/media/settingsEditor2.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 74caeb8c44e..eb2ca0e1907 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -512,6 +512,11 @@ color: var(--vscode-textLink-foreground); } +.settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-markdown a, +.settings-editor > .settings-body .settings-tree-container .setting-item-contents .edit-in-settings-button { + text-decoration: var(--text-link-decoration); +} + .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-markdown a:focus, .settings-editor > .settings-body .settings-tree-container .setting-item-contents .edit-in-settings-button:focus { outline: 1px solid -webkit-focus-ring-color; From 75280e7c4363bc63fb9d4991da0d72217f370c35 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 25 Jun 2024 14:16:13 -0700 Subject: [PATCH 0114/2222] Opt in to link underlines in extensions details page --- .../contrib/extensions/browser/media/extensionEditor.css | 1 + .../contrib/extensions/browser/media/extensionsWidgets.css | 1 + 2 files changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index a59e7c53d77..1de72e81ee9 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -485,6 +485,7 @@ overflow: hidden; white-space: nowrap; text-overflow: ellipsis; + text-decoration: var(--text-link-decoration); } .extension-editor > .body > .content > .details > .additional-details-container .resources-container > .resources > .resource:hover { diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css index d77881dfa39..9179fa761d3 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css @@ -39,6 +39,7 @@ .extension-verified-publisher > .extension-verified-publisher-domain { padding-left: 2px; color: var(--vscode-extensionIcon-verifiedForeground); + text-decoration: var(--text-link-decoration); } .extension-bookmark { From 9191f5dbeb05cfc0c32ed661be2c7a3f39aaf037 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 25 Jun 2024 14:26:22 -0700 Subject: [PATCH 0115/2222] Use link decoration in ports view --- src/vs/workbench/contrib/remote/browser/media/tunnelView.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/workbench/contrib/remote/browser/media/tunnelView.css b/src/vs/workbench/contrib/remote/browser/media/tunnelView.css index 70a8ba93492..b52d115425c 100644 --- a/src/vs/workbench/contrib/remote/browser/media/tunnelView.css +++ b/src/vs/workbench/contrib/remote/browser/media/tunnelView.css @@ -44,6 +44,11 @@ margin-top: -40px; } +.ports-view .monaco-list .monaco-list-row .ports-view-actionbar-cell .ports-view-actionbar-cell-localaddress { + color: var(--vscode-textLink-foreground); + text-decoration: var(--text-link-decoration); +} + .ports-view .monaco-list .monaco-list-row .ports-view-actionbar-cell .ports-view-actionbar-cell-localaddress:hover { text-decoration: underline; } From 1ab0dbf34bea5e1f686f2929396331a1c6dfac40 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 25 Jun 2024 14:54:14 -0700 Subject: [PATCH 0116/2222] Opt in to link underlines in hover status bar actions --- src/vs/base/browser/ui/hover/hoverWidget.css | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/base/browser/ui/hover/hoverWidget.css b/src/vs/base/browser/ui/hover/hoverWidget.css index a1d72c2e581..a1b3b43211e 100644 --- a/src/vs/base/browser/ui/hover/hoverWidget.css +++ b/src/vs/base/browser/ui/hover/hoverWidget.css @@ -134,6 +134,11 @@ padding-right: 4px; } +.monaco-hover .hover-row.status-bar .actions .action-container a { + color: var(--vscode-textLink-foreground); + text-decoration: var(--text-link-decoration); +} + .monaco-hover .markdown-hover .hover-contents .codicon { color: inherit; font-size: inherit; From 9b81d8a366bbe07d2a48f6f73be706170aac2005 Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 25 Jun 2024 14:57:07 -0700 Subject: [PATCH 0117/2222] Opt in language status actions too --- .../contrib/languageStatus/browser/media/languageStatus.css | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/languageStatus/browser/media/languageStatus.css b/src/vs/workbench/contrib/languageStatus/browser/media/languageStatus.css index 4354ad022df..25433bee4a4 100644 --- a/src/vs/workbench/contrib/languageStatus/browser/media/languageStatus.css +++ b/src/vs/workbench/contrib/languageStatus/browser/media/languageStatus.css @@ -113,6 +113,7 @@ .monaco-workbench .hover-language-status > .element .right .monaco-link { margin: auto 0; white-space: nowrap; + text-decoration: var(--text-link-decoration); } .monaco-workbench .hover-language-status > .element .right .monaco-action-bar:not(:first-child) { From e076510d89114ae8715e921be5101b2dd81d5eaa Mon Sep 17 00:00:00 2001 From: David Dossett Date: Tue, 25 Jun 2024 15:12:55 -0700 Subject: [PATCH 0118/2222] Use link color and underline var for setting overrides links (#218212) --- .../contrib/preferences/browser/media/settingsEditor2.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index eb2ca0e1907..b42d6a194a7 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -408,7 +408,8 @@ } .settings-editor > .settings-body .settings-tree-container .setting-item-contents .setting-item-title .setting-item-overrides a.modified-scope { - text-decoration: underline; + color: var(--vscode-textLink-foreground); + text-decoration: var(--text-link-decoration); cursor: pointer; } From 45db560b8daad3459c5a4772622c9536c6305479 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 25 Jun 2024 16:09:55 -0700 Subject: [PATCH 0119/2222] Check disposal in async rendering (#218235) Fixes #218000 --- src/vs/workbench/contrib/chat/browser/codeBlockPart.ts | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index 9346615d487..c9e952ffcd0 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -144,6 +144,7 @@ export class CodeBlockPart extends Disposable { private currentScrollWidth = 0; private readonly disposableStore = this._register(new DisposableStore()); + private isDisposed = false; constructor( private readonly options: ChatEditorOptions, @@ -264,6 +265,11 @@ export class CodeBlockPart extends Disposable { } } + override dispose() { + this.isDisposed = true; + super.dispose(); + } + get uri(): URI | undefined { return this.editor.getModel()?.uri; } @@ -356,6 +362,9 @@ export class CodeBlockPart extends Disposable { } await this.updateEditor(data); + if (this.isDisposed) { + return; + } this.layout(width); if (editable) { From e53968c415d2021cb1bf550f3cc89c0ffbab4f66 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 25 Jun 2024 16:41:10 -0700 Subject: [PATCH 0120/2222] Re #209158. Output replacement might use legacy output id. (#218237) --- .../contrib/notebook/browser/controller/cellOutputActions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index db449afafdb..1350fefa69d 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -96,7 +96,7 @@ function getOutputViewModelFromId(outputId: string, notebookEditor: INotebookEdi if (notebookViewModel) { const codeCells = notebookViewModel.viewCells.filter(cell => cell.cellKind === CellKind.Code) as CodeCellViewModel[]; for (const cell of codeCells) { - const output = cell.outputsViewModels.find(output => output.model.outputId === outputId); + const output = cell.outputsViewModels.find(output => output.model.outputId === outputId || output.model.alternativeOutputId === outputId); if (output) { return output; } From 0feeac7b7618ea5301168ec5c3f1177ccb743a13 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 25 Jun 2024 18:23:34 -0700 Subject: [PATCH 0121/2222] Still fix incomplete markdown when the response is complete but still streaming (#218241) Fix #217915 --- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 5a8915ba0ce..01a8fccfea9 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -822,7 +822,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer acc + (part instanceof ChatMarkdownContentPart ? part.codeblocks.length : 0), 0); const markdownPart = this.instantiationService.createInstance(ChatMarkdownContentPart, markdown, context, this._editorPool, fillInIncompleteTokens, codeBlockStartIndex, this.renderer, this._currentLayoutWidth, this.codeBlockModelCollection, this.rendererOptions); markdownPart.addDisposable(markdownPart.onDidChangeHeight(() => { From 6e345d46061e168159401db49cd659c89573ac16 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Tue, 25 Jun 2024 22:34:31 -0700 Subject: [PATCH 0122/2222] Remove getSessions API in favor of getAccounts (#218238) Azure folks (the only ones using this proposed API) have not depended on getSessions yet so it's safe to delete. --- .../api/browser/mainThreadAuthentication.ts | 12 ------------ .../workbench/api/common/extHost.api.impl.ts | 4 ---- .../workbench/api/common/extHost.protocol.ts | 1 - .../api/common/extHostAuthentication.ts | 11 ----------- .../vscode.proposed.authGetSessions.d.ts | 19 ------------------- 5 files changed, 47 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index c60acfa9739..5780ec8797e 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -260,18 +260,6 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu return session; } - async $getSessions(providerId: string, scopes: readonly string[], extensionId: string, extensionName: string): Promise { - const sessions = await this.authenticationService.getSessions(providerId, [...scopes], undefined, true); - const accessibleSessions = sessions.filter(s => this.authenticationAccessService.isAccessAllowed(providerId, s.account.label, extensionId)); - if (accessibleSessions.length) { - this.sendProviderUsageTelemetry(extensionId, providerId); - for (const session of accessibleSessions) { - this.authenticationUsageService.addAccountUsage(providerId, session.account.label, extensionId, extensionName); - } - } - return accessibleSessions; - } - async $getAccounts(providerId: string): Promise> { const sessions = await this.authenticationService.getSessions(providerId); const accounts = new Array(); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index fe15a50233d..4b6a73823dc 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -294,10 +294,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } return extHostAuthentication.getSession(extension, providerId, scopes, options as any); }, - getSessions(providerId: string, scopes: readonly string[]) { - checkProposedApiEnabled(extension, 'authGetSessions'); - return extHostAuthentication.getSessions(extension, providerId, scopes); - }, getAccounts(providerId: string) { checkProposedApiEnabled(extension, 'authGetSessions'); return extHostAuthentication.getAccounts(providerId); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 8fb7d046b4d..5785393c0e1 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -162,7 +162,6 @@ export interface MainThreadAuthenticationShape extends IDisposable { $ensureProvider(id: string): Promise; $sendDidChangeSessions(providerId: string, event: AuthenticationSessionsChangeEvent): void; $getSession(providerId: string, scopes: readonly string[], extensionId: string, extensionName: string, options: { createIfNone?: boolean; forceNewSession?: boolean | AuthenticationForceNewSessionOptions; clearSessionPreference?: boolean }): Promise; - $getSessions(providerId: string, scopes: readonly string[], extensionId: string, extensionName: string): Promise; $getAccounts(providerId: string): Promise>; $removeSession(providerId: string, sessionId: string): Promise; } diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index 5c75a1318ca..c5ba402ef07 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -32,7 +32,6 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { readonly onDidChangeSessions: Event = this._onDidChangeSessions.event; private _getSessionTaskSingler = new TaskSingler(); - private _getSessionsTaskSingler = new TaskSingler>(); constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService @@ -54,16 +53,6 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { }); } - async getSessions(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[]): Promise> { - const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier); - const sortedScopes = [...scopes].sort().join(' '); - return await this._getSessionsTaskSingler.getOrCreate(`${extensionId} ${sortedScopes}`, async () => { - await this._proxy.$ensureProvider(providerId); - const extensionName = requestingExtension.displayName || requestingExtension.name; - return this._proxy.$getSessions(providerId, scopes, extensionId, extensionName); - }); - } - async getAccounts(providerId: string) { await this._proxy.$ensureProvider(providerId); return await this._proxy.$getAccounts(providerId); diff --git a/src/vscode-dts/vscode.proposed.authGetSessions.d.ts b/src/vscode-dts/vscode.proposed.authGetSessions.d.ts index 45049789f50..6d425ed3232 100644 --- a/src/vscode-dts/vscode.proposed.authGetSessions.d.ts +++ b/src/vscode-dts/vscode.proposed.authGetSessions.d.ts @@ -67,23 +67,4 @@ declare module 'vscode' { */ createSession(scopes: readonly string[], options: AuthenticationProviderSessionOptions): Thenable; } - - // THE BELOW IS THE OLD PROPOSAL WHICH WILL BE REMOVED BUT KEPT UNTIL THE NEW PROPOSAL IS ADOPTED BY ALL PARTIES - - export namespace authentication { - /** - * Get all authentication sessions matching the desired scopes that this extension has access to. In order to request access, - * use {@link getSession}. To request an additional account, specify {@link AuthenticationGetSessionOptions.clearSessionPreference} - * and {@link AuthenticationGetSessionOptions.createIfNone} together. - * - * Currently, there are only two authentication providers that are contributed from built in extensions - * to the editor that implement GitHub and Microsoft authentication: their providerId's are 'github' and 'microsoft'. - * - * @param providerId The id of the provider to use - * @param scopes A list of scopes representing the permissions requested. These are dependent on the authentication provider - * @returns A thenable that resolves to a readonly array of authentication sessions. - * @deprecated Use {@link getAccounts} instead. - */ - export function getSessions(providerId: string, scopes: readonly string[]): Thenable; - } } From df8835a974606e016f3526b349ddd9808fc91a5e Mon Sep 17 00:00:00 2001 From: Robo Date: Wed, 26 Jun 2024 14:34:51 +0900 Subject: [PATCH 0123/2222] fix: processing runtime arguments with non boolean values (#218243) --- src/main.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/main.js b/src/main.js index 7da82450a4c..1b3d4632fad 100644 --- a/src/main.js +++ b/src/main.js @@ -246,10 +246,7 @@ function configureCommandlineSwitchesSync(cliArgs) { app.commandLine.appendSwitch(argvKey); } } else if (argvValue) { - if (argvKey === 'force-color-profile') { - // Color profile - app.commandLine.appendSwitch(argvKey, argvValue); - } else if (argvKey === 'password-store') { + if (argvKey === 'password-store') { // Password store // TODO@TylerLeonhardt: Remove this migration in 3 months let migratedArgvValue = argvValue; @@ -257,6 +254,8 @@ function configureCommandlineSwitchesSync(cliArgs) { migratedArgvValue = 'gnome-libsecret'; } app.commandLine.appendSwitch(argvKey, migratedArgvValue); + } else { + app.commandLine.appendSwitch(argvKey, argvValue); } } } From 0354163c1c66b950b0762364f5b4cd37937b624a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:35:35 +0200 Subject: [PATCH 0124/2222] SCM - fix commit input text selection (#218249) --- .../contrib/scm/browser/media/scm.css | 17 ---------- .../contrib/scm/browser/scmViewPane.ts | 32 +++++++++++++++++-- 2 files changed, 30 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index a84c1dd22c9..7b94eac4187 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -353,23 +353,6 @@ border-radius: 2px; } -.scm-view .scm-editor-container .monaco-editor .selected-text { - background-color: rgba(var(--vscode-input-background), 0.4); - border-radius: 0; -} - -.scm-view .scm-editor-container .monaco-editor .focused .selected-text { - background-color: var(--vscode-selection-background, var(--vscode-editor-selectionBackground)); -} - -.scm-view .scm-editor-container .monaco-editor .view-line span.inline-selected-text { - color: var(--vscode-input-foreground); -} - -.scm-view .scm-editor-container .monaco-editor-background { - background-color: var(--vscode-input-background); -} - .scm-view .scm-editor { box-sizing: border-box; width: 100%; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 0961bec2627..8b47b5cec4d 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -23,7 +23,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { MenuItemAction, IMenuService, registerAction2, MenuId, IAction2Options, MenuRegistry, Action2, IMenu } from 'vs/platform/actions/common/actions'; import { IAction, ActionRunner, Action, Separator, IActionRunner } from 'vs/base/common/actions'; import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IThemeService, IFileIconTheme } from 'vs/platform/theme/common/themeService'; +import { IThemeService, IFileIconTheme, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar, isSCMRepository, isSCMInput, collectContextMenuActions, getActionViewItemProvider, isSCMActionButton, isSCMViewService, isSCMHistoryItemGroupTreeElement, isSCMHistoryItemTreeElement, isSCMHistoryItemChangeTreeElement, toDiffEditorArguments, isSCMResourceNode, isSCMHistoryItemChangeNode, isSCMViewSeparator, connectPrimaryMenu, isSCMHistoryItemViewModelTreeElement } from './util'; import { WorkbenchCompressibleAsyncDataTree, IOpenEvent } from 'vs/platform/list/browser/listService'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; @@ -96,7 +96,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { EditOperation } from 'vs/editor/common/core/editOperation'; import { stripIcons } from 'vs/base/common/iconLabels'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; -import { foreground, listActiveSelectionForeground, registerColor, transparent } from 'vs/platform/theme/common/colorRegistry'; +import { editorSelectionBackground, foreground, inputBackground, inputForeground, listActiveSelectionForeground, registerColor, selectionBackground, transparent } from 'vs/platform/theme/common/colorRegistry'; import { IMenuWorkbenchToolBarOptions, MenuWorkbenchToolBar, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; @@ -4246,3 +4246,31 @@ export class SCMActionButton implements IDisposable { } } } + +// Override styles in selections.ts +registerThemingParticipant((theme, collector) => { + const selectionBackgroundColor = theme.getColor(selectionBackground); + + if (selectionBackgroundColor) { + // Override inactive selection bg + const inputBackgroundColor = theme.getColor(inputBackground); + if (inputBackgroundColor) { + collector.addRule(`.scm-view .scm-editor-container .monaco-editor .selected-text { background-color: ${inputBackgroundColor.transparent(0.4)}; }`); + } + + // Override selected fg + const inputForegroundColor = theme.getColor(inputForeground); + if (inputForegroundColor) { + collector.addRule(`.scm-view .scm-editor-container .monaco-editor .view-line span.inline-selected-text { color: ${inputForegroundColor}; }`); + } + + const backgroundColor = theme.getColor(inputBackground); + if (backgroundColor) { + collector.addRule(`.scm-view .scm-editor-container .monaco-editor-background { background-color: ${backgroundColor}; } `); + } + collector.addRule(`.scm-view .scm-editor-container .monaco-editor .focused .selected-text { background-color: ${selectionBackgroundColor}; }`); + } else { + // Use editor selection color if theme has not set a selection background color + collector.addRule(`.scm-view .scm-editor-container .monaco-editor .focused .selected-text { background-color: ${theme.getColor(editorSelectionBackground)}; }`); + } +}); From 44623fa268474d0b3ecac7824a56f57b4c59fe81 Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 26 Jun 2024 12:14:04 +0200 Subject: [PATCH 0125/2222] Add action label and keybindings for the editor hover status bar in the editor hover accessible view/help (#218269) adding label and keybindings into the editor hover accessibility view and help --- src/vs/base/browser/ui/hover/hoverWidget.ts | 6 ++++++ .../hover/browser/contentHoverRendered.ts | 21 ++++++++++++++++++- .../hover/browser/contentHoverStatusBar.ts | 6 +++++- 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/ui/hover/hoverWidget.ts b/src/vs/base/browser/ui/hover/hoverWidget.ts index 2e9ecbdd1fe..9d836121267 100644 --- a/src/vs/base/browser/ui/hover/hoverWidget.ts +++ b/src/vs/base/browser/ui/hover/hoverWidget.ts @@ -50,12 +50,18 @@ export class HoverAction extends Disposable { return new HoverAction(parent, actionOptions, keybindingLabel); } + public readonly actionLabel: string; + public readonly actionKeybindingLabel: string | null; + private readonly actionContainer: HTMLElement; private readonly action: HTMLElement; private constructor(parent: HTMLElement, actionOptions: { label: string; iconClass?: string; run: (target: HTMLElement) => void; commandId: string }, keybindingLabel: string | null) { super(); + this.actionLabel = actionOptions.label; + this.actionKeybindingLabel = keybindingLabel; + this.actionContainer = dom.append(parent, $('div.action-container')); this.actionContainer.setAttribute('tabindex', '0'); diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index 6384984b529..e808e9d5942 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -21,6 +21,7 @@ import { ColorHoverParticipant } from 'vs/editor/contrib/colorPicker/browser/col import { localize } from 'vs/nls'; import { InlayHintsHover } from 'vs/editor/contrib/inlayHints/browser/inlayHintsHover'; import { BugIndicatingError } from 'vs/base/common/errors'; +import { HoverAction } from 'vs/base/browser/ui/hover/hoverWidget'; export class RenderedContentHover extends Disposable { @@ -175,6 +176,10 @@ interface IRenderedContentStatusBar { * The HTML element containing the hover status bar. */ hoverElement: HTMLElement; + /** + * The actions of the hover status bar. + */ + actions: HoverAction[]; } type IRenderedContentHoverPartOrStatusBar = IRenderedContentHoverPart | IRenderedContentStatusBar; @@ -189,6 +194,10 @@ class RenderedStatusBar implements IDisposable { return this._statusBar.hoverElement; } + get actions(): HoverAction[] { + return this._statusBar.actions; + } + dispose() { this._statusBar.dispose(); } @@ -270,6 +279,7 @@ class RenderedContentHoverParts extends Disposable { this._renderedParts.push({ type: 'statusBar', hoverElement: renderedStatusBar.hoverElement, + actions: renderedStatusBar.actions, }); } return toDisposable(() => { disposables.dispose(); }); @@ -332,7 +342,16 @@ class RenderedContentHoverParts extends Disposable { return ''; } if (renderedPart.type === 'statusBar') { - return localize('hoverAccessibilityStatusBar', "This is a hover status bar."); + const statusBarDescription = [localize('hoverAccessibilityStatusBar', "This is a hover status bar.")]; + for (const action of renderedPart.actions) { + const keybinding = action.actionKeybindingLabel; + if (keybinding) { + statusBarDescription.push(localize('hoverAccessibilityStatusBarActionWithKeybinding', "It has an action with label {0} and keybinding {1}.", action.actionLabel, keybinding)); + } else { + statusBarDescription.push(localize('hoverAccessibilityStatusBarActionWithoutKeybinding', "It has an action with label {0}.", action.actionLabel)); + } + } + return statusBarDescription.join('\n'); } return renderedPart.participant.getAccessibleContent(renderedPart.hoverPart); } diff --git a/src/vs/editor/contrib/hover/browser/contentHoverStatusBar.ts b/src/vs/editor/contrib/hover/browser/contentHoverStatusBar.ts index 04d84593d06..bb43959419b 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverStatusBar.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverStatusBar.ts @@ -13,6 +13,8 @@ const $ = dom.$; export class EditorHoverStatusBar extends Disposable implements IEditorHoverStatusBar { public readonly hoverElement: HTMLElement; + public readonly actions: HoverAction[] = []; + private readonly actionsElement: HTMLElement; private _hasContent: boolean = false; @@ -39,7 +41,9 @@ export class EditorHoverStatusBar extends Disposable implements IEditorHoverStat const keybinding = this._keybindingService.lookupKeybinding(actionOptions.commandId); const keybindingLabel = keybinding ? keybinding.getLabel() : null; this._hasContent = true; - return this._register(HoverAction.render(this.actionsElement, actionOptions, keybindingLabel)); + const action = this._register(HoverAction.render(this.actionsElement, actionOptions, keybindingLabel)); + this.actions.push(action); + return action; } public append(element: HTMLElement): HTMLElement { From 979ad0af36f79113fd7d5e86705a181743812ccc Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 26 Jun 2024 12:14:41 +0200 Subject: [PATCH 0126/2222] Set focus back to the editor hover part on escape from the accessible view/help (#218274) adding code in order to focus back a specific hover part on escaping from the accessible view/help --- .../contrib/hover/browser/contentHoverController.ts | 4 ++++ .../contrib/hover/browser/contentHoverRendered.ts | 11 +++++++++++ .../contrib/hover/browser/hoverAccessibleViews.ts | 6 +++++- .../editor/contrib/hover/browser/hoverController.ts | 4 ++++ 4 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index 5ae7773bd1d..d79984f24c1 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -321,6 +321,10 @@ export class ContentHoverController extends Disposable implements IHoverWidget { this._contentHoverWidget.focus(); } + public focusHoverPartWithIndex(index: number): void { + this._renderedContentHover?.focusHoverPartWithIndex(index); + } + public scrollUp(): void { this._contentHoverWidget.scrollUp(); } diff --git a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts index e808e9d5942..9308e6f295d 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverRendered.ts @@ -77,6 +77,10 @@ export class RenderedContentHover extends Disposable { return this._renderedHoverParts.focusedHoverPartIndex; } + public focusHoverPartWithIndex(index: number): void { + this._renderedHoverParts.focusHoverPartWithIndex(index); + } + public getAccessibleWidgetContent(): string { return this._renderedHoverParts.getAccessibleContent(); } @@ -328,6 +332,13 @@ class RenderedContentHoverParts extends Disposable { this._colorHoverParticipant = participants.find(p => p instanceof ColorHoverParticipant); } + public focusHoverPartWithIndex(index: number): void { + if (index < 0 || index >= this._renderedParts.length) { + return; + } + this._renderedParts[index].hoverElement.focus(); + } + public getAccessibleContent(): string { const content: string[] = []; for (let i = 0; i < this._renderedParts.length; i++) { diff --git a/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts b/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts index 462df2e9d60..41a7fbdfce9 100644 --- a/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts +++ b/src/vs/editor/contrib/hover/browser/hoverAccessibleViews.ts @@ -117,8 +117,12 @@ abstract class BaseHoverAccessibleViewProvider extends Disposable implements IAc if (!this._hoverController) { return; } + if (this._focusedHoverPartIndex === -1) { + this._hoverController.focus(); + } else { + this._hoverController.focusHoverPartWithIndex(this._focusedHoverPartIndex); + } this._focusedHoverPartIndex = -1; - this._hoverController.focus(); this._hoverController.shouldKeepOpenOnEditorMouseMoveOrLeave = false; this.dispose(); } diff --git a/src/vs/editor/contrib/hover/browser/hoverController.ts b/src/vs/editor/contrib/hover/browser/hoverController.ts index 9dbae0ce643..b6ef34860ca 100644 --- a/src/vs/editor/contrib/hover/browser/hoverController.ts +++ b/src/vs/editor/contrib/hover/browser/hoverController.ts @@ -433,6 +433,10 @@ export class HoverController extends Disposable implements IEditorContribution { this._contentWidget?.focus(); } + public focusHoverPartWithIndex(index: number): void { + this._contentWidget?.focusHoverPartWithIndex(index); + } + public scrollUp(): void { this._contentWidget?.scrollUp(); } From e5d157c069adc9f9d4c6ad31c71ec6cf519f9ed0 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Jun 2024 13:18:47 +0200 Subject: [PATCH 0127/2222] Inline chat fixes (#218318) * fix https://github.com/microsoft/vscode/issues/218059 * Change enablement of "Toggle Changes" instead of visibility mitigates/fixes https://github.com/microsoft/vscode/issues/218099 * have padding per part (actions, input, list) fixes https://github.com/microsoft/vscode/issues/218083 * fixes https://github.com/microsoft/vscode/issues/218021 * fix notebook button groups for inline chat * tweak inline chat content widget when text btn are enabled --- .../contrib/chat/browser/chatListRenderer.ts | 2 +- .../contrib/chat/browser/media/chat.css | 8 +++++++- .../inlineChat/browser/inlineChatActions.ts | 6 +++--- .../browser/inlineChatContentWidget.ts | 8 ++++---- .../inlineChat/browser/inlineChatWidget.ts | 2 +- .../inlineChat/browser/media/inlineChat.css | 18 +++++++++++++----- .../browser/media/inlineChatContentWidget.css | 7 ++++++- .../browser/controller/chat/cellChatActions.ts | 4 ++-- 8 files changed, 37 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 01a8fccfea9..21047f36d1c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -255,7 +255,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer .username { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 063e2de835c..2009001a7ea 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -525,9 +525,9 @@ export class ToggleDiffForChange extends AbstractInlineChatAction { }, menu: { id: MENU_INLINE_CHAT_WIDGET_STATUS, - group: 'more', - when: ContextKeyExpr.and(CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Live), CTX_INLINE_CHAT_CHANGE_HAS_DIFF), - order: 10, + group: 'zzz', + when: ContextKeyExpr.and(CTX_INLINE_CHAT_EDIT_MODE.isEqualTo(EditMode.Live)), + order: 1, } }); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts index e22cab4975c..20004ab3eb2 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts @@ -101,9 +101,9 @@ export class InlineChatContentWidget implements IContentWidget { this._domNode.appendChild(this._inputContainer); this._toolbarContainer.classList.add('toolbar'); - if (!configurationService.getValue(InlineChatConfigKeys.ExpTextButtons)) { - this._toolbarContainer.style.display = 'none'; - this._domNode.style.paddingBottom = '6px'; + if (configurationService.getValue(InlineChatConfigKeys.ExpTextButtons)) { + this._toolbarContainer.style.display = 'inherit'; + this._domNode.style.paddingBottom = '4px'; } this._domNode.appendChild(this._toolbarContainer); @@ -188,7 +188,6 @@ export class InlineChatContentWidget implements IContentWidget { this._focusNext = true; this._editor.revealRangeNearTopIfOutsideViewport(Range.fromPositions(position), ScrollType.Immediate); - this._widget.inputEditor.setValue(''); const wordInfo = this._editor.getModel()?.getWordAtPosition(position); @@ -202,6 +201,7 @@ export class InlineChatContentWidget implements IContentWidget { if (this._visible) { this._visible = false; this._editor.removeContentWidget(this); + this._widget.inputEditor.setValue(''); this._widget.saveState(); this._widget.setVisible(false); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 4bc0e13939f..e5ff46f597f 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -361,7 +361,7 @@ export class InlineChatWidget { } protected _getExtraHeight(): number { - return 4 /* padding */ + 2 /*border*/ + 12 /*shadow*/; + return 4 /* padding */ + 2 /*border*/ + 4 /*shadow*/; } updateProgress(show: boolean) { diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index a345f1cc0c9..e8fd1d5e047 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -13,14 +13,16 @@ .monaco-workbench .inline-chat { color: inherit; - padding: 0 8px; border-radius: 4px; border: 1px solid var(--vscode-inlineChat-border); box-shadow: 0 2px 4px 0 var(--vscode-widget-shadow); - margin-top: 8px; background: var(--vscode-inlineChat-background); } +.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part { + padding: 4px 6px 0 6px; +} + .monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-input-part .interactive-execute-toolbar { margin-bottom: 1px; } @@ -35,8 +37,10 @@ } .monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list .interactive-item-container.interactive-item-compact { - padding: 2px 0; gap: 6px; + padding-top: 2px; + padding-right: 20px; + padding-left: 6px; } .monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list .interactive-item-container.interactive-item-compact .header .avatar { @@ -53,6 +57,10 @@ border: none; } +.monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list .interactive-item-container.minimal > .header { + right: 10px; +} + /* progress bit */ .monaco-workbench .inline-chat .progress { @@ -66,10 +74,11 @@ /* status */ -.monaco-workbench .inline-chat .status { +.monaco-workbench .inline-chat > .status { display: flex; justify-content: space-between; align-items: center; + padding: 4px 6px 0 6px } .monaco-workbench .inline-chat .status .actions.hidden { @@ -147,7 +156,6 @@ display: flex; height: 18px; - padding: 3px 0; /* makes space for action focus borders: https://github.com/microsoft/vscode-copilot/issues/5814 */ .actions-container { gap: 3px diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css index 8800ca13e35..cc5f706404c 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css @@ -5,7 +5,7 @@ .monaco-workbench .inline-chat-content-widget { z-index: 50; - padding: 6px 6px 0px 6px; + padding: 6px; border-radius: 4px; background-color: var(--vscode-inlineChat-background); box-shadow: 0 4px 8px var(--vscode-inlineChat-shadow); @@ -23,3 +23,8 @@ .monaco-workbench .inline-chat-content-widget.interactive-session .interactive-input-part.compact { padding: 0; } + +.monaco-workbench .inline-chat-content-widget.interactive-session .toolbar { + display: none; + padding-top: 4px; +} diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts index f7651b21fe9..f474b4dfc6b 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions.ts @@ -258,7 +258,7 @@ registerAction2(class extends NotebookAction { menu: [ { id: MENU_CELL_CHAT_WIDGET_STATUS, - group: 'inline', + group: '0_main', order: 0, when: CTX_INLINE_CHAT_RESPONSE_TYPE.notEqualsTo(InlineChatResponseType.Messages), } @@ -286,7 +286,7 @@ registerAction2(class extends NotebookAction { }, menu: { id: MENU_CELL_CHAT_WIDGET_STATUS, - group: 'main', + group: '0_main', order: 1 }, f1: false From ea9edd00c565a2287610b6fd89eb34e9d84681cb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Wed, 26 Jun 2024 14:03:10 +0200 Subject: [PATCH 0128/2222] Replace inner text whitespace unicode characters other than `\n` and `\r` with ` ` (#218324) replacing the inner text unicode characters other than \n and \r with whitespace --- .../editor/contrib/hover/browser/markdownHoverParticipant.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index 86626b4bf69..aaeb5796aa6 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -379,7 +379,8 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { if (!renderedHoverPart) { return undefined; } - const accessibleContent = renderedHoverPart.hoverElement.innerText.trim(); + const hoverElementInnerText = renderedHoverPart.hoverElement.innerText; + const accessibleContent = hoverElementInnerText.replace(/[^\S\n\r]+/gu, ' '); return accessibleContent; } From b4eb54551bcfd36866cc714e5702705165d5c67c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?H=2E=20Ng=E1=BB=8Dc=20Minh?= Date: Wed, 26 Jun 2024 19:21:17 +0700 Subject: [PATCH 0129/2222] fix: do not show activity bar's focus border on click (#217837) --- .../browser/parts/activitybar/media/activityaction.css | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css index f42d3307fb7..b40341d217c 100644 --- a/src/vs/workbench/browser/parts/activitybar/media/activityaction.css +++ b/src/vs/workbench/browser/parts/activitybar/media/activityaction.css @@ -104,7 +104,8 @@ display: none; } -.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.clicked:focus:before { +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.clicked:focus:before, +.monaco-workbench .activitybar > .content :not(.monaco-menu) > .monaco-action-bar .action-item.clicked:focus .active-item-indicator::before { border-left: none !important; /* no focus feedback when using mouse */ } From d193d821387ceaf45ee68674348ef3dad0007b02 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 26 Jun 2024 14:40:48 +0200 Subject: [PATCH 0130/2222] "default" colorCustomization doesn't get unset when removing setting override (#218362) --- .../services/themes/common/colorThemeData.ts | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/services/themes/common/colorThemeData.ts b/src/vs/workbench/services/themes/common/colorThemeData.ts index 6c2bd95258b..e74a4ab1e05 100644 --- a/src/vs/workbench/services/themes/common/colorThemeData.ts +++ b/src/vs/workbench/services/themes/common/colorThemeData.ts @@ -45,6 +45,10 @@ export type TokenStyleDefinitions = { [P in keyof TokenStyleData]?: TokenStyleDe export type TextMateThemingRuleDefinitions = { [P in keyof TokenStyleData]?: ITextMateThemingRule | undefined; } & { scope?: ProbeScope }; +interface IColorOrDefaultMap { + [id: string]: Color | typeof DEFAULT_COLOR_CONFIG_VALUE; +} + export class ColorThemeData implements IWorkbenchColorTheme { static readonly STORAGE_KEY = 'colorThemeData'; @@ -65,7 +69,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { private themeTokenColors: ITextMateThemingRule[] = []; private customTokenColors: ITextMateThemingRule[] = []; private colorMap: IColorMap = {}; - private customColorMap: IColorMap = {}; + private customColorMap: IColorOrDefaultMap = {}; private semanticTokenRules: SemanticTokenRule[] = []; private customSemanticTokenRules: SemanticTokenRule[] = []; @@ -132,15 +136,20 @@ export class ColorThemeData implements IWorkbenchColorTheme { } public getColor(colorId: ColorIdentifier, useDefault?: boolean): Color | undefined { - let color: Color | undefined = this.customColorMap[colorId]; - if (color) { - return color; + const customColor = this.customColorMap[colorId]; + if (customColor instanceof Color) { + return customColor; + } + if (customColor === undefined) { /* !== DEFAULT_COLOR_CONFIG_VALUE */ + const color = this.colorMap[colorId]; + if (color !== undefined) { + return color; + } } - color = this.colorMap[colorId]; - if (useDefault !== false && types.isUndefined(color)) { - color = this.getDefault(colorId); + if (useDefault !== false) { + return this.getDefault(colorId); } - return color; + return undefined; } private getTokenStyle(type: string, modifiers: string[], language: string, useDefault = true, definitions: TokenStyleDefinitions = {}): TokenStyle | undefined { @@ -346,7 +355,11 @@ export class ColorThemeData implements IWorkbenchColorTheme { } public defines(colorId: ColorIdentifier): boolean { - return this.customColorMap.hasOwnProperty(colorId) || this.colorMap.hasOwnProperty(colorId); + const customColor = this.customColorMap[colorId]; + if (customColor instanceof Color) { + return true; + } + return customColor === undefined /* !== DEFAULT_COLOR_CONFIG_VALUE */ && this.colorMap.hasOwnProperty(colorId); } public setCustomizations(settings: ThemeConfiguration) { @@ -373,7 +386,7 @@ export class ColorThemeData implements IWorkbenchColorTheme { for (const id in colors) { const colorVal = colors[id]; if (colorVal === DEFAULT_COLOR_CONFIG_VALUE) { - delete this.colorMap[id]; + this.customColorMap[id] = DEFAULT_COLOR_CONFIG_VALUE; } else if (typeof colorVal === 'string') { this.customColorMap[id] = Color.fromHex(colorVal); } @@ -719,9 +732,9 @@ async function _loadColorTheme(extensionResourceLoaderService: IExtensionResourc // new JSON color themes format for (const colorId in colors) { const colorVal = colors[colorId]; - if (colorVal === DEFAULT_COLOR_CONFIG_VALUE) { + if (colorVal === DEFAULT_COLOR_CONFIG_VALUE) { // ignore colors that are set to to default delete result.colors[colorId]; - } else if (typeof colorVal === 'string') { // ignore colors that are null + } else if (typeof colorVal === 'string') { result.colors[colorId] = Color.fromHex(colors[colorId]); } } From 7ebe6d364f25ae0b38b0320302d589c84f481246 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Jun 2024 14:55:52 +0200 Subject: [PATCH 0131/2222] use `globalThis` instead of `window` (#218368) fixes https://github.com/microsoft/vscode/issues/214945 --- src/vs/base/browser/trustedTypes.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/base/browser/trustedTypes.ts b/src/vs/base/browser/trustedTypes.ts index 48c02ca8c97..0ef4b084528 100644 --- a/src/vs/base/browser/trustedTypes.ts +++ b/src/vs/base/browser/trustedTypes.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { mainWindow } from 'vs/base/browser/window'; import { onUnexpectedError } from 'vs/base/common/errors'; export function createTrustedTypesPolicy( @@ -28,7 +27,7 @@ export function createTrustedTypesPolicy Date: Wed, 26 Jun 2024 15:01:21 +0200 Subject: [PATCH 0132/2222] Refactor createConfigureKeybindingAction to support disabling keybinding configuration for SCM statusbar actions (#218372) chore: refactor createConfigureKeybindingAction to support disabling keybinding configuration for scm statusbar actions --- src/vs/platform/actions/browser/toolbar.ts | 3 ++- src/vs/platform/actions/common/menuService.ts | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/actions/browser/toolbar.ts b/src/vs/platform/actions/browser/toolbar.ts index 4857d4bc07b..a525f06ae9e 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -202,7 +202,8 @@ export class WorkbenchToolBar extends ToolBar { if (action instanceof MenuItemAction && action.menuKeybinding) { primaryActions.push(action.menuKeybinding); } else if (!(action instanceof SubmenuItemAction || action instanceof ToggleMenuAction)) { - primaryActions.push(createConfigureKeybindingAction(action.id, undefined, this._commandService, this._keybindingService)); + const isDisabled = action.id.startsWith('statusbaraction'); // We can't support keybinding configuration for scm statusbar actions + primaryActions.push(createConfigureKeybindingAction(this._commandService, this._keybindingService, action.id, undefined, !isDisabled)); } // -- Hide Actions -- diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 61fefa8e4ae..0b2f255d9be 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -248,7 +248,7 @@ class MenuInfo { const menuHide = createMenuHide(this._id, isMenuItem ? item.command : item, this._hiddenStates); if (isMenuItem) { // MenuItemAction - const menuKeybinding = createConfigureKeybindingAction(item.command.id, item.when, this._commandService, this._keybindingService); + const menuKeybinding = createConfigureKeybindingAction(this._commandService, this._keybindingService, item.command.id, item.when); (activeActions ??= []).push(new MenuItemAction(item.command, item.alt, options, menuHide, menuKeybinding, this._contextKeyService, this._commandService)); } else { // SubmenuItemAction @@ -442,10 +442,11 @@ function createMenuHide(menu: MenuId, command: ICommandAction | ISubmenuItem, st }; } -export function createConfigureKeybindingAction(commandId: string, when: ContextKeyExpression | undefined = undefined, commandService: ICommandService, keybindingService: IKeybindingService): IAction { +export function createConfigureKeybindingAction(commandService: ICommandService, keybindingService: IKeybindingService, commandId: string, when: ContextKeyExpression | undefined = undefined, enabled = true): IAction { return toAction({ id: `configureKeybinding/${commandId}`, label: localize('configure keybinding', "Configure Keybinding"), + enabled, run() { // Only set the when clause when there is no keybinding // It is possible that the action and the keybinding have different when clauses From 64c1b1cc60a02dfe1a3db23670a641c884573189 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:04:40 +0200 Subject: [PATCH 0133/2222] Revert "Git - do not show smart commit dialog when using Commit (Amend) (#214595)" (#218377) This reverts commit 5422f5f940537615c984af76ff0ab4ba5932127e. --- extensions/git/src/commands.ts | 52 ++++++++++++++++------------------ 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 00045c14ea3..a5325033e77 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2103,38 +2103,36 @@ export class CommandCenter { } } - if (!opts.amend) { - // no changes, and the user has not configured to commit all in this case - if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.all) { - const suggestSmartCommit = config.get('suggestSmartCommit') === true; + // no changes, and the user has not configured to commit all in this case + if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.all) { + const suggestSmartCommit = config.get('suggestSmartCommit') === true; - if (!suggestSmartCommit) { - return; - } - - // prompt the user if we want to commit all or not - const message = l10n.t('There are no staged changes to commit.\n\nWould you like to stage all your changes and commit them directly?'); - const yes = l10n.t('Yes'); - const always = l10n.t('Always'); - const never = l10n.t('Never'); - const pick = await window.showWarningMessage(message, { modal: true }, yes, always, never); - - if (pick === always) { - config.update('enableSmartCommit', true, true); - } else if (pick === never) { - config.update('suggestSmartCommit', false, true); - return; - } else if (pick !== yes) { - return; // do not commit on cancel - } + if (!suggestSmartCommit) { + return; } - if (opts.all === undefined) { - opts = { ...opts, all: noStagedChanges }; - } else if (!opts.all && noStagedChanges) { - opts = { ...opts, all: true }; + // prompt the user if we want to commit all or not + const message = l10n.t('There are no staged changes to commit.\n\nWould you like to stage all your changes and commit them directly?'); + const yes = l10n.t('Yes'); + const always = l10n.t('Always'); + const never = l10n.t('Never'); + const pick = await window.showWarningMessage(message, { modal: true }, yes, always, never); + + if (pick === always) { + config.update('enableSmartCommit', true, true); + } else if (pick === never) { + config.update('suggestSmartCommit', false, true); + return; + } else if (pick !== yes) { + return; // do not commit on cancel } } + + if (opts.all === undefined) { + opts = { ...opts, all: noStagedChanges }; + } else if (!opts.all && noStagedChanges) { + opts = { ...opts, all: true }; + } } // enable signing of commits if configured From 68f6b9113e1031e896484860604e20651cdd6bf5 Mon Sep 17 00:00:00 2001 From: Cristopher Claeys Date: Wed, 26 Jun 2024 15:07:55 +0200 Subject: [PATCH 0134/2222] Add seen set to service instantiation --- src/vs/platform/instantiation/common/instantiationService.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index 4b313ed32eb..cab2957b146 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -216,8 +216,13 @@ export class InstantiationService implements IInstantiationService { let cycleCount = 0; const stack = [{ id, desc, _trace }]; + const seen = new Set(); while (stack.length) { const item = stack.pop()!; + + if (seen.has(String(item.id))) continue; + seen.add(String(item.id)); + graph.lookupOrInsertNode(item); // a weak but working heuristic for cycle checks From fdec5a0ec0d50bcfd61ae4a76e6f5a809b5c5d81 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Jun 2024 15:11:26 +0200 Subject: [PATCH 0135/2222] fix https://github.com/microsoft/vscode/issues/217853 (#218380) --- .../inlineChat/browser/media/inlineChatContentWidget.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css index cc5f706404c..7da5cc3e97e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChatContentWidget.css @@ -24,6 +24,10 @@ padding: 0; } +.monaco-workbench .inline-chat-content-widget.interactive-session .interactive-list { + display: none; +} + .monaco-workbench .inline-chat-content-widget.interactive-session .toolbar { display: none; padding-top: 4px; From 05b1eafd91974570a054aed949f642bbf24831c0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 26 Jun 2024 07:38:27 -0700 Subject: [PATCH 0136/2222] Improve casing of environment changes actions Fixes #218118 --- .../contrib/terminal/browser/environmentVariableInfo.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts b/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts index e355b287f63..606202e8837 100644 --- a/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts +++ b/src/vs/workbench/contrib/terminal/browser/environmentVariableInfo.ts @@ -39,7 +39,7 @@ export class EnvironmentVariableInfoStale implements IEnvironmentVariableInfo { private _getActions(): ITerminalStatusHoverAction[] { return [{ - label: localize('relaunchTerminalLabel', "Relaunch terminal"), + label: localize('relaunchTerminalLabel', "Relaunch Terminal"), run: () => this._terminalService.getInstanceFromId(this._terminalId)?.relaunch(), commandId: TerminalCommandId.Relaunch }]; @@ -77,7 +77,7 @@ export class EnvironmentVariableInfoChangesActive implements IEnvironmentVariabl private _getActions(scope: EnvironmentVariableScope | undefined): ITerminalStatusHoverAction[] { return [{ - label: localize('showEnvironmentContributions', "Show environment contributions"), + label: localize('showEnvironmentContributions', "Show Environment Contributions"), run: () => this._commandService.executeCommand(TerminalCommandId.ShowEnvironmentContributions, scope), commandId: TerminalCommandId.ShowEnvironmentContributions }]; From da06c08a328a7519e87e2fe085abdc7e691161a1 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 26 Jun 2024 16:48:28 +0200 Subject: [PATCH 0137/2222] Inline chat fixes and polish (#218398) * react to changes of `inlineChat.experimental.textButtons` (no more window reload) https://github.com/microsoft/vscode/issues/217845 * fix status label wrapping * remove unused toolbar * fix https://github.com/microsoft/vscode/issues/218395 --- .../browser/inlineChat.contribution.ts | 6 ++ .../inlineChat/browser/inlineChatWidget.ts | 81 ++++++++----------- .../inlineChat/browser/media/inlineChat.css | 24 +++++- .../contrib/inlineChat/common/inlineChat.ts | 2 +- 4 files changed, 61 insertions(+), 52 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts index 21f02771f96..7d3e4041271 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChat.contribution.ts @@ -125,9 +125,15 @@ class MenuCopier implements IDisposable { updateMenu(); } }); + const listener2 = configService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(InlineChatConfigKeys.ExpTextButtons)) { + updateMenu(); + } + }); this.dispose = () => { listener.dispose(); + listener2.dispose(); store.dispose(); }; } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index e5ff46f597f..fad3466eefe 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -64,11 +64,6 @@ export interface IInlineChatWidgetConstructionOptions { */ statusMenuId: MenuId | { menu: MenuId; options: IWorkbenchButtonBarOptions }; - /** - * The men that rendered in the lower right corner, use for feedback - */ - feedbackMenuId?: MenuId; - /** * The options for the chat widget */ @@ -97,9 +92,9 @@ export class InlineChatWidget { h('div.accessibleViewer@accessibleViewer'), h('div.status@status', [ h('div.label.info.hidden@infoLabel'), - h('div.actions.hidden@statusToolbar'), + h('div.actions.text-style.hidden@toolbar1'), + h('div.actions.button-style.hidden@toolbar2'), h('div.label.status.hidden@statusLabel'), - h('div.actions.hidden@feedbackToolbar'), ]), ] ); @@ -200,46 +195,34 @@ export class InlineChatWidget { const statusMenuId = options.statusMenuId instanceof MenuId ? options.statusMenuId : options.statusMenuId.menu; - if (this._configurationService.getValue(InlineChatConfigKeys.ExpTextButtons)) { - // TEXT-ONLY bar - const statusToolbarMenu = scopedInstaService.createInstance(MenuWorkbenchToolBar, this._elements.statusToolbar, statusMenuId, { - hiddenItemStrategy: HiddenItemStrategy.NoHide, - telemetrySource: options.chatWidgetViewOptions?.menus?.telemetrySource, - actionViewItemProvider: action => action instanceof MenuItemAction ? this._instantiationService.createInstance(TextOnlyMenuEntryActionViewItem, action, { conversational: true }) : undefined, - toolbarOptions: { primaryGroup: '0_main' }, - menuOptions: { renderShortTitle: true }, - label: true, - icon: false - }); - this._store.add(statusToolbarMenu.onDidChangeMenuItems(() => this._onDidChangeHeight.fire())); - this._store.add(statusToolbarMenu); - - } else { - // BUTTON bar - const statusMenuOptions = options.statusMenuId instanceof MenuId ? undefined : options.statusMenuId.options; - const statusButtonBar = scopedInstaService.createInstance(MenuWorkbenchButtonBar, this._elements.statusToolbar, statusMenuId, { - toolbarOptions: { primaryGroup: '0_main' }, - telemetrySource: options.chatWidgetViewOptions?.menus?.telemetrySource, - menuOptions: { renderShortTitle: true }, - ...statusMenuOptions, - }); - this._store.add(statusButtonBar.onDidChange(() => this._onDidChangeHeight.fire())); - this._store.add(statusButtonBar); - } - - const workbenchToolbarOptions = { + // TEXT-ONLY bar + const statusToolbarMenu = scopedInstaService.createInstance(MenuWorkbenchToolBar, this._elements.toolbar1, statusMenuId, { hiddenItemStrategy: HiddenItemStrategy.NoHide, - toolbarOptions: { - primaryGroup: () => true, - useSeparatorsInPrimaryActions: true - } - }; + telemetrySource: options.chatWidgetViewOptions?.menus?.telemetrySource, + actionViewItemProvider: action => action instanceof MenuItemAction ? this._instantiationService.createInstance(TextOnlyMenuEntryActionViewItem, action, { conversational: true }) : undefined, + toolbarOptions: { primaryGroup: '0_main' }, + menuOptions: { renderShortTitle: true }, + label: true, + icon: false + }); + this._store.add(statusToolbarMenu.onDidChangeMenuItems(() => this._onDidChangeHeight.fire())); + this._store.add(statusToolbarMenu); + + // BUTTON bar + const statusMenuOptions = options.statusMenuId instanceof MenuId ? undefined : options.statusMenuId.options; + const statusButtonBar = scopedInstaService.createInstance(MenuWorkbenchButtonBar, this._elements.toolbar2, statusMenuId, { + toolbarOptions: { primaryGroup: '0_main' }, + telemetrySource: options.chatWidgetViewOptions?.menus?.telemetrySource, + menuOptions: { renderShortTitle: true }, + ...statusMenuOptions, + }); + this._store.add(statusButtonBar.onDidChange(() => this._onDidChangeHeight.fire())); + this._store.add(statusButtonBar); + + const toggleToolbar = () => this._elements.status.classList.toggle('text', this._configurationService.getValue(InlineChatConfigKeys.ExpTextButtons)); + this._store.add(this._configurationService.onDidChangeConfiguration(e => e.affectsConfiguration(InlineChatConfigKeys.ExpTextButtons) && toggleToolbar())); + toggleToolbar(); - if (options.feedbackMenuId) { - const feedbackToolbar = this._instantiationService.createInstance(MenuWorkbenchToolBar, this._elements.feedbackToolbar, options.feedbackMenuId, { ...workbenchToolbarOptions, hiddenItemStrategy: HiddenItemStrategy.Ignore }); - this._store.add(feedbackToolbar.onDidChangeMenuItems(() => this._onDidChangeHeight.fire())); - this._store.add(feedbackToolbar); - } this._store.add(this._configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(AccessibilityVerbositySettingId.InlineChat)) { @@ -402,8 +385,8 @@ export class InlineChatWidget { updateToolbar(show: boolean) { this._elements.root.classList.toggle('toolbar', show); - this._elements.statusToolbar.classList.toggle('hidden', !show); - this._elements.feedbackToolbar.classList.toggle('hidden', !show); + this._elements.toolbar1.classList.toggle('hidden', !show); + this._elements.toolbar2.classList.toggle('hidden', !show); this._elements.status.classList.toggle('actions', show); this._elements.infoLabel.classList.toggle('hidden', show); this._onDidChangeHeight.fire(); @@ -533,8 +516,8 @@ export class InlineChatWidget { reset(this._elements.statusLabel); this._elements.statusLabel.classList.toggle('hidden', true); - this._elements.statusToolbar.classList.add('hidden'); - this._elements.feedbackToolbar.classList.add('hidden'); + this._elements.toolbar1.classList.add('hidden'); + this._elements.toolbar2.classList.add('hidden'); this.updateInfo(''); this.chatWidget.setModel(this._defaultChatModel, {}); diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index e8fd1d5e047..279fea6e71b 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -58,6 +58,7 @@ } .monaco-workbench .inline-chat .chat-widget .interactive-session .interactive-list .interactive-item-container.minimal > .header { + top: 5px; right: 10px; } @@ -89,7 +90,8 @@ overflow: hidden; color: var(--vscode-descriptionForeground); font-size: 11px; - display: inline-flex; + display: flex; + white-space: nowrap; } .monaco-workbench .inline-chat .status .label.info { @@ -114,7 +116,7 @@ } .monaco-workbench .inline-chat .status .label > .codicon { - padding: 0 5px; + padding: 0 3px; font-size: 12px; line-height: 18px; } @@ -185,6 +187,24 @@ } } +.monaco-workbench .inline-chat .status { + .actions.text-style { + display: none; + } + .actions.button-style { + display: inherit; + } +} + +.monaco-workbench .inline-chat .status.text { + .actions.text-style { + display: inherit; + } + .actions.button-style { + display: none; + } +} + .monaco-workbench .inline-chat .status .actions > .monaco-button, .monaco-workbench .inline-chat .status .actions > .monaco-button-dropdown { margin-right: 4px; diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index ee60b223f56..b4c754b1759 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -67,7 +67,7 @@ Registry.as(Extensions.Configuration).registerConfigurat ], }, [InlineChatConfigKeys.ExpTextButtons]: { - description: localize('txtButtons', "Whether to use textual buttons (Requires restart)."), + description: localize('txtButtons', "Whether to use textual buttons."), default: false, type: 'boolean', tags: ['experimental'] From 59c040741899a64de6c4a1bd6ce71066d4bcb234 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 26 Jun 2024 08:09:11 -0700 Subject: [PATCH 0138/2222] Reset inline chat widget before terminal chat widget Fixes #218402 --- .../contrib/terminalContrib/chat/browser/terminalChatWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index edb6b3af109..c6808146699 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -194,11 +194,11 @@ export class TerminalChatWidget extends Disposable { hide(): void { this._container.classList.add('hide'); + this._inlineChatWidget.reset(); this._reset(); this._inlineChatWidget.updateChatMessage(undefined); this._inlineChatWidget.updateProgress(false); this._inlineChatWidget.updateToolbar(false); - this._inlineChatWidget.reset(); this._focusedContextKey.set(false); this._visibleContextKey.set(false); this._inlineChatWidget.value = ''; From 14b094becc5b012b46f8694087b8a59e2550d2a4 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Wed, 26 Jun 2024 17:21:53 +0200 Subject: [PATCH 0139/2222] fix: add disposable to elementDisposables instead of templateDisposables in renderElement function in SettingEnumRenderer (#216855) --- src/vs/workbench/contrib/preferences/browser/settingsTree.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 06efb414415..0be46bd177a 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1737,7 +1737,7 @@ export class SettingEnumRenderer extends AbstractSettingRenderer implements ITre const enumDescriptionsAreMarkdown = dataElement.setting.enumDescriptionsAreMarkdown; const disposables = new DisposableStore(); - template.toDispose.add(disposables); + template.elementDisposables.add(disposables); let createdDefault = false; if (!settingEnum.includes(dataElement.defaultValue)) { From ed2036106c45099917689dbb413d4b3ff89b2066 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Wed, 26 Jun 2024 17:25:15 +0200 Subject: [PATCH 0140/2222] fix: possible memory leak in SettingTreeRenderers (#216768) --- .../preferences/browser/settingsEditor2.ts | 6 +++--- .../preferences/browser/settingsTree.ts | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index f583071dee1..f9b11cf76a8 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -794,10 +794,10 @@ export class SettingsEditor2 extends EditorPane { this.createTOC(this.tocTreeContainer); this.createSettingsTree(this.settingsTreeContainer); - this.splitView = new SplitView(this.bodyContainer, { + this.splitView = this._register(new SplitView(this.bodyContainer, { orientation: Orientation.HORIZONTAL, proportionalLayout: true - }); + })); const startingWidth = this.storageService.getNumber('settingsEditor2.splitViewWidth', StorageScope.PROFILE, SettingsEditor2.TOC_RESET_WIDTH); this.splitView.addView({ onDidChange: Event.None, @@ -914,7 +914,7 @@ export class SettingsEditor2 extends EditorPane { } private createSettingsTree(container: HTMLElement): void { - this.settingRenderers = this.instantiationService.createInstance(SettingTreeRenderers); + this.settingRenderers = this._register(this.instantiationService.createInstance(SettingTreeRenderers)); this._register(this.settingRenderers.onDidChangeSetting(e => this.onDidChangeSetting(e.key, e.value, e.type, e.manualReset, e.scope))); this._register(this.settingRenderers.onDidOpenSettings(settingKey => { this.openSettingsFile({ revealSetting: { key: settingKey, edit: true } }); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 0be46bd177a..62a948e5a08 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -1985,10 +1985,10 @@ export class SettingsExtensionToggleRenderer extends AbstractSettingRenderer imp } } -export class SettingTreeRenderers { +export class SettingTreeRenderers extends Disposable { readonly onDidClickOverrideElement: Event; - private readonly _onDidChangeSetting = new Emitter(); + private readonly _onDidChangeSetting = this._register(new Emitter()); readonly onDidChangeSetting: Event; readonly onDidOpenSettings: Event; @@ -2012,6 +2012,7 @@ export class SettingTreeRenderers { @IUserDataProfilesService private readonly _userDataProfilesService: IUserDataProfilesService, @IUserDataSyncEnablementService private readonly _userDataSyncEnablementService: IUserDataSyncEnablementService, ) { + super(); this.settingActions = [ new Action('settings.resetSetting', localize('resetSettingLabel', "Reset Setting"), undefined, undefined, async context => { if (context instanceof SettingsTreeSettingElement) { @@ -2117,6 +2118,20 @@ export class SettingTreeRenderers { const settingElement = this.getSettingDOMElementForDOMElement(element); return settingElement && settingElement.getAttribute(AbstractSettingRenderer.SETTING_ID_ATTR); } + + override dispose(): void { + super.dispose(); + this.settingActions.forEach(action => { + if (isDisposable(action)) { + action.dispose(); + } + }); + this.allRenderers.forEach(renderer => { + if (isDisposable(renderer)) { + renderer.dispose(); + } + }); + } } /** From c6e312a80ee32d6e6af957533f6e309ba100f581 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:10:25 -0700 Subject: [PATCH 0141/2222] Fix terminalChatFocus behavior Fixes #216624 --- .../terminalContrib/chat/browser/terminalChatWidget.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index c6808146699..33c01afbb63 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -96,11 +96,14 @@ export class TerminalChatWidget extends Disposable { this._container.appendChild(this._inlineChatWidget.domNode); this._focusTracker = this._register(trackFocus(this._container)); + this._register(this._focusTracker.onDidFocus(() => this._focusedContextKey.set(true))); this._register(this._focusTracker.onDidBlur(() => { + this._focusedContextKey.set(false); if (!this.inlineChatWidget.responseContent) { this.hide(); } })); + this.hide(); } @@ -150,7 +153,6 @@ export class TerminalChatWidget extends Disposable { reveal(): void { this._doLayout(this._inlineChatWidget.contentHeight); this._container.classList.remove('hide'); - this._focusedContextKey.set(true); this._visibleContextKey.set(true); this._inlineChatWidget.focus(); this._instance.scrollToBottom(); @@ -199,7 +201,6 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget.updateChatMessage(undefined); this._inlineChatWidget.updateProgress(false); this._inlineChatWidget.updateToolbar(false); - this._focusedContextKey.set(false); this._visibleContextKey.set(false); this._inlineChatWidget.value = ''; this._instance.focus(); From a11a0d7edcd2931f520916446743d3ba39429e00 Mon Sep 17 00:00:00 2001 From: Ole Date: Wed, 26 Jun 2024 18:10:56 +0200 Subject: [PATCH 0142/2222] Fix two bugs in #214589 fixing #213535. (#218357) * Fix two bugs in #214589. 1. We must not `dispose()` the `MutableDisposable` `this._commentThreadWidget` - as that disposes the MutableDisposable itself, and it cannot then later be reassigned to a new value. Instead, we need to assign `value=undefined`, which disposes the previous `value`, but keeps the `MutableDisposable` available to be reused later. 2. `initialize()` is a no-op if `this.currentElement` is already identical to the passed `element`, so we must not do that assignment before calling initialize - instead `initialize()` does the assignment after checking. * Fix blank line. * Register the _commentThreadWidget MutableDisposable so that it gets disposed when CellComments is disposed. --- .../notebook/browser/view/cellParts/cellComments.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts index cbf886228ef..44e5866b04f 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts @@ -20,14 +20,13 @@ import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; export class CellComments extends CellContentPart { - private readonly _commentThreadWidget = new MutableDisposable>; + private readonly _commentThreadWidget: MutableDisposable>; private currentElement: CodeCellViewModel | undefined; private readonly _commentThreadDisposables = this._register(new DisposableStore()); constructor( private readonly notebookEditor: INotebookEditorDelegate, private readonly container: HTMLElement, - @IContextKeyService private readonly contextKeyService: IContextKeyService, @IThemeService private readonly themeService: IThemeService, @ICommentService private readonly commentService: ICommentService, @@ -37,6 +36,8 @@ export class CellComments extends CellContentPart { super(); this.container.classList.add('review-widget'); + this._register(this._commentThreadWidget = new MutableDisposable>()); + this._register(this.themeService.onDidColorThemeChange(this._applyTheme, this)); // TODO @rebornix onDidChangeLayout (font change) // this._register(this.notebookEditor.onDidchangeLa) @@ -108,7 +109,7 @@ export class CellComments extends CellContentPart { if (this._commentThreadWidget.value) { if (!info) { this._commentThreadDisposables.clear(); - this._commentThreadWidget.dispose(); + this._commentThreadWidget.value = undefined; this.currentElement.commentHeight = 0; return; } @@ -154,7 +155,6 @@ export class CellComments extends CellContentPart { override didRenderCell(element: ICellViewModel): void { if (element.cellKind === CellKind.Code) { - this.currentElement = element as CodeCellViewModel; this.initialize(element); this._bindListeners(); } From b37c33ddcb0640891675d51d127eeb083e605f54 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:38:19 -0700 Subject: [PATCH 0143/2222] Fix missing cases for not reporting prompts Fixes #215482 --- .../terminal/browser/media/shellIntegration-bash.sh | 4 +++- .../terminal/browser/media/shellIntegration.ps1 | 10 ++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh index e46d83c6bb1..06036fcbaae 100755 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration-bash.sh @@ -175,7 +175,9 @@ __vsc_stable="$VSCODE_STABLE" unset VSCODE_STABLE # Report continuation prompt -builtin printf "\e]633;P;ContinuationPrompt=$(echo "$PS2" | sed 's/\x1b/\\\\x1b/g')\a" +if [ "$__vsc_stable" = "0" ]; then + builtin printf "\e]633;P;ContinuationPrompt=$(echo "$PS2" | sed 's/\x1b/\\\\x1b/g')\a" +fi __vsc_report_prompt() { # Expand the original PS1 similarly to how bash would normally diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 index 53603022c29..6c92130ec95 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 @@ -98,7 +98,7 @@ function Global:Prompt() { # Prompt # OSC 633 ; = ST - if ($isStable -eq "1") { + if ($isStable -eq "0") { $Result += "$([char]0x1b)]633;P;Prompt=$(__VSCode-Escape-Value $OriginalPrompt)`a" } @@ -147,9 +147,11 @@ else { } # Set ContinuationPrompt property -$ContinuationPrompt = (Get-PSReadLineOption).ContinuationPrompt -if ($ContinuationPrompt) { - [Console]::Write("$([char]0x1b)]633;P;ContinuationPrompt=$(__VSCode-Escape-Value $ContinuationPrompt)`a") +if ($isStable -eq "0") { + $ContinuationPrompt = (Get-PSReadLineOption).ContinuationPrompt + if ($ContinuationPrompt) { + [Console]::Write("$([char]0x1b)]633;P;ContinuationPrompt=$(__VSCode-Escape-Value $ContinuationPrompt)`a") + } } # Set always on key handlers which map to default VS Code keybindings From 47262beab72adf41e9194ddb19d862404a50628a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 26 Jun 2024 09:46:12 -0700 Subject: [PATCH 0144/2222] Disable terminal initial hint when ctrl+i is triggered Previously this would only happen when _clicked_. Fixes #213820 --- .../chat/browser/terminal.initialHint.contribution.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts index 9bd955a1ab7..21d5f887145 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminal.initialHint.contribution.ts @@ -261,6 +261,7 @@ class TerminalInitialHintWidget extends Disposable { }; this.toDispose.add(this.commandService.onDidExecuteCommand(e => { if (e.commandId === TerminalChatCommandId.Start) { + this._storageService.store(Constants.InitialHintHideStorageKey, true, StorageScope.APPLICATION, StorageTarget.USER); this.dispose(); } })); From 955be758398c3da33192f52d1a2dbaaa1f69ae24 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:10:19 -0700 Subject: [PATCH 0145/2222] Add new line before end of fenced code block Fixes #218397 --- src/vs/base/browser/markdownRenderer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 61e10f19359..49cb39a06b0 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -636,7 +636,7 @@ const plainTextRenderer = new Lazy((withCodeBlocks?: boolean) = const plainTextWithCodeBlocksRenderer = new Lazy(() => { const renderer = createRenderer(); renderer.code = (code: string): string => { - return '\n' + '```' + '\n' + code + '```' + '\n'; + return `\n\`\`\`\n${code}\n\`\`\`\n`; }; return renderer; }); From 7607e75d696186379355fd1e815e180bc27ba218 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Wed, 26 Jun 2024 11:08:09 -0700 Subject: [PATCH 0146/2222] debug: bump js-debug for 1.91 (#218469) --- product.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/product.json b/product.json index d2d3bf7a361..27ae53fe16b 100644 --- a/product.json +++ b/product.json @@ -50,8 +50,8 @@ }, { "name": "ms-vscode.js-debug", - "version": "1.90.0", - "sha256": "1317dd7d1ac50641c1534a3e957ecbc94349f4fbd897acb916da11eea3208a66", + "version": "1.91.0", + "sha256": "53b99146c7fa280f00c74414e09721530c622bf3e5eac2c967ddfb9906b51c80", "repo": "https://github.com/microsoft/vscode-js-debug", "metadata": { "id": "25629058-ddac-4e17-abba-74678e126c5d", From b9f2435180d51a26185b00b71ec43a07a4739ddd Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 26 Jun 2024 11:25:07 -0700 Subject: [PATCH 0147/2222] Fix up markdown lists that use * (#218483) * Fix up markdown lists that use * Fix #217946 * Comment --- src/vs/base/browser/markdownRenderer.ts | 4 ++-- src/vs/base/test/browser/markdownRenderer.test.ts | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 49cb39a06b0..265a57113f7 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -768,8 +768,8 @@ function completeListItemPattern(list: marked.Tokens.List): marked.Tokens.List | const previousListItemsText = mergeRawTokenText(list.items.slice(0, -1)); - // Grabbing the `- ` or `1. ` off the list item because I can't find a better way to do this - const lastListItemLead = lastListItem.raw.match(/^(\s*(-|\d+\.) +)/)?.[0]; + // Grabbing the `- ` or `1. ` or `* ` off the list item because I can't find a better way to do this + const lastListItemLead = lastListItem.raw.match(/^(\s*(-|\d+\.|\*) +)/)?.[0]; if (!lastListItemLead) { // Is badly formatted return; diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 81abf8c9c9e..4ea38c815c3 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -607,6 +607,15 @@ const y = 2; assert.deepStrictEqual(newTokens, completeTokens); }); + test(`incomplete ${name} in asterisk list`, () => { + const text = `* list item one\n* list item two and ${delimiter}text`; + const tokens = marked.lexer(text); + const newTokens = fillInIncompleteTokens(tokens); + + const completeTokens = marked.lexer(text + delimiter); + assert.deepStrictEqual(newTokens, completeTokens); + }); + test(`incomplete ${name} in numbered list`, () => { const text = `1. list item one\n2. list item two and ${delimiter}text`; const tokens = marked.lexer(text); From 9e7cd445fa9bf1cf67f695201130744ee7cc30da Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Wed, 26 Jun 2024 14:03:37 -0700 Subject: [PATCH 0148/2222] fix: show context menu option to disable empty editor hint (#218548) * fix: show context menu option to disable empty editor hint * Fix action name --- .../emptyTextEditorHint.ts | 45 +++++++++++++++---- .../contrib/editorHint/emptyCellEditorHint.ts | 7 ++- 2 files changed, 42 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts index c8a1443ab1e..327554fd781 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts @@ -34,6 +34,8 @@ import { SEARCH_RESULT_LANGUAGE_ID } from 'vs/workbench/services/search/common/s import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { IHoverService } from 'vs/platform/hover/browser/hover'; import { ChatAgentLocation, IChatAgent, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; const $ = dom.$; @@ -60,6 +62,7 @@ export class EmptyTextEditorHintContribution implements IEditorContribution { @IChatAgentService private readonly chatAgentService: IChatAgentService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IProductService protected readonly productService: IProductService, + @IContextMenuService private readonly contextMenuService: IContextMenuService ) { this.toDispose = []; this.toDispose.push(this.editor.onDidChangeModel(() => this.update())); @@ -145,7 +148,8 @@ export class EmptyTextEditorHintContribution implements IEditorContribution { this.keybindingService, this.chatAgentService, this.telemetryService, - this.productService + this.productService, + this.contextMenuService ); } else if (!shouldRenderHint && this.textHintContentWidget) { this.textHintContentWidget.dispose(); @@ -178,7 +182,8 @@ class EmptyTextEditorHintContentWidget implements IContentWidget { private readonly keybindingService: IKeybindingService, private readonly chatAgentService: IChatAgentService, private readonly telemetryService: ITelemetryService, - private readonly productService: IProductService + private readonly productService: IProductService, + private readonly contextMenuService: IContextMenuService, ) { this.toDispose = new DisposableStore(); this.toDispose.add(this.editor.onDidChangeConfiguration((e: ConfigurationChangedEvent) => { @@ -199,10 +204,34 @@ class EmptyTextEditorHintContentWidget implements IContentWidget { return EmptyTextEditorHintContentWidget.ID; } - private _disableHint() { - this.configurationService.updateValue(emptyTextEditorHintSetting, 'hidden'); - this.dispose(); - this.editor.focus(); + private _disableHint(e?: MouseEvent) { + const disableHint = () => { + this.configurationService.updateValue(emptyTextEditorHintSetting, 'hidden'); + this.dispose(); + this.editor.focus(); + }; + + if (!e) { + disableHint(); + return; + } + + this.contextMenuService.showContextMenu({ + getAnchor: () => { return new StandardMouseEvent(dom.getActiveWindow(), e); }, + getActions: () => { + return [{ + id: 'workench.action.disableEmptyEditorHint', + label: localize('disableEditorEmptyHint', "Disable Empty Editor Hint"), + tooltip: localize('disableEditorEmptyHint', "Disable Empty Editor Hint"), + enabled: true, + class: undefined, + run: () => { + disableHint(); + } + } + ]; + } + }); } private _getHintInlineChat(providers: IChatAgent[]) { @@ -244,7 +273,7 @@ class EmptyTextEditorHintContentWidget implements IContentWidget { const hintPart = $('a', undefined, fragment); hintPart.style.fontStyle = 'italic'; hintPart.style.cursor = 'pointer'; - this.toDispose.add(dom.addDisposableListener(hintPart, dom.EventType.CONTEXT_MENU, () => this._disableHint())); + this.toDispose.add(dom.addDisposableListener(hintPart, dom.EventType.CONTEXT_MENU, (e) => this._disableHint(e))); this.toDispose.add(dom.addDisposableListener(hintPart, dom.EventType.CLICK, handleClick)); return hintPart; } else { @@ -263,7 +292,7 @@ class EmptyTextEditorHintContentWidget implements IContentWidget { if (this.options.clickable) { label.element.style.cursor = 'pointer'; - this.toDispose.add(dom.addDisposableListener(label.element, dom.EventType.CONTEXT_MENU, () => this._disableHint())); + this.toDispose.add(dom.addDisposableListener(label.element, dom.EventType.CONTEXT_MENU, (e) => this._disableHint(e))); this.toDispose.add(dom.addDisposableListener(label.element, dom.EventType.CLICK, handleClick)); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts b/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts index eeef10e64d2..205bce806e1 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/editorHint/emptyCellEditorHint.ts @@ -8,6 +8,7 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IHoverService } from 'vs/platform/hover/browser/hover'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -32,7 +33,8 @@ export class EmptyCellEditorHintContribution extends EmptyTextEditorHintContribu @IInlineChatSessionService inlineChatSessionService: IInlineChatSessionService, @IChatAgentService chatAgentService: IChatAgentService, @ITelemetryService telemetryService: ITelemetryService, - @IProductService productService: IProductService + @IProductService productService: IProductService, + @IContextMenuService contextMenuService: IContextMenuService ) { super( editor, @@ -44,7 +46,8 @@ export class EmptyCellEditorHintContribution extends EmptyTextEditorHintContribu inlineChatSessionService, chatAgentService, telemetryService, - productService + productService, + contextMenuService ); const activeEditor = getNotebookEditorFromEditorPane(this._editorService.activeEditorPane); From fb3bace29e1f3e130c4d625d349f314f341cbfd4 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Wed, 26 Jun 2024 14:51:08 -0700 Subject: [PATCH 0149/2222] switch order of keybinding registration (#218622) --- .../interactive/browser/interactive.contribution.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts index 5705f2e7f3b..2109f7ef705 100644 --- a/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts +++ b/src/vs/workbench/contrib/interactive/browser/interactive.contribution.ts @@ -458,6 +458,11 @@ registerAction2(class extends Action2 { title: localize2('interactive.execute', 'Execute Code'), category: interactiveWindowCategory, keybinding: [{ + // when: NOTEBOOK_CELL_LIST_FOCUSED, + when: ContextKeyExpr.equals('activeEditor', 'workbench.editor.interactive'), + primary: KeyMod.CtrlCmd | KeyCode.Enter, + weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT + }, { when: ContextKeyExpr.and( ContextKeyExpr.equals('activeEditor', 'workbench.editor.interactive'), ContextKeyExpr.equals('config.interactiveWindow.executeWithShiftEnter', true) @@ -471,11 +476,6 @@ registerAction2(class extends Action2 { ), primary: KeyCode.Enter, weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT - }, { - // when: NOTEBOOK_CELL_LIST_FOCUSED, - when: ContextKeyExpr.equals('activeEditor', 'workbench.editor.interactive'), - primary: KeyMod.CtrlCmd | KeyCode.Enter, - weight: NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT }], menu: [ { From 5950c290984ea5b2c47ac6ec666eb1c62d72a155 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 26 Jun 2024 19:18:00 -0700 Subject: [PATCH 0150/2222] Fix 'cancel chat' keybinding on windows (#218647) Fix #217861 --- .../workbench/contrib/chat/browser/actions/chatExecuteActions.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts index 5a6574db112..b7d76a5a227 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts @@ -161,6 +161,7 @@ export class CancelAction extends Action2 { keybinding: { weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.Escape, + win: { primary: KeyMod.Alt | KeyCode.Backspace }, } }); } From cb9d1e4589a05bed292f298acba7369e812f8cf1 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 26 Jun 2024 19:18:24 -0700 Subject: [PATCH 0151/2222] Don't throw for $updateAgent failure (#218649) Fix #214157 This can happen after something else went wrong, and the exception doesn't go back to the extension or anywhere helpful besides error telemetry. --- src/vs/workbench/api/browser/mainThreadChatAgents2.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index 0d63847dae5..35126b7998d 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -199,7 +199,8 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA $updateAgent(handle: number, metadataUpdate: IExtensionChatAgentMetadata): void { const data = this._agents.get(handle); if (!data) { - throw new Error(`No agent with handle ${handle} registered`); + this._logService.error(`MainThreadChatAgents2#$updateAgent: No agent with handle ${handle} registered`); + return; } data.hasFollowups = metadataUpdate.hasFollowups; this._chatAgentService.updateAgent(data.id, revive(metadataUpdate)); From 5ba8a8b7a1b2413eecc5a38a56f4309d31833b9c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Jun 2024 11:21:43 +0200 Subject: [PATCH 0152/2222] fix #218126 (#218679) --- .../services/userDataProfile/common/userDataProfileIcons.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/userDataProfile/common/userDataProfileIcons.ts b/src/vs/workbench/services/userDataProfile/common/userDataProfileIcons.ts index 93251b9b1c3..6e0e28a0347 100644 --- a/src/vs/workbench/services/userDataProfile/common/userDataProfileIcons.ts +++ b/src/vs/workbench/services/userDataProfile/common/userDataProfileIcons.ts @@ -71,7 +71,7 @@ export const ICONS = [ Codicon.pulse, Codicon.radioTower, Codicon.smiley, - Codicon.symbolEvent, + Codicon.zap, Codicon.squirrel, Codicon.symbolColor, Codicon.mail, From 53ed07606e51c2047564d8cefebc36a177fa3e6f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 27 Jun 2024 11:29:37 +0200 Subject: [PATCH 0153/2222] Fix comment threads starting expanded (#218684) --- src/vs/workbench/api/browser/mainThreadComments.ts | 8 +++++++- src/vs/workbench/api/common/extHost.protocol.ts | 2 +- src/vs/workbench/api/common/extHostComments.ts | 4 +--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 2e342290e96..6bbeb159248 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -185,6 +185,7 @@ export class MainThreadCommentThread implements languages.CommentThread { public threadId: string, public resource: string, private _range: T | undefined, + comments: languages.Comment[] | undefined, private _canReply: boolean, private _isTemplate: boolean, public editorId?: string @@ -192,6 +193,8 @@ export class MainThreadCommentThread implements languages.CommentThread { this._isDisposed = false; if (_isTemplate) { this.comments = []; + } else if (comments) { + this._comments = comments; } } @@ -298,6 +301,7 @@ export class MainThreadCommentController implements ICommentController { threadId: string, resource: UriComponents, range: IRange | ICellRange | undefined, + comments: languages.Comment[], isTemplate: boolean, editorId?: string ): languages.CommentThread { @@ -308,6 +312,7 @@ export class MainThreadCommentController implements ICommentController { threadId, URI.revive(resource).toString(), range, + comments, true, isTemplate, editorId @@ -590,6 +595,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments threadId: string, resource: UriComponents, range: IRange | ICellRange | undefined, + comments: languages.Comment[], extensionId: ExtensionIdentifier, isTemplate: boolean, editorId?: string @@ -600,7 +606,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments return undefined; } - return provider.createCommentThread(extensionId.value, commentThreadHandle, threadId, resource, range, isTemplate, editorId); + return provider.createCommentThread(extensionId.value, commentThreadHandle, threadId, resource, range, comments, isTemplate, editorId); } $updateCommentThread(handle: number, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5785393c0e1..d810c7b892d 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -144,7 +144,7 @@ export interface MainThreadCommentsShape extends IDisposable { $registerCommentController(handle: number, id: string, label: string, extensionId: string): void; $unregisterCommentController(handle: number): void; $updateCommentControllerFeatures(handle: number, features: CommentProviderFeatures): void; - $createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange | ICellRange | undefined, extensionId: ExtensionIdentifier, isTemplate: boolean, editorId?: string): languages.CommentThread | undefined; + $createCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, range: IRange | ICellRange | undefined, comments: languages.Comment[], extensionId: ExtensionIdentifier, isTemplate: boolean, editorId?: string): languages.CommentThread | undefined; $updateCommentThread(handle: number, commentThreadHandle: number, threadId: string, resource: UriComponents, changes: CommentThreadChanges): void; $deleteCommentThread(handle: number, commentThreadHandle: number): void; $updateCommentingRanges(handle: number, resourceHints?: languages.CommentingRangeResourceHint): void; diff --git a/src/vs/workbench/api/common/extHostComments.ts b/src/vs/workbench/api/common/extHostComments.ts index 53b5176c25b..c84fb4782f9 100644 --- a/src/vs/workbench/api/common/extHostComments.ts +++ b/src/vs/workbench/api/common/extHostComments.ts @@ -424,6 +424,7 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo this._id, this._uri, extHostTypeConverter.Range.from(this._range), + this._comments.map(cmt => convertToDTOComment(this, cmt, this._commentsMap, this.extensionDescription)), extensionDescription.identifier, this._isTemplate, editorId @@ -436,9 +437,6 @@ export function createExtHostComments(mainContext: IMainContext, commands: ExtHo this.eventuallyUpdateCommentThread(); })); - // set up comments after ctor to batch update events. - this.comments = _comments; - this._localDisposables.push({ dispose: () => { proxy.$deleteCommentThread( From 56a2eb0dbbd056498013283317dc50d001c38c5c Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Jun 2024 12:27:05 +0200 Subject: [PATCH 0154/2222] fix #217851 (#218698) --- .../userDataProfile/browser/userDataProfilesEditorModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts index 4b3d9947286..7cbee83dc20 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts @@ -744,7 +744,7 @@ export class UserDataProfilesEditorModel extends EditorModel { const newWindowAction = disposables.add(new Action( 'userDataProfile.newWindow', - localize('open new window', "New Window"), + localize('open new window', "Open New Window with this Profile"), ThemeIcon.asClassName(Codicon.emptyWindow), true, () => this.openWindow(profile) From 9e9eb5e3f66cd52d62b180f6fc46aa3135739c70 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Thu, 27 Jun 2024 12:29:50 +0200 Subject: [PATCH 0155/2222] Don't check `locked` stat in file picker (#218707) Fixes #212408 --- src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 5f3b9cbda7d..95be556e81a 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -832,7 +832,7 @@ export class SimpleFileDialog implements ISimpleFileDialog { } else if (!statDirname.isDirectory) { this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateNonexistentDir', 'Please enter a path that exists.'); return Promise.resolve(false); - } else if (statDirname.readonly || statDirname.locked) { + } else if (statDirname.readonly) { this.filePickBox.validationMessage = nls.localize('remoteFileDialog.validateReadonlyFolder', 'This folder cannot be used as a save destination. Please choose another folder'); return Promise.resolve(false); } From 2e3a1d8bd9270480fc548d9351c7efebfd52bfed Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Jun 2024 12:46:29 +0200 Subject: [PATCH 0156/2222] fix #217902 (#218716) --- .../browser/userDataProfilesEditorModel.ts | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts index 7cbee83dc20..a2e56bc5c7b 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts @@ -716,14 +716,14 @@ export class UserDataProfilesEditorModel extends EditorModel { localize('active', "Use for Current Window"), ThemeIcon.asClassName(Codicon.check), true, - () => this.userDataProfileManagementService.switchProfile(profile) + () => this.userDataProfileManagementService.switchProfile(profileElement.profile) )); const copyFromProfileAction = disposables.add(new Action( 'userDataProfile.copyFromProfile', localize('copyFromProfile', "Duplicate..."), ThemeIcon.asClassName(Codicon.copy), - true, () => this.createNewProfile(profile) + true, () => this.createNewProfile(profileElement.profile) )); const exportAction = disposables.add(new Action( @@ -731,7 +731,7 @@ export class UserDataProfilesEditorModel extends EditorModel { localize('export', "Export..."), ThemeIcon.asClassName(Codicon.export), true, - () => this.exportProfile(profile) + () => this.exportProfile(profileElement.profile) )); const deleteAction = disposables.add(new Action( @@ -739,7 +739,7 @@ export class UserDataProfilesEditorModel extends EditorModel { localize('delete', "Delete"), ThemeIcon.asClassName(Codicon.trash), true, - () => this.removeProfile(profile) + () => this.removeProfile(profileElement.profile) )); const newWindowAction = disposables.add(new Action( @@ -747,12 +747,12 @@ export class UserDataProfilesEditorModel extends EditorModel { localize('open new window', "Open New Window with this Profile"), ThemeIcon.asClassName(Codicon.emptyWindow), true, - () => this.openWindow(profile) + () => this.openWindow(profileElement.profile) )); const useAsNewWindowProfileAction = disposables.add(new Action( 'userDataProfile.useAsNewWindowProfile', - localize('use as new window', "Use for New Windows", profile.name), + localize('use as new window', "Use for New Windows"), undefined, true, () => profileElement.toggleNewWindowProfile() @@ -788,9 +788,9 @@ export class UserDataProfilesEditorModel extends EditorModel { [primaryActions, secondaryActions] )); - activateAction.checked = this.userDataProfileService.currentProfile.id === profile.id; + activateAction.checked = this.userDataProfileService.currentProfile.id === profileElement.profile.id; disposables.add(this.userDataProfileService.onDidChangeCurrentProfile(() => - activateAction.checked = this.userDataProfileService.currentProfile.id === profile.id)); + activateAction.checked = this.userDataProfileService.currentProfile.id === profileElement.profile.id)); useAsNewWindowProfileAction.checked = profileElement.isNewWindowProfile; disposables.add(profileElement.onDidChange(e => { From 1ae48f15b767a33382ae54959018036ec3d19f8e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 27 Jun 2024 14:45:52 +0200 Subject: [PATCH 0157/2222] fix https://github.com/microsoft/vscode/issues/218804 (#218822) --- .../inlineChat/browser/inlineChatController.ts | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 8fdf2669f59..0befb284dbf 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -50,6 +50,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { isEqual } from 'vs/base/common/resources'; import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; +import { escapeRegExpCharacters } from 'vs/base/common/strings'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -433,7 +434,7 @@ export class InlineChatController implements IEditorContribution { })); // #region DEBT - // DEBT@jrieken + // DEBT@jrieken https://github.com/microsoft/vscode/issues/218819 // REMOVE when agents are adopted this._sessionStore.add(this._languageFeatureService.completionProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, hasAccessToAllModels: true }, { _debugDisplayName: 'inline chat commands', @@ -456,7 +457,7 @@ export class InlineChatController implements IEditorContribution { result.suggestions.push({ label: { label: withSlash, description: command.description ?? '' }, kind: CompletionItemKind.Text, - insertText: withSlash, + insertText: `${withSlash} `, range: Range.fromPositions(new Position(1, 1), position), }); } @@ -471,16 +472,12 @@ export class InlineChatController implements IEditorContribution { for (const command of (this._session?.agent.slashCommands ?? []).sort((a, b) => b.name.length - a.name.length)) { const withSlash = `/${command.name}`; const firstLine = model.getLineContent(1); - if (firstLine.startsWith(withSlash)) { + if (firstLine.match(new RegExp(`^${escapeRegExpCharacters(withSlash)}(\\s|$)`))) { newDecorations.push({ range: new Range(1, 1, 1, withSlash.length + 1), options: { description: 'inline-chat-slash-command', inlineClassName: 'inline-chat-slash-command', - after: { - // Force some space between slash command and placeholder - content: ' ' - } } }); From 9a2ef8f356fd5077497e87fa8ff4f5632b2669cf Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 27 Jun 2024 15:22:00 +0200 Subject: [PATCH 0158/2222] Update distro hash --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 49c83a768b7..ae706deebbc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.91.0", - "distro": "9cef7f933867933892cb3b591231ed071fe861a8", + "distro": "a08799837ca498c02f445ca7a896f446419af238", "author": { "name": "Microsoft Corporation" }, From 973dc3065b154f31e6ca21b5fe80f0b23cf858a6 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 27 Jun 2024 16:48:33 +0200 Subject: [PATCH 0159/2222] fix #218219 (#218876) --- .../contrib/extensions/browser/extensions.contribution.ts | 6 +++--- .../contrib/extensions/browser/extensionsActions.ts | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 55dfb610a32..33f961fc455 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -1493,7 +1493,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menu: { id: MenuId.ExtensionContext, group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), CONTEXT_SYNC_ENABLEMENT), + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.not('extensionDisallowInstall'), CONTEXT_SYNC_ENABLEMENT), order: 1 }, run: async (accessor: ServicesAccessor, extensionId: string) => { @@ -1516,7 +1516,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menu: { id: MenuId.ExtensionContext, group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), CONTEXT_SYNC_ENABLEMENT), + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion'), ContextKeyExpr.not('extensionDisallowInstall'), CONTEXT_SYNC_ENABLEMENT), order: 2 }, run: async (accessor: ServicesAccessor, extensionId: string) => { @@ -1540,7 +1540,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menu: { id: MenuId.ExtensionContext, group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension')), + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.not('extensionDisallowInstall')), order: 3 }, run: async (accessor: ServicesAccessor, extensionId: string) => { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 0b5323a75a0..fccbb361feb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1170,6 +1170,7 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n cksOverlay.push(['galleryExtensionHasPreReleaseVersion', extension.gallery?.hasPreReleaseVersion]); cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]); cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]); + cksOverlay.push(['extensionDisallowInstall', !!extension.deprecationInfo?.disallowInstall]); const [colorThemes, fileIconThemes, productIconThemes] = await Promise.all([workbenchThemeService.getColorThemes(), workbenchThemeService.getFileIconThemes(), workbenchThemeService.getProductIconThemes()]); cksOverlay.push(['extensionHasColorThemes', colorThemes.some(theme => isThemeFromExtension(theme, extension))]); From 52be6e6cb7e33efe7f46eac134435d7e64040994 Mon Sep 17 00:00:00 2001 From: Johannes Date: Thu, 27 Jun 2024 17:06:23 +0200 Subject: [PATCH 0160/2222] let inline chat use default command and agent completions --- .../chatMarkdownDecorationsRenderer.ts | 4 +- .../browser/contrib/chatInputCompletions.ts | 23 +++-- .../browser/contrib/chatInputEditorContrib.ts | 4 +- .../contrib/chat/common/chatParserTypes.ts | 4 + .../contrib/chat/common/chatRequestParser.ts | 12 ++- .../browser/inlineChatController.ts | 91 +------------------ 6 files changed, 40 insertions(+), 98 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index 9af542462c3..bb016c0f1fa 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts @@ -87,7 +87,9 @@ export class ChatMarkdownDecorationsRenderer { if (part instanceof ChatRequestTextPart) { result += part.text; } else if (part instanceof ChatRequestAgentPart) { - result += this.instantiationService.invokeFunction(accessor => agentToMarkdown(part.agent, false, accessor)); + if (!part.agent.isDefault) { + result += this.instantiationService.invokeFunction(accessor => agentToMarkdown(part.agent, false, accessor)); + } } else { const uri = part instanceof ChatRequestDynamicVariablePart && part.data instanceof URI ? part.data : diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 9509e38f8d1..4b10231de24 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -40,7 +40,7 @@ class SlashCommandCompletions extends Disposable { triggerCharacters: ['/'], provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, _token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); - if (!widget || !widget.viewModel || (widget.location !== ChatAgentLocation.Panel && widget.location !== ChatAgentLocation.Notebook) /* TODO@jrieken - enable when agents are adopted*/) { + if (!widget || !widget.viewModel) { return null; } @@ -96,7 +96,7 @@ class AgentCompletions extends Disposable { triggerCharacters: ['@'], provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, _token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); - if (!widget || !widget.viewModel || (widget.location !== ChatAgentLocation.Panel && widget.location !== ChatAgentLocation.Notebook) /* TODO@jrieken - enable when agents are adopted*/) { + if (!widget || !widget.viewModel) { return null; } @@ -140,7 +140,7 @@ class AgentCompletions extends Disposable { triggerCharacters: ['/'], provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); - if (!widget || !widget.viewModel || widget.location !== ChatAgentLocation.Panel /* TODO@jrieken - enable when agents are adopted*/) { + if (!widget || !widget.viewModel) { return; } @@ -192,7 +192,7 @@ class AgentCompletions extends Disposable { provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); const viewModel = widget?.viewModel; - if (!widget || !viewModel || widget.location !== ChatAgentLocation.Panel /* TODO@jrieken - enable when agents are adopted*/) { + if (!widget || !viewModel) { return; } @@ -239,7 +239,7 @@ class AgentCompletions extends Disposable { agents.flatMap(agent => agent.slashCommands.map((c, i) => { const { label: agentLabel, isDupe } = this.getAgentCompletionDetails(agent); const withSlash = `${chatSubcommandLeader}${c.name}`; - return { + const item: CompletionItem = { label: { label: withSlash, description: agentLabel, detail: isDupe ? ` (${agent.publisherDisplayName})` : undefined }, filterText: getFilterText(agent, c.name), commitCharacters: [' '], @@ -249,7 +249,16 @@ class AgentCompletions extends Disposable { kind: CompletionItemKind.Text, // The icons are disabled here anyway sortText: `${chatSubcommandLeader}${agent.name}${c.name}`, command: { id: AssignSelectedAgentAction.ID, title: AssignSelectedAgentAction.ID, arguments: [{ agent, widget } satisfies AssignSelectedAgentActionArgs] }, - } satisfies CompletionItem; + }; + + if (agent.isDefault) { + // default agent isn't mentioned or inserted + item.label = { label: withSlash, description: c.description }; + item.insertText = `${withSlash} `; + item.detail = c.description; + } + + return item; }))) }; } @@ -305,7 +314,7 @@ class BuiltinDynamicCompletions extends Disposable { triggerCharacters: [chatVariableLeader], provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, _token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); - if (!widget || !widget.supportsFileReferences || widget.location !== ChatAgentLocation.Panel /* TODO@jrieken - enable when agents are adopted*/) { + if (!widget || !widget.supportsFileReferences) { return null; } diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index e58d0fd8294..0234253d3b8 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -14,7 +14,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IChatWidget } from 'vs/workbench/contrib/chat/browser/chat'; import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { dynamicVariableDecorationType } from 'vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables'; -import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workbench/contrib/chat/common/chatColors'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, IParsedChatRequestPart, chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; @@ -275,7 +275,7 @@ class ChatTokenDeleter extends Disposable { // If this was a simple delete, try to find out whether it was inside a token if (!change.text && this.widget.viewModel) { - const previousParsedValue = parser.parseChatRequest(this.widget.viewModel.sessionId, previousInputValue, ChatAgentLocation.Panel, { selectedAgent: previousSelectedAgent }); + const previousParsedValue = parser.parseChatRequest(this.widget.viewModel.sessionId, previousInputValue, widget.location, { selectedAgent: previousSelectedAgent }); // For dynamic variables, this has to happen in ChatDynamicVariableModel with the other bookkeeping const deletableTokens = previousParsedValue.parts.filter(p => p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart || p instanceof ChatRequestSlashCommandPart || p instanceof ChatRequestVariablePart); diff --git a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts index 66bc10c2061..f155cd63194 100644 --- a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts @@ -81,6 +81,10 @@ export class ChatRequestAgentPart implements IParsedChatRequestPart { get promptText(): string { return ''; } + + get isSynthetic(): boolean { + return this.range.length === 0; + } } /** diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index 73276b9464d..21795795d34 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -31,6 +31,11 @@ export class ChatRequestParser { const parts: IParsedChatRequestPart[] = []; const references = this.variableService.getDynamicVariables(sessionId); // must access this list before any async calls + const defaultAgent = this.agentService.getDefaultAgent(location); + if (defaultAgent) { + parts.push(new ChatRequestAgentPart(OffsetRange.ofLength(0), new Range(1, 1, 1, 1), defaultAgent)); + } + let lineNumber = 1; let column = 1; for (let i = 0; i < message.length; i++) { @@ -90,12 +95,17 @@ export class ChatRequestParser { }; } - private tryToParseAgent(message: string, fullMessage: string, offset: number, position: IPosition, parts: ReadonlyArray, location: ChatAgentLocation, context: IChatParserContext | undefined): ChatRequestAgentPart | ChatRequestVariablePart | undefined { + private tryToParseAgent(message: string, fullMessage: string, offset: number, position: IPosition, parts: Array, location: ChatAgentLocation, context: IChatParserContext | undefined): ChatRequestAgentPart | ChatRequestVariablePart | undefined { const nextAgentMatch = message.match(agentReg); if (!nextAgentMatch) { return; } + const syntheticDefaultAgent = parts.findIndex(candidate => candidate instanceof ChatRequestAgentPart && candidate.isSynthetic); + if (syntheticDefaultAgent >= 0) { + parts.splice(syntheticDefaultAgent, 1); + } + const [full, name] = nextAgentMatch; const agentRange = new OffsetRange(offset, offset + full.length); const agentEditorRange = new Range(position.lineNumber, position.column, position.lineNumber, position.column + full.length); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 0befb284dbf..f0eee6e63a8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -18,8 +18,8 @@ import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IPosition, Position } from 'vs/editor/common/core/position'; import { IRange, Range } from 'vs/editor/common/core/range'; import { ISelection, Selection } from 'vs/editor/common/core/selection'; -import { IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; -import { CompletionItemKind, CompletionList, TextEdit } from 'vs/editor/common/languages'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { TextEdit } from 'vs/editor/common/languages'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; import { InlineCompletionsController } from 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsController'; import { localize } from 'vs/nls'; @@ -28,7 +28,7 @@ import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/c import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; -import { IChatWidgetService, showChatView } from 'vs/workbench/contrib/chat/browser/chat'; +import { showChatView } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IInlineChatSavingService } from './inlineChatSavingService'; import { EmptyResponse, ErrorResponse, ReplyResponse, Session, SessionPrompt } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; @@ -37,20 +37,17 @@ import { EditModeStrategy, IEditObserver, LiveStrategy, PreviewStrategy, Progres import { InlineChatZoneWidget } from './inlineChatZoneWidget'; import { CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, CTX_INLINE_CHAT_USER_DID_EDIT, CTX_INLINE_CHAT_VISIBLE, EditMode, INLINE_CHAT_ID, InlineChatConfigKeys, InlineChatResponseType } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { StashedSession } from './inlineChatSession'; -import { IModelDeltaDecoration, ITextModel, IValidEditOperation } from 'vs/editor/common/model'; +import { IValidEditOperation } from 'vs/editor/common/model'; import { InlineChatContentWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget'; import { MessageController } from 'vs/editor/contrib/message/browser/messageController'; import { ChatModel, ChatRequestRemovalReason, IChatRequestModel, IChatTextEditGroup, IChatTextEditGroupState, IResponse } from 'vs/workbench/contrib/chat/common/chatModel'; import { InlineChatError } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl'; -import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { DefaultModelSHA1Computer } from 'vs/editor/common/services/modelService'; import { generateUuid } from 'vs/base/common/uuid'; import { isEqual } from 'vs/base/common/resources'; import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; -import { escapeRegExpCharacters } from 'vs/base/common/strings'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -144,8 +141,6 @@ export class InlineChatController implements IEditorContribution { @IDialogService private readonly _dialogService: IDialogService, @IContextKeyService contextKeyService: IContextKeyService, @IChatService private readonly _chatService: IChatService, - @ILanguageFeaturesService private readonly _languageFeatureService: ILanguageFeaturesService, - @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, @INotebookEditorService notebookEditorService: INotebookEditorService, ) { this._ctxVisible = CTX_INLINE_CHAT_VISIBLE.bindTo(contextKeyService); @@ -433,84 +428,6 @@ export class InlineChatController implements IEditorContribution { } })); - // #region DEBT - // DEBT@jrieken https://github.com/microsoft/vscode/issues/218819 - // REMOVE when agents are adopted - this._sessionStore.add(this._languageFeatureService.completionProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, hasAccessToAllModels: true }, { - _debugDisplayName: 'inline chat commands', - triggerCharacters: ['/'], - provideCompletionItems: (model, position, context, token) => { - if (position.lineNumber !== 1) { - return undefined; - } - if (!this._session || !this._session.agent.slashCommands) { - return undefined; - } - const widget = this._chatWidgetService.getWidgetByInputUri(model.uri); - if (widget !== this._ui.value.zone.widget.chatWidget && widget !== this._ui.value.content.chatWidget) { - return undefined; - } - - const result: CompletionList = { suggestions: [], incomplete: false }; - for (const command of this._session.agent.slashCommands) { - const withSlash = `/${command.name}`; - result.suggestions.push({ - label: { label: withSlash, description: command.description ?? '' }, - kind: CompletionItemKind.Text, - insertText: `${withSlash} `, - range: Range.fromPositions(new Position(1, 1), position), - }); - } - - return result; - } - })); - - const updateSlashDecorations = (collection: IEditorDecorationsCollection, model: ITextModel) => { - - const newDecorations: IModelDeltaDecoration[] = []; - for (const command of (this._session?.agent.slashCommands ?? []).sort((a, b) => b.name.length - a.name.length)) { - const withSlash = `/${command.name}`; - const firstLine = model.getLineContent(1); - if (firstLine.match(new RegExp(`^${escapeRegExpCharacters(withSlash)}(\\s|$)`))) { - newDecorations.push({ - range: new Range(1, 1, 1, withSlash.length + 1), - options: { - description: 'inline-chat-slash-command', - inlineClassName: 'inline-chat-slash-command', - } - }); - - // inject detail when otherwise empty - if (firstLine.trim() === `/${command.name}`) { - newDecorations.push({ - range: new Range(1, withSlash.length, 1, withSlash.length), - options: { - description: 'inline-chat-slash-command-detail', - after: { - content: `${command.description}`, - inlineClassName: 'inline-chat-slash-command-detail' - } - } - }); - } - break; - } - } - collection.set(newDecorations); - }; - const inputInputEditor = this._ui.value.content.chatWidget.inputEditor; - const zoneInputEditor = this._ui.value.zone.widget.chatWidget.inputEditor; - const inputDecorations = inputInputEditor.createDecorationsCollection(); - const zoneDecorations = zoneInputEditor.createDecorationsCollection(); - this._sessionStore.add(inputInputEditor.onDidChangeModelContent(() => updateSlashDecorations(inputDecorations, inputInputEditor.getModel()!))); - this._sessionStore.add(zoneInputEditor.onDidChangeModelContent(() => updateSlashDecorations(zoneDecorations, zoneInputEditor.getModel()!))); - this._sessionStore.add(toDisposable(() => { - inputDecorations.clear(); - zoneDecorations.clear(); - })); - - //#endregion ------- DEBT if (!this._session.chatModel.hasRequests) { return State.WAIT_FOR_INPUT; From 4b28f5d48cc29faa1576e3da304854faeed35ad7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 27 Jun 2024 11:00:50 -0700 Subject: [PATCH 0161/2222] Use webgl in terminal sticky scroll Fixes #216817 Fixes #218427 --- .../browser/terminalStickyScrollOverlay.ts | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts index 194ee23694e..639ec2c8833 100644 --- a/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts +++ b/src/vs/workbench/contrib/terminalContrib/stickyScroll/browser/terminalStickyScrollOverlay.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type { SerializeAddon as SerializeAddonType } from '@xterm/addon-serialize'; +import type { WebglAddon as WebglAddonType } from '@xterm/addon-webgl'; import type { IBufferLine, IMarker, ITerminalOptions, ITheme, Terminal as RawXtermTerminal, Terminal as XTermTerminal } from '@xterm/xterm'; import { importAMDNodeModule } from 'vs/amdX'; import { $, addDisposableListener, addStandardDisposableListener, getWindow } from 'vs/base/browser/dom'; @@ -21,7 +22,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandDetectionCapability, ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { ICurrentPartialCommand } from 'vs/platform/terminal/common/capabilities/commandDetection/terminalCommand'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { ITerminalInstance, IXtermColorProvider, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalConfigurationService, ITerminalInstance, IXtermColorProvider, IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { openContextMenu } from 'vs/workbench/contrib/terminal/browser/terminalContextMenu'; import { IXtermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; import { TERMINAL_CONFIG_SECTION, TerminalCommandId } from 'vs/workbench/contrib/terminal/common/terminal'; @@ -46,6 +47,7 @@ const enum Constants { export class TerminalStickyScrollOverlay extends Disposable { private _stickyScrollOverlay?: RawXtermTerminal; private _serializeAddon?: SerializeAddonType; + private _webglAddon?: WebglAddonType; private _element?: HTMLElement; private _currentStickyCommand?: ITerminalCommand | ICurrentPartialCommand; @@ -69,6 +71,7 @@ export class TerminalStickyScrollOverlay extends Disposable { @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @IMenuService menuService: IMenuService, + @ITerminalConfigurationService private readonly _terminalConfigurationService: ITerminalConfigurationService, @IThemeService private readonly _themeService: IThemeService, ) { super(); @@ -101,6 +104,7 @@ export class TerminalStickyScrollOverlay extends Disposable { allowProposedApi: true, ...this._getOptions() })); + this._refreshGpuAcceleration(); this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(TERMINAL_CONFIG_SECTION)) { this._syncOptions(); @@ -413,6 +417,7 @@ export class TerminalStickyScrollOverlay extends Disposable { } this._stickyScrollOverlay.resize(this._xterm.raw.cols, this._stickyScrollOverlay.rows); this._stickyScrollOverlay.options = this._getOptions(); + this._refreshGpuAcceleration(); } private _getOptions(): ITerminalOptions { @@ -435,9 +440,29 @@ export class TerminalStickyScrollOverlay extends Disposable { minimumContrastRatio: o.minimumContrastRatio, tabStopWidth: o.tabStopWidth, overviewRulerWidth: o.overviewRulerWidth, + customGlyphs: o.customGlyphs, }; } + @throttle(0) + private async _refreshGpuAcceleration() { + if (this._shouldLoadWebgl() && !this._webglAddon) { + const WebglAddon = await this._getWebglAddonConstructor(); + if (this._store.isDisposed) { + return; + } + this._webglAddon = this._register(new WebglAddon()); + this._stickyScrollOverlay?.loadAddon(this._webglAddon); + } else if (!this._shouldLoadWebgl() && this._webglAddon) { + this._webglAddon.dispose(); + this._webglAddon = undefined; + } + } + + private _shouldLoadWebgl(): boolean { + return this._terminalConfigurationService.config.gpuAcceleration === 'auto' || this._terminalConfigurationService.config.gpuAcceleration === 'on'; + } + private _getTheme(isHovering: boolean): ITheme { const theme = this._themeService.getColorTheme(); return { @@ -452,8 +477,12 @@ export class TerminalStickyScrollOverlay extends Disposable { @memoize private async _getSerializeAddonConstructor(): Promise { - const m = await importAMDNodeModule('@xterm/addon-serialize', 'lib/addon-serialize.js'); - return m.SerializeAddon; + return (await importAMDNodeModule('@xterm/addon-serialize', 'lib/addon-serialize.js')).SerializeAddon; + } + + @memoize + private async _getWebglAddonConstructor(): Promise { + return (await importAMDNodeModule('@xterm/addon-webgl', 'lib/addon-webgl.js')).WebglAddon; } } From 8d54ffc9f6e7c2ba7de3e564290659a9a5381f5a Mon Sep 17 00:00:00 2001 From: Snoppy Date: Fri, 28 Jun 2024 04:01:19 +0800 Subject: [PATCH 0162/2222] chore: fix typos (#216562) Signed-off-by: snoppy --- cli/src/async_pipe.rs | 2 +- cli/src/auth.rs | 2 +- cli/src/commands/args.rs | 4 ++-- cli/src/constants.rs | 2 +- src/vs/workbench/contrib/debug/common/debug.ts | 6 +++--- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/cli/src/async_pipe.rs b/cli/src/async_pipe.rs index e9b710c1d68..78aed6fe3e7 100644 --- a/cli/src/async_pipe.rs +++ b/cli/src/async_pipe.rs @@ -227,7 +227,7 @@ impl hyper::server::accept::Accept for PollableAsyncListener { } } -/// Gets a random name for a pipe/socket on the paltform +/// Gets a random name for a pipe/socket on the platform pub fn get_socket_name() -> PathBuf { cfg_if::cfg_if! { if #[cfg(unix)] { diff --git a/cli/src/auth.rs b/cli/src/auth.rs index 67f1bfa6bc7..2d9162c5483 100644 --- a/cli/src/auth.rs +++ b/cli/src/auth.rs @@ -287,7 +287,7 @@ impl StorageImplementation for ThreadKeyringStorage { #[derive(Default)] struct KeyringStorage { - // keywring storage can be split into multiple entries due to entry length limits + // keyring storage can be split into multiple entries due to entry length limits // on Windows https://github.com/microsoft/vscode-cli/issues/358 entries: Vec, } diff --git a/cli/src/commands/args.rs b/cli/src/commands/args.rs index 79c4d3767a1..05e22e0cfb3 100644 --- a/cli/src/commands/args.rs +++ b/cli/src/commands/args.rs @@ -64,7 +64,7 @@ pub struct IntegratedCli { pub core: CliCore, } -/// Common CLI shared between intergated and standalone interfaces. +/// Common CLI shared between integrated and standalone interfaces. #[derive(Args, Debug, Default, Clone)] pub struct CliCore { /// One or more files, folders, or URIs to open. @@ -619,7 +619,7 @@ pub enum OutputFormat { #[derive(Args, Clone, Debug, Default)] pub struct ExistingTunnelArgs { /// Name you'd like to assign preexisting tunnel to use to connect the tunnel - /// Old option, new code sohuld just use `--name`. + /// Old option, new code should just use `--name`. #[clap(long, hide = true)] pub tunnel_name: Option, diff --git a/cli/src/constants.rs b/cli/src/constants.rs index 6f604e8876e..1e277a89d6a 100644 --- a/cli/src/constants.rs +++ b/cli/src/constants.rs @@ -13,7 +13,7 @@ use crate::options::Quality; pub const CONTROL_PORT: u16 = 31545; -/// Protocol version sent to clients. This can be used to indiciate new or +/// Protocol version sent to clients. This can be used to indicate new or /// changed capabilities that clients may wish to leverage. /// 1 - Initial protocol version /// 2 - Addition of `serve.compressed` property to control whether servermsg's diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index c9ec64206bd..41f1a55b557 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -51,9 +51,9 @@ export const CONTEXT_IN_DEBUG_REPL = new RawContextKey('inDebugRepl', f export const CONTEXT_BREAKPOINT_WIDGET_VISIBLE = new RawContextKey('breakpointWidgetVisible', false, { type: 'boolean', description: nls.localize('breakpointWidgetVisibile', "True when breakpoint editor zone widget is visible, false otherwise.") }); export const CONTEXT_IN_BREAKPOINT_WIDGET = new RawContextKey('inBreakpointWidget', false, { type: 'boolean', description: nls.localize('inBreakpointWidget', "True when focus is in the breakpoint editor zone widget, false otherwise.") }); export const CONTEXT_BREAKPOINTS_FOCUSED = new RawContextKey('breakpointsFocused', true, { type: 'boolean', description: nls.localize('breakpointsFocused', "True when the BREAKPOINTS view is focused, false otherwise.") }); -export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey('watchExpressionsFocused', true, { type: 'boolean', description: nls.localize('watchExpressionsFocused', "True when the WATCH view is focused, false otherwsie.") }); +export const CONTEXT_WATCH_EXPRESSIONS_FOCUSED = new RawContextKey('watchExpressionsFocused', true, { type: 'boolean', description: nls.localize('watchExpressionsFocused', "True when the WATCH view is focused, false otherwise.") }); export const CONTEXT_WATCH_EXPRESSIONS_EXIST = new RawContextKey('watchExpressionsExist', false, { type: 'boolean', description: nls.localize('watchExpressionsExist', "True when at least one watch expression exists, false otherwise.") }); -export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey('variablesFocused', true, { type: 'boolean', description: nls.localize('variablesFocused', "True when the VARIABLES views is focused, false otherwsie") }); +export const CONTEXT_VARIABLES_FOCUSED = new RawContextKey('variablesFocused', true, { type: 'boolean', description: nls.localize('variablesFocused', "True when the VARIABLES views is focused, false otherwise") }); export const CONTEXT_EXPRESSION_SELECTED = new RawContextKey('expressionSelected', false, { type: 'boolean', description: nls.localize('expressionSelected', "True when an expression input box is open in either the WATCH or the VARIABLES view, false otherwise.") }); export const CONTEXT_BREAKPOINT_INPUT_FOCUSED = new RawContextKey('breakpointInputFocused', false, { type: 'boolean', description: nls.localize('breakpointInputFocused', "True when the input box has focus in the BREAKPOINTS view.") }); export const CONTEXT_CALLSTACK_ITEM_TYPE = new RawContextKey('callStackItemType', undefined, { type: 'string', description: nls.localize('callStackItemType', "Represents the item type of the focused element in the CALL STACK view. For example: 'session', 'thread', 'stackFrame'") }); @@ -72,7 +72,7 @@ export const CONTEXT_FOCUSED_SESSION_IS_ATTACH = new RawContextKey('foc export const CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG = new RawContextKey('focusedSessionIsNoDebug', false, { type: 'boolean', description: nls.localize('focusedSessionIsNoDebug', "True when the focused session is run without debugging.") }); export const CONTEXT_STEP_BACK_SUPPORTED = new RawContextKey('stepBackSupported', false, { type: 'boolean', description: nls.localize('stepBackSupported', "True when the focused session supports 'stepBack' requests.") }); export const CONTEXT_RESTART_FRAME_SUPPORTED = new RawContextKey('restartFrameSupported', false, { type: 'boolean', description: nls.localize('restartFrameSupported', "True when the focused session supports 'restartFrame' requests.") }); -export const CONTEXT_STACK_FRAME_SUPPORTS_RESTART = new RawContextKey('stackFrameSupportsRestart', false, { type: 'boolean', description: nls.localize('stackFrameSupportsRestart', "True when the focused stack frame suppots 'restartFrame'.") }); +export const CONTEXT_STACK_FRAME_SUPPORTS_RESTART = new RawContextKey('stackFrameSupportsRestart', false, { type: 'boolean', description: nls.localize('stackFrameSupportsRestart', "True when the focused stack frame supports 'restartFrame'.") }); export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey('jumpToCursorSupported', false, { type: 'boolean', description: nls.localize('jumpToCursorSupported', "True when the focused session supports 'jumpToCursor' request.") }); export const CONTEXT_STEP_INTO_TARGETS_SUPPORTED = new RawContextKey('stepIntoTargetsSupported', false, { type: 'boolean', description: nls.localize('stepIntoTargetsSupported', "True when the focused session supports 'stepIntoTargets' request.") }); export const CONTEXT_BREAKPOINTS_EXIST = new RawContextKey('breakpointsExist', false, { type: 'boolean', description: nls.localize('breakpointsExist', "True when at least one breakpoint exists.") }); From 2eedc1373c9f73f75300e788b7f576ce21b4fa42 Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Thu, 27 Jun 2024 14:20:34 -0700 Subject: [PATCH 0163/2222] look for keylabel that is used for mac as well (#219015) look for mac keylabel as well --- .../browser/replInputHintContentWidget.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts b/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts index b42ea604df8..6dc4644b71a 100644 --- a/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts +++ b/src/vs/workbench/contrib/interactive/browser/replInputHintContentWidget.ts @@ -122,23 +122,25 @@ export class ReplInputHintContentWidget extends Disposable implements IContentWi private getKeybinding() { const keybindings = this.keybindingService.lookupKeybindings('interactive.execute'); const shiftEnterConfig = this.configurationService.getValue(InteractiveWindowSetting.executeWithShiftEnter); - const hasChord = (chord: string, kb: ResolvedKeybinding) => { + const hasEnterChord = (kb: ResolvedKeybinding, modifier: string = '') => { const chords = kb.getDispatchChords(); - return chords.length === 1 && chords[0] === chord; + const chord = modifier + 'Enter'; + const chordAlt = modifier + '[Enter]'; + return chords.length === 1 && (chords[0] === chord || chords[0] === chordAlt); }; if (shiftEnterConfig) { - const keybinding = keybindings.find(kb => hasChord('shift+Enter', kb)); + const keybinding = keybindings.find(kb => hasEnterChord(kb, 'shift+')); if (keybinding) { return keybinding; } } else { - let keybinding = keybindings.find(kb => hasChord('Enter', kb)); + let keybinding = keybindings.find(kb => hasEnterChord(kb)); if (keybinding) { return keybinding; } keybinding = this.keybindingService.lookupKeybindings('python.execInREPLEnter') - .find(kb => hasChord('Enter', kb)); + .find(kb => hasEnterChord(kb)); if (keybinding) { return keybinding; } From aea213b7fcc7de5c24ad797ac1af209b159d451f Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Thu, 27 Jun 2024 14:21:14 -0700 Subject: [PATCH 0164/2222] chore: bump braces in build (#219010) --- build/yarn.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/build/yarn.lock b/build/yarn.lock index d0c7960a11e..d99ceffaadf 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -848,11 +848,11 @@ brace-expansion@^1.1.7: concat-map "0.0.1" braces@~3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" - integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + version "3.0.3" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" + integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA== dependencies: - fill-range "^7.0.1" + fill-range "^7.1.1" buffer-alloc-unsafe@^1.1.0: version "1.1.0" @@ -1371,10 +1371,10 @@ fd-slicer@~1.1.0: dependencies: pend "~1.2.0" -fill-range@^7.0.1: - version "7.0.1" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" - integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== +fill-range@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292" + integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg== dependencies: to-regex-range "^5.0.1" From d390b22c6d0b9f9e90a74690fef3e385e0d1c5ca Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 28 Jun 2024 09:31:37 +0200 Subject: [PATCH 0165/2222] fix lint error --- src/vs/platform/instantiation/common/instantiationService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/instantiation/common/instantiationService.ts b/src/vs/platform/instantiation/common/instantiationService.ts index cab2957b146..a61ef74333d 100644 --- a/src/vs/platform/instantiation/common/instantiationService.ts +++ b/src/vs/platform/instantiation/common/instantiationService.ts @@ -220,7 +220,9 @@ export class InstantiationService implements IInstantiationService { while (stack.length) { const item = stack.pop()!; - if (seen.has(String(item.id))) continue; + if (seen.has(String(item.id))) { + continue; + } seen.add(String(item.id)); graph.lookupOrInsertNode(item); From cb1514f9a64ab342f94092f79bdf1a768635d96f Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 28 Jun 2024 10:08:40 +0200 Subject: [PATCH 0166/2222] Bump version to 1.92.0 (#219067) --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ae706deebbc..f8bfdf775a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "code-oss-dev", - "version": "1.91.0", + "version": "1.92.0", "distro": "a08799837ca498c02f445ca7a896f446419af238", "author": { "name": "Microsoft Corporation" From 8277c0eba8840d3248f988055ace0bf9d4466ea0 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 28 Jun 2024 11:12:56 +0200 Subject: [PATCH 0167/2222] allow `tryToParseSlashCommand` to respect default agent commands with default agent being part of the query --- .../browser/contrib/chatInputCompletions.ts | 4 ++-- .../contrib/chat/common/chatParserTypes.ts | 4 ---- .../contrib/chat/common/chatRequestParser.ts | 22 +++++++++---------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 4b10231de24..2c1cd05400b 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -252,8 +252,8 @@ class AgentCompletions extends Disposable { }; if (agent.isDefault) { - // default agent isn't mentioned or inserted - item.label = { label: withSlash, description: c.description }; + // default agent isn't mentioned nor inserted + item.label = withSlash; item.insertText = `${withSlash} `; item.detail = c.description; } diff --git a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts index f155cd63194..66bc10c2061 100644 --- a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts @@ -81,10 +81,6 @@ export class ChatRequestAgentPart implements IParsedChatRequestPart { get promptText(): string { return ''; } - - get isSynthetic(): boolean { - return this.range.length === 0; - } } /** diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index 21795795d34..c14bb36a65f 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -31,11 +31,6 @@ export class ChatRequestParser { const parts: IParsedChatRequestPart[] = []; const references = this.variableService.getDynamicVariables(sessionId); // must access this list before any async calls - const defaultAgent = this.agentService.getDefaultAgent(location); - if (defaultAgent) { - parts.push(new ChatRequestAgentPart(OffsetRange.ofLength(0), new Range(1, 1, 1, 1), defaultAgent)); - } - let lineNumber = 1; let column = 1; for (let i = 0; i < message.length; i++) { @@ -48,7 +43,7 @@ export class ChatRequestParser { } else if (char === chatAgentLeader) { newPart = this.tryToParseAgent(message.slice(i), message, i, new Position(lineNumber, column), parts, location, context); } else if (char === chatSubcommandLeader) { - newPart = this.tryToParseSlashCommand(message.slice(i), message, i, new Position(lineNumber, column), parts); + newPart = this.tryToParseSlashCommand(message.slice(i), message, i, new Position(lineNumber, column), parts, location); } if (!newPart) { @@ -101,11 +96,6 @@ export class ChatRequestParser { return; } - const syntheticDefaultAgent = parts.findIndex(candidate => candidate instanceof ChatRequestAgentPart && candidate.isSynthetic); - if (syntheticDefaultAgent >= 0) { - parts.splice(syntheticDefaultAgent, 1); - } - const [full, name] = nextAgentMatch; const agentRange = new OffsetRange(offset, offset + full.length); const agentEditorRange = new Range(position.lineNumber, position.column, position.lineNumber, position.column + full.length); @@ -169,7 +159,7 @@ export class ChatRequestParser { return; } - private tryToParseSlashCommand(remainingMessage: string, fullMessage: string, offset: number, position: IPosition, parts: ReadonlyArray): ChatRequestSlashCommandPart | ChatRequestAgentSubcommandPart | undefined { + private tryToParseSlashCommand(remainingMessage: string, fullMessage: string, offset: number, position: IPosition, parts: ReadonlyArray, location: ChatAgentLocation): ChatRequestSlashCommandPart | ChatRequestAgentSubcommandPart | undefined { const nextSlashMatch = remainingMessage.match(slashReg); if (!nextSlashMatch) { return; @@ -209,6 +199,14 @@ export class ChatRequestParser { if (slashCommand) { // Valid standalone slash command return new ChatRequestSlashCommandPart(slashRange, slashEditorRange, slashCommand); + } else { + // check for with default agent for this location + const defaultAgent = this.agentService.getDefaultAgent(location); + const subCommand = defaultAgent?.slashCommands.find(c => c.name === command); + if (subCommand) { + // Valid default agent subcommand + return new ChatRequestAgentSubcommandPart(slashRange, slashEditorRange, subCommand); + } } } From e4fa43f16886adb970b75e7578a317e4f8801115 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 28 Jun 2024 11:29:28 +0200 Subject: [PATCH 0168/2222] decorate agent command part when alone (with agent) --- .../browser/contrib/chatInputEditorContrib.ts | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index 0234253d3b8..24aafd6c101 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -175,8 +175,8 @@ class InputEditorDecorations extends Disposable { } } - const onlyAgentCommandAndWhitespace = agentPart && agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart); - if (onlyAgentCommandAndWhitespace) { + const onlyAgentAndAgentCommandAndWhitespace = agentPart && agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart); + if (onlyAgentAndAgentCommandAndWhitespace) { // Agent reference and subcommand with no other text - show the placeholder const isFollowupSlashCommand = this.previouslyUsedAgents.has(agentAndCommandToKey(agentPart.agent, agentSubcommandPart.command.name)); const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && agentSubcommandPart.command.followupPlaceholder; @@ -193,14 +193,30 @@ class InputEditorDecorations extends Disposable { } } + const onlyAgentCommandAndWhitespace = agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentSubcommandPart); + if (onlyAgentCommandAndWhitespace) { + // Agent subcommand with no other text - show the placeholder + if (agentSubcommandPart?.command.description && exactlyOneSpaceAfterPart(agentSubcommandPart)) { + placeholderDecoration = [{ + range: getRangeForPlaceholder(agentSubcommandPart), + renderOptions: { + after: { + contentText: agentSubcommandPart.command.description, + color: this.getPlaceholderColor(), + } + } + }]; + } + } + this.widget.inputEditor.setDecorationsByType(decorationDescription, placeholderDecorationType, placeholderDecoration ?? []); const textDecorations: IDecorationOptions[] | undefined = []; if (agentPart) { textDecorations.push({ range: agentPart.editorRange }); - if (agentSubcommandPart) { - textDecorations.push({ range: agentSubcommandPart.editorRange, hoverMessage: new MarkdownString(agentSubcommandPart.command.description) }); - } + } + if (agentSubcommandPart) { + textDecorations.push({ range: agentSubcommandPart.editorRange, hoverMessage: new MarkdownString(agentSubcommandPart.command.description) }); } if (slashCommandPart) { From 375d8c8bebfb43800a1f0ffc32f702b4a495a582 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 28 Jun 2024 11:42:01 +0200 Subject: [PATCH 0169/2222] make `SlashCommandService` location aware, scope /clear and /help to panel location --- .../contrib/chat/browser/chat.contribution.ts | 6 ++++-- .../browser/contrib/chatInputCompletions.ts | 2 +- .../contrib/chat/common/chatRequestParser.ts | 2 +- .../contrib/chat/common/chatServiceImpl.ts | 2 +- .../contrib/chat/common/chatSlashCommands.ts | 17 +++++++++-------- 5 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 4937c6afbfd..46c8fa3be70 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -176,7 +176,8 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { command: 'clear', detail: nls.localize('clear', "Start a new chat"), sortText: 'z2_clear', - executeImmediately: true + executeImmediately: true, + locations: [ChatAgentLocation.Panel] }, async () => { commandService.executeCommand(ACTION_ID_NEW_CHAT); })); @@ -184,7 +185,8 @@ class ChatSlashStaticSlashCommandsContribution extends Disposable { command: 'help', detail: '', sortText: 'z1_help', - executeImmediately: true + executeImmediately: true, + locations: [ChatAgentLocation.Panel] }, async (prompt, progress) => { const defaultAgent = chatAgentService.getDefaultAgent(ChatAgentLocation.Panel); const agents = chatAgentService.getAgents(); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 2c1cd05400b..8910e307eab 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -56,7 +56,7 @@ class SlashCommandCompletions extends Disposable { return; } - const slashCommands = this.chatSlashCommandService.getCommands(); + const slashCommands = this.chatSlashCommandService.getCommands(widget.location); if (!slashCommands) { return null; } diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index c14bb36a65f..e6492570295 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -194,7 +194,7 @@ export class ChatRequestParser { return new ChatRequestAgentSubcommandPart(slashRange, slashEditorRange, subCommand); } } else { - const slashCommands = this.slashCommandService.getCommands(); + const slashCommands = this.slashCommandService.getCommands(location); const slashCommand = slashCommands.find(c => c.command === command); if (slashCommand) { // Valid standalone slash command diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index ff8d83fc2c3..42d17335b84 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -617,7 +617,7 @@ export class ChatService extends Disposable implements IChatService { const message = parsedRequest.text; const commandResult = await this.chatSlashCommandService.executeCommand(commandPart.slashCommand.command, message.substring(commandPart.slashCommand.command.length + 1).trimStart(), new Progress(p => { progressCallback(p); - }), history, token); + }), history, location, token); agentOrCommandFollowups = Promise.resolve(commandResult?.followUp); rawResult = {}; diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index 2d43f1c0396..3b19cd512b9 100644 --- a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts +++ b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts @@ -11,6 +11,7 @@ import { IProgress } from 'vs/platform/progress/common/progress'; import { IChatMessage } from 'vs/workbench/contrib/chat/common/languageModels'; import { IChatFollowup, IChatProgress, IChatResponseProgressFileTreeData } from 'vs/workbench/contrib/chat/common/chatService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; //#region slash service, commands etc @@ -18,18 +19,18 @@ export interface IChatSlashData { command: string; detail: string; sortText?: string; - /** * Whether the command should execute as soon * as it is entered. Defaults to `false`. */ executeImmediately?: boolean; + locations: ChatAgentLocation[]; } export interface IChatSlashFragment { content: string | { treeData: IChatResponseProgressFileTreeData }; } -export type IChatSlashCallback = { (prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> }; +export type IChatSlashCallback = { (prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> }; export const IChatSlashCommandService = createDecorator('chatSlashCommandService'); @@ -40,8 +41,8 @@ export interface IChatSlashCommandService { _serviceBrand: undefined; readonly onDidChangeCommands: Event; registerSlashCommand(data: IChatSlashData, command: IChatSlashCallback): IDisposable; - executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>; - getCommands(): Array; + executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void>; + getCommands(location: ChatAgentLocation): Array; hasCommand(id: string): boolean; } @@ -80,15 +81,15 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom }); } - getCommands(): Array { - return Array.from(this._commands.values(), v => v.data); + getCommands(location: ChatAgentLocation): Array { + return Array.from(this._commands.values(), v => v.data).filter(c => c.locations.includes(location)); } hasCommand(id: string): boolean { return this._commands.has(id); } - async executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> { + async executeCommand(id: string, prompt: string, progress: IProgress, history: IChatMessage[], location: ChatAgentLocation, token: CancellationToken): Promise<{ followUp: IChatFollowup[] } | void> { const data = this._commands.get(id); if (!data) { throw new Error('No command with id ${id} NOT registered'); @@ -100,6 +101,6 @@ export class ChatSlashCommandService extends Disposable implements IChatSlashCom throw new Error(`No command with id ${id} NOT resolved`); } - return await data.command(prompt, progress, history, token); + return await data.command(prompt, progress, history, location, token); } } From 7b299714f521ff25fc3db85dfce0072fe52ea6e2 Mon Sep 17 00:00:00 2001 From: Johannes Date: Fri, 28 Jun 2024 11:44:11 +0200 Subject: [PATCH 0170/2222] disable file references for inline chat for now --- src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index fad3466eefe..9209fb404c0 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -153,7 +153,7 @@ export class InlineChatWidget { renderStyle: 'minimal', renderInputOnTop: false, renderFollowups: true, - supportsFileReferences: true, + supportsFileReferences: false, filter: item => !isWelcomeVM(item), ...options.chatWidgetViewOptions }, From f6f90e016301f288ac5b36936d15ff8af9d70923 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Jun 2024 11:55:48 +0200 Subject: [PATCH 0171/2222] Implement NLS without AMD loader (#214588) --- .eslintrc.json | 4 - .vscode/settings.json | 2 - build/azure-pipelines/product-build.yml | 209 +++++------ build/azure-pipelines/upload-nlsmetadata.js | 30 +- build/azure-pipelines/upload-nlsmetadata.ts | 138 +++++--- .../azure-pipelines/web/product-build-web.yml | 2 +- build/gulpfile.editor.js | 2 +- build/gulpfile.reh.js | 6 + build/gulpfile.vscode.js | 11 +- build/lib/compilation.js | 10 +- build/lib/compilation.ts | 19 +- build/lib/i18n.js | 172 ++-------- build/lib/i18n.ts | 181 +++------- build/lib/nls.js | 126 ++++--- build/lib/nls.ts | 150 ++++---- build/lib/optimize.js | 1 + build/lib/optimize.ts | 1 + src/bootstrap-amd.js | 102 +++++- src/bootstrap-window.js | 32 +- src/bootstrap.js | 135 -------- src/cli.js | 34 +- src/main.js | 111 +++--- src/server-cli.js | 38 +- src/server-main.js | 26 +- src/tsconfig.monaco.json | 1 + src/typings/vscode-globals-nls.d.ts | 36 ++ src/vs/base/browser/defaultWorkerFactory.ts | 48 +-- src/vs/base/common/platform.ts | 45 +-- src/vs/base/node/languagePacks.d.ts | 25 -- src/vs/base/node/languagePacks.js | 264 -------------- src/vs/base/node/nls.d.ts | 38 ++ src/vs/base/node/nls.js | 288 ++++++++++++++++ .../base/parts/sandbox/common/sandboxTypes.ts | 17 + src/vs/code/browser/workbench/workbench.html | 25 +- src/vs/code/electron-main/app.ts | 41 +-- .../electron-sandbox/workbench/workbench.js | 5 +- src/vs/nls.ts | 324 +++++++----------- .../electron-main/environmentMainService.ts | 6 - .../issue/electron-main/issueMainService.ts | 7 +- .../issue/electron-main/processMainService.ts | 7 +- .../electron-main/windowsMainService.ts | 6 + src/vs/server/node/extensionHostConnection.ts | 2 +- .../server/node/extensionsScannerService.ts | 6 +- src/vs/server/node/remoteLanguagePacks.ts | 61 ++-- src/vs/server/node/webClientServer.ts | 19 +- .../browser/webWorkerExtensionHost.ts | 17 + .../worker/webWorkerExtensionHostIframe.html | 37 +- .../localization/browser/localeService.ts | 78 ++++- .../workingCopyBackupService.test.ts | 4 + .../workbench/workbench.desktop.main.nls.js | 8 - src/vs/workbench/workbench.web.main.ts | 1 - test/unit/browser/index.js | 12 + test/unit/electron/renderer.js | 11 + test/unit/node/index.js | 8 + 54 files changed, 1490 insertions(+), 1499 deletions(-) create mode 100644 src/typings/vscode-globals-nls.d.ts delete mode 100644 src/vs/base/node/languagePacks.d.ts delete mode 100644 src/vs/base/node/languagePacks.js create mode 100644 src/vs/base/node/nls.d.ts create mode 100644 src/vs/base/node/nls.js delete mode 100644 src/vs/workbench/workbench.desktop.main.nls.js diff --git a/.eslintrc.json b/.eslintrc.json index 1a9117cfaeb..5e0a792f492 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1015,10 +1015,6 @@ "vs/base/common/*" ] }, - { - "target": "src/vs/workbench/{workbench.desktop.main.nls.js,workbench.web.main.nls.js}", - "restrictions": [] - }, { "target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.ts,nls.build.ts,nls.mock.ts}", "restrictions": [] diff --git a/.vscode/settings.json b/.vscode/settings.json index ef93d2deda1..52dac9d7960 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -40,8 +40,6 @@ "**/Cargo.lock": true, "src/vs/workbench/workbench.web.main.css": true, "src/vs/workbench/workbench.desktop.main.css": true, - "src/vs/workbench/workbench.desktop.main.nls.js": true, - "src/vs/workbench/workbench.web.main.nls.js": true, "build/**/*.js": true, "out/**": true, "out-build/**": true, diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 5357697484e..97adeb0cd83 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -216,104 +216,105 @@ extends: parameters: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - - stage: CompileCLI - dependsOn: [] - jobs: - - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: - - job: CLILinuxX64 - pool: - name: 1es-ubuntu-20.04-x64 - os: linux - steps: - - template: build/azure-pipelines/linux/cli-build-linux.yml@self - parameters: - VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }} - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_LINUX: ${{ parameters.VSCODE_BUILD_LINUX }} - - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), or(eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true))) }}: - - job: CLILinuxGnuARM - pool: - name: 1es-ubuntu-20.04-x64 - os: linux - steps: - - template: build/azure-pipelines/linux/cli-build-linux.yml@self - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_LINUX_ARMHF: ${{ parameters.VSCODE_BUILD_LINUX_ARMHF }} - VSCODE_BUILD_LINUX_ARM64: ${{ parameters.VSCODE_BUILD_LINUX_ARM64 }} - - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE, true)) }}: - - job: CLIAlpineX64 - pool: - name: 1es-ubuntu-20.04-x64 - os: linux - steps: - - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_ALPINE: ${{ parameters.VSCODE_BUILD_ALPINE }} - - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }}: - - job: CLIAlpineARM64 - pool: - name: 1es-mariner-2.0-arm64 - os: linux - hostArchitecture: arm64 - container: ubuntu-2004-arm64 - steps: - - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_ALPINE_ARM64 }} - - - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: - - job: CLIMacOSX64 - pool: - name: Azure Pipelines - image: macOS-13 - os: macOS - steps: - - template: build/azure-pipelines/darwin/cli-build-darwin.yml@self - parameters: - VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }} - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_MACOS: ${{ parameters.VSCODE_BUILD_MACOS }} - - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}: - - job: CLIMacOSARM64 - pool: - name: Azure Pipelines - image: macOS-13 - os: macOS - steps: - - template: build/azure-pipelines/darwin/cli-build-darwin.yml@self - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_MACOS_ARM64: ${{ parameters.VSCODE_BUILD_MACOS_ARM64 }} - - - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: - - job: CLIWindowsX64 - pool: - name: 1es-windows-2019-x64 - os: windows - steps: - - template: build/azure-pipelines/win32/cli-build-win32.yml@self - parameters: - VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }} - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_WIN32: ${{ parameters.VSCODE_BUILD_WIN32 }} - - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: - - job: CLIWindowsARM64 - pool: - name: 1es-windows-2019-x64 - os: windows - steps: - - template: build/azure-pipelines/win32/cli-build-win32.yml@self - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }} + - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: + - stage: CompileCLI + dependsOn: [] + jobs: + - ${{ if eq(parameters.VSCODE_BUILD_LINUX, true) }}: + - job: CLILinuxX64 + pool: + name: 1es-ubuntu-20.04-x64 + os: linux + steps: + - template: build/azure-pipelines/linux/cli-build-linux.yml@self + parameters: + VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_LINUX: ${{ parameters.VSCODE_BUILD_LINUX }} + + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), or(eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true))) }}: + - job: CLILinuxGnuARM + pool: + name: 1es-ubuntu-20.04-x64 + os: linux + steps: + - template: build/azure-pipelines/linux/cli-build-linux.yml@self + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_LINUX_ARMHF: ${{ parameters.VSCODE_BUILD_LINUX_ARMHF }} + VSCODE_BUILD_LINUX_ARM64: ${{ parameters.VSCODE_BUILD_LINUX_ARM64 }} + + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE, true)) }}: + - job: CLIAlpineX64 + pool: + name: 1es-ubuntu-20.04-x64 + os: linux + steps: + - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_ALPINE: ${{ parameters.VSCODE_BUILD_ALPINE }} + + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }}: + - job: CLIAlpineARM64 + pool: + name: 1es-mariner-2.0-arm64 + os: linux + hostArchitecture: arm64 + container: ubuntu-2004-arm64 + steps: + - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_ALPINE_ARM64 }} + + - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: + - job: CLIMacOSX64 + pool: + name: Azure Pipelines + image: macOS-13 + os: macOS + steps: + - template: build/azure-pipelines/darwin/cli-build-darwin.yml@self + parameters: + VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_MACOS: ${{ parameters.VSCODE_BUILD_MACOS }} + + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}: + - job: CLIMacOSARM64 + pool: + name: Azure Pipelines + image: macOS-13 + os: macOS + steps: + - template: build/azure-pipelines/darwin/cli-build-darwin.yml@self + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_MACOS_ARM64: ${{ parameters.VSCODE_BUILD_MACOS_ARM64 }} + + - ${{ if eq(parameters.VSCODE_BUILD_WIN32, true) }}: + - job: CLIWindowsX64 + pool: + name: 1es-windows-2019-x64 + os: windows + steps: + - template: build/azure-pipelines/win32/cli-build-win32.yml@self + parameters: + VSCODE_CHECK_ONLY: ${{ variables.VSCODE_CIBUILD }} + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_WIN32: ${{ parameters.VSCODE_BUILD_WIN32 }} + + - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: + - job: CLIWindowsARM64 + pool: + name: 1es-windows-2019-x64 + os: windows + steps: + - template: build/azure-pipelines/win32/cli-build-win32.yml@self + parameters: + VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }} - stage: CustomSDL dependsOn: [] @@ -331,7 +332,8 @@ extends: - stage: Windows dependsOn: - Compile - - CompileCLI + - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: + - CompileCLI pool: name: 1es-windows-2019-x64 os: windows @@ -422,7 +424,8 @@ extends: - stage: Linux dependsOn: - Compile - - CompileCLI + - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: + - CompileCLI pool: name: 1es-ubuntu-20.04-x64 os: linux @@ -580,7 +583,8 @@ extends: - stage: Alpine dependsOn: - Compile - - CompileCLI + - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: + - CompileCLI pool: name: 1es-ubuntu-20.04-x64 os: linux @@ -606,7 +610,8 @@ extends: - stage: macOS dependsOn: - Compile - - CompileCLI + - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: + - CompileCLI pool: name: Azure Pipelines image: macOS-13 diff --git a/build/azure-pipelines/upload-nlsmetadata.js b/build/azure-pipelines/upload-nlsmetadata.js index 34c2005a30f..5b6cd3ed1fd 100644 --- a/build/azure-pipelines/upload-nlsmetadata.js +++ b/build/azure-pipelines/upload-nlsmetadata.js @@ -16,13 +16,33 @@ const commit = process.env['BUILD_SOURCEVERSION']; const credential = new identity_1.ClientSecretCredential(process.env['AZURE_TENANT_ID'], process.env['AZURE_CLIENT_ID'], process.env['AZURE_CLIENT_SECRET']); function main() { return new Promise((c, e) => { - es.merge(vfs.src('out-vscode-web-min/nls.metadata.json', { base: 'out-vscode-web-min' }), vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })) + const combinedMetadataJson = es.merge( + // vscode: we are not using `out-build/nls.metadata.json` here because + // it includes metadata for translators for `keys`. but for our purpose + // we want only the `keys` and `messages` as `string`. + es.merge(vfs.src('out-build/nls.keys.json', { base: 'out-build' }), vfs.src('out-build/nls.messages.json', { base: 'out-build' })) .pipe(merge({ + fileName: 'vscode.json', + jsonSpace: '', + concatArrays: true, + edit: (parsedJson, file) => { + if (file.base === 'out-build') { + if (file.basename === 'nls.keys.json') { + return { keys: parsedJson }; + } + else { + return { messages: parsedJson }; + } + } + } + })), + // extensions + vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })).pipe(merge({ fileName: 'combined.nls.metadata.json', jsonSpace: '', concatArrays: true, edit: (parsedJson, file) => { - if (file.base === 'out-vscode-web-min') { + if (file.basename === 'vscode.json') { return { vscode: parsedJson }; } // Handle extensions and follow the same structure as the Core nls file. @@ -72,13 +92,15 @@ function main() { const key = manifestJson.publisher + '.' + manifestJson.name; return { [key]: parsedJson }; }, - })) + })); + const nlsMessagesJs = vfs.src('out-build/nls.messages.js', { base: 'out-build' }); + es.merge(combinedMetadataJson, nlsMessagesJs) .pipe(gzip({ append: false })) .pipe(vfs.dest('./nlsMetadata')) .pipe(es.through(function (data) { console.log(`Uploading ${data.path}`); // trigger artifact upload - console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=combined.nls.metadata.json]${data.path}`); + console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=${data.basename}]${data.path}`); this.emit('data', data); })) .pipe(azure.upload({ diff --git a/build/azure-pipelines/upload-nlsmetadata.ts b/build/azure-pipelines/upload-nlsmetadata.ts index 416d0eec408..030cc8f0e5a 100644 --- a/build/azure-pipelines/upload-nlsmetadata.ts +++ b/build/azure-pipelines/upload-nlsmetadata.ts @@ -24,79 +24,103 @@ interface NlsMetadata { function main(): Promise { return new Promise((c, e) => { + const combinedMetadataJson = es.merge( + // vscode: we are not using `out-build/nls.metadata.json` here because + // it includes metadata for translators for `keys`. but for our purpose + // we want only the `keys` and `messages` as `string`. + es.merge( + vfs.src('out-build/nls.keys.json', { base: 'out-build' }), + vfs.src('out-build/nls.messages.json', { base: 'out-build' })) + .pipe(merge({ + fileName: 'vscode.json', + jsonSpace: '', + concatArrays: true, + edit: (parsedJson, file) => { + if (file.base === 'out-build') { + if (file.basename === 'nls.keys.json') { + return { keys: parsedJson }; + } else { + return { messages: parsedJson }; + } + } + } + })), - es.merge( - vfs.src('out-vscode-web-min/nls.metadata.json', { base: 'out-vscode-web-min' }), + // extensions vfs.src('.build/extensions/**/nls.metadata.json', { base: '.build/extensions' }), vfs.src('.build/extensions/**/nls.metadata.header.json', { base: '.build/extensions' }), - vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' })) - .pipe(merge({ - fileName: 'combined.nls.metadata.json', - jsonSpace: '', - concatArrays: true, - edit: (parsedJson, file) => { - if (file.base === 'out-vscode-web-min') { - return { vscode: parsedJson }; - } + vfs.src('.build/extensions/**/package.nls.json', { base: '.build/extensions' }) + ).pipe(merge({ + fileName: 'combined.nls.metadata.json', + jsonSpace: '', + concatArrays: true, + edit: (parsedJson, file) => { + if (file.basename === 'vscode.json') { + return { vscode: parsedJson }; + } - // Handle extensions and follow the same structure as the Core nls file. - switch (file.basename) { - case 'package.nls.json': - // put package.nls.json content in Core NlsMetadata format - // language packs use the key "package" to specify that - // translations are for the package.json file - parsedJson = { - messages: { - package: Object.values(parsedJson) - }, - keys: { - package: Object.keys(parsedJson) - }, - bundles: { - main: ['package'] - } - }; - break; + // Handle extensions and follow the same structure as the Core nls file. + switch (file.basename) { + case 'package.nls.json': + // put package.nls.json content in Core NlsMetadata format + // language packs use the key "package" to specify that + // translations are for the package.json file + parsedJson = { + messages: { + package: Object.values(parsedJson) + }, + keys: { + package: Object.keys(parsedJson) + }, + bundles: { + main: ['package'] + } + }; + break; - case 'nls.metadata.header.json': - parsedJson = { header: parsedJson }; - break; + case 'nls.metadata.header.json': + parsedJson = { header: parsedJson }; + break; - case 'nls.metadata.json': { - // put nls.metadata.json content in Core NlsMetadata format - const modules = Object.keys(parsedJson); + case 'nls.metadata.json': { + // put nls.metadata.json content in Core NlsMetadata format + const modules = Object.keys(parsedJson); - const json: NlsMetadata = { - keys: {}, - messages: {}, - bundles: { - main: [] - } - }; - for (const module of modules) { - json.messages[module] = parsedJson[module].messages; - json.keys[module] = parsedJson[module].keys; - json.bundles.main.push(module); + const json: NlsMetadata = { + keys: {}, + messages: {}, + bundles: { + main: [] } - parsedJson = json; - break; + }; + for (const module of modules) { + json.messages[module] = parsedJson[module].messages; + json.keys[module] = parsedJson[module].keys; + json.bundles.main.push(module); } + parsedJson = json; + break; } + } - // Get extension id and use that as the key - const folderPath = path.join(file.base, file.relative.split('/')[0]); - const manifest = readFileSync(path.join(folderPath, 'package.json'), 'utf-8'); - const manifestJson = JSON.parse(manifest); - const key = manifestJson.publisher + '.' + manifestJson.name; - return { [key]: parsedJson }; - }, - })) + // Get extension id and use that as the key + const folderPath = path.join(file.base, file.relative.split('/')[0]); + const manifest = readFileSync(path.join(folderPath, 'package.json'), 'utf-8'); + const manifestJson = JSON.parse(manifest); + const key = manifestJson.publisher + '.' + manifestJson.name; + return { [key]: parsedJson }; + }, + })); + + const nlsMessagesJs = vfs.src('out-build/nls.messages.js', { base: 'out-build' }); + + es.merge(combinedMetadataJson, nlsMessagesJs) .pipe(gzip({ append: false })) .pipe(vfs.dest('./nlsMetadata')) .pipe(es.through(function (data: Vinyl) { console.log(`Uploading ${data.path}`); // trigger artifact upload - console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=combined.nls.metadata.json]${data.path}`); + console.log(`##vso[artifact.upload containerfolder=nlsmetadata;artifactname=${data.basename}]${data.path}`); this.emit('data', data); })) .pipe(azure.upload({ diff --git a/build/azure-pipelines/web/product-build-web.yml b/build/azure-pipelines/web/product-build-web.yml index 5e423d077c7..a522e845f3b 100644 --- a/build/azure-pipelines/web/product-build-web.yml +++ b/build/azure-pipelines/web/product-build-web.yml @@ -152,7 +152,7 @@ steps: - script: | set -e - AZURE_STORAGE_ACCOUNT="ticino" \ + AZURE_STORAGE_ACCOUNT="vscodeweb" \ AZURE_TENANT_ID="$(AZURE_TENANT_ID)" \ AZURE_CLIENT_ID="$(AZURE_CLIENT_ID)" \ AZURE_CLIENT_SECRET="$(AZURE_CLIENT_SECRET)" \ diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 22b70a953df..a02456e7a2e 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -86,7 +86,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { }); // Disable mangling for the editor, as it complicates debugging & quite a few users rely on private/protected fields. -const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, { disableMangle: true })); +const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, { disableMangle: true, preserveEnglish: true })); const optimizeEditorAMDTask = task.define('optimize-editor-amd', optimize.optimizeTask( { diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index a6cfbe5a61d..ff2bf482a8d 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -58,6 +58,9 @@ const serverResources = [ 'out-build/bootstrap-amd.js', 'out-build/bootstrap-node.js', + // NLS + 'out-build/nls.messages.json', + // Performance 'out-build/vs/base/common/performance.js', @@ -82,6 +85,9 @@ const serverWithWebResources = [ // Include all of server... ...serverResources, + // NLS + 'out-build/nls.messages.js', + // ...and all of web ...vscodeWebResourceIncludes ]; diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index c6202c70883..b2967504fc5 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -56,6 +56,8 @@ const vscodeResources = [ 'out-build/bootstrap-amd.js', 'out-build/bootstrap-node.js', 'out-build/bootstrap-window.js', + 'out-build/nls.messages.json', + 'out-build/nls.keys.json', 'out-build/vs/**/*.{svg,png,html,jpg,mp3}', '!out-build/vs/code/browser/**/*.html', '!out-build/vs/code/**/*-dev.html', @@ -496,17 +498,12 @@ gulp.task(task.define( core, compileExtensionsBuildTask, function () { - const pathToMetadata = './out-vscode/nls.metadata.json'; - const pathToRehWebMetadata = './out-vscode-reh-web/nls.metadata.json'; + const pathToMetadata = './out-build/nls.metadata.json'; const pathToExtensions = '.build/extensions/*'; const pathToSetup = 'build/win32/i18n/messages.en.isl'; return es.merge( - gulp.src([pathToMetadata, pathToRehWebMetadata]).pipe(merge({ - fileName: 'nls.metadata.json', - jsonSpace: '', - concatArrays: true - })).pipe(i18n.createXlfFilesForCoreBundle()), + gulp.src(pathToMetadata).pipe(i18n.createXlfFilesForCoreBundle()), gulp.src(pathToSetup).pipe(i18n.createXlfFilesForIsl()), gulp.src(pathToExtensions).pipe(i18n.createXlfFilesForExtensions()) ).pipe(vfs.dest('../vscode-translations-export')); diff --git a/build/lib/compilation.js b/build/lib/compilation.js index 2f408d562ce..cafca34a0d8 100644 --- a/build/lib/compilation.js +++ b/build/lib/compilation.js @@ -41,7 +41,7 @@ function getTypeScriptCompilerOptions(src) { options.newLine = /\r\n/.test(fs.readFileSync(__filename, 'utf8')) ? 0 : 1; return options; } -function createCompile(src, build, emitError, transpileOnly) { +function createCompile(src, { build, emitError, transpileOnly, preserveEnglish }) { const tsb = require('./tsb'); const sourcemaps = require('gulp-sourcemaps'); const projectPath = path.join(__dirname, '../../', src, 'tsconfig.json'); @@ -71,7 +71,7 @@ function createCompile(src, build, emitError, transpileOnly) { .pipe(util.loadSourcemaps()) .pipe(compilation(token)) .pipe(noDeclarationsFilter) - .pipe(util.$if(build, nls.nls())) + .pipe(util.$if(build, nls.nls({ preserveEnglish }))) .pipe(noDeclarationsFilter.restore) .pipe(util.$if(!transpileOnly, sourcemaps.write('.', { addComment: false, @@ -90,7 +90,7 @@ function createCompile(src, build, emitError, transpileOnly) { } function transpileTask(src, out, swc) { const task = () => { - const transpile = createCompile(src, false, true, { swc }); + const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { swc }, preserveEnglish: false }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); return srcPipe .pipe(transpile()) @@ -104,7 +104,7 @@ function compileTask(src, out, build, options = {}) { if (os.totalmem() < 4_000_000_000) { throw new Error('compilation requires 4GB of RAM'); } - const compile = createCompile(src, build, true, false); + const compile = createCompile(src, { build, emitError: true, transpileOnly: false, preserveEnglish: !!options.preserveEnglish }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); const generator = new MonacoGenerator(false); if (src === 'src') { @@ -141,7 +141,7 @@ function compileTask(src, out, build, options = {}) { } function watchTask(out, build) { const task = () => { - const compile = createCompile('src', build, false, false); + const compile = createCompile('src', { build, emitError: false, transpileOnly: false, preserveEnglish: false }); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); const generator = new MonacoGenerator(true); diff --git a/build/lib/compilation.ts b/build/lib/compilation.ts index 3b6fa2df339..8c3614b4c13 100644 --- a/build/lib/compilation.ts +++ b/build/lib/compilation.ts @@ -42,7 +42,14 @@ function getTypeScriptCompilerOptions(src: string): ts.CompilerOptions { return options; } -function createCompile(src: string, build: boolean, emitError: boolean, transpileOnly: boolean | { swc: boolean }) { +interface ICompileTaskOptions { + readonly build: boolean; + readonly emitError: boolean; + readonly transpileOnly: boolean | { swc: boolean }; + readonly preserveEnglish: boolean; +} + +function createCompile(src: string, { build, emitError, transpileOnly, preserveEnglish }: ICompileTaskOptions) { const tsb = require('./tsb') as typeof import('./tsb'); const sourcemaps = require('gulp-sourcemaps') as typeof import('gulp-sourcemaps'); @@ -79,7 +86,7 @@ function createCompile(src: string, build: boolean, emitError: boolean, transpil .pipe(util.loadSourcemaps()) .pipe(compilation(token)) .pipe(noDeclarationsFilter) - .pipe(util.$if(build, nls.nls())) + .pipe(util.$if(build, nls.nls({ preserveEnglish }))) .pipe(noDeclarationsFilter.restore) .pipe(util.$if(!transpileOnly, sourcemaps.write('.', { addComment: false, @@ -102,7 +109,7 @@ export function transpileTask(src: string, out: string, swc: boolean): task.Stre const task = () => { - const transpile = createCompile(src, false, true, { swc }); + const transpile = createCompile(src, { build: false, emitError: true, transpileOnly: { swc }, preserveEnglish: false }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); return srcPipe @@ -114,7 +121,7 @@ export function transpileTask(src: string, out: string, swc: boolean): task.Stre return task; } -export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean } = {}): task.StreamTask { +export function compileTask(src: string, out: string, build: boolean, options: { disableMangle?: boolean; preserveEnglish?: boolean } = {}): task.StreamTask { const task = () => { @@ -122,7 +129,7 @@ export function compileTask(src: string, out: string, build: boolean, options: { throw new Error('compilation requires 4GB of RAM'); } - const compile = createCompile(src, build, true, false); + const compile = createCompile(src, { build, emitError: true, transpileOnly: false, preserveEnglish: !!options.preserveEnglish }); const srcPipe = gulp.src(`${src}/**`, { base: `${src}` }); const generator = new MonacoGenerator(false); if (src === 'src') { @@ -166,7 +173,7 @@ export function compileTask(src: string, out: string, build: boolean, options: { export function watchTask(out: string, build: boolean): task.StreamTask { const task = () => { - const compile = createCompile('src', build, false, false); + const compile = createCompile('src', { build, emitError: false, transpileOnly: false, preserveEnglish: false }); const src = gulp.src('src/**', { base: 'src' }); const watchSrc = watch('src/**', { base: 'src', readDelay: 200 }); diff --git a/build/lib/i18n.js b/build/lib/i18n.js index c33994987f0..a837cbc4ac0 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -23,6 +23,7 @@ const fancyLog = require("fancy-log"); const ansiColors = require("ansi-colors"); const iconv = require("@vscode/iconv-lite-umd"); const l10n_dev_1 = require("@vscode/l10n-dev"); +const REPO_ROOT_PATH = path.join(__dirname, '../..'); function log(message, ...rest) { fancyLog(ansiColors.green('[i18n]'), message, ...rest); } @@ -63,6 +64,17 @@ var BundledFormat; } BundledFormat.is = is; })(BundledFormat || (BundledFormat = {})); +var NLSKeysFormat; +(function (NLSKeysFormat) { + function is(value) { + if (value === undefined) { + return false; + } + const candidate = value; + return Array.isArray(candidate) && Array.isArray(candidate[1]); + } + NLSKeysFormat.is = is; +})(NLSKeysFormat || (NLSKeysFormat = {})); class Line { buffer = []; constructor(indent = 0) { @@ -265,67 +277,8 @@ function stripComments(content) { }); return result; } -function escapeCharacters(value) { - const result = []; - for (let i = 0; i < value.length; i++) { - const ch = value.charAt(i); - switch (ch) { - case '\'': - result.push('\\\''); - break; - case '"': - result.push('\\"'); - break; - case '\\': - result.push('\\\\'); - break; - case '\n': - result.push('\\n'); - break; - case '\r': - result.push('\\r'); - break; - case '\t': - result.push('\\t'); - break; - case '\b': - result.push('\\b'); - break; - case '\f': - result.push('\\f'); - break; - default: - result.push(ch); - } - } - return result.join(''); -} -function processCoreBundleFormat(fileHeader, languages, json, emitter) { - const keysSection = json.keys; - const messageSection = json.messages; - const bundleSection = json.bundles; - const statistics = Object.create(null); - const defaultMessages = Object.create(null); - const modules = Object.keys(keysSection); - modules.forEach((module) => { - const keys = keysSection[module]; - const messages = messageSection[module]; - if (!messages || keys.length !== messages.length) { - emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`); - return; - } - const messageMap = Object.create(null); - defaultMessages[module] = messageMap; - keys.map((key, i) => { - if (typeof key === 'string') { - messageMap[key] = messages[i]; - } - else { - messageMap[key.key] = messages[i]; - } - }); - }); - const languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); +function processCoreBundleFormat(base, fileHeader, languages, json, emitter) { + const languageDirectory = path.join(REPO_ROOT_PATH, '..', 'vscode-loc', 'i18n'); if (!fs.existsSync(languageDirectory)) { log(`No VS Code localization repository found. Looking at ${languageDirectory}`); log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`); @@ -335,8 +288,6 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { if (process.env['VSCODE_BUILD_VERBOSE']) { log(`Generating nls bundles for: ${language.id}`); } - statistics[language.id] = 0; - const localizedModules = Object.create(null); const languageFolderName = language.translationId || language.id; const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); let allMessages; @@ -344,87 +295,36 @@ function processCoreBundleFormat(fileHeader, languages, json, emitter) { const content = stripComments(fs.readFileSync(i18nFile, 'utf8')); allMessages = JSON.parse(content); } - modules.forEach((module) => { - const order = keysSection[module]; - let moduleMessage; - if (allMessages) { - moduleMessage = allMessages.contents[module]; + let nlsIndex = 0; + const nlsResult = []; + for (const [moduleId, nlsKeys] of json) { + const moduleTranslations = allMessages?.contents[moduleId]; + for (const nlsKey of nlsKeys) { + nlsResult.push(moduleTranslations?.[nlsKey]); // pushing `undefined` is fine, as we keep english strings as fallback for monaco editor in the build + nlsIndex++; } - if (!moduleMessage) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log(`No localized messages found for module ${module}. Using default messages.`); - } - moduleMessage = defaultMessages[module]; - statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length; - } - const localizedMessages = []; - order.forEach((keyInfo) => { - let key = null; - if (typeof keyInfo === 'string') { - key = keyInfo; - } - else { - key = keyInfo.key; - } - let message = moduleMessage[key]; - if (!message) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log(`No localized message found for key ${key} in module ${module}. Using default message.`); - } - message = defaultMessages[module][key]; - statistics[language.id] = statistics[language.id] + 1; - } - localizedMessages.push(message); - }); - localizedModules[module] = localizedMessages; - }); - Object.keys(bundleSection).forEach((bundle) => { - const modules = bundleSection[bundle]; - const contents = [ - fileHeader, - `define("${bundle}.nls.${language.id}", {` - ]; - modules.forEach((module, index) => { - contents.push(`\t"${module}": [`); - const messages = localizedModules[module]; - if (!messages) { - emitter.emit('error', `Didn't find messages for module ${module}.`); - return; - } - messages.forEach((message, index) => { - contents.push(`\t\t"${escapeCharacters(message)}${index < messages.length ? '",' : '"'}`); - }); - contents.push(index < modules.length - 1 ? '\t],' : '\t]'); - }); - contents.push('});'); - emitter.queue(new File({ path: bundle + '.nls.' + language.id + '.js', contents: Buffer.from(contents.join('\n'), 'utf-8') })); - }); - }); - Object.keys(statistics).forEach(key => { - const value = statistics[key]; - log(`${key} has ${value} untranslated strings.`); - }); - sortedLanguages.forEach(language => { - const stats = statistics[language.id]; - if (!stats) { - log(`\tNo translations found for language ${language.id}. Using default language instead.`); } + emitter.queue(new File({ + contents: Buffer.from(`${fileHeader} +globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(nlsResult)}; +globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`), + base, + path: `${base}/nls.messages.${language.id}.js` + })); }); } function processNlsFiles(opts) { return (0, event_stream_1.through)(function (file) { const fileName = path.basename(file.path); - if (fileName === 'nls.metadata.json') { - let json = null; - if (file.isBuffer()) { - json = JSON.parse(file.contents.toString('utf8')); - } - else { - this.emit('error', `Failed to read component file: ${file.relative}`); - return; + if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles + try { + const json = JSON.parse(fs.readFileSync(path.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString()); + if (NLSKeysFormat.is(json)) { + processCoreBundleFormat(file.base, opts.fileHeader, opts.languages, json, this); + } } - if (BundledFormat.is(json)) { - processCoreBundleFormat(opts.fileHeader, opts.languages, json, this); + catch (error) { + this.emit('error', `Failed to read component file: ${error}`); } } this.queue(file); diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index 444e3abe59c..28f8cc993e6 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -16,6 +16,8 @@ import * as ansiColors from 'ansi-colors'; import * as iconv from '@vscode/iconv-lite-umd'; import { l10nJsonFormat, getL10nXlf, l10nJsonDetails, getL10nFilesFromXlf, getL10nJson } from '@vscode/l10n-dev'; +const REPO_ROOT_PATH = path.join(__dirname, '../..'); + function log(message: any, ...rest: any[]): void { fancyLog(ansiColors.green('[i18n]'), message, ...rest); } @@ -91,6 +93,19 @@ module BundledFormat { } } +type NLSKeysFormat = [string /* module ID */, string[] /* keys */]; + +module NLSKeysFormat { + export function is(value: any): value is NLSKeysFormat { + if (value === undefined) { + return false; + } + + const candidate = value as NLSKeysFormat; + return Array.isArray(candidate) && Array.isArray(candidate[1]); + } +} + interface BundledExtensionFormat { [key: string]: { messages: string[]; @@ -329,70 +344,8 @@ function stripComments(content: string): string { return result; } -function escapeCharacters(value: string): string { - const result: string[] = []; - for (let i = 0; i < value.length; i++) { - const ch = value.charAt(i); - switch (ch) { - case '\'': - result.push('\\\''); - break; - case '"': - result.push('\\"'); - break; - case '\\': - result.push('\\\\'); - break; - case '\n': - result.push('\\n'); - break; - case '\r': - result.push('\\r'); - break; - case '\t': - result.push('\\t'); - break; - case '\b': - result.push('\\b'); - break; - case '\f': - result.push('\\f'); - break; - default: - result.push(ch); - } - } - return result.join(''); -} - -function processCoreBundleFormat(fileHeader: string, languages: Language[], json: BundledFormat, emitter: ThroughStream) { - const keysSection = json.keys; - const messageSection = json.messages; - const bundleSection = json.bundles; - - const statistics: Record = Object.create(null); - - const defaultMessages: Record> = Object.create(null); - const modules = Object.keys(keysSection); - modules.forEach((module) => { - const keys = keysSection[module]; - const messages = messageSection[module]; - if (!messages || keys.length !== messages.length) { - emitter.emit('error', `Message for module ${module} corrupted. Mismatch in number of keys and messages.`); - return; - } - const messageMap: Record = Object.create(null); - defaultMessages[module] = messageMap; - keys.map((key, i) => { - if (typeof key === 'string') { - messageMap[key] = messages[i]; - } else { - messageMap[key.key] = messages[i]; - } - }); - }); - - const languageDirectory = path.join(__dirname, '..', '..', '..', 'vscode-loc', 'i18n'); +function processCoreBundleFormat(base: string, fileHeader: string, languages: Language[], json: NLSKeysFormat, emitter: ThroughStream) { + const languageDirectory = path.join(REPO_ROOT_PATH, '..', 'vscode-loc', 'i18n'); if (!fs.existsSync(languageDirectory)) { log(`No VS Code localization repository found. Looking at ${languageDirectory}`); log(`To bundle translations please check out the vscode-loc repository as a sibling of the vscode repository.`); @@ -403,8 +356,6 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json log(`Generating nls bundles for: ${language.id}`); } - statistics[language.id] = 0; - const localizedModules: Record = Object.create(null); const languageFolderName = language.translationId || language.id; const i18nFile = path.join(languageDirectory, `vscode-language-pack-${languageFolderName}`, 'translations', 'main.i18n.json'); let allMessages: I18nFormat | undefined; @@ -412,86 +363,38 @@ function processCoreBundleFormat(fileHeader: string, languages: Language[], json const content = stripComments(fs.readFileSync(i18nFile, 'utf8')); allMessages = JSON.parse(content); } - modules.forEach((module) => { - const order = keysSection[module]; - let moduleMessage: { [messageKey: string]: string } | undefined; - if (allMessages) { - moduleMessage = allMessages.contents[module]; - } - if (!moduleMessage) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log(`No localized messages found for module ${module}. Using default messages.`); - } - moduleMessage = defaultMessages[module]; - statistics[language.id] = statistics[language.id] + Object.keys(moduleMessage).length; + + let nlsIndex = 0; + const nlsResult: Array = []; + for (const [moduleId, nlsKeys] of json) { + const moduleTranslations = allMessages?.contents[moduleId]; + for (const nlsKey of nlsKeys) { + nlsResult.push(moduleTranslations?.[nlsKey]); // pushing `undefined` is fine, as we keep english strings as fallback for monaco editor in the build + nlsIndex++; } - const localizedMessages: string[] = []; - order.forEach((keyInfo) => { - let key: string | null = null; - if (typeof keyInfo === 'string') { - key = keyInfo; - } else { - key = keyInfo.key; - } - let message: string = moduleMessage![key]; - if (!message) { - if (process.env['VSCODE_BUILD_VERBOSE']) { - log(`No localized message found for key ${key} in module ${module}. Using default message.`); - } - message = defaultMessages[module][key]; - statistics[language.id] = statistics[language.id] + 1; - } - localizedMessages.push(message); - }); - localizedModules[module] = localizedMessages; - }); - Object.keys(bundleSection).forEach((bundle) => { - const modules = bundleSection[bundle]; - const contents: string[] = [ - fileHeader, - `define("${bundle}.nls.${language.id}", {` - ]; - modules.forEach((module, index) => { - contents.push(`\t"${module}": [`); - const messages = localizedModules[module]; - if (!messages) { - emitter.emit('error', `Didn't find messages for module ${module}.`); - return; - } - messages.forEach((message, index) => { - contents.push(`\t\t"${escapeCharacters(message)}${index < messages.length ? '",' : '"'}`); - }); - contents.push(index < modules.length - 1 ? '\t],' : '\t]'); - }); - contents.push('});'); - emitter.queue(new File({ path: bundle + '.nls.' + language.id + '.js', contents: Buffer.from(contents.join('\n'), 'utf-8') })); - }); - }); - Object.keys(statistics).forEach(key => { - const value = statistics[key]; - log(`${key} has ${value} untranslated strings.`); - }); - sortedLanguages.forEach(language => { - const stats = statistics[language.id]; - if (!stats) { - log(`\tNo translations found for language ${language.id}. Using default language instead.`); } + + emitter.queue(new File({ + contents: Buffer.from(`${fileHeader} +globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(nlsResult)}; +globalThis._VSCODE_NLS_LANGUAGE=${JSON.stringify(language.id)};`), + base, + path: `${base}/nls.messages.${language.id}.js` + })); }); } -export function processNlsFiles(opts: { fileHeader: string; languages: Language[] }): ThroughStream { +export function processNlsFiles(opts: { out: string; fileHeader: string; languages: Language[] }): ThroughStream { return through(function (this: ThroughStream, file: File) { const fileName = path.basename(file.path); - if (fileName === 'nls.metadata.json') { - let json = null; - if (file.isBuffer()) { - json = JSON.parse((file.contents).toString('utf8')); - } else { - this.emit('error', `Failed to read component file: ${file.relative}`); - return; - } - if (BundledFormat.is(json)) { - processCoreBundleFormat(opts.fileHeader, opts.languages, json, this); + if (fileName === 'bundleInfo.json') { // pick a root level file to put the core bundles + try { + const json = JSON.parse(fs.readFileSync(path.join(REPO_ROOT_PATH, opts.out, 'nls.keys.json')).toString()); + if (NLSKeysFormat.is(json)) { + processCoreBundleFormat(file.base, opts.fileHeader, opts.languages, json, this); + } + } catch (error) { + this.emit('error', `Failed to read component file: ${error}`); } } this.queue(file); diff --git a/build/lib/nls.js b/build/lib/nls.js index 48ca84f2433..ae235a5a534 100644 --- a/build/lib/nls.js +++ b/build/lib/nls.js @@ -38,21 +38,11 @@ function clone(object) { } return result; } -function template(lines) { - let indent = '', wrap = ''; - if (lines.length > 1) { - indent = '\t'; - wrap = '\n'; - } - return `/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ -define([], [${wrap + lines.map(l => indent + l).join(',\n') + wrap}]);`; -} /** * Returns a stream containing the patched JavaScript and source maps. */ -function nls() { +function nls(options) { + let base; const input = (0, event_stream_1.through)(); const output = input.pipe((0, event_stream_1.through)(function (f) { if (!f.sourceMap) { @@ -70,7 +60,40 @@ function nls() { if (!typescript) { return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`)); } - _nls.patchFiles(f, typescript).forEach(f => this.emit('data', f)); + base = f.base; + this.emit('data', _nls.patchFile(f, typescript, options)); + }, function () { + for (const file of [ + new File({ + contents: Buffer.from(JSON.stringify({ + keys: _nls.moduleToNLSKeys, + messages: _nls.moduleToNLSMessages, + }, null, '\t')), + base, + path: `${base}/nls.metadata.json` + }), + new File({ + contents: Buffer.from(JSON.stringify(_nls.allNLSMessages)), + base, + path: `${base}/nls.messages.json` + }), + new File({ + contents: Buffer.from(JSON.stringify(_nls.allNLSModulesAndKeys)), + base, + path: `${base}/nls.keys.json` + }), + new File({ + contents: Buffer.from(`/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ +globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(_nls.allNLSMessages)};`), + base, + path: `${base}/nls.messages.js` + }) + ]) { + this.emit('data', file); + } + this.emit('end'); })); return (0, event_stream_1.duplex)(input, output); } @@ -79,6 +102,11 @@ function isImportNode(ts, node) { } var _nls; (function (_nls) { + _nls.moduleToNLSKeys = {}; + _nls.moduleToNLSMessages = {}; + _nls.allNLSMessages = []; + _nls.allNLSModulesAndKeys = []; + let allNLSMessagesIndex = 0; function fileFrom(file, contents, path = file.path) { return new File({ contents: Buffer.from(contents), @@ -146,13 +174,6 @@ var _nls; .filter(d => d.moduleSpecifier.kind === ts.SyntaxKind.StringLiteral) .filter(d => d.moduleSpecifier.getText() === '\'vs/nls\'') .filter(d => !!d.importClause && !!d.importClause.namedBindings); - const nlsExpressions = importEqualsDeclarations - .map(d => d.moduleReference.expression) - .concat(importDeclarations.map(d => d.moduleSpecifier)) - .map(d => ({ - start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()), - end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd()) - })); // `nls.localize(...)` calls const nlsLocalizeCallExpressions = importDeclarations .filter(d => !!(d.importClause && d.importClause.namedBindings && d.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport)) @@ -206,8 +227,7 @@ var _nls; value: a[1].getText() })); return { - localizeCalls: localizeCalls.toArray(), - nlsExpressions: nlsExpressions.toArray() + localizeCalls: localizeCalls.toArray() }; } class TextModel { @@ -262,14 +282,10 @@ var _nls; .flatten().toArray().join(''); } } - function patchJavascript(patches, contents, moduleId) { + function patchJavascript(patches, contents) { const model = new TextModel(contents); // patch the localize calls lazy(patches).reverse().each(p => model.apply(p)); - // patch the 'vs/nls' imports - const firstLine = model.get(0); - const patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`); - model.set(0, patchedFirstLine); return model.toString(); } function patchSourcemap(patches, rsm, smc) { @@ -307,14 +323,21 @@ var _nls; } return JSON.parse(smg.toString()); } - function patch(ts, moduleId, typescript, javascript, sourcemap) { - const { localizeCalls, nlsExpressions } = analyze(ts, typescript, 'localize'); - const { localizeCalls: localize2Calls, nlsExpressions: nls2Expressions } = analyze(ts, typescript, 'localize2'); + function parseLocalizeKeyOrValue(sourceExpression) { + // sourceValue can be "foo", 'foo', `foo` or { .... } + // in its evalulated form + // we want to return either the string or the object + // eslint-disable-next-line no-eval + return eval(`(${sourceExpression})`); + } + function patch(ts, typescript, javascript, sourcemap, options) { + const { localizeCalls } = analyze(ts, typescript, 'localize'); + const { localizeCalls: localize2Calls } = analyze(ts, typescript, 'localize2'); if (localizeCalls.length === 0 && localize2Calls.length === 0) { return { javascript, sourcemap }; } - const nlsKeys = template(localizeCalls.map(lc => lc.key).concat(localize2Calls.map(lc => lc.key))); - const nls = template(localizeCalls.map(lc => lc.value).concat(localize2Calls.map(lc => lc.value))); + const nlsKeys = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.key)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.key))); + const nlsMessages = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.value)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.value))); const smc = new sm.SourceMapConsumer(sourcemap); const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]); // build patches @@ -323,16 +346,18 @@ var _nls; const end = lcFrom(smc.generatedPositionFor(positionFrom(c.range.end))); return { span: { start, end }, content: c.content }; }; - let i = 0; const localizePatches = lazy(localizeCalls) - .map(lc => ([ - { range: lc.keySpan, content: '' + (i++) }, + .map(lc => (options.preserveEnglish ? [ + { range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize('key', "message") => localize(, "message") + ] : [ + { range: lc.keySpan, content: `${allNLSMessagesIndex++}` }, // localize('key', "message") => localize(, null) { range: lc.valueSpan, content: 'null' } ])) .flatten() .map(toPatch); const localize2Patches = lazy(localize2Calls) - .map(lc => ({ range: lc.keySpan, content: '' + (i++) })) + .map(lc => ({ range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize2('key', "message") => localize(, "message") + )) .map(toPatch); // Sort patches by their start position const patches = localizePatches.concat(localize2Patches).toArray().sort((a, b) => { @@ -352,34 +377,29 @@ var _nls; return 0; } }); - javascript = patchJavascript(patches, javascript, moduleId); - // since imports are not within the sourcemap information, - // we must do this MacGyver style - if (nlsExpressions.length || nls2Expressions.length) { - javascript = javascript.replace(/^define\(.*$/m, line => { - return line.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`); - }); - } + javascript = patchJavascript(patches, javascript); sourcemap = patchSourcemap(patches, sourcemap, smc); - return { javascript, sourcemap, nlsKeys, nls }; + return { javascript, sourcemap, nlsKeys, nlsMessages }; } - function patchFiles(javascriptFile, typescript) { + function patchFile(javascriptFile, typescript, options) { const ts = require('typescript'); // hack? const moduleId = javascriptFile.relative .replace(/\.js$/, '') .replace(/\\/g, '/'); - const { javascript, sourcemap, nlsKeys, nls } = patch(ts, moduleId, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap); - const result = [fileFrom(javascriptFile, javascript)]; - result[0].sourceMap = sourcemap; + const { javascript, sourcemap, nlsKeys, nlsMessages } = patch(ts, typescript, javascriptFile.contents.toString(), javascriptFile.sourceMap, options); + const result = fileFrom(javascriptFile, javascript); + result.sourceMap = sourcemap; if (nlsKeys) { - result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js'))); + _nls.moduleToNLSKeys[moduleId] = nlsKeys; + _nls.allNLSModulesAndKeys.push([moduleId, nlsKeys.map(nlsKey => typeof nlsKey === 'string' ? nlsKey : nlsKey.key)]); } - if (nls) { - result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js'))); + if (nlsMessages) { + _nls.moduleToNLSMessages[moduleId] = nlsMessages; + _nls.allNLSMessages.push(...nlsMessages); } return result; } - _nls.patchFiles = patchFiles; + _nls.patchFile = patchFile; })(_nls || (_nls = {})); //# sourceMappingURL=nls.js.map \ No newline at end of file diff --git a/build/lib/nls.ts b/build/lib/nls.ts index c4ee031b2eb..066dc1440c2 100644 --- a/build/lib/nls.ts +++ b/build/lib/nls.ts @@ -48,24 +48,11 @@ function clone(object: T): T { return result; } -function template(lines: string[]): string { - let indent = '', wrap = ''; - - if (lines.length > 1) { - indent = '\t'; - wrap = '\n'; - } - - return `/*--------------------------------------------------------- - * Copyright (C) Microsoft Corporation. All rights reserved. - *--------------------------------------------------------*/ -define([], [${wrap + lines.map(l => indent + l).join(',\n') + wrap}]);`; -} - /** * Returns a stream containing the patched JavaScript and source maps. */ -export function nls(): NodeJS.ReadWriteStream { +export function nls(options: { preserveEnglish: boolean }): NodeJS.ReadWriteStream { + let base: string; const input = through(); const output = input.pipe(through(function (f: FileSourceMap) { if (!f.sourceMap) { @@ -87,7 +74,41 @@ export function nls(): NodeJS.ReadWriteStream { return this.emit('error', new Error(`File ${f.relative} does not have the original content in the source map.`)); } - _nls.patchFiles(f, typescript).forEach(f => this.emit('data', f)); + base = f.base; + this.emit('data', _nls.patchFile(f, typescript, options)); + }, function () { + for (const file of [ + new File({ + contents: Buffer.from(JSON.stringify({ + keys: _nls.moduleToNLSKeys, + messages: _nls.moduleToNLSMessages, + }, null, '\t')), + base, + path: `${base}/nls.metadata.json` + }), + new File({ + contents: Buffer.from(JSON.stringify(_nls.allNLSMessages)), + base, + path: `${base}/nls.messages.json` + }), + new File({ + contents: Buffer.from(JSON.stringify(_nls.allNLSModulesAndKeys)), + base, + path: `${base}/nls.keys.json` + }), + new File({ + contents: Buffer.from(`/*--------------------------------------------------------- + * Copyright (C) Microsoft Corporation. All rights reserved. + *--------------------------------------------------------*/ +globalThis._VSCODE_NLS_MESSAGES=${JSON.stringify(_nls.allNLSMessages)};`), + base, + path: `${base}/nls.messages.js` + }) + ]) { + this.emit('data', file); + } + + this.emit('end'); })); return duplex(input, output); @@ -99,11 +120,19 @@ function isImportNode(ts: typeof import('typescript'), node: ts.Node): boolean { module _nls { - interface INlsStringResult { + export const moduleToNLSKeys: { [name: string /* module ID */]: ILocalizeKey[] /* keys */ } = {}; + export const moduleToNLSMessages: { [name: string /* module ID */]: string[] /* messages */ } = {}; + export const allNLSMessages: string[] = []; + export const allNLSModulesAndKeys: Array<[string /* module ID */, string[] /* keys */]> = []; + let allNLSMessagesIndex = 0; + + type ILocalizeKey = string | { key: string }; // key might contain metadata for translators and then is not just a string + + interface INlsPatchResult { javascript: string; sourcemap: sm.RawSourceMap; - nls?: string; - nlsKeys?: string; + nlsMessages?: string[]; + nlsKeys?: ILocalizeKey[]; } interface ISpan { @@ -120,7 +149,6 @@ module _nls { interface ILocalizeAnalysisResult { localizeCalls: ILocalizeCall[]; - nlsExpressions: ISpan[]; } interface IPatch { @@ -210,14 +238,6 @@ module _nls { .filter(d => d.moduleSpecifier.getText() === '\'vs/nls\'') .filter(d => !!d.importClause && !!d.importClause.namedBindings); - const nlsExpressions = importEqualsDeclarations - .map(d => (d.moduleReference).expression) - .concat(importDeclarations.map(d => d.moduleSpecifier)) - .map(d => ({ - start: ts.getLineAndCharacterOfPosition(sourceFile, d.getStart()), - end: ts.getLineAndCharacterOfPosition(sourceFile, d.getEnd()) - })); - // `nls.localize(...)` calls const nlsLocalizeCallExpressions = importDeclarations .filter(d => !!(d.importClause && d.importClause.namedBindings && d.importClause.namedBindings.kind === ts.SyntaxKind.NamespaceImport)) @@ -280,8 +300,7 @@ module _nls { })); return { - localizeCalls: localizeCalls.toArray(), - nlsExpressions: nlsExpressions.toArray() + localizeCalls: localizeCalls.toArray() }; } @@ -351,17 +370,12 @@ module _nls { } } - function patchJavascript(patches: IPatch[], contents: string, moduleId: string): string { + function patchJavascript(patches: IPatch[], contents: string): string { const model = new TextModel(contents); // patch the localize calls lazy(patches).reverse().each(p => model.apply(p)); - // patch the 'vs/nls' imports - const firstLine = model.get(0); - const patchedFirstLine = firstLine.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`); - model.set(0, patchedFirstLine); - return model.toString(); } @@ -410,16 +424,24 @@ module _nls { return JSON.parse(smg.toString()); } - function patch(ts: typeof import('typescript'), moduleId: string, typescript: string, javascript: string, sourcemap: sm.RawSourceMap): INlsStringResult { - const { localizeCalls, nlsExpressions } = analyze(ts, typescript, 'localize'); - const { localizeCalls: localize2Calls, nlsExpressions: nls2Expressions } = analyze(ts, typescript, 'localize2'); + function parseLocalizeKeyOrValue(sourceExpression: string) { + // sourceValue can be "foo", 'foo', `foo` or { .... } + // in its evalulated form + // we want to return either the string or the object + // eslint-disable-next-line no-eval + return eval(`(${sourceExpression})`); + } + + function patch(ts: typeof import('typescript'), typescript: string, javascript: string, sourcemap: sm.RawSourceMap, options: { preserveEnglish: boolean }): INlsPatchResult { + const { localizeCalls } = analyze(ts, typescript, 'localize'); + const { localizeCalls: localize2Calls } = analyze(ts, typescript, 'localize2'); if (localizeCalls.length === 0 && localize2Calls.length === 0) { return { javascript, sourcemap }; } - const nlsKeys = template(localizeCalls.map(lc => lc.key).concat(localize2Calls.map(lc => lc.key))); - const nls = template(localizeCalls.map(lc => lc.value).concat(localize2Calls.map(lc => lc.value))); + const nlsKeys = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.key)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.key))); + const nlsMessages = localizeCalls.map(lc => parseLocalizeKeyOrValue(lc.value)).concat(localize2Calls.map(lc => parseLocalizeKeyOrValue(lc.value))); const smc = new sm.SourceMapConsumer(sourcemap); const positionFrom = mappedPositionFrom.bind(null, sourcemap.sources[0]); @@ -430,18 +452,20 @@ module _nls { return { span: { start, end }, content: c.content }; }; - let i = 0; const localizePatches = lazy(localizeCalls) - .map(lc => ([ - { range: lc.keySpan, content: '' + (i++) }, - { range: lc.valueSpan, content: 'null' } - ])) + .map(lc => ( + options.preserveEnglish ? [ + { range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize('key', "message") => localize(, "message") + ] : [ + { range: lc.keySpan, content: `${allNLSMessagesIndex++}` }, // localize('key', "message") => localize(, null) + { range: lc.valueSpan, content: 'null' } + ])) .flatten() .map(toPatch); const localize2Patches = lazy(localize2Calls) .map(lc => ( - { range: lc.keySpan, content: '' + (i++) } + { range: lc.keySpan, content: `${allNLSMessagesIndex++}` } // localize2('key', "message") => localize(, "message") )) .map(toPatch); @@ -460,45 +484,39 @@ module _nls { } }); - javascript = patchJavascript(patches, javascript, moduleId); - - // since imports are not within the sourcemap information, - // we must do this MacGyver style - if (nlsExpressions.length || nls2Expressions.length) { - javascript = javascript.replace(/^define\(.*$/m, line => { - return line.replace(/(['"])vs\/nls\1/g, `$1vs/nls!${moduleId}$1`); - }); - } + javascript = patchJavascript(patches, javascript); sourcemap = patchSourcemap(patches, sourcemap, smc); - return { javascript, sourcemap, nlsKeys, nls }; + return { javascript, sourcemap, nlsKeys, nlsMessages }; } - export function patchFiles(javascriptFile: File, typescript: string): File[] { + export function patchFile(javascriptFile: File, typescript: string, options: { preserveEnglish: boolean }): File { const ts = require('typescript') as typeof import('typescript'); // hack? const moduleId = javascriptFile.relative .replace(/\.js$/, '') .replace(/\\/g, '/'); - const { javascript, sourcemap, nlsKeys, nls } = patch( + const { javascript, sourcemap, nlsKeys, nlsMessages } = patch( ts, - moduleId, typescript, javascriptFile.contents.toString(), - (javascriptFile).sourceMap + (javascriptFile).sourceMap, + options ); - const result: File[] = [fileFrom(javascriptFile, javascript)]; - (result[0]).sourceMap = sourcemap; + const result = fileFrom(javascriptFile, javascript); + (result).sourceMap = sourcemap; if (nlsKeys) { - result.push(fileFrom(javascriptFile, nlsKeys, javascriptFile.path.replace(/\.js$/, '.nls.keys.js'))); + moduleToNLSKeys[moduleId] = nlsKeys; + allNLSModulesAndKeys.push([moduleId, nlsKeys.map(nlsKey => typeof nlsKey === 'string' ? nlsKey : nlsKey.key)]); } - if (nls) { - result.push(fileFrom(javascriptFile, nls, javascriptFile.path.replace(/\.js$/, '.nls.js'))); + if (nlsMessages) { + moduleToNLSMessages[moduleId] = nlsMessages; + allNLSMessages.push(...nlsMessages); } return result; diff --git a/build/lib/optimize.js b/build/lib/optimize.js index d48235ebf15..83527762f73 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -192,6 +192,7 @@ function optimizeAMDTask(opts) { includeContent: true })) .pipe(opts.languages && opts.languages.length ? (0, i18n_1.processNlsFiles)({ + out: opts.src, fileHeader: bundledFileHeader, languages: opts.languages }) : es.through()); diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index 5b6dee9bf65..cd269a0f02b 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -269,6 +269,7 @@ function optimizeAMDTask(opts: IOptimizeAMDTaskOpts): NodeJS.ReadWriteStream { includeContent: true })) .pipe(opts.languages && opts.languages.length ? processNlsFiles({ + out: opts.src, fileHeader: bundledFileHeader, languages: opts.languages }) : es.through()); diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index cc47b050fb5..87fea16e97a 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -6,6 +6,10 @@ //@ts-check 'use strict'; +/** + * @typedef {import('./vs/nls').INLSConfiguration} INLSConfiguration + */ + // Store the node.js require function in a variable // before loading our AMD loader to avoid issues // when this file is bundled with other files. @@ -31,16 +35,13 @@ globalThis._VSCODE_PACKAGE_JSON = require('../package.json'); const loader = require('./vs/loader'); const bootstrap = require('./bootstrap'); const performance = require('./vs/base/common/performance'); - -// Bootstrap: NLS -const nlsConfig = bootstrap.setupNLS(); +const fs = require('fs'); // Bootstrap: Loader loader.config({ baseUrl: bootstrap.fileUriFromPath(__dirname, { isWindows: process.platform === 'win32' }), catchError: true, nodeRequire, - 'vs/nls': nlsConfig, amdModulesPattern: /^vs\//, recordStats: true }); @@ -52,13 +53,90 @@ if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { }); } -// Pseudo NLS support -if (nlsConfig && nlsConfig.pseudo) { - loader(['vs/nls'], function (/** @type {import('vs/nls')} */nlsPlugin) { - nlsPlugin.setPseudoTranslation(!!nlsConfig.pseudo); - }); +//#region NLS helpers + +/** @type {Promise | undefined} */ +let setupNLSResult = undefined; + +/** + * @returns {Promise} + */ +function setupNLS() { + if (!setupNLSResult) { + setupNLSResult = doSetupNLS(); + } + + return setupNLSResult; } +/** + * @returns {Promise} + */ +async function doSetupNLS() { + performance.mark('code/fork/willLoadNls'); + + /** @type {INLSConfiguration | undefined} */ + let nlsConfig = undefined; + + /** @type {string | undefined} */ + let messagesFile; + if (process.env['VSCODE_NLS_CONFIG']) { + try { + /** @type {INLSConfiguration} */ + nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']); + if (nlsConfig?.languagePack?.messagesFile) { + messagesFile = nlsConfig.languagePack.messagesFile; + } else if (nlsConfig?.defaultMessagesFile) { + messagesFile = nlsConfig.defaultMessagesFile; + } + + // VSCODE_GLOBALS: NLS + globalThis._VSCODE_NLS_LANGUAGE = nlsConfig?.resolvedLanguage; + } catch (e) { + console.error(`Error reading VSCODE_NLS_CONFIG from environment: ${e}`); + } + } + + if ( + process.env['VSCODE_DEV'] || // no NLS support in dev mode + !messagesFile // no NLS messages file + ) { + return undefined; + } + + try { + // VSCODE_GLOBALS: NLS + globalThis._VSCODE_NLS_MESSAGES = JSON.parse((await fs.promises.readFile(messagesFile)).toString()); + } catch (error) { + console.error(`Error reading NLS messages file ${messagesFile}: ${error}`); + + // Mark as corrupt: this will re-create the language pack cache next startup + if (nlsConfig?.languagePack?.corruptMarkerFile) { + try { + await fs.promises.writeFile(nlsConfig.languagePack.corruptMarkerFile, 'corrupted'); + } catch (error) { + console.error(`Error writing corrupted NLS marker file: ${error}`); + } + } + + // Fallback to the default message file to ensure english translation at least + if (nlsConfig?.defaultMessagesFile && nlsConfig.defaultMessagesFile !== messagesFile) { + try { + // VSCODE_GLOBALS: NLS + globalThis._VSCODE_NLS_MESSAGES = JSON.parse((await fs.promises.readFile(nlsConfig.defaultMessagesFile)).toString()); + } catch (error) { + console.error(`Error reading default NLS messages file ${nlsConfig.defaultMessagesFile}: ${error}`); + } + } + } + + performance.mark('code/fork/didLoadNls'); + + return nlsConfig; +} + +//#endregion + /** * @param {string=} entrypoint * @param {(value: any) => void=} onLoad @@ -82,6 +160,8 @@ exports.load = function (entrypoint, onLoad, onError) { onLoad = onLoad || function () { }; onError = onError || function (err) { console.error(err); }; - performance.mark('code/fork/willLoadCode'); - loader([entrypoint], onLoad, onError); + setupNLS().then(() => { + performance.mark('code/fork/willLoadCode'); + loader([entrypoint], onLoad, onError); + }); }; diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index d231657094d..cd859a847f9 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -82,28 +82,23 @@ developerDeveloperKeybindingsDisposable = registerDeveloperKeybindings(disallowReloadKeybinding); } - // Get the nls configuration into the process.env as early as possible - // @ts-ignore - const nlsConfig = globalThis.MonacoBootstrap.setupNLS(); - - let locale = nlsConfig.availableLanguages['*'] || 'en'; - if (locale === 'zh-tw') { - locale = 'zh-Hant'; - } else if (locale === 'zh-cn') { - locale = 'zh-Hans'; + // VSCODE_GLOBALS: NLS + globalThis._VSCODE_NLS_MESSAGES = configuration.nls.messages; + globalThis._VSCODE_NLS_LANGUAGE = configuration.nls.language; + let language = configuration.nls.language || 'en'; + if (language === 'zh-tw') { + language = 'zh-Hant'; + } else if (language === 'zh-cn') { + language = 'zh-Hans'; } - window.document.documentElement.setAttribute('lang', locale); + window.document.documentElement.setAttribute('lang', language); window['MonacoEnvironment'] = {}; - /** - * @typedef {any} LoaderConfig - */ - /** @type {LoaderConfig} */ + /** @type {any} */ const loaderConfig = { baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out`, - 'vs/nls': nlsConfig, preferScriptTags: true }; @@ -147,13 +142,6 @@ // Configure loader require.config(loaderConfig); - // Handle pseudo NLS - if (nlsConfig.pseudo) { - require(['vs/nls'], function (nlsPlugin) { - nlsPlugin.setPseudoTranslation(nlsConfig.pseudo); - }); - } - // Signal before require() if (typeof options?.beforeRequire === 'function') { options.beforeRequire(configuration); diff --git a/src/bootstrap.js b/src/bootstrap.js index bd0e92e672c..8be098f4199 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -22,8 +22,6 @@ }(this, function () { const Module = typeof require === 'function' ? require('module') : undefined; const path = typeof require === 'function' ? require('path') : undefined; - const fs = typeof require === 'function' ? require('fs') : undefined; - const util = typeof require === 'function' ? require('util') : undefined; //#region global bootstrapping @@ -118,141 +116,8 @@ //#endregion - - //#region NLS helpers - - /** - * @returns {{locale?: string, availableLanguages: {[lang: string]: string;}, pseudo?: boolean } | undefined} - */ - function setupNLS() { - - // Get the nls configuration as early as possible. - const process = safeProcess(); - /** @type {{ availableLanguages: {}; loadBundle?: (bundle: string, language: string, cb: (err: Error | undefined, result: string | undefined) => void) => void; _resolvedLanguagePackCoreLocation?: string; _corruptedFile?: string }} */ - let nlsConfig = { availableLanguages: {} }; - if (process && process.env['VSCODE_NLS_CONFIG']) { - try { - nlsConfig = JSON.parse(process.env['VSCODE_NLS_CONFIG']); - } catch (e) { - // Ignore - } - } - - if (nlsConfig._resolvedLanguagePackCoreLocation) { - const bundles = Object.create(null); - - /** - * @param {string} bundle - * @param {string} language - * @param {(err: Error | undefined, result: string | undefined) => void} cb - */ - nlsConfig.loadBundle = function (bundle, language, cb) { - const result = bundles[bundle]; - if (result) { - cb(undefined, result); - - return; - } - - // @ts-ignore - safeReadNlsFile(nlsConfig._resolvedLanguagePackCoreLocation, `${bundle.replace(/\//g, '!')}.nls.json`).then(function (content) { - const json = JSON.parse(content); - bundles[bundle] = json; - - cb(undefined, json); - }).catch((error) => { - try { - if (nlsConfig._corruptedFile) { - safeWriteNlsFile(nlsConfig._corruptedFile, 'corrupted').catch(function (error) { console.error(error); }); - } - } finally { - cb(error, undefined); - } - }); - }; - } - - return nlsConfig; - } - - /** - * @returns {typeof import('./vs/base/parts/sandbox/electron-sandbox/globals') | undefined} - */ - function safeSandboxGlobals() { - const globals = (typeof self === 'object' ? self : typeof global === 'object' ? global : {}); - - // @ts-ignore - return globals.vscode; - } - - /** - * @returns {import('./vs/base/parts/sandbox/electron-sandbox/globals').ISandboxNodeProcess | NodeJS.Process | undefined} - */ - function safeProcess() { - const sandboxGlobals = safeSandboxGlobals(); - if (sandboxGlobals) { - return sandboxGlobals.process; // Native environment (sandboxed) - } - - if (typeof process !== 'undefined') { - return process; // Native environment (non-sandboxed) - } - - return undefined; - } - - /** - * @returns {import('./vs/base/parts/sandbox/electron-sandbox/electronTypes').IpcRenderer | undefined} - */ - function safeIpcRenderer() { - const sandboxGlobals = safeSandboxGlobals(); - if (sandboxGlobals) { - return sandboxGlobals.ipcRenderer; - } - - return undefined; - } - - /** - * @param {string[]} pathSegments - * @returns {Promise} - */ - async function safeReadNlsFile(...pathSegments) { - const ipcRenderer = safeIpcRenderer(); - if (ipcRenderer) { - return ipcRenderer.invoke('vscode:readNlsFile', ...pathSegments); - } - - if (fs && path && util) { - return (await util.promisify(fs.readFile)(path.join(...pathSegments))).toString(); - } - - throw new Error('Unsupported operation (read NLS files)'); - } - - /** - * @param {string} path - * @param {string} content - * @returns {Promise} - */ - function safeWriteNlsFile(path, content) { - const ipcRenderer = safeIpcRenderer(); - if (ipcRenderer) { - return ipcRenderer.invoke('vscode:writeNlsFile', path, content); - } - - if (fs && util) { - return util.promisify(fs.writeFile)(path, content); - } - - throw new Error('Unsupported operation (write NLS files)'); - } - - //#endregion - return { enableASARSupport, - setupNLS, fileUriFromPath }; })); diff --git a/src/cli.js b/src/cli.js index a8aaa1f0d54..686f7ab80c3 100644 --- a/src/cli.js +++ b/src/cli.js @@ -6,6 +6,10 @@ //@ts-check 'use strict'; +/** + * @import { IProductConfiguration } from './vs/base/common/product' + */ + // Delete `VSCODE_CWD` very early even before // importing bootstrap files. We have seen // reports where `code .` would use the wrong @@ -16,17 +20,29 @@ delete process.env['VSCODE_CWD']; const bootstrap = require('./bootstrap'); const bootstrapNode = require('./bootstrap-node'); +/** @type {Partial} */ +// @ts-ignore const product = require('../product.json'); +const { resolveNLSConfiguration } = require('./vs/base/node/nls'); -// Enable portable support -// @ts-ignore -bootstrapNode.configurePortable(product); +async function start() { + + // NLS + const nlsConfiguration = await resolveNLSConfiguration({ userLocale: 'en', osLocale: 'en', commit: product.commit, userDataPath: '', nlsMetadataPath: __dirname }); + process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfiguration); // required for `bootstrap-amd` to pick up NLS messages + + // Enable portable support + // @ts-ignore + bootstrapNode.configurePortable(product); + + // Enable ASAR support + bootstrap.enableASARSupport(); -// Enable ASAR support -bootstrap.enableASARSupport(); + // Signal processes that we got launched as CLI + process.env['VSCODE_CLI'] = '1'; -// Signal processes that we got launched as CLI -process.env['VSCODE_CLI'] = '1'; + // Load CLI through AMD loader + require('./bootstrap-amd').load('vs/code/node/cli'); +} -// Load CLI through AMD loader -require('./bootstrap-amd').load('vs/code/node/cli'); +start(); diff --git a/src/main.js b/src/main.js index 1b3d4632fad..795d8a31729 100644 --- a/src/main.js +++ b/src/main.js @@ -8,7 +8,7 @@ /** * @import { IProductConfiguration } from './vs/base/common/product' - * @import { NLSConfiguration } from './vs/base/node/languagePacks' + * @import { INLSConfiguration } from './vs/nls' * @import { NativeParsedArgs } from './vs/platform/environment/common/argv' */ @@ -109,27 +109,29 @@ protocol.registerSchemesAsPrivileged([ registerListeners(); /** - * Support user defined locale: load it early before app('ready') - * to have more things running in parallel. + * We can resolve the NLS configuration early if it is defined + * in argv.json before `app.ready` event. Otherwise we can only + * resolve NLS after `app.ready` event to resolve the OS locale. * - * @type {Promise | undefined} + * @type {Promise | undefined} */ let nlsConfigurationPromise = undefined; -/** - * @type {String} - **/ // Use the most preferred OS language for language recommendation. // The API might return an empty array on Linux, such as when // the 'C' locale is the user's only configured locale. // No matter the OS, if the array is empty, default back to 'en'. -const resolved = app.getPreferredSystemLanguages()?.[0] ?? 'en'; -const osLocale = processZhLocale(resolved.toLowerCase()); -const metaDataFile = path.join(__dirname, 'nls.metadata.json'); -const locale = getUserDefinedLocale(argvConfig); -if (locale) { - const { getNLSConfiguration } = require('./vs/base/node/languagePacks'); - nlsConfigurationPromise = getNLSConfiguration(product.commit, userDataPath, metaDataFile, locale, osLocale); +const osLocale = processZhLocale((app.getPreferredSystemLanguages()?.[0] ?? 'en').toLowerCase()); +const userLocale = getUserDefinedLocale(argvConfig); +if (userLocale) { + const { resolveNLSConfiguration } = require('./vs/base/node/nls'); + nlsConfigurationPromise = resolveNLSConfiguration({ + userLocale, + osLocale, + commit: product.commit, + userDataPath, + nlsMetadataPath: __dirname + }); } // Pass in the locale to Electron so that the @@ -141,7 +143,7 @@ if (locale) { // In that case, use `en` as the Electron locale. if (process.platform === 'win32' || process.platform === 'linux') { - const electronLocale = (!locale || locale === 'qps-ploc') ? 'en' : locale; + const electronLocale = (!userLocale || userLocale === 'qps-ploc') ? 'en' : userLocale; app.commandLine.appendSwitch('lang', electronLocale); } @@ -161,15 +163,28 @@ app.once('ready', function () { } }); +async function onReady() { + perf.mark('code/mainAppReady'); + + try { + const [, nlsConfig] = await Promise.all([ + mkdirpIgnoreError(codeCachePath), + resolveNlsConfiguration() + ]); + + startup(codeCachePath, nlsConfig); + } catch (error) { + console.error(error); + } +} + /** * Main startup routine * * @param {string | undefined} codeCachePath - * @param {NLSConfiguration} nlsConfig + * @param {INLSConfiguration} nlsConfig */ function startup(codeCachePath, nlsConfig) { - nlsConfig._languagePackSupport = true; - process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfig); process.env['VSCODE_CODE_CACHE_PATH'] = codeCachePath || ''; @@ -180,18 +195,6 @@ function startup(codeCachePath, nlsConfig) { }); } -async function onReady() { - perf.mark('code/mainAppReady'); - - try { - const [, nlsConfig] = await Promise.all([mkdirpIgnoreError(codeCachePath), resolveNlsConfiguration()]); - - startup(codeCachePath, nlsConfig); - } catch (error) { - console.error(error); - } -} - /** * @param {NativeParsedArgs} cliArgs */ @@ -643,35 +646,47 @@ function processZhLocale(appLocale) { /** * Resolve the NLS configuration * - * @return {Promise} + * @return {Promise} */ async function resolveNlsConfiguration() { - // First, we need to test a user defined locale. If it fails we try the app locale. + // First, we need to test a user defined locale. + // If it fails we try the app locale. // If that fails we fall back to English. - let nlsConfiguration = nlsConfigurationPromise ? await nlsConfigurationPromise : undefined; + + const nlsConfiguration = nlsConfigurationPromise ? await nlsConfigurationPromise : undefined; if (nlsConfiguration) { return nlsConfiguration; } - // Try to use the app locale. Please note that the app locale is only - // valid after we have received the app ready event. This is why the - // code is here. + // Try to use the app locale which is only valid + // after the app ready event has been fired. - /** - * @type string - */ - let appLocale = app.getLocale(); - if (!appLocale) { - return { locale: 'en', osLocale, availableLanguages: {} }; + let userLocale = app.getLocale(); + if (!userLocale) { + return { + userLocale: 'en', + osLocale, + resolvedLanguage: 'en', + defaultMessagesFile: path.join(__dirname, 'nls.messages.json'), + + // NLS: below 2 are a relic from old times only used by vscode-nls and deprecated + locale: 'en', + availableLanguages: {} + }; } // See above the comment about the loader and case sensitiveness - appLocale = processZhLocale(appLocale.toLowerCase()); - - const { getNLSConfiguration } = require('./vs/base/node/languagePacks'); - nlsConfiguration = await getNLSConfiguration(product.commit, userDataPath, metaDataFile, appLocale, osLocale); - return nlsConfiguration ?? { locale: 'en', osLocale, availableLanguages: {} }; + userLocale = processZhLocale(userLocale.toLowerCase()); + + const { resolveNLSConfiguration } = require('./vs/base/node/nls'); + return resolveNLSConfiguration({ + userLocale, + osLocale, + commit: product.commit, + userDataPath, + nlsMetadataPath: __dirname + }); } /** @@ -689,7 +704,7 @@ function getUserDefinedLocale(argvConfig) { return locale.toLowerCase(); // a directly provided --locale always wins } - return argvConfig.locale && typeof argvConfig.locale === 'string' ? argvConfig.locale.toLowerCase() : undefined; + return typeof argvConfig?.locale === 'string' ? argvConfig.locale.toLowerCase() : undefined; } //#endregion diff --git a/src/server-cli.js b/src/server-cli.js index fdfdac48b99..3479ae3f1d6 100644 --- a/src/server-cli.js +++ b/src/server-cli.js @@ -4,18 +4,36 @@ *--------------------------------------------------------------------------------------------*/ // @ts-check +'use strict'; + +/** + * @import { IProductConfiguration } from './vs/base/common/product' + */ const path = require('path'); +/** @type {Partial} */ +// @ts-ignore +const product = require('../product.json'); +const { resolveNLSConfiguration } = require('./vs/base/node/nls'); + +async function start() { -// Keep bootstrap-amd.js from redefining 'fs'. -delete process.env['ELECTRON_RUN_AS_NODE']; + // Keep bootstrap-amd.js from redefining 'fs'. + delete process.env['ELECTRON_RUN_AS_NODE']; -if (process.env['VSCODE_DEV']) { - // When running out of sources, we need to load node modules from remote/node_modules, - // which are compiled against nodejs, not electron - process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] = process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] || path.join(__dirname, '..', 'remote', 'node_modules'); - require('./bootstrap-node').injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']); -} else { - delete process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']; + // NLS + const nlsConfiguration = await resolveNLSConfiguration({ userLocale: 'en', osLocale: 'en', commit: product.commit, userDataPath: '', nlsMetadataPath: __dirname }); + process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfiguration); // required for `bootstrap-amd` to pick up NLS messages + + if (process.env['VSCODE_DEV']) { + // When running out of sources, we need to load node modules from remote/node_modules, + // which are compiled against nodejs, not electron + process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] = process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH'] || path.join(__dirname, '..', 'remote', 'node_modules'); + require('./bootstrap-node').injectNodeModuleLookupPath(process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']); + } else { + delete process.env['VSCODE_INJECT_NODE_MODULE_LOOKUP_PATH']; + } + require('./bootstrap-amd').load('vs/server/node/server.cli'); } -require('./bootstrap-amd').load('vs/server/node/server.cli'); + +start(); diff --git a/src/server-main.js b/src/server-main.js index e5feac4a627..18f700a404f 100644 --- a/src/server-main.js +++ b/src/server-main.js @@ -4,6 +4,12 @@ *--------------------------------------------------------------------------------------------*/ // @ts-check +'use strict'; + +/** + * @import { IProductConfiguration } from './vs/base/common/product' + * @import { INLSConfiguration } from './vs/nls' + */ /** * @import { IServerAPI } from './vs/server/node/remoteExtensionHostAgentServer' @@ -11,9 +17,12 @@ const perf = require('./vs/base/common/performance'); const performance = require('perf_hooks').performance; +/** @type {Partial} */ +// @ts-ignore const product = require('../product.json'); const readline = require('readline'); const http = require('http'); +const { resolveNLSConfiguration } = require('./vs/base/node/nls'); perf.mark('code/server/start'); // @ts-ignore @@ -42,8 +51,10 @@ async function start() { const shouldSpawnCli = parsedArgs.help || parsedArgs.version || extensionLookupArgs.some(a => !!parsedArgs[a]) || (extensionInstallArgs.some(a => !!parsedArgs[a]) && !parsedArgs['start-server']); + const nlsConfiguration = await resolveNLSConfiguration({ userLocale: 'en', osLocale: 'en', commit: product.commit, userDataPath: '', nlsMetadataPath: __dirname }); + if (shouldSpawnCli) { - loadCode().then((mod) => { + loadCode(nlsConfiguration).then((mod) => { mod.spawnCli(); }); return; @@ -56,7 +67,7 @@ async function start() { /** @returns {Promise} */ const getRemoteExtensionHostAgentServer = () => { if (!_remoteExtensionHostAgentServerPromise) { - _remoteExtensionHostAgentServerPromise = loadCode().then(async (mod) => { + _remoteExtensionHostAgentServerPromise = loadCode(nlsConfiguration).then(async (mod) => { const server = await mod.createServer(address); _remoteExtensionHostAgentServer = server; return server; @@ -249,13 +260,19 @@ async function findFreePort(host, start, end) { return undefined; } -/** @returns { Promise } */ -function loadCode() { +/** + * @param {INLSConfiguration} nlsConfiguration + * @returns { Promise } + */ +function loadCode(nlsConfiguration) { return new Promise((resolve, reject) => { const path = require('path'); delete process.env['ELECTRON_RUN_AS_NODE']; // Keep bootstrap-amd.js from redefining 'fs'. + /** @type {INLSConfiguration} */ + process.env['VSCODE_NLS_CONFIG'] = JSON.stringify(nlsConfiguration); // required for `bootstrap-amd` to pick up NLS messages + // See https://github.com/microsoft/vscode-remote-release/issues/6543 // We would normally install a SIGPIPE listener in bootstrap.js // But in certain situations, the console itself can be in a broken pipe state @@ -308,5 +325,4 @@ function prompt(question) { }); } - start(); diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 988f0485713..8f955fa3c9f 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -19,6 +19,7 @@ "typings/require.d.ts", "typings/thenable.d.ts", "typings/vscode-globals-product.d.ts", + "typings/vscode-globals-nls.d.ts", "vs/loader.d.ts", "vs/monaco.d.ts", "vs/editor/*", diff --git a/src/typings/vscode-globals-nls.d.ts b/src/typings/vscode-globals-nls.d.ts new file mode 100644 index 00000000000..bc480767623 --- /dev/null +++ b/src/typings/vscode-globals-nls.d.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// AMD2ESM mirgation relevant + +/** + * NLS Globals: these need to be defined in all contexts that make + * use of our `nls.localize` and `nls.localize2` functions. This includes: + * - Electron main process + * - Electron window (renderer) process + * - Utility Process + * - Node.js + * - Browser + * - Web worker + * + * That is because during build time we strip out all english strings from + * the resulting JS code and replace it with a that is then looked + * up from the `_VSCODE_NLS_MESSAGES` array. + */ +declare global { + /** + * All NLS messages produced by `localize` and `localize2` calls + * under `src/vs` translated to the language as indicated by + * `_VSCODE_NLS_LANGUAGE`. + */ + var _VSCODE_NLS_MESSAGES: string[]; + /** + * The actual language of the NLS messages (e.g. 'en', de' or 'pt-br'). + */ + var _VSCODE_NLS_LANGUAGE: string | undefined; +} + +// fake export to make global work +export { } diff --git a/src/vs/base/browser/defaultWorkerFactory.ts b/src/vs/base/browser/defaultWorkerFactory.ts index 71391b063f3..834fa4d3c1a 100644 --- a/src/vs/base/browser/defaultWorkerFactory.ts +++ b/src/vs/base/browser/defaultWorkerFactory.ts @@ -50,27 +50,35 @@ export function getWorkerBootstrapUrl(scriptPath: string, label: string): string if (/^((http:)|(https:)|(file:))/.test(scriptPath) && scriptPath.substring(0, globalThis.origin.length) !== globalThis.origin) { // this is the cross-origin case // i.e. the webpage is running at a different origin than where the scripts are loaded from - const myPath = 'vs/base/worker/defaultWorkerFactory.js'; - const workerBaseUrl = require.toUrl(myPath).slice(0, -myPath.length); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321 - const js = `/*${label}*/globalThis.MonacoEnvironment={baseUrl: '${workerBaseUrl}'};const ttPolicy = globalThis.trustedTypes?.createPolicy('defaultWorkerFactory', { createScriptURL: value => value });importScripts(ttPolicy?.createScriptURL('${scriptPath}') ?? '${scriptPath}');/*${label}*/`; - const blob = new Blob([js], { type: 'application/javascript' }); - return URL.createObjectURL(blob); - } - - const start = scriptPath.lastIndexOf('?'); - const end = scriptPath.lastIndexOf('#', start); - const params = start > 0 - ? new URLSearchParams(scriptPath.substring(start + 1, ~end ? end : undefined)) - : new URLSearchParams(); - - COI.addSearchParam(params, true, true); - const search = params.toString(); - - if (!search) { - return `${scriptPath}#${label}`; } else { - return `${scriptPath}?${params.toString()}#${label}`; + const start = scriptPath.lastIndexOf('?'); + const end = scriptPath.lastIndexOf('#', start); + const params = start > 0 + ? new URLSearchParams(scriptPath.substring(start + 1, ~end ? end : undefined)) + : new URLSearchParams(); + + COI.addSearchParam(params, true, true); + const search = params.toString(); + if (!search) { + scriptPath = `${scriptPath}#${label}`; + } else { + scriptPath = `${scriptPath}?${params.toString()}#${label}`; + } } + + const factoryModuleId = 'vs/base/worker/defaultWorkerFactory.js'; + const workerBaseUrl = require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); // explicitly using require.toUrl(), see https://github.com/microsoft/vscode/issues/107440#issuecomment-698982321 + const blob = new Blob([[ + `/*${label}*/`, + `globalThis.MonacoEnvironment = { baseUrl: '${workerBaseUrl}' };`, + // VSCODE_GLOBALS: NLS + `globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(globalThis._VSCODE_NLS_MESSAGES)};`, + `globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(globalThis._VSCODE_NLS_LANGUAGE)};`, + `const ttPolicy = globalThis.trustedTypes?.createPolicy('defaultWorkerFactory', { createScriptURL: value => value });`, + `importScripts(ttPolicy?.createScriptURL('${scriptPath}') ?? '${scriptPath}');`, + `/*${label}*/` + ].join('')], { type: 'application/javascript' }); + return URL.createObjectURL(blob); } // ESM-comment-end @@ -136,8 +144,6 @@ class WebWorker extends Disposable implements IWorker { } }); } - - } export class DefaultWorkerFactory implements IWorkerFactory { diff --git a/src/vs/base/common/platform.ts b/src/vs/base/common/platform.ts index 2251c7db5ba..d931e64dfa9 100644 --- a/src/vs/base/common/platform.ts +++ b/src/vs/base/common/platform.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as nls from 'vs/nls'; export const LANGUAGE_DEFAULT = 'en'; @@ -22,13 +23,6 @@ let _platformLocale: string = LANGUAGE_DEFAULT; let _translationsConfigFile: string | undefined = undefined; let _userAgent: string | undefined = undefined; -interface NLSConfig { - locale: string; - osLocale: string; - availableLanguages: { [key: string]: string }; - _translationsConfigFile: string; -} - export interface IProcessEnvironment { [key: string]: string | undefined; } @@ -89,13 +83,11 @@ if (typeof nodeProcess === 'object') { const rawNlsConfig = nodeProcess.env['VSCODE_NLS_CONFIG']; if (rawNlsConfig) { try { - const nlsConfig: NLSConfig = JSON.parse(rawNlsConfig); - const resolved = nlsConfig.availableLanguages['*']; - _locale = nlsConfig.locale; + const nlsConfig: nls.INLSConfiguration = JSON.parse(rawNlsConfig); + _locale = nlsConfig.userLocale; _platformLocale = nlsConfig.osLocale; - // VSCode's default language is 'en' - _language = resolved ? resolved : LANGUAGE_DEFAULT; - _translationsConfigFile = nlsConfig._translationsConfigFile; + _language = nlsConfig.resolvedLanguage || LANGUAGE_DEFAULT; + _translationsConfigFile = nlsConfig.languagePack?.translationsConfigFile; } catch (e) { } } @@ -111,18 +103,10 @@ else if (typeof navigator === 'object' && !isElectronRenderer) { _isLinux = _userAgent.indexOf('Linux') >= 0; _isMobile = _userAgent?.indexOf('Mobi') >= 0; _isWeb = true; - - const configuredLocale = nls.getConfiguredDefaultLocale( - // This call _must_ be done in the file that calls `nls.getConfiguredDefaultLocale` - // to ensure that the NLS AMD Loader plugin has been loaded and configured. - // This is because the loader plugin decides what the default locale is based on - // how it's able to resolve the strings. - nls.localize({ key: 'ensureLoaderPluginIsLoaded', comment: ['{Locked}'] }, '_') - ); - - _locale = configuredLocale || LANGUAGE_DEFAULT; - _language = _locale; - _platformLocale = navigator.language; + // VSCODE_GLOBALS: NLS + _language = globalThis._VSCODE_NLS_LANGUAGE || LANGUAGE_DEFAULT; + _locale = navigator.language.toLowerCase(); + _platformLocale = _locale; } // Unknown environment @@ -178,7 +162,7 @@ export const userAgent = _userAgent; /** * The language used for the user interface. The format of * the string is all lower case (e.g. zh-tw for Traditional - * Chinese) + * Chinese or de for German) */ export const language = _language; @@ -204,15 +188,16 @@ export namespace Language { } /** - * The OS locale or the locale specified by --locale. The format of - * the string is all lower case (e.g. zh-tw for Traditional - * Chinese). The UI is not necessarily shown in the provided locale. + * Desktop: The OS locale or the locale specified by --locale or `argv.json`. + * Web: matches `platformLocale`. + * + * The UI is not necessarily shown in the provided locale. */ export const locale = _locale; /** * This will always be set to the OS/browser's locale regardless of - * what was specified by --locale. The format of the string is all + * what was specified otherwise. The format of the string is all * lower case (e.g. zh-tw for Traditional Chinese). The UI is not * necessarily shown in the provided locale. */ diff --git a/src/vs/base/node/languagePacks.d.ts b/src/vs/base/node/languagePacks.d.ts deleted file mode 100644 index 5dd1b1f1ee9..00000000000 --- a/src/vs/base/node/languagePacks.d.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export interface NLSConfiguration { - locale: string; - osLocale: string; - availableLanguages: { - [key: string]: string; - }; - pseudo?: boolean; - _languagePackSupport?: boolean; -} - -export interface InternalNLSConfiguration extends NLSConfiguration { - _languagePackId: string; - _translationsConfigFile: string; - _cacheRoot: string; - _resolvedLanguagePackCoreLocation: string; - _corruptedFile: string; - _languagePackSupport?: boolean; -} - -export function getNLSConfiguration(commit: string | undefined, userDataPath: string, metaDataFile: string, locale: string, osLocale: string): Promise; diff --git a/src/vs/base/node/languagePacks.js b/src/vs/base/node/languagePacks.js deleted file mode 100644 index 0d3d1acf6a3..00000000000 --- a/src/vs/base/node/languagePacks.js +++ /dev/null @@ -1,264 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -/// - -//@ts-check -(function () { - 'use strict'; - - /** - * @param {typeof import('path')} path - * @param {typeof import('fs')} fs - * @param {typeof import('../common/performance')} perf - */ - function factory(path, fs, perf) { - - /** - * @param {string} file - * @returns {Promise} - */ - function exists(file) { - return new Promise(c => fs.exists(file, c)); - } - - /** - * @param {string} file - * @returns {Promise} - */ - function touch(file) { - return new Promise((c, e) => { const d = new Date(); fs.utimes(file, d, d, err => err ? e(err) : c()); }); - } - - /** - * @param {string} dir - * @returns {Promise} - */ - function mkdirp(dir) { - return new Promise((c, e) => fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? e(err) : c(dir))); - } - - /** - * @param {string} location - * @returns {Promise} - */ - function rimraf(location) { - return new Promise((c, e) => fs.rm(location, { recursive: true, force: true, maxRetries: 3 }, err => err ? e(err) : c())); - } - - /** - * @param {string} file - * @returns {Promise} - */ - function readFile(file) { - return new Promise((c, e) => fs.readFile(file, 'utf8', (err, data) => err ? e(err) : c(data))); - } - - /** - * @param {string} file - * @param {string} content - * @returns {Promise} - */ - function writeFile(file, content) { - return new Promise((c, e) => fs.writeFile(file, content, 'utf8', err => err ? e(err) : c())); - } - - /** - * @param {string} userDataPath - * @returns {Promise} - */ - async function getLanguagePackConfigurations(userDataPath) { - const configFile = path.join(userDataPath, 'languagepacks.json'); - try { - return JSON.parse(await readFile(configFile)); - } catch (err) { - // Do nothing. If we can't read the file we have no - // language pack config. - } - return undefined; - } - - /** - * @param {object} config - * @param {string | undefined} locale - */ - function resolveLanguagePackLocale(config, locale) { - try { - while (locale) { - if (config[locale]) { - return locale; - } else { - const index = locale.lastIndexOf('-'); - if (index > 0) { - locale = locale.substring(0, index); - } else { - return undefined; - } - } - } - } catch (err) { - console.error('Resolving language pack configuration failed.', err); - } - return undefined; - } - - /** - * @param {string | undefined} commit - * @param {string} userDataPath - * @param {string} metaDataFile - * @param {string} locale - * @param {string} osLocale - * @returns {Promise} - */ - function getNLSConfiguration(commit, userDataPath, metaDataFile, locale, osLocale) { - const defaultResult = function (locale) { - perf.mark('code/didGenerateNls'); - return Promise.resolve({ locale, osLocale, availableLanguages: {} }); - }; - - perf.mark('code/willGenerateNls'); - - if (locale === 'pseudo') { - return Promise.resolve({ locale, osLocale, availableLanguages: {}, pseudo: true }); - } - - if (process.env['VSCODE_DEV']) { - return Promise.resolve({ locale, osLocale, availableLanguages: {} }); - } - - // We have a built version so we have extracted nls file. Try to find - // the right file to use. - - // Check if we have an English or English US locale. If so fall to default since that is our - // English translation (we don't ship *.nls.en.json files) - if (locale && (locale === 'en' || locale === 'en-us')) { - return Promise.resolve({ locale, osLocale, availableLanguages: {} }); - } - - const initialLocale = locale; - - try { - if (!commit) { - return defaultResult(initialLocale); - } - return getLanguagePackConfigurations(userDataPath).then(configs => { - if (!configs) { - return defaultResult(initialLocale); - } - const resolvedLocale = resolveLanguagePackLocale(configs, locale); - if (!resolvedLocale) { - return defaultResult(initialLocale); - } - locale = resolvedLocale; - const packConfig = configs[locale]; - let mainPack; - if (!packConfig || typeof packConfig.hash !== 'string' || !packConfig.translations || typeof (mainPack = packConfig.translations['vscode']) !== 'string') { - return defaultResult(initialLocale); - } - return exists(mainPack).then(fileExists => { - if (!fileExists) { - return defaultResult(initialLocale); - } - const packId = packConfig.hash + '.' + locale; - const cacheRoot = path.join(userDataPath, 'clp', packId); - const coreLocation = path.join(cacheRoot, commit); - const translationsConfigFile = path.join(cacheRoot, 'tcf.json'); - const corruptedFile = path.join(cacheRoot, 'corrupted.info'); - const result = { - locale: initialLocale, - osLocale, - availableLanguages: { '*': locale }, - _languagePackId: packId, - _translationsConfigFile: translationsConfigFile, - _cacheRoot: cacheRoot, - _resolvedLanguagePackCoreLocation: coreLocation, - _corruptedFile: corruptedFile - }; - return exists(corruptedFile).then(corrupted => { - // The nls cache directory is corrupted. - let toDelete; - if (corrupted) { - toDelete = rimraf(cacheRoot); - } else { - toDelete = Promise.resolve(undefined); - } - return toDelete.then(() => { - return exists(coreLocation).then(fileExists => { - if (fileExists) { - // We don't wait for this. No big harm if we can't touch - touch(coreLocation).catch(() => { }); - perf.mark('code/didGenerateNls'); - return result; - } - return mkdirp(coreLocation).then(() => { - return Promise.all([readFile(metaDataFile), readFile(mainPack)]); - }).then(values => { - const metadata = JSON.parse(values[0]); - const packData = JSON.parse(values[1]).contents; - const bundles = Object.keys(metadata.bundles); - const writes = []; - for (const bundle of bundles) { - const modules = metadata.bundles[bundle]; - const target = Object.create(null); - for (const module of modules) { - const keys = metadata.keys[module]; - const defaultMessages = metadata.messages[module]; - const translations = packData[module]; - let targetStrings; - if (translations) { - targetStrings = []; - for (let i = 0; i < keys.length; i++) { - const elem = keys[i]; - const key = typeof elem === 'string' ? elem : elem.key; - let translatedMessage = translations[key]; - if (translatedMessage === undefined) { - translatedMessage = defaultMessages[i]; - } - targetStrings.push(translatedMessage); - } - } else { - targetStrings = defaultMessages; - } - target[module] = targetStrings; - } - writes.push(writeFile(path.join(coreLocation, bundle.replace(/\//g, '!') + '.nls.json'), JSON.stringify(target))); - } - writes.push(writeFile(translationsConfigFile, JSON.stringify(packConfig.translations))); - return Promise.all(writes); - }).then(() => { - perf.mark('code/didGenerateNls'); - return result; - }).catch(err => { - console.error('Generating translation files failed.', err); - return defaultResult(locale); - }); - }); - }); - }); - }); - }); - } catch (err) { - console.error('Generating translation files failed.', err); - return defaultResult(locale); - } - } - - return { - getNLSConfiguration - }; - } - - if (typeof define === 'function') { - // amd - define(['path', 'fs', 'vs/base/common/performance'], function (/** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(path, fs, perf); }); - } else if (typeof module === 'object' && typeof module.exports === 'object') { - const path = require('path'); - const fs = require('fs'); - const perf = require('../common/performance'); - module.exports = factory(path, fs, perf); - } else { - throw new Error('Unknown context'); - } -}()); diff --git a/src/vs/base/node/nls.d.ts b/src/vs/base/node/nls.d.ts new file mode 100644 index 00000000000..94ef503e4e3 --- /dev/null +++ b/src/vs/base/node/nls.d.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type { INLSConfiguration } from 'vs/nls'; + +export interface IResolveNLSConfigurationContext { + + /** + * Location where `nls.messages.json` and `nls.keys.json` are stored. + */ + readonly nlsMetadataPath: string; + + /** + * Path to the user data directory. Used as a cache for + * language packs converted to the format we need. + */ + readonly userDataPath: string; + + /** + * Commit of the running application. Can be `undefined` + * when not built. + */ + readonly commit: string | undefined; + + /** + * Locale as defined in `argv.json` or `app.getLocale()`. + */ + readonly userLocale: string; + + /** + * Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`). + */ + readonly osLocale: string; +} + +export function resolveNLSConfiguration(context: IResolveNLSConfigurationContext): Promise; diff --git a/src/vs/base/node/nls.js b/src/vs/base/node/nls.js new file mode 100644 index 00000000000..d4bac146516 --- /dev/null +++ b/src/vs/base/node/nls.js @@ -0,0 +1,288 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/// + +//@ts-check +'use strict'; + +/** + * @import { INLSConfiguration, ILanguagePacks } from '../../nls' + * @import { IResolveNLSConfigurationContext } from './nls' + */ + +(function () { + + /** + * @param {typeof import('path')} path + * @param {typeof import('fs')} fs + * @param {typeof import('../common/performance')} perf + */ + function factory(path, fs, perf) { + + //#region fs helpers + + /** + * @param {string} path + */ + async function exists(path) { + try { + await fs.promises.access(path); + + return true; + } catch { + return false; + } + } + + /** + * @param {string} path + */ + function touch(path) { + const date = new Date(); + return fs.promises.utimes(path, date, date); + } + + /** + * @param {string} path + */ + function mkdirp(path) { + return fs.promises.mkdir(path, { recursive: true }); + } + + /** + * @param {string} path + */ + function rimraf(path) { + return fs.promises.rm(path, { recursive: true, force: true, maxRetries: 3 }); + } + + /** + * @param {string} path + */ + function readFile(path) { + return fs.promises.readFile(path, 'utf-8'); + } + + /** + * @param {string} path + * @param {string} content + */ + function writeFile(path, content) { + return fs.promises.writeFile(path, content, 'utf-8'); + } + + //#endregion + + /** + * The `languagepacks.json` file is a JSON file that contains all metadata + * about installed language extensions per language. Specifically, for + * core (`vscode`) and all extensions it supports, it points to the related + * translation files. + * + * The file is updated whenever a new language pack is installed or removed. + * + * @param {string} userDataPath + * @returns {Promise} + */ + async function getLanguagePackConfigurations(userDataPath) { + const configFile = path.join(userDataPath, 'languagepacks.json'); + try { + return JSON.parse(await readFile(configFile)); + } catch (err) { + return undefined; // Do nothing. If we can't read the file we have no language pack config. + } + } + + /** + * @param {ILanguagePacks} languagePacks + * @param {string | undefined} locale + */ + function resolveLanguagePackLanguage(languagePacks, locale) { + try { + while (locale) { + if (languagePacks[locale]) { + return locale; + } + + const index = locale.lastIndexOf('-'); + if (index > 0) { + locale = locale.substring(0, index); + } else { + return undefined; + } + } + } catch (error) { + console.error('Resolving language pack configuration failed.', error); + } + + return undefined; + } + + /** + * @param {string} userLocale + * @param {string} osLocale + * @param {string} nlsMetadataPath + * @returns {INLSConfiguration} + */ + function defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath) { + perf.mark('code/didGenerateNls'); + + return { + userLocale, + osLocale, + resolvedLanguage: 'en', + defaultMessagesFile: path.join(nlsMetadataPath, 'nls.messages.json'), + + // NLS: below 2 are a relic from old times only used by vscode-nls and deprecated + locale: 'en', + availableLanguages: {} + }; + } + + /** + * @param {IResolveNLSConfigurationContext} context + * @returns {Promise} + */ + async function resolveNLSConfiguration({ userLocale, osLocale, userDataPath, commit, nlsMetadataPath }) { + perf.mark('code/willGenerateNls'); + + if ( + process.env['VSCODE_DEV'] || + userLocale === 'pseudo' || + userLocale === 'en' || userLocale === 'en-us' || + !commit || + !userDataPath + ) { + return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath); + } + + try { + const languagePacks = await getLanguagePackConfigurations(userDataPath); + if (!languagePacks) { + return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath); + } + + const resolvedLanguage = resolveLanguagePackLanguage(languagePacks, userLocale); + if (!resolvedLanguage) { + return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath); + } + + const languagePack = languagePacks[resolvedLanguage]; + const mainLanguagePackPath = languagePack?.translations?.['vscode']; + if ( + !languagePack || + typeof languagePack.hash !== 'string' || + !languagePack.translations || + typeof mainLanguagePackPath !== 'string' || + !(await exists(mainLanguagePackPath)) + ) { + return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath); + } + + const languagePackId = `${languagePack.hash}.${resolvedLanguage}`; + const globalLanguagePackCachePath = path.join(userDataPath, 'clp', languagePackId); + const commitLanguagePackCachePath = path.join(globalLanguagePackCachePath, commit); + const languagePackMessagesFile = path.join(commitLanguagePackCachePath, 'nls.messages.json'); + const translationsConfigFile = path.join(globalLanguagePackCachePath, 'tcf.json'); + const languagePackCorruptMarkerFile = path.join(globalLanguagePackCachePath, 'corrupted.info'); + + if (await exists(languagePackCorruptMarkerFile)) { + await rimraf(globalLanguagePackCachePath); // delete corrupted cache folder + } + + /** @type {INLSConfiguration} */ + const result = { + userLocale, + osLocale, + resolvedLanguage, + defaultMessagesFile: path.join(nlsMetadataPath, 'nls.messages.json'), + languagePack: { + translationsConfigFile, + messagesFile: languagePackMessagesFile, + corruptMarkerFile: languagePackCorruptMarkerFile + }, + + // NLS: below properties are a relic from old times only used by vscode-nls and deprecated + locale: userLocale, + availableLanguages: { '*': resolvedLanguage }, + _languagePackId: languagePackId, + _languagePackSupport: true, + _translationsConfigFile: translationsConfigFile, + _cacheRoot: globalLanguagePackCachePath, + _resolvedLanguagePackCoreLocation: commitLanguagePackCachePath, + _corruptedFile: languagePackCorruptMarkerFile + }; + + if (await exists(commitLanguagePackCachePath)) { + touch(commitLanguagePackCachePath).catch(() => { }); // We don't wait for this. No big harm if we can't touch + perf.mark('code/didGenerateNls'); + return result; + } + + /** @type {[unknown, Array<[string, string[]]>, string[], { contents: Record> }]} */ + // ^moduleId ^nlsKeys ^moduleId ^nlsKey ^nlsValue + const [ + , + nlsDefaultKeys, + nlsDefaultMessages, + nlsPackdata + ] = await Promise.all([ + mkdirp(commitLanguagePackCachePath), + JSON.parse(await readFile(path.join(nlsMetadataPath, 'nls.keys.json'))), + JSON.parse(await readFile(path.join(nlsMetadataPath, 'nls.messages.json'))), + JSON.parse(await readFile(mainLanguagePackPath)) + ]); + + /** @type {string[]} */ + const nlsResult = []; + + // We expect NLS messages to be in a flat array in sorted order as they + // where produced during build time. We use `nls.keys.json` to know the + // right order and then lookup the related message from the translation. + // If a translation does not exist, we fallback to the default message. + + let nlsIndex = 0; + for (const [moduleId, nlsKeys] of nlsDefaultKeys) { + const moduleTranslations = nlsPackdata.contents[moduleId]; + for (const nlsKey of nlsKeys) { + nlsResult.push(moduleTranslations?.[nlsKey] || nlsDefaultMessages[nlsIndex]); + nlsIndex++; + } + } + + await Promise.all([ + writeFile(languagePackMessagesFile, JSON.stringify(nlsResult)), + writeFile(translationsConfigFile, JSON.stringify(languagePack.translations)) + ]); + + perf.mark('code/didGenerateNls'); + + return result; + } catch (error) { + console.error('Generating translation files failed.', error); + } + + return defaultNLSConfiguration(userLocale, osLocale, nlsMetadataPath); + } + + return { + resolveNLSConfiguration + }; + } + + if (typeof define === 'function') { + // amd + define(['path', 'fs', 'vs/base/common/performance'], function (/** @type {typeof import('path')} */ path, /** @type {typeof import('fs')} */ fs, /** @type {typeof import('../common/performance')} */ perf) { return factory(path, fs, perf); }); + } else if (typeof module === 'object' && typeof module.exports === 'object') { + // commonjs + const path = require('path'); + const fs = require('fs'); + const perf = require('../common/performance'); + module.exports = factory(path, fs, perf); + } else { + throw new Error('vs/base/node/nls defined in UNKNOWN context (neither requirejs or commonjs)'); + } +})(); diff --git a/src/vs/base/parts/sandbox/common/sandboxTypes.ts b/src/vs/base/parts/sandbox/common/sandboxTypes.ts index 8c296184f7d..9b607da8103 100644 --- a/src/vs/base/parts/sandbox/common/sandboxTypes.ts +++ b/src/vs/base/parts/sandbox/common/sandboxTypes.ts @@ -53,4 +53,21 @@ export interface ISandboxConfiguration { * Location of V8 code cache. */ codeCachePath?: string; + + /** + * NLS support + */ + nls: { + + /** + * All NLS messages produced by `localize` and `localize2` calls + * under `src/vs`. + */ + messages: string[]; + + /** + * The actual language of the NLS messages (e.g. 'en', de' or 'pt-br'). + */ + language: string | undefined; + }; } diff --git a/src/vs/code/browser/workbench/workbench.html b/src/vs/code/browser/workbench/workbench.html index 700231f0357..019f0b949f0 100644 --- a/src/vs/code/browser/workbench/workbench.html +++ b/src/vs/code/browser/workbench/workbench.html @@ -36,25 +36,14 @@ - + + + + diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 1c4a565adec..890691d24ef 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -10,13 +10,12 @@ import { hostname, release } from 'os'; import { VSBuffer } from 'vs/base/common/buffer'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { isSigPipeError, onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors'; -import { isEqualOrParent } from 'vs/base/common/extpath'; import { Event } from 'vs/base/common/event'; import { parse } from 'vs/base/common/jsonc'; import { getPathLabel } from 'vs/base/common/labels'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas, VSCODE_AUTHORITY } from 'vs/base/common/network'; -import { isAbsolute, join, posix } from 'vs/base/common/path'; +import { join, posix } from 'vs/base/common/path'; import { IProcessEnvironment, isLinux, isLinuxSnap, isMacintosh, isWindows, OS } from 'vs/base/common/platform'; import { assertType } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -496,24 +495,6 @@ export class CodeApplication extends Disposable { return this.resolveShellEnvironment(args, env, false); }); - validatedIpcMain.handle('vscode:writeNlsFile', (event, path: unknown, data: unknown) => { - const uri = this.validateNlsPath([path]); - if (!uri || typeof data !== 'string') { - throw new Error('Invalid operation (vscode:writeNlsFile)'); - } - - return this.fileService.writeFile(uri, VSBuffer.fromString(data)); - }); - - validatedIpcMain.handle('vscode:readNlsFile', async (event, ...paths: unknown[]) => { - const uri = this.validateNlsPath(paths); - if (!uri) { - throw new Error('Invalid operation (vscode:readNlsFile)'); - } - - return (await this.fileService.readFile(uri)).value.toString(); - }); - validatedIpcMain.on('vscode:toggleDevTools', event => event.sender.toggleDevTools()); validatedIpcMain.on('vscode:openDevTools', event => event.sender.openDevTools()); @@ -529,26 +510,6 @@ export class CodeApplication extends Disposable { //#endregion } - private validateNlsPath(pathSegments: unknown[]): URI | undefined { - let path: string | undefined = undefined; - - for (const pathSegment of pathSegments) { - if (typeof pathSegment === 'string') { - if (typeof path !== 'string') { - path = pathSegment; - } else { - path = join(path, pathSegment); - } - } - } - - if (typeof path !== 'string' || !isAbsolute(path) || !isEqualOrParent(path, this.environmentMainService.cachedLanguagesPath, !isLinux)) { - return undefined; - } - - return URI.file(path); - } - private onUnexpectedError(error: Error): void { if (error) { diff --git a/src/vs/code/electron-sandbox/workbench/workbench.js b/src/vs/code/electron-sandbox/workbench/workbench.js index 25830623237..9f41e508bb3 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.js +++ b/src/vs/code/electron-sandbox/workbench/workbench.js @@ -20,13 +20,12 @@ // Add a perf entry right from the top performance.mark('code/didStartRenderer'); - // Load workbench main JS, CSS and NLS all in parallel. This is an + // Load workbench main JS and CSS all in parallel. This is an // optimization to prevent a waterfall of loading to happen, because // we know for a fact that workbench.desktop.main will depend on - // the related CSS and NLS counterparts. + // the related CSS counterpart. bootstrapWindow.load([ 'vs/workbench/workbench.desktop.main', - 'vs/nls!vs/workbench/workbench.desktop.main', 'vs/css!vs/workbench/workbench.desktop.main' ], function (desktopMain, configuration) { diff --git a/src/vs/nls.ts b/src/vs/nls.ts index 233840e65ab..5a546325fc7 100644 --- a/src/vs/nls.ts +++ b/src/vs/nls.ts @@ -3,27 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -let isPseudo = (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); -const DEFAULT_TAG = 'i-default'; - -interface INLSPluginConfig { - availableLanguages?: INLSPluginConfigAvailableLanguages; - loadBundle?: BundleLoader; - translationServiceUrl?: string; -} - -export interface INLSPluginConfigAvailableLanguages { - '*'?: string; - [module: string]: string | undefined; -} - -interface BundleLoader { - (bundle: string, locale: string | null, cb: (err: Error, messages: string[] | IBundledStrings) => void): void; -} - -interface IBundledStrings { - [moduleId: string]: string[]; -} +// VSCODE_GLOBALS: NLS +const isPseudo = globalThis._VSCODE_NLS_LANGUAGE === 'pseudo' || (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); export interface ILocalizeInfo { key: string; @@ -35,30 +16,6 @@ export interface ILocalizedString { value: string; } -interface ILocalizeFunc { - (info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; - (key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; -} - -interface IBoundLocalizeFunc { - (idx: number, defaultValue: null): string; -} - -interface ILocalize2Func { - (info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString; - (key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString; -} - -interface IBoundLocalize2Func { - (idx: number, defaultValue: string): ILocalizedString; -} - -interface IConsumerAPI { - localize: ILocalizeFunc | IBoundLocalizeFunc; - localize2: ILocalize2Func | IBoundLocalize2Func; - getConfiguredDefaultLocale(stringFromLocalizeCall: string): string | undefined; -} - function _format(message: string, args: (string | number | boolean | undefined | null)[]): string { let result: string; @@ -86,49 +43,6 @@ function _format(message: string, args: (string | number | boolean | undefined | return result; } -function findLanguageForModule(config: INLSPluginConfigAvailableLanguages, name: string) { - let result = config[name]; - if (result) { - return result; - } - result = config['*']; - if (result) { - return result; - } - return null; -} - -function endWithSlash(path: string): string { - if (path.charAt(path.length - 1) === '/') { - return path; - } - return path + '/'; -} - -async function getMessagesFromTranslationsService(translationServiceUrl: string, language: string, name: string): Promise { - const url = endWithSlash(translationServiceUrl) + endWithSlash(language) + 'vscode/' + endWithSlash(name); - const res = await fetch(url); - if (res.ok) { - const messages = await res.json() as string[] | IBundledStrings; - return messages; - } - throw new Error(`${res.status} - ${res.statusText}`); -} - -function createScopedLocalize(scope: string[]): IBoundLocalizeFunc { - return function (idx: number, defaultValue: null) { - const restArgs = Array.prototype.slice.call(arguments, 2); - return _format(scope[idx], restArgs); - }; -} - -function createScopedLocalize2(scope: string[]): IBoundLocalize2Func { - return (idx: number, defaultValue: string, ...args) => ({ - value: _format(scope[idx], args), - original: _format(defaultValue, args) - }); -} - /** * Marks a string to be localized. Returns the localized string. * @@ -160,10 +74,30 @@ export function localize(key: string, message: string, ...args: (string | number /** * @skipMangle */ -export function localize(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string { +export function localize(data: ILocalizeInfo | string /* | number when built */, message: string /* | null when built */, ...args: (string | number | boolean | undefined | null)[]): string { + if (typeof data === 'number') { + return _format(lookupMessage(data, message), args); + } return _format(message, args); } +/** + * Only used when built: Looks up the message in the global NLS table. + * This table is being made available as a global through bootstrapping + * depending on the target context. + */ +function lookupMessage(index: number, fallback: string | null): string { + // VSCODE_GLOBALS: NLS + const message = globalThis._VSCODE_NLS_MESSAGES?.[index]; + if (typeof message !== 'string') { + if (typeof fallback === 'string') { + return fallback; + } + throw new Error(`!!! NLS MISSING: ${index} !!!`); + } + return message; +} + /** * Marks a string to be localized. Returns an {@linkcode ILocalizedString} * which contains the localized string and the original string. @@ -197,123 +131,107 @@ export function localize2(key: string, message: string, ...args: (string | numbe /** * @skipMangle */ -export function localize2(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString { - const original = _format(message, args); - return { - value: original, - original - }; -} - -/** - * - * @param stringFromLocalizeCall You must pass in a string that was returned from a `nls.localize()` call - * in order to ensure the loader plugin has been initialized before this function is called. - */ -export function getConfiguredDefaultLocale(stringFromLocalizeCall: string): string | undefined; -/** - * @skipMangle - */ -export function getConfiguredDefaultLocale(_: string): string | undefined { - // This returns undefined because this implementation isn't used and is overwritten by the loader - // when loaded. - return undefined; -} +export function localize2(data: ILocalizeInfo | string /* | number when built */, originalMessage: string, ...args: (string | number | boolean | undefined | null)[]): ILocalizedString { + let message: string; + if (typeof data === 'number') { + message = lookupMessage(data, originalMessage); + } else { + message = originalMessage; + } -/** - * @skipMangle - */ -export function setPseudoTranslation(value: boolean) { - isPseudo = value; -} + const value = _format(message, args); -/** - * Invoked in a built product at run-time - * @skipMangle - */ -export function create(key: string, data: IBundledStrings & IConsumerAPI): IConsumerAPI { return { - localize: createScopedLocalize(data[key]), - localize2: createScopedLocalize2(data[key]), - getConfiguredDefaultLocale: data.getConfiguredDefaultLocale ?? ((_: string) => undefined) + value, + original: originalMessage === message ? value : _format(originalMessage, args) }; } -/** - * Invoked by the loader at run-time - * @skipMangle - */ -export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { - const pluginConfig: INLSPluginConfig = config['vs/nls'] ?? {}; - if (!name || name.length === 0) { - // TODO: We need to give back the mangled names here - return load({ - localize: localize, - localize2: localize2, - getConfiguredDefaultLocale: () => pluginConfig.availableLanguages?.['*'] - } as IConsumerAPI); - } - const language = pluginConfig.availableLanguages ? findLanguageForModule(pluginConfig.availableLanguages, name) : null; - const useDefaultLanguage = language === null || language === DEFAULT_TAG; - let suffix = '.nls'; - if (!useDefaultLanguage) { - suffix = suffix + '.' + language; - } - const messagesLoaded = (messages: string[] | IBundledStrings) => { - if (Array.isArray(messages)) { - (messages as any as IConsumerAPI).localize = createScopedLocalize(messages); - (messages as any as IConsumerAPI).localize2 = createScopedLocalize2(messages); - } else { - (messages as any as IConsumerAPI).localize = createScopedLocalize(messages[name]); - (messages as any as IConsumerAPI).localize2 = createScopedLocalize2(messages[name]); - } - (messages as any as IConsumerAPI).getConfiguredDefaultLocale = () => pluginConfig.availableLanguages?.['*']; - load(messages); - }; - if (typeof pluginConfig.loadBundle === 'function') { - (pluginConfig.loadBundle as BundleLoader)(name, language, (err: Error, messages) => { - // We have an error. Load the English default strings to not fail - if (err) { - req([name + '.nls'], messagesLoaded); - } else { - messagesLoaded(messages); - } - }); - } else if (pluginConfig.translationServiceUrl && !useDefaultLanguage) { - (async () => { - try { - const messages = await getMessagesFromTranslationsService(pluginConfig.translationServiceUrl!, language, name); - return messagesLoaded(messages); - } catch (err) { - // Language is already as generic as it gets, so require default messages - if (!language.includes('-')) { - console.error(err); - return req([name + '.nls'], messagesLoaded); - } - try { - // Since there is a dash, the language configured is a specific sub-language of the same generic language. - // Since we were unable to load the specific language, try to load the generic language. Ex. we failed to find a - // Swiss German (de-CH), so try to load the generic German (de) messages instead. - const genericLanguage = language.split('-')[0]; - const messages = await getMessagesFromTranslationsService(pluginConfig.translationServiceUrl!, genericLanguage, name); - // We got some messages, so we configure the configuration to use the generic language for this session. - pluginConfig.availableLanguages ??= {}; - pluginConfig.availableLanguages['*'] = genericLanguage; - return messagesLoaded(messages); - } catch (err) { - console.error(err); - return req([name + '.nls'], messagesLoaded); - } - } - })(); - } else { - req([name + suffix], messagesLoaded, (err: Error) => { - if (suffix === '.nls') { - console.error('Failed trying to load default language strings', err); - return; - } - console.error(`Failed to load message bundle for language ${language}. Falling back to the default language:`, err); - req([name + '.nls'], messagesLoaded); - }); - } -} +export interface INLSLanguagePackConfiguration { + + /** + * The path to the translations config file that contains pointers to + * all message bundles for `main` and extensions. + */ + readonly translationsConfigFile: string; + + /** + * The path to the file containing the translations for this language + * pack as flat string array. + */ + readonly messagesFile: string; + + /** + * The path to the file that can be used to signal a corrupt language + * pack, for example when reading the `messagesFile` fails. This will + * instruct the application to re-create the cache on next startup. + */ + readonly corruptMarkerFile: string; +} + +export interface INLSConfiguration { + + /** + * Locale as defined in `argv.json` or `app.getLocale()`. + */ + readonly userLocale: string; + + /** + * Locale as defined by the OS (e.g. `app.getPreferredSystemLanguages()`). + */ + readonly osLocale: string; + + /** + * The actual language of the UI that ends up being used considering `userLocale` + * and `osLocale`. + */ + readonly resolvedLanguage: string; + + /** + * Defined if a language pack is used that is not the + * default english language pack. This requires a language + * pack to be installed as extension. + */ + readonly languagePack?: INLSLanguagePackConfiguration; + + /** + * The path to the file containing the default english messages + * as flat string array. The file is only present in built + * versions of the application. + */ + readonly defaultMessagesFile: string; + + /** + * Below properties are deprecated and only there to continue support + * for `vscode-nls` module that depends on them. + * Refs https://github.com/microsoft/vscode-nls/blob/main/src/node/main.ts#L36-L46 + */ + /** @deprecated */ + readonly locale: string; + /** @deprecated */ + readonly availableLanguages: Record; + /** @deprecated */ + readonly _languagePackSupport?: boolean; + /** @deprecated */ + readonly _languagePackId?: string; + /** @deprecated */ + readonly _translationsConfigFile?: string; + /** @deprecated */ + readonly _cacheRoot?: string; + /** @deprecated */ + readonly _resolvedLanguagePackCoreLocation?: string; + /** @deprecated */ + readonly _corruptedFile?: string; +} + +export interface ILanguagePack { + readonly hash: string; + readonly label: string | undefined; + readonly extensions: { + readonly extensionIdentifier: { readonly id: string; readonly uuid?: string }; + readonly version: string; + }[]; + readonly translations: Record; +} + +export type ILanguagePacks = Record; diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 748ff075783..dec04406afa 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -19,9 +19,6 @@ export const IEnvironmentMainService = refineServiceDecorator = {}; - @memoize - get cachedLanguagesPath(): string { return join(this.userDataPath, 'clp'); } - @memoize get backupHome(): string { return join(this.userDataPath, 'Backups'); } diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 40f1df2e7be..8f7ac7dfcc3 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -81,7 +81,12 @@ export class IssueMainService implements IIssueMainService { arch: arch(), release: release(), }, - product + product, + nls: { + // VSCODE_GLOBALS: NLS + messages: globalThis._VSCODE_NLS_MESSAGES, + language: globalThis._VSCODE_NLS_LANGUAGE + } }); this.issueReporterWindow.loadURL( diff --git a/src/vs/platform/issue/electron-main/processMainService.ts b/src/vs/platform/issue/electron-main/processMainService.ts index 9e2cc9d0fd3..76da45d897c 100644 --- a/src/vs/platform/issue/electron-main/processMainService.ts +++ b/src/vs/platform/issue/electron-main/processMainService.ts @@ -153,7 +153,12 @@ export class ProcessMainService implements IProcessMainService { windowId: this.processExplorerWindow.id, userEnv: this.userEnv, data, - product + product, + nls: { + // VSCODE_GLOBALS: NLS + messages: globalThis._VSCODE_NLS_MESSAGES, + language: globalThis._VSCODE_NLS_LANGUAGE + } }); this.processExplorerWindow.loadURL( diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index fd704a5cfe6..290b3f7a67c 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1444,6 +1444,12 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic workspace: options.workspace, userEnv: { ...this.initialUserEnv, ...options.userEnv }, + nls: { + // VSCODE_GLOBALS: NLS + messages: globalThis._VSCODE_NLS_MESSAGES, + language: globalThis._VSCODE_NLS_LANGUAGE + }, + filesToOpenOrCreate: options.filesToOpen?.filesToOpenOrCreate, filesToDiff: options.filesToOpen?.filesToDiff, filesToMerge: options.filesToOpen?.filesToMerge, diff --git a/src/vs/server/node/extensionHostConnection.ts b/src/vs/server/node/extensionHostConnection.ts index 9fbb33d3ece..f345bd69a2d 100644 --- a/src/vs/server/node/extensionHostConnection.ts +++ b/src/vs/server/node/extensionHostConnection.ts @@ -43,7 +43,7 @@ export async function buildUserEnvironment(startParamsEnv: { [key: string]: stri ...{ VSCODE_AMD_ENTRYPOINT: 'vs/workbench/api/node/extensionHostProcess', VSCODE_HANDLES_UNCAUGHT_ERRORS: 'true', - VSCODE_NLS_CONFIG: JSON.stringify(nlsConfig, undefined, 0) + VSCODE_NLS_CONFIG: JSON.stringify(nlsConfig) }, ...startParamsEnv }; diff --git a/src/vs/server/node/extensionsScannerService.ts b/src/vs/server/node/extensionsScannerService.ts index 5430ae19162..78dc3bce4f0 100644 --- a/src/vs/server/node/extensionsScannerService.ts +++ b/src/vs/server/node/extensionsScannerService.ts @@ -14,7 +14,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; -import { getNLSConfiguration, InternalNLSConfiguration } from 'vs/server/node/remoteLanguagePacks'; +import { getNLSConfiguration } from 'vs/server/node/remoteLanguagePacks'; export class ExtensionsScannerService extends AbstractExtensionsScannerService implements IExtensionsScannerService { @@ -38,9 +38,9 @@ export class ExtensionsScannerService extends AbstractExtensionsScannerService i protected async getTranslations(language: string): Promise { const config = await getNLSConfiguration(language, this.nativeEnvironmentService.userDataPath); - if (InternalNLSConfiguration.is(config)) { + if (config.languagePack) { try { - const content = await this.fileService.readFile(URI.file(config._translationsConfigFile)); + const content = await this.fileService.readFile(URI.file(config.languagePack.translationsConfigFile)); return JSON.parse(content.value.toString()); } catch (err) { /* Ignore error */ } } diff --git a/src/vs/server/node/remoteLanguagePacks.ts b/src/vs/server/node/remoteLanguagePacks.ts index 682b6f2b088..2a1ea9f5699 100644 --- a/src/vs/server/node/remoteLanguagePacks.ts +++ b/src/vs/server/node/remoteLanguagePacks.ts @@ -3,46 +3,37 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; import { FileAccess } from 'vs/base/common/network'; -import * as path from 'vs/base/common/path'; - -import * as lp from 'vs/base/node/languagePacks'; +import { join } from 'vs/base/common/path'; +import type { INLSConfiguration } from 'vs/nls'; +import { resolveNLSConfiguration } from 'vs/base/node/nls'; +import { Promises } from 'vs/base/node/pfs'; import product from 'vs/platform/product/common/product'; -const metaData = path.join(FileAccess.asFileUri('').fsPath, 'nls.metadata.json'); -const _cache: Map> = new Map(); +const nlsMetadataPath = join(FileAccess.asFileUri('').fsPath); +const defaultMessagesFile = join(nlsMetadataPath, 'nls.messages.json'); +const nlsConfigurationCache = new Map>(); -function exists(file: string) { - return new Promise(c => fs.exists(file, c)); -} +export async function getNLSConfiguration(language: string, userDataPath: string): Promise { + if (!product.commit || !(await Promises.exists(defaultMessagesFile))) { + return { + userLocale: 'en', + osLocale: 'en', + resolvedLanguage: 'en', + defaultMessagesFile, -export function getNLSConfiguration(language: string, userDataPath: string): Promise { - return exists(metaData).then((fileExists) => { - if (!fileExists || !product.commit) { - // console.log(`==> MetaData or commit unknown. Using default language.`); - // The OS Locale on the remote side really doesn't matter, so we return the default locale - return Promise.resolve({ locale: 'en', osLocale: 'en', availableLanguages: {} }); - } - const key = `${language}||${userDataPath}`; - let result = _cache.get(key); - if (!result) { - // The OS Locale on the remote side really doesn't matter, so we pass in the same language - result = lp.getNLSConfiguration(product.commit, userDataPath, metaData, language, language).then(value => { - if (InternalNLSConfiguration.is(value)) { - value._languagePackSupport = true; - } - return value; - }); - _cache.set(key, result); - } - return result; - }); -} + // NLS: below 2 are a relic from old times only used by vscode-nls and deprecated + locale: 'en', + availableLanguages: {} + }; + } -export namespace InternalNLSConfiguration { - export function is(value: lp.NLSConfiguration): value is lp.InternalNLSConfiguration { - const candidate: lp.InternalNLSConfiguration = value as lp.InternalNLSConfiguration; - return candidate && typeof candidate._languagePackId === 'string'; + const cacheKey = `${language}||${userDataPath}`; + let result = nlsConfigurationCache.get(cacheKey); + if (!result) { + result = resolveNLSConfiguration({ userLocale: language, osLocale: language, commit: product.commit, userDataPath, nlsMetadataPath }); + nlsConfigurationCache.set(cacheKey, result); } + + return result; } diff --git a/src/vs/server/node/webClientServer.ts b/src/vs/server/node/webClientServer.ts index 6dc550b6cf0..20c96da8f1f 100644 --- a/src/vs/server/node/webClientServer.ts +++ b/src/vs/server/node/webClientServer.ts @@ -338,12 +338,23 @@ export class WebClientServer { callbackRoute: this._callbackRoute }; - const nlsBaseUrl = this._productService.extensionsGallery?.nlsBaseUrl; + const cookies = cookie.parse(req.headers.cookie || ''); + const locale = cookies['vscode.nls.locale'] || req.headers['accept-language']?.split(',')[0]?.toLowerCase() || 'en'; + let WORKBENCH_NLS_BASE_URL: string | undefined; + let WORKBENCH_NLS_URL: string; + if (!locale.startsWith('en')) { + WORKBENCH_NLS_BASE_URL = `https://www.vscode-unpkg.net/nls/`; + WORKBENCH_NLS_URL = `${WORKBENCH_NLS_BASE_URL}${this._productService.commit}/${this._productService.version}/${locale}/nls.messages.js`; // TODO@bpasero make it a product.json thing + } else { + WORKBENCH_NLS_URL = ''; // fallback will apply + } + const values: { [key: string]: string } = { WORKBENCH_WEB_CONFIGURATION: asJSON(workbenchWebConfiguration), WORKBENCH_AUTH_SESSION: authSessionInfo ? asJSON(authSessionInfo) : '', WORKBENCH_WEB_BASE_URL: this._staticRoute, - WORKBENCH_NLS_BASE_URL: nlsBaseUrl ? `${nlsBaseUrl}${!nlsBaseUrl.endsWith('/') ? '/' : ''}${this._productService.commit}/${this._productService.version}/` : '', + WORKBENCH_NLS_URL, + WORKBENCH_NLS_FALLBACK_URL: `${this._staticRoute}/out/nls.messages.js` }; if (useTestResolver) { @@ -364,13 +375,13 @@ export class WebClientServer { return void res.end('Not found'); } - const webWorkerExtensionHostIframeScriptSHA = 'sha256-75NYUUvf+5++1WbfCZOV3PSWxBhONpaxwx+mkOFRv/Y='; + const webWorkerExtensionHostIframeScriptSHA = 'sha256-V28GQnL3aYxbwgpV3yW1oJ+VKKe/PBSzWntNyH8zVXA='; const cspDirectives = [ 'default-src \'self\';', 'img-src \'self\' https: data: blob:;', 'media-src \'self\';', - `script-src 'self' 'unsafe-eval' ${this._getScriptCspHashes(data).join(' ')} '${webWorkerExtensionHostIframeScriptSHA}' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html + `script-src 'self' 'unsafe-eval' ${WORKBENCH_NLS_BASE_URL ?? ''} ${this._getScriptCspHashes(data).join(' ')} '${webWorkerExtensionHostIframeScriptSHA}' ${useTestResolver ? '' : `http://${remoteAuthority}`};`, // the sha is the same as in src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html 'child-src \'self\';', `frame-src 'self' https://*.vscode-cdn.net data:;`, 'worker-src \'self\' data: blob:;', diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 0c8d2e27317..8f4dc4a6b57 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -181,6 +181,23 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost err.stack = stack; return rejectBarrier(ExtensionHostExitCode.UnexpectedError, err); } + if (event.data.type === 'vscode.bootstrap.nls') { + const factoryModuleId = 'vs/base/worker/workerMain.js'; + const baseUrl = require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); + iframe.contentWindow!.postMessage({ + type: event.data.type, + data: { + baseUrl, + workerUrl: require.toUrl(factoryModuleId), + nls: { + // VSCODE_GLOBALS: NLS + messages: globalThis._VSCODE_NLS_MESSAGES, + language: globalThis._VSCODE_NLS_LANGUAGE + } + } + }, '*'); + return; + } const { data } = event.data; if (barrier.isOpen() || !(data instanceof MessagePort)) { console.warn('UNEXPECTED message', event); diff --git a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html index 6a16dd10210..aded7faefeb 100644 --- a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html +++ b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html @@ -4,7 +4,7 @@ @@ -66,13 +66,44 @@ } function start() { + + // Before we can load the worker, we need to get the current set of NLS + // configuration into this iframe. We ask the parent window to send it + // together with the necessary information to load the worker via Blob. + + const bootstrapNlsType = 'vscode.bootstrap.nls'; + + self.onmessage = (event) => { + if (event.origin !== parentOrigin || event.data.type !== bootstrapNlsType) { + return; + } + const { data } = event.data; + createWorker(data.baseUrl, data.workerUrl, data.nls.messages, data.nls.language); + }; + + window.parent.postMessage({ + vscodeWebWorkerExtHostId, + type: bootstrapNlsType + }, '*'); + } + + function createWorker(baseUrl, workerUrl, nlsMessages, nlsLanguage) { try { - let workerUrl = '../../../../base/worker/workerMain.js'; if (globalThis.crossOriginIsolated) { workerUrl += '?vscode-coi=2'; // COEP } - const worker = new Worker(workerUrl, { name }); + const blob = new Blob([[ + `/*extensionHostWorker*/`, + `globalThis.MonacoEnvironment = { baseUrl: '${baseUrl}' };`, + // VSCODE_GLOBALS: NLS + `globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(nlsMessages)};`, + `globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(nlsLanguage)};`, + `importScripts('${workerUrl}');`, + `/*extensionHostWorker*/` + ].join('')], { type: 'application/javascript' }); + + const worker = new Worker(URL.createObjectURL(blob), { name }); worker.postMessage('vs/workbench/api/worker/extensionHostWorker'); const nestedWorkers = new Map(); diff --git a/src/vs/workbench/services/localization/browser/localeService.ts b/src/vs/workbench/services/localization/browser/localeService.ts index 0d9c51c72cf..07616fa0a2e 100644 --- a/src/vs/workbench/services/localization/browser/localeService.ts +++ b/src/vs/workbench/services/localization/browser/localeService.ts @@ -14,11 +14,69 @@ import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/ import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ILogService } from 'vs/platform/log/common/log'; +import { getCookieValue } from 'vs/base/browser/dom'; + +const localeStorage = new class LocaleStorage { + + private static readonly LOCAL_STORAGE_LOCALE_KEY = 'vscode.nls.locale'; + private static readonly LOCAL_STORAGE_EXTENSION_ID_KEY = 'vscode.nls.languagePackExtensionId'; + + constructor() { + this.migrateCookie(); // TODO@bpasero remove me eventually + } + + private migrateCookie(): void { + const localeCookieValue = getCookieValue(LocaleStorage.LOCAL_STORAGE_LOCALE_KEY); + const localeStorageValue = localStorage.getItem(LocaleStorage.LOCAL_STORAGE_LOCALE_KEY); + + if ( + (typeof localeCookieValue !== 'string' && typeof localeStorageValue !== 'string') || + (localeCookieValue === localeStorageValue) + ) { + return; // already matching + } + + if (typeof localeStorageValue === 'string') { + this.doSetLocaleToCookie(localeStorageValue); + } else { + this.doClearLocaleToCookie(); + } + } + + setLocale(locale: string): void { + localStorage.setItem(LocaleStorage.LOCAL_STORAGE_LOCALE_KEY, locale); + this.doSetLocaleToCookie(locale); + } + + private doSetLocaleToCookie(locale: string): void { + document.cookie = `${LocaleStorage.LOCAL_STORAGE_LOCALE_KEY}=${locale};path=/;max-age=3153600000`; + } + + clearLocale(): void { + localStorage.removeItem(LocaleStorage.LOCAL_STORAGE_LOCALE_KEY); + this.doClearLocaleToCookie(); + } + + private doClearLocaleToCookie(): void { + document.cookie = `${LocaleStorage.LOCAL_STORAGE_LOCALE_KEY}=;path=/;max-age=0`; + } + + setExtensionId(extensionId: string): void { + localStorage.setItem(LocaleStorage.LOCAL_STORAGE_EXTENSION_ID_KEY, extensionId); + } + + getExtensionId(): string | null { + return localStorage.getItem(LocaleStorage.LOCAL_STORAGE_EXTENSION_ID_KEY); + } + + clearExtensionId(): void { + localStorage.removeItem(LocaleStorage.LOCAL_STORAGE_EXTENSION_ID_KEY); + } +}; export class WebLocaleService implements ILocaleService { + declare readonly _serviceBrand: undefined; - static readonly _LOCAL_STORAGE_EXTENSION_ID_KEY = 'vscode.nls.languagePackExtensionId'; - static readonly _LOCAL_STORAGE_LOCALE_KEY = 'vscode.nls.locale'; constructor( @IDialogService private readonly dialogService: IDialogService, @@ -32,13 +90,13 @@ export class WebLocaleService implements ILocaleService { return; } if (locale) { - localStorage.setItem(WebLocaleService._LOCAL_STORAGE_LOCALE_KEY, locale); + localeStorage.setLocale(locale); if (languagePackItem.extensionId) { - localStorage.setItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY, languagePackItem.extensionId); + localeStorage.setExtensionId(languagePackItem.extensionId); } } else { - localStorage.removeItem(WebLocaleService._LOCAL_STORAGE_LOCALE_KEY); - localStorage.removeItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY); + localeStorage.clearLocale(); + localeStorage.clearExtensionId(); } const restartDialog = await this.dialogService.confirm({ @@ -54,8 +112,8 @@ export class WebLocaleService implements ILocaleService { } async clearLocalePreference(): Promise { - localStorage.removeItem(WebLocaleService._LOCAL_STORAGE_LOCALE_KEY); - localStorage.removeItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY); + localeStorage.clearLocale(); + localeStorage.clearExtensionId(); if (Language.value() === navigator.language.toLowerCase()) { return; @@ -87,7 +145,7 @@ class WebActiveLanguagePackService implements IActiveLanguagePackService { if (language === LANGUAGE_DEFAULT) { return undefined; } - const extensionId = localStorage.getItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY); + const extensionId = localeStorage.getExtensionId(); if (extensionId) { return extensionId; } @@ -102,7 +160,7 @@ class WebActiveLanguagePackService implements IActiveLanguagePackService { // Only install extensions that are published by Microsoft and start with vscode-language-pack for extra certainty const extensionToInstall = tagResult.firstPage.find(e => e.publisher === 'MS-CEINTL' && e.name.startsWith('vscode-language-pack')); if (extensionToInstall) { - localStorage.setItem(WebLocaleService._LOCAL_STORAGE_EXTENSION_ID_KEY, extensionToInstall.identifier.id); + localeStorage.setExtensionId(extensionToInstall.identifier.id); return extensionToInstall.identifier.id; } diff --git a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts index 2e70ca1e8b4..daa2930e27a 100644 --- a/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts +++ b/src/vs/workbench/services/workingCopy/test/electron-sandbox/workingCopyBackupService.test.ts @@ -71,6 +71,10 @@ const TestNativeWindowConfiguration: INativeWindowConfiguration = { tmpDir: tmpDir.fsPath, userDataDir: joinPath(homeDir, product.nameShort).fsPath, profiles: { profile: NULL_PROFILE, all: [NULL_PROFILE], home: homeDir }, + nls: { + messages: [], + language: 'en' + }, _: [] }; diff --git a/src/vs/workbench/workbench.desktop.main.nls.js b/src/vs/workbench/workbench.desktop.main.nls.js deleted file mode 100644 index d6a8b487eaf..00000000000 --- a/src/vs/workbench/workbench.desktop.main.nls.js +++ /dev/null @@ -1,8 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -// NOTE: THIS FILE WILL BE OVERWRITTEN DURING BUILD TIME, DO NOT EDIT - -define([], {}); \ No newline at end of file diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index ca5c357ed7f..e4a0a7e3e72 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -156,7 +156,6 @@ import 'vs/workbench/contrib/tags/browser/workspaceTagsService'; // Issues import 'vs/workbench/contrib/issue/browser/issue.contribution'; - // Splash import 'vs/workbench/contrib/splash/browser/splash.contribution'; diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index 322e5b7d510..b0a0f4ddb23 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -246,6 +246,18 @@ async function runTestsInBrowser(testModules, browserType) { await page.goto(target.href); + if (args.build) { + const nlsMessages = await fs.promises.readFile(path.join(out, 'nls.messages.json'), 'utf8'); + await page.evaluate(value => { + // when running from `out-build`, ensure to load the default + // messages file, because all `nls.localize` calls have their + // english values removed and replaced by an index. + // VSCODE_GLOBALS: NLS + // @ts-ignore + globalThis._VSCODE_NLS_MESSAGES = JSON.parse(value); + }, nlsMessages); + } + page.on('console', async msg => { consoleLogFn(msg)(msg.text(), await Promise.all(msg.args().map(async arg => await arg.jsonValue()))); }); diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js index 22fdd384eab..d4d85a18521 100644 --- a/test/unit/electron/renderer.js +++ b/test/unit/electron/renderer.js @@ -97,6 +97,16 @@ const _tests_glob = '**/test/**/*.test.js'; let loader; let _out; +function initNls(opts) { + if (opts.build) { + // when running from `out-build`, ensure to load the default + // messages file, because all `nls.localize` calls have their + // english values removed and replaced by an index. + // VSCODE_GLOBALS: NLS + globalThis._VSCODE_NLS_MESSAGES = (require.__$__nodeRequire ?? require)(`../../../out-build/nls.messages.json`); + } +} + function initLoader(opts) { const outdir = opts.build ? 'out-build' : 'out'; _out = path.join(__dirname, `../../../${outdir}`); @@ -438,6 +448,7 @@ function runTests(opts) { } ipcRenderer.on('run', (e, opts) => { + initNls(opts); initLoader(opts); runTests(opts).catch(err => { if (typeof err !== 'string') { diff --git a/test/unit/node/index.js b/test/unit/node/index.js index e876bd1e007..9f0ec662a73 100644 --- a/test/unit/node/index.js +++ b/test/unit/node/index.js @@ -84,6 +84,14 @@ function main() { globalThis._VSCODE_PRODUCT_JSON = require(`${REPO_ROOT}/product.json`); globalThis._VSCODE_PACKAGE_JSON = require(`${REPO_ROOT}/package.json`); + if (args.build) { + // when running from `out-build`, ensure to load the default + // messages file, because all `nls.localize` calls have their + // english values removed and replaced by an index. + // VSCODE_GLOBALS: NLS + globalThis._VSCODE_NLS_MESSAGES = require(`../../../${out}/nls.messages.json`); + } + // Test file operations that are common across platforms. Used for test infra, namely snapshot tests Object.assign(globalThis, { __analyzeSnapshotInTests: takeSnapshotAndCountClasses, From ed4e6eda599f37bac9a11952403a6e63fd4f5a4d Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 28 Jun 2024 11:56:38 +0200 Subject: [PATCH 0172/2222] Update grammars (#219070) --- extensions/csharp/cgmanifest.json | 2 +- .../csharp/syntaxes/csharp.tmLanguage.json | 5 +- extensions/fsharp/cgmanifest.json | 2 +- .../fsharp/syntaxes/fsharp.tmLanguage.json | 13 +- extensions/go/cgmanifest.json | 4 +- extensions/go/syntaxes/go.tmLanguage.json | 113 +++++++----------- .../latex/syntaxes/LaTeX.tmLanguage.json | 8 +- extensions/python/cgmanifest.json | 2 +- extensions/swift/cgmanifest.json | 2 +- .../swift/syntaxes/swift.tmLanguage.json | 8 +- 10 files changed, 66 insertions(+), 93 deletions(-) diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index 1c88bd17296..58a7408dbbe 100644 --- a/extensions/csharp/cgmanifest.json +++ b/extensions/csharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "dotnet/csharp-tmLanguage", "repositoryUrl": "https://github.com/dotnet/csharp-tmLanguage", - "commitHash": "7a7482ffc72a6677a87eb1ed76005593a4f7f131" + "commitHash": "d63e2661d4e0c83b6c7810eb1d0eedc5da843b04" } }, "license": "MIT", diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index 96dbe04473c..4a2497a064a 100644 --- a/extensions/csharp/syntaxes/csharp.tmLanguage.json +++ b/extensions/csharp/syntaxes/csharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/dotnet/csharp-tmLanguage/commit/7a7482ffc72a6677a87eb1ed76005593a4f7f131", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/d63e2661d4e0c83b6c7810eb1d0eedc5da843b04", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -512,6 +512,9 @@ { "include": "#type-name" }, + { + "include": "#type-arguments" + }, { "include": "#attribute-arguments" } diff --git a/extensions/fsharp/cgmanifest.json b/extensions/fsharp/cgmanifest.json index 524b3fa0d46..0b3c5d112e5 100644 --- a/extensions/fsharp/cgmanifest.json +++ b/extensions/fsharp/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "ionide/ionide-fsgrammar", "repositoryUrl": "https://github.com/ionide/ionide-fsgrammar", - "commitHash": "7d029a46f17637228b2ee85dd02e511c3e8039b3" + "commitHash": "0100f551f6c32598a58aba97344bf828673fec7a" } }, "license": "MIT", diff --git a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json index 5063f1c5210..7806c100eae 100644 --- a/extensions/fsharp/syntaxes/fsharp.tmLanguage.json +++ b/extensions/fsharp/syntaxes/fsharp.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/ionide/ionide-fsgrammar/commit/7d029a46f17637228b2ee85dd02e511c3e8039b3", + "version": "https://github.com/ionide/ionide-fsgrammar/commit/0100f551f6c32598a58aba97344bf828673fec7a", "name": "fsharp", "scopeName": "source.fsharp", "patterns": [ @@ -617,7 +617,7 @@ }, { "name": "constant.numeric.float.fsharp", - "match": "\\b-?[0-9][0-9_]*((\\.([0-9][0-9_]*([eE][+-]??[0-9][0-9_]*)?)?)|([eE][+-]??[0-9][0-9_]*))" + "match": "\\b-?[0-9][0-9_]*((\\.(?!\\.)([0-9][0-9_]*([eE][+-]??[0-9][0-9_]*)?)?)|([eE][+-]??[0-9][0-9_]*))" }, { "name": "constant.numeric.integer.nativeint.fsharp", @@ -635,7 +635,7 @@ }, "abstract_definition": { "name": "abstract.definition.fsharp", - "begin": "\\b(abstract)\\s+(member)?(\\s+\\[\\<.*\\>\\])?\\s*([_[:alpha:]0-9,\\._`\\s]+)(<)?", + "begin": "\\b(static)?\\s+(abstract)\\s+(member)?(\\s+\\[\\<.*\\>\\])?\\s*([_[:alpha:]0-9,\\._`\\s]+)(<)?", "end": "\\s*(with)\\b|=|$", "beginCaptures": { "1": { @@ -645,6 +645,9 @@ "name": "keyword.fsharp" }, "3": { + "name": "keyword.fsharp" + }, + "4": { "name": "support.function.attribute.fsharp" }, "5": { @@ -933,7 +936,7 @@ "patterns": [ { "name": "binding.fsharp", - "begin": "\\b(let mutable|static let mutable|static let|let inline|let|and|member val|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", + "begin": "\\b(let mutable|static let mutable|static let|let inline|let|and|member val|member inline|static member inline|static member|default|member|override|let!)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9\\._`\\s]+|(?<=,)\\s)*)?", "end": "\\s*((with\\b)|(=|\\n+=|(?<=\\=)))", "beginCaptures": { "1": { @@ -1008,7 +1011,7 @@ }, { "name": "binding.fsharp", - "begin": "\\b(static val mutable|val mutable|val)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", + "begin": "\\b(static val mutable|val mutable|val inline|val)(\\s+rec|mutable)?(\\s+\\[\\<.*\\>\\])?\\s*(private|internal|public)?\\s+(\\[[^-=]*\\]|[_[:alpha:]]([_[:alpha:]0-9,\\._]+)*|``[_[:alpha:]]([_[:alpha:]0-9,\\._`\\s]+|(?<=,)\\s)*)?", "end": "\\n$", "beginCaptures": { "1": { diff --git a/extensions/go/cgmanifest.json b/extensions/go/cgmanifest.json index d27352e1339..bd8f2d6105f 100644 --- a/extensions/go/cgmanifest.json +++ b/extensions/go/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "go-syntax", "repositoryUrl": "https://github.com/worlpaker/go-syntax", - "commitHash": "092c45ec9a51fe40188408d1371f123eaa4796fa" + "commitHash": "21f28840e04d4fa04682d19d6fe64de437f40b64" } }, "license": "MIT", "description": "The file syntaxes/go.tmLanguage.json is from https://github.com/worlpaker/go-syntax, which in turn was derived from https://github.com/jeff-hykin/better-go-syntax.", - "version": "0.6.8" + "version": "0.7.5" } ], "version": 1 diff --git a/extensions/go/syntaxes/go.tmLanguage.json b/extensions/go/syntaxes/go.tmLanguage.json index 21b370514d0..b8a6604de88 100644 --- a/extensions/go/syntaxes/go.tmLanguage.json +++ b/extensions/go/syntaxes/go.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/worlpaker/go-syntax/commit/092c45ec9a51fe40188408d1371f123eaa4796fa", + "version": "https://github.com/worlpaker/go-syntax/commit/21f28840e04d4fa04682d19d6fe64de437f40b64", "name": "Go", "scopeName": "source.go", "patterns": [ @@ -321,7 +321,7 @@ "name": "punctuation.definition.begin.bracket.square.go" } }, - "end": "(?:(\\])((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?!(?:[\\[\\]\\*]+)?\\b(?:func|struct|map)\\b)(?:[\\*\\[\\]]+)?(?:[\\w\\.]+))?)", + "end": "(?:(\\])((?:(?:(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?!(?:[\\[\\]\\*]+)?\\b(?:func|struct|map)\\b)(?:[\\*\\[\\]]+)?(?:[\\w\\.]+)(?:\\[(?:(?:[\\w\\.\\*\\[\\]\\{\\}]+)(?:(?:\\,\\s*(?:[\\w\\.\\*\\[\\]\\{\\}]+))*))?\\])?)?)", "endCaptures": { "1": { "name": "punctuation.definition.end.bracket.square.go" @@ -1862,7 +1862,7 @@ }, { "comment": "property variables and types", - "match": "(?:((?:(?:\\w+\\,\\s*)+)?(?:\\w+\\s+))([\\s\\S]+))", + "match": "(?:((?:(?:\\w+\\,\\s*)+)?(?:\\w+\\s+))([^\\`]+))", "captures": { "1": { "patterns": [ @@ -2007,6 +2007,29 @@ } ] }, + { + "comment": "one type only with multi line raw string", + "begin": "(?:((?:(?:\\s*(?:[\\*\\[\\]]+)?(?:\\<\\-\\s*)?\\bchan\\b(?:\\s*\\<\\-)?\\s*)+)?(?\\|\\&]+))(\\.\\(\\btype\\b\\)\\s*)(\\{)", "beginCaptures": { "1": { "patterns": [ @@ -2770,7 +2796,7 @@ }, "slice_index_variables": { "comment": "slice index and capacity variables, to not scope them as property variables", - "match": "(?<=\\w\\[)((?:(?:\\b[\\w\\.\\*\\+/\\-\\*\\%\\<\\>\\|\\&]+\\:)|(?:\\:\\b[\\w\\.\\*\\+/\\-\\*\\%\\<\\>\\|\\&]+))(?:\\b[\\w\\.\\*\\+/\\-\\*\\%\\<\\>\\|\\&]+)?(?:\\:\\b[\\w\\.\\*\\+/\\-\\*\\%\\<\\>\\|\\&]+)?)(?=\\])", + "match": "(?<=\\w\\[)((?:(?:\\b[\\w\\.\\*\\+/\\-\\%\\<\\>\\|\\&]+\\:)|(?:\\:\\b[\\w\\.\\*\\+/\\-\\%\\<\\>\\|\\&]+))(?:\\b[\\w\\.\\*\\+/\\-\\%\\<\\>\\|\\&]+)?(?:\\:\\b[\\w\\.\\*\\+/\\-\\%\\<\\>\\|\\&]+)?)(?=\\])", "captures": { "1": { "patterns": [ @@ -2786,8 +2812,8 @@ } }, "property_variables": { - "comment": "Property variables in struct | parameter field in struct initialization", - "match": "(?:(?:((?:\\b[\\w\\.]+)(?:\\:(?!\\=))))(?:(?:\\s*([\\w\\.\\*\\&\\[\\]]+)(\\.\\w+)(?![\\w\\.\\*\\&\\[\\]]*(?:\\{|\\()))((?:\\s*(?:\\<|\\>|\\<\\=|\\>\\=|\\=\\=|\\!\\=|\\|\\||\\&\\&|\\+|/|\\-|\\*|\\%|\\||\\&)\\s*(?:[\\w\\.\\*\\&\\[\\]]+)(?:\\.\\w+)(?![\\w\\.\\*\\&\\[\\]]*(?:\\{|\\()))*))?)", + "comment": "Property variables in struct", + "match": "((?:\\b[\\w\\.]+)(?:\\:(?!\\=)))", "captures": { "1": { "patterns": [ @@ -2799,68 +2825,6 @@ "name": "variable.other.property.go" } ] - }, - "2": { - "patterns": [ - { - "include": "#type-declarations" - }, - { - "match": "\\w+", - "name": "variable.other.go" - }, - { - "include": "$self" - } - ] - }, - "3": { - "patterns": [ - { - "include": "#type-declarations" - }, - { - "match": "\\w+", - "name": "variable.other.property.field.go" - }, - { - "include": "$self" - } - ] - }, - "4": { - "patterns": [ - { - "match": "([\\w\\.\\*\\&\\[\\]]+)(\\.\\w+)", - "captures": { - "1": { - "patterns": [ - { - "include": "#type-declarations" - }, - { - "match": "\\w+", - "name": "variable.other.go" - } - ] - }, - "2": { - "patterns": [ - { - "include": "#type-declarations" - }, - { - "match": "\\w+", - "name": "variable.other.property.field.go" - } - ] - } - } - }, - { - "include": "$self" - } - ] } } }, @@ -2931,13 +2895,16 @@ }, "2": { "patterns": [ - { - "include": "#type-declarations" - }, { "match": "\\binvalid\\b\\s+\\btype\\b", "name": "invalid.field.go" }, + { + "include": "#type-declarations-without-brackets" + }, + { + "include": "#parameter-variable-types" + }, { "match": "\\w+", "name": "entity.name.type.go" diff --git a/extensions/latex/syntaxes/LaTeX.tmLanguage.json b/extensions/latex/syntaxes/LaTeX.tmLanguage.json index aad6c5c4bd9..76486732195 100644 --- a/extensions/latex/syntaxes/LaTeX.tmLanguage.json +++ b/extensions/latex/syntaxes/LaTeX.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jlelong/vscode-latex-basics/commit/3d141a124a16558958e95c54267f7ca37986de6f", + "version": "https://github.com/jlelong/vscode-latex-basics/commit/9cd6bc151f4b9df5d9aeb1e39e30071018d3cb2a", "name": "LaTeX", "scopeName": "text.tex.latex", "patterns": [ @@ -2646,7 +2646,7 @@ ] }, { - "begin": "((\\\\)(?:\\w*[rR]ef\\*?))(\\{)", + "begin": "((\\\\)(?:\\w*[rR]ef\\*?))(?:\\[[^\\]]*\\])?(\\{)", "beginCaptures": { "1": { "name": "keyword.control.ref.latex" @@ -3048,7 +3048,7 @@ "name": "punctuation.definition.variable.latex" } }, - "match": "(\\\\)[cgl](?:[_\\p{Alphabetic}@]+)+_(?:bitset|clist|dim|fp|int|muskip|str|tl|bool|box|coffin|flag|fparray|intarray|ior|iow|prop|regex|seq)", + "match": "(\\\\)[cgl](?:[_\\p{Alphabetic}@]+)+_[a-z]+", "name": "variable.other.latex3.latex" }, { @@ -3148,7 +3148,7 @@ "match": "\\s*((\\\\)(?:begin|end))(\\{)([a-zA-Z]*\\*?)(\\})(?:(\\[)([^\\]]*)(\\])){,2}(?:(\\{)([^{}]*)(\\}))?" }, "definition-label": { - "begin": "((\\\\)label)((?:\\[[^\\[]*?\\])*)(\\{)", + "begin": "((\\\\)z?label)((?:\\[[^\\[]*?\\])*)(\\{)", "beginCaptures": { "1": { "name": "keyword.control.label.latex" diff --git a/extensions/python/cgmanifest.json b/extensions/python/cgmanifest.json index 37a21b2de54..ace7056c995 100644 --- a/extensions/python/cgmanifest.json +++ b/extensions/python/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "MagicStack/MagicPython", "repositoryUrl": "https://github.com/MagicStack/MagicPython", - "commitHash": "c9b3409deb69acec31bbf7913830e93a046b30cc" + "commitHash": "7d0f2b22a5ad8fccbd7341bc7b7a715169283044" } }, "license": "MIT", diff --git a/extensions/swift/cgmanifest.json b/extensions/swift/cgmanifest.json index 816621e4170..cb1ca02310f 100644 --- a/extensions/swift/cgmanifest.json +++ b/extensions/swift/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "jtbandes/swift-tmlanguage", "repositoryUrl": "https://github.com/jtbandes/swift-tmlanguage", - "commitHash": "ab893c684dd7eeb7c249139e29e931334316fda7" + "commitHash": "860eface4241cf9f2174d5fa690bd34389ac8d26" } }, "license": "MIT" diff --git a/extensions/swift/syntaxes/swift.tmLanguage.json b/extensions/swift/syntaxes/swift.tmLanguage.json index 6259b151369..b18b340f2c6 100644 --- a/extensions/swift/syntaxes/swift.tmLanguage.json +++ b/extensions/swift/syntaxes/swift.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/jtbandes/swift-tmlanguage/commit/ab893c684dd7eeb7c249139e29e931334316fda7", + "version": "https://github.com/jtbandes/swift-tmlanguage/commit/860eface4241cf9f2174d5fa690bd34389ac8d26", "name": "Swift", "scopeName": "source.swift", "comment": "See swift.tmbundle/grammar-test.swift for test cases.", @@ -52,7 +52,7 @@ }, "patterns": [ { - "match": "\\b(swift|(?:iOS|macOS|OSX|watchOS|tvOS|UIKitForMac)(?:ApplicationExtension)?)\\b(?:\\s+([0-9]+(?:\\.[0-9]+)*\\b))?", + "match": "\\b(swift|(?:iOS|macOS|OSX|watchOS|tvOS|visionOS|UIKitForMac)(?:ApplicationExtension)?)\\b(?:\\s+([0-9]+(?:\\.[0-9]+)*\\b))?", "captures": { "1": { "name": "keyword.other.platform.os.swift" @@ -580,7 +580,7 @@ } }, { - "match": "\\b(os)\\s*(\\()\\s*(?:(macOS|OSX|iOS|tvOS|watchOS|Android|Linux|FreeBSD|Windows|PS4)|\\w+)\\s*(\\))", + "match": "\\b(os)\\s*(\\()\\s*(?:(macOS|OSX|iOS|tvOS|watchOS|visionOS|Android|Linux|FreeBSD|Windows|PS4)|\\w+)\\s*(\\))", "captures": { "1": { "name": "keyword.other.condition.swift" @@ -2586,7 +2586,7 @@ }, "patterns": [ { - "match": "\\s*\\b((?:iOS|macOS|OSX|watchOS|tvOS|UIKitForMac)(?:ApplicationExtension)?)\\b(?:\\s+([0-9]+(?:\\.[0-9]+)*\\b))", + "match": "\\s*\\b((?:iOS|macOS|OSX|watchOS|tvOS|visionOS|UIKitForMac)(?:ApplicationExtension)?)\\b(?:\\s+([0-9]+(?:\\.[0-9]+)*\\b))", "captures": { "1": { "name": "keyword.other.platform.os.swift" From d0db6d013faef7943da972136e38e5323337a152 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Jun 2024 14:46:05 +0200 Subject: [PATCH 0173/2222] fix https://github.com/microsoft/vscode/issues/218692 (#219078) --- build/gulpfile.vscode.web.js | 1 - 1 file changed, 1 deletion(-) diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 50c7e6fb631..8fb431e94f9 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -70,7 +70,6 @@ const vscodeWebEntryPoints = [ buildfile.workerNotebook, buildfile.workerLanguageDetection, buildfile.workerLocalFileSearch, - buildfile.workerProfileAnalysis, buildfile.keyboardMaps, buildfile.workbenchWeb ].flat(); From 388469b7b4bfbf0440761feff63eee3b37aeb9c6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Jun 2024 14:48:54 +0200 Subject: [PATCH 0174/2222] debt - CSS cleanup, accessibile diff viewer padding (#219102) * remove `InlineChatWidget#updateProgress` because the chat widget already shows progress * debt - CSS cleanup, accessibile diff viewer padding --- .../inlineChat/browser/inlineChatWidget.ts | 29 +--- .../inlineChat/browser/media/inlineChat.css | 124 +----------------- .../controller/chat/notebookChatController.ts | 1 - .../chat/browser/terminalChatController.ts | 2 - .../chat/browser/terminalChatWidget.ts | 6 - 5 files changed, 9 insertions(+), 153 deletions(-) diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index fad3466eefe..fe4509dabee 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -5,7 +5,6 @@ import { Dimension, getActiveElement, getTotalHeight, h, reset, trackFocus } from 'vs/base/browser/dom'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { ProgressBar } from 'vs/base/browser/ui/progressbar/progressbar'; import { Emitter, Event } from 'vs/base/common/event'; import { IMarkdownString, MarkdownString } from 'vs/base/common/htmlContent'; import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; @@ -87,8 +86,6 @@ export class InlineChatWidget { 'div.inline-chat@root', [ h('div.chat-widget@chatWidget'), - h('div.progress@progress'), - h('div.previewDiff.hidden@previewDiff'), h('div.accessibleViewer@accessibleViewer'), h('div.status@status', [ h('div.label.info.hidden@infoLabel'), @@ -105,7 +102,6 @@ export class InlineChatWidget { private readonly _ctxInputEditorFocused: IContextKey; private readonly _ctxResponseFocused: IContextKey; - private readonly _progressBar: ProgressBar; private readonly _chatWidget: ChatWidget; protected readonly _onDidChangeHeight = this._store.add(new Emitter()); @@ -131,10 +127,6 @@ export class InlineChatWidget { @IChatService private readonly _chatService: IChatService, @IHoverService private readonly _hoverService: IHoverService, ) { - // toolbars - this._progressBar = new ProgressBar(this._elements.progress); - this._store.add(this._progressBar); - this.scopedContextKeyService = this._store.add(_contextKeyService.createScoped(this._elements.chatWidget)); const scopedInstaService = _instantiationService.createChild( new ServiceCollection([ @@ -296,17 +288,15 @@ export class InlineChatWidget { protected _doLayout(dimension: Dimension): void { const extraHeight = this._getExtraHeight(); - const progressHeight = getTotalHeight(this._elements.progress); const statusHeight = getTotalHeight(this._elements.status); // console.log('ZONE#Widget#layout', { height: dimension.height, extraHeight, progressHeight, followUpsHeight, statusHeight, LIST: dimension.height - progressHeight - followUpsHeight - statusHeight - extraHeight }); this._elements.root.style.height = `${dimension.height - extraHeight}px`; this._elements.root.style.width = `${dimension.width}px`; - this._elements.progress.style.width = `${dimension.width}px`; this._chatWidget.layout( - dimension.height - progressHeight - statusHeight - extraHeight, + dimension.height - statusHeight - extraHeight, dimension.width ); } @@ -317,11 +307,10 @@ export class InlineChatWidget { get contentHeight(): number { const data = { chatWidgetContentHeight: this._chatWidget.contentHeight, - progressHeight: getTotalHeight(this._elements.progress), statusHeight: getTotalHeight(this._elements.status), extraHeight: this._getExtraHeight() }; - const result = data.progressHeight + data.chatWidgetContentHeight + data.statusHeight + data.extraHeight; + const result = data.chatWidgetContentHeight + data.statusHeight + data.extraHeight; return result; } @@ -347,16 +336,6 @@ export class InlineChatWidget { return 4 /* padding */ + 2 /*border*/ + 4 /*shadow*/; } - updateProgress(show: boolean) { - if (show) { - this._progressBar.show(); - this._progressBar.infinite(); - } else { - this._progressBar.stop(); - this._progressBar.hide(); - } - } - get value(): string { return this._chatWidget.getInput(); } @@ -565,7 +544,7 @@ export class EditorBasedInlineChatWidget extends InlineChatWidget { let result = super.contentHeight; if (this._accessibleViewer.value) { - result += this._accessibleViewer.value.height; + result += this._accessibleViewer.value.height + 8 /* padding */; } return result; @@ -577,7 +556,7 @@ export class EditorBasedInlineChatWidget extends InlineChatWidget { if (this._accessibleViewer.value) { this._accessibleViewer.value.width = dimension.width - 12; - newHeight -= this._accessibleViewer.value.height; + newHeight -= this._accessibleViewer.value.height + 8; } super._doLayout(dimension.with(undefined, newHeight)); diff --git a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css index 279fea6e71b..a6b855ff046 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css +++ b/src/vs/workbench/contrib/inlineChat/browser/media/inlineChat.css @@ -62,16 +62,6 @@ right: 10px; } -/* progress bit */ - -.monaco-workbench .inline-chat .progress { - position: relative; -} - -/* UGLY - fighting against workbench styles */ -.monaco-workbench .part.editor > .content .inline-chat .progress .monaco-progress-container { - top: 0; -} /* status */ @@ -121,37 +111,6 @@ line-height: 18px; } -.monaco-workbench .inline-chat .chatMessage .chatMessageContent .value { - overflow: hidden; - -webkit-user-select: text; - user-select: text; -} - -.monaco-workbench .inline-chat .followUps { - padding: 5px 5px; -} - -.monaco-workbench .inline-chat .followUps .interactive-session-followups .monaco-button { - display: block; - color: var(--vscode-textLink-foreground); - font-size: 12px; -} - -.monaco-workbench .inline-chat .followUps.hidden { - display: none; -} - -.monaco-workbench .inline-chat .chatMessage { - padding: 0 3px; -} - -.monaco-workbench .inline-chat .chatMessage .chatMessageContent { - padding: 2px 2px; -} - -.monaco-workbench .inline-chat .chatMessage.hidden { - display: none; -} .monaco-workbench .inline-chat .status .actions, .monaco-workbench .inline-chat-content-widget .toolbar { @@ -241,45 +200,17 @@ background-color: var(--vscode-button-hoverBackground); } -/* preview */ +/* accessible diff viewer */ -.monaco-workbench .inline-chat .preview { - display: none; +.monaco-workbench .inline-chat .diff-review { + padding: 4px 6px; + background-color: unset; } -.monaco-workbench .inline-chat .previewDiff, -.monaco-workbench .inline-chat .previewCreate { - display: inherit; - border: 1px solid var(--vscode-inlineChat-border); - border-radius: 2px; - margin: 6px 0px; -} - -.monaco-workbench .inline-chat .previewCreateTitle { - padding-top: 6px; -} - -.monaco-workbench .inline-chat .diff-review.hidden, -.monaco-workbench .inline-chat .previewDiff.hidden, -.monaco-workbench .inline-chat .previewCreate.hidden, -.monaco-workbench .inline-chat .previewCreateTitle.hidden { +.monaco-workbench .inline-chat .diff-review.hidden { display: none; } -.monaco-workbench .inline-chat-toolbar { - display: flex; -} - -.monaco-workbench .inline-chat-toolbar > .monaco-button { - margin-right: 6px; -} - -.monaco-workbench .inline-chat-toolbar .action-label.checked { - color: var(--vscode-inputOption-activeForeground); - background-color: var(--vscode-inputOption-activeBackground); - outline: 1px solid var(--vscode-inputOption-activeBorder); -} - /* decoration styles */ .monaco-workbench .inline-chat-inserted-range { @@ -303,51 +234,6 @@ background-color: var(--vscode-inlineChat-regionHighlight); } -.monaco-workbench .interactive-session .interactive-input-and-execute-toolbar .monaco-editor .inline-chat-slash-command { - background-color: var(--vscode-chat-slashCommandBackground); - color: var(--vscode-chat-slashCommandForeground); /* Overrides the foreground color rule in chat.css */ - border-radius: 2px; - padding: 1px; -} - -.monaco-workbench .inline-chat-slash-command-detail { - opacity: 0.5; -} - -/* diff zone */ - -.monaco-workbench .inline-chat-diff-widget .monaco-diff-editor .monaco-editor-background, -.monaco-workbench .inline-chat-diff-widget .monaco-diff-editor .monaco-workbench .margin-view-overlays { - background-color: var(--vscode-inlineChat-regionHighlight); -} - -/* create zone */ - -.monaco-workbench .inline-chat-newfile-widget { - background-color: var(--vscode-inlineChat-regionHighlight); -} - -.monaco-workbench .inline-chat-newfile-widget .title { - display: flex; - align-items: center; - justify-content: space-between; -} - -.monaco-workbench .inline-chat-newfile-widget .title .detail { - margin-left: 4px; -} - -.monaco-workbench .inline-chat-newfile-widget .buttonbar-widget { - display: flex; - margin-left: auto; - margin-right: 8px; -} - -.monaco-workbench .inline-chat-newfile-widget .buttonbar-widget > .monaco-button { - display: inline-flex; - white-space: nowrap; - margin-left: 4px; -} /* gutter decoration */ diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts index fc164d21321..d9eee2ec600 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts @@ -665,7 +665,6 @@ export class NotebookChatController extends Disposable implements INotebookEdito store.dispose(); this._ctxHasActiveRequest.set(false); - this._widget.inlineChatWidget.updateProgress(false); this._widget.inlineChatWidget.updateInfo(''); this._widget.inlineChatWidget.updateToolbar(true); } diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts index fb9ded6f870..005fdf6ca7e 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatController.ts @@ -227,7 +227,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr if (response) { store.add(response.onDidChange(async () => { responseContent += response.response.value; - this._chatWidget?.value.inlineChatWidget.updateProgress(true); if (response.isCanceled) { this._requestActiveContextKey.set(false); responsePromise.complete(undefined); @@ -243,7 +242,6 @@ export class TerminalChatController extends Disposable implements ITerminalContr this._responseContainsCodeBlockContextKey.set(!!firstCodeBlock); this._responseContainsMulitpleCodeBlocksContextKey.set(!!secondCodeBlock); this._chatWidget?.value.inlineChatWidget.updateToolbar(true); - this._chatWidget?.value.inlineChatWidget.updateProgress(false); responsePromise.complete(response); } })); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 33c01afbb63..61d1b7147f6 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -13,7 +13,6 @@ import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { IChatProgress } from 'vs/workbench/contrib/chat/common/chatService'; import { InlineChatWidget } from 'vs/workbench/contrib/inlineChat/browser/inlineChatWidget'; import { ITerminalInstance, type IXtermTerminal } from 'vs/workbench/contrib/terminal/browser/terminal'; import { MENU_TERMINAL_CHAT_INPUT, MENU_TERMINAL_CHAT_WIDGET, MENU_TERMINAL_CHAT_WIDGET_STATUS, TerminalChatCommandId, TerminalChatContextKeys } from 'vs/workbench/contrib/terminalContrib/chat/browser/terminalChat'; @@ -199,7 +198,6 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget.reset(); this._reset(); this._inlineChatWidget.updateChatMessage(undefined); - this._inlineChatWidget.updateProgress(false); this._inlineChatWidget.updateToolbar(false); this._visibleContextKey.set(false); this._inlineChatWidget.value = ''; @@ -240,10 +238,6 @@ export class TerminalChatWidget extends Disposable { this._instance.runCommand(code, shouldExecute); this.hide(); } - - updateProgress(progress?: IChatProgress): void { - this._inlineChatWidget.updateProgress(progress?.kind === 'markdownContent'); - } public get focusTracker(): IFocusTracker { return this._focusTracker; } From ff1b0028adb49cc14524f68bf170fff8759a8769 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 28 Jun 2024 14:49:35 +0200 Subject: [PATCH 0175/2222] =?UTF-8?q?SCM=20-=20=F0=9F=92=84=20remove=20cus?= =?UTF-8?q?tom=20observable=20logging=20(#219101)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/vs/workbench/api/browser/mainThreadSCM.ts | 10 +++----- src/vs/workbench/api/common/extHostSCM.ts | 5 +--- .../workbench/contrib/scm/browser/activity.ts | 24 ------------------- 3 files changed, 4 insertions(+), 35 deletions(-) diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 5ae48f45106..af9d3f401cf 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -27,7 +27,6 @@ import { IModelService } from 'vs/editor/common/services/model'; import { ITextModelContentProvider, ITextModelService } from 'vs/editor/common/services/resolverService'; import { Schemas } from 'vs/base/common/network'; import { ITextModel } from 'vs/editor/common/model'; -import { ILogService } from 'vs/platform/log/common/log'; function getIconFromIconDto(iconDto?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon): URI | { light: URI; dark: URI } | ThemeIcon | undefined { if (iconDto === undefined) { @@ -283,8 +282,7 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { private readonly _inputBoxTextModel: ITextModel, private readonly _quickDiffService: IQuickDiffService, private readonly _uriIdentService: IUriIdentityService, - private readonly _workspaceContextService: IWorkspaceContextService, - private readonly _logService: ILogService + private readonly _workspaceContextService: IWorkspaceContextService ) { if (_rootUri) { const folder = this._workspaceContextService.getWorkspaceFolder(_rootUri); @@ -309,7 +307,6 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { } if (typeof features.statusBarCommands !== 'undefined') { - this._logService.trace(`MainThreadSCMProvider#updateSourceControl (${this._id}): ${features.statusBarCommands.map(c => c.title).join(', ')}`); this._statusBarCommands.set(features.statusBarCommands, undefined); } @@ -490,8 +487,7 @@ export class MainThreadSCM implements MainThreadSCMShape { @ITextModelService private readonly textModelService: ITextModelService, @IQuickDiffService private readonly quickDiffService: IQuickDiffService, @IUriIdentityService private readonly _uriIdentService: IUriIdentityService, - @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, - @ILogService private readonly logService: ILogService + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSCM); @@ -512,7 +508,7 @@ export class MainThreadSCM implements MainThreadSCMShape { this._repositoryBarriers.set(handle, new Barrier()); const inputBoxTextModelRef = await this.textModelService.createModelReference(URI.revive(inputBoxDocumentUri)); - const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined, inputBoxTextModelRef.object.textEditorModel, this.quickDiffService, this._uriIdentService, this.workspaceContextService, this.logService); + const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined, inputBoxTextModelRef.object.textEditorModel, this.quickDiffService, this._uriIdentService, this.workspaceContextService); const repository = this.scmService.registerSCMProvider(provider); this._repositories.set(handle, repository); diff --git a/src/vs/workbench/api/common/extHostSCM.ts b/src/vs/workbench/api/common/extHostSCM.ts index 6ce319f7334..46f25cb7dd2 100644 --- a/src/vs/workbench/api/common/extHostSCM.ts +++ b/src/vs/workbench/api/common/extHostSCM.ts @@ -654,9 +654,7 @@ class ExtHostSourceControl implements vscode.SourceControl { } set statusBarCommands(statusBarCommands: vscode.Command[] | undefined) { - this.logService.trace('ExtHostSourceControl#statusBarCommands', (statusBarCommands ?? []).map(c => c.command).join(', ')); if (this._statusBarCommands && statusBarCommands && commandListEquals(this._statusBarCommands, statusBarCommands)) { - this.logService.trace('ExtHostSourceControl#statusBarCommands are equal'); return; } @@ -684,7 +682,6 @@ class ExtHostSourceControl implements vscode.SourceControl { _extHostDocuments: ExtHostDocuments, proxy: MainThreadSCMShape, private _commands: ExtHostCommands, - private readonly logService: ILogService, private _id: string, private _label: string, private _rootUri?: vscode.Uri @@ -864,7 +861,7 @@ export class ExtHostSCM implements ExtHostSCMShape { }); const handle = ExtHostSCM._handlePool++; - const sourceControl = new ExtHostSourceControl(extension, this._extHostDocuments, this._proxy, this._commands, this.logService, id, label, rootUri); + const sourceControl = new ExtHostSourceControl(extension, this._extHostDocuments, this._proxy, this._commands, id, label, rootUri); this._sourceControls.set(handle, sourceControl); const sourceControls = this._sourceControlsByExtension.get(extension.identifier) || []; diff --git a/src/vs/workbench/contrib/scm/browser/activity.ts b/src/vs/workbench/contrib/scm/browser/activity.ts index 718c1475233..de21368d9b9 100644 --- a/src/vs/workbench/contrib/scm/browser/activity.ts +++ b/src/vs/workbench/contrib/scm/browser/activity.ts @@ -27,7 +27,6 @@ import { observableConfigValue } from 'vs/platform/observable/common/platformObs import { derivedObservableWithCache, latestChangedValue, observableFromEventOpts } from 'vs/base/common/observableInternal/utils'; import { Command } from 'vs/editor/common/languages'; import { ISCMHistoryItemGroup } from 'vs/workbench/contrib/scm/common/history'; -import { ILogService } from 'vs/platform/log/common/log'; export class SCMActiveRepositoryController extends Disposable implements IWorkbenchContribution { private readonly _countBadgeConfig = observableConfigValue<'all' | 'focused' | 'off'>('scm.countBadge', 'all', this.configurationService); @@ -49,13 +48,11 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe private readonly _activeEditorRepository = derivedObservableWithCache(this, (reader, lastValue) => { const activeResource = EditorResourceAccessor.getOriginalUri(this._activeEditor.read(reader)); if (!activeResource) { - this.logService.trace('SCMActiveRepositoryController (activeEditorRepository derived): no activeResource'); return lastValue; } const repository = this.scmService.getRepository(activeResource); if (!repository) { - this.logService.trace(`SCMActiveRepositoryController (activeEditorRepository derived): no repository for '${activeResource.toString()}'`); return lastValue; } @@ -106,7 +103,6 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe @IConfigurationService private readonly configurationService: IConfigurationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IEditorService private readonly editorService: IEditorService, - @ILogService private readonly logService: ILogService, @ISCMService private readonly scmService: ISCMService, @ISCMViewService private readonly scmViewService: ISCMViewService, @IStatusbarService private readonly statusbarService: IStatusbarService, @@ -122,22 +118,6 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe { name: 'activeRepositoryBranchName', contextKey: ActiveRepositoryContextKeys.ActiveRepositoryBranchName.key, } ]); - this._register(autorun(reader => { - const repository = this._focusedRepository.read(reader); - const commands = repository?.provider.statusBarCommands.read(reader); - - this.logService.trace('SCMActiveRepositoryController (focusedRepository):', repository?.id ?? 'no id'); - this.logService.trace('SCMActiveRepositoryController (focusedRepository):', commands ? commands.map(c => c.title).join(', ') : 'no commands'); - })); - - this._register(autorun(reader => { - const repository = this._activeEditorRepository.read(reader); - const commands = repository?.provider.statusBarCommands.read(reader); - - this.logService.trace('SCMActiveRepositoryController (activeEditorRepository):', repository?.id ?? 'no id'); - this.logService.trace('SCMActiveRepositoryController (activeEditorRepository):', commands ? commands.map(c => c.title).join(', ') : 'no commands'); - })); - this._register(autorunWithStore((reader, store) => { this._updateActivityCountBadge(this._countBadge.read(reader), store); })); @@ -146,9 +126,6 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe const repository = this._activeRepository.read(reader); const commands = repository?.provider.statusBarCommands.read(reader); - this.logService.trace('SCMActiveRepositoryController (status bar):', repository?.id ?? 'no id'); - this.logService.trace('SCMActiveRepositoryController (status bar):', commands ? commands.map(c => c.title).join(', ') : 'no commands'); - this._updateStatusBar(repository, commands ?? [], store); })); @@ -175,7 +152,6 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe private _updateStatusBar(repository: ISCMRepository | undefined, commands: readonly Command[], store: DisposableStore): void { if (!repository) { - this.logService.trace('SCMActiveRepositoryController (status bar): repository is undefined'); return; } From 2014f1ddb28add8a3f07e80c8aa2961778f53b84 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 28 Jun 2024 15:45:52 +0200 Subject: [PATCH 0176/2222] nls - removal of loader plugin (#219098) --- .eslintrc.json | 2 +- build/gulpfile.editor.js | 10 ++--- build/gulpfile.reh.js | 10 ++--- build/lib/bundle.js | 5 --- build/lib/bundle.ts | 5 --- build/lib/mangle/index.js | 1 - build/lib/mangle/index.ts | 1 - build/lib/optimize.js | 7 +--- build/lib/optimize.ts | 8 +--- build/lib/standalone.js | 5 +-- build/lib/standalone.ts | 5 +-- src/buildfile.js | 6 +-- src/vs/nls.build.ts | 88 --------------------------------------- src/vs/nls.mock.ts | 43 ------------------- 14 files changed, 18 insertions(+), 178 deletions(-) delete mode 100644 src/vs/nls.build.ts delete mode 100644 src/vs/nls.mock.ts diff --git a/.eslintrc.json b/.eslintrc.json index 5e0a792f492..db383e0b9b4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1016,7 +1016,7 @@ ] }, { - "target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.ts,nls.build.ts,nls.mock.ts}", + "target": "src/vs/{loader.d.ts,css.ts,css.build.ts,monaco.d.ts,nls.ts}", "restrictions": [] }, { diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index a02456e7a2e..b585fa665c1 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -29,19 +29,17 @@ const editorEntryPoints = [ { name: 'vs/editor/editor.main', include: [], - exclude: ['vs/css', 'vs/nls'], + exclude: ['vs/css'], prepend: [ - { path: 'out-editor-build/vs/css.js', amdModuleId: 'vs/css' }, - { path: 'out-editor-build/vs/nls.js', amdModuleId: 'vs/nls' } + { path: 'out-editor-build/vs/css.js', amdModuleId: 'vs/css' } ], }, { name: 'vs/base/common/worker/simpleWorker', include: ['vs/editor/common/services/editorSimpleWorker'], - exclude: ['vs/nls'], + exclude: [], prepend: [ { path: 'vs/loader.js' }, - { path: 'vs/nls.js', amdModuleId: 'vs/nls' }, { path: 'vs/base/worker/workerMain.js' } ], dest: 'vs/base/worker/workerMain.js' @@ -99,7 +97,6 @@ const optimizeEditorAMDTask = task.define('optimize-editor-amd', optimize.optimi paths: { 'vs': 'out-editor-build/vs', 'vs/css': 'out-editor-build/vs/css.build', - 'vs/nls': 'out-editor-build/vs/nls.build', 'vscode': 'empty:' } }, @@ -124,7 +121,6 @@ const createESMSourcesAndResourcesTask = task.define('extract-editor-esm', () => 'vs/base/worker/workerMain.ts', ], renames: { - 'vs/nls.mock.ts': 'vs/nls.ts' } }); }); diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index ff2bf482a8d..c6e8699d5bd 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -95,23 +95,23 @@ const serverWithWebResources = [ const serverEntryPoints = [ { name: 'vs/server/node/server.main', - exclude: ['vs/css', 'vs/nls'] + exclude: ['vs/css'] }, { name: 'vs/server/node/server.cli', - exclude: ['vs/css', 'vs/nls'] + exclude: ['vs/css'] }, { name: 'vs/workbench/api/node/extensionHostProcess', - exclude: ['vs/css', 'vs/nls'] + exclude: ['vs/css'] }, { name: 'vs/platform/files/node/watcher/watcherMain', - exclude: ['vs/css', 'vs/nls'] + exclude: ['vs/css'] }, { name: 'vs/platform/terminal/node/ptyHostMain', - exclude: ['vs/css', 'vs/nls'] + exclude: ['vs/css'] } ]; diff --git a/build/lib/bundle.js b/build/lib/bundle.js index 1a2f870991f..a0989638f7c 100644 --- a/build/lib/bundle.js +++ b/build/lib/bundle.js @@ -36,14 +36,10 @@ function bundle(entryPoints, config, callback) { const loader = loaderModule.exports; config.isBuild = true; config.paths = config.paths || {}; - if (!config.paths['vs/nls']) { - config.paths['vs/nls'] = 'out-build/vs/nls.build'; - } if (!config.paths['vs/css']) { config.paths['vs/css'] = 'out-build/vs/css.build'; } config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; - config.buildForceInvokeFactory['vs/nls'] = true; config.buildForceInvokeFactory['vs/css'] = true; loader.config(config); loader(['require'], (localRequire) => { @@ -53,7 +49,6 @@ function bundle(entryPoints, config, callback) { r += '.js'; } // avoid packaging the build version of plugins: - r = r.replace('vs/nls.build.js', 'vs/nls.js'); r = r.replace('vs/css.build.js', 'vs/css.js'); return { path: r, amdModuleId: entry.amdModuleId }; }; diff --git a/build/lib/bundle.ts b/build/lib/bundle.ts index 2efa081b471..692100ff515 100644 --- a/build/lib/bundle.ts +++ b/build/lib/bundle.ts @@ -138,14 +138,10 @@ export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callba const loader: any = loaderModule.exports; config.isBuild = true; config.paths = config.paths || {}; - if (!config.paths['vs/nls']) { - config.paths['vs/nls'] = 'out-build/vs/nls.build'; - } if (!config.paths['vs/css']) { config.paths['vs/css'] = 'out-build/vs/css.build'; } config.buildForceInvokeFactory = config.buildForceInvokeFactory || {}; - config.buildForceInvokeFactory['vs/nls'] = true; config.buildForceInvokeFactory['vs/css'] = true; loader.config(config); @@ -156,7 +152,6 @@ export function bundle(entryPoints: IEntryPoint[], config: ILoaderConfig, callba r += '.js'; } // avoid packaging the build version of plugins: - r = r.replace('vs/nls.build.js', 'vs/nls.js'); r = r.replace('vs/css.build.js', 'vs/css.js'); return { path: r, amdModuleId: entry.amdModuleId }; }; diff --git a/build/lib/mangle/index.js b/build/lib/mangle/index.js index bb6b414e845..10e1aeb0d5a 100644 --- a/build/lib/mangle/index.js +++ b/build/lib/mangle/index.js @@ -250,7 +250,6 @@ function isNameTakenInFile(node, name) { const skippedExportMangledFiles = [ // Build 'css.build', - 'nls.build', // Monaco 'editorCommon', 'editorOptions', diff --git a/build/lib/mangle/index.ts b/build/lib/mangle/index.ts index 4a7544f162b..a148c4dd637 100644 --- a/build/lib/mangle/index.ts +++ b/build/lib/mangle/index.ts @@ -283,7 +283,6 @@ function isNameTakenInFile(node: ts.Node, name: string): boolean { const skippedExportMangledFiles = [ // Build 'css.build', - 'nls.build', // Monaco 'editorCommon', diff --git a/build/lib/optimize.js b/build/lib/optimize.js index 83527762f73..79509e9a2d5 100644 --- a/build/lib/optimize.js +++ b/build/lib/optimize.js @@ -53,7 +53,7 @@ function loaderPlugin(src, base, amdModuleId) { function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { let loaderStream = gulp.src(`${src}/vs/loader.js`, { base: `${src}` }); if (bundleLoader) { - loaderStream = es.merge(loaderStream, loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls')); + loaderStream = es.merge(loaderStream, loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css')); } const files = []; const order = (f) => { @@ -63,10 +63,7 @@ function loader(src, bundledFileHeader, bundleLoader, externalLoaderInfo) { if (f.path.endsWith('css.js')) { return 1; } - if (f.path.endsWith('nls.js')) { - return 2; - } - return 3; + return 2; }; return (loaderStream .pipe(es.through(function (data) { diff --git a/build/lib/optimize.ts b/build/lib/optimize.ts index cd269a0f02b..6f9786b4d1e 100644 --- a/build/lib/optimize.ts +++ b/build/lib/optimize.ts @@ -60,8 +60,7 @@ function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, e if (bundleLoader) { loaderStream = es.merge( loaderStream, - loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css'), - loaderPlugin(`${src}/vs/nls.js`, `${src}`, 'vs/nls'), + loaderPlugin(`${src}/vs/css.js`, `${src}`, 'vs/css') ); } @@ -73,10 +72,7 @@ function loader(src: string, bundledFileHeader: string, bundleLoader: boolean, e if (f.path.endsWith('css.js')) { return 1; } - if (f.path.endsWith('nls.js')) { - return 2; - } - return 3; + return 2; }; return ( diff --git a/build/lib/standalone.js b/build/lib/standalone.js index dbc47db0833..cf0e452aff3 100644 --- a/build/lib/standalone.js +++ b/build/lib/standalone.js @@ -106,10 +106,7 @@ function extractEditor(options) { 'vs/css.build.ts', 'vs/css.ts', 'vs/loader.js', - 'vs/loader.d.ts', - 'vs/nls.build.ts', - 'vs/nls.ts', - 'vs/nls.mock.ts', + 'vs/loader.d.ts' ].forEach(copyFile); } function createESMSourcesAndResources2(options) { diff --git a/build/lib/standalone.ts b/build/lib/standalone.ts index 775a1be5996..9a65bfa7444 100644 --- a/build/lib/standalone.ts +++ b/build/lib/standalone.ts @@ -118,10 +118,7 @@ export function extractEditor(options: tss.ITreeShakingOptions & { destRoot: str 'vs/css.build.ts', 'vs/css.ts', 'vs/loader.js', - 'vs/loader.d.ts', - 'vs/nls.build.ts', - 'vs/nls.ts', - 'vs/nls.mock.ts', + 'vs/loader.d.ts' ].forEach(copyFile); } diff --git a/src/buildfile.js b/src/buildfile.js index 9898cd44d1f..cfe4b389e55 100644 --- a/src/buildfile.js +++ b/src/buildfile.js @@ -9,7 +9,7 @@ */ function createModuleDescription(name, exclude) { - let excludes = ['vs/css', 'vs/nls']; + let excludes = ['vs/css']; if (Array.isArray(exclude) && exclude.length > 0) { excludes = excludes.concat(exclude); } @@ -32,7 +32,7 @@ exports.base = [ { name: 'vs/editor/common/services/editorSimpleWorker', include: ['vs/base/common/worker/simpleWorker'], - exclude: ['vs/nls'], + exclude: [], prepend: [ { path: 'vs/loader.js' }, { path: 'vs/base/worker/workerMain.js' } @@ -41,7 +41,7 @@ exports.base = [ }, { name: 'vs/base/common/worker/simpleWorker', - exclude: ['vs/nls'], + exclude: [], } ]; diff --git a/src/vs/nls.build.ts b/src/vs/nls.build.ts deleted file mode 100644 index 05c063fa291..00000000000 --- a/src/vs/nls.build.ts +++ /dev/null @@ -1,88 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -const buildMap: { [name: string]: string[] } = {}; -const buildMapKeys: { [name: string]: string[] } = {}; -const entryPoints: { [entryPoint: string]: string[] } = {}; - -export interface ILocalizeInfo { - key: string; - comment: string[]; -} - -export function localize(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): string { - throw new Error(`Not supported at build time!`); -} - -export function localize2(data: ILocalizeInfo | string, message: string, ...args: (string | number | boolean | undefined | null)[]): never { - throw new Error(`Not supported at build time!`); -} - -export function getConfiguredDefaultLocale(): string | undefined { - throw new Error(`Not supported at build time!`); -} - -/** - * Invoked by the loader at build-time - */ -export function load(name: string, req: AMDLoader.IRelativeRequire, load: AMDLoader.IPluginLoadCallback, config: AMDLoader.IConfigurationOptions): void { - if (!name || name.length === 0) { - load({ localize, localize2, getConfiguredDefaultLocale }); - } else { - req([name + '.nls', name + '.nls.keys'], function (messages: string[], keys: string[]) { - buildMap[name] = messages; - buildMapKeys[name] = keys; - load(messages); - }); - } -} - -/** - * Invoked by the loader at build-time - */ -export function write(pluginName: string, moduleName: string, write: AMDLoader.IPluginWriteCallback): void { - const entryPoint = write.getEntryPoint(); - - entryPoints[entryPoint] = entryPoints[entryPoint] || []; - entryPoints[entryPoint].push(moduleName); - - if (moduleName !== entryPoint) { - write.asModule(pluginName + '!' + moduleName, 'define([\'vs/nls\', \'vs/nls!' + entryPoint + '\'], function(nls, data) { return nls.create("' + moduleName + '", data); });'); - } -} - -/** - * Invoked by the loader at build-time - */ -export function writeFile(pluginName: string, moduleName: string, req: AMDLoader.IRelativeRequire, write: AMDLoader.IPluginWriteFileCallback, config: AMDLoader.IConfigurationOptions): void { - if (entryPoints.hasOwnProperty(moduleName)) { - const fileName = req.toUrl(moduleName + '.nls.js'); - const contents = [ - '/*---------------------------------------------------------', - ' * Copyright (c) Microsoft Corporation. All rights reserved.', - ' *--------------------------------------------------------*/' - ], - entries = entryPoints[moduleName]; - - const data: { [moduleName: string]: string[] } = {}; - for (let i = 0; i < entries.length; i++) { - data[entries[i]] = buildMap[entries[i]]; - } - - contents.push('define("' + moduleName + '.nls", ' + JSON.stringify(data, null, '\t') + ');'); - write(fileName, contents.join('\r\n')); - } -} - -/** - * Invoked by the loader at build-time - */ -export function finishBuild(write: AMDLoader.IPluginWriteFileCallback): void { - write('nls.metadata.json', JSON.stringify({ - keys: buildMapKeys, - messages: buildMap, - bundles: entryPoints - }, null, '\t')); -} diff --git a/src/vs/nls.mock.ts b/src/vs/nls.mock.ts deleted file mode 100644 index 5323c6c6340..00000000000 --- a/src/vs/nls.mock.ts +++ /dev/null @@ -1,43 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -export interface ILocalizeInfo { - key: string; - comment: string[]; -} - -export interface ILocalizedString { - original: string; - value: string; -} - -function _format(message: string, args: any[]): string { - let result: string; - if (args.length === 0) { - result = message; - } else { - result = message.replace(/\{(\d+)\}/g, function (match, rest) { - const index = rest[0]; - return typeof args[index] !== 'undefined' ? args[index] : match; - }); - } - return result; -} - -export function localize(data: ILocalizeInfo | string, message: string, ...args: any[]): string { - return _format(message, args); -} - -export function localize2(data: ILocalizeInfo | string, message: string, ...args: any[]): ILocalizedString { - const res = _format(message, args); - return { - original: res, - value: res - }; -} - -export function getConfiguredDefaultLocale(_: string) { - return undefined; -} From c50fcf3d7c26ec9bd12b39d505a617fdb25946cb Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:46:43 +0200 Subject: [PATCH 0177/2222] Fix selection issue with filtered group models (#218889) Review: Filtered Editor Group Models and Selections - fixes #213163 --- .../parts/editor/multiEditorTabsControl.ts | 26 +++++++++---------- .../common/editor/filteredEditorGroupModel.ts | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 588c334451a..36a087361f5 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -1288,24 +1288,24 @@ export class MultiEditorTabsControl extends EditorTabsControl { throw new BugIndicatingError(); } - const anchorIndex = this.groupView.getIndexOfEditor(anchor); - if (anchorIndex === -1) { + const anchorEditorIndex = this.groupView.getIndexOfEditor(anchor); + if (anchorEditorIndex === -1) { throw new BugIndicatingError(); } let selection = this.groupView.selectedEditors; // Unselect editors on other side of anchor in relation to the target - let currentIndex = anchorIndex; - while (currentIndex >= 0 && currentIndex <= this.groupView.count - 1) { - currentIndex = anchorIndex < editorIndex ? currentIndex - 1 : currentIndex + 1; + let currentEditorIndex = anchorEditorIndex; + while (currentEditorIndex >= 0 && currentEditorIndex <= this.groupView.count - 1) { + currentEditorIndex = anchorEditorIndex < editorIndex ? currentEditorIndex - 1 : currentEditorIndex + 1; - if (!this.tabsModel.isSelected(currentIndex)) { + const currentEditor = this.groupView.getEditorByIndex(currentEditorIndex); + if (!currentEditor) { break; } - const currentEditor = this.groupView.getEditorByIndex(currentIndex); - if (!currentEditor) { + if (!this.groupView.isSelected(currentEditor)) { break; } @@ -1313,12 +1313,12 @@ export class MultiEditorTabsControl extends EditorTabsControl { } // Select editors between anchor and target - const fromIndex = anchorIndex < editorIndex ? anchorIndex : editorIndex; - const toIndex = anchorIndex < editorIndex ? editorIndex : anchorIndex; + const fromEditorIndex = anchorEditorIndex < editorIndex ? anchorEditorIndex : editorIndex; + const toEditorIndex = anchorEditorIndex < editorIndex ? editorIndex : anchorEditorIndex; - const editorsToSelect = this.groupView.getEditors(EditorsOrder.SEQUENTIAL).slice(fromIndex, toIndex + 1); + const editorsToSelect = this.groupView.getEditors(EditorsOrder.SEQUENTIAL).slice(fromEditorIndex, toEditorIndex + 1); for (const editor of editorsToSelect) { - if (!this.tabsModel.isSelected(editor)) { + if (!this.groupView.isSelected(editor)) { selection.push(editor); } } @@ -1343,7 +1343,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { const recentEditors = this.groupView.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE); for (let i = 1; i < recentEditors.length; i++) { // First one is the active editor const recentEditor = recentEditors[i]; - if (this.tabsModel.isSelected(recentEditor)) { + if (this.groupView.isSelected(recentEditor)) { newActiveEditor = recentEditor; break; } diff --git a/src/vs/workbench/common/editor/filteredEditorGroupModel.ts b/src/vs/workbench/common/editor/filteredEditorGroupModel.ts index 61d4f6a7c80..b872c92677b 100644 --- a/src/vs/workbench/common/editor/filteredEditorGroupModel.ts +++ b/src/vs/workbench/common/editor/filteredEditorGroupModel.ts @@ -36,7 +36,7 @@ abstract class FilteredEditorGroupModel extends Disposable implements IReadonlyE get activeEditor(): EditorInput | null { return this.model.activeEditor && this.filter(this.model.activeEditor) ? this.model.activeEditor : null; } get previewEditor(): EditorInput | null { return this.model.previewEditor && this.filter(this.model.previewEditor) ? this.model.previewEditor : null; } - get selectedEditors(): EditorInput[] { return this.model.selectedEditors.filter(e => this.filter(e)); } + get selectedEditors(): EditorInput[] { throw new Error('Filtered Editor Group Model should not be used for selectedEditors'); } isPinned(editorOrIndex: EditorInput | number): boolean { return this.model.isPinned(editorOrIndex); } isTransient(editorOrIndex: EditorInput | number): boolean { return this.model.isTransient(editorOrIndex); } From f272e4d094a1d60835dc53492a6606eb11e9de2b Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:47:18 +0200 Subject: [PATCH 0178/2222] Disable compare action when no resource selected (#218875) Fix #212925 --- .../browser/parts/editor/editorGroupView.ts | 4 +- src/vs/workbench/common/contextkeys.ts | 1 + .../files/browser/fileActions.contribution.ts | 8 +- .../contrib/files/browser/fileConstants.ts | 1 + .../files/browser/views/openEditorsView.ts | 82 ++++++++++++------- 5 files changed, 60 insertions(+), 36 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 6c8c42ec330..405049cfd1b 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -6,7 +6,7 @@ import 'vs/css!./media/editorgroupview'; import { EditorGroupModel, IEditorOpenOptions, IGroupModelChangeEvent, ISerializedEditorGroupModel, isGroupEditorCloseEvent, isGroupEditorOpenEvent, isSerializedEditorGroupModel } from 'vs/workbench/common/editor/editorGroupModel'; import { GroupIdentifier, CloseDirection, IEditorCloseEvent, IEditorPane, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, EditorResourceAccessor, EditorInputCapabilities, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, SideBySideEditor, EditorCloseContext, IEditorWillMoveEvent, IEditorWillOpenEvent, IMatchEditorOptions, GroupModelChangeKind, IActiveEditorChangeEvent, IFindEditorOptions, IToolbarActions, TEXT_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; -import { ActiveEditorGroupLockedContext, ActiveEditorDirtyContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorPinnedContext, ActiveEditorLastInGroupContext, ActiveEditorFirstInGroupContext, ResourceContextKey, applyAvailableEditorIds, ActiveEditorAvailableEditorIdsContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorContext, ActiveEditorReadonlyContext, ActiveEditorCanRevertContext, ActiveEditorCanToggleReadonlyContext, ActiveCompareEditorCanSwapContext, MultipleEditorsSelectedInGroupContext, TwoEditorsSelectedInGroupContext } from 'vs/workbench/common/contextkeys'; +import { ActiveEditorGroupLockedContext, ActiveEditorDirtyContext, EditorGroupEditorsCountContext, ActiveEditorStickyContext, ActiveEditorPinnedContext, ActiveEditorLastInGroupContext, ActiveEditorFirstInGroupContext, ResourceContextKey, applyAvailableEditorIds, ActiveEditorAvailableEditorIdsContext, ActiveEditorCanSplitInGroupContext, SideBySideEditorActiveContext, TextCompareEditorVisibleContext, TextCompareEditorActiveContext, ActiveEditorContext, ActiveEditorReadonlyContext, ActiveEditorCanRevertContext, ActiveEditorCanToggleReadonlyContext, ActiveCompareEditorCanSwapContext, MultipleEditorsSelectedInGroupContext, TwoEditorsSelectedInGroupContext, SelectedEditorsInGroupFileOrUntitledResourceContextKey } from 'vs/workbench/common/contextkeys'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { Emitter, Relay } from 'vs/base/common/event'; @@ -259,6 +259,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const multipleEditorsSelectedContext = MultipleEditorsSelectedInGroupContext.bindTo(this.scopedContextKeyService); const twoEditorsSelectedContext = TwoEditorsSelectedInGroupContext.bindTo(this.scopedContextKeyService); + const selectedEditorsHaveFileOrUntitledResourceContext = SelectedEditorsInGroupFileOrUntitledResourceContextKey.bindTo(this.scopedContextKeyService); const groupActiveEditorContext = this.editorPartsView.bind(ActiveEditorContext, this); const groupActiveEditorIsReadonly = this.editorPartsView.bind(ActiveEditorReadonlyContext, this); @@ -355,6 +356,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { case GroupModelChangeKind.EDITORS_SELECTION: multipleEditorsSelectedContext.set(this.model.selectedEditors.length > 1); twoEditorsSelectedContext.set(this.model.selectedEditors.length === 2); + selectedEditorsHaveFileOrUntitledResourceContext.set(this.model.selectedEditors.every(e => e.resource && (this.fileService.hasProvider(e.resource) || e.resource.scheme === Schemas.untitled))); break; } diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts index 97937218bbb..d02f9e0e89d 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -74,6 +74,7 @@ export const MultipleEditorGroupsContext = new RawContextKey('multipleE export const SingleEditorGroupsContext = MultipleEditorGroupsContext.toNegated(); export const MultipleEditorsSelectedInGroupContext = new RawContextKey('multipleEditorsSelectedInGroup', false, localize('multipleEditorsSelectedInGroup', "Whether multiple editors have been selected in an editor group")); export const TwoEditorsSelectedInGroupContext = new RawContextKey('twoEditorsSelectedInGroup', false, localize('twoEditorsSelectedInGroup', "Whether exactly two editors have been selected in an editor group")); +export const SelectedEditorsInGroupFileOrUntitledResourceContextKey = new RawContextKey('SelectedEditorsInGroupFileOrUntitledResourceContextKey', true, localize('SelectedEditorsInGroupFileOrUntitledResourceContextKey', "Whether all selected editors in a group have a file or untitled resource associated")); // Editor Part Context Keys export const EditorPartMultipleEditorGroupsContext = new RawContextKey('editorPartMultipleEditorGroups', false, localize('editorPartMultipleEditorGroups', "Whether there are multiple editor groups opened in an editor part")); diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 963e5c56925..b910fb3f2ee 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -10,7 +10,7 @@ import { MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/commo import { ICommandAction } from 'vs/platform/action/common/action'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { openWindowCommand, newWindowCommand } from 'vs/workbench/contrib/files/browser/fileCommands'; -import { COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, OpenEditorsDirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, OpenEditorsReadonlyEditorContext, OPEN_WITH_EXPLORER_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID, NEW_UNTITLED_FILE_LABEL, SAVE_ALL_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileConstants'; +import { COPY_PATH_COMMAND_ID, REVEAL_IN_EXPLORER_COMMAND_ID, OPEN_TO_SIDE_COMMAND_ID, REVERT_FILE_COMMAND_ID, SAVE_FILE_COMMAND_ID, SAVE_FILE_LABEL, SAVE_FILE_AS_COMMAND_ID, SAVE_FILE_AS_LABEL, SAVE_ALL_IN_GROUP_COMMAND_ID, OpenEditorsGroupContext, COMPARE_WITH_SAVED_COMMAND_ID, COMPARE_RESOURCE_COMMAND_ID, SELECT_FOR_COMPARE_COMMAND_ID, ResourceSelectedForCompareContext, OpenEditorsDirtyEditorContext, COMPARE_SELECTED_COMMAND_ID, REMOVE_ROOT_FOLDER_COMMAND_ID, REMOVE_ROOT_FOLDER_LABEL, SAVE_FILES_COMMAND_ID, COPY_RELATIVE_PATH_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_COMMAND_ID, SAVE_FILE_WITHOUT_FORMATTING_LABEL, OpenEditorsReadonlyEditorContext, OPEN_WITH_EXPLORER_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID, NEW_UNTITLED_FILE_LABEL, SAVE_ALL_COMMAND_ID, OpenEditorsSelectedFileOrUntitledContext } from 'vs/workbench/contrib/files/browser/fileConstants'; import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; @@ -20,7 +20,7 @@ import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOS import { AutoSaveAfterShortDelayContext } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import { Schemas } from 'vs/base/common/network'; -import { DirtyWorkingCopiesContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, ActiveEditorAvailableEditorIdsContext, MultipleEditorsSelectedInGroupContext, TwoEditorsSelectedInGroupContext } from 'vs/workbench/common/contextkeys'; +import { DirtyWorkingCopiesContext, EnterMultiRootWorkspaceSupportContext, HasWebFileSystemAccess, WorkbenchStateContext, WorkspaceFolderCountContext, SidebarFocusContext, ActiveEditorCanRevertContext, ActiveEditorContext, ResourceContextKey, ActiveEditorAvailableEditorIdsContext, MultipleEditorsSelectedInGroupContext, TwoEditorsSelectedInGroupContext, SelectedEditorsInGroupFileOrUntitledResourceContextKey } from 'vs/workbench/common/contextkeys'; import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ThemeIcon } from 'vs/base/common/themables'; @@ -413,14 +413,14 @@ MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { group: '3_compare', order: 30, command: compareSelectedCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection, isFileOrUntitledResourceContextKey) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, WorkbenchListDoubleSelection, OpenEditorsSelectedFileOrUntitledContext) }); MenuRegistry.appendMenuItem(MenuId.EditorTitleContext, { group: '3_compare', order: 30, command: compareSelectedCommand, - when: ContextKeyExpr.and(ResourceContextKey.HasResource, TwoEditorsSelectedInGroupContext, isFileOrUntitledResourceContextKey) + when: ContextKeyExpr.and(ResourceContextKey.HasResource, TwoEditorsSelectedInGroupContext, SelectedEditorsInGroupFileOrUntitledResourceContextKey) }); MenuRegistry.appendMenuItem(MenuId.OpenEditorsContext, { diff --git a/src/vs/workbench/contrib/files/browser/fileConstants.ts b/src/vs/workbench/contrib/files/browser/fileConstants.ts index c38f9987d22..555cff9f3d2 100644 --- a/src/vs/workbench/contrib/files/browser/fileConstants.ts +++ b/src/vs/workbench/contrib/files/browser/fileConstants.ts @@ -35,6 +35,7 @@ export const SAVE_FILES_COMMAND_ID = 'workbench.action.files.saveFiles'; export const OpenEditorsGroupContext = new RawContextKey('groupFocusedInOpenEditors', false); export const OpenEditorsDirtyEditorContext = new RawContextKey('dirtyEditorFocusedInOpenEditors', false); export const OpenEditorsReadonlyEditorContext = new RawContextKey('readonlyEditorFocusedInOpenEditors', false); +export const OpenEditorsSelectedFileOrUntitledContext = new RawContextKey('openEditorsSelectedFileOrUntitled', true); export const ResourceSelectedForCompareContext = new RawContextKey('resourceSelectedForCompare', false); export const REMOVE_ROOT_FOLDER_COMMAND_ID = 'removeRootFolder'; diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index a5de264bfcf..f7560f5a89f 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -18,7 +18,7 @@ import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { SaveAllInGroupAction, CloseGroupAction } from 'vs/workbench/contrib/files/browser/fileActions'; import { OpenEditorsFocusedContext, ExplorerFocusedContext, IFilesConfiguration, OpenEditor } from 'vs/workbench/contrib/files/common/files'; import { CloseAllEditorsAction, CloseEditorAction, UnpinEditorAction } from 'vs/workbench/browser/parts/editor/editorActions'; -import { IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { asCssVariable, badgeBackground, badgeForeground, contrastBorder } from 'vs/platform/theme/common/colorRegistry'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; @@ -28,7 +28,7 @@ import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { DisposableMap, IDisposable, dispose } from 'vs/base/common/lifecycle'; import { MenuId, Action2, registerAction2, MenuRegistry } from 'vs/platform/actions/common/actions'; -import { OpenEditorsDirtyEditorContext, OpenEditorsGroupContext, OpenEditorsReadonlyEditorContext, SAVE_ALL_LABEL, SAVE_ALL_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID } from 'vs/workbench/contrib/files/browser/fileConstants'; +import { OpenEditorsDirtyEditorContext, OpenEditorsGroupContext, OpenEditorsReadonlyEditorContext, SAVE_ALL_LABEL, SAVE_ALL_COMMAND_ID, NEW_UNTITLED_FILE_COMMAND_ID, OpenEditorsSelectedFileOrUntitledContext } from 'vs/workbench/contrib/files/browser/fileConstants'; import { ResourceContextKey, MultipleEditorGroupsContext } from 'vs/workbench/common/contextkeys'; import { CodeDataTransfers, containsDragType } from 'vs/platform/dnd/browser/dnd'; import { ResourcesDropHandler, fillEditorsDragData } from 'vs/workbench/browser/dnd'; @@ -55,6 +55,7 @@ import { ILocalizedString } from 'vs/platform/action/common/action'; import { mainWindow } from 'vs/base/browser/window'; import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { IFileService } from 'vs/platform/files/common/files'; const $ = dom.$; @@ -74,10 +75,6 @@ export class OpenEditorsView extends ViewPane { private needsRefresh = false; private elements: (OpenEditor | IEditorGroup)[] = []; private sortOrder: 'editorOrder' | 'alphabetical' | 'fullPath'; - private resourceContext!: ResourceContextKey; - private groupFocusedContext!: IContextKey; - private dirtyEditorFocusedContext!: IContextKey; - private readonlyEditorFocusedContext!: IContextKey; private blockFocusActiveEditorTracking = false; constructor( @@ -95,6 +92,7 @@ export class OpenEditorsView extends ViewPane { @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @IOpenerService openerService: IOpenerService, + @IFileService private readonly fileService: IFileService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); @@ -245,32 +243,8 @@ export class OpenEditorsView extends ViewPane { this.updateSize(); - // Bind context keys - OpenEditorsFocusedContext.bindTo(this.list.contextKeyService); - ExplorerFocusedContext.bindTo(this.list.contextKeyService); - - this.resourceContext = this.instantiationService.createInstance(ResourceContextKey); - this._register(this.resourceContext); - this.groupFocusedContext = OpenEditorsGroupContext.bindTo(this.contextKeyService); - this.dirtyEditorFocusedContext = OpenEditorsDirtyEditorContext.bindTo(this.contextKeyService); - this.readonlyEditorFocusedContext = OpenEditorsReadonlyEditorContext.bindTo(this.contextKeyService); - + this.handleContextKeys(); this._register(this.list.onContextMenu(e => this.onListContextMenu(e))); - this._register(this.list.onDidChangeFocus(e => { - this.resourceContext.reset(); - this.groupFocusedContext.reset(); - this.dirtyEditorFocusedContext.reset(); - this.readonlyEditorFocusedContext.reset(); - const element = e.elements.length ? e.elements[0] : undefined; - if (element instanceof OpenEditor) { - const resource = element.getResource(); - this.dirtyEditorFocusedContext.set(element.editor.isDirty() && !element.editor.isSaving()); - this.readonlyEditorFocusedContext.set(!!element.editor.isReadonly()); - this.resourceContext.set(resource ?? null); - } else if (!!element) { - this.groupFocusedContext.set(true); - } - })); // Open when selecting via keyboard this._register(this.list.onMouseMiddleClick(e => { @@ -318,6 +292,52 @@ export class OpenEditorsView extends ViewPane { })); } + private handleContextKeys() { + if (!this.list) { + return; + } + + // Bind context keys + OpenEditorsFocusedContext.bindTo(this.list.contextKeyService); + ExplorerFocusedContext.bindTo(this.list.contextKeyService); + + const groupFocusedContext = OpenEditorsGroupContext.bindTo(this.contextKeyService); + const dirtyEditorFocusedContext = OpenEditorsDirtyEditorContext.bindTo(this.contextKeyService); + const readonlyEditorFocusedContext = OpenEditorsReadonlyEditorContext.bindTo(this.contextKeyService); + const openEditorsSelectedFileOrUntitledContext = OpenEditorsSelectedFileOrUntitledContext.bindTo(this.contextKeyService); + + const resourceContext = this.instantiationService.createInstance(ResourceContextKey); + this._register(resourceContext); + + this._register(this.list.onDidChangeFocus(e => { + resourceContext.reset(); + groupFocusedContext.reset(); + dirtyEditorFocusedContext.reset(); + readonlyEditorFocusedContext.reset(); + + const element = e.elements.length ? e.elements[0] : undefined; + if (element instanceof OpenEditor) { + const resource = element.getResource(); + dirtyEditorFocusedContext.set(element.editor.isDirty() && !element.editor.isSaving()); + readonlyEditorFocusedContext.set(!!element.editor.isReadonly()); + resourceContext.set(resource ?? null); + } else if (!!element) { + groupFocusedContext.set(true); + } + })); + + this._register(this.list.onDidChangeSelection(e => { + const selectedAreFileOrUntitled = e.elements.every(e => { + if (e instanceof OpenEditor) { + const resource = e.getResource(); + return resource && (resource.scheme === Schemas.untitled || this.fileService.hasProvider(resource)); + } + return false; + }); + openEditorsSelectedFileOrUntitledContext.set(selectedAreFileOrUntitled); + })); + } + override focus(): void { super.focus(); From efa1397e63cc9184a2e838fcc05e43f6ed8744b7 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:47:48 +0200 Subject: [PATCH 0179/2222] Update default color for sticky scroll and location specific ones (#218712) default color for sticky scroll and location specific ones --- src/vs/platform/theme/browser/defaultStyles.ts | 2 +- .../contrib/extensions/browser/extensionsViews.ts | 5 +---- .../contrib/scm/browser/scmRepositoriesViewPane.ts | 5 +---- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 7 ++----- 4 files changed, 5 insertions(+), 14 deletions(-) diff --git a/src/vs/platform/theme/browser/defaultStyles.ts b/src/vs/platform/theme/browser/defaultStyles.ts index 871178faf38..a0df50920ab 100644 --- a/src/vs/platform/theme/browser/defaultStyles.ts +++ b/src/vs/platform/theme/browser/defaultStyles.ts @@ -176,7 +176,7 @@ export const defaultListStyles: IListStyles = { treeInactiveIndentGuidesStroke: asCssVariable(treeInactiveIndentGuidesStroke), treeStickyScrollBackground: undefined, treeStickyScrollBorder: undefined, - treeStickyScrollShadow: undefined, + treeStickyScrollShadow: asCssVariable(scrollbarShadow), tableColumnsBorder: asCssVariable(tableColumnsBorder), tableOddRowsBackgroundColor: asCssVariable(tableOddRowsBackgroundColor), }; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index 4dcb55bb040..933788b162f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -41,7 +41,6 @@ import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from 'vs import { IProductService } from 'vs/platform/product/common/productService'; import { SeverityIcon } from 'vs/platform/severityIcon/browser/severityIcon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; @@ -214,9 +213,7 @@ export class ExtensionsListView extends ViewPane { return localize('extensions', "Extensions"); } }, - overrideStyles: { - listBackground: SIDE_BAR_BACKGROUND - }, + overrideStyles: this.getLocationBasedColors().listOverrideStyles, openOnSingleClick: true }) as WorkbenchPagedList; this._register(this.list.onContextMenu(e => this.onContextMenu(e), this)); diff --git a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts index 890e39b459f..99e9f61f835 100644 --- a/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmRepositoriesViewPane.ts @@ -18,7 +18,6 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { RepositoryActionRunner, RepositoryRenderer } from 'vs/workbench/contrib/scm/browser/scmRepositoryRenderer'; @@ -82,9 +81,7 @@ export class SCMRepositoriesViewPane extends ViewPane { this.list = this.instantiationService.createInstance(WorkbenchList, `SCM Main`, listContainer, delegate, [renderer], { identityProvider, horizontalScrolling: false, - overrideStyles: { - listBackground: SIDE_BAR_BACKGROUND - }, + overrideStyles: this.getLocationBasedColors().listOverrideStyles, accessibilityProvider: { getAriaLabel(r: ISCMRepository) { return r.provider.label; diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 8b47b5cec4d..028ef92e159 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -37,11 +37,10 @@ import { URI } from 'vs/base/common/uri'; import { FileKind } from 'vs/platform/files/common/files'; import { compareFileNames, comparePaths } from 'vs/base/common/comparers'; import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters'; -import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; -import { SIDE_BAR_BACKGROUND, PANEL_BACKGROUND } from 'vs/workbench/common/theme'; import { CodeEditorWidget, ICodeEditorWidgetOptions } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { IEditorConstructionOptions } from 'vs/editor/browser/config/editorConfiguration'; import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; @@ -3089,9 +3088,7 @@ export class SCMViewPane extends ViewPane { identityProvider: new SCMResourceIdentityProvider(), sorter: new SCMTreeSorter(() => this.viewMode, () => this.viewSortKey), keyboardNavigationLabelProvider: this.instantiationService.createInstance(SCMTreeKeyboardNavigationLabelProvider, () => this.viewMode), - overrideStyles: { - listBackground: this.viewDescriptorService.getViewLocationById(this.id) === ViewContainerLocation.Panel ? PANEL_BACKGROUND : SIDE_BAR_BACKGROUND - }, + overrideStyles: this.getLocationBasedColors().listOverrideStyles, collapseByDefault: (e: unknown) => { // Repository, Resource Group, Resource Folder (Tree), History Item Change Folder (Tree) if (isSCMRepository(e) || isSCMResourceGroup(e) || isSCMResourceNode(e) || isSCMHistoryItemChangeNode(e)) { From ecf818849d272b41e0d4d17ae5e2301de4474e68 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Jun 2024 16:27:08 +0200 Subject: [PATCH 0180/2222] add location specific data along when making chat request (#219114) with this PR the implicit variables tricks aren't needed anymore --- .vscode/settings.json | 3 +- .../common/extensionsApiProposals.ts | 1 + .../workbench/api/common/extHost.api.impl.ts | 4 +- .../workbench/api/common/extHost.protocol.ts | 4 +- .../api/common/extHostChatAgents2.ts | 41 +++++++++++++++---- .../api/common/extHostTypeConverters.ts | 5 ++- src/vs/workbench/api/common/extHostTypes.ts | 14 +++++++ .../contrib/chat/browser/chatWidget.ts | 31 ++++++++++++-- .../contrib/chat/common/chatAgents.ts | 3 +- .../contrib/chat/common/chatService.ts | 21 ++++++++++ .../contrib/chat/common/chatServiceImpl.ts | 1 + .../browser/inlineChatContentWidget.ts | 4 +- .../browser/inlineChatController.ts | 22 ++++++++-- .../inlineChat/browser/inlineChatWidget.ts | 6 +-- .../browser/inlineChatZoneWidget.ts | 4 +- .../controller/chat/notebookChatController.ts | 14 ++++++- .../chat/browser/terminalChatWidget.ts | 8 +++- ...scode.proposed.chatParticipantPrivate.d.ts | 26 ++++++++++++ 18 files changed, 180 insertions(+), 32 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 52dac9d7960..9255a781bd4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -160,8 +160,7 @@ "@xterm/headless", "node-pty", "vscode-notebook-renderer", - "src/vs/workbench/workbench.web.main.ts", - "src/vs/workbench/api/common/extHostTypes.ts" + "src/vs/workbench/workbench.web.main.ts" ], "[github-issues]": { "editor.wordWrap": "on" diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index abfd6c5228c..a3f677336d1 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -35,6 +35,7 @@ const _allApiProposals = { }, chatParticipantPrivate: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts', + version: 2 }, chatProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts', diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 4b6a73823dc..214c68cc394 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -210,7 +210,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostUriOpeners = rpcProtocol.set(ExtHostContext.ExtHostUriOpeners, new ExtHostUriOpeners(rpcProtocol)); const extHostProfileContentHandlers = rpcProtocol.set(ExtHostContext.ExtHostProfileContentHandlers, new ExtHostProfileContentHandlers(rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService)); - const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands)); + const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands, extHostDocuments, extHostNotebook)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostLanguageModelTools = rpcProtocol.set(ExtHostContext.ExtHostLanguageModelTools, new ExtHostLanguageModelTools(rpcProtocol)); const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol)); @@ -1740,6 +1740,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ChatRequestTurn: extHostTypes.ChatRequestTurn, ChatResponseTurn: extHostTypes.ChatResponseTurn, ChatLocation: extHostTypes.ChatLocation, + ChatRequestEditorData: extHostTypes.ChatRequestEditorData, + ChatRequestNotebookData: extHostTypes.ChatRequestNotebookData, LanguageModelChatMessageRole: extHostTypes.LanguageModelChatMessageRole, LanguageModelChatMessage: extHostTypes.LanguageModelChatMessage, LanguageModelChatMessageFunctionResultPart: extHostTypes.LanguageModelFunctionResultPart, diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index d810c7b892d..b1dcc4981a3 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -1274,8 +1274,8 @@ export type IChatAgentHistoryEntryDto = { }; export interface ExtHostChatAgentsShape2 { - $invokeAgent(handle: number, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise; - $provideFollowups(request: IChatAgentRequest, handle: number, result: IChatAgentResult, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise; + $invokeAgent(handle: number, request: Dto, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise; + $provideFollowups(request: Dto, handle: number, result: IChatAgentResult, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise; $acceptFeedback(handle: number, result: IChatAgentResult, vote: ChatAgentVoteDirection, reportIssue?: boolean): void; $acceptAction(handle: number, result: IChatAgentResult, action: IChatUserActionEvent): void; $invokeCompletionProvider(handle: number, query: string, token: CancellationToken): Promise; diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 37a81b0034d..a34438f1da1 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -11,6 +11,7 @@ import { Emitter } from 'vs/base/common/event'; import { IMarkdownString } from 'vs/base/common/htmlContent'; import { Iterable } from 'vs/base/common/iterator'; import { Disposable, DisposableMap, DisposableStore } from 'vs/base/common/lifecycle'; +import { revive } from 'vs/base/common/marshalling'; import { StopWatch } from 'vs/base/common/stopwatch'; import { assertType } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -19,6 +20,8 @@ import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensio import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IChatAgentHistoryEntryDto, IChatProgressDto, IExtensionChatAgentMetadata, IMainContext, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; +import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; +import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { ChatAgentLocation, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; @@ -262,7 +265,9 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS constructor( mainContext: IMainContext, private readonly _logService: ILogService, - private readonly commands: ExtHostCommands, + private readonly _commands: ExtHostCommands, + private readonly _documents: ExtHostDocuments, + private readonly _notebooks: ExtHostNotebookController ) { super(); this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2); @@ -290,12 +295,14 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS return agent.apiAgent; } - async $invokeAgent(handle: number, request: IChatAgentRequest, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise { + async $invokeAgent(handle: number, requestDto: Dto, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise { const agent = this._agents.get(handle); if (!agent) { throw new Error(`[CHAT](${handle}) CANNOT invoke agent because the agent is not registered`); } + const request = revive(requestDto); + // Init session disposables let sessionDisposables = this._sessionDisposables.get(request.sessionId); if (!sessionDisposables) { @@ -303,11 +310,28 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS this._sessionDisposables.set(request.sessionId, sessionDisposables); } - const stream = new ChatAgentResponseStream(agent.extension, request, this._proxy, this.commands.converter, sessionDisposables); + const stream = new ChatAgentResponseStream(agent.extension, request, this._proxy, this._commands.converter, sessionDisposables); try { const convertedHistory = await this.prepareHistoryTurns(request.agentId, context); + + // in-place converting for location-data + let location2: vscode.ChatRequestEditorData | vscode.ChatRequestNotebookData | undefined; + if (request.locationData?.type === ChatAgentLocation.Editor) { + // editor data + const document = this._documents.getDocument(request.locationData.document); + location2 = new extHostTypes.ChatRequestEditorData(document, typeConvert.Selection.to(request.locationData.selection), typeConvert.Range.to(request.locationData.wholeRange)); + + } else if (request.locationData?.type === ChatAgentLocation.Notebook) { + // notebook data + const notebook = this._notebooks.getNotebookDocument(request.locationData.sessionInputUri); + location2 = new extHostTypes.ChatRequestNotebookData(notebook.apiNotebook); + + } else if (request.locationData?.type === ChatAgentLocation.Terminal) { + // TBD + } + const task = agent.invoke( - typeConvert.ChatAgentRequest.to(request), + typeConvert.ChatAgentRequest.to(request, location2), { history: convertedHistory }, stream.apiObject, token @@ -364,7 +388,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS res.push(new extHostTypes.ChatRequestTurn(h.request.message, h.request.command, h.request.variables.variables.map(typeConvert.ChatAgentValueReference.to), h.request.agentId)); // RESPONSE turn - const parts = coalesce(h.response.map(r => typeConvert.ChatResponsePart.toContent(r, this.commands.converter))); + const parts = coalesce(h.response.map(r => typeConvert.ChatResponsePart.toContent(r, this._commands.converter))); res.push(new extHostTypes.ChatResponseTurn(parts, result, h.request.agentId, h.request.command)); } @@ -375,12 +399,13 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS this._sessionDisposables.deleteAndDispose(sessionId); } - async $provideFollowups(request: IChatAgentRequest, handle: number, result: IChatAgentResult, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise { + async $provideFollowups(requestDto: Dto, handle: number, result: IChatAgentResult, context: { history: IChatAgentHistoryEntryDto[] }, token: CancellationToken): Promise { const agent = this._agents.get(handle); if (!agent) { return Promise.resolve([]); } + const request = revive(requestDto); const convertedHistory = await this.prepareHistoryTurns(agent.id, context); const ehResult = typeConvert.ChatAgentResult.to(result); @@ -429,7 +454,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS return; } - const ehAction = typeConvert.ChatAgentUserActionEvent.to(result, event, this.commands.converter); + const ehAction = typeConvert.ChatAgentUserActionEvent.to(result, event, this._commands.converter); if (ehAction) { agent.acceptAction(Object.freeze(ehAction)); } @@ -452,7 +477,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS const items = await agent.invokeCompletionProvider(query, token); - return items.map((i) => typeConvert.ChatAgentCompletionItem.from(i, this.commands.converter, disposables)); + return items.map((i) => typeConvert.ChatAgentCompletionItem.from(i, this._commands.converter, disposables)); } async $provideWelcomeMessage(handle: number, location: ChatAgentLocation, token: CancellationToken): Promise<(string | IMarkdownString)[] | undefined> { diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 32d972e9862..36f83b564e0 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2592,7 +2592,7 @@ export namespace ChatResponsePart { } export namespace ChatAgentRequest { - export function to(request: IChatAgentRequest): vscode.ChatRequest { + export function to(request: IChatAgentRequest, location2: vscode.ChatRequestEditorData | vscode.ChatRequestNotebookData | undefined): vscode.ChatRequest { return { prompt: request.message, command: request.command, @@ -2601,7 +2601,8 @@ export namespace ChatAgentRequest { references: request.variables.variables.map(ChatAgentValueReference.to), location: ChatLocation.to(request.location), acceptedConfirmationData: request.acceptedConfirmationData, - rejectedConfirmationData: request.rejectedConfirmationData + rejectedConfirmationData: request.rejectedConfirmationData, + location2 }; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 65854c65512..4189c61331f 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4449,6 +4449,20 @@ export enum ChatLocation { Editor = 4, } +export class ChatRequestEditorData { + constructor( + readonly document: vscode.TextDocument, + readonly selection: vscode.Selection, + readonly wholeRange: vscode.Range, + ) { } +} + +export class ChatRequestNotebookData { + constructor( + readonly notebook: vscode.NotebookDocument + ) { } +} + export enum LanguageModelChatMessageRole { User = 1, Assistant = 2, diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 1d1dc4b817c..4a8aca892cf 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -36,7 +36,7 @@ import { CONTEXT_CHAT_INPUT_HAS_AGENT, CONTEXT_CHAT_LOCATION, CONTEXT_CHAT_REQUE import { ChatModelInitState, IChatModel, IChatRequestVariableEntry, IChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestAgentPart, IParsedChatRequest, chatAgentLeader, chatSubcommandLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; -import { IChatFollowup, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatFollowup, IChatLocationData, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { CodeBlockModelCollection } from 'vs/workbench/contrib/chat/common/codeBlockModelCollection'; @@ -77,6 +77,11 @@ export interface IChatWidgetContrib extends IDisposable { setInputState?(s: any): void; } +export interface IChatWidgetLocationOptions { + location: ChatAgentLocation; + resolveData?(): IChatLocationData | undefined; +} + export class ChatWidget extends Disposable implements IChatWidget { public static readonly CONTRIBS: { new(...args: [IChatWidget, ...any]): IChatWidgetContrib }[] = []; @@ -176,8 +181,14 @@ export class ChatWidget extends Disposable implements IChatWidget { return this.contextKeyService; } + private readonly _location: IChatWidgetLocationOptions; + + get location() { + return this._location.location; + } + constructor( - readonly location: ChatAgentLocation, + location: ChatAgentLocation | IChatWidgetLocationOptions, readonly viewContext: IChatWidgetViewContext, private readonly viewOptions: IChatWidgetViewOptions, private readonly styles: IChatWidgetStyles, @@ -194,8 +205,15 @@ export class ChatWidget extends Disposable implements IChatWidget { @IChatSlashCommandService private readonly chatSlashCommandService: IChatSlashCommandService, ) { super(); + + if (typeof location === 'object') { + this._location = location; + } else { + this._location = { location }; + } + CONTEXT_IN_CHAT_SESSION.bindTo(contextKeyService).set(true); - CONTEXT_CHAT_LOCATION.bindTo(contextKeyService).set(location); + CONTEXT_CHAT_LOCATION.bindTo(contextKeyService).set(this._location.location); CONTEXT_IN_QUICK_CHAT.bindTo(contextKeyService).set('resource' in viewContext); this.agentInInput = CONTEXT_CHAT_INPUT_HAS_AGENT.bindTo(contextKeyService); this.requestInProgress = CONTEXT_CHAT_REQUEST_IN_PROGRESS.bindTo(contextKeyService); @@ -748,7 +766,12 @@ export class ChatWidget extends Disposable implements IChatWidget { 'query' in opts ? opts.query : `${opts.prefix} ${editorValue}`; const isUserQuery = !opts || 'prefix' in opts; - const result = await this.chatService.sendRequest(this.viewModel.sessionId, input, { location: this.location, parserContext: { selectedAgent: this._lastSelectedAgent }, attachedContext: [...this.inputPart.attachedContext.values()] }); + const result = await this.chatService.sendRequest(this.viewModel.sessionId, input, { + location: this.location, + locationData: this._location.resolveData?.(), + parserContext: { selectedAgent: this._lastSelectedAgent }, + attachedContext: [...this.inputPart.attachedContext.values()] + }); if (result) { this.inputPart.attachedContext.clear(); diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index e2f703325b1..a3c9016944b 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -27,7 +27,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { CONTEXT_CHAT_ENABLED } from 'vs/workbench/contrib/chat/common/chatContextKeys'; import { IChatProgressResponseContent, IChatRequestVariableData, ISerializableChatAgentData } from 'vs/workbench/contrib/chat/common/chatModel'; import { IRawChatCommandContribution, RawChatParticipantLocation } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes'; -import { IChatFollowup, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatFollowup, IChatLocationData, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from 'vs/workbench/contrib/chat/common/chatService'; //#region agent service, commands etc @@ -126,6 +126,7 @@ export interface IChatAgentRequest { enableCommandDetection?: boolean; variables: IChatRequestVariableData; location: ChatAgentLocation; + locationData?: IChatLocationData; acceptedConfirmationData?: any[]; rejectedConfirmationData?: any[]; } diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index a6921314d18..fc4add785ff 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -10,6 +10,7 @@ import { IMarkdownString } from 'vs/base/common/htmlContent'; import { ThemeIcon } from 'vs/base/common/themables'; import { URI } from 'vs/base/common/uri'; import { IRange, Range } from 'vs/editor/common/core/range'; +import { ISelection } from 'vs/editor/common/core/selection'; import { Command, Location, TextEdit } from 'vs/editor/common/languages'; import { FileType } from 'vs/platform/files/common/files'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -298,8 +299,28 @@ export interface IChatSendRequestData extends IChatSendRequestResponseState { slashCommand?: IChatAgentCommand; } +export interface IChatEditorLocationData { + type: ChatAgentLocation.Editor; + document: URI; + selection: ISelection; + wholeRange: IRange; +} + +export interface IChatNotebookLocationData { + type: ChatAgentLocation.Notebook; + sessionInputUri: URI; +} + +export interface IChatTerminalLocationData { + type: ChatAgentLocation.Terminal; + // TBD +} + +export type IChatLocationData = IChatEditorLocationData | IChatNotebookLocationData | IChatTerminalLocationData; + export interface IChatSendRequestOptions { location?: ChatAgentLocation; + locationData?: IChatLocationData; parserContext?: IChatParserContext; attempt?: number; noCommandDetection?: boolean; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index ff8d83fc2c3..7c115514e91 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -594,6 +594,7 @@ export class ChatService extends Disposable implements IChatService { enableCommandDetection, attempt, location, + locationData: options?.locationData, acceptedConfirmationData: options?.acceptedConfirmationData, rejectedConfirmationData: options?.rejectedConfirmationData, }; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts index 20004ab3eb2..92ba84eb48e 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatContentWidget.ts @@ -13,7 +13,7 @@ import { IPosition, Position } from 'vs/editor/common/core/position'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { inlineChatBackground, InlineChatConfigKeys, MENU_INLINE_CHAT_CONTENT_STATUS, MENU_INLINE_CHAT_EXECUTE } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; import { Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; -import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { ChatWidget, IChatWidgetLocationOptions } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; import { editorBackground, editorForeground, inputBackground } from 'vs/platform/theme/common/colorRegistry'; import { ChatModel } from 'vs/workbench/contrib/chat/common/chatModel'; @@ -49,7 +49,7 @@ export class InlineChatContentWidget implements IContentWidget { private readonly _widget: ChatWidget; constructor( - location: ChatAgentLocation, + location: IChatWidgetLocationOptions, private readonly _editor: ICodeEditor, @IInstantiationService instaService: IInstantiationService, @IContextKeyService contextKeyService: IContextKeyService, diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 0befb284dbf..ec0d7cf665d 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -51,6 +51,7 @@ import { isEqual } from 'vs/base/common/resources'; import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { escapeRegExpCharacters } from 'vs/base/common/strings'; +import { IChatWidgetLocationOptions } from 'vs/workbench/contrib/chat/browser/chatWidget'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -154,19 +155,34 @@ export class InlineChatController implements IEditorContribution { this._ctxRequestInProgress = CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.bindTo(contextKeyService); this._ui = new Lazy(() => { - let location = ChatAgentLocation.Editor; + + const location: IChatWidgetLocationOptions = { + location: ChatAgentLocation.Editor, + resolveData: () => { + assertType(this._editor.hasModel()); + assertType(this._session); + return { + type: ChatAgentLocation.Editor, + selection: this._editor.getSelection(), + document: this._session.textModelN.uri, + wholeRange: this._session?.wholeRange.trackedInitialRange, + }; + } + }; // inline chat in notebooks // check if this editor is part of a notebook editor - // and iff so, use the notebook location + // and iff so, use the notebook location but keep the resolveData + // talk about editor data for (const notebookEditor of notebookEditorService.listNotebookEditors()) { for (const [, codeEditor] of notebookEditor.codeEditors) { if (codeEditor === this._editor) { - location = ChatAgentLocation.Notebook; + location.location = ChatAgentLocation.Notebook; break; } } } + const content = this._store.add(_instaService.createInstance(InlineChatContentWidget, location, this._editor)); const zone = this._store.add(_instaService.createInstance(InlineChatZoneWidget, location, this._editor)); return { content, zone }; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index fe4509dabee..48539e61df8 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -37,7 +37,7 @@ import { ChatModel, IChatModel } from 'vs/workbench/contrib/chat/common/chatMode import { isResponseVM, isWelcomeVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { HunkInformation, Session } from 'vs/workbench/contrib/inlineChat/browser/inlineChatSession'; import { CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_RESPONSE_FOCUSED, inlineChatBackground, InlineChatConfigKeys, inlineChatForeground } from 'vs/workbench/contrib/inlineChat/common/inlineChat'; -import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { ChatWidget, IChatWidgetLocationOptions } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { chatRequestBackground } from 'vs/workbench/contrib/chat/common/chatColors'; import { Selection } from 'vs/editor/common/core/selection'; import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; @@ -115,7 +115,7 @@ export class InlineChatWidget { readonly scopedContextKeyService: IContextKeyService; constructor( - location: ChatAgentLocation, + location: IChatWidgetLocationOptions, options: IInlineChatWidgetConstructionOptions, @IInstantiationService protected readonly _instantiationService: IInstantiationService, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @@ -522,7 +522,7 @@ export class EditorBasedInlineChatWidget extends InlineChatWidget { private readonly _accessibleViewer = this._store.add(new MutableDisposable()); constructor( - location: ChatAgentLocation, + location: IChatWidgetLocationOptions, private readonly _parentEditor: ICodeEditor, options: IInlineChatWidgetConstructionOptions, @IContextKeyService contextKeyService: IContextKeyService, diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts index 793600ccf0c..adc3b413558 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts @@ -20,8 +20,8 @@ import { isEqual } from 'vs/base/common/resources'; import { StableEditorBottomScrollState } from 'vs/editor/browser/stableEditorScroll'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ChatAgentLocation } from 'vs/workbench/contrib/chat/common/chatAgents'; import { ILogService } from 'vs/platform/log/common/log'; +import { IChatWidgetLocationOptions } from 'vs/workbench/contrib/chat/browser/chatWidget'; export class InlineChatZoneWidget extends ZoneWidget { @@ -31,7 +31,7 @@ export class InlineChatZoneWidget extends ZoneWidget { private _dimension?: Dimension; constructor( - location: ChatAgentLocation, + location: IChatWidgetLocationOptions, editor: ICodeEditor, @IInstantiationService private readonly _instaService: IInstantiationService, @ILogService private _logService: ILogService, diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts index d9eee2ec600..3de91da698d 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts @@ -411,7 +411,19 @@ export class NotebookChatController extends Disposable implements INotebookEdito const inlineChatWidget = this._widgetDisposableStore.add(this._instantiationService.createInstance( InlineChatWidget, - ChatAgentLocation.Notebook, + { + location: ChatAgentLocation.Notebook, + resolveData: () => { + const sessionInputUri = this.getSessionInputUri(); + if (!sessionInputUri) { + return undefined; + } + return { + type: ChatAgentLocation.Notebook, + sessionInputUri + }; + } + }, { statusMenuId: MENU_CELL_CHAT_WIDGET_STATUS, chatWidgetViewOptions: { diff --git a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts index 61d1b7147f6..d28f3cf8455 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/browser/terminalChatWidget.ts @@ -56,7 +56,13 @@ export class TerminalChatWidget extends Disposable { this._inlineChatWidget = this._instantiationService.createInstance( InlineChatWidget, - ChatAgentLocation.Terminal, + { + location: ChatAgentLocation.Terminal, + resolveData: () => { + // TODO@meganrogge return something that identifies this terminal + return undefined; + } + }, { statusMenuId: { menu: MENU_TERMINAL_CHAT_WIDGET_STATUS, diff --git a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts index 4e328978a9d..d3c0ca44e26 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// version: 2 + declare module 'vscode' { /** @@ -27,6 +29,22 @@ declare module 'vscode' { Editor = 4 } + export class ChatRequestEditorData { + //TODO@API should be the editor + document: TextDocument; + selection: Selection; + wholeRange: Range; + + constructor(document: TextDocument, selection: Selection, wholeRange: Range); + } + + export class ChatRequestNotebookData { + //TODO@API should be the editor + notebook: NotebookDocument; + + constructor(notebook: NotebookDocument); + } + export interface ChatRequest { /** * The attempt number of the request. The first request has attempt number 0. @@ -40,8 +58,16 @@ declare module 'vscode' { /** * The location at which the chat is happening. This will always be one of the supported values + * + * @deprecated */ readonly location: ChatLocation; + + /** + * Information that is specific to the location at which chat is happening, e.g within a document, notebook, + * or terminal. Will be `undefined` for the chat panel. + */ + readonly location2: ChatRequestEditorData | ChatRequestNotebookData | undefined; } export interface ChatParticipant { From 6889c34d61849e2b33fa6878aefbe0cb0d41b017 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 28 Jun 2024 16:55:07 +0200 Subject: [PATCH 0181/2222] Adopt editor placeholder for comment widget (#219119) --- .../contrib/comments/browser/commentReply.ts | 33 +++---------------- 1 file changed, 5 insertions(+), 28 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentReply.ts b/src/vs/workbench/contrib/comments/browser/commentReply.ts index 3868281f8e5..0a73f27023a 100644 --- a/src/vs/workbench/contrib/comments/browser/commentReply.ts +++ b/src/vs/workbench/contrib/comments/browser/commentReply.ts @@ -22,8 +22,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { editorForeground, resolveColorValue } from 'vs/platform/theme/common/colorRegistry'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; import { CommentFormActions } from 'vs/workbench/contrib/comments/browser/commentFormActions'; import { CommentMenus } from 'vs/workbench/contrib/comments/browser/commentMenus'; import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; @@ -63,7 +61,6 @@ export class CommentReply extends Disposable { focus: boolean, private _actionRunDelegate: (() => void) | null, @ICommentService private commentService: ICommentService, - @IThemeService private themeService: IThemeService, @IConfigurationService configurationService: IConfigurationService, @IKeybindingService private keybindingService: IKeybindingService, @IHoverService private hoverService: IHoverService, @@ -213,32 +210,12 @@ export class CommentReply extends Disposable { } setCommentEditorDecorations() { - const model = this.commentEditor.getModel(); - if (model) { - const valueLength = model.getValueLength(); - const hasExistingComments = this._commentThread.comments && this._commentThread.comments.length > 0; - const placeholder = valueLength > 0 - ? '' - : hasExistingComments - ? (this._commentOptions?.placeHolder || nls.localize('reply', "Reply...")) - : (this._commentOptions?.placeHolder || nls.localize('newComment', "Type a new comment")); - const decorations = [{ - range: { - startLineNumber: 0, - endLineNumber: 0, - startColumn: 0, - endColumn: 1 - }, - renderOptions: { - after: { - contentText: placeholder, - color: `${resolveColorValue(editorForeground, this.themeService.getColorTheme())?.transparent(0.4)}` - } - } - }]; + const hasExistingComments = this._commentThread.comments && this._commentThread.comments.length > 0; + const placeholder = hasExistingComments + ? (this._commentOptions?.placeHolder || nls.localize('reply', "Reply...")) + : (this._commentOptions?.placeHolder || nls.localize('newComment', "Type a new comment")); - this.commentEditor.setDecorationsByType('review-zone-widget', COMMENTEDITOR_DECORATION_KEY, decorations); - } + this.commentEditor.updateOptions({ placeholder }); } private createTextModelListener(commentEditor: ICodeEditor, commentForm: HTMLElement) { From dd7d9376fb6b9a361da6070a40196edb13465dfb Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 28 Jun 2024 17:03:26 +0200 Subject: [PATCH 0182/2222] remove dead types from old proposal versions (#219120) --- src/vs/workbench/api/common/extHost.api.impl.ts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 214c68cc394..402b55888f4 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1747,10 +1747,6 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I LanguageModelChatMessageFunctionResultPart: extHostTypes.LanguageModelFunctionResultPart, LanguageModelChatResponseTextPart: extHostTypes.LanguageModelTextPart, LanguageModelChatResponseFunctionUsePart: extHostTypes.LanguageModelFunctionUsePart, - LanguageModelChatMessage2: extHostTypes.LanguageModelChatMessage, // TODO@jrieken REMOVE - LanguageModelChatSystemMessage: extHostTypes.LanguageModelChatSystemMessage,// TODO@jrieken REMOVE - LanguageModelChatUserMessage: extHostTypes.LanguageModelChatUserMessage,// TODO@jrieken REMOVE - LanguageModelChatAssistantMessage: extHostTypes.LanguageModelChatAssistantMessage,// TODO@jrieken REMOVE LanguageModelError: extHostTypes.LanguageModelError, NewSymbolName: extHostTypes.NewSymbolName, NewSymbolNameTag: extHostTypes.NewSymbolNameTag, From d9744cf206355348e541fdaa1ab226236abeff86 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Jun 2024 17:59:27 +0200 Subject: [PATCH 0183/2222] skip version check in oss (#219127) --- .../extensionManagement/common/extensionsScannerService.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 1b6d8f3dab9..386dfed2a29 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -562,10 +562,11 @@ class ExtensionsScanner extends Disposable { @IUriIdentityService protected readonly uriIdentityService: IUriIdentityService, @IFileService protected readonly fileService: IFileService, @IProductService productService: IProductService, + @IEnvironmentService environmentService: IEnvironmentService, @ILogService protected readonly logService: ILogService ) { super(); - this.extensionsEnabledWithApiProposalVersion = productService.extensionsEnabledWithApiProposalVersion?.map(id => id.toLowerCase()) ?? []; + this.extensionsEnabledWithApiProposalVersion = environmentService.isBuilt ? productService.extensionsEnabledWithApiProposalVersion?.map(id => id.toLowerCase()) ?? [] : []; } async scanExtensions(input: ExtensionScannerInput): Promise { @@ -890,9 +891,10 @@ class CachedExtensionsScanner extends ExtensionsScanner { @IUriIdentityService uriIdentityService: IUriIdentityService, @IFileService fileService: IFileService, @IProductService productService: IProductService, + @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService ) { - super(obsoleteFile, extensionsProfileScannerService, uriIdentityService, fileService, productService, logService); + super(obsoleteFile, extensionsProfileScannerService, uriIdentityService, fileService, productService, environmentService, logService); } override async scanExtensions(input: ExtensionScannerInput): Promise { From 46362deb3698b288289fd050f1a9f997935b1884 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Jun 2024 11:06:45 -0700 Subject: [PATCH 0184/2222] Support and in chat (#219053) --- src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts index 50c6029a9e8..4f08d298d9c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts @@ -29,6 +29,8 @@ const allowedHtmlTags = [ 'p', 'pre', 'strong', + 'sub', + 'sup', 'table', 'tbody', 'td', From fe473c806fcd2b76d63644584fad1e355180aa11 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Fri, 28 Jun 2024 13:05:29 -0700 Subject: [PATCH 0185/2222] issue reporter short circuit logic fix (#219002) * add better desccription check * check on listener change --- src/vs/workbench/contrib/issue/browser/issue.ts | 7 ++++--- .../issue/electron-sandbox/issueReporterService2.ts | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/issue/browser/issue.ts b/src/vs/workbench/contrib/issue/browser/issue.ts index 3bc09a5a6c6..4f097ba4603 100644 --- a/src/vs/workbench/contrib/issue/browser/issue.ts +++ b/src/vs/workbench/contrib/issue/browser/issue.ts @@ -770,7 +770,9 @@ export class BaseIssueReporterService extends Disposable { const inputElement = (this.getElementById(inputId)); const inputValidationMessage = this.getElementById(`${inputId}-empty-error`); const descriptionShortMessage = this.getElementById(`description-short-error`); - if (!inputElement.value) { + if (inputId === 'description' && this.nonGitHubIssueUrl && this.data.extensionId) { + return true; + } else if (!inputElement.value) { inputElement.classList.add('invalid-input'); inputValidationMessage?.classList.remove('hidden'); descriptionShortMessage?.classList.add('hidden'); @@ -780,8 +782,7 @@ export class BaseIssueReporterService extends Disposable { descriptionShortMessage?.classList.remove('hidden'); inputValidationMessage?.classList.add('hidden'); return false; - } - else { + } else { inputElement.classList.remove('invalid-input'); inputValidationMessage?.classList.add('hidden'); if (inputId === 'description') { diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService2.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService2.ts index abb6680a5d2..02b3adb817d 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService2.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService2.ts @@ -238,6 +238,7 @@ export class IssueReporter2 extends BaseIssueReporterService { if (this.issueReporterModel.fileOnExtension()) { this.addEventListener('extension-selector', 'change', _ => { this.validateInput('extension-selector'); + this.validateInput('description'); }); } From c91009aea5684f25fa7a97fda494ab82de63f3ac Mon Sep 17 00:00:00 2001 From: Cody Beyer Date: Fri, 28 Jun 2024 13:22:11 -0700 Subject: [PATCH 0186/2222] adding js/py lib for tagging --- .../tags/electron-sandbox/workspaceTagsService.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts index 1fd7dd05886..9f731c0d526 100644 --- a/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-sandbox/workspaceTagsService.ts @@ -229,8 +229,8 @@ const ModulesToLookFor = [ '@azure/cognitiveservices-customvision-training', '@azure/cognitiveservices-face', '@azure/cognitiveservices-translatortext', - 'microsoft-cognitiveservices-speech-sdk' - + 'microsoft-cognitiveservices-speech-sdk', + '@google/generative-ai' ]; const PyMetaModulesToLookFor = [ @@ -394,7 +394,8 @@ const PyModulesToLookFor = [ 'azure-cognitiveservices-vision-contentmoderator', 'azure-cognitiveservices-vision-face', 'azure-mgmt-cognitiveservices', - 'azure-mgmt-search' + 'azure-mgmt-search', + 'google-generativeai' ]; const GoModulesToLookFor = [ @@ -691,6 +692,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.npm.@azure/arm-kubernetesconfiguration" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.react-native-macos" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.react-native-windows" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.@google/generative-ai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.cordova.high" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -911,6 +913,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.trulens" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.trulens-eval" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.py.wandb" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.py.google-generativeai" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/storage/azblob" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/storage/azfile" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.go.mod.github.com/Azure/azure-sdk-for-go/sdk/storage/azqueue" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, From 5fc4b6e87337784009de1006228b0dfe65960c7f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Jun 2024 22:31:08 +0200 Subject: [PATCH 0187/2222] skip version check in oss (#219206) --- .../common/extensionsScannerService.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 386dfed2a29..55073608fa9 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -562,11 +562,11 @@ class ExtensionsScanner extends Disposable { @IUriIdentityService protected readonly uriIdentityService: IUriIdentityService, @IFileService protected readonly fileService: IFileService, @IProductService productService: IProductService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, @ILogService protected readonly logService: ILogService ) { super(); - this.extensionsEnabledWithApiProposalVersion = environmentService.isBuilt ? productService.extensionsEnabledWithApiProposalVersion?.map(id => id.toLowerCase()) ?? [] : []; + this.extensionsEnabledWithApiProposalVersion = productService.extensionsEnabledWithApiProposalVersion?.map(id => id.toLowerCase()) ?? []; } async scanExtensions(input: ExtensionScannerInput): Promise { @@ -673,7 +673,7 @@ class ExtensionsScanner extends Disposable { if (input.validate) { extension = this.validate(extension, input); } - if (manifest.enabledApiProposals && this.extensionsEnabledWithApiProposalVersion.includes(id.toLowerCase())) { + if (manifest.enabledApiProposals && (!this.environmentService.isBuilt || this.extensionsEnabledWithApiProposalVersion.includes(id.toLowerCase()))) { manifest.enabledApiProposals = parseEnabledApiProposalNames([...manifest.enabledApiProposals]); } return extension; @@ -688,7 +688,7 @@ class ExtensionsScanner extends Disposable { validate(extension: IRelaxedScannedExtension, input: ExtensionScannerInput): IRelaxedScannedExtension { let isValid = true; - const validateApiVersion = this.extensionsEnabledWithApiProposalVersion.includes(extension.identifier.id.toLowerCase()); + const validateApiVersion = this.environmentService.isBuilt && this.extensionsEnabledWithApiProposalVersion.includes(extension.identifier.id.toLowerCase()); const validations = validateExtensionManifest(input.productVersion, input.productDate, input.location, extension.manifest, extension.isBuiltin, validateApiVersion); for (const [severity, message] of validations) { if (severity === Severity.Error) { From da36fe717e16f8989b19724e8eaba8bea31a2e21 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Fri, 28 Jun 2024 22:47:41 +0200 Subject: [PATCH 0188/2222] fix #218133 (#219212) --- .../contrib/userDataProfile/browser/userDataProfilesEditor.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts index 8ddcc3527b5..b84e1af173b 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts @@ -1001,9 +1001,11 @@ class ExistingProfileResourceTreeRenderer extends AbstractProfileResourceTreeRen templateData.label.textContent = this.getResourceTypeTitle(element.resourceType); if (root instanceof UserDataProfileElement && root.profile.isDefault) { + templateData.checkbox.domNode.removeAttribute('tabindex'); templateData.checkbox.domNode.classList.add('hide'); } else { templateData.checkbox.domNode.classList.remove('hide'); + templateData.checkbox.domNode.setAttribute('tabindex', '0'); templateData.checkbox.checked = root.getFlag(element.resourceType); templateData.elementDisposables.add(templateData.checkbox.onChange(() => root.setFlag(element.resourceType, templateData.checkbox.checked))); } @@ -1136,6 +1138,7 @@ class ProfileResourceChildTreeItemRenderer extends AbstractProfileResourceTreeRe } if (element.checkbox) { + templateData.checkbox.domNode.setAttribute('tabindex', '0'); templateData.checkbox.domNode.classList.remove('hide'); templateData.checkbox.checked = element.checkbox.isChecked; templateData.checkbox.domNode.ariaLabel = element.checkbox.accessibilityInformation?.label ?? ''; @@ -1143,6 +1146,7 @@ class ProfileResourceChildTreeItemRenderer extends AbstractProfileResourceTreeRe templateData.checkbox.domNode.role = element.checkbox.accessibilityInformation.role; } } else { + templateData.checkbox.domNode.removeAttribute('tabindex'); templateData.checkbox.domNode.classList.add('hide'); } From 206e438ff4390f873715c6ab41fae493eca8aeca Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Jun 2024 13:48:14 -0700 Subject: [PATCH 0189/2222] Fix #218031 (#219215) --- src/vs/workbench/contrib/chat/browser/media/chat.css | 1 - 1 file changed, 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index 403c41b83a1..23de8d70e1d 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -40,7 +40,6 @@ display: flex; align-items: center; gap: 8px; - width: 100%; } .interactive-item-container .header .username { From 032c1b75447ade317715c3d2a82c2d9cd3e55dde Mon Sep 17 00:00:00 2001 From: Aaron Munger Date: Fri, 28 Jun 2024 14:07:28 -0700 Subject: [PATCH 0190/2222] don't show output items if they are empty (#216844) * wait for content to attach toolbar * command to show hidden outputs * avoid perf hit of checking innerText on resize * empty based on 0 content height * just rely on height=0 for empty output * use event to attach toolbar * leave border in, but account for the height it adds * hide again if content goes back to zero height * zero * tests, fix memory leaks * move magic number to common function --- .../browser/controller/cellOutputActions.ts | 31 ++++- .../browser/controller/editActions.ts | 4 +- .../notebook/browser/notebookBrowser.ts | 3 + .../contrib/notebook/browser/view/cellPart.ts | 2 +- .../browser/view/cellParts/cellOutput.ts | 59 +++++--- .../browser/view/renderers/webviewPreloads.ts | 40 ++++-- .../browser/viewModel/cellOutputViewModel.ts | 17 +++ .../browser/viewModel/codeCellViewModel.ts | 9 +- .../notebook/common/notebookContextKeys.ts | 2 + .../notebook/test/browser/cellOutput.test.ts | 130 ++++++++++++++++++ .../test/browser/testNotebookEditor.ts | 3 +- 11 files changed, 261 insertions(+), 39 deletions(-) create mode 100644 src/vs/workbench/contrib/notebook/test/browser/cellOutput.test.ts diff --git a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts index 1350fefa69d..310a367f197 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/cellOutputActions.ts @@ -9,7 +9,7 @@ import { Action2, MenuId, registerAction2 } from 'vs/platform/actions/common/act import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { INotebookOutputActionContext, NOTEBOOK_ACTIONS_CATEGORY } from 'vs/workbench/contrib/notebook/browser/controller/coreActions'; -import { NOTEBOOK_CELL_HAS_OUTPUTS } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS, NOTEBOOK_CELL_HAS_OUTPUTS } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import * as icons from 'vs/workbench/contrib/notebook/browser/notebookIcons'; import { ILogService } from 'vs/platform/log/common/log'; import { copyCellOutput } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; @@ -17,9 +17,38 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { ICellOutputViewModel, ICellViewModel, INotebookEditor, getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellKind, CellUri } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; +import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; export const COPY_OUTPUT_COMMAND_ID = 'notebook.cellOutput.copy'; +registerAction2(class ShowAllOutputsAction extends Action2 { + constructor() { + super({ + id: 'notebook.cellOuput.showEmptyOutputs', + title: localize('notebookActions.showAllOutput', "Show empty outputs"), + menu: { + id: MenuId.NotebookOutputToolbar, + when: ContextKeyExpr.and(NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS) + }, + f1: false, + category: NOTEBOOK_ACTIONS_CATEGORY + }); + } + + run(accessor: ServicesAccessor, context: INotebookOutputActionContext): void { + const cell = context.cell; + if (cell && cell.cellKind === CellKind.Code) { + + for (let i = 1; i < cell.outputsViewModels.length; i++) { + if (!cell.outputsViewModels[i].visible.get()) { + cell.outputsViewModels[i].setVisible(true, true); + (cell as CodeCellViewModel).updateOutputHeight(i, 1, 'command'); + } + } + } + } +}); + registerAction2(class CopyCellOutputAction extends Action2 { constructor() { super({ diff --git a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts index c73973e433e..82b3f2b8eaf 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/editActions.ts @@ -28,7 +28,7 @@ import { CELL_TITLE_CELL_GROUP_ID, CELL_TITLE_OUTPUT_GROUP_ID, CellToolbarOrder, import { NotebookChangeTabDisplaySize, NotebookIndentUsingSpaces, NotebookIndentUsingTabs, NotebookIndentationToSpacesAction, NotebookIndentationToTabsAction } from 'vs/workbench/contrib/notebook/browser/controller/notebookIndentationActions'; import { CHANGE_CELL_LANGUAGE, CellEditState, DETECT_CELL_LANGUAGE, QUIT_EDIT_CELL_COMMAND_ID, getNotebookEditorFromEditorPane } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellEditType, CellKind, ICellEditOperation, NotebookCellExecutionState, NotebookSetting } from 'vs/workbench/contrib/notebook/common/notebookCommon'; -import { NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_INPUT_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; +import { NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_LIST_FOCUSED, NOTEBOOK_CELL_MARKDOWN_EDIT_MODE, NOTEBOOK_CELL_TYPE, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_HAS_OUTPUTS, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_OUTPUT_INPUT_FOCUSED, NOTEBOOK_OUTPUT_FOCUSED, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON, NOTEBOOK_CELL_IS_FIRST_OUTPUT } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; import { INotebookKernelService } from 'vs/workbench/contrib/notebook/common/notebookKernelService'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; @@ -227,7 +227,7 @@ registerAction2(class ClearCellOutputsAction extends NotebookCellAction { }, { id: MenuId.NotebookOutputToolbar, - when: ContextKeyExpr.and(NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE) + when: ContextKeyExpr.and(NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_CELL_EDITABLE, NOTEBOOK_CELL_IS_FIRST_OUTPUT, NOTEBOOK_USE_CONSOLIDATED_OUTPUT_BUTTON) }, ], keybinding: { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 18e44f2a663..f08c5886c42 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -31,6 +31,7 @@ import { IWebviewElement } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { IObservable } from 'vs/base/common/observable'; //#region Shared commands export const EXPAND_CELL_INPUT_COMMAND_ID = 'notebook.cell.expandCellInput'; @@ -108,6 +109,8 @@ export interface ICellOutputViewModel extends IDisposable { pickedMimeType: IOrderedMimeType | undefined; hasMultiMimeType(): boolean; readonly onDidResetRenderer: Event; + readonly visible: IObservable; + setVisible(visible: boolean, force?: boolean): void; resetRenderer(): void; toRawJSON(): any; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts index 546637fae83..1ae8bf98ef5 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellPart.ts @@ -16,7 +16,7 @@ import { ICellExecutionStateChangedEvent } from 'vs/workbench/contrib/notebook/c */ export abstract class CellContentPart extends Disposable { protected currentCell: ICellViewModel | undefined; - protected readonly cellDisposables = new DisposableStore(); + protected readonly cellDisposables = this._register(new DisposableStore()); constructor() { super(); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts index f864ea9345d..5300567754b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput.ts @@ -33,8 +33,9 @@ import { INotebookKernel } from 'vs/workbench/contrib/notebook/common/notebookKe import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite'; import { COPY_OUTPUT_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/controller/cellOutputActions'; -import { CLEAR_CELL_OUTPUTS_COMMAND_ID } from 'vs/workbench/contrib/notebook/browser/controller/editActions'; import { TEXT_BASED_MIMETYPES } from 'vs/workbench/contrib/notebook/browser/contrib/clipboard/cellOutputClipboard'; +import { autorun, observableValue } from 'vs/base/common/observable'; +import { NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS, NOTEBOOK_CELL_IS_FIRST_OUTPUT } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; interface IMimeTypeRenderer extends IQuickPickItem { index: number; @@ -60,13 +61,14 @@ interface IRenderResult { // | | #cell-output-toolbar // | | #output-element class CellOutputElement extends Disposable { - private readonly _renderDisposableStore = this._register(new DisposableStore()); + private readonly toolbarDisposables = this._register(new DisposableStore()); innerContainer?: HTMLElement; renderedOutputContainer!: HTMLElement; renderResult?: IInsetRenderOutput; private readonly contextKeyService: IContextKeyService; + private toolbarAttached = false; constructor( private notebookEditor: INotebookEditorDelegate, @@ -151,7 +153,7 @@ class CellOutputElement extends Disposable { } else { // Another mimetype or renderer is picked, we need to clear the current output and re-render const nextElement = this.innerContainer.nextElementSibling; - this._renderDisposableStore.clear(); + this.toolbarDisposables.clear(); const element = this.innerContainer; if (element) { element.remove(); @@ -207,7 +209,20 @@ class CellOutputElement extends Disposable { } const innerContainer = this._generateInnerOutputContainer(previousSibling, selectedPresentation); - this._attachToolbar(innerContainer, notebookTextModel, this.notebookEditor.activeKernel, index, mimeTypes); + if (index === 0 || this.output.visible.get()) { + this._attachToolbar(innerContainer, notebookTextModel, this.notebookEditor.activeKernel, index, mimeTypes); + } else { + this._register(autorun((reader) => { + const visible = reader.readObservable(this.output.visible); + if (visible && !this.toolbarAttached) { + this._attachToolbar(innerContainer, notebookTextModel, this.notebookEditor.activeKernel, index, mimeTypes); + } else if (!visible) { + this.toolbarDisposables.clear(); + } + this.cellOutputContainer.checkForHiddenOutputs(); + })); + this.cellOutputContainer.hasHiddenOutputs.set(true, undefined); + } this.renderedOutputContainer = DOM.append(innerContainer, DOM.$('.rendered-output')); @@ -291,14 +306,12 @@ class CellOutputElement extends Disposable { return; } - const useConsolidatedButton = this.notebookEditor.notebookOptions.getDisplayOptions().consolidatedOutputButton; - outputItemDiv.style.position = 'relative'; const mimeTypePicker = DOM.$('.cell-output-toolbar'); outputItemDiv.appendChild(mimeTypePicker); - const toolbar = this._renderDisposableStore.add(this.instantiationService.createInstance(WorkbenchToolBar, mimeTypePicker, { + const toolbar = this.toolbarDisposables.add(this.instantiationService.createInstance(WorkbenchToolBar, mimeTypePicker, { renderDropdownAsChildElement: false })); toolbar.context = { @@ -310,20 +323,22 @@ class CellOutputElement extends Disposable { }; // TODO: This could probably be a real registered action, but it has to talk to this output element - const pickAction = new Action('notebook.output.pickMimetype', nls.localize('pickMimeType', "Change Presentation"), ThemeIcon.asClassName(mimetypeIcon), undefined, - async _context => this._pickActiveMimeTypeRenderer(outputItemDiv, notebookTextModel, kernel, this.output)); + const pickAction = this.toolbarDisposables.add(new Action('notebook.output.pickMimetype', nls.localize('pickMimeType', "Change Presentation"), ThemeIcon.asClassName(mimetypeIcon), undefined, + async _context => this._pickActiveMimeTypeRenderer(outputItemDiv, notebookTextModel, kernel, this.output))); + + const menuContextKeyService = this.toolbarDisposables.add(this.contextKeyService.createScoped(outputItemDiv)); + const hasHiddenOutputs = NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS.bindTo(menuContextKeyService); + const isFirstCellOutput = NOTEBOOK_CELL_IS_FIRST_OUTPUT.bindTo(menuContextKeyService); + isFirstCellOutput.set(index === 0); + this.toolbarDisposables.add(autorun((reader) => { hasHiddenOutputs.set(reader.readObservable(this.cellOutputContainer.hasHiddenOutputs)); })); + const menu = this.toolbarDisposables.add(this.menuService.createMenu(MenuId.NotebookOutputToolbar, menuContextKeyService)); - const menu = this._renderDisposableStore.add(this.menuService.createMenu(MenuId.NotebookOutputToolbar, this.contextKeyService)); const updateMenuToolbar = () => { const primary: IAction[] = []; let secondary: IAction[] = []; const result = { primary, secondary }; - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, result, () => false); - if (index > 0 || !useConsolidatedButton) { - // clear outputs should only appear in the first output item's menu - secondary = secondary.filter((action) => action.id !== CLEAR_CELL_OUTPUTS_COMMAND_ID); - } + createAndFillInActionBarActions(menu!, { shouldForwardArgs: true }, result, () => false); if (!isCopyEnabled) { secondary = secondary.filter((action) => action.id !== COPY_OUTPUT_COMMAND_ID); } @@ -334,8 +349,7 @@ class CellOutputElement extends Disposable { toolbar.setActions([], secondary); }; updateMenuToolbar(); - this._renderDisposableStore.add(menu.onDidChange(updateMenuToolbar)); - + this.toolbarDisposables.add(menu.onDidChange(updateMenuToolbar)); } private async _pickActiveMimeTypeRenderer(outputItemDiv: HTMLElement, notebookTextModel: NotebookTextModel, kernel: INotebookKernel | undefined, viewModel: ICellOutputViewModel) { @@ -397,7 +411,7 @@ class CellOutputElement extends Disposable { // user chooses another mimetype const nextElement = outputItemDiv.nextElementSibling; - this._renderDisposableStore.clear(); + this.toolbarDisposables.clear(); const element = this.innerContainer; if (element) { element.remove(); @@ -479,6 +493,15 @@ export class CellOutputContainer extends CellContentPart { private _outputEntries: OutputEntryViewHandler[] = []; private _hasStaleOutputs: boolean = false; + hasHiddenOutputs = observableValue('hasHiddenOutputs', false); + checkForHiddenOutputs() { + if (this._outputEntries.find(entry => { return entry.model.visible; })) { + this.hasHiddenOutputs.set(true, undefined); + } else { + this.hasHiddenOutputs.set(false, undefined); + } + } + get renderedOutputEntries() { return this._outputEntries; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 7f79531b37e..f80e4f40ccc 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -461,7 +461,7 @@ async function webviewPreloads(ctx: PreloadContext) { id, height, init: update.init, - isOutput: update.isOutput, + isOutput: update.isOutput }); } else { this.pending.set(id, { @@ -484,6 +484,11 @@ async function webviewPreloads(ctx: PreloadContext) { } }; + function elementHasContent(height: number) { + // we need to account for a potential 1px top and bottom border on a child within the output container + return height > 2.1; + } + const resizeObserver = new class { private readonly _observer: ResizeObserver; @@ -519,23 +524,23 @@ async function webviewPreloads(ctx: PreloadContext) { continue; } - const newHeight = entry.contentRect.height; + const hasContent = elementHasContent(entry.contentRect.height); const shouldUpdatePadding = - (newHeight !== 0 && observedElementInfo.lastKnownPadding === 0) || - (newHeight === 0 && observedElementInfo.lastKnownPadding !== 0); + (hasContent && observedElementInfo.lastKnownPadding === 0) || + (!hasContent && observedElementInfo.lastKnownPadding !== 0); if (shouldUpdatePadding) { // Do not update dimension in resize observer window.requestAnimationFrame(() => { - if (newHeight !== 0) { + if (hasContent) { entry.target.style.padding = `${ctx.style.outputNodePadding}px ${ctx.style.outputNodePadding}px ${ctx.style.outputNodePadding}px ${ctx.style.outputNodeLeftPadding}px`; } else { entry.target.style.padding = `0px`; } - this.updateHeight(observedElementInfo, entry.target.offsetHeight); + this.updateHeight(observedElementInfo, hasContent ? entry.target.offsetHeight : 0); }); } else { - this.updateHeight(observedElementInfo, entry.target.offsetHeight); + this.updateHeight(observedElementInfo, hasContent ? entry.target.offsetHeight : 0); } } }); @@ -2755,10 +2760,6 @@ async function webviewPreloads(ctx: PreloadContext) { this.element.style.visibility = ''; this.element.style.top = `${top}px`; - - dimensionUpdater.updateHeight(outputId, outputContainer.element.offsetHeight, { - isOutput: true, - }); } public hide() { @@ -2941,17 +2942,26 @@ async function webviewPreloads(ctx: PreloadContext) { const offsetHeight = this.element.offsetHeight; const cps = document.defaultView!.getComputedStyle(this.element); - if (offsetHeight !== 0 && cps.padding === '0px') { - // we set padding to zero if the output height is zero (then we can have a zero-height output DOM node) + const verticalPadding = parseFloat(cps.paddingTop) + parseFloat(cps.paddingBottom); + const contentHeight = offsetHeight - verticalPadding; + if (elementHasContent(contentHeight) && cps.padding === '0px') { + // we set padding to zero if the output has no content (then we can have a zero-height output DOM node) // thus we need to ensure the padding is accounted when updating the init height of the output dimensionUpdater.updateHeight(this.outputId, offsetHeight + ctx.style.outputNodePadding * 2, { isOutput: true, - init: true, + init: true }); this.element.style.padding = `${ctx.style.outputNodePadding}px ${ctx.style.outputNodePadding}px ${ctx.style.outputNodePadding}px ${ctx.style.outputNodeLeftPadding}`; - } else { + } else if (elementHasContent(contentHeight)) { dimensionUpdater.updateHeight(this.outputId, this.element.offsetHeight, { + isOutput: true, + init: true + }); + this.element.style.padding = `0 ${ctx.style.outputNodePadding}px 0 ${ctx.style.outputNodeLeftPadding}`; + } else { + // we have a zero-height output DOM node + dimensionUpdater.updateHeight(this.outputId, 0, { isOutput: true, init: true, }); diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel.ts index e8cf31df9e3..5c523a51fac 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/cellOutputViewModel.ts @@ -5,6 +5,7 @@ import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { observableValue } from 'vs/base/common/observable'; import { ICellOutputViewModel, IGenericCellViewModel } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookTextModel } from 'vs/workbench/contrib/notebook/common/model/notebookTextModel'; import { ICellOutput, IOrderedMimeType, RENDERER_NOT_AVAILABLE } from 'vs/workbench/contrib/notebook/common/notebookCommon'; @@ -14,6 +15,22 @@ let handle = 0; export class CellOutputViewModel extends Disposable implements ICellOutputViewModel { private _onDidResetRendererEmitter = this._register(new Emitter()); readonly onDidResetRenderer = this._onDidResetRendererEmitter.event; + + private alwaysShow = false; + visible = observableValue('outputVisible', false); + setVisible(visible = true, force: boolean = false) { + if (!visible && this.alwaysShow) { + // we are forced to show, so no-op + return; + } + + if (force && visible) { + this.alwaysShow = true; + } + + this.visible.set(visible, undefined); + } + outputHandle = handle++; get model(): ICellOutput { return this._outputRawData; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 06c8c851f3b..8ce1e7f7369 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -464,7 +464,14 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod } this._ensureOutputsTop(); - if (height < 28 && this._outputViewModels[index].hasMultiMimeType()) { + + if (index === 0 || height > 0) { + this._outputViewModels[index].setVisible(true); + } else if (height === 0) { + this._outputViewModels[index].setVisible(false); + } + + if (this._outputViewModels[index].visible.get() && height < 28) { height = 28; } diff --git a/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts b/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts index c4961dd6dad..e659674d527 100644 --- a/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts +++ b/src/vs/workbench/contrib/notebook/common/notebookContextKeys.ts @@ -47,6 +47,8 @@ export type NotebookCellExecutionStateContext = 'idle' | 'pending' | 'executing' export const NOTEBOOK_CELL_EXECUTION_STATE = new RawContextKey('notebookCellExecutionState', undefined); export const NOTEBOOK_CELL_EXECUTING = new RawContextKey('notebookCellExecuting', false); // This only exists to simplify a context key expression, see #129625 export const NOTEBOOK_CELL_HAS_OUTPUTS = new RawContextKey('notebookCellHasOutputs', false); +export const NOTEBOOK_CELL_IS_FIRST_OUTPUT = new RawContextKey('notebookCellIsFirstOutput', false); +export const NOTEBOOK_CELL_HAS_HIDDEN_OUTPUTS = new RawContextKey('hasHiddenOutputs', false); export const NOTEBOOK_CELL_INPUT_COLLAPSED = new RawContextKey('notebookCellInputIsCollapsed', false); export const NOTEBOOK_CELL_OUTPUT_COLLAPSED = new RawContextKey('notebookCellOutputIsCollapsed', false); export const NOTEBOOK_CELL_RESOURCE = new RawContextKey('notebookCellResource', ''); diff --git a/src/vs/workbench/contrib/notebook/test/browser/cellOutput.test.ts b/src/vs/workbench/contrib/notebook/test/browser/cellOutput.test.ts new file mode 100644 index 00000000000..f08ca269a47 --- /dev/null +++ b/src/vs/workbench/contrib/notebook/test/browser/cellOutput.test.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { CellOutputContainer } from 'vs/workbench/contrib/notebook/browser/view/cellParts/cellOutput'; +import { CodeCellRenderTemplate } from 'vs/workbench/contrib/notebook/browser/view/notebookRenderingCommon'; +import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; +import { CellKind, INotebookRendererInfo, IOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { setupInstantiationService, withTestNotebook } from 'vs/workbench/contrib/notebook/test/browser/testNotebookEditor'; +import { FastDomNode } from 'vs/base/browser/fastDomNode'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +import { mock } from 'vs/base/test/common/mock'; +import { IMenu, IMenuService } from 'vs/platform/actions/common/actions'; +import { Event } from 'vs/base/common/event'; +import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; + +suite('CellOutput', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + let instantiationService: TestInstantiationService; + let outputMenus: IMenu[] = []; + + setup(() => { + outputMenus = []; + instantiationService = setupInstantiationService(store); + instantiationService.stub(INotebookService, new class extends mock() { + override getOutputMimeTypeInfo() { + return [{ + rendererId: 'plainTextRendererId', + mimeType: 'text/plain', + isTrusted: true + }, { + rendererId: 'htmlRendererId', + mimeType: 'text/html', + isTrusted: true + }]; + } + override getRendererInfo(): INotebookRendererInfo { + return { + id: 'rendererId', + displayName: 'Stubbed Renderer', + extensionId: { _lower: 'id', value: 'id' }, + } as INotebookRendererInfo; + } + }); + instantiationService.stub(IMenuService, new class extends mock() { + override createMenu() { + const menu = new class extends mock() { + override onDidChange = Event.None; + override getActions() { return []; } + override dispose() { outputMenus = outputMenus.filter(item => item !== menu); } + }; + outputMenus.push(menu); + return menu; + } + }); + }); + + test('Render cell output items with multiple mime types', async function () { + const outputItem = { data: VSBuffer.fromString('output content'), mime: 'text/plain' }; + const htmlOutputItem = { data: VSBuffer.fromString('output content'), mime: 'text/html' }; + const output1: IOutputDto = { outputId: 'abc', outputs: [outputItem, htmlOutputItem] }; + const output2: IOutputDto = { outputId: 'def', outputs: [outputItem, htmlOutputItem] }; + + await withTestNotebook( + [ + ['print(output content)', 'python', CellKind.Code, [output1, output2], {}], + ], + (editor, viewModel, disposables, accessor) => { + + const cell = viewModel.viewCells[0] as CodeCellViewModel; + const cellTemplate = createCellTemplate(disposables); + const output = disposables.add(accessor.createInstance(CellOutputContainer, editor, cell, cellTemplate, { limit: 100 })); + output.render(); + cell.outputsViewModels[0].setVisible(true); + assert.strictEqual(outputMenus.length, 1, 'should have 1 output menus'); + assert(cellTemplate.outputContainer.domNode.style.display !== 'none', 'output container should be visible'); + cell.outputsViewModels[1].setVisible(true); + assert.strictEqual(outputMenus.length, 2, 'should have 2 output menus'); + cell.outputsViewModels[1].setVisible(true); + assert.strictEqual(outputMenus.length, 2, 'should still have 2 output menus'); + }, + instantiationService + ); + }); + + test('One of many cell outputs becomes hidden', async function () { + const outputItem = { data: VSBuffer.fromString('output content'), mime: 'text/plain' }; + const htmlOutputItem = { data: VSBuffer.fromString('output content'), mime: 'text/html' }; + const output1: IOutputDto = { outputId: 'abc', outputs: [outputItem, htmlOutputItem] }; + const output2: IOutputDto = { outputId: 'def', outputs: [outputItem, htmlOutputItem] }; + const output3: IOutputDto = { outputId: 'ghi', outputs: [outputItem, htmlOutputItem] }; + + await withTestNotebook( + [ + ['print(output content)', 'python', CellKind.Code, [output1, output2, output3], {}], + ], + (editor, viewModel, disposables, accessor) => { + + const cell = viewModel.viewCells[0] as CodeCellViewModel; + const cellTemplate = createCellTemplate(disposables); + const output = disposables.add(accessor.createInstance(CellOutputContainer, editor, cell, cellTemplate, { limit: 100 })); + output.render(); + cell.outputsViewModels[0].setVisible(true); + cell.outputsViewModels[1].setVisible(true); + cell.outputsViewModels[2].setVisible(true); + cell.outputsViewModels[1].setVisible(false); + assert(cellTemplate.outputContainer.domNode.style.display !== 'none', 'output container should be visible'); + assert.strictEqual(outputMenus.length, 2, 'should have 2 output menus'); + }, + instantiationService + ); + }); + + +}); + +function createCellTemplate(disposables: DisposableStore) { + return { + outputContainer: new FastDomNode(document.createElement('div')), + outputShowMoreContainer: new FastDomNode(document.createElement('div')), + focusSinkElement: document.createElement('div'), + templateDisposables: disposables, + elementDisposables: disposables, + } as unknown as CodeCellRenderTemplate; +} diff --git a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts index 830ac41e6a8..117ce7aaeda 100644 --- a/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/test/browser/testNotebookEditor.ts @@ -178,7 +178,7 @@ export class NotebookEditorTestModel extends EditorModel implements INotebookEdi } } -export function setupInstantiationService(disposables: DisposableStore) { +export function setupInstantiationService(disposables: Pick) { const instantiationService = disposables.add(new TestInstantiationService()); const testThemeService = new TestThemeService(); instantiationService.stub(ILanguageService, disposables.add(new LanguageService())); @@ -296,6 +296,7 @@ function _createTestNotebookEditor(instantiationService: TestInstantiationServic override setCellEditorSelection() { } override async revealRangeInCenterIfOutsideViewportAsync() { } override async layoutNotebookCell() { } + override async createOutput() { } override async removeInset() { } override async focusNotebookCell(cell: ICellViewModel, focusItem: 'editor' | 'container' | 'output') { cell.focusMode = focusItem === 'editor' ? CellFocusMode.Editor From c8a4a255778fd9f8ef729b521f0ed0d8c691e5be Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Jun 2024 14:25:19 -0700 Subject: [PATCH 0191/2222] Add 'command' to chat telemetry (#219218) --- .../browser/actions/chatCodeblockActions.ts | 5 + .../chat/browser/actions/chatTitleActions.ts | 3 + .../contrib/chat/browser/chatWidget.ts | 1 + .../contrib/chat/common/chatService.ts | 1 + .../contrib/chat/common/chatServiceImpl.ts | 94 +------------- .../chat/common/chatServiceTelemetry.ts | 121 ++++++++++++++++++ .../browser/inlineChatController.ts | 2 + 7 files changed, 138 insertions(+), 89 deletions(-) create mode 100644 src/vs/workbench/contrib/chat/common/chatServiceTelemetry.ts diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index b94a9434cc5..378d6857e48 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -112,6 +112,7 @@ export function registerChatCodeBlockActions() { const chatService = accessor.get(IChatService); chatService.notifyUserAction({ agentId: context.element.agent?.id, + command: context.element.slashCommand?.name, sessionId: context.element.sessionId, requestId: context.element.requestId, result: context.element.result, @@ -157,6 +158,7 @@ export function registerChatCodeBlockActions() { if (element) { chatService.notifyUserAction({ agentId: element.agent?.id, + command: element.slashCommand?.name, sessionId: element.sessionId, requestId: element.requestId, result: element.result, @@ -356,6 +358,7 @@ export function registerChatCodeBlockActions() { const chatService = accessor.get(IChatService); chatService.notifyUserAction({ agentId: context.element.agent?.id, + command: context.element.slashCommand?.name, sessionId: context.element.sessionId, requestId: context.element.requestId, result: context.element.result, @@ -401,6 +404,7 @@ export function registerChatCodeBlockActions() { if (isResponseVM(context.element)) { chatService.notifyUserAction({ agentId: context.element.agent?.id, + command: context.element.slashCommand?.name, sessionId: context.element.sessionId, requestId: context.element.requestId, result: context.element.result, @@ -493,6 +497,7 @@ export function registerChatCodeBlockActions() { if (isResponseVM(context.element)) { chatService.notifyUserAction({ agentId: context.element.agent?.id, + command: context.element.slashCommand?.name, sessionId: context.element.sessionId, requestId: context.element.requestId, result: context.element.result, diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index 4a2455ea71f..e5ce3cbefbc 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -51,6 +51,7 @@ export function registerChatTitleActions() { const chatService = accessor.get(IChatService); chatService.notifyUserAction({ agentId: item.agent?.id, + command: item.slashCommand?.name, sessionId: item.sessionId, requestId: item.requestId, result: item.result, @@ -90,6 +91,7 @@ export function registerChatTitleActions() { const chatService = accessor.get(IChatService); chatService.notifyUserAction({ agentId: item.agent?.id, + command: item.slashCommand?.name, sessionId: item.sessionId, requestId: item.requestId, result: item.result, @@ -128,6 +130,7 @@ export function registerChatTitleActions() { const chatService = accessor.get(IChatService); chatService.notifyUserAction({ agentId: item.agent?.id, + command: item.slashCommand?.name, sessionId: item.sessionId, requestId: item.requestId, result: item.result, diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 4a8aca892cf..c6493b1a4f0 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -628,6 +628,7 @@ export class ChatWidget extends Disposable implements IChatWidget { sessionId: this.viewModel.sessionId, requestId: e.response.requestId, agentId: e.response.agent?.id, + command: e.response.slashCommand?.name, result: e.response.result, action: { kind: 'followUp', diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index fc4add785ff..f10564d7828 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -252,6 +252,7 @@ export type ChatUserAction = IChatVoteAction | IChatCopyAction | IChatInsertActi export interface IChatUserActionEvent { action: ChatUserAction; agentId: string | undefined; + command: string | undefined; sessionId: string; requestId: string; result: IChatAgentResult | undefined; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 7c115514e91..a0cf7da28a9 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -16,7 +16,6 @@ import { revive } from 'vs/base/common/marshalling'; import { StopWatch } from 'vs/base/common/stopwatch'; import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { Progress } from 'vs/platform/progress/common/progress'; @@ -27,7 +26,8 @@ import { ChatAgentLocation, IChatAgent, IChatAgentRequest, IChatAgentResult, ICh import { ChatModel, ChatRequestModel, ChatRequestRemovalReason, ChatWelcomeMessageModel, IChatModel, IChatRequestModel, IChatRequestVariableData, IChatRequestVariableEntry, IChatResponseModel, IExportableChatData, ISerializableChatData, ISerializableChatsData, getHistoryEntriesFromModel, updateRanges } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, IParsedChatRequest, chatAgentLeader, chatSubcommandLeader, getPromptText } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; -import { ChatCopyKind, IChatCompleteResponse, IChatDetail, IChatFollowup, IChatProgress, IChatSendRequestData, IChatSendRequestOptions, IChatSendRequestResponseState, IChatService, IChatTransferredSessionData, IChatUserActionEvent, ChatAgentVoteDirection } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatCompleteResponse, IChatDetail, IChatFollowup, IChatProgress, IChatSendRequestData, IChatSendRequestOptions, IChatSendRequestResponseState, IChatService, IChatTransferredSessionData, IChatUserActionEvent } from 'vs/workbench/contrib/chat/common/chatService'; +import { ChatServiceTelemetry } from 'vs/workbench/contrib/chat/common/chatServiceTelemetry'; import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import { ChatMessageRole, IChatMessage } from 'vs/workbench/contrib/chat/common/languageModels'; @@ -68,64 +68,6 @@ type ChatProviderInvokedClassification = { comment: 'Provides insight into the performance of Chat agents.'; }; -type ChatVoteEvent = { - direction: 'up' | 'down'; - agentId: string; -}; - -type ChatVoteClassification = { - direction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user voted up or down.' }; - agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that this vote is for.' }; - owner: 'roblourens'; - comment: 'Provides insight into the performance of Chat agents.'; -}; - -type ChatCopyEvent = { - copyKind: 'action' | 'toolbar'; - agentId: string; -}; - -type ChatCopyClassification = { - copyKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How the copy was initiated.' }; - agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that the copy acted on.' }; - owner: 'roblourens'; - comment: 'Provides insight into the usage of Chat features.'; -}; - -type ChatInsertEvent = { - newFile: boolean; - agentId: string; -}; - -type ChatInsertClassification = { - newFile: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the code was inserted into a new untitled file.' }; - agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that this insertion is for.' }; - owner: 'roblourens'; - comment: 'Provides insight into the usage of Chat features.'; -}; - -type ChatCommandEvent = { - commandId: string; - agentId: string; -}; - -type ChatCommandClassification = { - commandId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The id of the command that was executed.' }; - agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the related chat agent.' }; - owner: 'roblourens'; - comment: 'Provides insight into the usage of Chat features.'; -}; - -type ChatTerminalEvent = { - languageId: string; -}; - -type ChatTerminalClassification = { - languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The language of the code that was run in the terminal.' }; - owner: 'roblourens'; - comment: 'Provides insight into the usage of Chat features.'; -}; - const maxPersistedSessions = 25; export class ChatService extends Disposable implements IChatService { @@ -148,6 +90,7 @@ export class ChatService extends Disposable implements IChatService { public readonly onDidDisposeSession = this._onDidDisposeSession.event; private readonly _sessionFollowupCancelTokens = this._register(new DisposableMap()); + private readonly _chatServiceTelemetry: ChatServiceTelemetry; constructor( @IStorageService private readonly storageService: IStorageService, @@ -162,6 +105,7 @@ export class ChatService extends Disposable implements IChatService { ) { super(); + this._chatServiceTelemetry = this.instantiationService.createInstance(ChatServiceTelemetry); const sessionData = storageService.get(serializedChatKey, StorageScope.WORKSPACE, ''); if (sessionData) { this._persistedSessions = this.deserializeChats(sessionData); @@ -212,35 +156,7 @@ export class ChatService extends Disposable implements IChatService { } notifyUserAction(action: IChatUserActionEvent): void { - if (action.action.kind === 'vote') { - this.telemetryService.publicLog2('interactiveSessionVote', { - direction: action.action.direction === ChatAgentVoteDirection.Up ? 'up' : 'down', - agentId: action.agentId ?? '' - }); - } else if (action.action.kind === 'copy') { - this.telemetryService.publicLog2('interactiveSessionCopy', { - copyKind: action.action.copyKind === ChatCopyKind.Action ? 'action' : 'toolbar', - agentId: action.agentId ?? '' - }); - } else if (action.action.kind === 'insert') { - this.telemetryService.publicLog2('interactiveSessionInsert', { - newFile: !!action.action.newFile, - agentId: action.agentId ?? '' - }); - } else if (action.action.kind === 'command') { - // TODO not currently called - const command = CommandsRegistry.getCommand(action.action.commandButton.command.id); - const commandId = command ? action.action.commandButton.command.id : 'INVALID'; - this.telemetryService.publicLog2('interactiveSessionCommand', { - commandId, - agentId: action.agentId ?? '' - }); - } else if (action.action.kind === 'runInTerminal') { - this.telemetryService.publicLog2('interactiveSessionRunInTerminal', { - languageId: action.action.languageId ?? '' - }); - } - + this._chatServiceTelemetry.notifyUserAction(action); this._onDidPerformUserAction.fire(action); } diff --git a/src/vs/workbench/contrib/chat/common/chatServiceTelemetry.ts b/src/vs/workbench/contrib/chat/common/chatServiceTelemetry.ts new file mode 100644 index 00000000000..ec8d5aa339e --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/chatServiceTelemetry.ts @@ -0,0 +1,121 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IChatUserActionEvent, ChatAgentVoteDirection, ChatCopyKind } from 'vs/workbench/contrib/chat/common/chatService'; + +type ChatVoteEvent = { + direction: 'up' | 'down'; + agentId: string; + command: string | undefined; +}; + +type ChatVoteClassification = { + direction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user voted up or down.' }; + agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that this vote is for.' }; + command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the slash command that this vote is for.' }; + owner: 'roblourens'; + comment: 'Provides insight into the performance of Chat agents.'; +}; + +type ChatCopyEvent = { + copyKind: 'action' | 'toolbar'; + agentId: string; + command: string | undefined; +}; + +type ChatCopyClassification = { + copyKind: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How the copy was initiated.' }; + agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that the copy acted on.' }; + command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the slash command the copy acted on.' }; + owner: 'roblourens'; + comment: 'Provides insight into the usage of Chat features.'; +}; + +type ChatInsertEvent = { + newFile: boolean; + agentId: string; + command: string | undefined; +}; + +type ChatInsertClassification = { + newFile: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the code was inserted into a new untitled file.' }; + agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the chat agent that this insertion is for.' }; + command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the slash command that this insertion is for.' }; + owner: 'roblourens'; + comment: 'Provides insight into the usage of Chat features.'; +}; + +type ChatCommandEvent = { + commandId: string; + agentId: string; + command: string | undefined; +}; + +type ChatCommandClassification = { + commandId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The id of the command that was executed.' }; + agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the related chat agent.' }; + command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the related slash command.' }; + owner: 'roblourens'; + comment: 'Provides insight into the usage of Chat features.'; +}; + +type ChatTerminalEvent = { + languageId: string; + agentId: string; + command: string | undefined; +}; + +type ChatTerminalClassification = { + languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The language of the code that was run in the terminal.' }; + agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the related chat agent.' }; + command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the related slash command.' }; + owner: 'roblourens'; + comment: 'Provides insight into the usage of Chat features.'; +}; + +export class ChatServiceTelemetry { + constructor( + @ITelemetryService private readonly telemetryService: ITelemetryService, + ) { } + + notifyUserAction(action: IChatUserActionEvent): void { + if (action.action.kind === 'vote') { + this.telemetryService.publicLog2('interactiveSessionVote', { + direction: action.action.direction === ChatAgentVoteDirection.Up ? 'up' : 'down', + agentId: action.agentId ?? '', + command: action.command, + }); + } else if (action.action.kind === 'copy') { + this.telemetryService.publicLog2('interactiveSessionCopy', { + copyKind: action.action.copyKind === ChatCopyKind.Action ? 'action' : 'toolbar', + agentId: action.agentId ?? '', + command: action.command, + }); + } else if (action.action.kind === 'insert') { + this.telemetryService.publicLog2('interactiveSessionInsert', { + newFile: !!action.action.newFile, + agentId: action.agentId ?? '', + command: action.command, + }); + } else if (action.action.kind === 'command') { + // TODO not currently called + const command = CommandsRegistry.getCommand(action.action.commandButton.command.id); + const commandId = command ? action.action.commandButton.command.id : 'INVALID'; + this.telemetryService.publicLog2('interactiveSessionCommand', { + commandId, + agentId: action.agentId ?? '', + command: action.command, + }); + } else if (action.action.kind === 'runInTerminal') { + this.telemetryService.publicLog2('interactiveSessionRunInTerminal', { + languageId: action.action.languageId ?? '', + agentId: action.agentId ?? '', + command: action.command, + }); + } + } +} diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index ec0d7cf665d..eda96113c94 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -1074,6 +1074,7 @@ export class InlineChatController implements IEditorContribution { sessionId: response.session.sessionId, requestId: response.requestId, agentId: response.agent?.id, + command: response.slashCommand?.name, result: response.result, action: { kind: 'inlineChat', @@ -1099,6 +1100,7 @@ export class InlineChatController implements IEditorContribution { sessionId: response.session.sessionId, requestId: response.requestId, agentId: response.agent?.id, + command: response.slashCommand?.name, result: response.result, action: { kind: 'inlineChat', From 5bece9e46a005096f4b137b292327d4af1b57073 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Fri, 28 Jun 2024 15:39:18 -0700 Subject: [PATCH 0192/2222] Clear all workspace chats should also clear all open chats (#219198) Fix #218034 --- .../chat/browser/actions/chatActions.ts | 20 +++++++++++++++++++ .../contrib/chat/browser/actions/chatClear.ts | 14 ++++++++----- .../chat/browser/actions/chatClearActions.ts | 2 +- .../chat/browser/actions/chatMoveActions.ts | 13 ++++++------ src/vs/workbench/contrib/chat/browser/chat.ts | 8 ++------ .../contrib/chat/browser/chatEditor.ts | 8 +++++--- .../contrib/chat/browser/chatViewPane.ts | 5 ++--- .../contrib/chat/browser/chatWidget.ts | 2 -- 8 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index b872b925229..6b2e2c26435 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -15,6 +15,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { IsLinuxContext, IsWindowsContext } from 'vs/platform/contextkey/common/contextkeys'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { IQuickInputButton, IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { clearChatEditor } from 'vs/workbench/contrib/chat/browser/actions/chatClear'; import { CHAT_VIEW_ID, IChatWidgetService, showChatView } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor'; import { ChatEditorInput } from 'vs/workbench/contrib/chat/browser/chatEditorInput'; @@ -24,6 +25,7 @@ import { CONTEXT_CHAT_ENABLED, CONTEXT_CHAT_INPUT_CURSOR_AT_TOP, CONTEXT_CHAT_LO import { IChatDetail, IChatService } from 'vs/workbench/contrib/chat/common/chatService'; import { IChatRequestViewModel, IChatResponseViewModel, isRequestVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; import { IChatWidgetHistoryService } from 'vs/workbench/contrib/chat/common/chatWidgetHistoryService'; +import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP, IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; @@ -228,8 +230,26 @@ export function registerChatActions() { }); } async run(accessor: ServicesAccessor, ...args: any[]) { + const editorGroupsService = accessor.get(IEditorGroupsService); + const viewsService = accessor.get(IViewsService); + const chatService = accessor.get(IChatService); chatService.clearAllHistoryEntries(); + + const chatView = viewsService.getViewWithId(CHAT_VIEW_ID) as ChatViewPane | undefined; + if (chatView) { + chatView.widget.clear(); + } + + // Clear all chat editors. Have to go this route because the chat editor may be in the background and + // not have a ChatEditorInput. + editorGroupsService.groups.forEach(group => { + group.editors.forEach(editor => { + if (editor instanceof ChatEditorInput) { + clearChatEditor(accessor, editor); + } + }); + }); } }); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClear.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClear.ts index 00ffcaed8c7..cd85340c42a 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClear.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClear.ts @@ -6,18 +6,22 @@ import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatEditor'; import { ChatEditorInput } from 'vs/workbench/contrib/chat/browser/chatEditorInput'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -export async function clearChatEditor(accessor: ServicesAccessor): Promise { +export async function clearChatEditor(accessor: ServicesAccessor, chatEditorInput?: ChatEditorInput): Promise { const editorService = accessor.get(IEditorService); - const editorGroupsService = accessor.get(IEditorGroupsService); - const chatEditorInput = editorService.activeEditor; + if (!chatEditorInput) { + const editorInput = editorService.activeEditor; + chatEditorInput = editorInput instanceof ChatEditorInput ? editorInput : undefined; + } + if (chatEditorInput instanceof ChatEditorInput) { + // A chat editor can only be open in one group + const identifier = editorService.findEditors(chatEditorInput.resource)[0]; await editorService.replaceEditors([{ editor: chatEditorInput, replacement: { resource: ChatEditorInput.getNewEditorUri(), options: { pinned: true } satisfies IChatEditorOptions } - }], editorGroupsService.activeGroup); + }], identifier.groupId); } } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index b74d1b38b9c..1c6572f3415 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -81,7 +81,7 @@ export function registerNewChatActions() { if (isChatViewTitleActionContext(context)) { // Is running in the Chat view title announceChatCleared(accessibilitySignalService); - context.chatView.clear(); + context.chatView.widget.clear(); context.chatView.widget.focusInput(); } else { // Is running from f1 or keybinding diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts index 195ab2b3aa1..d04e49d6218 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts @@ -92,7 +92,6 @@ export function registerMoveActions() { async function executeMoveToAction(accessor: ServicesAccessor, moveTo: MoveToNewLocation, chatView?: ChatViewPane) { const widgetService = accessor.get(IChatWidgetService); - const viewService = accessor.get(IViewsService); const editorService = accessor.get(IEditorService); const widget = chatView?.widget ?? widgetService.lastFocusedWidget; @@ -107,9 +106,8 @@ async function executeMoveToAction(accessor: ServicesAccessor, moveTo: MoveToNew } const sessionId = viewModel.sessionId; - const view = await viewService.openView(widget.viewContext.viewId) as ChatViewPane; - const viewState = view.widget.getViewState(); - view.clear(); + const viewState = widget.getViewState(); + widget.clear(); await editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options: { target: { sessionId }, pinned: true, viewState: viewState } }, moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP); } @@ -120,11 +118,14 @@ async function moveToSidebar(accessor: ServicesAccessor): Promise { const editorGroupService = accessor.get(IEditorGroupsService); const chatEditorInput = editorService.activeEditor; + let view: ChatViewPane; if (chatEditorInput instanceof ChatEditorInput && chatEditorInput.sessionId) { await editorService.closeEditor({ editor: chatEditorInput, groupId: editorGroupService.activeGroup.id }); - const view = await viewsService.openView(CHAT_VIEW_ID) as ChatViewPane; + view = await viewsService.openView(CHAT_VIEW_ID) as ChatViewPane; view.loadSession(chatEditorInput.sessionId); } else { - await viewsService.openView(CHAT_VIEW_ID); + view = await viewsService.openView(CHAT_VIEW_ID) as ChatViewPane; } + + view.focus(); } diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index c02338c87be..99bb40c2ca4 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -13,7 +13,7 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ChatViewPane } from 'vs/workbench/contrib/chat/browser/chatViewPane'; -import { IChatWidgetContrib } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { IChatViewState, IChatWidgetContrib } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { ICodeBlockActionContext } from 'vs/workbench/contrib/chat/browser/codeBlockPart'; import { ChatAgentLocation, IChatAgentCommand, IChatAgentData } from 'vs/workbench/contrib/chat/common/chatAgents'; import { IChatRequestVariableEntry, IChatResponseModel } from 'vs/workbench/contrib/chat/common/chatModel'; @@ -34,7 +34,6 @@ export interface IChatWidgetService { readonly lastFocusedWidget: IChatWidget | undefined; getWidgetByInputUri(uri: URI): IChatWidget | undefined; - getWidgetBySessionId(sessionId: string): IChatWidget | undefined; } @@ -172,10 +171,7 @@ export interface IChatWidget { getLastFocusedFileTreeForResponse(response: IChatResponseViewModel): IChatFileTreeInfo | undefined; setContext(overwrite: boolean, ...context: IChatRequestVariableEntry[]): void; clear(): void; -} - -export interface IChatViewPane { - clear(): void; + getViewState(): IChatViewState; } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditor.ts b/src/vs/workbench/contrib/chat/browser/chatEditor.ts index 1da5d57b810..83d025ae706 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditor.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditor.ts @@ -50,8 +50,10 @@ export class ChatEditor extends EditorPane { super(ChatEditorInput.EditorID, group, telemetryService, themeService, storageService); } - public async clear() { - return this.instantiationService.invokeFunction(clearChatEditor); + private async clear() { + if (this.input) { + return this.instantiationService.invokeFunction(clearChatEditor, this.input as ChatEditorInput); + } } protected override createEditor(parent: HTMLElement): void { @@ -75,7 +77,7 @@ export class ChatEditor extends EditorPane { this.widget.setVisible(true); } - protected override setEditorVisible(visible: boolean): void { + protected override setEditorVisible(visible: boolean): void { super.setEditorVisible(visible); this.widget?.setVisible(visible); diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index b0c54404399..83a9ef05a8f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -22,7 +22,6 @@ import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/vie import { Memento } from 'vs/workbench/common/memento'; import { SIDE_BAR_FOREGROUND } from 'vs/workbench/common/theme'; import { IViewDescriptorService } from 'vs/workbench/common/views'; -import { IChatViewPane } from 'vs/workbench/contrib/chat/browser/chat'; import { IChatViewState, ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; import { ChatAgentLocation, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; import { CHAT_PROVIDER_ID } from 'vs/workbench/contrib/chat/common/chatParticipantContribTypes'; @@ -35,7 +34,7 @@ interface IViewPaneState extends IChatViewState { } export const CHAT_SIDEBAR_PANEL_ID = 'workbench.panel.chatSidebar'; -export class ChatViewPane extends ViewPane implements IChatViewPane { +export class ChatViewPane extends ViewPane { private _widget!: ChatWidget; get widget(): ChatWidget { return this._widget; } @@ -176,7 +175,7 @@ export class ChatViewPane extends ViewPane implements IChatViewPane { this._widget.acceptInput(query); } - clear(): void { + private clear(): void { if (this.widget.viewModel) { this.chatService.clearSession(this.widget.viewModel.sessionId); } diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index c6493b1a4f0..231d671577c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -963,8 +963,6 @@ export class ChatWidget extends Disposable implements IChatWidget { this.inputPart.saveState(); return { inputValue: this.getInput(), inputState: this.collectInputState() }; } - - } export class ChatWidgetService implements IChatWidgetService { From 39aba8e0afffb3ed32005b1456d911a3cc77d97d Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Sat, 29 Jun 2024 05:31:50 -0700 Subject: [PATCH 0193/2222] Tune chat render rate (#219241) Count words more correctly Don't measure the time before text starts to appear Better logs --- .../chat/browser/actions/chatActions.ts | 2 +- .../chat/browser/actions/chatTitleActions.ts | 2 +- .../chat/browser/chatAccessibilityProvider.ts | 8 +-- .../chat/browser/chatAccessibilityService.ts | 3 +- .../contrib/chat/browser/chatListRenderer.ts | 26 +++---- .../browser/chatResponseAccessibleView.ts | 2 +- .../contrib/chat/common/chatModel.ts | 35 ++++++++-- .../contrib/chat/common/chatServiceImpl.ts | 2 +- .../contrib/chat/common/chatViewModel.ts | 16 +++-- .../contrib/chat/common/chatWordCounter.ts | 8 ++- .../actions/voiceChatActions.ts | 6 +- .../chat/test/common/chatModel.test.ts | 4 +- .../chat/test/common/chatService.test.ts | 2 +- .../chat/test/common/chatWordCounter.test.ts | 67 +++++++++++++------ .../inlineChat/browser/inlineChatWidget.ts | 2 +- 15 files changed, 121 insertions(+), 64 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index 6b2e2c26435..29731511a13 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -310,6 +310,6 @@ export function stringifyItem(item: IChatRequestViewModel | IChatResponseViewMod if (isRequestVM(item)) { return (includeName ? `${item.username}: ` : '') + item.messageText; } else { - return (includeName ? `${item.username}: ` : '') + item.response.asString(); + return (includeName ? `${item.username}: ` : '') + item.response.toString(); } } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index e5ce3cbefbc..9338bf5b51b 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -177,7 +177,7 @@ export function registerChatTitleActions() { return; } - const value = item.response.asString(); + const value = item.response.toString(); const splitContents = splitMarkdownAndCodeBlocks(value); const focusRange = notebookEditor.getFocus(); diff --git a/src/vs/workbench/contrib/chat/browser/chatAccessibilityProvider.ts b/src/vs/workbench/contrib/chat/browser/chatAccessibilityProvider.ts index 5eb8edf7f6d..ecfaa1653f1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAccessibilityProvider.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAccessibilityProvider.ts @@ -62,16 +62,16 @@ export class ChatAccessibilityProvider implements IListAccessibilityProvider token.type === 'code')?.length ?? 0; + const codeBlockCount = marked.lexer(element.response.toString()).filter(token => token.type === 'code')?.length ?? 0; switch (codeBlockCount) { case 0: - label = accessibleViewHint ? localize('noCodeBlocksHint', "{0} {1} {2}", fileTreeCountHint, element.response.asString(), accessibleViewHint) : localize('noCodeBlocks', "{0} {1}", fileTreeCountHint, element.response.asString()); + label = accessibleViewHint ? localize('noCodeBlocksHint', "{0} {1} {2}", fileTreeCountHint, element.response.toString(), accessibleViewHint) : localize('noCodeBlocks', "{0} {1}", fileTreeCountHint, element.response.toString()); break; case 1: - label = accessibleViewHint ? localize('singleCodeBlockHint', "{0} 1 code block: {1} {2}", fileTreeCountHint, element.response.asString(), accessibleViewHint) : localize('singleCodeBlock', "{0} 1 code block: {1}", fileTreeCountHint, element.response.asString()); + label = accessibleViewHint ? localize('singleCodeBlockHint', "{0} 1 code block: {1} {2}", fileTreeCountHint, element.response.toString(), accessibleViewHint) : localize('singleCodeBlock', "{0} 1 code block: {1}", fileTreeCountHint, element.response.toString()); break; default: - label = accessibleViewHint ? localize('multiCodeBlockHint', "{0} {1} code blocks: {2}", fileTreeCountHint, codeBlockCount, element.response.asString(), accessibleViewHint) : localize('multiCodeBlock', "{0} {1} code blocks", fileTreeCountHint, codeBlockCount, element.response.asString()); + label = accessibleViewHint ? localize('multiCodeBlockHint', "{0} {1} code blocks: {2}", fileTreeCountHint, codeBlockCount, element.response.toString(), accessibleViewHint) : localize('multiCodeBlock', "{0} {1} code blocks", fileTreeCountHint, codeBlockCount, element.response.toString()); break; } return label; diff --git a/src/vs/workbench/contrib/chat/browser/chatAccessibilityService.ts b/src/vs/workbench/contrib/chat/browser/chatAccessibilityService.ts index d35b283ae34..06e0d0e292f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAccessibilityService.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAccessibilityService.ts @@ -34,7 +34,7 @@ export class ChatAccessibilityService extends Disposable implements IChatAccessi acceptResponse(response: IChatResponseViewModel | string | undefined, requestId: number): void { this._pendingSignalMap.deleteAndDispose(requestId); const isPanelChat = typeof response !== 'string'; - const responseContent = typeof response === 'string' ? response : response?.response.asString(); + const responseContent = typeof response === 'string' ? response : response?.response.toString(); this._accessibilitySignalService.playSignal(AccessibilitySignal.chatResponseReceived, { allowManyInParallel: true }); if (!response || !responseContent) { return; @@ -44,4 +44,3 @@ export class ChatAccessibilityService extends Disposable implements IChatAccessi status(plainTextResponse + errorDetails); } } - diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 21047f36d1c..1424cf276b1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -172,21 +172,19 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer 0 && newRenderedWordCount !== element.renderData?.renderedWordCount) { // Only update lastRenderTime when we actually render new content element.renderData = { lastRenderTime: Date.now(), renderedWordCount: newRenderedWordCount, renderedParts: partsToRender }; } diff --git a/src/vs/workbench/contrib/chat/browser/chatResponseAccessibleView.ts b/src/vs/workbench/contrib/chat/browser/chatResponseAccessibleView.ts index 009d9f4c3b1..6d682b5d72e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatResponseAccessibleView.ts +++ b/src/vs/workbench/contrib/chat/browser/chatResponseAccessibleView.ts @@ -47,7 +47,7 @@ export class ChatResponseAccessibleView implements IAccessibleViewImplentation { widget.focus(focusedItem); const isWelcome = focusedItem instanceof ChatWelcomeMessageModel; - let responseContent = isResponseVM(focusedItem) ? focusedItem.response.asString() : undefined; + let responseContent = isResponseVM(focusedItem) ? focusedItem.response.toString() : undefined; if (isWelcome) { const welcomeReplyContents = []; for (const content of focusedItem.content) { diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index 3e71298a2cb..b7c992fe5ae 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -80,7 +80,8 @@ export type IChatProgressRenderableResponseContent = Exclude; - asString(): string; + toMarkdown(): string; + toString(): string; } export interface IChatResponseModel { @@ -159,10 +160,18 @@ export class Response implements IResponse { return this._onDidChangeValue.event; } - // responseParts internally tracks all the response parts, including strings which are currently resolving, so that they can be updated when they do resolve private _responseParts: IChatProgressResponseContent[]; - // responseRepr externally presents the response parts with consolidated contiguous strings (excluding tree data) - private _responseRepr!: string; + + /** + * A stringified representation of response data which might be presented to a screenreader or used when copying a response. + */ + private _responseRepr = ''; + + /** + * Just the markdown content of the response, used for determining the rendering rate of markdown + */ + private _markdownContent = ''; + get value(): IChatProgressResponseContent[] { return this._responseParts; @@ -176,10 +185,14 @@ export class Response implements IResponse { this._updateRepr(true); } - asString(): string { + toString(): string { return this._responseRepr; } + toMarkdown(): string { + return this._markdownContent; + } + clear(): void { this._responseParts = []; this._updateRepr(true); @@ -264,6 +277,18 @@ export class Response implements IResponse { .filter(s => s.length > 0) .join('\n\n'); + this._markdownContent = this._responseParts.map(part => { + if (part.kind === 'inlineReference') { + return basename('uri' in part.inlineReference ? part.inlineReference.uri : part.inlineReference); + } else if (part.kind === 'markdownContent' || part.kind === 'markdownVuln') { + return part.content.value; + } else { + return ''; + } + }) + .filter(s => s.length > 0) + .join('\n\n'); + if (!quiet) { this._onDidChangeValue.fire(); } diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index a0cf7da28a9..48e83a11ea9 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -529,7 +529,7 @@ export class ChatService extends Disposable implements IChatService { continue; } history.push({ role: ChatMessageRole.User, content: { type: 'text', value: request.message.text } }); - history.push({ role: ChatMessageRole.Assistant, content: { type: 'text', value: request.response.response.asString() } }); + history.push({ role: ChatMessageRole.Assistant, content: { type: 'text', value: request.response.response.toString() } }); } const message = parsedRequest.text; const commandResult = await this.chatSlashCommandService.executeCommand(commandPart.slashCommand.command, message.substring(commandPart.slashCommand.command.length + 1).trimStart(), new Progress(p => { diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index bde3c18d399..e4e742afcd5 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -129,7 +129,7 @@ export interface IChatReferences { export type IChatRendererContent = IChatProgressRenderableResponseContent | IChatReferences; export interface IChatLiveUpdateData { - loadingStartTime: number; + firstWordTime: number; lastUpdateTime: number; impliedWordLoadRate: number; lastWordCount: number; @@ -506,7 +506,7 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi if (!_model.isComplete) { this._contentUpdateTimings = { - loadingStartTime: Date.now(), + firstWordTime: 0, lastUpdateTime: Date.now(), impliedWordLoadRate: 0, lastWordCount: 0 @@ -514,15 +514,17 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi } this._register(_model.onDidChange(() => { + // This should be true, if the model is changing if (this._contentUpdateTimings) { - // This should be true, if the model is changing const now = Date.now(); - const wordCount = countWords(_model.response.asString()); - const timeDiff = now - this._contentUpdateTimings.loadingStartTime; + const wordCount = countWords(_model.response.toString()); + + // Apply a min time difference, or the rate is typically too high for first few words + const timeDiff = Math.max(now - this._contentUpdateTimings.firstWordTime, 250); const impliedWordLoadRate = this._contentUpdateTimings.lastWordCount / (timeDiff / 1000); - this.trace('onDidChange', `Update- got ${this._contentUpdateTimings.lastWordCount} words over ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${wordCount} words are now available.`); + this.trace('onDidChange', `Update- got ${this._contentUpdateTimings.lastWordCount} words over last ${timeDiff}ms = ${impliedWordLoadRate} words/s. ${wordCount} words are now available.`); this._contentUpdateTimings = { - loadingStartTime: this._contentUpdateTimings.loadingStartTime, + firstWordTime: this._contentUpdateTimings.firstWordTime === 0 && this.response.value.some(v => v.kind === 'markdownContent') ? now : this._contentUpdateTimings.firstWordTime, lastUpdateTime: now, impliedWordLoadRate, lastWordCount: wordCount diff --git a/src/vs/workbench/contrib/chat/common/chatWordCounter.ts b/src/vs/workbench/contrib/chat/common/chatWordCounter.ts index edd27ddc435..b81d391186e 100644 --- a/src/vs/workbench/contrib/chat/common/chatWordCounter.ts +++ b/src/vs/workbench/contrib/chat/common/chatWordCounter.ts @@ -11,8 +11,12 @@ export interface IWordCountResult { } export function getNWords(str: string, numWordsToCount: number): IWordCountResult { - // Match words and markdown style links - const allWordMatches = Array.from(str.matchAll(/\[([^\]]+)\]\(([^)]+)\)|\p{sc=Han}|[^\s\|\-|\p{sc=Han}]+/gu)); + // This regex matches each word and skips over whitespace and separators. A word is: + // A markdown link + // One chinese character + // One or more + - =, handled so that code like "a=1+2-3" is broken up better + // One or more characters that aren't whitepace or any of the above + const allWordMatches = Array.from(str.matchAll(/\[([^\]]+)\]\(([^)]+)\)|\p{sc=Han}|=+|\++|-+|[^\s\|\p{sc=Han}|=|\+|\-]+/gu)); const targetWords = allWordMatches.slice(0, numWordsToCount); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 1c0cdf3fe19..58586530c70 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -805,7 +805,7 @@ class ChatSynthesizerSessions { let totalOffset = 0; let complete = false; do { - const responseLength = response.response.asString().length; + const responseLength = response.response.toString().length; const { chunk, offset } = this.parseNextChatResponseChunk(response, totalOffset); totalOffset = offset; complete = response.isComplete; @@ -818,7 +818,7 @@ class ChatSynthesizerSessions { return; } - if (!complete && responseLength === response.response.asString().length) { + if (!complete && responseLength === response.response.toString().length) { await raceCancellation(Event.toPromise(response.onDidChange), token); // wait for the response to change } } while (!token.isCancellationRequested && !complete); @@ -827,7 +827,7 @@ class ChatSynthesizerSessions { private parseNextChatResponseChunk(response: IChatResponseModel, offset: number): { readonly chunk: string | undefined; readonly offset: number } { let chunk: string | undefined = undefined; - const text = response.response.asString(); + const text = response.response.toString(); if (response.isComplete) { chunk = text.substring(offset); diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index e7fb3834f67..81efbdca4b6 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -149,7 +149,7 @@ suite('ChatModel', () => { model2.acceptResponseProgress(request1, { content: new MarkdownString('Hello'), kind: 'markdownContent' }); - assert.strictEqual(request1.response.response.asString(), 'Hello'); + assert.strictEqual(request1.response.response.toString(), 'Hello'); }); }); @@ -162,7 +162,7 @@ suite('Response', () => { response.updateContent({ content: new MarkdownString('markdown2'), kind: 'markdownContent' }); await assertSnapshot(response.value); - assert.strictEqual(response.asString(), 'markdown1markdown2'); + assert.strictEqual(response.toString(), 'markdown1markdown2'); }); test('not mergeable markdown', async () => { diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index cf1f2a174d5..df32dfed2f4 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -127,7 +127,7 @@ suite('ChatService', () => { await testService.addCompleteRequest(model.sessionId, 'test request', undefined, 0, { message: 'test response' }); assert.strictEqual(model.getRequests().length, 1); assert.ok(model.getRequests()[0].response); - assert.strictEqual(model.getRequests()[0].response?.response.asString(), 'test response'); + assert.strictEqual(model.getRequests()[0].response?.response.toString(), 'test response'); }); test('sendRequest fails', async () => { diff --git a/src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts b/src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts index 9330dc5be75..665c2e386b1 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts @@ -16,28 +16,53 @@ suite('ChatWordCounter', () => { assert.strictEqual(result.returnedWordCount, nWords); } - test('getNWords, matching actualWordCount', () => { - const cases: [string, number, string][] = [ - ['hello world', 1, 'hello'], - ['hello', 1, 'hello'], - ['hello world', 0, ''], - ['here\'s, some. punctuation?', 3, 'here\'s, some. punctuation?'], - ['| markdown | _table_ | header |', 3, '| markdown | _table_ | header'], - ['| --- | --- | --- |', 1, '| --- | --- | --- |'], - [' \t some \n whitespace \n\n\nhere ', 3, ' \t some \n whitespace \n\n\nhere'], - ]; - - cases.forEach(([str, nWords, result]) => doTest(str, nWords, result)); - }); + suite('getNWords', () => { + test('matching actualWordCount', () => { + const cases: [string, number, string][] = [ + ['hello world', 1, 'hello'], + ['hello', 1, 'hello'], + ['hello world', 0, ''], + ['here\'s, some. punctuation?', 3, 'here\'s, some. punctuation?'], + ['| markdown | _table_ | header |', 3, '| markdown | _table_ | header'], + ['| --- | --- | --- |', 1, '| ---'], + ['| --- | --- | --- |', 3, '| --- | --- | ---'], + [' \t some \n whitespace \n\n\nhere ', 3, ' \t some \n whitespace \n\n\nhere'], + ]; + + cases.forEach(([str, nWords, result]) => doTest(str, nWords, result)); + }); + + test('matching links', () => { + const cases: [string, number, string][] = [ + ['[hello](https://example.com) world', 1, '[hello](https://example.com)'], + ['[hello](https://example.com) world', 2, '[hello](https://example.com) world'], + ['oh [hello](https://example.com "title") world', 1, 'oh'], + ['oh [hello](https://example.com "title") world', 2, 'oh [hello](https://example.com "title")'], + ]; + + cases.forEach(([str, nWords, result]) => doTest(str, nWords, result)); + }); - test('getNWords, matching links', () => { - const cases: [string, number, string][] = [ - ['[hello](https://example.com) world', 1, '[hello](https://example.com)'], - ['[hello](https://example.com) world', 2, '[hello](https://example.com) world'], - ['oh [hello](https://example.com "title") world', 1, 'oh'], - ['oh [hello](https://example.com "title") world', 2, 'oh [hello](https://example.com "title")'], - ]; + test('code', () => { + const cases: [string, number, string][] = [ + ['let a=1-2', 2, 'let a'], + ['let a=1-2', 3, 'let a='], + ['let a=1-2', 4, 'let a=1'], + ['const myVar = 1+2', 4, 'const myVar = 1'], + ['
', 3, '
', 4, '
'], + ]; - cases.forEach(([str, nWords, result]) => doTest(str, nWords, result)); + cases.forEach(([str, nWords, result]) => doTest(str, nWords, result)); + }); + + test('chinese characters', () => { + const cases: [string, number, string][] = [ + ['我喜欢中国菜', 3, '我喜欢'], + ]; + + cases.forEach(([str, nWords, result]) => doTest(str, nWords, result)); + }); }); + }); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts index 48539e61df8..f68ed1d3241 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatWidget.ts @@ -389,7 +389,7 @@ export class InlineChatWidget { if (!isNonEmptyArray(requests)) { return undefined; } - return tail(requests)?.response?.response.asString(); + return tail(requests)?.response?.response.toString(); } From 943b47ae1726a6c0970d50f90a84e64be102c2d1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sat, 29 Jun 2024 17:47:45 +0200 Subject: [PATCH 0194/2222] debt - adopt `nlsCoreBaseUrl` (#219165) --- package.json | 2 +- src/vs/base/common/product.ts | 1 + src/vs/server/node/webClientServer.ts | 6 +++--- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index f8bfdf775a3..e8e6a3d2e34 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.92.0", - "distro": "a08799837ca498c02f445ca7a896f446419af238", + "distro": "58e7d90e05684b6937db21dd372f7a088bdc9dc1", "author": { "name": "Microsoft Corporation" }, diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index 026545ba4ae..754eba49584 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -82,6 +82,7 @@ export interface IProductConfiguration { readonly webEndpointUrlTemplate?: string; readonly webviewContentExternalBaseUrlTemplate?: string; readonly target?: string; + readonly nlsCoreBaseUrl?: string; readonly settingsSearchBuildId?: number; readonly settingsSearchUrl?: string; diff --git a/src/vs/server/node/webClientServer.ts b/src/vs/server/node/webClientServer.ts index 20c96da8f1f..d9c27e100c8 100644 --- a/src/vs/server/node/webClientServer.ts +++ b/src/vs/server/node/webClientServer.ts @@ -342,9 +342,9 @@ export class WebClientServer { const locale = cookies['vscode.nls.locale'] || req.headers['accept-language']?.split(',')[0]?.toLowerCase() || 'en'; let WORKBENCH_NLS_BASE_URL: string | undefined; let WORKBENCH_NLS_URL: string; - if (!locale.startsWith('en')) { - WORKBENCH_NLS_BASE_URL = `https://www.vscode-unpkg.net/nls/`; - WORKBENCH_NLS_URL = `${WORKBENCH_NLS_BASE_URL}${this._productService.commit}/${this._productService.version}/${locale}/nls.messages.js`; // TODO@bpasero make it a product.json thing + if (!locale.startsWith('en') && this._productService.nlsCoreBaseUrl) { + WORKBENCH_NLS_BASE_URL = this._productService.nlsCoreBaseUrl; + WORKBENCH_NLS_URL = `${WORKBENCH_NLS_BASE_URL}${this._productService.commit}/${this._productService.version}/${locale}/nls.messages.js`; } else { WORKBENCH_NLS_URL = ''; // fallback will apply } From 02255461788a6fb943cde4809d84328c9772f3b0 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sat, 29 Jun 2024 19:40:07 +0200 Subject: [PATCH 0195/2222] #212879 dispose child instantiation service (#219217) * #212879 dispose child instantiation service * fix tests --- src/vs/workbench/browser/parts/views/viewPane.ts | 6 ++++-- .../test/electron-sandbox/extensionsViews.test.ts | 6 ------ src/vs/workbench/contrib/output/browser/outputView.ts | 4 ++-- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index b95f8a1071c..28a82a629df 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -391,7 +391,8 @@ export abstract class ViewPane extends Pane implements IView { const viewLocationKey = this.scopedContextKeyService.createKey('viewLocation', ViewContainerLocationToString(viewDescriptorService.getViewLocationById(this.id)!)); this._register(Event.filter(viewDescriptorService.onDidChangeLocation, e => e.views.some(view => view.id === this.id))(() => viewLocationKey.set(ViewContainerLocationToString(viewDescriptorService.getViewLocationById(this.id)!)))); - this.menuActions = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])).createInstance(CompositeMenuActions, options.titleMenuId ?? MenuId.ViewTitle, MenuId.ViewTitleContext, { shouldForwardArgs: !options.donotForwardArgs, renderShortTitle: true })); + const childInstantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService]))); + this.menuActions = this._register(childInstantiationService.createInstance(CompositeMenuActions, options.titleMenuId ?? MenuId.ViewTitle, MenuId.ViewTitleContext, { shouldForwardArgs: !options.donotForwardArgs, renderShortTitle: true })); this._register(this.menuActions.onDidChange(() => this.updateActions())); } @@ -747,7 +748,8 @@ export abstract class FilterViewPane extends ViewPane { accessibleViewService?: IAccessibleViewInformationService ) { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService, accessibleViewService); - this.filterWidget = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])).createInstance(FilterWidget, options.filterOptions)); + const childInstantiationService = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService]))); + this.filterWidget = this._register(childInstantiationService.createInstance(FilterWidget, options.filterOptions)); } override getFilterWidget(): FilterWidget { diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsViews.test.ts index 6d62ecebd61..ef60bf96605 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsViews.test.ts @@ -505,9 +505,6 @@ suite('ExtensionsViews Tests', () => { }] }); - testableView.dispose(); - testableView = disposableStore.add(instantiationService.createInstance(ExtensionsListView, {}, { id: '', title: '' })); - return testableView.show('search-me').then(result => { const options: IQueryOptions = queryTarget.args[0][0]; @@ -532,9 +529,6 @@ suite('ExtensionsViews Tests', () => { const queryTarget = instantiationService.stubPromise(IExtensionGalleryService, 'query', aPage(...realResults)); - testableView.dispose(); - disposableStore.add(testableView = instantiationService.createInstance(ExtensionsListView, {}, { id: '', title: '' })); - return testableView.show('search-me @sort:installs').then(result => { const options: IQueryOptions = queryTarget.args[0][0]; diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index 19e5642f145..9a23e945d85 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -62,8 +62,8 @@ export class OutputViewPane extends ViewPane { super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); this.scrollLockContextKey = CONTEXT_OUTPUT_SCROLL_LOCK.bindTo(this.contextKeyService); - const editorInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); - this.editor = editorInstantiationService.createInstance(OutputEditor); + const editorInstantiationService = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService]))); + this.editor = this._register(editorInstantiationService.createInstance(OutputEditor)); this._register(this.editor.onTitleAreaUpdate(() => { this.updateTitle(this.editor.getTitle()); this.updateActions(); From 0426dd4e2a555f71d003d1abef455d0301a06bd0 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Sat, 29 Jun 2024 20:06:00 +0200 Subject: [PATCH 0196/2222] =?UTF-8?q?SCM=20-=20=F0=9F=92=84=20adopt=20obse?= =?UTF-8?q?rvables=20(#219162)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../workbench/contrib/scm/browser/activity.ts | 115 +++++++----------- 1 file changed, 46 insertions(+), 69 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/activity.ts b/src/vs/workbench/contrib/scm/browser/activity.ts index de21368d9b9..36a6aeffdb3 100644 --- a/src/vs/workbench/contrib/scm/browser/activity.ts +++ b/src/vs/workbench/contrib/scm/browser/activity.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { basename } from 'vs/base/common/resources'; -import { IDisposable, dispose, Disposable, DisposableStore, combinedDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Emitter, Event } from 'vs/base/common/event'; import { VIEW_PANE_ID, ISCMService, ISCMRepository, ISCMViewService } from 'vs/workbench/contrib/scm/common/scm'; import { IActivityService, NumberBadge } from 'vs/workbench/services/activity/common/activity'; @@ -16,7 +16,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { EditorResourceAccessor } from 'vs/workbench/common/editor'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { Schemas } from 'vs/base/common/network'; import { Iterable } from 'vs/base/common/iterator'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; import { IEditorGroupContextKeyProvider, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; @@ -28,6 +27,11 @@ import { derivedObservableWithCache, latestChangedValue, observableFromEventOpts import { Command } from 'vs/editor/common/languages'; import { ISCMHistoryItemGroup } from 'vs/workbench/contrib/scm/common/history'; +const ActiveRepositoryContextKeys = { + ActiveRepositoryName: new RawContextKey('scmActiveRepositoryName', ''), + ActiveRepositoryBranchName: new RawContextKey('scmActiveRepositoryBranchName', ''), +}; + export class SCMActiveRepositoryController extends Disposable implements IWorkbenchContribution { private readonly _countBadgeConfig = observableConfigValue<'all' | 'focused' | 'off'>('scm.countBadge', 'all', this.configurationService); @@ -198,105 +202,78 @@ export class SCMActiveRepositoryController extends Disposable implements IWorkbe } } -const ActiveRepositoryContextKeys = { - ActiveRepositoryName: new RawContextKey('scmActiveRepositoryName', ''), - ActiveRepositoryBranchName: new RawContextKey('scmActiveRepositoryBranchName', ''), -}; - -export class SCMActiveResourceContextKeyController implements IWorkbenchContribution { +export class SCMActiveResourceContextKeyController extends Disposable implements IWorkbenchContribution { + private readonly _repositories = observableFromEvent(this, + Event.any(this.scmService.onDidAddRepository, this.scmService.onDidRemoveRepository), + () => this.scmService.repositories); - private readonly disposables = new DisposableStore(); - private repositoryDisposables = new Set(); - private onDidRepositoryChange = new Emitter(); + private readonly _onDidRepositoryChange = new Emitter(); constructor( @IEditorGroupsService editorGroupsService: IEditorGroupsService, @ISCMService private readonly scmService: ISCMService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService ) { + super(); + const activeResourceHasChangesContextKey = new RawContextKey('scmActiveResourceHasChanges', false, localize('scmActiveResourceHasChanges', "Whether the active resource has changes")); const activeResourceRepositoryContextKey = new RawContextKey('scmActiveResourceRepository', undefined, localize('scmActiveResourceRepository', "The active resource's repository")); - this.scmService.onDidAddRepository(this.onDidAddRepository, this, this.disposables); - - for (const repository of this.scmService.repositories) { - this.onDidAddRepository(repository); - } + this._store.add(autorunWithStore((reader, store) => { + for (const repository of this._repositories.read(reader)) { + store.add(Event.runAndSubscribe(repository.provider.onDidChangeResources, () => { + this._onDidRepositoryChange.fire(); + })); + } + })); // Create context key providers which will update the context keys based on each groups active editor const hasChangesContextKeyProvider: IEditorGroupContextKeyProvider = { contextKey: activeResourceHasChangesContextKey, - getGroupContextKeyValue: (group) => this.getEditorHasChanges(group.activeEditor), - onDidChange: this.onDidRepositoryChange.event + getGroupContextKeyValue: (group) => this._getEditorHasChanges(group.activeEditor), + onDidChange: this._onDidRepositoryChange.event }; const repositoryContextKeyProvider: IEditorGroupContextKeyProvider = { contextKey: activeResourceRepositoryContextKey, - getGroupContextKeyValue: (group) => this.getEditorRepositoryId(group.activeEditor), - onDidChange: this.onDidRepositoryChange.event + getGroupContextKeyValue: (group) => this._getEditorRepositoryId(group.activeEditor), + onDidChange: this._onDidRepositoryChange.event }; - this.disposables.add(editorGroupsService.registerContextKeyProvider(hasChangesContextKeyProvider)); - this.disposables.add(editorGroupsService.registerContextKeyProvider(repositoryContextKeyProvider)); + this._store.add(editorGroupsService.registerContextKeyProvider(hasChangesContextKeyProvider)); + this._store.add(editorGroupsService.registerContextKeyProvider(repositoryContextKeyProvider)); } - private onDidAddRepository(repository: ISCMRepository): void { - const onDidChange = Event.any(repository.provider.onDidChange, repository.provider.onDidChangeResources); - const changeDisposable = onDidChange(() => { - this.onDidRepositoryChange.fire(); - }); - - const onDidRemove = Event.filter(this.scmService.onDidRemoveRepository, e => e === repository); - const removeDisposable = onDidRemove(() => { - disposable.dispose(); - this.repositoryDisposables.delete(disposable); - this.onDidRepositoryChange.fire(); - }); - - const disposable = combinedDisposable(changeDisposable, removeDisposable); - this.repositoryDisposables.add(disposable); - } - - private getEditorRepositoryId(activeEditor: EditorInput | null): string | undefined { + private _getEditorHasChanges(activeEditor: EditorInput | null): boolean { const activeResource = EditorResourceAccessor.getOriginalUri(activeEditor); + if (!activeResource) { + return false; + } - if (activeResource?.scheme === Schemas.file || activeResource?.scheme === Schemas.vscodeRemote) { - const activeResourceRepository = Iterable.find( - this.scmService.repositories, - r => Boolean(r.provider.rootUri && this.uriIdentityService.extUri.isEqualOrParent(activeResource, r.provider.rootUri)) - ); - - return activeResourceRepository?.id; + const activeResourceRepository = this.scmService.getRepository(activeResource); + for (const resourceGroup of activeResourceRepository?.provider.groups ?? []) { + if (resourceGroup.resources + .some(scmResource => + this.uriIdentityService.extUri.isEqual(activeResource, scmResource.sourceUri))) { + return true; + } } - return undefined; + return false; } - private getEditorHasChanges(activeEditor: EditorInput | null): boolean { + private _getEditorRepositoryId(activeEditor: EditorInput | null): string | undefined { const activeResource = EditorResourceAccessor.getOriginalUri(activeEditor); - - if (activeResource?.scheme === Schemas.file || activeResource?.scheme === Schemas.vscodeRemote) { - const activeResourceRepository = Iterable.find( - this.scmService.repositories, - r => Boolean(r.provider.rootUri && this.uriIdentityService.extUri.isEqualOrParent(activeResource, r.provider.rootUri)) - ); - - for (const resourceGroup of activeResourceRepository?.provider.groups ?? []) { - if (resourceGroup.resources - .some(scmResource => - this.uriIdentityService.extUri.isEqual(activeResource, scmResource.sourceUri))) { - return true; - } - } + if (!activeResource) { + return undefined; } - return false; + const activeResourceRepository = this.scmService.getRepository(activeResource); + return activeResourceRepository?.id; } - dispose(): void { - this.disposables.dispose(); - dispose(this.repositoryDisposables.values()); - this.repositoryDisposables.clear(); - this.onDidRepositoryChange.dispose(); + override dispose(): void { + this._onDidRepositoryChange.dispose(); + super.dispose(); } } From 44ac947b5f414ece573191a4df8ddbeeaf0f54e0 Mon Sep 17 00:00:00 2001 From: Johannes Date: Mon, 1 Jul 2024 09:02:51 +0200 Subject: [PATCH 0197/2222] review feedback: remove unneccessary check --- .../contrib/chat/browser/chatMarkdownDecorationsRenderer.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index bb016c0f1fa..9af542462c3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts @@ -87,9 +87,7 @@ export class ChatMarkdownDecorationsRenderer { if (part instanceof ChatRequestTextPart) { result += part.text; } else if (part instanceof ChatRequestAgentPart) { - if (!part.agent.isDefault) { - result += this.instantiationService.invokeFunction(accessor => agentToMarkdown(part.agent, false, accessor)); - } + result += this.instantiationService.invokeFunction(accessor => agentToMarkdown(part.agent, false, accessor)); } else { const uri = part instanceof ChatRequestDynamicVariablePart && part.data instanceof URI ? part.data : From abe5b7d16f33011ed0013ed68c6ebdec1f8aff80 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 1 Jul 2024 09:18:40 +0200 Subject: [PATCH 0198/2222] debt - more direct `fs` use (#219261) --- src/vs/base/node/extpath.ts | 2 +- src/vs/base/node/osDisplayProtocolInfo.ts | 5 +- src/vs/base/node/osReleaseInfo.ts | 7 ++- src/vs/base/node/pfs.ts | 51 +++++-------------- src/vs/base/node/processes.ts | 6 +-- src/vs/base/node/zip.ts | 6 +-- src/vs/base/parts/storage/node/storage.ts | 5 +- .../test/node/storage.integrationTest.ts | 11 ++-- src/vs/base/test/node/crypto.test.ts | 3 +- src/vs/base/test/node/extpath.test.ts | 3 +- src/vs/base/test/node/pfs/pfs.test.ts | 20 ++++---- src/vs/base/test/node/snapshot.test.ts | 7 +-- src/vs/base/test/node/zip/zip.test.ts | 3 +- src/vs/code/electron-main/main.ts | 6 +-- src/vs/code/node/cliProcessMain.ts | 4 +- .../sharedProcess/contrib/codeCacheCleaner.ts | 3 +- .../contrib/languagePackCachedDataCleaner.ts | 5 +- .../electron-main/backupMainService.test.ts | 6 +-- .../diagnostics/node/diagnosticsService.ts | 4 +- src/vs/platform/environment/node/stdin.ts | 8 +-- .../node/extensionManagementService.ts | 3 +- .../files/node/diskFileSystemProvider.ts | 18 +++---- .../node/watcher/nodejs/nodejsWatcherLib.ts | 4 +- .../node/diskFileService.integrationTest.ts | 30 +++++------ .../node/nodejsWatcher.integrationTest.ts | 41 +++++++-------- .../node/parcelWatcher.integrationTest.ts | 48 ++++++++--------- .../languagePacks/node/languagePacks.ts | 3 +- .../electron-main/nativeHostMainService.ts | 7 +-- src/vs/platform/remote/node/wsl.ts | 4 +- src/vs/platform/state/test/node/state.test.ts | 4 +- .../storage/electron-main/storageMain.ts | 3 +- src/vs/platform/telemetry/node/telemetry.ts | 9 ++-- .../platform/terminal/node/terminalProcess.ts | 12 ++--- .../terminal/node/terminalProfiles.ts | 3 +- .../electron-main/updateService.win32.ts | 4 +- .../electron-main/windowsMainService.ts | 4 +- .../workspacesManagementMainService.ts | 5 +- .../test/electron-main/workspaces.test.ts | 2 +- .../workspacesManagementMainService.test.ts | 2 +- src/vs/server/node/serverConnectionToken.ts | 2 +- src/vs/server/node/webClientServer.ts | 13 +++-- .../workbench/api/node/extHostStoragePaths.ts | 12 ++--- .../api/node/extHostTunnelService.ts | 11 ++-- .../test/node/colorRegistry.releaseTest.ts | 5 +- .../test/node/keyboardMapperTestUtils.ts | 5 +- 45 files changed, 206 insertions(+), 213 deletions(-) diff --git a/src/vs/base/node/extpath.ts b/src/vs/base/node/extpath.ts index a7ec9cf6d36..eb91392e390 100644 --- a/src/vs/base/node/extpath.ts +++ b/src/vs/base/node/extpath.ts @@ -119,7 +119,7 @@ export async function realpath(path: string): Promise { // to not resolve links but to simply see if the path is read accessible or not. const normalizedPath = normalizePath(path); - await Promises.access(normalizedPath, fs.constants.R_OK); + await fs.promises.access(normalizedPath, fs.constants.R_OK); return normalizedPath; } diff --git a/src/vs/base/node/osDisplayProtocolInfo.ts b/src/vs/base/node/osDisplayProtocolInfo.ts index c028dc88536..90c825bd29b 100644 --- a/src/vs/base/node/osDisplayProtocolInfo.ts +++ b/src/vs/base/node/osDisplayProtocolInfo.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { constants as FSConstants } from 'fs'; -import { access } from 'fs/promises'; +import { constants as FSConstants, promises as FSPromises } from 'fs'; import { join } from 'vs/base/common/path'; import { env } from 'vs/base/common/process'; @@ -44,7 +43,7 @@ export async function getDisplayProtocol(errorLogger: (error: any) => void): Pro const waylandServerPipe = join(xdgRuntimeDir, 'wayland-0'); try { - await access(waylandServerPipe, FSConstants.R_OK); + await FSPromises.access(waylandServerPipe, FSConstants.R_OK); // If the file exists, then the session is wayland. return DisplayProtocolType.Wayland; diff --git a/src/vs/base/node/osReleaseInfo.ts b/src/vs/base/node/osReleaseInfo.ts index f72b0fe82ba..60101f545cb 100644 --- a/src/vs/base/node/osReleaseInfo.ts +++ b/src/vs/base/node/osReleaseInfo.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { constants as FSConstants } from 'fs'; -import { open, FileHandle } from 'fs/promises'; +import { constants as FSConstants, promises as FSPromises } from 'fs'; import { createInterface as readLines } from 'readline'; import * as Platform from 'vs/base/common/platform'; @@ -22,10 +21,10 @@ export async function getOSReleaseInfo(errorLogger: (error: any) => void): Promi // Extract release information on linux based systems // using the identifiers specified in // https://www.freedesktop.org/software/systemd/man/os-release.html - let handle: FileHandle | undefined; + let handle: FSPromises.FileHandle | undefined; for (const filePath of ['/etc/os-release', '/usr/lib/os-release', '/etc/lsb-release']) { try { - handle = await open(filePath, FSConstants.R_OK); + handle = await FSPromises.open(filePath, FSConstants.R_OK); break; } catch (err) { } } diff --git a/src/vs/base/node/pfs.ts b/src/vs/base/node/pfs.ts index 1c0ce081625..52e8c9e87d4 100644 --- a/src/vs/base/node/pfs.ts +++ b/src/vs/base/node/pfs.ts @@ -134,7 +134,7 @@ async function safeReaddirWithFileTypes(path: string): Promise { let isSymbolicLink = false; try { - const lstat = await Promises.lstat(join(path, child)); + const lstat = await fs.promises.lstat(join(path, child)); isFile = lstat.isFile(); isDirectory = lstat.isDirectory(); @@ -259,7 +259,7 @@ export namespace SymlinkSupport { // First stat the link let lstats: fs.Stats | undefined; try { - lstats = await Promises.lstat(path); + lstats = await fs.promises.lstat(path); // Return early if the stat is not a symbolic link at all if (!lstats.isSymbolicLink()) { @@ -272,7 +272,7 @@ export namespace SymlinkSupport { // If the stat is a symbolic link or failed to stat, use fs.stat() // which for symbolic links will stat the target they point to try { - const stats = await Promises.stat(path); + const stats = await fs.promises.stat(path); return { stat: stats, symbolicLink: lstats?.isSymbolicLink() ? { dangling: false } : undefined }; } catch (error) { @@ -287,7 +287,7 @@ export namespace SymlinkSupport { // are not supported (https://github.com/nodejs/node/issues/36790) if (isWindows && error.code === 'EACCES') { try { - const stats = await Promises.stat(await Promises.readlink(path)); + const stats = await fs.promises.stat(await fs.promises.readlink(path)); return { stat: stats, symbolicLink: { dangling: false } }; } catch (error) { @@ -620,7 +620,7 @@ async function doCopy(source: string, target: string, payload: ICopyPayload): Pr async function doCopyDirectory(source: string, target: string, mode: number, payload: ICopyPayload): Promise { // Create folder - await Promises.mkdir(target, { recursive: true, mode }); + await fs.promises.mkdir(target, { recursive: true, mode }); // Copy each file recursively const files = await readdir(source); @@ -632,16 +632,16 @@ async function doCopyDirectory(source: string, target: string, mode: number, pay async function doCopyFile(source: string, target: string, mode: number): Promise { // Copy file - await Promises.copyFile(source, target); + await fs.promises.copyFile(source, target); // restore mode (https://github.com/nodejs/node/issues/1104) - await Promises.chmod(target, mode); + await fs.promises.chmod(target, mode); } async function doCopySymlink(source: string, target: string, payload: ICopyPayload): Promise { // Figure out link target - let linkTarget = await Promises.readlink(source); + let linkTarget = await fs.promises.readlink(source); // Special case: the symlink points to a target that is // actually within the path that is being copied. In that @@ -652,7 +652,7 @@ async function doCopySymlink(source: string, target: string, payload: ICopyPaylo } // Create symlink - await Promises.symlink(linkTarget, target); + await fs.promises.symlink(linkTarget, target); } //#endregion @@ -660,28 +660,19 @@ async function doCopySymlink(source: string, target: string, payload: ICopyPaylo //#region Promise based fs methods /** - * Provides promise based 'fs' methods by wrapping around the - * original callback based methods. + * Some low level `fs` methods provided as `Promises` similar to + * `fs.promises` but with notable differences, either implemented + * by us or by restoring the original callback based behavior. * * At least `realpath` is implemented differently in the promise * based implementation compared to the callback based one. The * promise based implementation actually calls `fs.realpath.native`. * (https://github.com/microsoft/vscode/issues/118562) - * - * TODO@bpasero we should move away from this towards `fs.promises` - * eventually and only keep those methods around where we explicitly - * want the callback based behaviour. */ export const Promises = new class { //#region Implemented by node.js - get access() { return fs.promises.access; } - - get stat() { return fs.promises.stat; } - get lstat() { return fs.promises.lstat; } - get utimes() { return fs.promises.utimes; } - get read() { // Not using `promisify` here for a reason: the return @@ -700,7 +691,6 @@ export const Promises = new class { }); }; } - get readFile() { return fs.promises.readFile; } get write() { @@ -721,26 +711,11 @@ export const Promises = new class { }; } - get appendFile() { return fs.promises.appendFile; } - get fdatasync() { return promisify(fs.fdatasync); } // not exposed as API in 20.x yet - get truncate() { return fs.promises.truncate; } - - get copyFile() { return fs.promises.copyFile; } get open() { return promisify(fs.open); } // changed to return `FileHandle` in promise API get close() { return promisify(fs.close); } // not exposed as API due to the `FileHandle` return type of `open` - get symlink() { return fs.promises.symlink; } - get readlink() { return fs.promises.readlink; } - - get chmod() { return fs.promises.chmod; } - - get mkdir() { return fs.promises.mkdir; } - - get unlink() { return fs.promises.unlink; } - get rmdir() { return fs.promises.rmdir; } - get realpath() { return promisify(fs.realpath); } // `fs.promises.realpath` will use `fs.realpath.native` which we do not want //#endregion @@ -749,7 +724,7 @@ export const Promises = new class { async exists(path: string): Promise { try { - await Promises.access(path); + await fs.promises.access(path); return true; } catch { diff --git a/src/vs/base/node/processes.ts b/src/vs/base/node/processes.ts index 9c07f106711..7bd20f80c07 100644 --- a/src/vs/base/node/processes.ts +++ b/src/vs/base/node/processes.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as cp from 'child_process'; -import { Stats } from 'fs'; +import { Stats, promises } from 'fs'; import * as path from 'vs/base/common/path'; import * as Platform from 'vs/base/common/platform'; import * as process from 'vs/base/common/process'; @@ -91,11 +91,11 @@ export namespace win32 { if (await pfs.Promises.exists(path)) { let statValue: Stats | undefined; try { - statValue = await pfs.Promises.stat(path); + statValue = await promises.stat(path); } catch (e) { if (e.message.startsWith('EACCES')) { // it might be symlink - statValue = await pfs.Promises.lstat(path); + statValue = await promises.lstat(path); } } return statValue ? !statValue.isDirectory() : false; diff --git a/src/vs/base/node/zip.ts b/src/vs/base/node/zip.ts index c0d9b4b8ecb..1295944efcf 100644 --- a/src/vs/base/node/zip.ts +++ b/src/vs/base/node/zip.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createWriteStream, WriteStream } from 'fs'; +import { createWriteStream, WriteStream, promises } from 'fs'; import { Readable } from 'stream'; import { createCancelablePromise, Sequencer } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -85,7 +85,7 @@ function extractEntry(stream: Readable, fileName: string, mode: number, targetPa istream?.destroy(); }); - return Promise.resolve(Promises.mkdir(targetDirName, { recursive: true })).then(() => new Promise((c, e) => { + return Promise.resolve(promises.mkdir(targetDirName, { recursive: true })).then(() => new Promise((c, e) => { if (token.isCancellationRequested) { return; } @@ -148,7 +148,7 @@ function extractZip(zipfile: ZipFile, targetPath: string, options: IOptions, tok // directory file names end with '/' if (/\/$/.test(fileName)) { const targetFileName = path.join(targetPath, fileName); - last = createCancelablePromise(token => Promises.mkdir(targetFileName, { recursive: true }).then(() => readNextEntry(token)).then(undefined, e)); + last = createCancelablePromise(token => promises.mkdir(targetFileName, { recursive: true }).then(() => readNextEntry(token)).then(undefined, e)); return; } diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index 5d942007580..33a8498c7bb 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { timeout } from 'vs/base/common/async'; import { Event } from 'vs/base/common/event'; import { mapToString, setToString } from 'vs/base/common/map'; @@ -194,7 +195,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { // Delete the existing DB. If the path does not exist or fails to // be deleted, we do not try to recover anymore because we assume // that the path is no longer writeable for us. - return Promises.unlink(this.path).then(() => { + return fs.promises.unlink(this.path).then(() => { // Re-open the DB fresh return this.doConnect(this.path).then(recoveryConnection => { @@ -280,7 +281,7 @@ export class SQLiteStorageDatabase implements IStorageDatabase { // folder is really not writeable for us. // try { - await Promises.unlink(path); + await fs.promises.unlink(path); try { await Promises.rename(this.toBackupPath(path), path, false /* no retry */); } catch (error) { diff --git a/src/vs/base/parts/storage/test/node/storage.integrationTest.ts b/src/vs/base/parts/storage/test/node/storage.integrationTest.ts index 51c66ca1af8..450687ab705 100644 --- a/src/vs/base/parts/storage/test/node/storage.integrationTest.ts +++ b/src/vs/base/parts/storage/test/node/storage.integrationTest.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { deepStrictEqual, ok, strictEqual } from 'assert'; import { tmpdir } from 'os'; import { timeout } from 'vs/base/common/async'; @@ -24,7 +25,7 @@ flakySuite('Storage Library', function () { setup(function () { testDir = getRandomTestPath(tmpdir(), 'vsctests', 'storagelibrary'); - return Promises.mkdir(testDir, { recursive: true }); + return fs.promises.mkdir(testDir, { recursive: true }); }); teardown(function () { @@ -353,7 +354,7 @@ flakySuite('SQLite Storage Library', function () { setup(function () { testdir = getRandomTestPath(tmpdir(), 'vsctests', 'storagelibrary'); - return Promises.mkdir(testdir, { recursive: true }); + return fs.promises.mkdir(testdir, { recursive: true }); }); teardown(function () { @@ -534,7 +535,7 @@ flakySuite('SQLite Storage Library', function () { // on shutdown. await storage.checkIntegrity(true).then(null, error => { } /* error is expected here but we do not want to fail */); - await Promises.unlink(backupPath); // also test that the recovery DB is backed up properly + await fs.promises.unlink(backupPath); // also test that the recovery DB is backed up properly let recoveryCalled = false; await storage.close(() => { @@ -798,7 +799,7 @@ flakySuite('SQLite Storage Library', function () { await storage.optimize(); await storage.close(); - const sizeBeforeDeleteAndOptimize = (await Promises.stat(dbPath)).size; + const sizeBeforeDeleteAndOptimize = (await fs.promises.stat(dbPath)).size; storage = new SQLiteStorageDatabase(dbPath); @@ -820,7 +821,7 @@ flakySuite('SQLite Storage Library', function () { await storage.close(); - const sizeAfterDeleteAndOptimize = (await Promises.stat(dbPath)).size; + const sizeAfterDeleteAndOptimize = (await fs.promises.stat(dbPath)).size; strictEqual(sizeAfterDeleteAndOptimize < sizeBeforeDeleteAndOptimize, true); }); diff --git a/src/vs/base/test/node/crypto.test.ts b/src/vs/base/test/node/crypto.test.ts index ff929aaa21c..5da7d91f743 100644 --- a/src/vs/base/test/node/crypto.test.ts +++ b/src/vs/base/test/node/crypto.test.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { tmpdir } from 'os'; import { join } from 'vs/base/common/path'; import { checksum } from 'vs/base/node/crypto'; @@ -19,7 +20,7 @@ flakySuite('Crypto', () => { setup(function () { testDir = getRandomTestPath(tmpdir(), 'vsctests', 'crypto'); - return Promises.mkdir(testDir, { recursive: true }); + return fs.promises.mkdir(testDir, { recursive: true }); }); teardown(function () { diff --git a/src/vs/base/test/node/extpath.test.ts b/src/vs/base/test/node/extpath.test.ts index b7d78dbee46..bf895f6750d 100644 --- a/src/vs/base/test/node/extpath.test.ts +++ b/src/vs/base/test/node/extpath.test.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import assert from 'assert'; import { tmpdir } from 'os'; import { realcase, realcaseSync, realpath, realpathSync } from 'vs/base/node/extpath'; @@ -16,7 +17,7 @@ flakySuite('Extpath', () => { setup(() => { testDir = getRandomTestPath(tmpdir(), 'vsctests', 'extpath'); - return Promises.mkdir(testDir, { recursive: true }); + return fs.promises.mkdir(testDir, { recursive: true }); }); teardown(() => { diff --git a/src/vs/base/test/node/pfs/pfs.test.ts b/src/vs/base/test/node/pfs/pfs.test.ts index 8fec28d2bfc..1d97e68b689 100644 --- a/src/vs/base/test/node/pfs/pfs.test.ts +++ b/src/vs/base/test/node/pfs/pfs.test.ts @@ -25,7 +25,7 @@ flakySuite('PFS', function () { setup(() => { testDir = getRandomTestPath(tmpdir(), 'vsctests', 'pfs'); - return Promises.mkdir(testDir, { recursive: true }); + return fs.promises.mkdir(testDir, { recursive: true }); }); teardown(() => { @@ -39,7 +39,7 @@ flakySuite('PFS', function () { await Promises.writeFile(testFile, 'Hello World', (null!)); - assert.strictEqual((await Promises.readFile(testFile)).toString(), 'Hello World'); + assert.strictEqual((await fs.promises.readFile(testFile)).toString(), 'Hello World'); }); test('writeFile - parallel write on different files works', async () => { @@ -241,7 +241,7 @@ flakySuite('PFS', function () { const symLink = randomPath(testDir); const copyTarget = randomPath(testDir); - await Promises.mkdir(symbolicLinkTarget, { recursive: true }); + await fs.promises.mkdir(symbolicLinkTarget, { recursive: true }); fs.symlinkSync(symbolicLinkTarget, symLink, 'junction'); @@ -258,7 +258,7 @@ flakySuite('PFS', function () { assert.ok(symbolicLink); assert.ok(!symbolicLink.dangling); - const target = await Promises.readlink(copyTarget); + const target = await fs.promises.readlink(copyTarget); assert.strictEqual(target, symbolicLinkTarget); // Copy does not preserve symlinks if configured as such @@ -294,7 +294,7 @@ flakySuite('PFS', function () { const sourceLinkTestFolder = join(sourceFolder, 'link-test'); // copy-test/link-test const sourceLinkMD5JSFolder = join(sourceLinkTestFolder, 'md5'); // copy-test/link-test/md5 const sourceLinkMD5JSFile = join(sourceLinkMD5JSFolder, 'md5.js'); // copy-test/link-test/md5/md5.js - await Promises.mkdir(sourceLinkMD5JSFolder, { recursive: true }); + await fs.promises.mkdir(sourceLinkMD5JSFolder, { recursive: true }); await Promises.writeFile(sourceLinkMD5JSFile, 'Hello from MD5'); const sourceLinkMD5JSFolderLinked = join(sourceLinkTestFolder, 'md5-linked'); // copy-test/link-test/md5-linked @@ -319,7 +319,7 @@ flakySuite('PFS', function () { assert.ok(fs.existsSync(targetLinkMD5JSFolderLinked)); assert.ok(fs.lstatSync(targetLinkMD5JSFolderLinked).isSymbolicLink()); - const linkTarget = await Promises.readlink(targetLinkMD5JSFolderLinked); + const linkTarget = await fs.promises.readlink(targetLinkMD5JSFolderLinked); assert.strictEqual(linkTarget, targetLinkMD5JSFolder); await Promises.rm(targetLinkTestFolder); @@ -353,7 +353,7 @@ flakySuite('PFS', function () { const directory = randomPath(testDir); const symbolicLink = randomPath(testDir); - await Promises.mkdir(directory, { recursive: true }); + await fs.promises.mkdir(directory, { recursive: true }); fs.symlinkSync(directory, symbolicLink, 'junction'); @@ -369,7 +369,7 @@ flakySuite('PFS', function () { const directory = randomPath(testDir); const symbolicLink = randomPath(testDir); - await Promises.mkdir(directory, { recursive: true }); + await fs.promises.mkdir(directory, { recursive: true }); fs.symlinkSync(directory, symbolicLink, 'junction'); @@ -385,7 +385,7 @@ flakySuite('PFS', function () { const parent = randomPath(join(testDir, 'pfs')); const newDir = join(parent, 'öäü'); - await Promises.mkdir(newDir, { recursive: true }); + await fs.promises.mkdir(newDir, { recursive: true }); assert.ok(fs.existsSync(newDir)); @@ -397,7 +397,7 @@ flakySuite('PFS', function () { test('readdir (with file types)', async () => { if (typeof process.versions['electron'] !== 'undefined' /* needs electron */) { const newDir = join(testDir, 'öäü'); - await Promises.mkdir(newDir, { recursive: true }); + await fs.promises.mkdir(newDir, { recursive: true }); await Promises.writeFile(join(testDir, 'somefile.txt'), 'contents'); diff --git a/src/vs/base/test/node/snapshot.test.ts b/src/vs/base/test/node/snapshot.test.ts index 48621bed95f..c98dfc763cd 100644 --- a/src/vs/base/test/node/snapshot.test.ts +++ b/src/vs/base/test/node/snapshot.test.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { tmpdir } from 'os'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { Promises } from 'vs/base/node/pfs'; @@ -23,7 +24,7 @@ suite('snapshot', () => { setup(function () { testDir = getRandomTestPath(tmpdir(), 'vsctests', 'snapshot'); - return Promises.mkdir(testDir, { recursive: true }); + return fs.promises.mkdir(testDir, { recursive: true }); }); teardown(function () { @@ -46,8 +47,8 @@ suite('snapshot', () => { const children = await Promises.readdir(dir); for (const child of children) { const p = path.join(dir, child); - if ((await Promises.stat(p)).isFile()) { - const content = await Promises.readFile(p, 'utf-8'); + if ((await fs.promises.stat(p)).isFile()) { + const content = await fs.promises.readFile(p, 'utf-8'); str += `${' '.repeat(indent)}${child}:\n`; for (const line of content.split('\n')) { str += `${' '.repeat(indent + 2)}${line}\n`; diff --git a/src/vs/base/test/node/zip/zip.test.ts b/src/vs/base/test/node/zip/zip.test.ts index bde3eaf7482..0db7981f743 100644 --- a/src/vs/base/test/node/zip/zip.test.ts +++ b/src/vs/base/test/node/zip/zip.test.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import assert from 'assert'; import { tmpdir } from 'os'; import { createCancelablePromise } from 'vs/base/common/async'; @@ -19,7 +20,7 @@ suite('Zip', () => { test('extract should handle directories', async () => { const testDir = getRandomTestPath(tmpdir(), 'vsctests', 'zip'); - await Promises.mkdir(testDir, { recursive: true }); + await fs.promises.mkdir(testDir, { recursive: true }); const fixtures = FileAccess.asFileUri('vs/base/test/node/zip/fixtures').fsPath; const fixture = path.join(fixtures, 'extract.zip'); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 8548fa7ce4d..deb0d1eab56 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -6,7 +6,7 @@ import 'vs/platform/update/common/update.config.contribution'; import { app, dialog } from 'electron'; -import { unlinkSync } from 'fs'; +import { unlinkSync, promises } from 'fs'; import { URI } from 'vs/base/common/uri'; import { coalesce, distinct } from 'vs/base/common/arrays'; import { Promises } from 'vs/base/common/async'; @@ -139,7 +139,7 @@ class CodeMain { Event.once(lifecycleMainService.onWillShutdown)(evt => { fileService.dispose(); configurationService.dispose(); - evt.join('instanceLockfile', FSPromises.unlink(environmentMainService.mainLockfile).catch(() => { /* ignored */ })); + evt.join('instanceLockfile', promises.unlink(environmentMainService.mainLockfile).catch(() => { /* ignored */ })); }); return instantiationService.createInstance(CodeApplication, mainProcessNodeIpcServer, instanceEnvironment).startup(); @@ -257,7 +257,7 @@ class CodeMain { environmentMainService.workspaceStorageHome.with({ scheme: Schemas.file }).fsPath, environmentMainService.localHistoryHome.with({ scheme: Schemas.file }).fsPath, environmentMainService.backupHome - ].map(path => path ? FSPromises.mkdir(path, { recursive: true }) : undefined)), + ].map(path => path ? promises.mkdir(path, { recursive: true }) : undefined)), // State service stateService.init(), diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 2c1d7afc54c..f940e372ab3 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { hostname, release } from 'os'; import { raceTimeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -13,7 +14,6 @@ import { isAbsolute, join } from 'vs/base/common/path'; import { isWindows } from 'vs/base/common/platform'; import { cwd } from 'vs/base/common/process'; import { URI } from 'vs/base/common/uri'; -import { Promises } from 'vs/base/node/pfs'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IDownloadService } from 'vs/platform/download/common/download'; @@ -124,7 +124,7 @@ class CliMain extends Disposable { await Promise.all([ this.allowWindowsUNCPath(environmentService.appSettingsHome.with({ scheme: Schemas.file }).fsPath), this.allowWindowsUNCPath(environmentService.extensionsPath) - ].map(path => path ? Promises.mkdir(path, { recursive: true }) : undefined)); + ].map(path => path ? fs.promises.mkdir(path, { recursive: true }) : undefined)); // Logger const loggerService = new LoggerService(getLogLevel(environmentService), environmentService.logsHome); diff --git a/src/vs/code/node/sharedProcess/contrib/codeCacheCleaner.ts b/src/vs/code/node/sharedProcess/contrib/codeCacheCleaner.ts index 77ae5d9786a..7ca147e384d 100644 --- a/src/vs/code/node/sharedProcess/contrib/codeCacheCleaner.ts +++ b/src/vs/code/node/sharedProcess/contrib/codeCacheCleaner.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { RunOnceScheduler } from 'vs/base/common/async'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -54,7 +55,7 @@ export class CodeCacheCleaner extends Disposable { // Delete cache folder if old enough const codeCacheEntryPath = join(codeCacheRootPath, codeCache); - const codeCacheEntryStat = await Promises.stat(codeCacheEntryPath); + const codeCacheEntryStat = await fs.promises.stat(codeCacheEntryPath); if (codeCacheEntryStat.isDirectory() && (now - codeCacheEntryStat.mtime.getTime()) > this._DataMaxAge) { this.logService.trace(`[code cache cleanup]: Removing code cache folder ${codeCache}.`); diff --git a/src/vs/code/node/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/node/sharedProcess/contrib/languagePackCachedDataCleaner.ts index 68d2cc16661..0c7e517d4bc 100644 --- a/src/vs/code/node/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/node/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IStringDictionary } from 'vs/base/common/collections'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -58,7 +59,7 @@ export class LanguagePackCachedDataCleaner extends Disposable { try { const installed: IStringDictionary = Object.create(null); - const metaData: ILanguagePackFile = JSON.parse(await Promises.readFile(join(this.environmentService.userDataPath, 'languagepacks.json'), 'utf8')); + const metaData: ILanguagePackFile = JSON.parse(await fs.promises.readFile(join(this.environmentService.userDataPath, 'languagepacks.json'), 'utf8')); for (const locale of Object.keys(metaData)) { const entry = metaData[locale]; installed[`${entry.hash}.${locale}`] = true; @@ -93,7 +94,7 @@ export class LanguagePackCachedDataCleaner extends Disposable { } const candidate = join(folder, entry); - const stat = await Promises.stat(candidate); + const stat = await fs.promises.stat(candidate); if (stat.isDirectory() && (now - stat.mtime.getTime()) > this._DataMaxAge) { this.logService.trace(`[language pack cache cleanup]: Removing language pack cache folder: ${join(packEntry, entry)}`); diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index 749cfa4e6e6..5ff0da22ae5 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -131,7 +131,7 @@ flakySuite('BackupMainService', () => { environmentService = new EnvironmentMainService(parseArgs(process.argv, OPTIONS), { _serviceBrand: undefined, ...product }); - await Promises.mkdir(backupHome, { recursive: true }); + await fs.promises.mkdir(backupHome, { recursive: true }); configService = new TestConfigurationService(); stateMainService = new InMemoryTestStateMainService(); @@ -584,8 +584,8 @@ flakySuite('BackupMainService', () => { assert.strictEqual(((await service.getDirtyWorkspaces()).length), 0); try { - await Promises.mkdir(path.join(folderBackupPath, Schemas.file), { recursive: true }); - await Promises.mkdir(path.join(workspaceBackupPath, Schemas.untitled), { recursive: true }); + await fs.promises.mkdir(path.join(folderBackupPath, Schemas.file), { recursive: true }); + await fs.promises.mkdir(path.join(workspaceBackupPath, Schemas.untitled), { recursive: true }); } catch (error) { // ignore - folder might exist already } diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 26a715bfc19..7e0bc1170e0 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -2,6 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs'; import * as osLib from 'os'; import { Promises } from 'vs/base/common/async'; import { getNodeType, parse, ParseError } from 'vs/base/common/json'; @@ -178,7 +180,7 @@ export async function collectLaunchConfigs(folder: string): Promise(); const launchConfig = join(folder, '.vscode', 'launch.json'); - const contents = await pfs.readFile(launchConfig); + const contents = await fs.promises.readFile(launchConfig); const errors: ParseError[] = []; const json = parse(contents.toString(), errors); diff --git a/src/vs/platform/environment/node/stdin.ts b/src/vs/platform/environment/node/stdin.ts index 56ab151407d..b3e71e04493 100644 --- a/src/vs/platform/environment/node/stdin.ts +++ b/src/vs/platform/environment/node/stdin.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { tmpdir } from 'os'; import { Queue } from 'vs/base/common/async'; import { randomPath } from 'vs/base/common/extpath'; -import { Promises } from 'vs/base/node/pfs'; import { resolveTerminalEncoding } from 'vs/base/node/terminalEncoding'; export function hasStdinWithoutTty() { @@ -43,7 +43,7 @@ export async function readFromStdin(targetPath: string, verbose: boolean, onEnd? let [encoding, iconv] = await Promise.all([ resolveTerminalEncoding(verbose), // respect terminal encoding when piping into file import('@vscode/iconv-lite-umd'), // lazy load encoding module for usage - Promises.appendFile(targetPath, '') // make sure file exists right away (https://github.com/microsoft/vscode/issues/155341) + fs.promises.appendFile(targetPath, '') // make sure file exists right away (https://github.com/microsoft/vscode/issues/155341) ]); if (!iconv.encodingExists(encoding)) { @@ -63,7 +63,7 @@ export async function readFromStdin(targetPath: string, verbose: boolean, onEnd? process.stdin.on('data', chunk => { const chunkStr = decoder.write(chunk); - appendFileQueue.queue(() => Promises.appendFile(targetPath, chunkStr)); + appendFileQueue.queue(() => fs.promises.appendFile(targetPath, chunkStr)); }); process.stdin.on('end', () => { @@ -72,7 +72,7 @@ export async function readFromStdin(targetPath: string, verbose: boolean, onEnd? appendFileQueue.queue(async () => { try { if (typeof end === 'string') { - await Promises.appendFile(targetPath, end); + await fs.promises.appendFile(targetPath, end); } } finally { onEnd?.(); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 29613dba832..d877db4d3c9 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { Promises, Queue } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -363,7 +364,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi const collectFilesFromDirectory = async (dir: string): Promise => { let entries = await pfs.Promises.readdir(dir); entries = entries.map(e => path.join(dir, e)); - const stats = await Promise.all(entries.map(e => pfs.Promises.stat(e))); + const stats = await Promise.all(entries.map(e => fs.promises.stat(e))); let promise: Promise = Promise.resolve([]); stats.forEach((stat, index) => { const entry = entries[index]; diff --git a/src/vs/platform/files/node/diskFileSystemProvider.ts b/src/vs/platform/files/node/diskFileSystemProvider.ts index 5e923bdfab5..9993c5f82ba 100644 --- a/src/vs/platform/files/node/diskFileSystemProvider.ts +++ b/src/vs/platform/files/node/diskFileSystemProvider.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Stats } from 'fs'; +import { Stats, promises } from 'fs'; import { Barrier, retry } from 'vs/base/common/async'; import { ResourceMap } from 'vs/base/common/map'; import { VSBuffer } from 'vs/base/common/buffer'; @@ -203,7 +203,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple const filePath = this.toFilePath(resource); - return await Promises.readFile(filePath); + return await promises.readFile(filePath); } catch (error) { throw this.toFileSystemProviderError(error); } finally { @@ -354,7 +354,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple try { const { stat } = await SymlinkSupport.stat(filePath); if (!(stat.mode & 0o200 /* File mode indicating writable by owner */)) { - await Promises.chmod(filePath, stat.mode | 0o200); + await promises.chmod(filePath, stat.mode | 0o200); } } catch (error) { if (error.code !== 'ENOENT') { @@ -373,7 +373,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple // by first truncating the file and then writing with r+ flag. This helps to save hidden files on Windows // (see https://github.com/microsoft/vscode/issues/931) and prevent removing alternate data streams // (see https://github.com/microsoft/vscode/issues/6363) - await Promises.truncate(filePath, 0); + await promises.truncate(filePath, 0); // After a successful truncate() the flag can be set to 'r+' which will not truncate. flags = 'r+'; @@ -595,7 +595,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple async mkdir(resource: URI): Promise { try { - await Promises.mkdir(this.toFilePath(resource)); + await promises.mkdir(this.toFilePath(resource)); } catch (error) { throw this.toFileSystemProviderError(error); } @@ -613,7 +613,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple await Promises.rm(filePath, RimRafMode.MOVE, rmMoveToPath); } else { try { - await Promises.unlink(filePath); + await promises.unlink(filePath); } catch (unlinkError) { // `fs.unlink` will throw when used on directories @@ -631,7 +631,7 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple } if (isDirectory) { - await Promises.rmdir(filePath); + await promises.rmdir(filePath); } else { throw unlinkError; } @@ -778,10 +778,10 @@ export class DiskFileSystemProvider extends AbstractDiskFileSystemProvider imple locks.add(await this.createResourceLock(to)); if (mkdir) { - await Promises.mkdir(dirname(toFilePath), { recursive: true }); + await promises.mkdir(dirname(toFilePath), { recursive: true }); } - await Promises.copyFile(fromFilePath, toFilePath); + await promises.copyFile(fromFilePath, toFilePath); } catch (error) { if (error.code === 'ENOENT' && !mkdir) { return this.doCloneFile(from, to, true); diff --git a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts index c71f1581cf6..eec6a2232c9 100644 --- a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts +++ b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { watch } from 'fs'; +import { watch, promises } from 'fs'; import { RunOnceWorker, ThrottledWorker } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { isEqualOrParent } from 'vs/base/common/extpath'; @@ -82,7 +82,7 @@ export class NodeJSFileWatcherLibrary extends Disposable { return; } - const stat = await Promises.stat(realPath); + const stat = await promises.stat(realPath); if (this.cts.token.isCancellationRequested) { return; diff --git a/src/vs/platform/files/test/node/diskFileService.integrationTest.ts b/src/vs/platform/files/test/node/diskFileService.integrationTest.ts index fc2f28ffa97..c3c271abbdd 100644 --- a/src/vs/platform/files/test/node/diskFileService.integrationTest.ts +++ b/src/vs/platform/files/test/node/diskFileService.integrationTest.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { createReadStream, existsSync, readdirSync, readFileSync, statSync, writeFileSync } from 'fs'; +import { createReadStream, existsSync, readdirSync, readFileSync, statSync, writeFileSync, promises } from 'fs'; import { tmpdir } from 'os'; import { timeout } from 'vs/base/common/async'; import { bufferToReadable, bufferToStream, streamToBuffer, streamToBufferReadableStream, VSBuffer, VSBufferReadable, VSBufferReadableStream } from 'vs/base/common/buffer'; @@ -429,7 +429,7 @@ flakySuite('Disk File Service', function () { test('resolve - folder symbolic link', async () => { const link = URI.file(join(testDir, 'deep-link')); - await Promises.symlink(join(testDir, 'deep'), link.fsPath, 'junction'); + await promises.symlink(join(testDir, 'deep'), link.fsPath, 'junction'); const resolved = await service.resolve(link); assert.strictEqual(resolved.children!.length, 4); @@ -439,7 +439,7 @@ flakySuite('Disk File Service', function () { (isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('resolve - file symbolic link', async () => { const link = URI.file(join(testDir, 'lorem.txt-linked')); - await Promises.symlink(join(testDir, 'lorem.txt'), link.fsPath); + await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath); const resolved = await service.resolve(link); assert.strictEqual(resolved.isDirectory, false); @@ -447,7 +447,7 @@ flakySuite('Disk File Service', function () { }); test('resolve - symbolic link pointing to nonexistent file does not break', async () => { - await Promises.symlink(join(testDir, 'foo'), join(testDir, 'bar'), 'junction'); + await promises.symlink(join(testDir, 'foo'), join(testDir, 'bar'), 'junction'); const resolved = await service.resolve(URI.file(testDir)); assert.strictEqual(resolved.isDirectory, true); @@ -530,7 +530,7 @@ flakySuite('Disk File Service', function () { (isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (exists)', async () => { const target = URI.file(join(testDir, 'lorem.txt')); const link = URI.file(join(testDir, 'lorem.txt-linked')); - await Promises.symlink(target.fsPath, link.fsPath); + await promises.symlink(target.fsPath, link.fsPath); const source = await service.resolve(link); @@ -552,7 +552,7 @@ flakySuite('Disk File Service', function () { (isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('deleteFile - symbolic link (pointing to nonexistent file)', async () => { const target = URI.file(join(testDir, 'foo')); const link = URI.file(join(testDir, 'bar')); - await Promises.symlink(target.fsPath, link.fsPath); + await promises.symlink(target.fsPath, link.fsPath); let event: FileOperationEvent; disposables.add(service.onDidRunOperation(e => event = e)); @@ -1692,7 +1692,7 @@ flakySuite('Disk File Service', function () { (isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('readFile - dangling symbolic link - https://github.com/microsoft/vscode/issues/116049', async () => { const link = URI.file(join(testDir, 'small.js-link')); - await Promises.symlink(join(testDir, 'small.js'), link.fsPath); + await promises.symlink(join(testDir, 'small.js'), link.fsPath); let error: FileOperationError | undefined = undefined; try { @@ -1833,7 +1833,7 @@ flakySuite('Disk File Service', function () { (isWindows ? test.skip /* windows: cannot create file symbolic link without elevated context */ : test)('writeFile - atomic writing does not break symlinks', async () => { const link = URI.file(join(testDir, 'lorem.txt-linked')); - await Promises.symlink(join(testDir, 'lorem.txt'), link.fsPath); + await promises.symlink(join(testDir, 'lorem.txt'), link.fsPath); const content = 'Updates to the lorem file'; await service.writeFile(link, VSBuffer.fromString(content), { atomic: { postfix: '.vsctmp' } }); @@ -2006,7 +2006,7 @@ flakySuite('Disk File Service', function () { // Here since `close` is not called, all other writes are // waiting on the barrier to release, so doing a readFile // should give us a consistent view of the file contents - assert.strictEqual((await Promises.readFile(resource.fsPath)).toString(), content); + assert.strictEqual((await promises.readFile(resource.fsPath)).toString(), content); } finally { await provider.close(fd); } @@ -2030,10 +2030,10 @@ flakySuite('Disk File Service', function () { try { await provider.write(fd1, 0, VSBuffer.fromString(newContent).buffer, 0, VSBuffer.fromString(newContent).buffer.byteLength); - assert.strictEqual((await Promises.readFile(resource1.fsPath)).toString(), newContent); + assert.strictEqual((await promises.readFile(resource1.fsPath)).toString(), newContent); await provider.write(fd2, 0, VSBuffer.fromString(newContent).buffer, 0, VSBuffer.fromString(newContent).buffer.byteLength); - assert.strictEqual((await Promises.readFile(resource2.fsPath)).toString(), newContent); + assert.strictEqual((await promises.readFile(resource2.fsPath)).toString(), newContent); } finally { await Promise.allSettled([ await provider.close(fd1), @@ -2059,7 +2059,7 @@ flakySuite('Disk File Service', function () { assert.ok(error); // expected because `new-folder` does not exist - await Promises.mkdir(newFolder); + await promises.mkdir(newFolder); const content = readFileSync(URI.file(join(testDir, 'lorem.txt')).fsPath); const newContent = content.toString() + content.toString(); @@ -2069,7 +2069,7 @@ flakySuite('Disk File Service', function () { try { await provider.write(fd, 0, newContentBuffer, 0, newContentBuffer.byteLength); - assert.strictEqual((await Promises.readFile(newResource.fsPath)).toString(), newContent); + assert.strictEqual((await promises.readFile(newResource.fsPath)).toString(), newContent); } finally { await provider.close(fd); } @@ -2291,8 +2291,8 @@ flakySuite('Disk File Service', function () { const content = await service.writeFile(lockedFile, VSBuffer.fromString('Locked File')); assert.strictEqual(content.locked, false); - const stats = await Promises.stat(lockedFile.fsPath); - await Promises.chmod(lockedFile.fsPath, stats.mode & ~0o200); + const stats = await promises.stat(lockedFile.fsPath); + await promises.chmod(lockedFile.fsPath, stats.mode & ~0o200); let stat = await service.stat(lockedFile); assert.strictEqual(stat.locked, true); diff --git a/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts b/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts index 77b3946042d..d47cf0f69fd 100644 --- a/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import assert from 'assert'; import { tmpdir } from 'os'; import { basename, dirname, join } from 'vs/base/common/path'; @@ -158,7 +159,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // New folder const newFolderPath = join(testDir, 'New Folder'); changeFuture = awaitEvent(watcher, newFolderPath, FileChangeType.ADDED); - await Promises.mkdir(newFolderPath); + await fs.promises.mkdir(newFolderPath); await changeFuture; // Rename file @@ -220,7 +221,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // Copy file const copiedFilepath = join(testDir, 'copiedFile.txt'); changeFuture = awaitEvent(watcher, copiedFilepath, FileChangeType.ADDED); - await Promises.copyFile(movedFilepath, copiedFilepath); + await fs.promises.copyFile(movedFilepath, copiedFilepath); await changeFuture; // Copy folder @@ -242,12 +243,12 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // Delete file changeFuture = awaitEvent(watcher, copiedFilepath, FileChangeType.DELETED); - await Promises.unlink(copiedFilepath); + await fs.promises.unlink(copiedFilepath); await changeFuture; // Delete folder changeFuture = awaitEvent(watcher, copiedFolderpath, FileChangeType.DELETED); - await Promises.rmdir(copiedFolderpath); + await fs.promises.rmdir(copiedFolderpath); await changeFuture; watcher.dispose(); @@ -270,7 +271,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // Delete file changeFuture = awaitEvent(watcher, filePath, FileChangeType.DELETED); - await Promises.unlink(filePath); + await fs.promises.unlink(filePath); await changeFuture; // Recreate watcher @@ -290,7 +291,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // Delete + Recreate file const newFilePath = join(testDir, 'lorem.txt'); const changeFuture: Promise = awaitEvent(watcher, newFilePath, FileChangeType.UPDATED); - await Promises.unlink(newFilePath); + await fs.promises.unlink(newFilePath); Promises.writeFile(newFilePath, 'Hello Atomic World'); await changeFuture; }); @@ -302,7 +303,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // Delete + Recreate file const newFilePath = join(filePath); const changeFuture: Promise = awaitEvent(watcher, newFilePath, FileChangeType.UPDATED); - await Promises.unlink(newFilePath); + await fs.promises.unlink(newFilePath); Promises.writeFile(newFilePath, 'Hello Atomic World'); await changeFuture; }); @@ -363,9 +364,9 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int const deleteFuture3: Promise = awaitEvent(watcher, newFilePath3, FileChangeType.DELETED); await Promise.all([ - await Promises.unlink(newFilePath1), - await Promises.unlink(newFilePath2), - await Promises.unlink(newFilePath3) + await fs.promises.unlink(newFilePath1), + await fs.promises.unlink(newFilePath2), + await fs.promises.unlink(newFilePath3) ]); await Promise.all([deleteFuture1, deleteFuture2, deleteFuture3]); @@ -444,7 +445,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int (isWindows /* windows: cannot create file symbolic link without elevated context */ ? test.skip : test)('symlink support (folder watch)', async function () { const link = join(testDir, 'deep-linked'); const linkTarget = join(testDir, 'deep'); - await Promises.symlink(linkTarget, link); + await fs.promises.symlink(linkTarget, link); await watcher.watch([{ path: link, excludes: [], recursive: false }]); @@ -471,14 +472,14 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // Delete file changeFuture = awaitEvent(watcher, filePath, FileChangeType.DELETED, correlationId, expectedCount); - await Promises.unlink(await Promises.realpath(filePath)); // support symlinks + await fs.promises.unlink(await Promises.realpath(filePath)); // support symlinks await changeFuture; } (isWindows /* windows: cannot create file symbolic link without elevated context */ ? test.skip : test)('symlink support (file watch)', async function () { const link = join(testDir, 'lorem.txt-linked'); const linkTarget = join(testDir, 'lorem.txt'); - await Promises.symlink(linkTarget, link); + await fs.promises.symlink(linkTarget, link); await watcher.watch([{ path: link, excludes: [], recursive: false }]); @@ -583,7 +584,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int const onDidWatchFail = Event.toPromise(watcher.onWatchFail); const changeFuture = awaitEvent(watcher, filePath, FileChangeType.DELETED, 1); - Promises.unlink(filePath); + fs.promises.unlink(filePath); await onDidWatchFail; await changeFuture; assert.strictEqual(instance.failed, true); @@ -638,7 +639,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int let changeFuture = awaitEvent(watcher, folderPath, FileChangeType.ADDED, 1); let onDidWatch = Event.toPromise(watcher.onDidWatch); - await Promises.mkdir(folderPath); + await fs.promises.mkdir(folderPath); await changeFuture; await onDidWatch; @@ -649,12 +650,12 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int if (!isMacintosh) { // macOS does not report DELETE events for folders onDidWatchFail = Event.toPromise(watcher.onWatchFail); - await Promises.rmdir(folderPath); + await fs.promises.rmdir(folderPath); await onDidWatchFail; changeFuture = awaitEvent(watcher, folderPath, FileChangeType.ADDED, 1); onDidWatch = Event.toPromise(watcher.onDidWatch); - await Promises.mkdir(folderPath); + await fs.promises.mkdir(folderPath); await changeFuture; await onDidWatch; @@ -677,7 +678,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int const changeFuture = awaitEvent(watcher, folderPath, FileChangeType.ADDED, 1); const onDidWatch = Event.toPromise(watcher.onDidWatch); - await Promises.mkdir(folderPath); + await fs.promises.mkdir(folderPath); await changeFuture; await onDidWatch; @@ -777,7 +778,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // Delete file changeFuture = awaitEvent(watcher, filePath, FileChangeType.DELETED, 1); - await Promises.unlink(filePath); + await fs.promises.unlink(filePath); await changeFuture; }); @@ -793,7 +794,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // Delete file changeFuture = awaitEvent(watcher, filePath, FileChangeType.DELETED, 1); - await Promises.unlink(filePath); + await fs.promises.unlink(filePath); await changeFuture; }); }); diff --git a/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts b/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts index 341edbff2a1..633e4033415 100644 --- a/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { realpathSync } from 'fs'; +import { realpathSync, promises } from 'fs'; import { tmpdir } from 'os'; import { timeout } from 'vs/base/common/async'; import { dirname, join } from 'vs/base/common/path'; @@ -220,7 +220,7 @@ export class TestParcelWatcher extends ParcelWatcher { assert.strictEqual(instance.include(newFolderPath), true); assert.strictEqual(instance.exclude(newFolderPath), false); changeFuture = awaitEvent(watcher, newFolderPath, FileChangeType.ADDED); - await Promises.mkdir(newFolderPath); + await promises.mkdir(newFolderPath); await changeFuture; assert.strictEqual(subscriptions1.get(newFolderPath), FileChangeType.ADDED); assert.strictEqual(subscriptions2.has(newFolderPath), false /* subscription was disposed before the event */); @@ -290,7 +290,7 @@ export class TestParcelWatcher extends ParcelWatcher { // Copy file const copiedFilepath = join(testDir, 'deep', 'copiedFile.txt'); changeFuture = awaitEvent(watcher, copiedFilepath, FileChangeType.ADDED); - await Promises.copyFile(movedFilepath, copiedFilepath); + await promises.copyFile(movedFilepath, copiedFilepath); await changeFuture; // Copy folder @@ -312,30 +312,30 @@ export class TestParcelWatcher extends ParcelWatcher { // Read file does not emit event changeFuture = awaitEvent(watcher, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-read-file'); - await Promises.readFile(anotherNewFilePath); + await promises.readFile(anotherNewFilePath); await Promise.race([timeout(100), changeFuture]); // Stat file does not emit event changeFuture = awaitEvent(watcher, anotherNewFilePath, FileChangeType.UPDATED, 'unexpected-event-from-stat'); - await Promises.stat(anotherNewFilePath); + await promises.stat(anotherNewFilePath); await Promise.race([timeout(100), changeFuture]); // Stat folder does not emit event changeFuture = awaitEvent(watcher, copiedFolderpath, FileChangeType.UPDATED, 'unexpected-event-from-stat'); - await Promises.stat(copiedFolderpath); + await promises.stat(copiedFolderpath); await Promise.race([timeout(100), changeFuture]); // Delete file changeFuture = awaitEvent(watcher, copiedFilepath, FileChangeType.DELETED); disposables.add(instance.subscribe(copiedFilepath, change => subscriptions1.set(change.resource.fsPath, change.type))); - await Promises.unlink(copiedFilepath); + await promises.unlink(copiedFilepath); await changeFuture; assert.strictEqual(subscriptions1.get(copiedFilepath), FileChangeType.DELETED); // Delete folder changeFuture = awaitEvent(watcher, copiedFolderpath, FileChangeType.DELETED); disposables.add(instance.subscribe(copiedFolderpath, change => subscriptions1.set(change.resource.fsPath, change.type))); - await Promises.rmdir(copiedFolderpath); + await promises.rmdir(copiedFolderpath); await changeFuture; assert.strictEqual(subscriptions1.get(copiedFolderpath), FileChangeType.DELETED); @@ -348,7 +348,7 @@ export class TestParcelWatcher extends ParcelWatcher { // Delete + Recreate file const newFilePath = join(testDir, 'deep', 'conway.js'); const changeFuture = awaitEvent(watcher, newFilePath, FileChangeType.UPDATED); - await Promises.unlink(newFilePath); + await promises.unlink(newFilePath); Promises.writeFile(newFilePath, 'Hello Atomic World'); await changeFuture; }); @@ -373,13 +373,13 @@ export class TestParcelWatcher extends ParcelWatcher { // Delete file changeFuture = awaitEvent(watcher, filePath, FileChangeType.DELETED, undefined, correlationId, expectedCount); - await Promises.unlink(filePath); + await promises.unlink(filePath); await changeFuture; } test('multiple events', async function () { await watcher.watch([{ path: testDir, excludes: [], recursive: true }]); - await Promises.mkdir(join(testDir, 'deep-multiple')); + await promises.mkdir(join(testDir, 'deep-multiple')); // multiple add @@ -449,12 +449,12 @@ export class TestParcelWatcher extends ParcelWatcher { const deleteFuture6 = awaitEvent(watcher, newFilePath6, FileChangeType.DELETED); await Promise.all([ - await Promises.unlink(newFilePath1), - await Promises.unlink(newFilePath2), - await Promises.unlink(newFilePath3), - await Promises.unlink(newFilePath4), - await Promises.unlink(newFilePath5), - await Promises.unlink(newFilePath6) + await promises.unlink(newFilePath1), + await promises.unlink(newFilePath2), + await promises.unlink(newFilePath3), + await promises.unlink(newFilePath4), + await promises.unlink(newFilePath5), + await promises.unlink(newFilePath6) ]); await Promise.all([deleteFuture1, deleteFuture2, deleteFuture3, deleteFuture4, deleteFuture5, deleteFuture6]); @@ -563,7 +563,7 @@ export class TestParcelWatcher extends ParcelWatcher { (isWindows /* windows: cannot create file symbolic link without elevated context */ ? test.skip : test)('symlink support (root)', async function () { const link = join(testDir, 'deep-linked'); const linkTarget = join(testDir, 'deep'); - await Promises.symlink(linkTarget, link); + await promises.symlink(linkTarget, link); await watcher.watch([{ path: link, excludes: [], recursive: true }]); @@ -573,7 +573,7 @@ export class TestParcelWatcher extends ParcelWatcher { (isWindows /* windows: cannot create file symbolic link without elevated context */ ? test.skip : test)('symlink support (via extra watch)', async function () { const link = join(testDir, 'deep-linked'); const linkTarget = join(testDir, 'deep'); - await Promises.symlink(linkTarget, link); + await promises.symlink(linkTarget, link); await watcher.watch([{ path: testDir, excludes: [], recursive: true }, { path: link, excludes: [], recursive: true }]); @@ -617,7 +617,7 @@ export class TestParcelWatcher extends ParcelWatcher { // Restore watched path await timeout(1500); // node.js watcher used for monitoring folder restore is async - await Promises.mkdir(watchedPath); + await promises.mkdir(watchedPath); await timeout(1500); // restart is delayed await watcher.whenReady(); @@ -776,7 +776,7 @@ export class TestParcelWatcher extends ParcelWatcher { let changeFuture = awaitEvent(watcher, folderPath, FileChangeType.ADDED, undefined, 1); let onDidWatch = Event.toPromise(watcher.onDidWatch); - await Promises.mkdir(folderPath); + await promises.mkdir(folderPath); await changeFuture; await onDidWatch; @@ -791,7 +791,7 @@ export class TestParcelWatcher extends ParcelWatcher { changeFuture = awaitEvent(watcher, folderPath, FileChangeType.ADDED, undefined, 1); onDidWatch = Event.toPromise(watcher.onDidWatch); - await Promises.mkdir(folderPath); + await promises.mkdir(folderPath); await changeFuture; await onDidWatch; @@ -825,7 +825,7 @@ export class TestParcelWatcher extends ParcelWatcher { const changeFuture = awaitEvent(watcher, folderPath, FileChangeType.ADDED, undefined, 1); const onDidWatch = Event.toPromise(watcher.onDidWatch); - await Promises.mkdir(folderPath); + await promises.mkdir(folderPath); await changeFuture; await onDidWatch; @@ -864,7 +864,7 @@ export class TestParcelWatcher extends ParcelWatcher { // Delete file changeFuture = awaitEvent(watcher, filePath, FileChangeType.DELETED, undefined, 1); - await Promises.unlink(filePath); + await promises.unlink(filePath); await changeFuture; }); }); diff --git a/src/vs/platform/languagePacks/node/languagePacks.ts b/src/vs/platform/languagePacks/node/languagePacks.ts index 3a7df771bc2..698b9b56de7 100644 --- a/src/vs/platform/languagePacks/node/languagePacks.ts +++ b/src/vs/platform/languagePacks/node/languagePacks.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { createHash } from 'crypto'; import { equals } from 'vs/base/common/arrays'; import { Queue } from 'vs/base/common/async'; @@ -180,7 +181,7 @@ class LanguagePacksCache extends Disposable { private withLanguagePacks(fn: (languagePacks: { [language: string]: ILanguagePack }) => T | null = () => null): Promise { return this.languagePacksFileLimiter.queue(() => { let result: T | null = null; - return Promises.readFile(this.languagePacksFilePath, 'utf8') + return fs.promises.readFile(this.languagePacksFilePath, 'utf8') .then(undefined, err => err.code === 'ENOENT' ? Promise.resolve('{}') : Promise.reject(err)) .then<{ [language: string]: ILanguagePack }>(raw => { try { return JSON.parse(raw); } catch (e) { return {}; } }) .then(languagePacks => { result = fn(languagePacks); return languagePacks; }) diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index c7c6cf552ae..2718d0fd7bd 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { exec } from 'child_process'; import { app, BrowserWindow, clipboard, Display, Menu, MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, OpenDialogOptions, OpenDialogReturnValue, powerMonitor, SaveDialogOptions, SaveDialogReturnValue, screen, shell, webContents } from 'electron'; import { arch, cpus, freemem, loadavg, platform, release, totalmem, type } from 'os'; @@ -322,7 +323,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } // Different source, delete it first - await Promises.unlink(source); + await fs.promises.unlink(source); } catch (error) { if (error.code !== 'ENOENT') { throw error; // throw on any error but file not found @@ -330,7 +331,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain } try { - await Promises.symlink(target, source); + await fs.promises.symlink(target, source); } catch (error) { if (error.code !== 'EACCES' && error.code !== 'ENOENT') { throw error; @@ -362,7 +363,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain const { source } = await this.getShellCommandLink(); try { - await Promises.unlink(source); + await fs.promises.unlink(source); } catch (error) { switch (error.code) { case 'EACCES': { diff --git a/src/vs/platform/remote/node/wsl.ts b/src/vs/platform/remote/node/wsl.ts index 3ba33f74b96..4bc71a35f0c 100644 --- a/src/vs/platform/remote/node/wsl.ts +++ b/src/vs/platform/remote/node/wsl.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import * as os from 'os'; import * as cp from 'child_process'; -import { Promises } from 'vs/base/node/pfs'; import * as path from 'path'; let hasWSLFeaturePromise: Promise | undefined; @@ -33,7 +33,7 @@ async function testWSLFeatureInstalled(): Promise { const dllPath = getLxssManagerDllPath(); if (dllPath) { try { - if ((await Promises.stat(dllPath)).isFile()) { + if ((await fs.promises.stat(dllPath)).isFile()) { return true; } } catch (e) { diff --git a/src/vs/platform/state/test/node/state.test.ts b/src/vs/platform/state/test/node/state.test.ts index acce49d3e9c..4674f20a4ee 100644 --- a/src/vs/platform/state/test/node/state.test.ts +++ b/src/vs/platform/state/test/node/state.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { readFileSync } from 'fs'; +import { readFileSync, promises } from 'fs'; import { tmpdir } from 'os'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { Schemas } from 'vs/base/common/network'; @@ -37,7 +37,7 @@ flakySuite('StateService', () => { diskFileSystemProvider = disposables.add(new DiskFileSystemProvider(logService)); disposables.add(fileService.registerProvider(Schemas.file, diskFileSystemProvider)); - return Promises.mkdir(testDir, { recursive: true }); + return promises.mkdir(testDir, { recursive: true }); }); teardown(() => { diff --git a/src/vs/platform/storage/electron-main/storageMain.ts b/src/vs/platform/storage/electron-main/storageMain.ts index c110f28015b..8d6d1b539d5 100644 --- a/src/vs/platform/storage/electron-main/storageMain.ts +++ b/src/vs/platform/storage/electron-main/storageMain.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { top } from 'vs/base/common/arrays'; import { DeferredPromise } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; @@ -403,7 +404,7 @@ export class WorkspaceStorageMain extends BaseStorageMain { } // Ensure storage folder exists - await Promises.mkdir(workspaceStorageFolderPath, { recursive: true }); + await fs.promises.mkdir(workspaceStorageFolderPath, { recursive: true }); // Write metadata into folder (but do not await) this.ensureWorkspaceStorageFolderMeta(workspaceStorageFolderPath); diff --git a/src/vs/platform/telemetry/node/telemetry.ts b/src/vs/platform/telemetry/node/telemetry.ts index 72f87dddf35..d1770958d3b 100644 --- a/src/vs/platform/telemetry/node/telemetry.ts +++ b/src/vs/platform/telemetry/node/telemetry.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { join } from 'vs/base/common/path'; import { Promises } from 'vs/base/node/pfs'; @@ -21,7 +22,7 @@ export async function buildTelemetryMessage(appRoot: string, extensionsPath?: st const files = await Promises.readdir(extensionsPath); for (const file of files) { try { - const fileStat = await Promises.stat(join(extensionsPath, file)); + const fileStat = await fs.promises.stat(join(extensionsPath, file)); if (fileStat.isDirectory()) { dirs.push(file); } @@ -39,15 +40,15 @@ export async function buildTelemetryMessage(appRoot: string, extensionsPath?: st } for (const folder of telemetryJsonFolders) { - const contents = (await Promises.readFile(join(extensionsPath, folder, 'telemetry.json'))).toString(); + const contents = (await fs.promises.readFile(join(extensionsPath, folder, 'telemetry.json'))).toString(); mergeTelemetry(contents, folder); } } - let contents = (await Promises.readFile(join(appRoot, 'telemetry-core.json'))).toString(); + let contents = (await fs.promises.readFile(join(appRoot, 'telemetry-core.json'))).toString(); mergeTelemetry(contents, 'vscode-core'); - contents = (await Promises.readFile(join(appRoot, 'telemetry-extensions.json'))).toString(); + contents = (await fs.promises.readFile(join(appRoot, 'telemetry-extensions.json'))).toString(); mergeTelemetry(contents, 'vscode-extensions'); return JSON.stringify(mergedTelemetry, null, 4); diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index a799870b548..2679ea3683a 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { exec } from 'child_process'; import { timeout } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; @@ -10,7 +11,6 @@ import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import * as path from 'vs/base/common/path'; import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; -import { Promises } from 'vs/base/node/pfs'; import { localize } from 'vs/nls'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -211,9 +211,9 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } if (injection.filesToCopy) { for (const f of injection.filesToCopy) { - await Promises.mkdir(path.dirname(f.dest), { recursive: true }); + await fs.promises.mkdir(path.dirname(f.dest), { recursive: true }); try { - await Promises.copyFile(f.source, f.dest); + await fs.promises.copyFile(f.source, f.dest); } catch { // Swallow error, this should only happen when multiple users are on the same // machine. Since the shell integration scripts rarely change, plus the other user @@ -241,7 +241,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess private async _validateCwd(): Promise { try { - const result = await Promises.stat(this._initialCwd); + const result = await fs.promises.stat(this._initialCwd); if (!result.isDirectory()) { return { message: localize('launchFail.cwdNotDirectory', "Starting directory (cwd) \"{0}\" is not a directory", this._initialCwd.toString()) }; } @@ -268,7 +268,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } try { - const result = await Promises.stat(executable); + const result = await fs.promises.stat(executable); if (!result.isFile() && !result.isSymbolicLink()) { return { message: localize('launchFail.executableIsNotFileOrSymlink', "Path to shell executable \"{0}\" is not a file or a symlink", slc.executable) }; } @@ -608,7 +608,7 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess } this._logService.trace('node-pty.IPty#pid'); try { - return await Promises.readlink(`/proc/${this._ptyProcess.pid}/cwd`); + return await fs.promises.readlink(`/proc/${this._ptyProcess.pid}/cwd`); } catch (error) { return this._initialCwd; } diff --git a/src/vs/platform/terminal/node/terminalProfiles.ts b/src/vs/platform/terminal/node/terminalProfiles.ts index e289fbc34fc..97a85375c95 100644 --- a/src/vs/platform/terminal/node/terminalProfiles.ts +++ b/src/vs/platform/terminal/node/terminalProfiles.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import * as cp from 'child_process'; import { Codicon } from 'vs/base/common/codicons'; import { basename, delimiter, normalize } from 'vs/base/common/path'; @@ -38,7 +39,7 @@ export function detectAvailableProfiles( ): Promise { fsProvider = fsProvider || { existsFile: pfs.SymlinkSupport.existsFile, - readFile: pfs.Promises.readFile + readFile: fs.promises.readFile }; if (isWindows) { return detectAvailableWindowsProfiles( diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 4c49a758185..a2561be0c11 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -55,7 +55,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun @memoize get cachePath(): Promise { const result = path.join(tmpdir(), `vscode-${this.productService.quality}-${this.productService.target}-${process.arch}`); - return pfs.Promises.mkdir(result, { recursive: true }).then(() => result); + return fs.promises.mkdir(result, { recursive: true }).then(() => result); } constructor( @@ -197,7 +197,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun const promises = versions.filter(filter).map(async one => { try { - await pfs.Promises.unlink(path.join(cachePath, one)); + await fs.promises.unlink(path.join(cachePath, one)); } catch (err) { // ignore } diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 290b3f7a67c..070c7ae05d3 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -3,8 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { app, BrowserWindow, WebContents, shell } from 'electron'; -import { Promises } from 'vs/base/node/pfs'; import { addUNCHostToAllowlist } from 'vs/base/node/unc'; import { hostname, release, arch } from 'os'; import { coalesce, distinct } from 'vs/base/common/arrays'; @@ -1057,7 +1057,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic path = sanitizeFilePath(normalize(path), cwd()); try { - const pathStat = await Promises.stat(path); + const pathStat = await fs.promises.stat(path); // File if (pathStat.isFile()) { diff --git a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts index 236f6d6fc32..dc232fbda1e 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { BrowserWindow } from 'electron'; import { Emitter, Event } from 'vs/base/common/event'; import { parse } from 'vs/base/common/json'; @@ -102,7 +103,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork } resolveLocalWorkspace(uri: URI): Promise { - return this.doResolveLocalWorkspace(uri, path => Promises.readFile(path, 'utf8')); + return this.doResolveLocalWorkspace(uri, path => fs.promises.readFile(path, 'utf8')); } private doResolveLocalWorkspace(uri: URI, contentsFn: (path: string) => string): IResolvedWorkspace | undefined; @@ -169,7 +170,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork const { workspace, storedWorkspace } = this.newUntitledWorkspace(folders, remoteAuthority); const configPath = workspace.configPath.fsPath; - await Promises.mkdir(dirname(configPath), { recursive: true }); + await fs.promises.mkdir(dirname(configPath), { recursive: true }); await Promises.writeFile(configPath, JSON.stringify(storedWorkspace, null, '\t')); this.untitledWorkspaces.push({ workspace, remoteAuthority }); diff --git a/src/vs/platform/workspaces/test/electron-main/workspaces.test.ts b/src/vs/platform/workspaces/test/electron-main/workspaces.test.ts index 42fd463b9bf..553be4fa37c 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspaces.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspaces.test.ts @@ -23,7 +23,7 @@ flakySuite('Workspaces', () => { setup(async () => { testDir = getRandomTestPath(tmpDir, 'vsctests', 'workspacesmanagementmainservice'); - return pfs.Promises.mkdir(testDir, { recursive: true }); + return fs.promises.mkdir(testDir, { recursive: true }); }); teardown(() => { diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts index fe5bd2994c8..92f1ac9f589 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesManagementMainService.test.ts @@ -112,7 +112,7 @@ flakySuite('WorkspacesManagementMainService', () => { const fileService = new FileService(logService); service = new WorkspacesManagementMainService(environmentMainService, logService, new UserDataProfilesMainService(new StateService(SaveStrategy.DELAYED, environmentMainService, logService, fileService), new UriIdentityService(fileService), environmentMainService, fileService, logService), new TestBackupMainService(), new TestDialogMainService()); - return pfs.Promises.mkdir(untitledWorkspacesHomePath, { recursive: true }); + return fs.promises.mkdir(untitledWorkspacesHomePath, { recursive: true }); }); teardown(() => { diff --git a/src/vs/server/node/serverConnectionToken.ts b/src/vs/server/node/serverConnectionToken.ts index f976d0e379a..6a6bdcdb56a 100644 --- a/src/vs/server/node/serverConnectionToken.ts +++ b/src/vs/server/node/serverConnectionToken.ts @@ -100,7 +100,7 @@ export async function determineServerConnectionToken(args: ServerParsedArgs): Pr // First try to find a connection token try { - const fileContents = await Promises.readFile(storageLocation); + const fileContents = await fs.promises.readFile(storageLocation); const connectionToken = fileContents.toString().replace(/\r?\n$/, ''); if (connectionTokenRegex.test(connectionToken)) { return connectionToken; diff --git a/src/vs/server/node/webClientServer.ts b/src/vs/server/node/webClientServer.ts index d9c27e100c8..a8ac6043a51 100644 --- a/src/vs/server/node/webClientServer.ts +++ b/src/vs/server/node/webClientServer.ts @@ -3,8 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createReadStream } from 'fs'; -import { Promises } from 'vs/base/node/pfs'; +import { createReadStream, promises } from 'fs'; import * as path from 'path'; import * as http from 'http'; import * as url from 'url'; @@ -55,7 +54,7 @@ export const enum CacheControl { */ export async function serveFile(filePath: string, cacheControl: CacheControl, logService: ILogService, req: http.IncomingMessage, res: http.ServerResponse, responseHeaders: Record): Promise { try { - const stat = await Promises.stat(filePath); // throws an error if file doesn't exist + const stat = await promises.stat(filePath); // throws an error if file doesn't exist if (cacheControl === CacheControl.ETAG) { // Check if file modified since @@ -320,7 +319,7 @@ export class WebClientServer { if (!this._environmentService.isBuilt) { try { - const productOverrides = JSON.parse((await Promises.readFile(join(APP_ROOT, 'product.overrides.json'))).toString()); + const productOverrides = JSON.parse((await promises.readFile(join(APP_ROOT, 'product.overrides.json'))).toString()); Object.assign(productConfiguration, productOverrides); } catch (err) {/* Ignore Error */ } } @@ -360,7 +359,7 @@ export class WebClientServer { if (useTestResolver) { const bundledExtensions: { extensionPath: string; packageJSON: IExtensionManifest }[] = []; for (const extensionPath of ['vscode-test-resolver', 'github-authentication']) { - const packageJSON = JSON.parse((await Promises.readFile(FileAccess.asFileUri(`${builtinExtensionsPath}/${extensionPath}/package.json`).fsPath)).toString()); + const packageJSON = JSON.parse((await promises.readFile(FileAccess.asFileUri(`${builtinExtensionsPath}/${extensionPath}/package.json`).fsPath)).toString()); bundledExtensions.push({ extensionPath, packageJSON }); } values['WORKBENCH_BUILTIN_EXTENSIONS'] = asJSON(bundledExtensions); @@ -368,7 +367,7 @@ export class WebClientServer { let data; try { - const workbenchTemplate = (await Promises.readFile(filePath)).toString(); + const workbenchTemplate = (await promises.readFile(filePath)).toString(); data = workbenchTemplate.replace(/\{\{([^}]+)\}\}/g, (_, key) => values[key] ?? 'undefined'); } catch (e) { res.writeHead(404, { 'Content-Type': 'text/plain' }); @@ -437,7 +436,7 @@ export class WebClientServer { */ private async _handleCallback(res: http.ServerResponse): Promise { const filePath = FileAccess.asFileUri('vs/code/browser/workbench/callback.html').fsPath; - const data = (await Promises.readFile(filePath)).toString(); + const data = (await promises.readFile(filePath)).toString(); const cspDirectives = [ 'default-src \'self\';', 'img-src \'self\' https: data: blob:;', diff --git a/src/vs/workbench/api/node/extHostStoragePaths.ts b/src/vs/workbench/api/node/extHostStoragePaths.ts index 8348d9490b2..e259b3b0d6d 100644 --- a/src/vs/workbench/api/node/extHostStoragePaths.ts +++ b/src/vs/workbench/api/node/extHostStoragePaths.ts @@ -69,14 +69,14 @@ export class ExtensionStoragePaths extends CommonExtensionStoragePaths { async function mkdir(dir: string): Promise { try { - await Promises.stat(dir); + await fs.promises.stat(dir); return; } catch { // doesn't exist, that's OK } try { - await Promises.mkdir(dir, { recursive: true }); + await fs.promises.mkdir(dir, { recursive: true }); } catch { } } @@ -103,7 +103,7 @@ class Lock extends Disposable { this._timer.cancel(); } try { - await Promises.utimes(filename, new Date(), new Date()); + await fs.promises.utimes(filename, new Date(), new Date()); } catch (err) { logService.error(err); logService.info(`Lock '${filename}': Could not update mtime.`); @@ -174,7 +174,7 @@ interface ILockfileContents { async function readLockfileContents(logService: ILogService, filename: string): Promise { let contents: Buffer; try { - contents = await Promises.readFile(filename); + contents = await fs.promises.readFile(filename); } catch (err) { // cannot read the file logService.error(err); @@ -196,7 +196,7 @@ async function readLockfileContents(logService: ILogService, filename: string): async function readmtime(logService: ILogService, filename: string): Promise { let stats: fs.Stats; try { - stats = await Promises.stat(filename); + stats = await fs.promises.stat(filename); } catch (err) { // cannot read the file stats to check if it is stale or not logService.error(err); @@ -279,7 +279,7 @@ async function checkStaleAndTryAcquireLock(logService: ILogService, filename: st async function tryDeleteAndAcquireLock(logService: ILogService, filename: string): Promise { logService.info(`Lock '${filename}': Deleting a stale lock.`); try { - await Promises.unlink(filename); + await fs.promises.unlink(filename); } catch (err) { // cannot delete the file // maybe the file is already deleted diff --git a/src/vs/workbench/api/node/extHostTunnelService.ts b/src/vs/workbench/api/node/extHostTunnelService.ts index 13498806104..54b83109489 100644 --- a/src/vs/workbench/api/node/extHostTunnelService.ts +++ b/src/vs/workbench/api/node/extHostTunnelService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { exec } from 'child_process'; import { VSBuffer } from 'vs/base/common/buffer'; import { Emitter } from 'vs/base/common/event'; @@ -243,8 +244,8 @@ export class NodeExtHostTunnelService extends ExtHostTunnelService { let tcp: string = ''; let tcp6: string = ''; try { - tcp = await pfs.Promises.readFile('/proc/net/tcp', 'utf8'); - tcp6 = await pfs.Promises.readFile('/proc/net/tcp6', 'utf8'); + tcp = await fs.promises.readFile('/proc/net/tcp', 'utf8'); + tcp6 = await fs.promises.readFile('/proc/net/tcp6', 'utf8'); } catch (e) { // File reading error. No additional handling needed. } @@ -265,10 +266,10 @@ export class NodeExtHostTunnelService extends ExtHostTunnelService { try { const pid: number = Number(childName); const childUri = resources.joinPath(URI.file('/proc'), childName); - const childStat = await pfs.Promises.stat(childUri.fsPath); + const childStat = await fs.promises.stat(childUri.fsPath); if (childStat.isDirectory() && !isNaN(pid)) { - const cwd = await pfs.Promises.readlink(resources.joinPath(childUri, 'cwd').fsPath); - const cmd = await pfs.Promises.readFile(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8'); + const cwd = await fs.promises.readlink(resources.joinPath(childUri, 'cwd').fsPath); + const cmd = await fs.promises.readFile(resources.joinPath(childUri, 'cmdline').fsPath, 'utf8'); processes.push({ pid, cwd, cmd }); } } catch (e) { diff --git a/src/vs/workbench/contrib/themes/test/node/colorRegistry.releaseTest.ts b/src/vs/workbench/contrib/themes/test/node/colorRegistry.releaseTest.ts index 710fb4da3c4..656f9ec5cab 100644 --- a/src/vs/workbench/contrib/themes/test/node/colorRegistry.releaseTest.ts +++ b/src/vs/workbench/contrib/themes/test/node/colorRegistry.releaseTest.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import { Registry } from 'vs/platform/registry/common/platform'; import { IColorRegistry, Extensions, ColorContribution, asCssVariableName } from 'vs/platform/theme/common/colorRegistry'; import { asTextOrError } from 'vs/platform/request/common/request'; @@ -40,7 +41,7 @@ suite('Color Registry', function () { test(`update colors in ${knwonVariablesFileName}`, async function () { const varFilePath = FileAccess.asFileUri(`vs/../../build/lib/stylelint/${knwonVariablesFileName}`).fsPath; - const content = (await pfs.Promises.readFile(varFilePath)).toString(); + const content = (await fs.promises.readFile(varFilePath)).toString(); const variablesInfo = JSON.parse(content); @@ -171,7 +172,7 @@ async function getColorsFromExtension(): Promise<{ [id: string]: string }> { const result: { [id: string]: string } = Object.create(null); for (const folder of extFolders) { try { - const packageJSON = JSON.parse((await pfs.Promises.readFile(path.join(extPath, folder, 'package.json'))).toString()); + const packageJSON = JSON.parse((await fs.promises.readFile(path.join(extPath, folder, 'package.json'))).toString()); const contributes = packageJSON['contributes']; if (contributes) { const colors = contributes['colors']; diff --git a/src/vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts b/src/vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts index 1ef7cb70863..909109ade3a 100644 --- a/src/vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts +++ b/src/vs/workbench/services/keybinding/test/node/keyboardMapperTestUtils.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as fs from 'fs'; import assert from 'assert'; import * as path from 'vs/base/common/path'; import { SingleModifierChord, ResolvedKeybinding, Keybinding } from 'vs/base/common/keybindings'; @@ -46,7 +47,7 @@ export function assertResolveKeybinding(mapper: IKeyboardMapper, keybinding: Key } export function readRawMapping(file: string): Promise { - return Promises.readFile(FileAccess.asFileUri(`vs/workbench/services/keybinding/test/node/${file}.js`).fsPath).then((buff) => { + return fs.promises.readFile(FileAccess.asFileUri(`vs/workbench/services/keybinding/test/node/${file}.js`).fsPath).then((buff) => { const contents = buff.toString(); const func = new Function('define', contents);// CodeQL [SM01632] This is used in tests and we read the files as JS to avoid slowing down TS compilation let rawMappings: T | null = null; @@ -60,7 +61,7 @@ export function readRawMapping(file: string): Promise { export function assertMapping(writeFileIfDifferent: boolean, mapper: IKeyboardMapper, file: string): Promise { const filePath = path.normalize(FileAccess.asFileUri(`vs/workbench/services/keybinding/test/node/${file}`).fsPath); - return Promises.readFile(filePath).then((buff) => { + return fs.promises.readFile(filePath).then((buff) => { const expected = buff.toString().replace(/\r\n/g, '\n'); const actual = mapper.dumpDebugInfo().replace(/\r\n/g, '\n'); if (actual !== expected && writeFileIfDifferent) { From d3035ba351caee12ad2dc4491254796680d3aa9f Mon Sep 17 00:00:00 2001 From: Robo Date: Mon, 1 Jul 2024 16:26:45 +0900 Subject: [PATCH 0199/2222] ci: disable cli glibc checks (#219327) --- build/azure-pipelines/cli/cli-compile.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/cli/cli-compile.yml b/build/azure-pipelines/cli/cli-compile.yml index 267682f7f6d..a70f607ad69 100644 --- a/build/azure-pipelines/cli/cli-compile.yml +++ b/build/azure-pipelines/cli/cli-compile.yml @@ -78,8 +78,8 @@ steps: fi done < <("$OBJDUMP" -T "$PWD/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code") if [[ "$glibc_version" != "2.17" ]]; then - echo "Error: binary has dependency on GLIBC > 2.17" - exit 1 + echo "Error: binary has dependency on GLIBC > 2.17, found $glibc_version" + #exit 1 fi fi displayName: Compile ${{ parameters.VSCODE_CLI_TARGET }} From ea03089beec1e7e5c4e5417660a4d6252782ef58 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 1 Jul 2024 10:29:54 +0200 Subject: [PATCH 0200/2222] fixup `ChatRequestNotebookData` (#219331) re https://github.com/microsoft/vscode/issues/218363 --- src/vs/workbench/api/common/extHost.api.impl.ts | 2 +- src/vs/workbench/api/common/extHostChatAgents2.ts | 8 +++----- src/vs/workbench/api/common/extHostTypes.ts | 6 +++--- .../vscode.proposed.chatParticipantPrivate.d.ts | 4 ++-- 4 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 402b55888f4..b6b4f5746bb 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -210,7 +210,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostUriOpeners = rpcProtocol.set(ExtHostContext.ExtHostUriOpeners, new ExtHostUriOpeners(rpcProtocol)); const extHostProfileContentHandlers = rpcProtocol.set(ExtHostContext.ExtHostProfileContentHandlers, new ExtHostProfileContentHandlers(rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService)); - const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands, extHostDocuments, extHostNotebook)); + const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands, extHostDocuments)); const extHostChatVariables = rpcProtocol.set(ExtHostContext.ExtHostChatVariables, new ExtHostChatVariables(rpcProtocol)); const extHostLanguageModelTools = rpcProtocol.set(ExtHostContext.ExtHostLanguageModelTools, new ExtHostLanguageModelTools(rpcProtocol)); const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol)); diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index a34438f1da1..8b2209b5363 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -21,7 +21,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IChatAgentHistoryEntryDto, IChatProgressDto, IExtensionChatAgentMetadata, IMainContext, MainContext, MainThreadChatAgentsShape2 } from 'vs/workbench/api/common/extHost.protocol'; import { CommandsConverter, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { ExtHostDocuments } from 'vs/workbench/api/common/extHostDocuments'; -import { ExtHostNotebookController } from 'vs/workbench/api/common/extHostNotebook'; import * as typeConvert from 'vs/workbench/api/common/extHostTypeConverters'; import * as extHostTypes from 'vs/workbench/api/common/extHostTypes'; import { ChatAgentLocation, IChatAgentRequest, IChatAgentResult } from 'vs/workbench/contrib/chat/common/chatAgents'; @@ -266,8 +265,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS mainContext: IMainContext, private readonly _logService: ILogService, private readonly _commands: ExtHostCommands, - private readonly _documents: ExtHostDocuments, - private readonly _notebooks: ExtHostNotebookController + private readonly _documents: ExtHostDocuments ) { super(); this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2); @@ -323,8 +321,8 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS } else if (request.locationData?.type === ChatAgentLocation.Notebook) { // notebook data - const notebook = this._notebooks.getNotebookDocument(request.locationData.sessionInputUri); - location2 = new extHostTypes.ChatRequestNotebookData(notebook.apiNotebook); + const cell = this._documents.getDocument(request.locationData.sessionInputUri); + location2 = new extHostTypes.ChatRequestNotebookData(cell); } else if (request.locationData?.type === ChatAgentLocation.Terminal) { // TBD diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 4189c61331f..92b968e001e 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4449,7 +4449,7 @@ export enum ChatLocation { Editor = 4, } -export class ChatRequestEditorData { +export class ChatRequestEditorData implements vscode.ChatRequestEditorData { constructor( readonly document: vscode.TextDocument, readonly selection: vscode.Selection, @@ -4457,9 +4457,9 @@ export class ChatRequestEditorData { ) { } } -export class ChatRequestNotebookData { +export class ChatRequestNotebookData implements vscode.ChatRequestNotebookData { constructor( - readonly notebook: vscode.NotebookDocument + readonly cell: vscode.TextDocument ) { } } diff --git a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts index d3c0ca44e26..ce699b1fec9 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts @@ -40,9 +40,9 @@ declare module 'vscode' { export class ChatRequestNotebookData { //TODO@API should be the editor - notebook: NotebookDocument; + readonly cell: TextDocument; - constructor(notebook: NotebookDocument); + constructor(cell: TextDocument); } export interface ChatRequest { From d65fd5ba2e7202cac14316c2935680222e1af9cb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 1 Jul 2024 11:44:19 +0200 Subject: [PATCH 0201/2222] nls follow up debt work (#219265) --- build/gulpfile.editor.js | 1 + build/gulpfile.reh.js | 12 +---- build/gulpfile.vscode.js | 10 ++-- build/gulpfile.vscode.web.js | 1 + src/bootstrap-amd.js | 4 +- src/main.js | 15 ++---- src/vs/base/node/nls.js | 47 ++++--------------- .../localization.contribution.ts | 2 +- 8 files changed, 22 insertions(+), 70 deletions(-) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index b585fa665c1..fe29d4fe183 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -84,6 +84,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { }); // Disable mangling for the editor, as it complicates debugging & quite a few users rely on private/protected fields. +// Disable NLS task to remove english strings to preserve backwards compatibility when we removed the `vs/nls!` AMD plugin. const compileEditorAMDTask = task.define('compile-editor-amd', compilation.compileTask('out-editor-src', 'out-editor-build', true, { disableMangle: true, preserveEnglish: true })); const optimizeEditorAMDTask = task.define('optimize-editor-amd', optimize.optimizeTask( diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index c6e8699d5bd..b6030c6102e 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -52,18 +52,9 @@ const BUILD_TARGETS = [ const serverResources = [ - // Bootstrap - 'out-build/bootstrap.js', - 'out-build/bootstrap-fork.js', - 'out-build/bootstrap-amd.js', - 'out-build/bootstrap-node.js', - // NLS 'out-build/nls.messages.json', - // Performance - 'out-build/vs/base/common/performance.js', - // Process monitor 'out-build/vs/base/node/cpuUsage.sh', 'out-build/vs/base/node/ps.sh', @@ -427,7 +418,8 @@ function tweakProductForServerWeb(product) { src: 'out-build', entryPoints: [ 'out-build/server-main.js', - 'out-build/server-cli.js' + 'out-build/server-cli.js', + 'out-build/bootstrap-fork.js', ], platform: 'node', external: [ diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index b2967504fc5..5861cb5067a 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -51,18 +51,12 @@ const vscodeEntryPoints = [ ].flat(); const vscodeResources = [ - 'out-build/bootstrap.js', - 'out-build/bootstrap-fork.js', - 'out-build/bootstrap-amd.js', - 'out-build/bootstrap-node.js', - 'out-build/bootstrap-window.js', 'out-build/nls.messages.json', 'out-build/nls.keys.json', 'out-build/vs/**/*.{svg,png,html,jpg,mp3}', '!out-build/vs/code/browser/**/*.html', '!out-build/vs/code/**/*-dev.html', '!out-build/vs/editor/standalone/**/*.svg', - 'out-build/vs/base/common/performance.js', 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', 'out-build/vs/base/browser/ui/codicons/codicon/**', 'out-build/vs/base/parts/sandbox/electron-sandbox/preload.js', @@ -111,12 +105,14 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series( src: 'out-build', entryPoints: [ 'out-build/main.js', - 'out-build/cli.js' + 'out-build/cli.js', + 'out-build/bootstrap-fork.js', ], platform: 'node', external: [ 'electron', 'minimist', + 'original-fs', // TODO: we cannot inline `product.json` because // it is being changed during build time at a later // point in time (such as `checksums`) diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 8fb431e94f9..4480f5e1046 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -31,6 +31,7 @@ const quality = product.quality; const version = (quality && quality !== 'stable') ? `${packageJson.version}-${quality}` : packageJson.version; const vscodeWebResourceIncludes = [ + // Workbench 'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png,jpg,mp3}', 'out-build/vs/code/browser/workbench/*.html', diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 87fea16e97a..aba8ee48fb6 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -73,7 +73,7 @@ function setupNLS() { * @returns {Promise} */ async function doSetupNLS() { - performance.mark('code/fork/willLoadNls'); + performance.mark('code/amd/willLoadNls'); /** @type {INLSConfiguration | undefined} */ let nlsConfig = undefined; @@ -130,7 +130,7 @@ async function doSetupNLS() { } } - performance.mark('code/fork/didLoadNls'); + performance.mark('code/amd/didLoadNls'); return nlsConfig; } diff --git a/src/main.js b/src/main.js index 795d8a31729..a0b8f09fff3 100644 --- a/src/main.js +++ b/src/main.js @@ -16,7 +16,7 @@ const perf = require('./vs/base/common/performance'); perf.mark('code/didStartMain'); const path = require('path'); -const fs = require('fs'); +const fs = require('original-fs'); const os = require('os'); const bootstrap = require('./bootstrap'); const bootstrapNode = require('./bootstrap-node'); @@ -215,6 +215,7 @@ function configureCommandlineSwitchesSync(cliArgs) { ]; if (process.platform === 'linux') { + // Force enable screen readers on Linux via this flag SUPPORTED_ELECTRON_SWITCHES.push('force-renderer-accessibility'); @@ -589,16 +590,6 @@ function getCodeCachePath() { return path.join(userDataPath, 'CachedData', commit); } -/** - * @param {string} dir - * @returns {Promise} - */ -function mkdirp(dir) { - return new Promise((resolve, reject) => { - fs.mkdir(dir, { recursive: true }, err => (err && err.code !== 'EEXIST') ? reject(err) : resolve(dir)); - }); -} - /** * @param {string | undefined} dir * @returns {Promise} @@ -606,7 +597,7 @@ function mkdirp(dir) { async function mkdirpIgnoreError(dir) { if (typeof dir === 'string') { try { - await mkdirp(dir); + await fs.promises.mkdir(dir, { recursive: true }); return dir; } catch (error) { diff --git a/src/vs/base/node/nls.js b/src/vs/base/node/nls.js index d4bac146516..c171eb6542e 100644 --- a/src/vs/base/node/nls.js +++ b/src/vs/base/node/nls.js @@ -45,35 +45,6 @@ return fs.promises.utimes(path, date, date); } - /** - * @param {string} path - */ - function mkdirp(path) { - return fs.promises.mkdir(path, { recursive: true }); - } - - /** - * @param {string} path - */ - function rimraf(path) { - return fs.promises.rm(path, { recursive: true, force: true, maxRetries: 3 }); - } - - /** - * @param {string} path - */ - function readFile(path) { - return fs.promises.readFile(path, 'utf-8'); - } - - /** - * @param {string} path - * @param {string} content - */ - function writeFile(path, content) { - return fs.promises.writeFile(path, content, 'utf-8'); - } - //#endregion /** @@ -90,7 +61,7 @@ async function getLanguagePackConfigurations(userDataPath) { const configFile = path.join(userDataPath, 'languagepacks.json'); try { - return JSON.parse(await readFile(configFile)); + return JSON.parse(await fs.promises.readFile(configFile, 'utf-8')); } catch (err) { return undefined; // Do nothing. If we can't read the file we have no language pack config. } @@ -152,7 +123,7 @@ if ( process.env['VSCODE_DEV'] || userLocale === 'pseudo' || - userLocale === 'en' || userLocale === 'en-us' || + userLocale.startsWith('en') || !commit || !userDataPath ) { @@ -190,7 +161,7 @@ const languagePackCorruptMarkerFile = path.join(globalLanguagePackCachePath, 'corrupted.info'); if (await exists(languagePackCorruptMarkerFile)) { - await rimraf(globalLanguagePackCachePath); // delete corrupted cache folder + await fs.promises.rm(globalLanguagePackCachePath, { recursive: true, force: true, maxRetries: 3 }); // delete corrupted cache folder } /** @type {INLSConfiguration} */ @@ -230,10 +201,10 @@ nlsDefaultMessages, nlsPackdata ] = await Promise.all([ - mkdirp(commitLanguagePackCachePath), - JSON.parse(await readFile(path.join(nlsMetadataPath, 'nls.keys.json'))), - JSON.parse(await readFile(path.join(nlsMetadataPath, 'nls.messages.json'))), - JSON.parse(await readFile(mainLanguagePackPath)) + fs.promises.mkdir(commitLanguagePackCachePath, { recursive: true }), + JSON.parse(await fs.promises.readFile(path.join(nlsMetadataPath, 'nls.keys.json'), 'utf-8')), + JSON.parse(await fs.promises.readFile(path.join(nlsMetadataPath, 'nls.messages.json'), 'utf-8')), + JSON.parse(await fs.promises.readFile(mainLanguagePackPath, 'utf-8')) ]); /** @type {string[]} */ @@ -254,8 +225,8 @@ } await Promise.all([ - writeFile(languagePackMessagesFile, JSON.stringify(nlsResult)), - writeFile(translationsConfigFile, JSON.stringify(languagePack.translations)) + fs.promises.writeFile(languagePackMessagesFile, JSON.stringify(nlsResult), 'utf-8'), + fs.promises.writeFile(translationsConfigFile, JSON.stringify(languagePack.translations), 'utf-8') ]); perf.mark('code/didGenerateNls'); diff --git a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts index b603ed444e6..a93fc9f9901 100644 --- a/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts +++ b/src/vs/workbench/contrib/localization/electron-sandbox/localization.contribution.ts @@ -103,7 +103,7 @@ class NativeLocalizationWorkbenchContribution extends BaseLocalizationWorkbenchC if (!this.galleryService.isEnabled()) { return; } - if (!language || !locale || locale === 'en' || locale.indexOf('en-') === 0) { + if (!language || !locale || platform.Language.isDefaultVariant()) { return; } if (locale.startsWith(language) || languagePackSuggestionIgnoreList.includes(locale)) { From 4c69ceba2f9d9dc6b679eeb7b1e4cf22fcb4aca3 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Mon, 1 Jul 2024 12:13:03 +0200 Subject: [PATCH 0202/2222] rename editor.collapsedText to foldPlaceholderForeground (#219338) rename vscode-editor-collapsedText --- build/lib/stylelint/vscode-known-variables.json | 2 +- src/vs/editor/contrib/folding/browser/folding.css | 2 +- src/vs/editor/contrib/folding/browser/foldingDecorations.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 272347c6c99..148aa2786dd 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -128,7 +128,6 @@ "--vscode-dropdown-foreground", "--vscode-dropdown-listBackground", "--vscode-editor-background", - "--vscode-editor-collapsedText", "--vscode-editor-findMatchBackground", "--vscode-editor-findMatchBorder", "--vscode-editor-findMatchForeground", @@ -139,6 +138,7 @@ "--vscode-editor-findRangeHighlightBorder", "--vscode-editor-focusedStackFrameHighlightBackground", "--vscode-editor-foldBackground", + "--vscode-editor-foldPlaceholderForeground", "--vscode-editor-foreground", "--vscode-editor-hoverHighlightBackground", "--vscode-editor-inactiveSelectionBackground", diff --git a/src/vs/editor/contrib/folding/browser/folding.css b/src/vs/editor/contrib/folding/browser/folding.css index 9a7c91ced43..5f7ab05db78 100644 --- a/src/vs/editor/contrib/folding/browser/folding.css +++ b/src/vs/editor/contrib/folding/browser/folding.css @@ -31,7 +31,7 @@ } .monaco-editor .inline-folded:after { - color: var(--vscode-editor-collapsedText); + color: var(--vscode-editor-foldPlaceholderForeground); margin: 0.1em 0.2em 0 0.2em; content: "\22EF"; /* ellipses unicode character */ display: inline; diff --git a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts index d30d019ca98..2350f9aad34 100644 --- a/src/vs/editor/contrib/folding/browser/foldingDecorations.ts +++ b/src/vs/editor/contrib/folding/browser/foldingDecorations.ts @@ -15,7 +15,7 @@ import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { ThemeIcon } from 'vs/base/common/themables'; const foldBackground = registerColor('editor.foldBackground', { light: transparent(editorSelectionBackground, 0.3), dark: transparent(editorSelectionBackground, 0.3), hcDark: null, hcLight: null }, localize('foldBackgroundBackground', "Background color behind folded ranges. The color must not be opaque so as not to hide underlying decorations."), true); -registerColor('editor.collapsedText', { light: '#808080', dark: '#808080', hcDark: null, hcLight: null }, localize('collapsedTextColor', "Color of the collapsed text after the first line of a folded range.")); +registerColor('editor.foldPlaceholderForeground', { light: '#808080', dark: '#808080', hcDark: null, hcLight: null }, localize('collapsedTextColor', "Color of the collapsed text after the first line of a folded range.")); registerColor('editorGutter.foldingControlForeground', iconForeground, localize('editorGutter.foldingControlForeground', 'Color of the folding control in the editor gutter.')); export const foldingExpandedIcon = registerIcon('folding-expanded', Codicon.chevronDown, localize('foldingExpandedIcon', 'Icon for expanded ranges in the editor glyph margin.')); From af5cf640d81fd44b2ec16a5b0bf6d2939a1eb4be Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 1 Jul 2024 12:23:41 +0200 Subject: [PATCH 0203/2222] :up: `"@playwright/test": "^1.45.0"` (#219324) --- .../common/installPlaywright.ts | 14 ---- package.json | 4 +- .../clipboard/browser/clipboardService.ts | 16 +++-- test/automation/src/playwrightDriver.ts | 2 +- yarn.lock | 65 ++++++++----------- 5 files changed, 42 insertions(+), 59 deletions(-) delete mode 100644 build/azure-pipelines/common/installPlaywright.ts diff --git a/build/azure-pipelines/common/installPlaywright.ts b/build/azure-pipelines/common/installPlaywright.ts deleted file mode 100644 index 742b6e0e399..00000000000 --- a/build/azure-pipelines/common/installPlaywright.ts +++ /dev/null @@ -1,14 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -process.env.DEBUG='pw:install'; // enable logging for this (https://github.com/microsoft/playwright/issues/17394) - -const { installDefaultBrowsersForNpmInstall } = require('playwright-core/lib/server'); - -async function install() { - await installDefaultBrowsersForNpmInstall(); -} - -install(); diff --git a/package.json b/package.json index e8e6a3d2e34..8e5b8e35bed 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "watch-cli": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js watch-cli", "eslint": "node build/eslint", "stylelint": "node build/stylelint", - "playwright-install": "node build/azure-pipelines/common/installPlaywright.js", + "playwright-install": "yarn playwright install", "compile-build": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js compile-build", "compile-extensions-build": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js compile-extensions-build", "minify-vscode": "node --max-old-space-size=4095 ./node_modules/gulp/bin/gulp.js minify-vscode", @@ -108,7 +108,7 @@ "yazl": "^2.4.3" }, "devDependencies": { - "@playwright/test": "^1.40.1", + "@playwright/test": "^1.45.0", "@swc/core": "1.3.62", "@types/cookie": "^0.3.3", "@types/debug": "^4.1.5", diff --git a/src/vs/workbench/services/clipboard/browser/clipboardService.ts b/src/vs/workbench/services/clipboard/browser/clipboardService.ts index fc2b1b582e6..1c1472d0cc1 100644 --- a/src/vs/workbench/services/clipboard/browser/clipboardService.ts +++ b/src/vs/workbench/services/clipboard/browser/clipboardService.ts @@ -28,7 +28,19 @@ export class BrowserClipboardService extends BaseBrowserClipboardService { super(layoutService, logService); } + override async writeText(text: string, type?: string): Promise { + if (!!this.environmentService.extensionTestsLocationURI && typeof type !== 'string') { + type = 'vscode-tests'; // force in-memory clipboard for tests to avoid permission issues + } + + return super.writeText(text, type); + } + override async readText(type?: string): Promise { + if (!!this.environmentService.extensionTestsLocationURI && typeof type !== 'string') { + type = 'vscode-tests'; // force in-memory clipboard for tests to avoid permission issues + } + if (type) { return super.readText(type); } @@ -36,10 +48,6 @@ export class BrowserClipboardService extends BaseBrowserClipboardService { try { return await getActiveWindow().navigator.clipboard.readText(); } catch (error) { - if (!!this.environmentService.extensionTestsLocationURI) { - return ''; // do not ask for input in tests (https://github.com/microsoft/vscode/issues/112264) - } - return new Promise(resolve => { // Inform user about permissions problem (https://github.com/microsoft/vscode/issues/112089) diff --git a/test/automation/src/playwrightDriver.ts b/test/automation/src/playwrightDriver.ts index 5e77d55e6bf..0aeb50e2194 100644 --- a/test/automation/src/playwrightDriver.ts +++ b/test/automation/src/playwrightDriver.ts @@ -231,7 +231,7 @@ export class PlaywrightDriver { return this.page.evaluate(([driver]) => driver.getLogs(), [await this.getDriverHandle()] as const); } - private async evaluateWithDriver(pageFunction: PageFunction[], T>) { + private async evaluateWithDriver(pageFunction: PageFunction) { return this.page.evaluate(pageFunction, [await this.getDriverHandle()]); } diff --git a/yarn.lock b/yarn.lock index 71aef4295fa..c445f43d995 100644 --- a/yarn.lock +++ b/yarn.lock @@ -909,12 +909,12 @@ dependencies: playwright-core "1.40.1" -"@playwright/test@^1.40.1": - version "1.40.1" - resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.40.1.tgz#9e66322d97b1d74b9f8718bacab15080f24cde65" - integrity sha512-EaaawMTOeEItCRvfmkI9v6rBkF1svM8wjl/YPRrg2N2Wmp+4qJYkWtJsbew1szfKKDm6fPLy4YAanBhIlf9dWw== +"@playwright/test@^1.45.0": + version "1.45.0" + resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.45.0.tgz#790a66165a46466c0d7099dd260881802f5aba7e" + integrity sha512-TVYsfMlGAaxeUllNkywbwek67Ncf8FRGn8ZlRdO291OL3NjG9oMbfVhyP82HQF0CZLMrYsvesqoUekxdWuF9Qw== dependencies: - playwright "1.40.1" + playwright "1.45.0" "@sindresorhus/is@^4.0.0": version "4.6.0" @@ -7930,12 +7930,17 @@ playwright-core@1.40.1: resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.40.1.tgz#442d15e86866a87d90d07af528e0afabe4c75c05" integrity sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ== -playwright@1.40.1, playwright@^1.40.1: - version "1.40.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.40.1.tgz#a11bf8dca15be5a194851dbbf3df235b9f53d7ae" - integrity sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw== +playwright-core@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.45.0.tgz#5741a670b7c9060ce06852c0051d84736fb94edc" + integrity sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ== + +playwright@1.45.0: + version "1.45.0" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.45.0.tgz#400c709c64438690f13705cb9c88ef93089c5c27" + integrity sha512-4z3ac3plDfYzGB6r0Q3LF8POPR20Z8D0aXcxbJvmfMgSSq1hkcgvFRXJk9rUq5H/MJ0Ktal869hhOdI/zUTeLA== dependencies: - playwright-core "1.40.1" + playwright-core "1.45.0" optionalDependencies: fsevents "2.3.2" @@ -7946,6 +7951,15 @@ playwright@^1.29.2: dependencies: playwright-core "1.30.0" +playwright@^1.40.1: + version "1.40.1" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.40.1.tgz#a11bf8dca15be5a194851dbbf3df235b9f53d7ae" + integrity sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw== + dependencies: + playwright-core "1.40.1" + optionalDependencies: + fsevents "2.3.2" + plist@^3.0.1: version "3.0.5" resolved "https://registry.yarnpkg.com/plist/-/plist-3.0.5.tgz#2cbeb52d10e3cdccccf0c11a63a85d830970a987" @@ -9326,7 +9340,7 @@ streamx@^2.15.0: fast-fifo "^1.1.0" queue-tick "^1.0.1" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9370,15 +9384,6 @@ string-width@^4.1.0, string-width@^4.2.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.0" -string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -9432,7 +9437,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9467,13 +9472,6 @@ strip-ansi@^6.0.0: dependencies: ansi-regex "^5.0.0" -strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -10629,7 +10627,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10664,15 +10662,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From b213c86284a421af473651f5ff95ac508e0e1999 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 1 Jul 2024 15:58:39 +0200 Subject: [PATCH 0204/2222] Extracts hot reload utilities, makes placeholderText contribution hot reloadable --- src/vs/base/common/hotReloadHelpers.ts | 30 +++++++++ .../widget/diffEditor/diffEditorViewModel.ts | 3 +- .../widget/diffEditor/diffEditorWidget.ts | 3 +- .../editor/browser/widget/diffEditor/utils.ts | 26 +------- .../multiDiffEditor/multiDiffEditorWidget.ts | 2 +- .../browser/inlineEditsController.ts | 2 +- .../inlineEdits/browser/inlineEditsWidget.ts | 2 +- .../browser/placeholderText.contribution.ts | 63 ++----------------- .../browser/placeholderTextContribution.ts | 62 ++++++++++++++++++ .../common/wrapInReloadableClass.ts} | 6 +- .../accessibilitySignal.contribution.ts | 2 +- 11 files changed, 107 insertions(+), 94 deletions(-) create mode 100644 src/vs/base/common/hotReloadHelpers.ts create mode 100644 src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts rename src/vs/{workbench/contrib/accessibilitySignals/browser/reloadableWorkbenchContribution.ts => platform/observable/common/wrapInReloadableClass.ts} (93%) diff --git a/src/vs/base/common/hotReloadHelpers.ts b/src/vs/base/common/hotReloadHelpers.ts new file mode 100644 index 00000000000..174b1adcbcd --- /dev/null +++ b/src/vs/base/common/hotReloadHelpers.ts @@ -0,0 +1,30 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { isHotReloadEnabled, registerHotReloadHandler } from 'vs/base/common/hotReload'; +import { IReader, observableSignalFromEvent } from 'vs/base/common/observable'; + +export function readHotReloadableExport(value: T, reader: IReader | undefined): T { + observeHotReloadableExports([value], reader); + return value; +} + +export function observeHotReloadableExports(values: any[], reader: IReader | undefined): void { + if (isHotReloadEnabled()) { + const o = observableSignalFromEvent( + 'reload', + event => registerHotReloadHandler(({ oldExports }) => { + if (![...Object.values(oldExports)].some(v => values.includes(v))) { + return undefined; + } + return (_newExports) => { + event(undefined); + return true; + }; + }) + ); + o.read(reader); + } +} diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts index b1520c8821f..67f8505cc28 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorViewModel.ts @@ -8,7 +8,8 @@ import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { IObservable, IReader, ISettableObservable, ITransaction, autorun, autorunWithStore, derived, observableSignal, observableSignalFromEvent, observableValue, transaction, waitForState } from 'vs/base/common/observable'; import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService'; -import { filterWithPrevious, readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils'; +import { filterWithPrevious } from 'vs/editor/browser/widget/diffEditor/utils'; +import { readHotReloadableExport } from 'vs/base/common/hotReloadHelpers'; import { ISerializedLineRange, LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; import { IDocumentDiff } from 'vs/editor/common/diff/documentDiffProvider'; diff --git a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts index 29942806d0c..9c239778773 100644 --- a/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts +++ b/src/vs/editor/browser/widget/diffEditor/diffEditorWidget.ts @@ -26,7 +26,8 @@ import { HideUnchangedRegionsFeature } from 'vs/editor/browser/widget/diffEditor import { MovedBlocksLinesFeature } from 'vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature'; import { OverviewRulerFeature } from 'vs/editor/browser/widget/diffEditor/features/overviewRulerFeature'; import { RevertButtonsFeature } from 'vs/editor/browser/widget/diffEditor/features/revertButtonsFeature'; -import { CSSStyle, ObservableElementSizeObserver, applyStyle, applyViewZones, readHotReloadableExport, translatePosition } from 'vs/editor/browser/widget/diffEditor/utils'; +import { CSSStyle, ObservableElementSizeObserver, applyStyle, applyViewZones, translatePosition } from 'vs/editor/browser/widget/diffEditor/utils'; +import { readHotReloadableExport } from 'vs/base/common/hotReloadHelpers'; import { bindContextKey } from 'vs/platform/observable/common/platformObservableUtils'; import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions'; import { IDimension } from 'vs/editor/common/core/dimension'; diff --git a/src/vs/editor/browser/widget/diffEditor/utils.ts b/src/vs/editor/browser/widget/diffEditor/utils.ts index 99d9580c94a..1e6d5eee428 100644 --- a/src/vs/editor/browser/widget/diffEditor/utils.ts +++ b/src/vs/editor/browser/widget/diffEditor/utils.ts @@ -6,9 +6,8 @@ import { IDimension } from 'vs/base/browser/dom'; import { findLast } from 'vs/base/common/arraysFind'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { isHotReloadEnabled, registerHotReloadHandler } from 'vs/base/common/hotReload'; import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IObservable, IReader, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, autorunWithStore, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; +import { IObservable, ISettableObservable, autorun, autorunHandleChanges, autorunOpts, autorunWithStore, observableValue, transaction } from 'vs/base/common/observable'; import { ElementSizeObserver } from 'vs/editor/browser/config/elementSizeObserver'; import { ICodeEditor, IOverlayWidget, IViewZone } from 'vs/editor/browser/editorBrowser'; import { Position } from 'vs/editor/common/core/position'; @@ -298,29 +297,6 @@ export function applyStyle(domNode: HTMLElement, style: Partial<{ [TKey in keyof }); } -export function readHotReloadableExport(value: T, reader: IReader | undefined): T { - observeHotReloadableExports([value], reader); - return value; -} - -export function observeHotReloadableExports(values: any[], reader: IReader | undefined): void { - if (isHotReloadEnabled()) { - const o = observableSignalFromEvent( - 'reload', - event => registerHotReloadHandler(({ oldExports }) => { - if (![...Object.values(oldExports)].some(v => values.includes(v))) { - return undefined; - } - return (_newExports) => { - event(undefined); - return true; - }; - }) - ); - o.read(reader); - } -} - export function applyViewZones(editor: ICodeEditor, viewZones: IObservable, setIsUpdating?: (isUpdatingViewZones: boolean) => void, zoneIds?: Set): IDisposable { const store = new DisposableStore(); const lastViewZoneIds: string[] = []; diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts index 496b002489b..9d8ace1ea5b 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts @@ -6,7 +6,7 @@ import { Dimension } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { derived, derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable'; -import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils'; +import { readHotReloadableExport } from 'vs/base/common/hotReloadHelpers'; import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditor/model'; import { IMultiDiffEditorViewState, IMultiDiffResourceId, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl'; import { MultiDiffEditorViewModel } from './multiDiffEditorViewModel'; diff --git a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsController.ts b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsController.ts index 5d0afd8ca0e..9055ec56719 100644 --- a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsController.ts +++ b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsController.ts @@ -8,7 +8,7 @@ import { derived, derivedObservableWithCache, IReader, ISettableObservable, obse import { derivedDisposable, derivedWithSetter } from 'vs/base/common/observableInternal/derived'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { observableCodeEditor } from 'vs/editor/browser/observableCodeEditor'; -import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils'; +import { readHotReloadableExport } from 'vs/base/common/hotReloadHelpers'; import { Selection } from 'vs/editor/common/core/selection'; import { ILanguageFeatureDebounceService } from 'vs/editor/common/services/languageFeatureDebounce'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; diff --git a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsWidget.ts b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsWidget.ts index 0a1498915d4..331520cffe0 100644 --- a/src/vs/editor/contrib/inlineEdits/browser/inlineEditsWidget.ts +++ b/src/vs/editor/contrib/inlineEdits/browser/inlineEditsWidget.ts @@ -22,7 +22,7 @@ import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry' import { IModelDeltaDecoration } from 'vs/editor/common/model'; import { TextModel } from 'vs/editor/common/model/textModel'; import { ContextMenuController } from 'vs/editor/contrib/contextmenu/browser/contextmenu'; -import { PlaceholderTextContribution } from 'vs/editor/contrib/placeholderText/browser/placeholderText.contribution'; +import { PlaceholderTextContribution } from '../../placeholderText/browser/placeholderTextContribution'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { MenuId } from 'vs/platform/actions/common/actions'; diff --git a/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts b/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts index 33c069c4485..1c54ca29a2d 100644 --- a/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts +++ b/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts @@ -3,69 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { structuralEquals } from 'vs/base/common/equals'; -import { Disposable } from 'vs/base/common/lifecycle'; -import { derived, derivedOpts } from 'vs/base/common/observable'; import 'vs/css!./placeholderText'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorContributionInstantiation, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { observableCodeEditor } from 'vs/editor/browser/observableCodeEditor'; -import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { ghostTextForeground } from 'vs/editor/common/core/editorColorRegistry'; -import { Range } from 'vs/editor/common/core/range'; -import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { IModelDeltaDecoration, InjectedTextCursorStops } from 'vs/editor/common/model'; import { localize } from 'vs/nls'; import { registerColor } from 'vs/platform/theme/common/colorUtils'; +import { PlaceholderTextContribution } from './placeholderTextContribution'; +import { wrapInReloadableClass } from 'vs/platform/observable/common/wrapInReloadableClass'; -/** - * Use the editor option to set the placeholder text. -*/ -export class PlaceholderTextContribution extends Disposable implements IEditorContribution { - public static get(editor: ICodeEditor): PlaceholderTextContribution { - return editor.getContribution(PlaceholderTextContribution.ID)!; - } +registerEditorContribution(PlaceholderTextContribution.ID, wrapInReloadableClass(() => PlaceholderTextContribution), EditorContributionInstantiation.Eager); - public static readonly ID = 'editor.contrib.placeholderText'; - private readonly _editorObs = observableCodeEditor(this._editor); - - private readonly _placeholderText = this._editorObs.getOption(EditorOption.placeholder); - - private readonly _decorationOptions = derivedOpts<{ placeholder: string } | undefined>({ owner: this, equalsFn: structuralEquals }, reader => { - const p = this._placeholderText.read(reader); - if (!p) { return undefined; } - if (!this._editorObs.valueIsEmpty.read(reader)) { return undefined; } - - return { placeholder: p }; - }); - - private readonly _decorations = derived(this, (reader) => { - const options = this._decorationOptions.read(reader); - if (!options) { return []; } - - return [{ - range: new Range(1, 1, 1, 1), - options: { - description: 'placeholder', - showIfCollapsed: true, - after: { - content: options.placeholder, - cursorStops: InjectedTextCursorStops.None, - inlineClassName: 'placeholder-text' - } - } - }]; - }); - - constructor( - private readonly _editor: ICodeEditor, - ) { - super(); - - this._register(this._editorObs.setDecorations(this._decorations)); - } -} - -registerEditorContribution(PlaceholderTextContribution.ID, PlaceholderTextContribution, EditorContributionInstantiation.Eager); - -registerColor('editor.placeholder.foreground', { dark: ghostTextForeground, light: ghostTextForeground, hcDark: ghostTextForeground, hcLight: ghostTextForeground }, localize('placeholderForeground', 'Foreground color of the placeholder text in the editor.')); +registerColor('editor.placeholder.foreground', ghostTextForeground, localize('placeholderForeground', 'Foreground color of the placeholder text in the editor.')); diff --git a/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts b/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts new file mode 100644 index 00000000000..4a302f18af6 --- /dev/null +++ b/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts @@ -0,0 +1,62 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { structuralEquals } from 'vs/base/common/equals'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { derived, derivedOpts } from 'vs/base/common/observable'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { observableCodeEditor } from 'vs/editor/browser/observableCodeEditor'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Range } from 'vs/editor/common/core/range'; +import { IEditorContribution } from 'vs/editor/common/editorCommon'; +import { IModelDeltaDecoration, InjectedTextCursorStops } from 'vs/editor/common/model'; + +/** + * Use the editor option to set the placeholder text. +*/ +export class PlaceholderTextContribution extends Disposable implements IEditorContribution { + public static get(editor: ICodeEditor): PlaceholderTextContribution { + return editor.getContribution(PlaceholderTextContribution.ID)!; + } + + public static readonly ID = 'editor.contrib.placeholderText'; + private readonly _editorObs = observableCodeEditor(this._editor); + + private readonly _placeholderText = this._editorObs.getOption(EditorOption.placeholder); + + private readonly _decorationOptions = derivedOpts<{ placeholder: string } | undefined>({ owner: this, equalsFn: structuralEquals }, reader => { + const p = this._placeholderText.read(reader); + if (!p) { return undefined; } + if (!this._editorObs.valueIsEmpty.read(reader)) { return undefined; } + + return { placeholder: p }; + }); + + private readonly _decorations = derived(this, (reader) => { + const options = this._decorationOptions.read(reader); + if (!options) { return []; } + + return [{ + range: new Range(1, 1, 1, 1), + options: { + description: 'placeholder', + showIfCollapsed: true, + after: { + content: options.placeholder, + cursorStops: InjectedTextCursorStops.None, + inlineClassName: 'placeholder-text' + } + } + }]; + }); + + constructor( + private readonly _editor: ICodeEditor, + ) { + super(); + + this._register(this._editorObs.setDecorations(this._decorations)); + } +} diff --git a/src/vs/workbench/contrib/accessibilitySignals/browser/reloadableWorkbenchContribution.ts b/src/vs/platform/observable/common/wrapInReloadableClass.ts similarity index 93% rename from src/vs/workbench/contrib/accessibilitySignals/browser/reloadableWorkbenchContribution.ts rename to src/vs/platform/observable/common/wrapInReloadableClass.ts index 43fb32eed29..68124c5f399 100644 --- a/src/vs/workbench/contrib/accessibilitySignals/browser/reloadableWorkbenchContribution.ts +++ b/src/vs/platform/observable/common/wrapInReloadableClass.ts @@ -2,11 +2,10 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - import { isHotReloadEnabled } from 'vs/base/common/hotReload'; +import { readHotReloadableExport } from 'vs/base/common/hotReloadHelpers'; import { IDisposable } from 'vs/base/common/lifecycle'; import { autorunWithStore } from 'vs/base/common/observable'; -import { readHotReloadableExport } from 'vs/editor/browser/widget/diffEditor/utils'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; /** @@ -34,10 +33,9 @@ export function wrapInReloadableClass(getClass: () => (new (...args: any[]) => a } }; } - class BaseClass { constructor( - @IInstantiationService protected readonly instantiationService: IInstantiationService, + @IInstantiationService protected readonly instantiationService: IInstantiationService ) { this.init(); } diff --git a/src/vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignal.contribution.ts b/src/vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignal.contribution.ts index cb298c79733..d25ee91798a 100644 --- a/src/vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignal.contribution.ts +++ b/src/vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignal.contribution.ts @@ -10,7 +10,7 @@ import { registerWorkbenchContribution2, WorkbenchPhase } from 'vs/workbench/com import { AccessibilitySignalLineDebuggerContribution } from 'vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignalDebuggerContribution'; import { ShowAccessibilityAnnouncementHelp, ShowSignalSoundHelp } from 'vs/workbench/contrib/accessibilitySignals/browser/commands'; import { EditorTextPropertySignalsContribution } from 'vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution'; -import { wrapInReloadableClass } from 'vs/workbench/contrib/accessibilitySignals/browser/reloadableWorkbenchContribution'; +import { wrapInReloadableClass } from 'vs/platform/observable/common/wrapInReloadableClass'; registerSingleton(IAccessibilitySignalService, AccessibilitySignalService, InstantiationType.Delayed); From 473e3485913833595efe1360d50e24b912a38435 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 1 Jul 2024 16:31:26 +0200 Subject: [PATCH 0205/2222] Improve compact badge display (#219552) Show up to 3 characters in compact badge --- src/vs/workbench/browser/parts/compositeBarActions.ts | 9 ++++----- .../workbench/browser/parts/media/paneCompositePart.css | 6 ++++++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index de10721d07c..24b1b97b7dd 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -319,11 +319,7 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { else if (badge instanceof NumberBadge) { if (badge.number) { let number = badge.number.toString(); - if (this.options.compact) { - if (badge.number > 99) { - number = ''; - } - } else if (badge.number > 999) { + if (badge.number > 999) { const noOfThousands = badge.number / 1000; const floor = Math.floor(noOfThousands); if (noOfThousands > floor) { @@ -332,6 +328,9 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { number = `${noOfThousands}K`; } } + if (this.options.compact && number.length >= 3) { + classes.push('compact-content'); + } this.badgeContent.textContent = number; show(this.badge); } diff --git a/src/vs/workbench/browser/parts/media/paneCompositePart.css b/src/vs/workbench/browser/parts/media/paneCompositePart.css index 52baa5324f7..174997a925d 100644 --- a/src/vs/workbench/browser/parts/media/paneCompositePart.css +++ b/src/vs/workbench/browser/parts/media/paneCompositePart.css @@ -237,6 +237,12 @@ text-align: center; } +.monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .badge.compact.compact-content .badge-content, +.monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .badge.compact.compact-content .badge-content { + font-size: 8px; + padding: 0 3px; +} + .monaco-workbench .pane-composite-part > .title > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .badge.compact.progress-badge .badge-content::before, .monaco-workbench .pane-composite-part > .header-or-footer > .composite-bar-container > .composite-bar > .monaco-action-bar .action-item.icon .badge.compact.progress-badge .badge-content::before { mask-size: 11px; From 26016181625e2fccc2e95fecff6977b0dbd88eeb Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 1 Jul 2024 16:37:47 +0200 Subject: [PATCH 0206/2222] fix #219336 (#219553) fix #219336 (#219337) * fix #219336 * fix owner --- .../contrib/telemetry/browser/telemetry.contribution.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts index 755566d5435..65b2301495f 100644 --- a/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts +++ b/src/vs/workbench/contrib/telemetry/browser/telemetry.contribution.ts @@ -431,11 +431,11 @@ class ConfigurationTelemetryContribution extends Disposable implements IWorkbenc ? 'default' : 'custom'; this.telemetryService.publicLog2('window.systemColorTheme', { settingValue, source }); + }>('window.newWindowProfile', { settingValue, source }); return; } @@ -445,7 +445,7 @@ class ConfigurationTelemetryContribution extends Disposable implements IWorkbenc comment: 'This is used to know if extensions are getting auto restarted or not'; settingValue: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'value of the setting' }; source: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'source of the setting' }; - }>('window.systemColorTheme', { settingValue: this.getValueToReport(key, target), source }); + }>('extensions.autoRestart', { settingValue: this.getValueToReport(key, target), source }); return; } } From db7e8f1f1d007bcde3c0d63285e073a63abf0d52 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 1 Jul 2024 17:32:55 +0200 Subject: [PATCH 0207/2222] Disables wrapInReloadableClass (#219555) --- src/vs/platform/observable/common/wrapInReloadableClass.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/vs/platform/observable/common/wrapInReloadableClass.ts b/src/vs/platform/observable/common/wrapInReloadableClass.ts index 68124c5f399..4b5e2e2d3c4 100644 --- a/src/vs/platform/observable/common/wrapInReloadableClass.ts +++ b/src/vs/platform/observable/common/wrapInReloadableClass.ts @@ -14,6 +14,11 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti * When the original class changes, the instance is re-created. */ export function wrapInReloadableClass(getClass: () => (new (...args: any[]) => any)): (new (...args: any[]) => any) { + // Disables this function as it does not work. + if (1 === 1) { + // TODO@hediet fix this asap. + return getClass(); + } if (!isHotReloadEnabled()) { return getClass(); } From 327279994e030d1a7da815c1d6d0e9c45bf673cc Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 1 Jul 2024 18:30:08 +0200 Subject: [PATCH 0208/2222] debt - update `path.ts` to node.js `20.x` (#219560) first cut update --- src/vs/base/common/path.ts | 44 ++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 83140948bba..6f40f7d0356 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -4,7 +4,9 @@ *--------------------------------------------------------------------------------------------*/ // NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace -// Copied from: https://github.com/nodejs/node/blob/v16.14.2/lib/path.js +// Copied from: https://github.com/nodejs/node/commits/v20.9.0/lib/path.js +// Excluding: the change that adds primordials +// (https://github.com/nodejs/node/commit/187a862d221dec42fa9a5c4214e7034d9092792f and others) /** * Copyright Joyent, Inc. and other Node contributors. @@ -159,11 +161,15 @@ function normalizeString(path: string, allowAboveRoot: boolean, separator: strin return res; } +function formatExt(ext: string): string { + return ext ? `${ext[0] === '.' ? '' : '.'}${ext}` : ''; +} + function _format(sep: string, pathObject: ParsedPath) { validateObject(pathObject, 'pathObject'); const dir = pathObject.dir || pathObject.root; const base = pathObject.base || - `${pathObject.name || ''}${pathObject.ext || ''}`; + `${pathObject.name || ''}${formatExt(pathObject.ext)}`; if (!dir) { return base; } @@ -185,7 +191,7 @@ export interface IPath { resolve(...pathSegments: string[]): string; relative(from: string, to: string): string; dirname(path: string): string; - basename(path: string, ext?: string): string; + basename(path: string, suffix?: string): string; extname(path: string): string; format(pathObject: ParsedPath): string; parse(path: string): ParsedPath; @@ -207,7 +213,7 @@ export const win32: IPath = { let path; if (i >= 0) { path = pathSegments[i]; - validateString(path, 'path'); + validateString(path, `paths[${i}]`); // Skip empty entries if (path.length === 0) { @@ -757,9 +763,9 @@ export const win32: IPath = { return path.slice(0, end); }, - basename(path: string, ext?: string): string { - if (ext !== undefined) { - validateString(ext, 'ext'); + basename(path: string, suffix?: string): string { + if (suffix !== undefined) { + validateString(suffix, 'suffix'); } validateString(path, 'path'); let start = 0; @@ -776,11 +782,11 @@ export const win32: IPath = { start = 2; } - if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { - if (ext === path) { + if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) { + if (suffix === path) { return ''; } - let extIdx = ext.length - 1; + let extIdx = suffix.length - 1; let firstNonSlashEnd = -1; for (i = path.length - 1; i >= start; --i) { const code = path.charCodeAt(i); @@ -800,7 +806,7 @@ export const win32: IPath = { } if (extIdx >= 0) { // Try to match the explicit extension - if (code === ext.charCodeAt(extIdx)) { + if (code === suffix.charCodeAt(extIdx)) { if (--extIdx === -1) { // We matched the extension, so mark this as the end of our path // component @@ -1095,7 +1101,7 @@ export const posix: IPath = { for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { const path = i >= 0 ? pathSegments[i] : posixCwd(); - validateString(path, 'path'); + validateString(path, `paths[${i}]`); // Skip empty entries if (path.length === 0) { @@ -1280,9 +1286,9 @@ export const posix: IPath = { return path.slice(0, end); }, - basename(path: string, ext?: string): string { - if (ext !== undefined) { - validateString(ext, 'ext'); + basename(path: string, suffix?: string): string { + if (suffix !== undefined) { + validateString(suffix, 'ext'); } validateString(path, 'path'); @@ -1291,11 +1297,11 @@ export const posix: IPath = { let matchedSlash = true; let i; - if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { - if (ext === path) { + if (suffix !== undefined && suffix.length > 0 && suffix.length <= path.length) { + if (suffix === path) { return ''; } - let extIdx = ext.length - 1; + let extIdx = suffix.length - 1; let firstNonSlashEnd = -1; for (i = path.length - 1; i >= 0; --i) { const code = path.charCodeAt(i); @@ -1315,7 +1321,7 @@ export const posix: IPath = { } if (extIdx >= 0) { // Try to match the explicit extension - if (code === ext.charCodeAt(extIdx)) { + if (code === suffix.charCodeAt(extIdx)) { if (--extIdx === -1) { // We matched the extension, so mark this as the end of our path // component From cf3e2a4e81159d4da0570cb37be3c9e7e662780b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 1 Jul 2024 19:12:02 +0200 Subject: [PATCH 0209/2222] Fixes #211403 --- .../browser/placeholderText.css | 13 +++- .../browser/placeholderTextContribution.ts | 64 ++++++++++++------- 2 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/vs/editor/contrib/placeholderText/browser/placeholderText.css b/src/vs/editor/contrib/placeholderText/browser/placeholderText.css index 3912e17fc2f..043b6f15632 100644 --- a/src/vs/editor/contrib/placeholderText/browser/placeholderText.css +++ b/src/vs/editor/contrib/placeholderText/browser/placeholderText.css @@ -5,8 +5,15 @@ .monaco-editor { --vscode-editor-placeholder-foreground: var(--vscode-editorGhostText-foreground); -} -.monaco-editor .placeholder-text { - color: var(--vscode-editor-placeholder-foreground); + .editorPlaceholder { + top: 0px; + position: absolute; + overflow: hidden; + text-overflow: ellipsis; + text-wrap: nowrap; + pointer-events: none; + + color: var(--vscode-editor-placeholder-foreground); + } } diff --git a/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts b/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts index 4a302f18af6..8ac0fc821c7 100644 --- a/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts +++ b/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts @@ -3,15 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { h } from 'vs/base/browser/dom'; import { structuralEquals } from 'vs/base/common/equals'; import { Disposable } from 'vs/base/common/lifecycle'; -import { derived, derivedOpts } from 'vs/base/common/observable'; +import { autorun, constObservable, derivedObservableWithCache, derivedOpts, IObservable, IReader } from 'vs/base/common/observable'; +import { DebugOwner } from 'vs/base/common/observableInternal/debugName'; +import { derivedWithStore } from 'vs/base/common/observableInternal/derived'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { observableCodeEditor } from 'vs/editor/browser/observableCodeEditor'; import { EditorOption } from 'vs/editor/common/config/editorOptions'; -import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution } from 'vs/editor/common/editorCommon'; -import { IModelDeltaDecoration, InjectedTextCursorStops } from 'vs/editor/common/model'; /** * Use the editor option to set the placeholder text. @@ -26,30 +27,42 @@ export class PlaceholderTextContribution extends Disposable implements IEditorCo private readonly _placeholderText = this._editorObs.getOption(EditorOption.placeholder); - private readonly _decorationOptions = derivedOpts<{ placeholder: string } | undefined>({ owner: this, equalsFn: structuralEquals }, reader => { + private readonly _state = derivedOpts<{ placeholder: string } | undefined>({ owner: this, equalsFn: structuralEquals }, reader => { const p = this._placeholderText.read(reader); if (!p) { return undefined; } if (!this._editorObs.valueIsEmpty.read(reader)) { return undefined; } - return { placeholder: p }; }); - private readonly _decorations = derived(this, (reader) => { - const options = this._decorationOptions.read(reader); - if (!options) { return []; } - - return [{ - range: new Range(1, 1, 1, 1), - options: { - description: 'placeholder', - showIfCollapsed: true, - after: { - content: options.placeholder, - cursorStops: InjectedTextCursorStops.None, - inlineClassName: 'placeholder-text' - } - } - }]; + private readonly _shouldViewBeAlive = isOrWasTrue(this, reader => this._state.read(reader)?.placeholder !== undefined); + + private readonly _view = derivedWithStore((reader, store) => { + if (!this._shouldViewBeAlive.read(reader)) { return; } + + const element = h('div.editorPlaceholder'); + element.root.style.top = `1px`; + + store.add(autorun(reader => { + const data = this._state.read(reader); + const shouldBeVisibile = data?.placeholder !== undefined; + element.root.style.display = shouldBeVisibile ? 'block' : 'none'; + element.root.innerText = data?.placeholder ?? ''; + })); + store.add(autorun(reader => { + const info = this._editorObs.layoutInfo.read(reader); + element.root.style.left = `${info.contentLeft}px`; + element.root.style.width = (info.contentWidth - info.verticalScrollbarWidth) + 'px'; + })); + store.add(autorun(reader => { + element.root.style.fontFamily = this._editorObs.getOption(EditorOption.fontFamily).read(reader); + element.root.style.fontSize = this._editorObs.getOption(EditorOption.fontSize).read(reader) + 'px'; + })); + store.add(this._editorObs.createOverlayWidget({ + allowEditorOverflow: false, + minContentWidthInPx: constObservable(0), + position: constObservable(null), + domNode: element.root, + })); }); constructor( @@ -57,6 +70,13 @@ export class PlaceholderTextContribution extends Disposable implements IEditorCo ) { super(); - this._register(this._editorObs.setDecorations(this._decorations)); + this._view.recomputeInitiallyAndOnChange(this._store); } } + +function isOrWasTrue(owner: DebugOwner, fn: (reader: IReader) => boolean): IObservable { + return derivedObservableWithCache(owner, (reader, lastValue) => { + if (lastValue === true) { return true; } + return fn(reader); + }); +} From 013b1861dc7a3ffc6bc6ed7fd05c5b3c88d53a06 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 1 Jul 2024 19:22:29 +0200 Subject: [PATCH 0210/2222] Fixes potential leak --- .../browser/editorTextPropertySignalsContribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts b/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts index 4fa9f2d85c9..f5dddf8ada4 100644 --- a/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts +++ b/src/vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts @@ -73,7 +73,7 @@ export class EditorTextPropertySignalsContribution extends Disposable implements let lastLine = -1; const ignoredLineSignalsForCurrentLine = new Set(); - const timeouts = new DisposableStore(); + const timeouts = store.add(new DisposableStore()); const propertySources = this._textProperties.map(p => ({ source: p.createSource(editor, editorModel), property: p })); From 01a77403bfbfd26ad22a56f03d9333501961835a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 1 Jul 2024 19:27:00 +0200 Subject: [PATCH 0211/2222] Fixes wrapInReloadableClass* functions. --- .../browser/placeholderText.contribution.ts | 4 +- .../common/wrapInReloadableClass.ts | 57 ++++++++++++------- .../accessibilitySignal.contribution.ts | 4 +- 3 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts b/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts index 1c54ca29a2d..e9994c4b01c 100644 --- a/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts +++ b/src/vs/editor/contrib/placeholderText/browser/placeholderText.contribution.ts @@ -9,8 +9,8 @@ import { ghostTextForeground } from 'vs/editor/common/core/editorColorRegistry'; import { localize } from 'vs/nls'; import { registerColor } from 'vs/platform/theme/common/colorUtils'; import { PlaceholderTextContribution } from './placeholderTextContribution'; -import { wrapInReloadableClass } from 'vs/platform/observable/common/wrapInReloadableClass'; +import { wrapInReloadableClass1 } from 'vs/platform/observable/common/wrapInReloadableClass'; -registerEditorContribution(PlaceholderTextContribution.ID, wrapInReloadableClass(() => PlaceholderTextContribution), EditorContributionInstantiation.Eager); +registerEditorContribution(PlaceholderTextContribution.ID, wrapInReloadableClass1(() => PlaceholderTextContribution), EditorContributionInstantiation.Eager); registerColor('editor.placeholder.foreground', ghostTextForeground, localize('placeholderForeground', 'Foreground color of the placeholder text in the editor.')); diff --git a/src/vs/platform/observable/common/wrapInReloadableClass.ts b/src/vs/platform/observable/common/wrapInReloadableClass.ts index 4b5e2e2d3c4..bb05ee95bfb 100644 --- a/src/vs/platform/observable/common/wrapInReloadableClass.ts +++ b/src/vs/platform/observable/common/wrapInReloadableClass.ts @@ -6,44 +6,57 @@ import { isHotReloadEnabled } from 'vs/base/common/hotReload'; import { readHotReloadableExport } from 'vs/base/common/hotReloadHelpers'; import { IDisposable } from 'vs/base/common/lifecycle'; import { autorunWithStore } from 'vs/base/common/observable'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { BrandedService, GetLeadingNonServiceArgs, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; /** * Wrap a class in a reloadable wrapper. * When the wrapper is created, the original class is created. * When the original class changes, the instance is re-created. */ -export function wrapInReloadableClass(getClass: () => (new (...args: any[]) => any)): (new (...args: any[]) => any) { - // Disables this function as it does not work. - if (1 === 1) { - // TODO@hediet fix this asap. - return getClass(); - } - if (!isHotReloadEnabled()) { - return getClass(); - } - - return class ReloadableWrapper extends BaseClass { +export function wrapInReloadableClass0(getClass: () => Result): Result> { + return !isHotReloadEnabled() ? getClass() : createWrapper(getClass, BaseClass0); +} + +type Result = new (...args: TArgs) => IDisposable; + +class BaseClass { + constructor( + protected readonly instantiationService: IInstantiationService, + ) { } + + init(...params: any[]): void { } +} + +function createWrapper(getClass: () => any, B: new (...args: T) => BaseClass) { + return (class ReloadableWrapper extends B { private _autorun: IDisposable | undefined = undefined; - override init() { + override init(...params: any[]) { this._autorun = autorunWithStore((reader, store) => { const clazz = readHotReloadableExport(getClass(), reader); - store.add(this.instantiationService.createInstance(clazz)); + store.add(this.instantiationService.createInstance(clazz as any, ...params) as IDisposable); }); } dispose(): void { this._autorun?.dispose(); } - }; + }) as any; +} + +class BaseClass0 extends BaseClass { + constructor(@IInstantiationService i: IInstantiationService) { super(i); this.init(); } +} + +/** + * Wrap a class in a reloadable wrapper. + * When the wrapper is created, the original class is created. + * When the original class changes, the instance is re-created. +*/ +export function wrapInReloadableClass1(getClass: () => Result): Result> { + return !isHotReloadEnabled() ? getClass() as any : createWrapper(getClass, BaseClass1); } -class BaseClass { - constructor( - @IInstantiationService protected readonly instantiationService: IInstantiationService - ) { - this.init(); - } - init(): void { } +class BaseClass1 extends BaseClass { + constructor(param1: any, @IInstantiationService i: IInstantiationService,) { super(i); this.init(param1); } } diff --git a/src/vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignal.contribution.ts b/src/vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignal.contribution.ts index d25ee91798a..deb77085819 100644 --- a/src/vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignal.contribution.ts +++ b/src/vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignal.contribution.ts @@ -10,11 +10,11 @@ import { registerWorkbenchContribution2, WorkbenchPhase } from 'vs/workbench/com import { AccessibilitySignalLineDebuggerContribution } from 'vs/workbench/contrib/accessibilitySignals/browser/accessibilitySignalDebuggerContribution'; import { ShowAccessibilityAnnouncementHelp, ShowSignalSoundHelp } from 'vs/workbench/contrib/accessibilitySignals/browser/commands'; import { EditorTextPropertySignalsContribution } from 'vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution'; -import { wrapInReloadableClass } from 'vs/platform/observable/common/wrapInReloadableClass'; +import { wrapInReloadableClass0 } from 'vs/platform/observable/common/wrapInReloadableClass'; registerSingleton(IAccessibilitySignalService, AccessibilitySignalService, InstantiationType.Delayed); -registerWorkbenchContribution2('EditorTextPropertySignalsContribution', wrapInReloadableClass(() => EditorTextPropertySignalsContribution), WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2('EditorTextPropertySignalsContribution', wrapInReloadableClass0(() => EditorTextPropertySignalsContribution), WorkbenchPhase.AfterRestored); registerWorkbenchContribution2('AccessibilitySignalLineDebuggerContribution', AccessibilitySignalLineDebuggerContribution, WorkbenchPhase.AfterRestored); registerAction2(ShowSignalSoundHelp); From 3652f9c8b3a2ff91cc238fe4e31469a175a5b71c Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 1 Jul 2024 19:29:08 +0200 Subject: [PATCH 0212/2222] Repairs hot reload for monaco editor playground. --- scripts/playground-server.ts | 212 ++++++++++++++++++++++++++++------- 1 file changed, 169 insertions(+), 43 deletions(-) diff --git a/scripts/playground-server.ts b/scripts/playground-server.ts index 1c2074ee191..1e8479a2d1f 100644 --- a/scripts/playground-server.ts +++ b/scripts/playground-server.ts @@ -223,10 +223,10 @@ class DirWatcher { function handleGetFileChangesRequest(watcher: DirWatcher, fileServer: FileServer, moduleIdMapper: SimpleModuleIdPathMapper): ChainableRequestHandler { return async (req, res) => { res.writeHead(200, { 'Content-Type': 'text/plain' }); - const d = watcher.onDidChange(fsPath => { + const d = watcher.onDidChange((fsPath, newContent) => { const path = fileServer.filePathToUrlPath(fsPath); if (path) { - res.write(JSON.stringify({ changedPath: path, moduleId: moduleIdMapper.getModuleId(fsPath) }) + '\n'); + res.write(JSON.stringify({ changedPath: path, moduleId: moduleIdMapper.getModuleId(fsPath), newContent }) + '\n'); } }); res.on('close', () => d.dispose()); @@ -262,49 +262,18 @@ function makeLoaderJsHotReloadable(loaderJsCode: string, fileChangesUrl: URL): s buffer += new TextDecoder().decode(value); const lines = buffer.split('\n'); buffer = lines.pop()!; + + const changes: { relativePath: string; config: HotReloadConfig | undefined; path: string; newContent: string }[] = []; + for (const line of lines) { const data = JSON.parse(line); - let handled = false; - if (data.changedPath.endsWith('.css')) { - if (typeof document !== 'undefined') { - console.log('css changed', data.changedPath); - const styleSheet = [...document.querySelectorAll(`link[rel='stylesheet']`)].find((l: any) => new URL(l.href, document.location.href).pathname.endsWith(data.changedPath)) as any; - if (styleSheet) { - styleSheet.href = styleSheet.href.replace(/\?.*/, '') + '?' + Date.now(); - } - } - handled = true; - } else if (data.changedPath.endsWith('.js') && data.moduleId) { - console.log('js changed', data.changedPath); - const moduleId = ___globalModuleManager._moduleIdProvider.getModuleId(data.moduleId); - if (___globalModuleManager._modules2[moduleId]) { - const srcUrl = ___globalModuleManager._config.moduleIdToPaths(data.moduleId); - const newSrc = await (await fetch(srcUrl)).text(); - (new Function('define', newSrc))(function (deps, callback) { // CodeQL [SM01632] This code is only executed during development (as part of the dev-only playground-server). It is required for the hot-reload functionality. - const oldModule = ___globalModuleManager._modules2[moduleId]; - delete ___globalModuleManager._modules2[moduleId]; - - ___globalModuleManager.defineModule(data.moduleId, deps, callback); - const newModule = ___globalModuleManager._modules2[moduleId]; - const oldExports = { ...oldModule.exports }; - - Object.assign(oldModule.exports, newModule.exports); - newModule.exports = oldModule.exports; - - handled = true; - - for (const cb of [...globalThis.$hotReload_deprecateExports]) { - cb(oldExports, newModule.exports); - } - - if (handled) { - console.log('hot reloaded', data.moduleId); - } - }); - } - } - - if (!handled) { reloadFn(); } + const relativePath = data.changedPath.replace(/\\/g, '/').split('/out/')[1]; + changes.push({ config: {}, path: data.changedPath, relativePath, newContent: data.newContent }); + } + + const result = handleChanges(changes, 'playground-server'); + if (result.reloadFailedJsFiles.length > 0) { + reloadFn(); } } }).catch(err => { @@ -312,6 +281,163 @@ function makeLoaderJsHotReloadable(loaderJsCode: string, fileChangesUrl: URL): s setTimeout(() => $watchChanges(fileChangesUrl), 1000); }); + + function handleChanges(changes: { + relativePath: string; + config: HotReloadConfig | undefined; + path: string; + newContent: string; + }[], debugSessionName: string) { + // This function is stringified and injected into the debuggee. + + const hotReloadData: { count: number; originalWindowTitle: any; timeout: any; shouldReload: boolean } = globalThis.$hotReloadData || (globalThis.$hotReloadData = { count: 0, messageHideTimeout: undefined, shouldReload: false }); + + const reloadFailedJsFiles: { relativePath: string; path: string }[] = []; + + for (const change of changes) { + handleChange(change.relativePath, change.path, change.newContent, change.config); + } + + return { reloadFailedJsFiles }; + + function handleChange(relativePath: string, path: string, newSrc: string, config: any) { + if (relativePath.endsWith('.css')) { + handleCssChange(relativePath); + } else if (relativePath.endsWith('.js')) { + handleJsChange(relativePath, path, newSrc, config); + } + } + + function handleCssChange(relativePath: string) { + if (typeof document === 'undefined') { + return; + } + + const styleSheet = (([...document.querySelectorAll(`link[rel='stylesheet']`)] as HTMLLinkElement[])) + .find(l => new URL(l.href, document.location.href).pathname.endsWith(relativePath)); + if (styleSheet) { + setMessage(`reload ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + console.log(debugSessionName, 'css reloaded', relativePath); + styleSheet.href = styleSheet.href.replace(/\?.*/, '') + '?' + Date.now(); + } else { + setMessage(`could not reload ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + console.log(debugSessionName, 'ignoring css change, as stylesheet is not loaded', relativePath); + } + } + + + function handleJsChange(relativePath: string, path: string, newSrc: string, config: any) { + const moduleIdStr = trimEnd(relativePath, '.js'); + + const requireFn: any = globalThis.require; + const moduleManager = (requireFn as any).moduleManager; + if (!moduleManager) { + console.log(debugSessionName, 'ignoring js change, as moduleManager is not available', relativePath); + return; + } + + const moduleId = moduleManager._moduleIdProvider.getModuleId(moduleIdStr); + const oldModule = moduleManager._modules2[moduleId]; + + if (!oldModule) { + console.log(debugSessionName, 'ignoring js change, as module is not loaded', relativePath); + return; + } + + // Check if we can reload + const g: GlobalThisAddition = globalThis as any; + + // A frozen copy of the previous exports + const oldExports = Object.freeze({ ...oldModule.exports }); + const reloadFn = g.$hotReload_applyNewExports?.({ oldExports, newSrc, config }); + + if (!reloadFn) { + console.log(debugSessionName, 'ignoring js change, as module does not support hot-reload', relativePath); + hotReloadData.shouldReload = true; + + reloadFailedJsFiles.push({ relativePath, path }); + + setMessage(`hot reload not supported for ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + return; + } + + // Eval maintains source maps + function newScript(/* this parameter is used by newSrc */ define) { + // eslint-disable-next-line no-eval + eval(newSrc); // CodeQL [SM01632] This code is only executed during development. It is required for the hot-reload functionality. + } + + newScript(/* define */ function (deps, callback) { + // Evaluating the new code was successful. + + // Redefine the module + delete moduleManager._modules2[moduleId]; + moduleManager.defineModule(moduleIdStr, deps, callback); + const newModule = moduleManager._modules2[moduleId]; + + + // Patch the exports of the old module, so that modules using the old module get the new exports + Object.assign(oldModule.exports, newModule.exports); + // We override the exports so that future reloads still patch the initial exports. + newModule.exports = oldModule.exports; + + const successful = reloadFn(newModule.exports); + if (!successful) { + hotReloadData.shouldReload = true; + setMessage(`hot reload failed ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + console.log(debugSessionName, 'hot reload was not successful', relativePath); + return; + } + + console.log(debugSessionName, 'hot reloaded', moduleIdStr); + setMessage(`successfully reloaded ${formatPath(relativePath)} - ${new Date().toLocaleTimeString()}`); + }); + } + + function setMessage(message: string) { + const domElem = (document.querySelector('.titlebar-center .window-title')) as HTMLDivElement | undefined; + if (!domElem) { return; } + if (!hotReloadData.timeout) { + hotReloadData.originalWindowTitle = domElem.innerText; + } else { + clearTimeout(hotReloadData.timeout); + } + if (hotReloadData.shouldReload) { + message += ' (manual reload required)'; + } + + domElem.innerText = message; + hotReloadData.timeout = setTimeout(() => { + hotReloadData.timeout = undefined; + // If wanted, we can restore the previous title message + // domElem.replaceChildren(hotReloadData.originalWindowTitle); + }, 5000); + } + + function formatPath(path: string): string { + const parts = path.split('/'); + parts.reverse(); + let result = parts[0]; + parts.shift(); + for (const p of parts) { + if (result.length + p.length > 40) { + break; + } + result = p + '/' + result; + if (result.length > 20) { + break; + } + } + return result; + } + + function trimEnd(str, suffix) { + if (str.endsWith(suffix)) { + return str.substring(0, str.length - suffix.length); + } + return str; + } + } } const additionalJsCode = ` From a02f3c923dea8a9c37854e58ac88ac982f1bc451 Mon Sep 17 00:00:00 2001 From: Robo Date: Tue, 2 Jul 2024 02:59:38 +0900 Subject: [PATCH 0213/2222] cli: restrict pkg config search directory to sysroot (#219572) --- build/azure-pipelines/cli/cli-compile.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/build/azure-pipelines/cli/cli-compile.yml b/build/azure-pipelines/cli/cli-compile.yml index a70f607ad69..e77ba78a999 100644 --- a/build/azure-pipelines/cli/cli-compile.yml +++ b/build/azure-pipelines/cli/cli-compile.yml @@ -49,16 +49,22 @@ steps: export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER="$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc" export CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" export CC_aarch64_unknown_linux_gnu="$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc --sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" + export PKG_CONFIG_LIBDIR_aarch64_unknown_linux_gnu="$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu/pkgconfig:$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/share/pkgconfig" + export PKG_CONFIG_SYSROOT_DIR_aarch64_unknown_linux_gnu="$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" export OBJDUMP="$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/bin/objdump" elif [ "$SYSROOT_ARCH" == "amd64" ]; then export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER="$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc" export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUSTFLAGS="-C link-arg=--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -C link-arg=-L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu" export CC_x86_64_unknown_linux_gnu="$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" + export PKG_CONFIG_LIBDIR_x86_64_unknown_linux_gnu="$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu/pkgconfig:$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/share/pkgconfig" + export PKG_CONFIG_SYSROOT_DIR_x86_64_unknown_linux_gnu="$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" export OBJDUMP="$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/bin/objdump" elif [ "$SYSROOT_ARCH" == "armhf" ]; then export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER="$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-gcc" export CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUSTFLAGS="-C link-arg=--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot" export CC_armv7_unknown_linux_gnueabihf="$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-gcc --sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot" + export PKG_CONFIG_LIBDIR_armv7_unknown_linux_gnueabihf="$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/usr/lib/arm-rpi-linux-gnueabihf/pkgconfig:$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/usr/share/pkgconfig" + export PKG_CONFIG_SYSROOT_DIR_armv7_unknown_linux_gnueabihf="$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot" export OBJDUMP="$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/bin/objdump" fi fi @@ -79,7 +85,7 @@ steps: done < <("$OBJDUMP" -T "$PWD/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code") if [[ "$glibc_version" != "2.17" ]]; then echo "Error: binary has dependency on GLIBC > 2.17, found $glibc_version" - #exit 1 + exit 1 fi fi displayName: Compile ${{ parameters.VSCODE_CLI_TARGET }} From 0db947b90c90ebaca89cc8f587f25b068c7a2d09 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 1 Jul 2024 15:52:12 -0700 Subject: [PATCH 0214/2222] Small chat renderer cleanup (#219597) --- .../contrib/chat/browser/chatListRenderer.ts | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 1424cf276b1..34640bcba87 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -567,43 +567,44 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer part === null); + return true; + } - if (isFullyRendered && element.isComplete) { + let isFullyRendered = false; + this.traceLayout('doNextProgressiveRender', `START progressive render, index=${index}, renderData=${JSON.stringify(element.renderData)}`); + const contentForThisTurn = this.getNextProgressiveRenderContent(element); + const partsToRender = this.diff(templateData.renderedParts ?? [], contentForThisTurn, element); + isFullyRendered = partsToRender.every(part => part === null); + + if (isFullyRendered) { + if (element.isComplete) { // Response is done and content is rendered, so do a normal render this.traceLayout('doNextProgressiveRender', `END progressive render, index=${index} and clearing renderData, response is complete`); element.renderData = undefined; this.basicRenderElement(element, index, templateData); - // TODO return here - } else if (!isFullyRendered) { - this.traceLayout('doNextProgressiveRender', `doing progressive render, ${partsToRender.length} parts to render`); - this.renderChatContentDiff(partsToRender, contentForThisTurn, element, templateData); - } else { - // Nothing new to render, not done, keep waiting - this.traceLayout('doNextProgressiveRender', 'caught up with the stream- no new content to render'); - return false; + return true; } + + // Nothing new to render, not done, keep waiting + this.traceLayout('doNextProgressiveRender', 'caught up with the stream- no new content to render'); + return false; } - // Some render happened - update the height + // Do an actual progressive render + this.traceLayout('doNextProgressiveRender', `doing progressive render, ${partsToRender.length} parts to render`); + this.renderChatContentDiff(partsToRender, contentForThisTurn, element, templateData); + const height = templateData.rowContainer.offsetHeight; element.currentRenderedHeight = height; if (!isInRenderElement) { this._onDidChangeItemHeight.fire({ element, height: templateData.rowContainer.offsetHeight }); } - return isFullyRendered; + return false; } private renderChatContentDiff(partsToRender: ReadonlyArray, contentForThisTurn: ReadonlyArray, element: IChatResponseViewModel, templateData: IChatListItemTemplate): void { From 42b3bac02e37836393b4c4b46fcc91aa03e02aa8 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Mon, 1 Jul 2024 16:01:01 -0700 Subject: [PATCH 0215/2222] Dispose some child InstantiationServices (#219607) #212879 --- src/vs/workbench/contrib/chat/browser/chatEditor.ts | 2 +- src/vs/workbench/contrib/chat/browser/chatInputPart.ts | 2 +- src/vs/workbench/contrib/chat/browser/chatListRenderer.ts | 4 ++-- src/vs/workbench/contrib/chat/browser/chatViewPane.ts | 2 +- src/vs/workbench/contrib/chat/browser/chatWidget.ts | 2 +- src/vs/workbench/contrib/chat/browser/codeBlockPart.ts | 8 ++++---- .../preferences/test/browser/preferencesService.test.ts | 2 +- 7 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatEditor.ts b/src/vs/workbench/contrib/chat/browser/chatEditor.ts index 83d025ae706..94fe53297a8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditor.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditor.ts @@ -58,7 +58,7 @@ export class ChatEditor extends EditorPane { protected override createEditor(parent: HTMLElement): void { this._scopedContextKeyService = this._register(this.contextKeyService.createScoped(parent)); - const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); + const scopedInstantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService]))); this.widget = this._register( scopedInstantiationService.createInstance( diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index abf61519b38..9fcc9fb8e50 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -359,7 +359,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const inputScopedContextKeyService = this._register(this.contextKeyService.createScoped(inputContainer)); CONTEXT_IN_CHAT_INPUT.bindTo(inputScopedContextKeyService).set(true); - const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, inputScopedContextKeyService])); + const scopedInstantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, inputScopedContextKeyService]))); const { historyNavigationBackwardsEnablement, historyNavigationForwardsEnablement } = this._register(registerAndCreateHistoryNavigationContext(inputScopedContextKeyService, this)); this.historyNavigationBackwardsEnablement = historyNavigationBackwardsEnablement; diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 34640bcba87..fb4eae44143 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -274,7 +274,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { if (Array.isArray(item)) { - const scopedInstaService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService])); + const scopedInstaService = templateData.elementDisposables.add(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, templateData.contextKeyService]))); templateData.elementDisposables.add( scopedInstaService.createInstance, ChatFollowups>( ChatFollowups, diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index 83a9ef05a8f..80022029e40 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -133,7 +133,7 @@ export class ChatViewPane extends ViewPane { try { super.renderBody(parent); - const scopedInstantiationService = this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService])); + const scopedInstantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService]))); const locationBasedColors = this.getLocationBasedColors(); this._widget = this._register(scopedInstantiationService.createInstance( ChatWidget, diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index 231d671577c..21ad0513b63 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -458,7 +458,7 @@ export class ChatWidget extends Disposable implements IChatWidget { } private createList(listContainer: HTMLElement, options: IChatListItemRendererOptions): void { - const scopedInstantiationService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService]))); + const scopedInstantiationService = this._register(this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService])))); const delegate = scopedInstantiationService.createInstance(ChatListDelegate, this.viewOptions.defaultElementHeight ?? 200); const rendererDelegate: IChatRendererDelegate = { getListLength: () => this.tree.getNode(null).visibleChildrenCount, diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index c9e952ffcd0..7caa40d9684 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -161,7 +161,7 @@ export class CodeBlockPart extends Disposable { this.element = $('.interactive-result-code-block'); this.contextKeyService = this._register(contextKeyService.createScoped(this.element)); - const scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService])); + const scopedInstantiationService = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService]))); const editorElement = dom.append(this.element, $('.interactive-result-editor')); this.editor = this.createEditor(scopedInstantiationService, editorElement, { ...getSimpleEditorOptions(this.configurationService), @@ -191,7 +191,7 @@ export class CodeBlockPart extends Disposable { const toolbarElement = dom.append(this.element, $('.interactive-result-code-block-toolbar')); const editorScopedService = this.editor.contextKeyService.createScoped(toolbarElement); - const editorScopedInstantiationService = scopedInstantiationService.createChild(new ServiceCollection([IContextKeyService, editorScopedService])); + const editorScopedInstantiationService = this._register(scopedInstantiationService.createChild(new ServiceCollection([IContextKeyService, editorScopedService]))); this.toolbar = this._register(editorScopedInstantiationService.createInstance(MenuWorkbenchToolBar, toolbarElement, menuId, { menuOptions: { shouldForwardArgs: true @@ -509,7 +509,7 @@ export class CodeCompareBlockPart extends Disposable { this.messageElement.tabIndex = 0; this.contextKeyService = this._register(contextKeyService.createScoped(this.element)); - const scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService])); + const scopedInstantiationService = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService]))); const editorHeader = dom.append(this.element, $('.interactive-result-header.show-file-icons')); const editorElement = dom.append(this.element, $('.interactive-result-editor')); this.diffEditor = this.createDiffEditor(scopedInstantiationService, editorElement, { @@ -540,7 +540,7 @@ export class CodeCompareBlockPart extends Disposable { this.resourceLabel = this._register(scopedInstantiationService.createInstance(ResourceLabel, editorHeader, { supportIcons: true })); const editorScopedService = this.diffEditor.getModifiedEditor().contextKeyService.createScoped(editorHeader); - const editorScopedInstantiationService = scopedInstantiationService.createChild(new ServiceCollection([IContextKeyService, editorScopedService])); + const editorScopedInstantiationService = this._register(scopedInstantiationService.createChild(new ServiceCollection([IContextKeyService, editorScopedService]))); this.toolbar = this._register(editorScopedInstantiationService.createInstance(MenuWorkbenchToolBar, editorHeader, menuId, { menuOptions: { shouldForwardArgs: true diff --git a/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts b/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts index af47ad2f8ea..7e5107a3827 100644 --- a/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts +++ b/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts @@ -36,7 +36,7 @@ suite('PreferencesService', () => { // PreferencesService creates a PreferencesEditorInput which depends on IPreferencesService, add the real one, not a stub const collection = new ServiceCollection(); collection.set(IPreferencesService, new SyncDescriptor(PreferencesService)); - const instantiationService = testInstantiationService.createChild(collection); + const instantiationService = disposables.add(testInstantiationService.createChild(collection)); testObject = disposables.add(instantiationService.createInstance(PreferencesService)); }); test('options are preserved when calling openEditor', async () => { From 01b9e0111242eef64f87bf133358f95cc9f63349 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 2 Jul 2024 08:54:16 +0200 Subject: [PATCH 0216/2222] Fixes mangling issue in CI --- src/vs/platform/observable/common/wrapInReloadableClass.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/observable/common/wrapInReloadableClass.ts b/src/vs/platform/observable/common/wrapInReloadableClass.ts index bb05ee95bfb..cfecf902c5f 100644 --- a/src/vs/platform/observable/common/wrapInReloadableClass.ts +++ b/src/vs/platform/observable/common/wrapInReloadableClass.ts @@ -21,10 +21,10 @@ type Result = new (...args: TArgs) => IDisposable; class BaseClass { constructor( - protected readonly instantiationService: IInstantiationService, + public readonly instantiationService: IInstantiationService, ) { } - init(...params: any[]): void { } + public init(...params: any[]): void { } } function createWrapper(getClass: () => any, B: new (...args: T) => BaseClass) { From 3ad2eb18b5a8ffba3f2d09b9abfe0a819cf2e99f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 2 Jul 2024 09:43:59 +0200 Subject: [PATCH 0217/2222] bump node-gyp in build/npm/gyp (#219647) --- build/npm/gyp/package.json | 2 +- build/npm/gyp/yarn.lock | 384 +++++++++++++------------------------ 2 files changed, 134 insertions(+), 252 deletions(-) diff --git a/build/npm/gyp/package.json b/build/npm/gyp/package.json index 3961e955a5f..8f35c3c8189 100644 --- a/build/npm/gyp/package.json +++ b/build/npm/gyp/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "devDependencies": { - "node-gyp": "^9.4.0" + "node-gyp": "^9.4.1" }, "scripts": {} } diff --git a/build/npm/gyp/yarn.lock b/build/npm/gyp/yarn.lock index 96d132e7943..b733fb26d97 100644 --- a/build/npm/gyp/yarn.lock +++ b/build/npm/gyp/yarn.lock @@ -2,29 +2,26 @@ # yarn lockfile v1 -"@isaacs/cliui@^8.0.2": - version "8.0.2" - resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" - integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== - dependencies: - string-width "^5.1.2" - string-width-cjs "npm:string-width@^4.2.0" - strip-ansi "^7.0.1" - strip-ansi-cjs "npm:strip-ansi@^6.0.1" - wrap-ansi "^8.1.0" - wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" - -"@npmcli/fs@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.0.tgz#233d43a25a91d68c3a863ba0da6a3f00924a173e" - integrity sha512-7kZUAaLscfgbwBQRbvdMYaZOWyMEcPTH/tJjnyAWJ/dvvs9Ef+CERx/qJb9GExJpl1qipaDGn7KqHnFGGixd0w== +"@gar/promisify@^1.1.3": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" + integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== + +"@npmcli/fs@^2.1.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" + integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== dependencies: + "@gar/promisify" "^1.1.3" semver "^7.3.5" -"@pkgjs/parseargs@^0.11.0": - version "0.11.0" - resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" - integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== +"@npmcli/move-file@^2.0.0": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" + integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== + dependencies: + mkdirp "^1.0.4" + rimraf "^3.0.2" "@tootallnate/once@2": version "2.0.0" @@ -44,12 +41,10 @@ agent-base@6, agent-base@^6.0.2: debug "4" agentkeepalive@^4.2.1: - version "4.3.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.3.0.tgz#bb999ff07412653c1803b3ced35e50729830a255" - integrity sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg== + version "4.5.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" + integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== dependencies: - debug "^4.1.0" - depd "^2.0.0" humanize-ms "^1.2.1" aggregate-error@^3.0.0: @@ -65,23 +60,6 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -ansi-regex@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" - integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== - -ansi-styles@^4.0.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" - integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== - dependencies: - color-convert "^2.0.1" - -ansi-styles@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" - integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== - "aproba@^1.0.3 || ^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" @@ -115,23 +93,29 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -cacache@^17.0.0: - version "17.1.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-17.1.3.tgz#c6ac23bec56516a7c0c52020fd48b4909d7c7044" - integrity sha512-jAdjGxmPxZh0IipMdR7fK/4sDSrHMLUV0+GvVUsjwyGNKHsh79kW/otg+GkbXwl6Uzvy9wsvHOX4nUoWldeZMg== +cacache@^16.1.0: + version "16.1.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" + integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== dependencies: - "@npmcli/fs" "^3.1.0" - fs-minipass "^3.0.0" - glob "^10.2.2" + "@npmcli/fs" "^2.1.0" + "@npmcli/move-file" "^2.0.0" + chownr "^2.0.0" + fs-minipass "^2.1.0" + glob "^8.0.1" + infer-owner "^1.0.4" lru-cache "^7.7.1" - minipass "^5.0.0" + minipass "^3.1.6" minipass-collect "^1.0.2" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" + mkdirp "^1.0.4" p-map "^4.0.0" - ssri "^10.0.0" + promise-inflight "^1.0.1" + rimraf "^3.0.2" + ssri "^9.0.0" tar "^6.1.11" - unique-filename "^3.0.0" + unique-filename "^2.0.0" chownr@^2.0.0: version "2.0.0" @@ -143,18 +127,6 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -color-convert@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" - integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== - dependencies: - color-name "~1.1.4" - -color-name@~1.1.4: - version "1.1.4" - resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" - integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== - color-support@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" @@ -170,47 +142,30 @@ console-control-strings@^1.1.0: resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== -cross-spawn@^7.0.0: - version "7.0.3" - resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" - integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== - dependencies: - path-key "^3.1.0" - shebang-command "^2.0.0" - which "^2.0.1" - -debug@4, debug@^4.1.0, debug@^4.3.3: +debug@4: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +debug@^4.3.3: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== -depd@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" - integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== - -eastasianwidth@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" - integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== - emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== -emoji-regex@^9.2.2: - version "9.2.2" - resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" - integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== - encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -233,28 +188,13 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -foreground-child@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.1.1.tgz#1d173e776d75d2772fed08efe4a0de1ea1b12d0d" - integrity sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg== - dependencies: - cross-spawn "^7.0.0" - signal-exit "^4.0.1" - -fs-minipass@^2.0.0: +fs-minipass@^2.0.0, fs-minipass@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" -fs-minipass@^3.0.0: - version "3.0.2" - resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.2.tgz#5b383858efa8c1eb8c33b39e994f7e8555b8b3a3" - integrity sha512-2GAfyfoaCDRrM6jaOS3UsBts8yJ55VioXdWcOL7dK9zdAuKT71+WBA4ifnNYqVjYv+4SsPxjK0JT4yIIn4cA/g== - dependencies: - minipass "^5.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -274,17 +214,6 @@ gauge@^4.0.3: strip-ansi "^6.0.1" wide-align "^1.1.5" -glob@^10.2.2: - version "10.3.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-10.3.3.tgz#8360a4ffdd6ed90df84aa8d52f21f452e86a123b" - integrity sha512-92vPiMb/iqpmEgsOoIDvTjc50wf9CCCvMzsi6W0JLPeUKE8TWP1a73PgqSrqy7iAZxaSD1YdzU7QZR5LF51MJw== - dependencies: - foreground-child "^3.1.0" - jackspeak "^2.0.3" - minimatch "^9.0.1" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" - path-scurry "^1.10.1" - glob@^7.1.3, glob@^7.1.4: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" @@ -297,6 +226,17 @@ glob@^7.1.3, glob@^7.1.4: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" + integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + graceful-fs@^4.2.6: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" @@ -307,7 +247,7 @@ has-unicode@^2.0.1: resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== -http-cache-semantics@^4.1.1: +http-cache-semantics@^4.1.0: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== @@ -353,6 +293,11 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== +infer-owner@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" + integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== + inflight@^1.0.4: version "1.0.6" resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" @@ -366,10 +311,13 @@ inherits@2, inherits@^2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== -ip@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" - integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" is-fullwidth-code-point@^3.0.0: version "3.0.0" @@ -386,14 +334,10 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== -jackspeak@^2.0.3: - version "2.2.2" - resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-2.2.2.tgz#707c62733924b8dc2a0a629dc6248577788b5385" - integrity sha512-mgNtVv4vUuaKA97yxUHoA3+FkuhtxkjdXEWOyB/N76fjy0FjezEt34oy3epBtvCvS+7DyKwqCFWx/oJLV5+kCg== - dependencies: - "@isaacs/cliui" "^8.0.2" - optionalDependencies: - "@pkgjs/parseargs" "^0.11.0" +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== lru-cache@^6.0.0: version "6.0.0" @@ -407,31 +351,27 @@ lru-cache@^7.7.1: resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== -"lru-cache@^9.1.1 || ^10.0.0": - version "10.0.0" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.0.tgz#b9e2a6a72a129d81ab317202d93c7691df727e61" - integrity sha512-svTf/fzsKHffP42sujkO/Rjs37BCIsQVRCeNYIm9WN8rgT7ffoUnRtZCqU+6BqcSBdv8gwJeTz8knJpgACeQMw== - -make-fetch-happen@^11.0.3: - version "11.1.1" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-11.1.1.tgz#85ceb98079584a9523d4bf71d32996e7e208549f" - integrity sha512-rLWS7GCSTcEujjVBs2YqG7Y4643u8ucvCJeSRqiLYhesrDuzeuFIk37xREzAsfQaqzl8b9rNCE4m6J8tvX4Q8w== +make-fetch-happen@^10.0.3: + version "10.2.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" + integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== dependencies: agentkeepalive "^4.2.1" - cacache "^17.0.0" - http-cache-semantics "^4.1.1" + cacache "^16.1.0" + http-cache-semantics "^4.1.0" http-proxy-agent "^5.0.0" https-proxy-agent "^5.0.0" is-lambda "^1.0.1" lru-cache "^7.7.1" - minipass "^5.0.0" - minipass-fetch "^3.0.0" + minipass "^3.1.6" + minipass-collect "^1.0.2" + minipass-fetch "^2.0.3" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" negotiator "^0.6.3" promise-retry "^2.0.1" socks-proxy-agent "^7.0.0" - ssri "^10.0.0" + ssri "^9.0.0" minimatch@^3.1.1: version "3.1.2" @@ -440,10 +380,10 @@ minimatch@^3.1.1: dependencies: brace-expansion "^1.1.7" -minimatch@^9.0.1: - version "9.0.3" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.3.tgz#a6e00c3de44c3a542bfaae70abfc22420a6da825" - integrity sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg== +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== dependencies: brace-expansion "^2.0.1" @@ -454,12 +394,12 @@ minipass-collect@^1.0.2: dependencies: minipass "^3.0.0" -minipass-fetch@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.3.tgz#d9df70085609864331b533c960fd4ffaa78d15ce" - integrity sha512-n5ITsTkDqYkYJZjcRWzZt9qnZKCT7nKCosJhHoj7S7zD+BP4jVbWs+odsniw5TA3E0sLomhTKOKjF86wf11PuQ== +minipass-fetch@^2.0.3: + version "2.1.2" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" + integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== dependencies: - minipass "^5.0.0" + minipass "^3.1.6" minipass-sized "^1.0.3" minizlib "^2.1.2" optionalDependencies: @@ -486,7 +426,7 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0: +minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -498,11 +438,6 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== -"minipass@^5.0.0 || ^6.0.2 || ^7.0.0": - version "7.0.2" - resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.2.tgz#58a82b7d81c7010da5bd4b2c0c85ac4b4ec5131e" - integrity sha512-eL79dXrE1q9dBbDCLg7xfn/vl7MS4F1gvJAgjJrQli/jbQWdUttuVawphqpffoIYfRdq78LHx6GP4bU/EQ2ATA== - minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -511,7 +446,7 @@ minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mkdirp@^1.0.3: +mkdirp@^1.0.3, mkdirp@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -531,16 +466,16 @@ negotiator@^0.6.3: resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -node-gyp@^9.4.0: - version "9.4.0" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.0.tgz#2a7a91c7cba4eccfd95e949369f27c9ba704f369" - integrity sha512-dMXsYP6gc9rRbejLXmTbVRYjAHw7ppswsKyMxuxJxxOHzluIO1rGp9TOQgjFJ+2MCqcOcQTOPB/8Xwhr+7s4Eg== +node-gyp@^9.4.1: + version "9.4.1" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" + integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== dependencies: env-paths "^2.2.0" exponential-backoff "^3.1.1" glob "^7.1.4" graceful-fs "^4.2.6" - make-fetch-happen "^11.0.3" + make-fetch-happen "^10.0.3" nopt "^6.0.0" npmlog "^6.0.0" rimraf "^3.0.2" @@ -584,18 +519,10 @@ path-is-absolute@^1.0.0: resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== -path-key@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" - integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== - -path-scurry@^1.10.1: - version "1.10.1" - resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.10.1.tgz#9ba6bf5aa8500fe9fd67df4f0d9483b2b0bfc698" - integrity sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ== - dependencies: - lru-cache "^9.1.1 || ^10.0.0" - minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +promise-inflight@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" + integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== promise-retry@^2.0.1: version "2.0.1" @@ -606,9 +533,9 @@ promise-retry@^2.0.1: retry "^0.12.0" readable-stream@^3.6.0: - version "3.6.0" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" - integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + version "3.6.2" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" + integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== dependencies: inherits "^2.0.3" string_decoder "^1.1.1" @@ -648,28 +575,11 @@ set-blocking@^2.0.0: resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== -shebang-command@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" - integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== - dependencies: - shebang-regex "^3.0.0" - -shebang-regex@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" - integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== - signal-exit@^3.0.7: version "3.0.7" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== -signal-exit@^4.0.1: - version "4.1.0" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" - integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== - smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -685,21 +595,26 @@ socks-proxy-agent@^7.0.0: socks "^2.6.2" socks@^2.6.2: - version "2.7.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== dependencies: - ip "^2.0.0" + ip-address "^9.0.5" smart-buffer "^4.2.0" -ssri@^10.0.0: - version "10.0.4" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.4.tgz#5a20af378be586df139ddb2dfb3bf992cf0daba6" - integrity sha512-12+IR2CB2C28MMAw0Ncqwj5QbTcs0nGIhgJzYWzDkb21vWmfNI83KS4f3Ci6GI98WreIfG7o9UXp3C0qbpA8nQ== +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + +ssri@^9.0.0: + version "9.0.1" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" + integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== dependencies: - minipass "^5.0.0" + minipass "^3.1.1" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.3: +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -708,15 +623,6 @@ ssri@^10.0.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string-width@^5.0.1, string-width@^5.1.2: - version "5.1.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" - integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== - dependencies: - eastasianwidth "^0.2.0" - emoji-regex "^9.2.2" - strip-ansi "^7.0.1" - string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -724,20 +630,14 @@ string_decoder@^1.1.1: dependencies: safe-buffer "~5.2.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +strip-ansi@^6.0.1: + name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: - version "7.1.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" - integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== - dependencies: - ansi-regex "^6.0.1" - tar@^6.1.11, tar@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" @@ -750,17 +650,17 @@ tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -unique-filename@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" - integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== +unique-filename@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" + integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== dependencies: - unique-slug "^4.0.0" + unique-slug "^3.0.0" -unique-slug@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" - integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== +unique-slug@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" + integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== dependencies: imurmurhash "^0.1.4" @@ -769,7 +669,7 @@ util-deprecate@^1.0.1: resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== -which@^2.0.1, which@^2.0.2: +which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== @@ -783,24 +683,6 @@ wide-align@^1.1.5: dependencies: string-width "^1.0.2 || 2 || 3 || 4" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - -wrap-ansi@^8.1.0: - version "8.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" - integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== - dependencies: - ansi-styles "^6.1.0" - string-width "^5.0.1" - strip-ansi "^7.0.1" - wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" From 9664fa5b79065cef99322a5e82f43c3b397caa3b Mon Sep 17 00:00:00 2001 From: Ole Date: Tue, 2 Jul 2024 09:46:17 +0200 Subject: [PATCH 0218/2222] Refactoring: Avoid repeating all layout properties in restoreEditorViewState. This way it is easier to see what is not actually kept the same, and harder to forget to add a property when adding one. --- .../browser/viewModel/codeCellViewModel.ts | 15 +-------------- .../browser/viewModel/markupCellViewModel.ts | 6 +----- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 8ce1e7f7369..100519ae20a 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -359,22 +359,9 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod super.restoreEditorViewState(editorViewStates); if (totalHeight !== undefined && this._layoutInfo.layoutState !== CellLayoutState.Measured) { this._layoutInfo = { - fontInfo: this._layoutInfo.fontInfo, - chatHeight: this._layoutInfo.chatHeight, - editorHeight: this._layoutInfo.editorHeight, - editorWidth: this._layoutInfo.editorWidth, - statusBarHeight: this.layoutInfo.statusBarHeight, - commentHeight: this.layoutInfo.commentHeight, - outputContainerOffset: this._layoutInfo.outputContainerOffset, - outputTotalHeight: this._layoutInfo.outputTotalHeight, - outputShowMoreContainerHeight: this._layoutInfo.outputShowMoreContainerHeight, - outputShowMoreContainerOffset: this._layoutInfo.outputShowMoreContainerOffset, + ...this._layoutInfo, totalHeight: totalHeight, - codeIndicatorHeight: this._layoutInfo.codeIndicatorHeight, - outputIndicatorHeight: this._layoutInfo.outputIndicatorHeight, - bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset, layoutState: CellLayoutState.FromCache, - estimatedHasHorizontalScrolling: this._layoutInfo.estimatedHasHorizontalScrolling }; } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts index ee1b0d37d9f..345fdb1caa4 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts @@ -257,16 +257,12 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM // we might already warmup the viewport so the cell has a total height computed if (totalHeight !== undefined && this.layoutInfo.layoutState === CellLayoutState.Uninitialized) { this._layoutInfo = { - fontInfo: this._layoutInfo.fontInfo, - editorWidth: this._layoutInfo.editorWidth, - previewHeight: this._layoutInfo.previewHeight, - bottomToolbarOffset: this._layoutInfo.bottomToolbarOffset, + ...this.layoutInfo, totalHeight: totalHeight, chatHeight: this._chatHeight, editorHeight: this._editorHeight, statusBarHeight: this._statusBarHeight, layoutState: CellLayoutState.FromCache, - foldHintHeight: this._layoutInfo.foldHintHeight }; this.layoutChange({}); } From a3d69a69775e05727a09cf5e3a28f6ec58de315e Mon Sep 17 00:00:00 2001 From: Ole Date: Tue, 2 Jul 2024 09:52:41 +0200 Subject: [PATCH 0219/2222] Refactoring: Move the computation of the commentOffset into the cell layouting function. #214332. The cellComments should not have to know about outputs. The reason this is undesirable is that these exist only for code cells, not for markup cells - and I would like to make cellComments work for markup cells as well. --- .../workbench/contrib/notebook/browser/notebookBrowser.ts | 1 + .../notebook/browser/view/cellParts/cellComments.ts | 4 ++-- .../notebook/browser/viewModel/codeCellViewModel.ts | 7 +++++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index f08c5886c42..45bc2f000a9 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -182,6 +182,7 @@ export interface CodeCellLayoutInfo { readonly editorWidth: number; readonly estimatedHasHorizontalScrolling: boolean; readonly statusBarHeight: number; + readonly commentOffset: number; readonly commentHeight: number; readonly totalHeight: number; readonly outputContainerOffset: number; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts index 44e5866b04f..5e43df877cf 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts @@ -101,7 +101,7 @@ export class CellComments extends CellContentPart { if (!this._commentThreadWidget.value && info) { await this._createCommentTheadWidget(info.owner, info.thread); const layoutInfo = (this.currentElement as CodeCellViewModel).layoutInfo; - this.container.style.top = `${layoutInfo.outputContainerOffset + layoutInfo.outputTotalHeight}px`; + this.container.style.top = `${layoutInfo.commentOffset}px`; this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value!.getDimensions().height); return; } @@ -170,7 +170,7 @@ export class CellComments extends CellContentPart { override updateInternalLayoutNow(element: ICellViewModel): void { if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget.value) { const layoutInfo = (element as CodeCellViewModel).layoutInfo; - this.container.style.top = `${layoutInfo.outputContainerOffset + layoutInfo.outputTotalHeight}px`; + this.container.style.top = `${layoutInfo.commentOffset}px`; } } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 100519ae20a..7f6da4b52f8 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -193,6 +193,7 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod : 0, chatHeight: 0, statusBarHeight: 0, + commentOffset: 0, commentHeight: 0, outputContainerOffset: 0, outputTotalHeight: 0, @@ -289,11 +290,12 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod editorHeight, editorWidth, statusBarHeight, - commentHeight, outputContainerOffset, outputTotalHeight, outputShowMoreContainerHeight, outputShowMoreContainerOffset, + commentOffset: outputContainerOffset + outputTotalHeight, + commentHeight, totalHeight, codeIndicatorHeight, outputIndicatorHeight, @@ -330,11 +332,12 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod editorWidth, chatHeight: chatHeight, statusBarHeight: 0, - commentHeight, outputContainerOffset, outputTotalHeight, outputShowMoreContainerHeight, outputShowMoreContainerOffset, + commentOffset: outputContainerOffset + outputTotalHeight, + commentHeight, totalHeight, codeIndicatorHeight, outputIndicatorHeight, From 0cc941e89368c9bfe35986c385349e1a1b09f73a Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 2 Jul 2024 10:09:50 +0200 Subject: [PATCH 0220/2222] Fixes placeholder placement issues --- .../placeholderText/browser/placeholderTextContribution.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts b/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts index 8ac0fc821c7..f048991d0f9 100644 --- a/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts +++ b/src/vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts @@ -40,7 +40,6 @@ export class PlaceholderTextContribution extends Disposable implements IEditorCo if (!this._shouldViewBeAlive.read(reader)) { return; } const element = h('div.editorPlaceholder'); - element.root.style.top = `1px`; store.add(autorun(reader => { const data = this._state.read(reader); @@ -52,10 +51,12 @@ export class PlaceholderTextContribution extends Disposable implements IEditorCo const info = this._editorObs.layoutInfo.read(reader); element.root.style.left = `${info.contentLeft}px`; element.root.style.width = (info.contentWidth - info.verticalScrollbarWidth) + 'px'; + element.root.style.top = `${this._editor.getTopForLineNumber(0)}px`; })); store.add(autorun(reader => { element.root.style.fontFamily = this._editorObs.getOption(EditorOption.fontFamily).read(reader); element.root.style.fontSize = this._editorObs.getOption(EditorOption.fontSize).read(reader) + 'px'; + element.root.style.lineHeight = this._editorObs.getOption(EditorOption.lineHeight).read(reader) + 'px'; })); store.add(this._editorObs.createOverlayWidget({ allowEditorOverflow: false, @@ -69,7 +70,6 @@ export class PlaceholderTextContribution extends Disposable implements IEditorCo private readonly _editor: ICodeEditor, ) { super(); - this._view.recomputeInitiallyAndOnChange(this._store); } } From c2e17e233713d02bddf3c883ec6a36bb51bd27cf Mon Sep 17 00:00:00 2001 From: Ole Date: Tue, 2 Jul 2024 10:08:00 +0200 Subject: [PATCH 0221/2222] Refactoring: Extract shared fields of CodeCellLayoutInfo and MarkupCellLayoutInfo. #214332. Some layouting tasks, like soon layouting comments, should not have to deal with this distinction. I am extracting the fields that are already shared into an interface that other code can use without differentiating. I am not yet actually moving their computation into a separate file, as that will require some small behavior changes. --- .../notebook/browser/notebookBrowser.ts | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 45bc2f000a9..5e6f216df89 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -175,27 +175,36 @@ export enum CellLayoutState { Measured } -export interface CodeCellLayoutInfo { +/** LayoutInfo of the parts that are shared between all cell types. */ +export interface CellLayoutInfo { + readonly layoutState: CellLayoutState; readonly fontInfo: FontInfo | null; readonly chatHeight: number; - readonly editorHeight: number; readonly editorWidth: number; - readonly estimatedHasHorizontalScrolling: boolean; + readonly editorHeight: number; readonly statusBarHeight: number; + readonly bottomToolbarOffset: number; + readonly totalHeight: number; +} + +export interface CellLayoutChangeEvent { + readonly font?: FontInfo; + readonly outerWidth?: number; +} + +export interface CodeCellLayoutInfo extends CellLayoutInfo { + readonly estimatedHasHorizontalScrolling: boolean; readonly commentOffset: number; readonly commentHeight: number; - readonly totalHeight: number; readonly outputContainerOffset: number; readonly outputTotalHeight: number; readonly outputShowMoreContainerHeight: number; readonly outputShowMoreContainerOffset: number; - readonly bottomToolbarOffset: number; - readonly layoutState: CellLayoutState; readonly codeIndicatorHeight: number; readonly outputIndicatorHeight: number; } -export interface CodeCellLayoutChangeEvent { +export interface CodeCellLayoutChangeEvent extends CellLayoutChangeEvent { readonly source?: string; readonly chatHeight?: boolean; readonly editorHeight?: boolean; @@ -203,20 +212,10 @@ export interface CodeCellLayoutChangeEvent { readonly outputHeight?: boolean; readonly outputShowMoreContainerHeight?: number; readonly totalHeight?: boolean; - readonly outerWidth?: number; - readonly font?: FontInfo; } -export interface MarkupCellLayoutInfo { - readonly fontInfo: FontInfo | null; - readonly chatHeight: number; - readonly editorWidth: number; - readonly editorHeight: number; - readonly statusBarHeight: number; +export interface MarkupCellLayoutInfo extends CellLayoutInfo { readonly previewHeight: number; - readonly bottomToolbarOffset: number; - readonly totalHeight: number; - readonly layoutState: CellLayoutState; readonly foldHintHeight: number; } @@ -224,9 +223,7 @@ export enum CellLayoutContext { Fold } -export interface MarkupCellLayoutChangeEvent { - readonly font?: FontInfo; - readonly outerWidth?: number; +export interface MarkupCellLayoutChangeEvent extends CellLayoutChangeEvent { readonly editorHeight?: number; readonly previewHeight?: number; totalHeight?: number; @@ -242,7 +239,7 @@ export interface ICellViewModel extends IGenericCellViewModel { readonly model: NotebookCellTextModel; readonly id: string; readonly textBuffer: IReadonlyTextBuffer; - readonly layoutInfo: { totalHeight: number; bottomToolbarOffset: number; editorWidth: number; editorHeight: number; statusBarHeight: number; chatHeight: number }; + readonly layoutInfo: CellLayoutInfo; readonly onDidChangeLayout: Event; readonly onDidChangeCellStatusBarItems: Event; readonly onCellDecorationsChanged: Event<{ added: INotebookCellDecorationOptions[]; removed: INotebookCellDecorationOptions[] }>; From c77c717e90c499377b13ab98e1ccbd174e0198d7 Mon Sep 17 00:00:00 2001 From: Ole Date: Tue, 2 Jul 2024 10:31:38 +0200 Subject: [PATCH 0222/2222] Support comments on Markup cells. Fixes #214332. --- .../notebook/browser/notebookBrowser.ts | 7 +- .../browser/view/cellParts/cellComments.ts | 25 ++--- .../browser/viewModel/baseCellViewModel.ts | 12 ++- .../browser/viewModel/codeCellViewModel.ts | 10 -- .../browser/viewModel/markupCellViewModel.ts | 93 +++++++++++-------- 5 files changed, 76 insertions(+), 71 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index 5e6f216df89..b0abb66c5fb 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -183,6 +183,8 @@ export interface CellLayoutInfo { readonly editorWidth: number; readonly editorHeight: number; readonly statusBarHeight: number; + readonly commentOffset: number; + readonly commentHeight: number; readonly bottomToolbarOffset: number; readonly totalHeight: number; } @@ -190,12 +192,11 @@ export interface CellLayoutInfo { export interface CellLayoutChangeEvent { readonly font?: FontInfo; readonly outerWidth?: number; + readonly commentHeight?: boolean; } export interface CodeCellLayoutInfo extends CellLayoutInfo { readonly estimatedHasHorizontalScrolling: boolean; - readonly commentOffset: number; - readonly commentHeight: number; readonly outputContainerOffset: number; readonly outputTotalHeight: number; readonly outputShowMoreContainerHeight: number; @@ -208,7 +209,6 @@ export interface CodeCellLayoutChangeEvent extends CellLayoutChangeEvent { readonly source?: string; readonly chatHeight?: boolean; readonly editorHeight?: boolean; - readonly commentHeight?: boolean; readonly outputHeight?: boolean; readonly outputShowMoreContainerHeight?: number; readonly totalHeight?: boolean; @@ -257,6 +257,7 @@ export interface ICellViewModel extends IGenericCellViewModel { cellKind: CellKind; lineNumbers: 'on' | 'off' | 'inherit'; chatHeight: number; + commentHeight: number; focusMode: CellFocusMode; focusedOutputId?: string | undefined; outputIsHovered: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts index 5e43df877cf..dcdd1a08860 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellComments.ts @@ -15,13 +15,11 @@ import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentSe import { CommentThreadWidget } from 'vs/workbench/contrib/comments/browser/commentThreadWidget'; import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellContentPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; -import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; -import { CellKind } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { ICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; export class CellComments extends CellContentPart { private readonly _commentThreadWidget: MutableDisposable>; - private currentElement: CodeCellViewModel | undefined; + private currentElement: ICellViewModel | undefined; private readonly _commentThreadDisposables = this._register(new DisposableStore()); constructor( @@ -49,7 +47,7 @@ export class CellComments extends CellContentPart { return; } - this.currentElement = element as CodeCellViewModel; + this.currentElement = element; await this._updateThread(); } @@ -83,7 +81,7 @@ export class CellComments extends CellContentPart { this._applyTheme(); this._commentThreadDisposables.add(this._commentThreadWidget.value.onDidResize(() => { - if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget.value) { + if (this.currentElement && this._commentThreadWidget.value) { this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value.getDimensions().height); } })); @@ -100,8 +98,7 @@ export class CellComments extends CellContentPart { const info = await this._getCommentThreadForCell(this.currentElement); if (!this._commentThreadWidget.value && info) { await this._createCommentTheadWidget(info.owner, info.thread); - const layoutInfo = (this.currentElement as CodeCellViewModel).layoutInfo; - this.container.style.top = `${layoutInfo.commentOffset}px`; + this.container.style.top = `${this.currentElement.layoutInfo.commentOffset}px`; this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value!.getDimensions().height); return; } @@ -154,23 +151,19 @@ export class CellComments extends CellContentPart { } override didRenderCell(element: ICellViewModel): void { - if (element.cellKind === CellKind.Code) { - this.initialize(element); - this._bindListeners(); - } - + this.initialize(element); + this._bindListeners(); } override prepareLayout(): void { - if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget.value) { + if (this.currentElement && this._commentThreadWidget.value) { this.currentElement.commentHeight = this._calculateCommentThreadHeight(this._commentThreadWidget.value.getDimensions().height); } } override updateInternalLayoutNow(element: ICellViewModel): void { - if (this.currentElement?.cellKind === CellKind.Code && this._commentThreadWidget.value) { - const layoutInfo = (element as CodeCellViewModel).layoutInfo; - this.container.style.top = `${layoutInfo.commentOffset}px`; + if (this.currentElement && this._commentThreadWidget.value) { + this.container.style.top = `${element.layoutInfo.commentOffset}px`; } } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index b0751fc5b96..0ec9a055e13 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -158,6 +158,16 @@ export abstract class BaseCellViewModel extends Disposable { this._onDidChangeState.fire({ outputCollapsedChanged: true }); } + protected _commentHeight = 0; + + set commentHeight(height: number) { + if (this._commentHeight === height) { + return; + } + this._commentHeight = height; + this.layoutChange({ commentHeight: true }, 'BaseCellViewModel#commentHeight'); + } + private _isDisposed = false; constructor( @@ -204,7 +214,7 @@ export abstract class BaseCellViewModel extends Disposable { abstract updateOptions(e: NotebookOptionsChangeEvent): void; abstract getHeight(lineHeight: number): number; abstract onDeselect(): void; - abstract layoutChange(change: any): void; + abstract layoutChange(change: CellLayoutChangeEvent, source?: string): void; assertTextModelAttached(): boolean { if (this.textModel && this._textEditor && this._textEditor.getModel() === this.textModel) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts index 7f6da4b52f8..3ef89aa05e3 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel.ts @@ -80,16 +80,6 @@ export class CodeCellViewModel extends BaseCellViewModel implements ICellViewMod return this._chatHeight; } - private _commentHeight = 0; - - set commentHeight(height: number) { - if (this._commentHeight === height) { - return; - } - this._commentHeight = height; - this.layoutChange({ commentHeight: true }, 'CodeCellViewModel#commentHeight'); - } - private _hoveringOutput: boolean = false; public get outputIsHovered(): boolean { return this._hoveringOutput; diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts index 345fdb1caa4..3652f955558 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markupCellViewModel.ts @@ -134,6 +134,8 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM editorWidth: initialNotebookLayoutInfo?.width ? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(initialNotebookLayoutInfo.width) : 0, + commentOffset: 0, + commentHeight: 0, bottomToolbarOffset: bottomToolbarGap, totalHeight: 100, layoutState: CellLayoutState.Uninitialized, @@ -160,13 +162,14 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM + layoutConfiguration.markdownCellTopMargin + layoutConfiguration.markdownCellBottomMargin + bottomToolbarGap - + this._statusBarHeight; + + this._statusBarHeight + + this._commentHeight; } else { // @rebornix // On file open, the previewHeight + bottomToolbarGap for a cell out of viewport can be 0 // When it's 0, the list view will never try to render it anymore even if we scroll the cell into view. // Thus we make sure it's greater than 0 - return Math.max(1, this._previewHeight + bottomToolbarGap + foldHintHeight); + return Math.max(1, this._previewHeight + bottomToolbarGap + foldHintHeight + this._commentHeight); } } @@ -204,51 +207,59 @@ export class MarkupCellViewModel extends BaseCellViewModel implements ICellViewM } layoutChange(state: MarkupCellLayoutChangeEvent) { - // recompute - const foldHintHeight = this._computeFoldHintHeight(); + let totalHeight: number; + let foldHintHeight: number; if (!this.isInputCollapsed) { - const editorWidth = state.outerWidth !== undefined - ? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(state.outerWidth) - : this._layoutInfo.editorWidth; - const totalHeight = state.totalHeight === undefined - ? (this._layoutInfo.layoutState === CellLayoutState.Uninitialized ? 100 : this._layoutInfo.totalHeight) - : state.totalHeight; - const previewHeight = this._previewHeight; - - this._layoutInfo = { - fontInfo: state.font || this._layoutInfo.fontInfo, - editorWidth, - previewHeight, - chatHeight: this._chatHeight, - editorHeight: this._editorHeight, - statusBarHeight: this._statusBarHeight, - bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight, this.viewType), - totalHeight, - layoutState: CellLayoutState.Measured, - foldHintHeight - }; + totalHeight = state.totalHeight === undefined ? + (this._layoutInfo.layoutState === + CellLayoutState.Uninitialized ? + 100 : + this._layoutInfo.totalHeight) : + state.totalHeight; + // recompute + foldHintHeight = this._computeFoldHintHeight(); } else { - const editorWidth = state.outerWidth !== undefined - ? this.viewContext.notebookOptions.computeMarkdownCellEditorWidth(state.outerWidth) - : this._layoutInfo.editorWidth; - const totalHeight = this.viewContext.notebookOptions.computeCollapsedMarkdownCellHeight(this.viewType); - + totalHeight = + this.viewContext.notebookOptions + .computeCollapsedMarkdownCellHeight(this.viewType); state.totalHeight = totalHeight; - this._layoutInfo = { - fontInfo: state.font || this._layoutInfo.fontInfo, - editorWidth, - chatHeight: this._chatHeight, - editorHeight: this._editorHeight, - statusBarHeight: this._statusBarHeight, - previewHeight: this._previewHeight, - bottomToolbarOffset: this.viewContext.notebookOptions.computeBottomToolbarOffset(totalHeight, this.viewType), - totalHeight, - layoutState: CellLayoutState.Measured, - foldHintHeight: 0 - }; + foldHintHeight = 0; + } + let commentOffset: number; + if (this.getEditState() === CellEditState.Editing) { + const notebookLayoutConfiguration = this.viewContext.notebookOptions.getLayoutConfiguration(); + commentOffset = notebookLayoutConfiguration.editorToolbarHeight + + notebookLayoutConfiguration.cellTopMargin // CELL_TOP_MARGIN + + this._chatHeight + + this._editorHeight + + this._statusBarHeight; + } else { + commentOffset = this._previewHeight; } + this._layoutInfo = { + fontInfo: state.font || this._layoutInfo.fontInfo, + editorWidth: state.outerWidth !== undefined ? + this.viewContext.notebookOptions + .computeMarkdownCellEditorWidth(state.outerWidth) : + this._layoutInfo.editorWidth, + chatHeight: this._chatHeight, + editorHeight: this._editorHeight, + statusBarHeight: this._statusBarHeight, + previewHeight: this._previewHeight, + bottomToolbarOffset: this.viewContext.notebookOptions + .computeBottomToolbarOffset( + totalHeight, this.viewType), + totalHeight, + layoutState: CellLayoutState.Measured, + foldHintHeight, + commentOffset, + commentHeight: state.commentHeight ? + this._commentHeight : + this._layoutInfo.commentHeight, + }; + this._onDidChangeLayout.fire(state); } From 0a16d3c1be42fb7dc94db40ce292cb8e9c0f7253 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 2 Jul 2024 10:14:45 +0200 Subject: [PATCH 0223/2222] Allows multiple hot reload handlers to participate --- src/vs/base/common/hotReload.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/hotReload.ts b/src/vs/base/common/hotReload.ts index 94dec8e9b2f..609fd9d8ef2 100644 --- a/src/vs/base/common/hotReload.ts +++ b/src/vs/base/common/hotReload.ts @@ -41,9 +41,23 @@ function registerGlobalHotReloadHandler() { g.$hotReload_applyNewExports = args => { const args2 = { config: { mode: undefined }, ...args }; + const results: AcceptNewExportsHandler[] = []; for (const h of hotReloadHandlers!) { const result = h(args2); - if (result) { return result; } + if (result) { + results.push(result); + } + } + if (results.length > 0) { + return newExports => { + let result = false; + for (const r of results) { + if (r(newExports)) { + result = true; + } + } + return result; + }; } return undefined; }; From ad50f01dc1823838d14c608094d4fac2555d7995 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 2 Jul 2024 10:17:19 +0200 Subject: [PATCH 0224/2222] Enables advanced hot reloading in monaco playground server. --- scripts/playground-server.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/scripts/playground-server.ts b/scripts/playground-server.ts index 1e8479a2d1f..474f83def86 100644 --- a/scripts/playground-server.ts +++ b/scripts/playground-server.ts @@ -235,13 +235,15 @@ function handleGetFileChangesRequest(watcher: DirWatcher, fileServer: FileServer function makeLoaderJsHotReloadable(loaderJsCode: string, fileChangesUrl: URL): string { loaderJsCode = loaderJsCode.replace( /constructor\(env, scriptLoader, defineFunc, requireFunc, loaderAvailableTimestamp = 0\) {/, - '$&globalThis.___globalModuleManager = this;' + '$&globalThis.___globalModuleManager = this; globalThis.vscode = { process: { env: { VSCODE_DEV: true } } }' ); const ___globalModuleManager: any = undefined; // This code will be appended to loader.js function $watchChanges(fileChangesUrl: string) { + interface HotReloadConfig { } + let reloadFn; if (globalThis.$sendMessageToParent) { reloadFn = () => globalThis.$sendMessageToParent({ kind: 'reload' }); @@ -345,7 +347,7 @@ function makeLoaderJsHotReloadable(loaderJsCode: string, fileChangesUrl: URL): s } // Check if we can reload - const g: GlobalThisAddition = globalThis as any; + const g = globalThis as any; // A frozen copy of the previous exports const oldExports = Object.freeze({ ...oldModule.exports }); From b5c96d96cae43331e6a4be14ea7b7063bbcaf495 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Tue, 2 Jul 2024 11:09:55 +0200 Subject: [PATCH 0225/2222] debt - remove `defaultImplicitVariables` from agents and remove variable resolvers for inline chat (editor, notebook) (#219659) --- .../browser/chatParticipantContributions.ts | 1 - .../contrib/chat/common/chatAgents.ts | 2 - .../contrib/chat/common/chatServiceImpl.ts | 18 +------ .../browser/inlineChatSessionServiceImpl.ts | 50 ++----------------- .../chat/notebook.chat.contribution.ts | 20 -------- 5 files changed, 4 insertions(+), 87 deletions(-) diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts b/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts index 33c61973533..a1c6d7a732d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipantContributions.ts @@ -239,7 +239,6 @@ export class ChatExtensionPointHandler implements IWorkbenchContribution { name: providerDescriptor.name, fullName: providerDescriptor.fullName, isDefault: providerDescriptor.isDefault, - defaultImplicitVariables: providerDescriptor.defaultImplicitVariables, locations: isNonEmptyArray(providerDescriptor.locations) ? providerDescriptor.locations.map(ChatAgentLocation.fromRaw) : [ChatAgentLocation.Panel], diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index a3c9016944b..08d7013d770 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -73,7 +73,6 @@ export interface IChatAgentData { isDynamic?: boolean; metadata: IChatAgentMetadata; slashCommands: IChatAgentCommand[]; - defaultImplicitVariables?: string[]; locations: ChatAgentLocation[]; } @@ -397,7 +396,6 @@ export class MergedChatAgent implements IChatAgent { get isDefault(): boolean | undefined { return this.data.isDefault; } get metadata(): IChatAgentMetadata { return this.data.metadata; } get slashCommands(): IChatAgentCommand[] { return this.data.slashCommands; } - get defaultImplicitVariables(): string[] | undefined { return this.data.defaultImplicitVariables; } get locations(): ChatAgentLocation[] { return this.data.locations; } async invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index f0a503c2c0b..b18e59ac6b4 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce } from 'vs/base/common/arrays'; import { DeferredPromise } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -23,7 +22,7 @@ import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storag import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { ChatAgentLocation, IChatAgent, IChatAgentRequest, IChatAgentResult, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { ChatModel, ChatRequestModel, ChatRequestRemovalReason, ChatWelcomeMessageModel, IChatModel, IChatRequestModel, IChatRequestVariableData, IChatRequestVariableEntry, IChatResponseModel, IExportableChatData, ISerializableChatData, ISerializableChatsData, getHistoryEntriesFromModel, updateRanges } from 'vs/workbench/contrib/chat/common/chatModel'; +import { ChatModel, ChatRequestModel, ChatRequestRemovalReason, ChatWelcomeMessageModel, IChatModel, IChatRequestModel, IChatRequestVariableData, IChatResponseModel, IExportableChatData, ISerializableChatData, ISerializableChatsData, getHistoryEntriesFromModel, updateRanges } from 'vs/workbench/contrib/chat/common/chatModel'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, IParsedChatRequest, chatAgentLeader, chatSubcommandLeader, getPromptText } from 'vs/workbench/contrib/chat/common/chatParserTypes'; import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; import { IChatCompleteResponse, IChatDetail, IChatFollowup, IChatProgress, IChatSendRequestData, IChatSendRequestOptions, IChatSendRequestResponseState, IChatService, IChatTransferredSessionData, IChatUserActionEvent } from 'vs/workbench/contrib/chat/common/chatService'; @@ -485,21 +484,6 @@ export class ChatService extends Disposable implements IChatService { const promptTextResult = getPromptText(request.message); const updatedVariableData = updateRanges(variableData, promptTextResult.diff); // TODO bit of a hack - // TODO- should figure out how to get rid of implicit variables for inline chat - const implicitVariablesEnabled = (location === ChatAgentLocation.Editor || location === ChatAgentLocation.Notebook); - if (implicitVariablesEnabled) { - const implicitVariables = agent.defaultImplicitVariables; - if (implicitVariables) { - const resolvedImplicitVariables = await Promise.all(implicitVariables.map(async v => { - const id = this.chatVariablesService.getVariable(v)?.id ?? ''; - const value = await this.chatVariablesService.resolveVariable(v, parsedRequest.text, model, progressCallback, token); - return value ? { id, name: v, value } satisfies IChatRequestVariableEntry : - undefined; - })); - updatedVariableData.variables.push(...coalesce(resolvedImplicitVariables)); - } - } - const requestProps: IChatAgentRequest = { sessionId, requestId: request.id, diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index e77461a562c..44aecd6d917 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -10,7 +10,7 @@ import { Schemas } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IActiveCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IRange, Range } from 'vs/editor/common/core/range'; +import { Range } from 'vs/editor/common/core/range'; import { IValidEditOperation } from 'vs/editor/common/model'; import { createTextBufferFactoryFromSnapshot } from 'vs/editor/common/model/textModel'; import { IEditorWorkerService } from 'vs/editor/common/services/editorWorker'; @@ -27,8 +27,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; import { EmptyResponse, ErrorResponse, HunkData, ReplyResponse, Session, SessionExchange, SessionWholeRange, StashedSession, TelemetryData, TelemetryDataClassification } from './inlineChatSession'; import { IInlineChatSessionEndEvent, IInlineChatSessionEvent, IInlineChatSessionService, ISessionKeyComputer, Recording } from './inlineChatSessionService'; -import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; -import { ISelection } from 'vs/editor/common/core/selection'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -46,19 +44,6 @@ export class InlineChatError extends Error { } } -const _inlineChatContext = '_inlineChatContext'; -const _inlineChatDocument = '_inlineChatDocument'; - -class InlineChatContext { - - static readonly variableName = '_inlineChatContext'; - - constructor( - readonly uri: URI, - readonly selection: ISelection, - readonly wholeRange: IRange, - ) { } -} export class InlineChatSessionServiceImpl implements IInlineChatSessionService { @@ -92,37 +77,8 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { @IInstantiationService private readonly _instaService: IInstantiationService, @IEditorService private readonly _editorService: IEditorService, @IChatService private readonly _chatService: IChatService, - @IChatAgentService private readonly _chatAgentService: IChatAgentService, - @IChatVariablesService chatVariableService: IChatVariablesService, - ) { - - - // MARK: implicit variable for editor selection and (tracked) whole range - - this._store.add(chatVariableService.registerVariable( - { id: _inlineChatContext, name: _inlineChatContext, description: '', hidden: true }, - async (_message, _arg, model) => { - for (const [, data] of this._sessions) { - if (data.session.chatModel === model) { - return JSON.stringify(new InlineChatContext(data.session.textModelN.uri, data.editor.getSelection()!, data.session.wholeRange.trackedInitialRange)); - } - } - return undefined; - } - )); - this._store.add(chatVariableService.registerVariable( - { id: _inlineChatDocument, name: _inlineChatDocument, description: '', hidden: true }, - async (_message, _arg, model) => { - for (const [, data] of this._sessions) { - if (data.session.chatModel === model) { - return data.session.textModelN.uri; - } - } - return undefined; - } - )); - - } + @IChatAgentService private readonly _chatAgentService: IChatAgentService + ) { } dispose() { this._store.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts index 0460f9e57da..d642cbdcda9 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts @@ -7,11 +7,8 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IWorkbenchContribution, registerWorkbenchContribution2, WorkbenchPhase } from 'vs/workbench/common/contributions'; import { ChatAgentLocation, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; -import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; import 'vs/workbench/contrib/notebook/browser/controller/chat/cellChatActions'; import { CTX_NOTEBOOK_CHAT_HAS_AGENT } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatContext'; -import { NotebookChatController } from 'vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController'; -import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; class NotebookChatContribution extends Disposable implements IWorkbenchContribution { @@ -20,28 +17,11 @@ class NotebookChatContribution extends Disposable implements IWorkbenchContribut private readonly _ctxHasProvider: IContextKey; constructor( - @IChatVariablesService private readonly _chatVariableService: IChatVariablesService, - @INotebookEditorService private readonly _notebookEditorService: INotebookEditorService, @IContextKeyService contextKeyService: IContextKeyService, @IChatAgentService chatAgentService: IChatAgentService ) { super(); - this._register(this._chatVariableService.registerVariable( - { id: '_notebookChatInput', name: '_notebookChatInput', description: '', hidden: true }, - async (_message, _arg, model) => { - const editors = this._notebookEditorService.listNotebookEditors(); - for (const editor of editors) { - const chatController = editor.getContribution(NotebookChatController.id) as NotebookChatController | undefined; - if (chatController?.hasSession(model)) { - return chatController.getSessionInputUri(); - } - } - - return undefined; - } - )); - this._ctxHasProvider = CTX_NOTEBOOK_CHAT_HAS_AGENT.bindTo(contextKeyService); const updateNotebookAgentStatus = () => { From 4fb0b152981e862eea6d6cc45345298d36a2b256 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 2 Jul 2024 11:10:11 +0200 Subject: [PATCH 0226/2222] Fix issue with clicking between active tabs (#219651) * fix #213160 * cleanup * :lipstick: --- .../browser/parts/editor/multiEditorTabsControl.ts | 13 +++++++++++++ .../common/editor/filteredEditorGroupModel.ts | 2 +- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 36a087361f5..4ea2a840df4 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -684,7 +684,20 @@ export class MultiEditorTabsControl extends EditorTabsControl { this.layout(this.dimensions, { forceRevealActiveTab: true }); } + private previousSelectedCount = 0; updateEditorSelections(): void { + // We only need to redraw from here when a selection got removed + // otherwise it will be handled by the open editor change event. + // Checking count is currently enough but might require checking + // each editor in the future if selection is changed programatically. + const newSelectedCount = this.tabsModel.selectedEditors.length; + const previousSelectedCount = this.previousSelectedCount; + this.previousSelectedCount = newSelectedCount; + + if (newSelectedCount >= previousSelectedCount) { + return; + } + this.forEachTab((editor, tabIndex, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => { this.redrawTabSelectedActiveAndDirty(this.groupsView.activeGroup === this.groupView, editor, tabContainer, tabActionBar); }); diff --git a/src/vs/workbench/common/editor/filteredEditorGroupModel.ts b/src/vs/workbench/common/editor/filteredEditorGroupModel.ts index b872c92677b..61d4f6a7c80 100644 --- a/src/vs/workbench/common/editor/filteredEditorGroupModel.ts +++ b/src/vs/workbench/common/editor/filteredEditorGroupModel.ts @@ -36,7 +36,7 @@ abstract class FilteredEditorGroupModel extends Disposable implements IReadonlyE get activeEditor(): EditorInput | null { return this.model.activeEditor && this.filter(this.model.activeEditor) ? this.model.activeEditor : null; } get previewEditor(): EditorInput | null { return this.model.previewEditor && this.filter(this.model.previewEditor) ? this.model.previewEditor : null; } - get selectedEditors(): EditorInput[] { throw new Error('Filtered Editor Group Model should not be used for selectedEditors'); } + get selectedEditors(): EditorInput[] { return this.model.selectedEditors.filter(e => this.filter(e)); } isPinned(editorOrIndex: EditorInput | number): boolean { return this.model.isPinned(editorOrIndex); } isTransient(editorOrIndex: EditorInput | number): boolean { return this.model.isTransient(editorOrIndex); } From c1f025d79f4ae4232aae4ed9f23adb6b5eebc94b Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:13:39 +0200 Subject: [PATCH 0227/2222] =?UTF-8?q?SCM=20-=20=F0=9F=92=84=20use=20editor?= =?UTF-8?q?=20placeholder=20instead=20of=20custom=20solution=20(#219662)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contrib/scm/browser/media/scm.css | 20 ++------- .../contrib/scm/browser/scmViewPane.ts | 45 +++++++------------ 2 files changed, 19 insertions(+), 46 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/media/scm.css b/src/vs/workbench/contrib/scm/browser/media/scm.css index 7b94eac4187..288452b11c0 100644 --- a/src/vs/workbench/contrib/scm/browser/media/scm.css +++ b/src/vs/workbench/contrib/scm/browser/media/scm.css @@ -492,22 +492,6 @@ margin-top: 1px; } -.scm-view .scm-editor-placeholder { - position: absolute; - pointer-events: none; - z-index: 1; - padding: 2px 6px; - box-sizing: border-box; - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; - color: var(--vscode-input-placeholderForeground); -} - -.scm-view .scm-editor-placeholder.hidden { - display: none; -} - .scm-view .scm-editor-container .monaco-editor-background, .scm-view .scm-editor-container .monaco-editor, .scm-view .scm-editor-container .monaco-editor .margin, @@ -522,6 +506,10 @@ color: var(--vscode-input-foreground); } +.scm-view .scm-editor-container .placeholder-text.mtk1 { + color: var(--vscode-input-placeholderForeground); +} + /* Repositories */ .scm-repositories-view .scm-provider { diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 028ef92e159..e969b6a0e09 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -110,6 +110,7 @@ import { ITextModel } from 'vs/editor/common/model'; import { autorun } from 'vs/base/common/observable'; import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { renderSCMHistoryItemGraph, toISCMHistoryItemViewModelArray } from 'vs/workbench/contrib/scm/browser/scmHistory'; +import { PlaceholderTextContribution } from 'vs/editor/contrib/placeholderText/browser/placeholderTextContribution'; // type SCMResourceTreeNode = IResourceNode; // type SCMHistoryItemChangeResourceTreeNode = IResourceNode; @@ -2338,7 +2339,6 @@ class SCMInputWidget { private element: HTMLElement; private editorContainer: HTMLElement; - private placeholderTextContainer: HTMLElement; private readonly inputEditor: CodeEditorWidget; private readonly inputEditorOptions: SCMInputWidgetEditorOptions; private toolbarContainer: HTMLElement; @@ -2429,14 +2429,11 @@ class SCMInputWidget { this.repositoryDisposables.add(input.onDidChangeValidationMessage((e) => this.setValidation(e, { focus: true, timeout: true }))); this.repositoryDisposables.add(input.onDidChangeValidateInput((e) => triggerValidation())); - // Keep API in sync with model, update placeholder visibility and validate - const updatePlaceholderVisibility = () => this.placeholderTextContainer.classList.toggle('hidden', textModel.getValueLength() > 0); + // Keep API in sync with model and validate this.repositoryDisposables.add(textModel.onDidChangeContent(() => { input.setValue(textModel.getValue(), true); - updatePlaceholderVisibility(); triggerValidation(); })); - updatePlaceholderVisibility(); // Update placeholder text const updatePlaceholderText = () => { @@ -2444,8 +2441,7 @@ class SCMInputWidget { const label = binding ? binding.getLabel() : (platform.isMacintosh ? 'Cmd+Enter' : 'Ctrl+Enter'); const placeholderText = format(input.placeholder, label); - this.inputEditor.updateOptions({ ariaLabel: placeholderText }); - this.placeholderTextContainer.textContent = placeholderText; + this.inputEditor.updateOptions({ placeholder: placeholderText }); }; this.repositoryDisposables.add(input.onDidChangePlaceholder(updatePlaceholderText)); this.repositoryDisposables.add(this.keybindingService.onDidUpdateKeybindings(updatePlaceholderText)); @@ -2526,7 +2522,6 @@ class SCMInputWidget { ) { this.element = append(container, $('.scm-editor')); this.editorContainer = append(this.element, $('.scm-editor-container')); - this.placeholderTextContainer = append(this.editorContainer, $('.scm-editor-placeholder')); this.toolbarContainer = append(this.element, $('.scm-editor-toolbar')); this.contextKeyService = contextKeyService.createScoped(this.element); @@ -2536,33 +2531,32 @@ class SCMInputWidget { this.disposables.add(this.inputEditorOptions.onDidChange(this.onDidChangeEditorOptions, this)); this.disposables.add(this.inputEditorOptions); - const editorConstructionOptions = this.inputEditorOptions.getEditorConstructionOptions(); - this.setPlaceholderFontStyles(editorConstructionOptions.fontFamily!, editorConstructionOptions.fontSize!, editorConstructionOptions.lineHeight!); - const codeEditorWidgetOptions: ICodeEditorWidgetOptions = { - isSimpleWidget: true, contributions: EditorExtensionsRegistry.getSomeEditorContributions([ + CodeActionController.ID, ColorDetector.ID, ContextMenuController.ID, - DragAndDropController.ID, CopyPasteController.ID, + DragAndDropController.ID, DropIntoEditorController.ID, + EditorDictation.ID, + FormatOnType.ID, + HoverController.ID, + InlineCompletionsController.ID, LinkDetector.ID, MenuPreventer.ID, MessageController.ID, - HoverController.ID, + PlaceholderTextContribution.ID, SelectionClipboardContributionID, SnippetController2.ID, - SuggestController.ID, - InlineCompletionsController.ID, - CodeActionController.ID, - FormatOnType.ID, - EditorDictation.ID, - ]) + SuggestController.ID + ]), + isSimpleWidget: true }; const services = new ServiceCollection([IContextKeyService, this.contextKeyService]); const instantiationService2 = instantiationService.createChild(services, this.disposables); + const editorConstructionOptions = this.inputEditorOptions.getEditorConstructionOptions(); this.inputEditor = instantiationService2.createInstance(CodeEditorWidget, this.editorContainer, editorConstructionOptions, codeEditorWidgetOptions); this.disposables.add(this.inputEditor); @@ -2652,7 +2646,6 @@ class SCMInputWidget { this.lastLayoutWasTrash = false; this.inputEditor.layout(dimension); - this.placeholderTextContainer.style.width = `${dimension.width}px`; this.renderValidation(); const showInputActionButton = this.configurationService.getValue('scm.showInputActionButton') === true; @@ -2680,10 +2673,7 @@ class SCMInputWidget { } private onDidChangeEditorOptions(): void { - const editorOptions = this.inputEditorOptions.getEditorOptions(); - - this.inputEditor.updateOptions(editorOptions); - this.setPlaceholderFontStyles(editorOptions.fontFamily!, editorOptions.fontSize!, editorOptions.lineHeight!); + this.inputEditor.updateOptions(this.inputEditorOptions.getEditorOptions()); } private renderValidation(): void { @@ -2773,11 +2763,6 @@ class SCMInputWidget { 26 /* 22px action + 4px margin */ : 39 /* 35px action + 4px margin */; } - private setPlaceholderFontStyles(fontFamily: string, fontSize: number, lineHeight: number): void { - this.placeholderTextContainer.style.fontFamily = fontFamily; - this.placeholderTextContainer.style.fontSize = `${fontSize}px`; - this.placeholderTextContainer.style.lineHeight = `${lineHeight}px`; - } clearValidation(): void { this.validationContextView?.close(); From c57f7bbe783b56ec009c263e01c23b767a37f9e1 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:33:13 +0200 Subject: [PATCH 0228/2222] fixes #213334 (#219669) fixes 213334 --- src/vs/workbench/browser/contextkeys.ts | 26 +++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index c22caa967b3..ccfe454ba34 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -203,17 +203,16 @@ export class WorkbenchContextKeysHandler extends Disposable { private registerListeners(): void { this.editorGroupService.whenReady.then(() => { this.updateEditorAreaContextKeys(); - this.updateEditorGroupContextKeys(); + this.updateActiveEditorGroupContextKeys(); this.updateVisiblePanesContextKeys(); }); - this._register(this.editorService.onDidActiveEditorChange(() => this.updateEditorGroupContextKeys())); + this._register(this.editorService.onDidActiveEditorChange(() => this.updateActiveEditorGroupContextKeys())); this._register(this.editorService.onDidVisibleEditorsChange(() => this.updateVisiblePanesContextKeys())); - this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorGroupContextKeys())); - this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorGroupContextKeys())); - this._register(this.editorGroupService.onDidChangeGroupIndex(() => this.updateEditorGroupContextKeys())); - this._register(this.editorGroupService.onDidChangeActiveGroup(() => this.updateEditorGroupsContextKeys())); - this._register(this.editorGroupService.onDidChangeGroupLocked(() => this.updateEditorGroupsContextKeys())); + this._register(this.editorGroupService.onDidAddGroup(() => this.updateEditorGroupsContextKeys())); + this._register(this.editorGroupService.onDidRemoveGroup(() => this.updateEditorGroupsContextKeys())); + this._register(this.editorGroupService.onDidChangeGroupIndex(() => this.updateActiveEditorGroupContextKeys())); + this._register(this.editorGroupService.onDidChangeGroupLocked(() => this.updateActiveEditorGroupContextKeys())); this._register(this.editorGroupService.onDidChangeEditorPartOptions(() => this.updateEditorAreaContextKeys())); @@ -266,16 +265,25 @@ export class WorkbenchContextKeysHandler extends Disposable { } } - private updateEditorGroupContextKeys(): void { + // Context keys depending on the state of the editor group itself + private updateActiveEditorGroupContextKeys(): void { + console.log('active group'); if (!this.editorService.activeEditor) { this.activeEditorGroupEmpty.set(true); } else { this.activeEditorGroupEmpty.reset(); } + + const activeGroup = this.editorGroupService.activeGroup; + this.activeEditorGroupIndex.set(activeGroup.index + 1); // not zero-indexed + this.activeEditorGroupLocked.set(activeGroup.isLocked); + this.updateEditorGroupsContextKeys(); } + // Context keys depending on the state of other editor groups private updateEditorGroupsContextKeys(): void { + console.log('all groups'); const groupCount = this.editorGroupService.count; if (groupCount > 1) { this.multipleEditorGroupsContext.set(true); @@ -284,9 +292,7 @@ export class WorkbenchContextKeysHandler extends Disposable { } const activeGroup = this.editorGroupService.activeGroup; - this.activeEditorGroupIndex.set(activeGroup.index + 1); // not zero-indexed this.activeEditorGroupLast.set(activeGroup.index === groupCount - 1); - this.activeEditorGroupLocked.set(activeGroup.isLocked); } private updateEditorAreaContextKeys(): void { From c395df82fbfeb1809fa6c72e00747a9b5ee00292 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Tue, 2 Jul 2024 13:56:24 +0200 Subject: [PATCH 0229/2222] bump node-gyp once again --- build/npm/gyp/package.json | 2 +- build/npm/gyp/yarn.lock | 631 +++++++++++++++++-------------------- 2 files changed, 298 insertions(+), 335 deletions(-) diff --git a/build/npm/gyp/package.json b/build/npm/gyp/package.json index 8f35c3c8189..a1564133a1e 100644 --- a/build/npm/gyp/package.json +++ b/build/npm/gyp/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "devDependencies": { - "node-gyp": "^9.4.1" + "node-gyp": "^10.1.0" }, "scripts": {} } diff --git a/build/npm/gyp/yarn.lock b/build/npm/gyp/yarn.lock index b733fb26d97..a9bf901727e 100644 --- a/build/npm/gyp/yarn.lock +++ b/build/npm/gyp/yarn.lock @@ -2,50 +2,52 @@ # yarn lockfile v1 -"@gar/promisify@^1.1.3": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" - integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== - -"@npmcli/fs@^2.1.0": - version "2.1.2" - resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-2.1.2.tgz#a9e2541a4a2fec2e69c29b35e6060973da79b865" - integrity sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ== +"@isaacs/cliui@^8.0.2": + version "8.0.2" + resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" + integrity sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA== + dependencies: + string-width "^5.1.2" + string-width-cjs "npm:string-width@^4.2.0" + strip-ansi "^7.0.1" + strip-ansi-cjs "npm:strip-ansi@^6.0.1" + wrap-ansi "^8.1.0" + wrap-ansi-cjs "npm:wrap-ansi@^7.0.0" + +"@npmcli/agent@^2.0.0": + version "2.2.2" + resolved "https://registry.yarnpkg.com/@npmcli/agent/-/agent-2.2.2.tgz#967604918e62f620a648c7975461c9c9e74fc5d5" + integrity sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og== + dependencies: + agent-base "^7.1.0" + http-proxy-agent "^7.0.0" + https-proxy-agent "^7.0.1" + lru-cache "^10.0.1" + socks-proxy-agent "^8.0.3" + +"@npmcli/fs@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@npmcli/fs/-/fs-3.1.1.tgz#59cdaa5adca95d135fc00f2bb53f5771575ce726" + integrity sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg== dependencies: - "@gar/promisify" "^1.1.3" semver "^7.3.5" -"@npmcli/move-file@^2.0.0": - version "2.0.1" - resolved "https://registry.yarnpkg.com/@npmcli/move-file/-/move-file-2.0.1.tgz#26f6bdc379d87f75e55739bab89db525b06100e4" - integrity sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ== - dependencies: - mkdirp "^1.0.4" - rimraf "^3.0.2" +"@pkgjs/parseargs@^0.11.0": + version "0.11.0" + resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" + integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@tootallnate/once@2": +abbrev@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-2.0.0.tgz#f544a148d3ab35801c1f633a7441fd87c2e484bf" - integrity sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A== - -abbrev@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf" + integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ== -agent-base@6, agent-base@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" - integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ== +agent-base@^7.0.2, agent-base@^7.1.0, agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== dependencies: - debug "4" - -agentkeepalive@^4.2.1: - version "4.5.0" - resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.5.0.tgz#2673ad1389b3c418c5a20c5d7364f93ca04be923" - integrity sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew== - dependencies: - humanize-ms "^1.2.1" + debug "^4.3.4" aggregate-error@^3.0.0: version "3.1.0" @@ -60,32 +62,28 @@ ansi-regex@^5.0.1: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== -"aproba@^1.0.3 || ^2.0.0": - version "2.0.0" - resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc" - integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ== +ansi-regex@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-6.0.1.tgz#3183e38fae9a65d7cb5e53945cd5897d0260a06a" + integrity sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA== -are-we-there-yet@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz#679df222b278c64f2cdba1175cdc00b0d96164bd" - integrity sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg== +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== dependencies: - delegates "^1.0.0" - readable-stream "^3.6.0" + color-convert "^2.0.1" + +ansi-styles@^6.1.0: + version "6.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-6.2.1.tgz#0e62320cf99c21afff3b3012192546aacbfb05c5" + integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug== balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -brace-expansion@^1.1.7: - version "1.1.11" - resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" - integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== - dependencies: - balanced-match "^1.0.0" - concat-map "0.0.1" - brace-expansion@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" @@ -93,29 +91,23 @@ brace-expansion@^2.0.1: dependencies: balanced-match "^1.0.0" -cacache@^16.1.0: - version "16.1.3" - resolved "https://registry.yarnpkg.com/cacache/-/cacache-16.1.3.tgz#a02b9f34ecfaf9a78c9f4bc16fceb94d5d67a38e" - integrity sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ== - dependencies: - "@npmcli/fs" "^2.1.0" - "@npmcli/move-file" "^2.0.0" - chownr "^2.0.0" - fs-minipass "^2.1.0" - glob "^8.0.1" - infer-owner "^1.0.4" - lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" +cacache@^18.0.0: + version "18.0.3" + resolved "https://registry.yarnpkg.com/cacache/-/cacache-18.0.3.tgz#864e2c18414e1e141ae8763f31e46c2cb96d1b21" + integrity sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg== + dependencies: + "@npmcli/fs" "^3.1.0" + fs-minipass "^3.0.0" + glob "^10.2.2" + lru-cache "^10.0.1" + minipass "^7.0.3" + minipass-collect "^2.0.1" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" - mkdirp "^1.0.4" p-map "^4.0.0" - promise-inflight "^1.0.1" - rimraf "^3.0.2" - ssri "^9.0.0" + ssri "^10.0.0" tar "^6.1.11" - unique-filename "^2.0.0" + unique-filename "^3.0.0" chownr@^2.0.0: version "2.0.0" @@ -127,20 +119,26 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" -concat-map@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" - integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -console-control-strings@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" - integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ== +cross-spawn@^7.0.0: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" debug@4: version "4.3.4" @@ -149,23 +147,28 @@ debug@4: dependencies: ms "2.1.2" -debug@^4.3.3: +debug@^4.3.4: version "4.3.5" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== dependencies: ms "2.1.2" -delegates@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" - integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ== +eastasianwidth@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz#696ce2ec0aa0e6ea93a397ffcf24aa7840c827cb" + integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA== emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== +emoji-regex@^9.2.2: + version "9.2.2" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-9.2.2.tgz#840c8803b0d8047f4ff0cf963176b32d4ef3ed72" + integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg== + encoding@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/encoding/-/encoding-0.1.13.tgz#56574afdd791f54a8e9b2785c0582a2d26210fa9" @@ -188,94 +191,66 @@ exponential-backoff@^3.1.1: resolved "https://registry.yarnpkg.com/exponential-backoff/-/exponential-backoff-3.1.1.tgz#64ac7526fe341ab18a39016cd22c787d01e00bf6" integrity sha512-dX7e/LHVJ6W3DE1MHWi9S1EYzDESENfLrYohG2G++ovZrYOkm4Knwa0mc1cn84xJOR4KEU0WSchhLbd0UklbHw== -fs-minipass@^2.0.0, fs-minipass@^2.1.0: +foreground-child@^3.1.0: + version "3.2.1" + resolved "https://registry.yarnpkg.com/foreground-child/-/foreground-child-3.2.1.tgz#767004ccf3a5b30df39bed90718bab43fe0a59f7" + integrity sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA== + dependencies: + cross-spawn "^7.0.0" + signal-exit "^4.0.1" + +fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg== dependencies: minipass "^3.0.0" -fs.realpath@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" - integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== - -gauge@^4.0.3: - version "4.0.4" - resolved "https://registry.yarnpkg.com/gauge/-/gauge-4.0.4.tgz#52ff0652f2bbf607a989793d53b751bef2328dce" - integrity sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg== - dependencies: - aproba "^1.0.3 || ^2.0.0" - color-support "^1.1.3" - console-control-strings "^1.1.0" - has-unicode "^2.0.1" - signal-exit "^3.0.7" - string-width "^4.2.3" - strip-ansi "^6.0.1" - wide-align "^1.1.5" - -glob@^7.1.3, glob@^7.1.4: - version "7.2.3" - resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" - integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== +fs-minipass@^3.0.0: + version "3.0.3" + resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-3.0.3.tgz#79a85981c4dc120065e96f62086bf6f9dc26cc54" + integrity sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^3.1.1" - once "^1.3.0" - path-is-absolute "^1.0.0" + minipass "^7.0.3" -glob@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/glob/-/glob-8.1.0.tgz#d388f656593ef708ee3e34640fdfb99a9fd1c33e" - integrity sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ== +glob@^10.2.2, glob@^10.3.10: + version "10.4.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5" + integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w== dependencies: - fs.realpath "^1.0.0" - inflight "^1.0.4" - inherits "2" - minimatch "^5.0.1" - once "^1.3.0" + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" graceful-fs@^4.2.6: version "4.2.10" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c" integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA== -has-unicode@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" - integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ== - -http-cache-semantics@^4.1.0: +http-cache-semantics@^4.1.1: version "4.1.1" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz#abe02fcb2985460bf0323be664436ec3476a6d5a" integrity sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ== -http-proxy-agent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz#5129800203520d434f142bc78ff3c170800f2b43" - integrity sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w== +http-proxy-agent@^7.0.0: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== dependencies: - "@tootallnate/once" "2" - agent-base "6" - debug "4" + agent-base "^7.1.0" + debug "^4.3.4" -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== +https-proxy-agent@^7.0.1: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== dependencies: - agent-base "6" + agent-base "^7.0.2" debug "4" -humanize-ms@^1.2.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" - integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== - dependencies: - ms "^2.0.0" - iconv-lite@^0.6.2: version "0.6.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" @@ -293,24 +268,6 @@ indent-string@^4.0.0: resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251" integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg== -infer-owner@^1.0.4: - version "1.0.4" - resolved "https://registry.yarnpkg.com/infer-owner/-/infer-owner-1.0.4.tgz#c4cefcaa8e51051c2a40ba2ce8a3d27295af9467" - integrity sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A== - -inflight@^1.0.4: - version "1.0.6" - resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" - integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== - dependencies: - once "^1.3.0" - wrappy "1" - -inherits@2, inherits@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - ip-address@^9.0.5: version "9.0.5" resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" @@ -334,11 +291,30 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== +isexe@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" + integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== + +jackspeak@^3.1.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" + integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jsbn@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== +lru-cache@^10.0.1, lru-cache@^10.2.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b" + integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ== + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -346,60 +322,44 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lru-cache@^7.7.1: - version "7.18.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.18.3.tgz#f793896e0fd0e954a59dfdd82f0773808df6aa89" - integrity sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA== - -make-fetch-happen@^10.0.3: - version "10.2.1" - resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz#f5e3835c5e9817b617f2770870d9492d28678164" - integrity sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w== - dependencies: - agentkeepalive "^4.2.1" - cacache "^16.1.0" - http-cache-semantics "^4.1.0" - http-proxy-agent "^5.0.0" - https-proxy-agent "^5.0.0" +make-fetch-happen@^13.0.0: + version "13.0.1" + resolved "https://registry.yarnpkg.com/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz#273ba2f78f45e1f3a6dca91cede87d9fa4821e36" + integrity sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA== + dependencies: + "@npmcli/agent" "^2.0.0" + cacache "^18.0.0" + http-cache-semantics "^4.1.1" is-lambda "^1.0.1" - lru-cache "^7.7.1" - minipass "^3.1.6" - minipass-collect "^1.0.2" - minipass-fetch "^2.0.3" + minipass "^7.0.2" + minipass-fetch "^3.0.0" minipass-flush "^1.0.5" minipass-pipeline "^1.2.4" negotiator "^0.6.3" + proc-log "^4.2.0" promise-retry "^2.0.1" - socks-proxy-agent "^7.0.0" - ssri "^9.0.0" - -minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== - dependencies: - brace-expansion "^1.1.7" + ssri "^10.0.0" -minimatch@^5.0.1: - version "5.1.6" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" - integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: brace-expansion "^2.0.1" -minipass-collect@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-1.0.2.tgz#22b813bf745dc6edba2576b940022ad6edc8c617" - integrity sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA== +minipass-collect@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/minipass-collect/-/minipass-collect-2.0.1.tgz#1621bc77e12258a12c60d34e2276ec5c20680863" + integrity sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw== dependencies: - minipass "^3.0.0" + minipass "^7.0.3" -minipass-fetch@^2.0.3: - version "2.1.2" - resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-2.1.2.tgz#95560b50c472d81a3bc76f20ede80eaed76d8add" - integrity sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA== +minipass-fetch@^3.0.0: + version "3.0.5" + resolved "https://registry.yarnpkg.com/minipass-fetch/-/minipass-fetch-3.0.5.tgz#f0f97e40580affc4a35cc4a1349f05ae36cb1e4c" + integrity sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg== dependencies: - minipass "^3.1.6" + minipass "^7.0.3" minipass-sized "^1.0.3" minizlib "^2.1.2" optionalDependencies: @@ -426,7 +386,7 @@ minipass-sized@^1.0.3: dependencies: minipass "^3.0.0" -minipass@^3.0.0, minipass@^3.1.1, minipass@^3.1.6: +minipass@^3.0.0: version "3.3.6" resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a" integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw== @@ -438,6 +398,11 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d" integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ== +"minipass@^5.0.0 || ^6.0.2 || ^7.0.0", minipass@^7.0.2, minipass@^7.0.3, minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^2.1.1, minizlib@^2.1.2: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -446,7 +411,7 @@ minizlib@^2.1.1, minizlib@^2.1.2: minipass "^3.0.0" yallist "^4.0.0" -mkdirp@^1.0.3, mkdirp@^1.0.4: +mkdirp@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== @@ -456,56 +421,33 @@ ms@2.1.2: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -ms@^2.0.0: - version "2.1.3" - resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" - integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== - negotiator@^0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== -node-gyp@^9.4.1: - version "9.4.1" - resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-9.4.1.tgz#8a1023e0d6766ecb52764cc3a734b36ff275e185" - integrity sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ== +node-gyp@^10.1.0: + version "10.1.0" + resolved "https://registry.yarnpkg.com/node-gyp/-/node-gyp-10.1.0.tgz#75e6f223f2acb4026866c26a2ead6aab75a8ca7e" + integrity sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA== dependencies: env-paths "^2.2.0" exponential-backoff "^3.1.1" - glob "^7.1.4" + glob "^10.3.10" graceful-fs "^4.2.6" - make-fetch-happen "^10.0.3" - nopt "^6.0.0" - npmlog "^6.0.0" - rimraf "^3.0.2" + make-fetch-happen "^13.0.0" + nopt "^7.0.0" + proc-log "^3.0.0" semver "^7.3.5" tar "^6.1.2" - which "^2.0.2" - -nopt@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" - integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== - dependencies: - abbrev "^1.0.0" + which "^4.0.0" -npmlog@^6.0.0: - version "6.0.2" - resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-6.0.2.tgz#c8166017a42f2dea92d6453168dd865186a70830" - integrity sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg== +nopt@^7.0.0: + version "7.2.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-7.2.1.tgz#1cac0eab9b8e97c9093338446eddd40b2c8ca1e7" + integrity sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w== dependencies: - are-we-there-yet "^3.0.0" - console-control-strings "^1.1.0" - gauge "^4.0.3" - set-blocking "^2.0.0" - -once@^1.3.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" - integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== - dependencies: - wrappy "1" + abbrev "^2.0.0" p-map@^4.0.0: version "4.0.0" @@ -514,15 +456,33 @@ p-map@^4.0.0: dependencies: aggregate-error "^3.0.0" -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== +package-json-from-dist@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== -promise-inflight@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/promise-inflight/-/promise-inflight-1.0.1.tgz#98472870bf228132fcbdd868129bad12c3c029e3" - integrity sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g== +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + +proc-log@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-3.0.0.tgz#fb05ef83ccd64fd7b20bbe9c8c1070fc08338dd8" + integrity sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A== + +proc-log@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-4.2.0.tgz#b6f461e4026e75fdfe228b265e9f7a00779d7034" + integrity sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA== promise-retry@^2.0.1: version "2.0.1" @@ -532,32 +492,11 @@ promise-retry@^2.0.1: err-code "^2.0.2" retry "^0.12.0" -readable-stream@^3.6.0: - version "3.6.2" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967" - integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA== - dependencies: - inherits "^2.0.3" - string_decoder "^1.1.1" - util-deprecate "^1.0.1" - retry@^0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/retry/-/retry-0.12.0.tgz#1b42a6266a21f07421d1b0b54b7dc167b01c013b" integrity sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow== -rimraf@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" - integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== - dependencies: - glob "^7.1.3" - -safe-buffer@~5.2.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" @@ -570,31 +509,38 @@ semver@^7.3.5: dependencies: lru-cache "^6.0.0" -set-blocking@^2.0.0: +shebang-command@^2.0.0: version "2.0.0" - resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" - integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" -signal-exit@^3.0.7: - version "3.0.7" - resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9" - integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ== +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +signal-exit@^4.0.1: + version "4.1.0" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-4.1.0.tgz#952188c1cbd546070e2dd20d0f41c0ae0530cb04" + integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== smart-buffer@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== -socks-proxy-agent@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz#dc069ecf34436621acb41e3efa66ca1b5fed15b6" - integrity sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww== +socks-proxy-agent@^8.0.3: + version "8.0.4" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== dependencies: - agent-base "^6.0.2" - debug "^4.3.3" - socks "^2.6.2" + agent-base "^7.1.1" + debug "^4.3.4" + socks "^2.8.3" -socks@^2.6.2: +socks@^2.8.3: version "2.8.3" resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== @@ -607,14 +553,14 @@ sprintf-js@^1.1.3: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== -ssri@^9.0.0: - version "9.0.1" - resolved "https://registry.yarnpkg.com/ssri/-/ssri-9.0.1.tgz#544d4c357a8d7b71a19700074b6883fcb4eae057" - integrity sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q== +ssri@^10.0.0: + version "10.0.6" + resolved "https://registry.yarnpkg.com/ssri/-/ssri-10.0.6.tgz#a8aade2de60ba2bce8688e3fa349bad05c7dc1e5" + integrity sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ== dependencies: - minipass "^3.1.1" + minipass "^7.0.3" -"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -623,14 +569,16 @@ ssri@^9.0.0: is-fullwidth-code-point "^3.0.0" strip-ansi "^6.0.1" -string_decoder@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" - integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== +string-width@^5.0.1, string-width@^5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" + integrity sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA== dependencies: - safe-buffer "~5.2.0" + eastasianwidth "^0.2.0" + emoji-regex "^9.2.2" + strip-ansi "^7.0.1" -strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: name strip-ansi-cjs version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" @@ -638,6 +586,13 @@ strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" +strip-ansi@^7.0.1: + version "7.1.0" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" + integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== + dependencies: + ansi-regex "^6.0.1" + tar@^6.1.11, tar@^6.1.2: version "6.2.1" resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.1.tgz#717549c541bc3c2af15751bea94b1dd068d4b03a" @@ -650,43 +605,51 @@ tar@^6.1.11, tar@^6.1.2: mkdirp "^1.0.3" yallist "^4.0.0" -unique-filename@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-2.0.1.tgz#e785f8675a9a7589e0ac77e0b5c34d2eaeac6da2" - integrity sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A== +unique-filename@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unique-filename/-/unique-filename-3.0.0.tgz#48ba7a5a16849f5080d26c760c86cf5cf05770ea" + integrity sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g== dependencies: - unique-slug "^3.0.0" + unique-slug "^4.0.0" -unique-slug@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-3.0.0.tgz#6d347cf57c8a7a7a6044aabd0e2d74e4d76dc7c9" - integrity sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w== +unique-slug@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/unique-slug/-/unique-slug-4.0.0.tgz#6bae6bb16be91351badd24cdce741f892a6532e3" + integrity sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ== dependencies: imurmurhash "^0.1.4" -util-deprecate@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw== - -which@^2.0.2: +which@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== dependencies: isexe "^2.0.0" -wide-align@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3" - integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg== +which@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/which/-/which-4.0.0.tgz#cd60b5e74503a3fbcfbf6cd6b4138a8bae644c1a" + integrity sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg== + dependencies: + isexe "^3.1.1" + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== dependencies: - string-width "^1.0.2 || 2 || 3 || 4" + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" -wrappy@1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" - integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== +wrap-ansi@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" + integrity sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ== + dependencies: + ansi-styles "^6.1.0" + string-width "^5.0.1" + strip-ansi "^7.0.1" yallist@^4.0.0: version "4.0.0" From 3c7cbd264e4313f236dbbad13303da54c23582d9 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 2 Jul 2024 14:50:50 +0200 Subject: [PATCH 0230/2222] Git - include unstaged changes when amending a commit and smart commit is enabled (#219675) --- extensions/git/src/commands.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index a5325033e77..fb53eee2976 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2104,7 +2104,7 @@ export class CommandCenter { } // no changes, and the user has not configured to commit all in this case - if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.all) { + if (!noUnstagedChanges && noStagedChanges && !enableSmartCommit && !opts.all && !opts.amend) { const suggestSmartCommit = config.get('suggestSmartCommit') === true; if (!suggestSmartCommit) { @@ -2128,10 +2128,9 @@ export class CommandCenter { } } - if (opts.all === undefined) { + // smart commit + if (enableSmartCommit && !opts.all) { opts = { ...opts, all: noStagedChanges }; - } else if (!opts.all && noStagedChanges) { - opts = { ...opts, all: true }; } } From 48e40d3c8e551bfe82aa7f02e84f5cd26c035c9a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Tue, 2 Jul 2024 16:32:59 +0200 Subject: [PATCH 0231/2222] =?UTF-8?q?SCM=20-=20=F0=9F=92=84=20adopt=20obse?= =?UTF-8?q?rvable=20in=20working=20sets=20(#219701)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../contrib/scm/browser/workingSet.ts | 90 +++++++++---------- 1 file changed, 41 insertions(+), 49 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/workingSet.ts b/src/vs/workbench/contrib/scm/browser/workingSet.ts index 3a023470ea2..274bd713910 100644 --- a/src/vs/workbench/contrib/scm/browser/workingSet.ts +++ b/src/vs/workbench/contrib/scm/browser/workingSet.ts @@ -3,9 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; -import { DisposableMap, DisposableStore } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableMap, DisposableStore } from 'vs/base/common/lifecycle'; +import { autorun, autorunWithStore } from 'vs/base/common/observable'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { observableConfigValue } from 'vs/platform/observable/common/platformObservableUtils'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { getProviderKey } from 'vs/workbench/contrib/scm/browser/util'; @@ -24,13 +25,13 @@ interface ISCMRepositoryWorkingSet { readonly editorWorkingSets: Map; } -export class SCMWorkingSetController implements IWorkbenchContribution { +export class SCMWorkingSetController extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.scmWorkingSets'; private _workingSets!: Map; + private _enabledConfig = observableConfigValue('scm.workingSets.enabled', false, this.configurationService); + private readonly _repositoryDisposables = new DisposableMap(); - private readonly _scmServiceDisposables = new DisposableStore(); - private readonly _disposables = new DisposableStore(); constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @@ -39,69 +40,61 @@ export class SCMWorkingSetController implements IWorkbenchContribution { @IStorageService private readonly storageService: IStorageService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { - const onDidChangeConfiguration = Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('scm.workingSets.enabled'), this._disposables); - this._disposables.add(Event.runAndSubscribe(onDidChangeConfiguration, () => this._onDidChangeConfiguration())); - } - - private _onDidChangeConfiguration(): void { - if (!this.configurationService.getValue('scm.workingSets.enabled')) { - this.storageService.remove('scm.workingSets', StorageScope.WORKSPACE); + super(); - this._scmServiceDisposables.clear(); - this._repositoryDisposables.clearAndDisposeAll(); - - return; - } + this._store.add(autorunWithStore((reader, store) => { + if (!this._enabledConfig.read(reader)) { + this.storageService.remove('scm.workingSets', StorageScope.WORKSPACE); + this._repositoryDisposables.clearAndDisposeAll(); + return; + } - this._workingSets = this._loadWorkingSets(); + this._workingSets = this._loadWorkingSets(); - this.scmService.onDidAddRepository(this._onDidAddRepository, this, this._scmServiceDisposables); - this.scmService.onDidRemoveRepository(this._onDidRemoveRepository, this, this._scmServiceDisposables); + this.scmService.onDidAddRepository(this._onDidAddRepository, this, store); + this.scmService.onDidRemoveRepository(this._onDidRemoveRepository, this, store); - for (const repository of this.scmService.repositories) { - this._onDidAddRepository(repository); - } + for (const repository of this.scmService.repositories) { + this._onDidAddRepository(repository); + } + })); } private _onDidAddRepository(repository: ISCMRepository): void { const disposables = new DisposableStore(); - disposables.add(Event.runAndSubscribe(repository.provider.onDidChangeHistoryProvider, () => { - if (!repository.provider.historyProvider) { + disposables.add(autorun(async reader => { + const historyProvider = repository.provider.historyProviderObs.read(reader); + const currentHistoryItemGroupId = historyProvider?.currentHistoryItemGroupObs.read(reader)?.id; + + if (!currentHistoryItemGroupId) { return; } - disposables.add(Event.runAndSubscribe(repository.provider.historyProvider.onDidChangeCurrentHistoryItemGroup, async () => { - if (!repository.provider.historyProvider?.currentHistoryItemGroup?.id) { - return; - } - - const providerKey = getProviderKey(repository.provider); - const currentHistoryItemGroupId = repository.provider.historyProvider.currentHistoryItemGroup.id; - const repositoryWorkingSets = this._workingSets.get(providerKey); + const providerKey = getProviderKey(repository.provider); + const repositoryWorkingSets = this._workingSets.get(providerKey); - if (!repositoryWorkingSets) { - this._workingSets.set(providerKey, { currentHistoryItemGroupId, editorWorkingSets: new Map() }); - return; - } + if (!repositoryWorkingSets) { + this._workingSets.set(providerKey, { currentHistoryItemGroupId, editorWorkingSets: new Map() }); + return; + } - if (repositoryWorkingSets.currentHistoryItemGroupId === currentHistoryItemGroupId) { - return; - } + // Editors for the current working set are automatically restored + if (repositoryWorkingSets.currentHistoryItemGroupId === currentHistoryItemGroupId) { + return; + } - // Save the working set - this._saveWorkingSet(providerKey, currentHistoryItemGroupId, repositoryWorkingSets); + // Save the working set + this._saveWorkingSet(providerKey, currentHistoryItemGroupId, repositoryWorkingSets); - // Restore the working set - await this._restoreWorkingSet(providerKey, currentHistoryItemGroupId); - })); + // Restore the working set + await this._restoreWorkingSet(providerKey, currentHistoryItemGroupId); })); this._repositoryDisposables.set(repository, disposables); } private _onDidRemoveRepository(repository: ISCMRepository): void { - this._workingSets.delete(getProviderKey(repository.provider)); this._repositoryDisposables.deleteAndDispose(repository); } @@ -159,9 +152,8 @@ export class SCMWorkingSetController implements IWorkbenchContribution { } } - dispose(): void { + override dispose(): void { this._repositoryDisposables.dispose(); - this._scmServiceDisposables.dispose(); - this._disposables.dispose(); + super.dispose(); } } From cacaccb42fc85c8622c444c6ff4da2f1e8c3211a Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 2 Jul 2024 08:56:00 -0700 Subject: [PATCH 0232/2222] net: gracefully disconnect better from remote (#218972) * net: gracefully disconnect better from remote This adds in a step to gracefully disconnect (send a "disconnect" message and then await the flush) before closing the workbench. In some cases, like Remote - SSH, sending messages is more async than others. In the exec server connection we handle a `zlib.flate` stream to compress data to and from the VS Code server, and its API is asynchronous, so this lets us ensure the stream is drained before giving the go-ahead to close up shop This lifecycle phase is a little awkward and I ended it putting it directly in the lifecycle service: we don't want to do this in `onWillShutdown` because some contributions want to save data to the remote workspace, and if shutdown is vetoed it would leave a broken state. But `onDidShutdown` is synchronous and already depended upon by several other points, and changing that also felt a bit risky. cc @alexdima Refs #211462, will require some small adoption in Remote - SSH to close. * undo unrelated change * add priority to joiners * some cleanup * cleanup * tweaks * `runWithFakedTimers` * comment * :lipstick: --------- Co-authored-by: Benjamin Pasero --- src/vs/base/parts/ipc/common/ipc.net.ts | 12 ++++-- .../common/abstractExtensionService.ts | 12 +++++- .../services/lifecycle/common/lifecycle.ts | 42 +++++++++++++++++-- .../electron-sandbox/lifecycleService.ts | 31 +++++++++----- .../electron-sandbox/lifecycleService.test.ts | 33 ++++++++++++++- .../common/abstractRemoteAgentService.ts | 13 ++++++ .../remote/common/remoteAgentService.ts | 6 +++ .../test/browser/workbenchTestServices.ts | 7 ++-- 8 files changed, 135 insertions(+), 21 deletions(-) diff --git a/src/vs/base/parts/ipc/common/ipc.net.ts b/src/vs/base/parts/ipc/common/ipc.net.ts index 1fe8ee07800..39ba4f16236 100644 --- a/src/vs/base/parts/ipc/common/ipc.net.ts +++ b/src/vs/base/parts/ipc/common/ipc.net.ts @@ -597,6 +597,8 @@ export class Client extends IPCClient { override dispose(): void { super.dispose(); const socket = this.protocol.getSocket(); + // should be sent gracefully with a .flush(), but try to send it out as a + // last resort here if nothing else: this.protocol.sendDisconnect(); this.protocol.dispose(); socket.end(); @@ -808,6 +810,7 @@ export interface PersistentProtocolOptions { export class PersistentProtocol implements IMessagePassingProtocol { private _isReconnecting: boolean; + private _didSendDisconnect?: boolean; private _outgoingUnackMsg: Queue; private _outgoingMsgId: number; @@ -910,9 +913,12 @@ export class PersistentProtocol implements IMessagePassingProtocol { } sendDisconnect(): void { - const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer()); - this._socketWriter.write(msg); - this._socketWriter.flush(); + if (!this._didSendDisconnect) { + this._didSendDisconnect = true; + const msg = new ProtocolMessage(ProtocolMessageType.Disconnect, 0, 0, getEmptyBuffer()); + this._socketWriter.write(msg); + this._socketWriter.flush(); + } } sendPause(): void { diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 731a48bf5b0..be9bb04b104 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -51,7 +51,7 @@ import { ExtensionMessageCollector, ExtensionPoint, ExtensionsRegistry, IExtensi import { LazyCreateExtensionHostManager } from 'vs/workbench/services/extensions/common/lazyCreateExtensionHostManager'; import { ResponsiveState } from 'vs/workbench/services/extensions/common/rpcProtocol'; import { IExtensionActivationHost as IWorkspaceContainsActivationHost, checkActivateWorkspaceContainsExtension, checkGlobFileExists } from 'vs/workbench/services/extensions/common/workspaceContains'; -import { ILifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { ILifecycleService, WillShutdownJoinerOrder } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IExtensionHostExitInfo, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; const hasOwnProperty = Object.hasOwnProperty; @@ -195,6 +195,16 @@ export abstract class AbstractExtensionService extends Disposable implements IEx } })); + this._register(this._lifecycleService.onWillShutdown(event => { + if (this._remoteAgentService.getConnection()) { + event.join(() => this._remoteAgentService.endConnection(), { + id: 'join.disconnectRemote', + label: nls.localize('disconnectRemote', "Disconnect Remote Agent"), + order: WillShutdownJoinerOrder.Last // after others have joined that might depend on a remote connection + }); + } + })); + this._register(this._lifecycleService.onDidShutdown(() => { // We need to disconnect the management connection before killing the local extension host. // Otherwise, the local extension host might terminate the underlying tunnel before the diff --git a/src/vs/workbench/services/lifecycle/common/lifecycle.ts b/src/vs/workbench/services/lifecycle/common/lifecycle.ts index 7a795423d5d..ec5c8098e40 100644 --- a/src/vs/workbench/services/lifecycle/common/lifecycle.ts +++ b/src/vs/workbench/services/lifecycle/common/lifecycle.ts @@ -65,9 +65,34 @@ export interface BeforeShutdownErrorEvent { readonly error: Error; } +export enum WillShutdownJoinerOrder { + + /** + * Joiners to run before the `Last` joiners. This is the default order and best for + * most cases. You can be sure that services are still functional at this point. + */ + Default = 1, + + /** + * The joiners to run last. This should ONLY be used in rare cases when you have no + * dependencies to workbench services or state. The workbench may be in a state where + * resources can no longer be accessed or changed. + */ + Last +} + export interface IWillShutdownEventJoiner { - id: string; - label: string; + readonly id: string; + readonly label: string; + readonly order?: WillShutdownJoinerOrder; +} + +export interface IWillShutdownEventDefaultJoiner extends IWillShutdownEventJoiner { + readonly order?: WillShutdownJoinerOrder.Default; +} + +export interface IWillShutdownEventLastJoiner extends IWillShutdownEventJoiner { + readonly order: WillShutdownJoinerOrder.Last; } /** @@ -95,10 +120,21 @@ export interface WillShutdownEvent { * Allows to join the shutdown. The promise can be a long running operation but it * will block the application from closing. * + * @param promise the promise to join the shutdown event. + * @param joiner to identify the join operation in case it takes very long or never + * completes. + */ + join(promise: Promise, joiner: IWillShutdownEventDefaultJoiner): void; + + /** + * Allows to join the shutdown at the end. The promise can be a long running operation but it + * will block the application from closing. + * + * @param promiseFn the promise to join the shutdown event. * @param joiner to identify the join operation in case it takes very long or never * completes. */ - join(promise: Promise, joiner: IWillShutdownEventJoiner): void; + join(promiseFn: (() => Promise), joiner: IWillShutdownEventLastJoiner): void; /** * Allows to access the joiners that have not finished joining this event. diff --git a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts index 9d4d9418201..0434e6ded59 100644 --- a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; -import { ShutdownReason, ILifecycleService, IWillShutdownEventJoiner } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { ShutdownReason, ILifecycleService, IWillShutdownEventJoiner, WillShutdownJoinerOrder } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ipcRenderer } from 'vs/base/parts/sandbox/electron-sandbox/globals'; import { ILogService } from 'vs/platform/log/common/log'; @@ -155,19 +155,24 @@ export class NativeLifecycleService extends AbstractLifecycleService { protected async handleWillShutdown(reason: ShutdownReason): Promise { const joiners: Promise[] = []; + const lastJoiners: (() => Promise)[] = []; const pendingJoiners = new Set(); const cts = new CancellationTokenSource(); - this._onWillShutdown.fire({ reason, token: cts.token, joiners: () => Array.from(pendingJoiners.values()), - join(promise, joiner) { - joiners.push(promise); - - // Track promise completion + join(promiseOrPromiseFn, joiner) { pendingJoiners.add(joiner); - promise.finally(() => pendingJoiners.delete(joiner)); + + if (joiner.order === WillShutdownJoinerOrder.Last) { + const promiseFn = typeof promiseOrPromiseFn === 'function' ? promiseOrPromiseFn : () => promiseOrPromiseFn; + lastJoiners.push(() => promiseFn().finally(() => pendingJoiners.delete(joiner))); + } else { + const promise = typeof promiseOrPromiseFn === 'function' ? promiseOrPromiseFn() : promiseOrPromiseFn; + promise.finally(() => pendingJoiners.delete(joiner)); + joiners.push(promise); + } }, force: () => { cts.dispose(true); @@ -181,10 +186,16 @@ export class NativeLifecycleService extends AbstractLifecycleService { try { await raceCancellation(Promises.settled(joiners), cts.token); } catch (error) { - this.logService.error(`[lifecycle]: Error during will-shutdown phase (error: ${toErrorMessage(error)})`); // this error will not prevent the shutdown - } finally { - longRunningWillShutdownWarning.dispose(); + this.logService.error(`[lifecycle]: Error during will-shutdown phase in default joiners (error: ${toErrorMessage(error)})`); + } + + try { + await raceCancellation(Promises.settled(lastJoiners.map(lastJoiner => lastJoiner())), cts.token); + } catch (error) { + this.logService.error(`[lifecycle]: Error during will-shutdown phase in last joiners (error: ${toErrorMessage(error)})`); } + + longRunningWillShutdownWarning.dispose(); } shutdown(): Promise { diff --git a/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts b/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts index 0d71a208693..66d4f8b3c71 100644 --- a/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts +++ b/src/vs/workbench/services/lifecycle/test/electron-sandbox/lifecycleService.test.ts @@ -4,9 +4,11 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; +import { timeout } from 'vs/base/common/async'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { runWithFakedTimers } from 'vs/base/test/common/timeTravelScheduler'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; -import { ShutdownReason } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { ShutdownReason, WillShutdownJoinerOrder } from 'vs/workbench/services/lifecycle/common/lifecycle'; import { NativeLifecycleService } from 'vs/workbench/services/lifecycle/electron-sandbox/lifecycleService'; import { workbenchInstantiationService } from 'vs/workbench/test/electron-sandbox/workbenchTestServices'; @@ -155,5 +157,34 @@ suite('Lifecycleservice', function () { assert.strictEqual(joinCalled, true); }); + test('onWillShutdown - join order', async function () { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + const order: string[] = []; + + disposables.add(lifecycleService.onWillShutdown(e => { + e.join(async () => { + order.push('disconnect start'); + await timeout(1); + order.push('disconnect end'); + }, { id: 'test', label: 'test', order: WillShutdownJoinerOrder.Last }); + + e.join((async () => { + order.push('default start'); + await timeout(1); + order.push('default end'); + })(), { id: 'test', label: 'test', order: WillShutdownJoinerOrder.Default }); + })); + + await lifecycleService.testHandleWillShutdown(ShutdownReason.QUIT); + + assert.deepStrictEqual(order, [ + 'default start', + 'default end', + 'disconnect start', + 'disconnect end' + ]); + }); + }); + ensureNoDisposablesAreLeakedInTestSuite(); }); diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index 9550c6a76d6..fe802daf50b 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -114,6 +114,13 @@ export abstract class AbstractRemoteAgentService extends Disposable implements I ); } + async endConnection(): Promise { + if (this._connection) { + await this._connection.end(); + this._connection.dispose(); + } + } + private _withChannel(callback: (channel: IChannel, connection: IRemoteAgentConnection) => Promise, fallback: R): Promise { const connection = this.getConnection(); if (!connection) { @@ -159,6 +166,8 @@ class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection this._connection = null; } + end: () => Promise = () => Promise.resolve(); + getChannel(channelName: string): T { return getDelayedChannel(this._getOrCreateConnection().then(c => c.getChannel(channelName))); } @@ -222,6 +231,10 @@ class RemoteAgentConnection extends Disposable implements IRemoteAgentConnection connection.protocol.onDidDispose(() => { connection.dispose(); }); + this.end = () => { + connection.protocol.sendDisconnect(); + return connection.protocol.drain(); + }; this._register(connection.onDidStateChange(e => this._onDidStateChange.fire(e))); return connection.client; } diff --git a/src/vs/workbench/services/remote/common/remoteAgentService.ts b/src/vs/workbench/services/remote/common/remoteAgentService.ts index db2d598dd8d..e3849b607d4 100644 --- a/src/vs/workbench/services/remote/common/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/remoteAgentService.ts @@ -37,6 +37,11 @@ export interface IRemoteAgentService { */ getRoundTripTime(): Promise; + /** + * Gracefully ends the current connection, if any. + */ + endConnection(): Promise; + getDiagnosticInfo(options: IDiagnosticInfoOptions): Promise; updateTelemetryLevel(telemetryLevel: TelemetryLevel): Promise; logTelemetry(eventName: string, data?: ITelemetryData): Promise; @@ -54,6 +59,7 @@ export interface IRemoteAgentConnection { readonly onReconnecting: Event; readonly onDidStateChange: Event; + end(): Promise; dispose(): void; getChannel(channelName: string): T; withChannel(channelName: string, callback: (channel: T) => Promise): Promise; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 5680778d5c8..46cb0e01277 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -1364,7 +1364,7 @@ export class TestLifecycleService extends Disposable implements ILifecycleServic this._onWillShutdown.fire({ join: p => { - this.shutdownJoiners.push(p); + this.shutdownJoiners.push(typeof p === 'function' ? p() : p); }, joiners: () => [], force: () => { /* No-Op in tests */ }, @@ -1405,8 +1405,8 @@ export class TestWillShutdownEvent implements WillShutdownEvent { reason = ShutdownReason.CLOSE; token = CancellationToken.None; - join(promise: Promise, joiner: IWillShutdownEventJoiner): void { - this.value.push(promise); + join(promise: Promise | (() => Promise), joiner: IWillShutdownEventJoiner): void { + this.value.push(typeof promise === 'function' ? promise() : promise); } force() { /* No-Op in tests */ } @@ -2117,6 +2117,7 @@ export class TestRemoteAgentService implements IRemoteAgentService { async logTelemetry(eventName: string, data?: ITelemetryData): Promise { } async flushTelemetry(): Promise { } async getRoundTripTime(): Promise { return undefined; } + async endConnection(): Promise { } } export class TestRemoteExtensionsScannerService implements IRemoteExtensionsScannerService { From a375a9bb4ffa26595bf61e07cdfa486f10833dbb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 2 Jul 2024 18:40:07 +0200 Subject: [PATCH 0233/2222] Multi tab: cannot drop multiple tabs into separate workspace window (#219671) (fix #212885) --- src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 4ea2a840df4..637d3486046 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -1106,7 +1106,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { } // Apply some datatransfer types to allow for dragging the element outside of the application - this.doFillResourceDataTransfers([editor], e, isNewWindowOperation); + this.doFillResourceDataTransfers(selectedEditors, e, isNewWindowOperation); scheduleAtNextAnimationFrame(getWindow(this.parent), () => this.updateDropFeedback(tab, false, e, tabIndex)); }, From ff6e9b259c811bebfc8b5c549b0499260dcfec14 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Tue, 2 Jul 2024 19:19:49 +0200 Subject: [PATCH 0234/2222] Remove console.log (#219715) remove console.log --- src/vs/workbench/browser/contextkeys.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index ccfe454ba34..cffce2ce267 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -267,7 +267,6 @@ export class WorkbenchContextKeysHandler extends Disposable { // Context keys depending on the state of the editor group itself private updateActiveEditorGroupContextKeys(): void { - console.log('active group'); if (!this.editorService.activeEditor) { this.activeEditorGroupEmpty.set(true); } else { @@ -283,7 +282,6 @@ export class WorkbenchContextKeysHandler extends Disposable { // Context keys depending on the state of other editor groups private updateEditorGroupsContextKeys(): void { - console.log('all groups'); const groupCount = this.editorGroupService.count; if (groupCount > 1) { this.multipleEditorGroupsContext.set(true); From 33efeef884a64114e0b997c69f3b611a8ba24ecc Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Tue, 2 Jul 2024 10:58:36 -0700 Subject: [PATCH 0235/2222] additions --- src/vs/platform/actions/common/actions.ts | 2 +- .../testing/browser/testingExplorerView.ts | 17 ++++++++--------- .../actions/common/menusExtensionPoint.ts | 6 +++--- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 5ae47ed133e..ec2ccb4f87f 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -138,7 +138,7 @@ export class MenuId { static readonly StickyScrollContext = new MenuId('StickyScrollContext'); static readonly TestItem = new MenuId('TestItem'); static readonly TestItemGutter = new MenuId('TestItemGutter'); - static readonly TestPythonConfigMenu = new MenuId('TestPythonConfigMenu'); + static readonly TestProfilesContext = new MenuId('TestProfilesContext'); static readonly TestMessageContext = new MenuId('TestMessageContext'); static readonly TestMessageContent = new MenuId('TestMessageContent'); static readonly TestPeekElement = new MenuId('TestPeekElement'); diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 5adaefd1412..22a25823255 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -355,18 +355,17 @@ export class TestingExplorerView extends ViewPane { const menuActions: IAction[] = []; const key = this.contextKeyService.createOverlay([]); - const menu = this.menuService.createMenu(MenuId.TestPythonConfigMenu, key); - const actions = menu.getActions({ renderShortTitle: true }).flatMap(entry => entry[1]); - if (actions.length > 0) { - // fill if there are any actions - try { - createAndFillInContextMenuActions(menu, undefined, menuActions); - } finally { - menu.dispose(); - } + const menu = this.menuService.createMenu(MenuId.TestProfilesContext, key); + // fill if there are any actions + try { + createAndFillInContextMenuActions(menu, undefined, menuActions); + } finally { + menu.dispose(); } + + const postActions: IAction[] = []; if (profileActions.length > 1) { postActions.push(new Action( diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index a8cf20f1132..2b0dbd7442c 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -344,9 +344,9 @@ const apiMenus: IAPIMenu[] = [ description: localize('testing.item.gutter.title', "The menu for a gutter decoration for a test item"), }, { - key: 'testing/pythonConfig', - id: MenuId.TestPythonConfigMenu, - description: localize('testing.pythonConfig.title', "The menu for configuring Python tests"), + key: 'testing/profiles/context', + id: MenuId.TestProfilesContext, + description: localize('testing.profiles.context.title', "The menu for configuring testing profiles."), }, { key: 'testing/item/result', From 139fa9d01c6e65bccb1fbc8dc2fb2c73ea77c12e Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Tue, 2 Jul 2024 12:14:02 -0700 Subject: [PATCH 0236/2222] chore: use ip-address instead of ip (#219760) --- remote/yarn.lock | 48 ++++++++++++++++++-------- yarn.lock | 88 ++++++++++++++++++++++++++++++------------------ 2 files changed, 90 insertions(+), 46 deletions(-) diff --git a/remote/yarn.lock b/remote/yarn.lock index c8e84fc81e1..54318a1a762 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -171,6 +171,13 @@ agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" +agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -332,10 +339,13 @@ ini@~1.3.0: resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== -ip@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" - integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" is-extglob@^2.1.1: version "2.1.1" @@ -359,6 +369,11 @@ js-base64@^3.7.5: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79" integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jschardet@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" @@ -570,22 +585,27 @@ smart-buffer@^4.2.0: integrity sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg== socks-proxy-agent@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120" - integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ== + version "8.0.4" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== dependencies: - agent-base "^7.0.1" + agent-base "^7.1.1" debug "^4.3.4" - socks "^2.7.1" + socks "^2.8.3" -socks@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== +socks@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== dependencies: - ip "^2.0.0" + ip-address "^9.0.5" smart-buffer "^4.2.0" +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" diff --git a/yarn.lock b/yarn.lock index c445f43d995..3ed17cd0af6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2099,6 +2099,13 @@ agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: dependencies: debug "^4.3.4" +agent-base@^7.1.1: + version "7.1.1" + resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.1.tgz#bdbded7dfb096b751a2a087eeeb9664725b2e317" + integrity sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA== + dependencies: + debug "^4.3.4" + ajv-formats@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/ajv-formats/-/ajv-formats-2.1.1.tgz#6e669400659eb74973bbf2e33327180a0996b520" @@ -2201,11 +2208,6 @@ ansi-regex@^4.1.0: resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997" integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg== -ansi-regex@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75" - integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg== - ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -5729,10 +5731,13 @@ invert-kv@^2.0.0: resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02" integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA== -ip@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/ip/-/ip-2.0.1.tgz#e8f3595d33a3ea66490204234b77636965307105" - integrity sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ== +ip-address@^9.0.5: + version "9.0.5" + resolved "https://registry.yarnpkg.com/ip-address/-/ip-address-9.0.5.tgz#117a960819b08780c3bd1f14ef3c1cc1d3f3ea5a" + integrity sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g== + dependencies: + jsbn "1.1.0" + sprintf-js "^1.1.3" is-absolute@^1.0.0: version "1.0.0" @@ -6234,6 +6239,11 @@ js-yaml@^3.13.0: argparse "^1.0.7" esprima "^4.0.0" +jsbn@1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" + integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== + jschardet@3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" @@ -9095,20 +9105,20 @@ snapdragon@^0.8.1: use "^3.1.0" socks-proxy-agent@^8.0.1: - version "8.0.1" - resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.1.tgz#ffc5859a66dac89b0c4dab90253b96705f3e7120" - integrity sha512-59EjPbbgg8U3x62hhKOFVAmySQUcfRQ4C7Q/D5sEHnZTQRrQlNKINks44DMR1gwXp0p4LaVIeccX2KHTTcHVqQ== + version "8.0.4" + resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz#9071dca17af95f483300316f4b063578fa0db08c" + integrity sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw== dependencies: - agent-base "^7.0.1" + agent-base "^7.1.1" debug "^4.3.4" - socks "^2.7.1" + socks "^2.8.3" -socks@^2.7.1: - version "2.7.1" - resolved "https://registry.yarnpkg.com/socks/-/socks-2.7.1.tgz#d8e651247178fde79c0663043e07240196857d55" - integrity sha512-7maUZy1N7uo6+WVEX6psASxtNlKaNVMlGQKkG/63nEDdLOWNbiUMoLK7X4uYoLhQstau72mLgfEWcXcwsaHbYQ== +socks@^2.8.3: + version "2.8.3" + resolved "https://registry.yarnpkg.com/socks/-/socks-2.8.3.tgz#1ebd0f09c52ba95a09750afe3f3f9f724a800cb5" + integrity sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw== dependencies: - ip "^2.0.0" + ip-address "^9.0.5" smart-buffer "^4.2.0" source-map-js@^1.0.1: @@ -9247,6 +9257,11 @@ sprintf-js@^1.1.2: resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.2.tgz#da1765262bf8c0f571749f2ad6c26300207ae673" integrity sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug== +sprintf-js@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.1.3.tgz#4914b903a2f8b685d17fdf78a70e917e872e444a" + integrity sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -9340,7 +9355,7 @@ streamx@^2.15.0: fast-fifo "^1.1.0" queue-tick "^1.0.1" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9375,14 +9390,14 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5" - integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg== +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== dependencies: emoji-regex "^8.0.0" is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.0" + strip-ansi "^6.0.1" string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" @@ -9437,7 +9452,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9465,12 +9480,12 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0: - version "6.0.0" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" - integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w== +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== dependencies: - ansi-regex "^5.0.0" + ansi-regex "^5.0.1" strip-ansi@^7.0.1: version "7.1.0" @@ -10627,7 +10642,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10662,6 +10677,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 0cdc9b33419a39557fc6109ad629f196cd92439e Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Tue, 2 Jul 2024 21:25:39 +0200 Subject: [PATCH 0237/2222] fix: memory leak in extension tabs (#219726) --- .../contrib/extensions/browser/extensionFeaturesTab.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts b/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts index 42680a4ba1f..794127b7f6a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts @@ -178,10 +178,10 @@ export class ExtensionFeaturesTab extends Themable { return; } - const splitView = new SplitView(this.domNode, { + const splitView = this._register(new SplitView(this.domNode, { orientation: Orientation.HORIZONTAL, proportionalLayout: true - }); + })); this.layoutParticipants.push({ layout: (height: number, width: number) => { splitView.el.style.height = `${height - 14}px`; @@ -190,7 +190,7 @@ export class ExtensionFeaturesTab extends Themable { }); const featuresListContainer = $('.features-list-container'); - const list = this.createFeaturesList(featuresListContainer); + const list = this._register(this.createFeaturesList(featuresListContainer)); list.splice(0, list.length, features); const featureViewContainer = $('.feature-view-container'); From e3d7449068e534e083d656c15c0061aef8710630 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 2 Jul 2024 14:34:46 -0700 Subject: [PATCH 0238/2222] Fix a few more linting errors (#219803) --- src/vs/editor/contrib/codelens/browser/codeLensCache.ts | 4 ++-- src/vs/editor/contrib/inlineEdits/browser/commands.ts | 2 +- .../editor/test/browser/widget/observableCodeEditor.test.ts | 4 ++-- src/vs/platform/product/common/product.ts | 2 +- src/vs/workbench/api/browser/mainThreadNotebook.ts | 2 +- src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts | 3 +-- .../contrib/bulkEdit/browser/preview/bulkEditPane.ts | 4 ++-- src/vscode-dts/vscode.proposed.fileComments.d.ts | 2 +- 8 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts index 67fa3b8009c..47fc3260b1f 100644 --- a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts @@ -71,8 +71,8 @@ export class CodeLensCache implements ICodeLensCache { put(model: ITextModel, data: CodeLensModel): void { // create a copy of the model that is without command-ids // but with comand-labels - const copyItems = data.lenses.map(item => { - return { + const copyItems = data.lenses.map((item): CodeLens => { + return { range: item.symbol.range, command: item.symbol.command && { id: '', title: item.symbol.command?.title }, }; diff --git a/src/vs/editor/contrib/inlineEdits/browser/commands.ts b/src/vs/editor/contrib/inlineEdits/browser/commands.ts index c5ce0e90296..ec7255fd9f0 100644 --- a/src/vs/editor/contrib/inlineEdits/browser/commands.ts +++ b/src/vs/editor/contrib/inlineEdits/browser/commands.ts @@ -18,7 +18,7 @@ import { MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -function labelAndAlias(str: nls.ILocalizedString): { label: string, alias: string } { +function labelAndAlias(str: nls.ILocalizedString): { label: string; alias: string } { return { label: str.value, alias: str.original, diff --git a/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts b/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts index 5ae9f3d81f5..0a104966470 100644 --- a/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts +++ b/src/vs/editor/test/browser/widget/observableCodeEditor.test.ts @@ -18,7 +18,7 @@ suite("CodeEditorWidget", () => { ensureNoDisposablesAreLeakedInTestSuite(); function withTestFixture( - cb: (args: { editor: ICodeEditor; viewModel: ViewModel; log: Log; derived: IObservable; }) => void + cb: (args: { editor: ICodeEditor; viewModel: ViewModel; log: Log; derived: IObservable }) => void ) { withEditorSetupTestFixture(undefined, cb); } @@ -27,7 +27,7 @@ suite("CodeEditorWidget", () => { preSetupCallback: | ((editor: ICodeEditor, disposables: DisposableStore) => void) | undefined, - cb: (args: { editor: ICodeEditor; viewModel: ViewModel; log: Log; derived: IObservable; }) => void + cb: (args: { editor: ICodeEditor; viewModel: ViewModel; log: Log; derived: IObservable }) => void ) { withTestCodeEditor("hello world", {}, (editor, viewModel) => { const disposables = new DisposableStore(); diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 9d3f34e443d..58278d978f9 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -53,7 +53,7 @@ else if (globalThis._VSCODE_PRODUCT_JSON && globalThis._VSCODE_PACKAGE_JSON) { else { // Built time configuration (do NOT modify) - product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as IProductConfiguration; + product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as any; // Running out of sources if (Object.keys(product).length === 0) { diff --git a/src/vs/workbench/api/browser/mainThreadNotebook.ts b/src/vs/workbench/api/browser/mainThreadNotebook.ts index c036901f907..b50fee3527c 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebook.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebook.ts @@ -104,7 +104,7 @@ export class MainThreadNotebooks implements MainThreadNotebookShape { }; } - const thisPriorityInfo = coalesce([{ isFromSettings: false, filenamePatterns: includes }, ...allPriorityInfo.get(viewType) ?? []]); + const thisPriorityInfo = coalesce([{ isFromSettings: false, filenamePatterns: includes }, ...allPriorityInfo.get(viewType) ?? []]); const otherEditorsPriorityInfo = Array.from(allPriorityInfo.keys()) .flatMap(key => { if (key !== viewType) { diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index e2fefb174e6..6bba2fcc4c5 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -34,7 +34,6 @@ import { IEditorGroupView } from 'vs/workbench/browser/parts/editor/editor'; import { PixelRatio } from 'vs/base/browser/pixelRatio'; import { ILabelService } from 'vs/platform/label/common/label'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { IOutline } from 'vs/workbench/services/outline/browser/outline'; import { registerIcon } from 'vs/platform/theme/common/iconRegistry'; import { Codicon } from 'vs/base/common/codicons'; @@ -85,7 +84,7 @@ class OutlineItem extends BreadcrumbsItem { } const template = renderer.renderTemplate(container); - renderer.renderElement(>{ + renderer.renderElement({ element, children: [], depth: 0, diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 36ef150418a..3c68921a8e5 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -22,7 +22,7 @@ import { IContextMenuService } from 'vs/platform/contextview/browser/contextView import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { ResourceLabels, IResourceLabelsContainer } from 'vs/workbench/browser/labels'; +import { ResourceLabels } from 'vs/workbench/browser/labels'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { MenuId } from 'vs/platform/actions/common/actions'; import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; @@ -120,7 +120,7 @@ export class BulkEditPane extends ViewPane { const resourceLabels = this._instaService.createInstance( ResourceLabels, - { onDidChangeVisibility: this.onDidChangeBodyVisibility } + { onDidChangeVisibility: this.onDidChangeBodyVisibility } ); this._disposables.add(resourceLabels); diff --git a/src/vscode-dts/vscode.proposed.fileComments.d.ts b/src/vscode-dts/vscode.proposed.fileComments.d.ts index 8f8c7d2d943..96e9b181bc6 100644 --- a/src/vscode-dts/vscode.proposed.fileComments.d.ts +++ b/src/vscode-dts/vscode.proposed.fileComments.d.ts @@ -26,7 +26,7 @@ declare module 'vscode' { /** * The ranges which allow new comment threads creation. */ - ranges?: Range[] + ranges?: Range[]; } export interface CommentController { From 39c4f66a4638f5f6e619ee97b10532cf3c93e499 Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Tue, 2 Jul 2024 16:00:01 -0700 Subject: [PATCH 0239/2222] add current group as menu context --- .../contrib/testing/browser/testingExplorerView.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index 22a25823255..d999c5e3301 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -353,8 +353,18 @@ export class TestingExplorerView extends ViewPane { } const menuActions: IAction[] = []; - - const key = this.contextKeyService.createOverlay([]); + const contextKeys: [string, unknown][] = []; + // allow extension author to define context for when to show the test menu actions for run or debug menus + if (group === TestRunProfileBitset.Run) { + contextKeys.push(['testing.profile.context.group', 'run']); + } + if (group === TestRunProfileBitset.Debug) { + contextKeys.push(['testing.profile.context.group', 'debug']); + } + if (group === TestRunProfileBitset.Coverage) { + contextKeys.push(['testing.profile.context.group', 'coverage']); + } + const key = this.contextKeyService.createOverlay(contextKeys); const menu = this.menuService.createMenu(MenuId.TestProfilesContext, key); // fill if there are any actions From 6ab3a15691ff9a1aa619a53d9dcd29c0b53d76a1 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Tue, 2 Jul 2024 17:07:12 -0700 Subject: [PATCH 0240/2222] testing: simplify tests when actioning gutter items (#219809) This adds a function to "simplify" tests by preferring to pass parent items rather than child items when running tests via test gutter decorations, when all children have been included. For https://github.com/microsoft/vscode-python/issues/21151 --- src/vs/base/common/prefixTree.ts | 5 + .../testing/browser/testingDecorations.ts | 4 +- .../contrib/testing/common/testId.ts | 2 +- .../contrib/testing/common/testService.ts | 62 +++++++++- .../testing/test/common/testService.test.ts | 108 ++++++++++++++++++ .../contrib/testing/test/common/testStubs.ts | 20 ++++ 6 files changed, 197 insertions(+), 4 deletions(-) create mode 100644 src/vs/workbench/contrib/testing/test/common/testService.test.ts diff --git a/src/vs/base/common/prefixTree.ts b/src/vs/base/common/prefixTree.ts index 8e839e2b4ff..53f02964d36 100644 --- a/src/vs/base/common/prefixTree.ts +++ b/src/vs/base/common/prefixTree.ts @@ -32,6 +32,11 @@ export class WellDefinedPrefixTree { return this.root.children?.values() || Iterable.empty(); } + /** Gets the top-level nodes of the tree */ + public get entries(): Iterable<[string, IPrefixTreeNode]> { + return this.root.children?.entries() || Iterable.empty(); + } + /** * Inserts a new value in the prefix tree. * @param onNode - called for each node as we descend to the insertion point, diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index 9afbdafa192..dd48c16b941 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -47,7 +47,7 @@ import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; import { ITestResult, LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; -import { ITestService, getContextForTestItem, testsInFile } from 'vs/workbench/contrib/testing/common/testService'; +import { ITestService, getContextForTestItem, simplifyTestsToExecute, testsInFile } from 'vs/workbench/contrib/testing/common/testService'; import { IRichLocation, ITestMessage, ITestRunProfile, IncrementalTestCollectionItem, InternalTestItem, TestDiffOpType, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset } from 'vs/workbench/contrib/testing/common/testTypes'; import { ITestDecoration as IPublicTestDecoration, ITestingDecorationsService, TestDecorations } from 'vs/workbench/contrib/testing/common/testingDecorations'; import { ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener'; @@ -806,7 +806,7 @@ abstract class RunTestDecoration { protected runWith(profile: TestRunProfileBitset) { return this.testService.runTests({ - tests: this.tests.map(({ test }) => test), + tests: simplifyTestsToExecute(this.testService.collection, this.tests.map(({ test }) => test)), group: profile, }); } diff --git a/src/vs/workbench/contrib/testing/common/testId.ts b/src/vs/workbench/contrib/testing/common/testId.ts index 79fcec77b1d..0b66669342a 100644 --- a/src/vs/workbench/contrib/testing/common/testId.ts +++ b/src/vs/workbench/contrib/testing/common/testId.ts @@ -105,7 +105,7 @@ export class TestId { * todo@connor4312: review usages of this to see if using the WellDefinedPrefixTree is better */ public static isChild(maybeParent: string, maybeChild: string) { - return maybeChild.startsWith(maybeParent) && maybeChild[maybeParent.length] === TestIdPathParts.Delimiter; + return maybeChild[maybeParent.length] === TestIdPathParts.Delimiter && maybeChild.startsWith(maybeParent); } /** diff --git a/src/vs/workbench/contrib/testing/common/testService.ts b/src/vs/workbench/contrib/testing/common/testService.ts index 919fb4dbe1f..48ebe0e230e 100644 --- a/src/vs/workbench/contrib/testing/common/testService.ts +++ b/src/vs/workbench/contrib/testing/common/testService.ts @@ -3,19 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { assert } from 'vs/base/common/assert'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { Iterable } from 'vs/base/common/iterator'; import { IDisposable } from 'vs/base/common/lifecycle'; import { MarshalledId } from 'vs/base/common/marshallingIds'; +import { IPrefixTreeNode, WellDefinedPrefixTree } from 'vs/base/common/prefixTree'; import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { IObservableValue, MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; -import { AbstractIncrementalTestCollection, ICallProfileRunHandler, IncrementalTestCollectionItem, InternalTestItem, ITestItemContext, ResolvedTestRunRequest, IStartControllerTests, IStartControllerTestsResult, TestItemExpandState, TestRunProfileBitset, TestsDiff, TestMessageFollowupResponse, TestMessageFollowupRequest } from 'vs/workbench/contrib/testing/common/testTypes'; import { TestExclusions } from 'vs/workbench/contrib/testing/common/testExclusions'; import { TestId } from 'vs/workbench/contrib/testing/common/testId'; import { ITestResult } from 'vs/workbench/contrib/testing/common/testResult'; +import { AbstractIncrementalTestCollection, ICallProfileRunHandler, IncrementalTestCollectionItem, InternalTestItem, IStartControllerTests, IStartControllerTestsResult, ITestItemContext, ResolvedTestRunRequest, TestItemExpandState, TestMessageFollowupRequest, TestMessageFollowupResponse, TestRunProfileBitset, TestsDiff } from 'vs/workbench/contrib/testing/common/testTypes'; export const ITestService = createDecorator('testService'); @@ -216,6 +218,64 @@ export const testsUnderUri = async function* (testService: ITestService, ident: } }; +/** + * Simplifies the array of tests by preferring test item parents if all of + * their children are included. + */ +export const simplifyTestsToExecute = (collection: IMainThreadTestCollection, tests: IncrementalTestCollectionItem[]): IncrementalTestCollectionItem[] => { + if (tests.length < 2) { + return tests; + } + + const tree = new WellDefinedPrefixTree(); + for (const test of tests) { + tree.insert(TestId.fromString(test.item.extId).path, test); + } + + const out: IncrementalTestCollectionItem[] = []; + + // Returns the node if it and any children should be included. Otherwise + // pushes into the `out` any individual children that should be included. + const process = (currentId: string[], node: IPrefixTreeNode) => { + // directly included, don't try to over-specify, and children should be ignored + if (node.value) { + return node.value; + } + + assert(!!node.children, 'expect to have children'); + + const thisChildren: IncrementalTestCollectionItem[] = []; + for (const [part, child] of node.children) { + currentId.push(part); + const c = process(currentId, child); + if (c) { thisChildren.push(c); } + currentId.pop(); + } + + if (!thisChildren.length) { + return; + } + + // If there are multiple children and we have all of them, then tell the + // parent this node should be included. Otherwise include children individually. + const id = new TestId(currentId); + const test = collection.getNodeById(id.toString()); + if (test?.children.size === thisChildren.length) { + return test; + } + + out.push(...thisChildren); + return; + }; + + for (const [id, node] of tree.entries) { + const n = process([id], node); + if (n) { out.push(n); } + } + + return out; +}; + /** * A run request that expresses the intent of the request and allows the * test service to resolve the specifics of the group. diff --git a/src/vs/workbench/contrib/testing/test/common/testService.test.ts b/src/vs/workbench/contrib/testing/test/common/testService.test.ts new file mode 100644 index 00000000000..66ae8c791f3 --- /dev/null +++ b/src/vs/workbench/contrib/testing/test/common/testService.test.ts @@ -0,0 +1,108 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { TestId } from 'vs/workbench/contrib/testing/common/testId'; +import { simplifyTestsToExecute } from 'vs/workbench/contrib/testing/common/testService'; +import { getInitializedMainTestCollection, makeSimpleStubTree } from 'vs/workbench/contrib/testing/test/common/testStubs'; + +suite('Workbench - Test Service', () => { + ensureNoDisposablesAreLeakedInTestSuite(); + + suite('simplifyTestsToExecute', () => { + const tree1 = { + a: { + b1: { + c1: { + d: undefined + }, + c2: { + d: undefined + }, + }, + b2: undefined, + } + } as const; + + test('noop on single item', async () => { + const c = await getInitializedMainTestCollection(makeSimpleStubTree(tree1)); + + const t = simplifyTestsToExecute(c, [ + c.getNodeById(new TestId(['ctrlId', 'a', 'b1']).toString())! + ]); + + assert.deepStrictEqual(t.map(t => t.item.extId.toString()), [ + new TestId(['ctrlId', 'a', 'b1']).toString() + ]); + }); + + test('goes to common root 1', async () => { + const c = await getInitializedMainTestCollection(makeSimpleStubTree(tree1)); + + const t = simplifyTestsToExecute(c, [ + c.getNodeById(new TestId(['ctrlId', 'a', 'b1', 'c1', 'd']).toString())!, + c.getNodeById(new TestId(['ctrlId', 'a', 'b1', 'c2']).toString())!, + ]); + + assert.deepStrictEqual(t.map(t => t.item.extId.toString()), [ + new TestId(['ctrlId', 'a', 'b1']).toString() + ]); + }); + + test('goes to common root 2', async () => { + const c = await getInitializedMainTestCollection(makeSimpleStubTree(tree1)); + + const t = simplifyTestsToExecute(c, [ + c.getNodeById(new TestId(['ctrlId', 'a', 'b1', 'c1']).toString())!, + c.getNodeById(new TestId(['ctrlId', 'a', 'b1']).toString())!, + ]); + + assert.deepStrictEqual(t.map(t => t.item.extId.toString()), [ + new TestId(['ctrlId', 'a', 'b1']).toString() + ]); + }); + + test('goes to common root 3', async () => { + const c = await getInitializedMainTestCollection(makeSimpleStubTree(tree1)); + + const t = simplifyTestsToExecute(c, [ + c.getNodeById(new TestId(['ctrlId', 'a', 'b1', 'c1', 'd']).toString())!, + c.getNodeById(new TestId(['ctrlId', 'a', 'b1', 'c2']).toString())!, + ]); + + assert.deepStrictEqual(t.map(t => t.item.extId.toString()), [ + new TestId(['ctrlId', 'a', 'b1']).toString() + ]); + }); + + test('goes to common root 4', async () => { + const c = await getInitializedMainTestCollection(makeSimpleStubTree(tree1)); + + const t = simplifyTestsToExecute(c, [ + c.getNodeById(new TestId(['ctrlId', 'a', 'b2']).toString())!, + c.getNodeById(new TestId(['ctrlId', 'a', 'b1']).toString())!, + ]); + + assert.deepStrictEqual(t.map(t => t.item.extId.toString()), [ + new TestId(['ctrlId']).toString() + ]); + }); + + test('no-op divergent trees', async () => { + const c = await getInitializedMainTestCollection(makeSimpleStubTree(tree1)); + + const t = simplifyTestsToExecute(c, [ + c.getNodeById(new TestId(['ctrlId', 'a', 'b1', 'c2']).toString())!, + c.getNodeById(new TestId(['ctrlId', 'a', 'b2']).toString())!, + ]); + + assert.deepStrictEqual(t.map(t => t.item.extId.toString()), [ + new TestId(['ctrlId', 'a', 'b1', 'c2']).toString(), + new TestId(['ctrlId', 'a', 'b2']).toString(), + ]); + }); + }); +}); diff --git a/src/vs/workbench/contrib/testing/test/common/testStubs.ts b/src/vs/workbench/contrib/testing/test/common/testStubs.ts index 836fe2143e8..c32350bc356 100644 --- a/src/vs/workbench/contrib/testing/test/common/testStubs.ts +++ b/src/vs/workbench/contrib/testing/test/common/testStubs.ts @@ -113,6 +113,26 @@ export const getInitializedMainTestCollection = async (singleUse = testStubs.nes return c; }; +type StubTreeIds = Readonly<{ [id: string]: StubTreeIds | undefined }>; + +export const makeSimpleStubTree = (ids: StubTreeIds): TestTestCollection => { + const collection = new TestTestCollection(); + + const add = (parent: TestTestItem, children: StubTreeIds, path: readonly string[]) => { + for (const id of Object.keys(children)) { + const item = new TestTestItem(new TestId([...path, id]), id); + parent.children.add(item); + if (children[id]) { + add(item, children[id]!, [...path, id]); + } + } + }; + + add(collection.root, ids, ['ctrlId']); + + return collection; +}; + export const testStubs = { nested: (idPrefix = 'id-') => { const collection = new TestTestCollection(); From 4b261a6d99bbe9b1839ce066a601b6d486664930 Mon Sep 17 00:00:00 2001 From: tisilent Date: Wed, 3 Jul 2024 11:47:59 +0800 Subject: [PATCH 0241/2222] Add Icons. --- .../contrib/snippets/browser/commands/configureSnippets.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts index 77dad6efa4a..f0ea01793f2 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts @@ -8,6 +8,7 @@ import { extname } from 'vs/base/common/path'; import { basename, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { ILanguageService } from 'vs/editor/common/languages/language'; +import { getIconClassesForLanguageId } from 'vs/editor/common/services/getIconClasses'; import * as nls from 'vs/nls'; import { MenuId } from 'vs/platform/actions/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; @@ -115,7 +116,8 @@ async function computePicks(snippetService: ISnippetsService, userDataProfileSer label: languageId, description: `(${label})`, filepath: joinPath(dir, `${languageId}.json`), - hint: true + hint: true, + iconClasses: getIconClassesForLanguageId(languageId) }); } } From facf5e065010b6b7cbdbbacb099f103a7ba9a16c Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 2 Jul 2024 21:46:11 -0700 Subject: [PATCH 0242/2222] Fix import. --- .../contrib/notebook/browser/viewModel/baseCellViewModel.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts index 0ec9a055e13..9ed29128b2c 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/baseCellViewModel.ts @@ -19,7 +19,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { IUndoRedoService } from 'vs/platform/undoRedo/common/undoRedo'; import { IWordWrapTransientState, readTransientState, writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; import { InlineChatController } from 'vs/workbench/contrib/inlineChat/browser/inlineChatController'; -import { CellEditState, CellFocusMode, CursorAtBoundary, CursorAtLineBoundary, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; +import { CellEditState, CellFocusMode, CellLayoutChangeEvent, CursorAtBoundary, CursorAtLineBoundary, IEditableCellViewModel, INotebookCellDecorationOptions } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { NotebookOptionsChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookOptions'; import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { ViewContext } from 'vs/workbench/contrib/notebook/browser/viewModel/viewContext'; From e2af3117bf0074b10f2170ec2096bd4bd365fbe3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Moreno?= Date: Wed, 3 Jul 2024 09:48:33 +0200 Subject: [PATCH 0243/2222] use unofficial 1espt (#219824) --- build/azure-pipelines/product-build.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 97adeb0cd83..0c4f98aa511 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -167,7 +167,7 @@ resources: ref: refs/tags/release extends: - template: v1/1ES.Official.PipelineTemplate.yml@1esPipelines + template: v1/1ES.Unofficial.PipelineTemplate.yml@1esPipelines parameters: sdl: tsa: @@ -183,7 +183,7 @@ extends: allTools: true codeql: compiled: - enabled: true + enabled: false runSourceLanguagesInSourceAnalysis: true credscan: suppressionsFile: $(Build.SourcesDirectory)/build/azure-pipelines/config/CredScanSuppressions.json From 5c3b102d73a0ff81788b418396bb5478c555f8ee Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 3 Jul 2024 10:26:06 +0200 Subject: [PATCH 0244/2222] SCM - remove duplicate check code (#219826) SCM - remove duplicate check --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index e969b6a0e09..aa0e50ccf49 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -4237,6 +4237,7 @@ registerThemingParticipant((theme, collector) => { // Override inactive selection bg const inputBackgroundColor = theme.getColor(inputBackground); if (inputBackgroundColor) { + collector.addRule(`.scm-view .scm-editor-container .monaco-editor-background { background-color: ${inputBackgroundColor}; } `); collector.addRule(`.scm-view .scm-editor-container .monaco-editor .selected-text { background-color: ${inputBackgroundColor.transparent(0.4)}; }`); } @@ -4246,10 +4247,6 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.scm-view .scm-editor-container .monaco-editor .view-line span.inline-selected-text { color: ${inputForegroundColor}; }`); } - const backgroundColor = theme.getColor(inputBackground); - if (backgroundColor) { - collector.addRule(`.scm-view .scm-editor-container .monaco-editor-background { background-color: ${backgroundColor}; } `); - } collector.addRule(`.scm-view .scm-editor-container .monaco-editor .focused .selected-text { background-color: ${selectionBackgroundColor}; }`); } else { // Use editor selection color if theme has not set a selection background color From 9255a27d883c812feae2473d7af428267c875030 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Wed, 3 Jul 2024 12:13:46 +0200 Subject: [PATCH 0245/2222] Try out new YAML grammar (#219833) * Try out new YAML grammar Part of #180523 * Pull in other/plain update --- .../colorize-results/issue-1550_yaml.json | 40 +- .../colorize-results/issue-4008_yaml.json | 48 +- .../colorize-results/issue-6303_yaml.json | 132 +- .../test/colorize-results/test_yaml.json | 374 ++-- extensions/yaml/build/update-grammar.js | 17 + extensions/yaml/cgmanifest.json | 31 +- extensions/yaml/package.json | 18 +- .../yaml/syntaxes/yaml-1.0.tmLanguage.json | 1621 +++++++++++++++ .../yaml/syntaxes/yaml-1.1.tmLanguage.json | 1792 +++++++++++++++++ .../yaml/syntaxes/yaml-1.2.tmLanguage.json | 1714 ++++++++++++++++ .../yaml/syntaxes/yaml-1.3.tmLanguage.json | 1714 ++++++++++++++++ extensions/yaml/syntaxes/yaml.tmLanguage.json | 614 +----- 12 files changed, 7267 insertions(+), 848 deletions(-) create mode 100644 extensions/yaml/build/update-grammar.js create mode 100644 extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json create mode 100644 extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json create mode 100644 extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json create mode 100644 extensions/yaml/syntaxes/yaml-1.3.tmLanguage.json diff --git a/extensions/vscode-colorize-tests/test/colorize-results/issue-1550_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/issue-1550_yaml.json index dac84162b3c..cc1450808af 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/issue-1550_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/issue-1550_yaml.json @@ -1,7 +1,7 @@ [ { "c": "test1", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -15,7 +15,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -29,7 +29,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": "dsd", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -71,7 +71,7 @@ }, { "c": "test2", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -85,7 +85,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "abc-def", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -141,7 +141,7 @@ }, { "c": "test-3", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -155,7 +155,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -169,7 +169,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +197,7 @@ }, { "c": "abcdef", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -211,7 +211,7 @@ }, { "c": "test-4", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -225,7 +225,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -239,7 +239,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -253,7 +253,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +267,7 @@ }, { "c": "abc-def", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/issue-4008_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/issue-4008_yaml.json index e1aa82e9ca3..c8c2d57d903 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/issue-4008_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/issue-4008_yaml.json @@ -1,7 +1,7 @@ [ { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -15,7 +15,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -29,7 +29,7 @@ }, { "c": "blue", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -43,7 +43,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "a=\"brown,not_brown\"", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": "not_blue", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -127,7 +127,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -141,7 +141,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "foo", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -169,7 +169,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +197,7 @@ }, { "c": "blue", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -211,7 +211,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -225,7 +225,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -239,7 +239,7 @@ }, { "c": "foo=\"}\"", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -253,7 +253,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +267,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -281,7 +281,7 @@ }, { "c": "not_blue", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -295,7 +295,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +309,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -323,7 +323,7 @@ }, { "c": "1", - "t": "source.yaml constant.numeric.integer.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml constant.numeric.integer.decimal.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/issue-6303_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/issue-6303_yaml.json index 2066b677e2c..e5267d62494 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/issue-6303_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/issue-6303_yaml.json @@ -1,7 +1,7 @@ [ { "c": "swagger", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -15,7 +15,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -29,7 +29,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": "'", - "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -57,7 +57,7 @@ }, { "c": "2.0", - "t": "source.yaml string.quoted.single.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -71,7 +71,7 @@ }, { "c": "'", - "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "info", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -99,7 +99,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "description", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -141,7 +141,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -169,7 +169,7 @@ }, { "c": "'", - "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.definition.string.begin.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -183,7 +183,7 @@ }, { "c": "The API Management Service API defines an updated and refined version", - "t": "source.yaml string.quoted.single.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -196,8 +196,64 @@ } }, { - "c": " of the concepts currently known as Developer, APP, and API Product in Edge. Of", - "t": "source.yaml string.quoted.single.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.whitespace.separator.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string.quoted.single.yaml: #0F4A85", + "light_modern": "string.quoted.single.yaml: #0000FF" + } + }, + { + "c": "of the concepts currently known as Developer, APP, and API Product in Edge. Of", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml", + "r": { + "dark_plus": "string: #CE9178", + "light_plus": "string.quoted.single.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.quoted.single.yaml: #0000FF", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string.quoted.single.yaml: #0F4A85", + "light_modern": "string.quoted.single.yaml: #0000FF" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -210,8 +266,8 @@ } }, { - "c": " note is the introduction of the API concept, missing previously from Edge", - "t": "source.yaml string.quoted.single.yaml", + "c": "note is the introduction of the API concept, missing previously from Edge", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -224,8 +280,22 @@ } }, { - "c": " ", - "t": "source.yaml string.quoted.single.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -239,7 +309,7 @@ }, { "c": "'", - "t": "source.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.quoted.single.yaml punctuation.definition.string.end.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.quoted.single.yaml: #0000FF", @@ -253,7 +323,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +337,7 @@ }, { "c": "title", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -281,7 +351,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -295,7 +365,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +379,7 @@ }, { "c": "API Management Service API", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -323,7 +393,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -337,7 +407,7 @@ }, { "c": "version", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -351,7 +421,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -365,7 +435,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -379,7 +449,7 @@ }, { "c": "initial", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test_yaml.json b/extensions/vscode-colorize-tests/test/colorize-results/test_yaml.json index 407cc7c7a1a..9ab753e7556 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_yaml.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_yaml.json @@ -1,7 +1,7 @@ [ { "c": "#", - "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "t": "source.yaml meta.stream.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -15,7 +15,7 @@ }, { "c": " sequencer protocols for Laser eye surgery", - "t": "source.yaml comment.line.number-sign.yaml", + "t": "source.yaml meta.stream.yaml comment.line.number-sign.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -29,7 +29,7 @@ }, { "c": "---", - "t": "source.yaml entity.other.document.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml entity.other.document.begin.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "step", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -85,7 +85,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": "&", - "t": "source.yaml meta.property.yaml keyword.control.property.anchor.yaml punctuation.definition.anchor.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.anchor.yaml punctuation.definition.anchor.yaml", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -127,21 +127,21 @@ }, { "c": "id001", - "t": "source.yaml meta.property.yaml entity.name.type.anchor.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.anchor.yaml variable.other.anchor.yaml", "r": { - "dark_plus": "entity.name.type: #4EC9B0", - "light_plus": "entity.name.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "entity.name.type: #4EC9B0", - "dark_modern": "entity.name.type: #4EC9B0", - "hc_light": "entity.name.type: #185E73", - "light_modern": "entity.name.type: #267F99" + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" } }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "#", - "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -169,7 +169,7 @@ }, { "c": " defines anchor label &id001", - "t": "source.yaml comment.line.number-sign.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml comment.line.number-sign.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -182,8 +182,22 @@ } }, { - "c": " ", - "t": "source.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +211,7 @@ }, { "c": "instrument", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -211,7 +225,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -225,7 +239,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -239,7 +253,7 @@ }, { "c": "Lasik 2000", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -252,8 +266,22 @@ } }, { - "c": " ", - "t": "source.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +295,7 @@ }, { "c": "pulseEnergy", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -281,7 +309,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -295,7 +323,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +337,7 @@ }, { "c": "5.4", - "t": "source.yaml constant.numeric.float.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml constant.numeric.float.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -322,8 +350,22 @@ } }, { - "c": " ", - "t": "source.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -337,7 +379,7 @@ }, { "c": "spotSize", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -351,7 +393,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -365,7 +407,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -379,7 +421,7 @@ }, { "c": "1mm", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -393,7 +435,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -407,7 +449,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -421,7 +463,7 @@ }, { "c": "step", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -435,7 +477,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -449,7 +491,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -463,7 +505,7 @@ }, { "c": "*", - "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -477,12 +519,12 @@ }, { "c": "id001", - "t": "source.yaml variable.other.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml variable.other.alias.yaml", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -491,7 +533,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -505,7 +547,7 @@ }, { "c": "#", - "t": "source.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml comment.line.number-sign.yaml punctuation.definition.comment.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -519,7 +561,7 @@ }, { "c": " refers to the first step (with anchor &id001)", - "t": "source.yaml comment.line.number-sign.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml comment.line.number-sign.yaml", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -533,7 +575,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -547,7 +589,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -561,7 +603,7 @@ }, { "c": "step", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -575,7 +617,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -589,7 +631,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -603,7 +645,7 @@ }, { "c": "*", - "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -617,12 +659,12 @@ }, { "c": "id001", - "t": "source.yaml variable.other.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml variable.other.alias.yaml", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -630,8 +672,8 @@ } }, { - "c": " ", - "t": "source.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -644,22 +686,8 @@ } }, { - "c": "spotSize", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", - "r": { - "dark_plus": "entity.name.tag: #569CD6", - "light_plus": "entity.name.tag: #800000", - "dark_vs": "entity.name.tag: #569CD6", - "light_vs": "entity.name.tag: #800000", - "hc_black": "entity.name.tag: #569CD6", - "dark_modern": "entity.name.tag: #569CD6", - "hc_light": "entity.name.tag: #0F4A85", - "light_modern": "entity.name.tag: #800000" - } - }, - { - "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "c": " ", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -671,9 +699,23 @@ "light_modern": "default: #3B3B3B" } }, + { + "c": "spotSize:", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml invalid.illegal.unrecognized.yaml markup.strikethrough", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -687,7 +729,7 @@ }, { "c": "2mm", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -701,21 +743,21 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml punctuation.whitespace.separator.yaml", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "string: #CE9178", + "light_plus": "string.unquoted.plain.out.yaml: #0000FF", + "dark_vs": "string: #CE9178", + "light_vs": "string.unquoted.plain.out.yaml: #0000FF", + "hc_black": "string: #CE9178", + "dark_modern": "string: #CE9178", + "hc_light": "string.unquoted.plain.out.yaml: #0F4A85", + "light_modern": "string.unquoted.plain.out.yaml: #0000FF" } }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -729,7 +771,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -743,7 +785,7 @@ }, { "c": "step", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -757,7 +799,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -771,7 +813,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -785,7 +827,7 @@ }, { "c": "*", - "t": "source.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml punctuation.definition.alias.yaml", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -799,12 +841,12 @@ }, { "c": "id002", - "t": "source.yaml variable.other.alias.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml keyword.control.flow.alias.yaml variable.other.alias.yaml", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -813,7 +855,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -827,7 +869,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -841,7 +883,7 @@ }, { "c": "{", - "t": "source.yaml meta.flow-mapping.yaml punctuation.definition.mapping.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml punctuation.definition.mapping.begin.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -855,7 +897,7 @@ }, { "c": "name", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.map.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -869,7 +911,7 @@ }, { "c": ":", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -883,7 +925,7 @@ }, { "c": " ", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -897,7 +939,7 @@ }, { "c": "John Smith", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml string.unquoted.plain.in.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml string.unquoted.plain.in.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.in.yaml: #0000FF", @@ -911,7 +953,7 @@ }, { "c": ",", - "t": "source.yaml meta.flow-mapping.yaml punctuation.separator.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml punctuation.separator.mapping.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +967,7 @@ }, { "c": " ", - "t": "source.yaml meta.flow-mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -939,7 +981,7 @@ }, { "c": "age", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.map.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -953,7 +995,7 @@ }, { "c": ":", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -967,7 +1009,7 @@ }, { "c": " ", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -981,7 +1023,7 @@ }, { "c": "33", - "t": "source.yaml meta.flow-mapping.yaml meta.flow-pair.yaml meta.flow-pair.value.yaml constant.numeric.integer.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml meta.flow.map.implicit.yaml meta.flow.pair.value.yaml string.unquoted.plain.in.yaml constant.numeric.integer.decimal.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -995,7 +1037,7 @@ }, { "c": "}", - "t": "source.yaml meta.flow-mapping.yaml punctuation.definition.mapping.end.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.flow.mapping.yaml punctuation.definition.mapping.end.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1009,7 +1051,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1023,7 +1065,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1037,7 +1079,7 @@ }, { "c": "name", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1051,7 +1093,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1065,7 +1107,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1079,7 +1121,7 @@ }, { "c": "Mary Smith", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -1093,7 +1135,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1107,7 +1149,7 @@ }, { "c": "age", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1121,7 +1163,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1135,7 +1177,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1149,7 +1191,7 @@ }, { "c": "27", - "t": "source.yaml constant.numeric.integer.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml string.unquoted.plain.out.yaml constant.numeric.integer.decimal.yaml", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1163,7 +1205,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1176,8 +1218,22 @@ } }, { - "c": "men", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "c": "m", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml invalid.illegal.expected-indentation.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": "en", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1191,7 +1247,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1205,7 +1261,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1219,7 +1275,7 @@ }, { "c": "[", - "t": "source.yaml meta.flow-sequence.yaml punctuation.definition.sequence.begin.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml punctuation.definition.sequence.begin.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1233,7 +1289,7 @@ }, { "c": "John Smith", - "t": "source.yaml meta.flow-sequence.yaml string.unquoted.plain.in.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml string.unquoted.plain.in.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.in.yaml: #0000FF", @@ -1247,7 +1303,7 @@ }, { "c": ",", - "t": "source.yaml meta.flow-sequence.yaml punctuation.separator.sequence.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml punctuation.separator.sequence.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1261,7 +1317,7 @@ }, { "c": " ", - "t": "source.yaml meta.flow-sequence.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1275,7 +1331,7 @@ }, { "c": "Bill Jones", - "t": "source.yaml meta.flow-sequence.yaml string.unquoted.plain.in.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml string.unquoted.plain.in.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.in.yaml: #0000FF", @@ -1289,7 +1345,7 @@ }, { "c": "]", - "t": "source.yaml meta.flow-sequence.yaml punctuation.definition.sequence.end.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.flow.sequence.yaml punctuation.definition.sequence.end.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1302,8 +1358,36 @@ } }, { - "c": "women", - "t": "source.yaml string.unquoted.plain.out.yaml entity.name.tag.yaml", + "c": "w", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml invalid.illegal.expected-indentation.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": "o", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml invalid.illegal.expected-indentation.yaml", + "r": { + "dark_plus": "invalid: #F44747", + "light_plus": "invalid: #CD3131", + "dark_vs": "invalid: #F44747", + "light_vs": "invalid: #CD3131", + "hc_black": "invalid: #F44747", + "dark_modern": "invalid: #F44747", + "hc_light": "invalid: #B5200D", + "light_modern": "invalid: #CD3131" + } + }, + { + "c": "men", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", "r": { "dark_plus": "entity.name.tag: #569CD6", "light_plus": "entity.name.tag: #800000", @@ -1317,7 +1401,7 @@ }, { "c": ":", - "t": "source.yaml punctuation.separator.key-value.mapping.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml punctuation.separator.map.value.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1331,7 +1415,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1345,7 +1429,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1359,7 +1443,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1373,7 +1457,7 @@ }, { "c": "Mary Smith", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", @@ -1387,7 +1471,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml punctuation.whitespace.indentation.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1401,7 +1485,7 @@ }, { "c": "-", - "t": "source.yaml punctuation.definition.block.sequence.item.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml punctuation.definition.block.sequence.item.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1415,7 +1499,7 @@ }, { "c": " ", - "t": "source.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml punctuation.whitespace.separator.yaml", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1429,7 +1513,7 @@ }, { "c": "Susan Williams", - "t": "source.yaml string.unquoted.plain.out.yaml", + "t": "source.yaml meta.stream.yaml meta.document.yaml meta.block.sequence.yaml meta.mapping.yaml meta.map.value.yaml meta.block.sequence.yaml string.unquoted.plain.out.yaml", "r": { "dark_plus": "string: #CE9178", "light_plus": "string.unquoted.plain.out.yaml: #0000FF", diff --git a/extensions/yaml/build/update-grammar.js b/extensions/yaml/build/update-grammar.js new file mode 100644 index 00000000000..8684bc3e5d0 --- /dev/null +++ b/extensions/yaml/build/update-grammar.js @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +'use strict'; + +var updateGrammar = require('vscode-grammar-updater'); + +async function updateGrammars() { + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-1.0.tmLanguage.json', './syntaxes/yaml-1.0.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-1.1.tmLanguage.json', './syntaxes/yaml-1.1.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-1.2.tmLanguage.json', './syntaxes/yaml-1.2.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml-1.3.tmLanguage.json', './syntaxes/yaml-1.3.tmLanguage.json', undefined, 'main'); + await updateGrammar.update('RedCMD/YAML-Syntax-Highlighter', 'syntaxes/yaml.tmLanguage.json', './syntaxes/yaml.tmLanguage.json', undefined, 'main'); +} + +updateGrammars(); diff --git a/extensions/yaml/cgmanifest.json b/extensions/yaml/cgmanifest.json index e6c3ca158b5..43dc4ca637e 100644 --- a/extensions/yaml/cgmanifest.json +++ b/extensions/yaml/cgmanifest.json @@ -4,33 +4,24 @@ "component": { "type": "git", "git": { - "name": "textmate/yaml.tmbundle", - "repositoryUrl": "https://github.com/textmate/yaml.tmbundle", - "commitHash": "e54ceae3b719506dba7e481a77cea4a8b576ae46" + "name": "RedCMD/YAML-Syntax-Highlighter", + "repositoryUrl": "https://github.com/RedCMD/YAML-Syntax-Highlighter", + "commitHash": "287c71aeb0773759497822b5e5ce4bdc4d5ef2aa" } }, "licenseDetail": [ - "Copyright (c) 2015 FichteFoll ", + "MIT License", "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", + "Copyright 2024 RedCMD", "", - "The above copyright notice and this permission notice shall be included in all", - "copies or substantial portions of the Software.", + "Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:", "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." + "The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.", + "", + "THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ], - "license": "TextMate Bundle License", - "version": "0.0.0" + "license": "MIT", + "version": "1.0.0" } ], "version": 1 diff --git a/extensions/yaml/package.json b/extensions/yaml/package.json index 5223f71c52b..d19c507bdfe 100644 --- a/extensions/yaml/package.json +++ b/extensions/yaml/package.json @@ -9,7 +9,7 @@ "vscode": "*" }, "scripts": { - "update-grammar": "node ../node_modules/vscode-grammar-updater/bin textmate/yaml.tmbundle Syntaxes/YAML.tmLanguage ./syntaxes/yaml.tmLanguage.json" + "update-grammar": "node ./build/update-grammar.js" }, "categories": ["Programming Languages"], "contributes": { @@ -56,6 +56,22 @@ "scopeName": "source.yaml", "path": "./syntaxes/yaml.tmLanguage.json" }, + { + "scopeName": "source.yaml.1.3", + "path": "./syntaxes/yaml-1.3.tmLanguage.json" + }, + { + "scopeName": "source.yaml.1.2", + "path": "./syntaxes/yaml-1.2.tmLanguage.json" + }, + { + "scopeName": "source.yaml.1.1", + "path": "./syntaxes/yaml-1.1.tmLanguage.json" + }, + { + "scopeName": "source.yaml.1.0", + "path": "./syntaxes/yaml-1.0.tmLanguage.json" + }, { "language": "yaml", "scopeName": "source.yaml", diff --git a/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json new file mode 100644 index 00000000000..9f2a401ca5e --- /dev/null +++ b/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json @@ -0,0 +1,1621 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/RedCMD/YAML-Syntax-Highlighter/blob/master/syntaxes/yaml-1.0.tmLanguage.json", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/287c71aeb0773759497822b5e5ce4bdc4d5ef2aa", + "name": "YAML 1.0", + "scopeName": "source.yaml.1.0", + "comment": "https://yaml.org/spec/1.0/", + "patterns": [ + { + "include": "#stream" + } + ], + "repository": { + "stream": { + "patterns": [ + { + "comment": "allows me to just use `\\G` instead of the performance heavy `(^|\\G)`", + "begin": "^(?!\\G)", + "while": "^", + "name": "meta.stream.yaml", + "patterns": [ + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G", + "while": "\\G", + "name": "meta.stream.yaml", + "patterns": [ + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "directive-YAML": { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "(?=%YAML:1\\.0(?=[\\x{85 2028 2029}\r\n\t ]))", + "end": "\\G(?=%(?!YAML:1\\.0))", + "name": "meta.1.0.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "\\G(%)(YAML)(:)(1\\.0)", + "while": "\\G(?!---[\\x{85 2028 2029}\r\n\t ])", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.yaml.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "constant.numeric.yaml-version.yaml" + } + }, + "name": "meta.directives.yaml", + "patterns": [ + { + "include": "#directive-invalid" + }, + { + "include": "#directives" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G(?=---[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?!%)", + "patterns": [ + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#presentation-detail" + } + ] + }, + "directives": { + "comment": "https://yaml.org/spec/1.2.2/#68-directives", + "patterns": [ + { + "include": "source.yaml.1.3#directive-YAML" + }, + { + "include": "source.yaml.1.2#directive-YAML" + }, + { + "include": "source.yaml.1.1#directive-YAML" + }, + { + "include": "source.yaml.1.0#directive-YAML" + }, + { + "begin": "(?=%)", + "while": "\\G(?!%|---[\\x{85 2028 2029}\r\n\t ])", + "name": "meta.directives.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-reserved-directive", + "begin": "(%)([^: \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.other.yaml" + } + }, + "patterns": [ + { + "match": "\\G(:)([^ \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-name.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "match": "\\G\\.{3}(?=[\\x{85 2028 2029}\r\n\t ])", + "name": "invalid.illegal.entity.other.document.end.yaml" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "directive-invalid": { + "patterns": [ + { + "match": "\\G\\.{3}(?=[\\x{85 2028 2029}\r\n\t ])", + "name": "invalid.illegal.entity.other.document.end.yaml" + }, + { + "begin": "\\G(%)(YAML)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "invalid.illegal.keyword.other.directive.yaml.yaml" + } + }, + "name": "meta.directive.yaml", + "patterns": [ + { + "match": "\\G([\t ]++|:)([0-9]++\\.[0-9]++)?+", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "constant.numeric.yaml-version.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "document": { + "comment": "https://yaml.org/spec/1.2.2/#91-documents", + "patterns": [ + { + "begin": "---(?=[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?!(?>\\.{3}|---)[\\x{85 2028 2029}\r\n\t ])", + "beginCaptures": { + "0": { + "name": "entity.other.document.begin.yaml" + } + }, + "name": "meta.document.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + { + "begin": "(?=\\.{3}[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?=[\t \\x{FEFF}]*+(?>#|$))", + "patterns": [ + { + "begin": "\\G\\.{3}", + "end": "$", + "beginCaptures": { + "0": { + "name": "entity.other.document.end.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G(?!%|[\t \\x{FEFF}]*+(?>#|$))", + "while": "\\G(?!(?>\\.{3}|---)[\\x{85 2028 2029}\r\n\t ])", + "name": "meta.document.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + } + ] + }, + "block-node": { + "patterns": [ + { + "include": "#block-sequence" + }, + { + "include": "#block-mapping" + }, + { + "include": "#block-scalar" + }, + { + "include": "#anchor-property" + }, + { + "include": "#tag-property" + }, + { + "include": "#alias" + }, + { + "begin": "(?=\"|')", + "while": "\\G", + "patterns": [ + { + "begin": "(?!\\G)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#double" + }, + { + "include": "#single" + } + ] + }, + { + "begin": "(?=\\[|{)", + "while": "\\G", + "patterns": [ + { + "include": "#block-mapping" + }, + { + "begin": "(?!\\G)(?![\r\n\t ])", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + } + ] + }, + { + "include": "#block-plain-out" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-mapping": { + "//": "The check for plain keys is expensive", + "begin": "(?=((?<=[-?:]) )?+)(?[!&*][^\\x{85 2028 2029}\r\n\t ]*+[\t ]++)*+)(?=(?>(?#Double Quote)\"(?>[^\\\\\"]++|\\\\.)*+\"|(?#Single Quote)'(?>[^']++|'')*+'|(?#Plain)(?>[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ]))(?>[^:#]++|:(?![\\x{85 2028 2029}\r\n\t ])|(?(\\1\\2)((?>[!&*][^\\x{85 2028 2029}\r\n\t ]*+[\t ]++)*+)((?>\t[\t ]*+)?+[^\\x{85 2028 2029}\r\n\t ?:\\-#!&*\"'\\[\\]{}0-9A-Za-z$()+./;<=\\\\^_~\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}])?+|( *+)([\t ]*+[^\\x{85 2028 2029}\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "comment": "May cause lag on long lines starting with a tag, anchor or alias", + "patterns": [ + { + "include": "#tag-property" + }, + { + "include": "#anchor-property" + }, + { + "include": "#alias" + }, + { + "include": "#presentation-detail" + } + ] + }, + "5": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "comment": "May cause lag on long lines starting with a tag, anchor or alias", + "patterns": [ + { + "include": "#tag-property" + }, + { + "include": "#anchor-property" + }, + { + "include": "#alias" + }, + { + "include": "#presentation-detail" + } + ] + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "4": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "5": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.mapping.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style (BLOCK-KEY)", + "begin": "\\G\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "include": "#double-escape" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + { + "include": "#block-key-plain" + }, + { + "include": "#block-map-value" + }, + { + "include": "#block-map-explicit" + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#rule-l+block-sequence", + "begin": "(?=((?<=[-?:]) )?+)(?(\\1\\2)(?!-[\\x{85 2028 2029}\r\n\t ])((?>\t[\t ]*+)?+[^\\x{85 2028 2029}\r\n\t #\\]}])?+|(?!\\1\\2)( *+)([\t ]*+[^\\x{85 2028 2029}\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.definition.block.sequence.item.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "4": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.block.sequence.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "block-map-explicit": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-explicit-key", + "begin": "(?=((?<=[-?:]) )?+)\\G( *+)(\\?)(?=[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?>(\\1\\2)(?![?:0-9A-Za-z$()+./;<=\\\\^_~\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{2028 2029 FEFF}]])((?>\t[\t ]*+)?+[^\\x{85 2028 2029}\r\n\t #\\-\\[\\]{}])?+|(?!\\1\\2)( *+)([\t ]*+[^\\x{85 2028 2029}\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.definition.map.key.yaml" + }, + "4": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "4": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.map.explicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-key-plain-out" + }, + { + "include": "#block-map-value" + }, + { + "include": "#block-node" + } + ] + }, + "block-map-value": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", + "begin": "(:)(?=[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{2028 2029 FEFF}]]|-[^\\x{85 2028 2029}\r\n\t ])", + "beginCaptures": { + "1": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.map.value.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "block-key-plain": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (BLOCK-KEY)", + "begin": "\\G(?=[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ]))", + "end": "(?=[\t ]*+:[\\x{85 2028 2029}\r\n\t ]|(?>[\t ]++|\\G)#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": "\\G([\t ]++)(.)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "invalid.illegal.multiline-key.yaml" + } + } + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "block-scalar": { + "comment": "https://yaml.org/spec/1.2.2/#81-block-scalar-styles", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#8111-block-indentation-indicator", + "begin": "([\t ]*+)(?>(\\|)|(>))(?[+-])?+((0)|(1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)|(9))(?()|([+-]))?+", + "while": "\\G(?>(?>(?!\\6)|(?!\\7) |(?!\\8) {2}|(?!\\9) {3}|(?!\\10) {4}|(?!\\11) {5}|(?!\\12) {6}|(?!\\13) {7}|(?!\\14) {8}|(?!\\15) {9})| *+($|[^#]))", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "3": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + }, + "5": { + "name": "constant.numeric.indentation-indicator.yaml" + }, + "16": { + "name": "storage.modifier.chomping-indicator.yaml" + } + }, + "whileCaptures": { + "0": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "1": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "begin": "$", + "while": "\\G", + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + { + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-b-block-header", + "//": "Soooooooo many edge cases", + "begin": "([\t ]*+)(?>(\\|)|(>))([+-]?+)", + "while": "\\G", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "3": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-literal-content", + "begin": "$", + "while": "\\G", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "//": "Find the highest indented line", + "begin": "\\G( ++)$", + "while": "\\G(?>(\\1)$|(?!\\1)( *+)($|.))", + "captures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-b-nb-literal-next", + "//": [ + "Funky wrapper function", + "The `end` pattern clears the parent `\\G` anchor", + "Affectively forcing this rule to only match at most once", + "https://github.com/microsoft/vscode-textmate/issues/114" + ], + "begin": "\\G(?!$)(?=( *+))", + "end": "\\G(?!\\1)(?=[\t ]*+#)", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "begin": "\\G( *+)", + "while": "\\G(?>(\\1)|( *+)($|[^\t#]|[\t ]++[^#]))", + "captures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-chomped-empty", + "begin": "(?!\\G)(?=[\t ]*+#)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + { + "comment": "Header Comment", + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + } + ] + }, + "block-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-OUT)", + "begin": "(?=[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ]))", + "while": "\\G", + "patterns": [ + { + "begin": "\\G", + "end": "(?=(?>[\t ]++|\\G)#)", + "name": "string.unquoted.plain.out.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": ":(?=[\\x{85 2028 2029}\r\n\t ])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + { + "begin": "(?!\\G)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-node": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-seq-entry (FLOW-IN)", + "patterns": [ + { + "begin": "(?=\\[|{)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "begin": "(?!\\G)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + } + ] + }, + { + "include": "#anchor-property" + }, + { + "include": "#tag-property" + }, + { + "include": "#alias" + }, + { + "begin": "(?=\"|')", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "begin": "(?!\\G)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#double" + }, + { + "include": "#single" + } + ] + }, + { + "include": "#flow-plain-in" + }, + { + "include": "#presentation-detail" + } + ] + }, + "flow-mapping": { + "comment": "https://yaml.org/spec/1.2.2/#742-flow-mappings", + "begin": "{", + "end": "}", + "beginCaptures": { + "0": { + "name": "punctuation.definition.mapping.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.mapping.end.yaml" + } + }, + "name": "meta.flow.mapping.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-flow-map-entries", + "begin": "(?<={)\\G(?=[\\x{85 2028 2029}\r\n\t ,#])|,", + "end": "(?=[^\\x{85 2028 2029}\r\n\t ,#])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.mapping.yaml" + } + }, + "patterns": [ + { + "match": ",++", + "name": "invalid.illegal.separator.sequence.yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-map-key-mapping" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#741-flow-sequences", + "begin": "\\[", + "end": "]", + "beginCaptures": { + "0": { + "name": "punctuation.definition.sequence.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.sequence.end.yaml" + } + }, + "name": "meta.flow.sequence.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-flow-seq-entries", + "begin": "(?<=\\[)\\G(?=[\\x{85 2028 2029}\r\n\t ,#])|,", + "end": "(?=[^\\x{85 2028 2029}\r\n\t ,#])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.sequence.yaml" + } + }, + "patterns": [ + { + "match": ",++", + "name": "invalid.illegal.separator.sequence.yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-map-key-sequence" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-map-key-mapping": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-entry (FLOW-IN)", + "patterns": [ + { + "begin": "\\?(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\[\\]{}])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "name": "meta.flow.map.explicit.yaml", + "patterns": [ + { + "include": "#flow-map-key-mapping" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?=(?>[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ,\\[\\]{}])))", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#flow-key-plain-in" + }, + { + "match": ":(?=\\[|{)", + "name": "invalid.illegal.separator.map.yaml" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?=\"|')", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-map-key-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-entry (FLOW-IN)", + "patterns": [ + { + "begin": "\\?(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\[\\]{}])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "name": "meta.flow.map.explicit.yaml", + "patterns": [ + { + "include": "#flow-map-key-mapping" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?<=[\t ,\\[{]|^)(?=(?>[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))(?>[^:#,\\[\\]{}]++|:(?![\\x{85 2028 2029}\r\n\t ,\\[\\]{}])|(?\"(?>[^\\\\\"]++|\\\\.)*+\"|'(?>[^']++|'')*+')[\t ]*+:)", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-map-value-yaml": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-flow-map-separate-value (FLOW-IN)", + "begin": ":(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\]}])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.flow.pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + }, + "flow-map-value-json": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-flow-map-separate-value (FLOW-IN)", + "begin": "(?<=(?>[\"'\\]}]|^)[\t ]*+):", + "end": "(?=[,\\]}])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.flow.pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + }, + "flow-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-IN)", + "begin": "(?=[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "end": "(?=(?>[\t ]++|\\G)#|[\t ]*+[,\\[\\]{}])", + "name": "string.unquoted.plain.in.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-in" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": ":(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "flow-key-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (FLOW-OUT)", + "begin": "(?=[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ]))", + "end": "(?=[\t ]*+:[\\x{85 2028 2029}\r\n\t ]|[\t ]++#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "flow-key-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-implicit-yaml-key (FLOW-KEY)", + "begin": "\\G(?![\\x{85 2028 2029}\r\n\t #])", + "end": "(?=[\t ]*+(?>:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]|[,\\[\\]{}])|[\t ]++#)", + "name": "meta.flow.map.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-in" + }, + { + "include": "#non-printable" + } + ] + }, + "key-double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style", + "begin": "\\G\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "include": "#double-escape" + } + ] + }, + "key-single": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + "double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style", + "begin": "\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "string.quoted.double.yaml", + "patterns": [ + { + "match": "(?x[^\"]{2,0}|u[^\"]{4,0}|U[^\"]{8,0}|.)", + "name": "invalid.illegal.constant.character.escape.yaml" + } + ] + }, + "tag-implicit-plain-in": { + "comment": "https://yaml.org/type/index.html", + "patterns": [ + { + "match": "\\G(?>null|Null|NULL|~)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.null.yaml" + }, + { + "match": "\\G(?>true|True|TRUE|false|False|FALSE|y|Y|yes|Yes|YES|n|N|no|No|NO|on|On|ON|off|Off|OFF)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.boolean.yaml" + }, + { + "match": "\\G[-+]?+(0|[1-9][0-9_]*+)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.decimal.yaml" + }, + { + "match": "\\G[-+]?+0b[0-1_]++(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.binary.yaml" + }, + { + "match": "\\G[-+]?0[0-7_]++(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.octal.yaml" + }, + { + "match": "\\G[-+]?+0x[0-9a-fA-F_]++(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.hexadecimal.yaml" + }, + { + "match": "\\G[-+]?+[1-9][0-9_]*+(?>:[0-5]?[0-9])++(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.Sexagesimal.yaml" + }, + { + "match": "\\G[-+]?+(?>[0-9][0-9_]*+)?+\\.[0-9.]*+(?>[eE][-+][0-9]+)?+(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.decimal.yaml" + }, + { + "match": "\\G[-+]?+[0-9][0-9_]*+(?>:[0-5]?[0-9])++\\.[0-9_]*+(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.Sexagesimal.yaml" + }, + { + "match": "\\G[-+]?+\\.(?>inf|Inf|INF)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.inf.yaml" + }, + { + "match": "\\G\\.(?>nan|NaN|NAN)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.nan.yaml" + }, + { + "comment": "https://www.w3.org/TR/NOTE-datetime does not allow spaces, however https://yaml.org/type/timestamp.html does, but the provided regex doesn't match the TZD space in many of the YAML examples", + "match": "\\G(?>[0-9]{4}-[0-9]{2,1}-[0-9]{2,1}(?>T|t|[\t ]++)[0-9]{2,1}:[0-9]{2}:[0-9]{2}(?>\\.[0-9]*+)?+[\t ]*+(?>Z|[-+][0-9]{2,1}(?>:[0-9]{2})?+)?+|[0-9]{4}-[0-9]{2}-[0-9]{2})(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.timestamp.yaml" + }, + { + "match": "\\G<<(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.merge.yaml" + }, + { + "match": "\\G=(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.value.yaml" + }, + { + "match": "\\G(?>!|&|\\*)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.yaml.yaml" + } + ] + }, + "tag-implicit-plain-out": { + "comment": "https://yaml.org/type/index.html", + "patterns": [ + { + "match": "\\G(?>null|Null|NULL|~)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.null.yaml" + }, + { + "match": "\\G(?>true|True|TRUE|false|False|FALSE|yes|Yes|YES|y|Y|no|No|NO|n|N|on|On|ON|off|Off|OFF)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.boolean.yaml" + }, + { + "match": "\\G[-+]?+(0|[1-9][0-9_]*+)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.decimal.yaml" + }, + { + "match": "\\G[-+]?+0b[0-1_]++(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.binary.yaml" + }, + { + "match": "\\G[-+]?0[0-7_]++(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.octal.yaml" + }, + { + "match": "\\G[-+]?+0x[0-9a-fA-F_]++(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.hexadecimal.yaml" + }, + { + "match": "\\G[-+]?+[1-9][0-9_]*+(?>:[0-5]?[0-9])++(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.Sexagesimal.yaml" + }, + { + "match": "\\G[-+]?+(?>[0-9][0-9_]*+)?+\\.[0-9.]*+(?>[eE][-+][0-9]+)?+(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.float.decimal.yaml" + }, + { + "match": "\\G[-+]?+[0-9][0-9_]*+(?>:[0-5]?[0-9])++\\.[0-9_]*+(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.float.Sexagesimal.yaml" + }, + { + "match": "\\G[-+]?+\\.(?>inf|Inf|INF)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.float.inf.yaml" + }, + { + "match": "\\G\\.(?>nan|NaN|NAN)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.float.nan.yaml" + }, + { + "comment": "https://www.w3.org/TR/NOTE-datetime does not allow spaces, however https://yaml.org/type/timestamp.html does, but the provided regex doesn't match the TZD space in many of the YAML examples", + "match": "\\G(?>[0-9]{4}-[0-9]{2,1}-[0-9]{2,1}(?>T|t|[\t ]++)[0-9]{2,1}:[0-9]{2}:[0-9]{2}(?>\\.[0-9]*+)?+[\t ]*+(?>Z|[-+][0-9]{2,1}(?>:[0-9]{2})?+)?+|[0-9]{4}-[0-9]{2}-[0-9]{2})(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.timestamp.yaml" + }, + { + "match": "\\G<<(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.merge.yaml" + }, + { + "match": "\\G=(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.value.yaml" + }, + { + "match": "\\G(?>!|&|\\*)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.yaml.yaml" + } + ] + }, + "tag-property": { + "comment": "https://yaml.org/spec/1.0/#c-ns-tag-property", + "//": [ + "!^", + "!!private_ns-tag-char+", + "!global_core_ns-tag-char+_no-:/!", + "!global_vocabulary_az09-_/ns-tag-char", + "!global_domain_ns-tag-char+.ns-tag-char+,1234(-12(-12)?)?/ns-tag-char*" + ], + "begin": "(?=!)", + "end": "(?=[\\x{2028 2029}\r\n\t ])", + "name": "storage.type.tag.yaml", + "patterns": [ + { + "match": "\\G!(?=[\\x{85 2028 2029}\r\n\t ])", + "name": "punctuation.definition.tag.non-specific.yaml" + }, + { + "comment": "https://yaml.org/spec/1.0/#c-ns-private-tag", + "match": "\\G!!", + "name": "punctuation.definition.tag.private.yaml" + }, + { + "comment": "https://yaml.org/spec/1.0/#ns-ns-global-tag", + "match": "\\G!", + "name": "punctuation.definition.tag.global.yaml" + }, + { + "comment": "https://yaml.org/spec/1.0/#c-prefix", + "match": "\\^", + "name": "punctuation.definition.tag.prefix.yaml" + }, + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\\x{85 2028 2029}\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "include": "#double-escape" + }, + { + "include": "#non-printable" + } + ] + }, + "anchor-property": { + "match": "(&)([^ \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)|(&)", + "captures": { + "0": { + "name": "keyword.control.flow.anchor.yaml" + }, + "1": { + "name": "punctuation.definition.anchor.yaml" + }, + "2": { + "name": "variable.other.anchor.yaml" + }, + "3": { + "name": "invalid.illegal.flow.anchor.yaml" + } + } + }, + "alias": { + "begin": "(\\*)([^ \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)|(\\*)", + "end": "(?=:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]|[,\\[\\]{}])", + "captures": { + "0": { + "name": "keyword.control.flow.alias.yaml" + }, + "1": { + "name": "punctuation.definition.alias.yaml" + }, + "2": { + "name": "variable.other.alias.yaml" + }, + "3": { + "name": "invalid.illegal.flow.alias.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + "presentation-detail": { + "patterns": [ + { + "match": "[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + }, + { + "include": "#comment" + }, + { + "include": "#unknown" + } + ] + }, + "non-printable": { + "//": { + "85": "…", + "2028": "
", + "2029": "
", + "10000": "𐀀", + "A0": " ", + "D7FF": "퟿", + "E000": "", + "FFFD": "�", + "FFFF": "￿", + "10FFFF": "􏿿" + }, + "match": "[^\t\n\r -~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]++", + "name": "invalid.illegal.non-printable.yaml" + }, + "comment": { + "comment": "Comments must be separated from other tokens by white space characters. `space`, `tab`, `newline` or `carriage-return`. `#(.*)` causes performance issues", + "begin": "(?<=^|[\\x{85 2028 2029} ])#", + "end": "[\\x{85 2028 2029}\r\n]", + "captures": { + "0": { + "name": "punctuation.definition.comment.yaml" + } + }, + "name": "comment.line.number-sign.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + "unknown": { + "match": ".[[^\\x{85}#\"':,\\[\\]{}]&&!-~\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]*+", + "name": "invalid.illegal.unrecognized.yaml markup.strikethrough" + } + } +} \ No newline at end of file diff --git a/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json new file mode 100644 index 00000000000..bda3a191ce8 --- /dev/null +++ b/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json @@ -0,0 +1,1792 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/RedCMD/YAML-Syntax-Highlighter/blob/master/syntaxes/yaml-1.1.tmLanguage.json", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/287c71aeb0773759497822b5e5ce4bdc4d5ef2aa", + "name": "YAML 1.1", + "scopeName": "source.yaml.1.1", + "comment": "https://yaml.org/spec/1.1/", + "patterns": [ + { + "include": "#stream" + } + ], + "repository": { + "stream": { + "patterns": [ + { + "comment": "allows me to just use `\\G` instead of the performance heavy `(^|\\G)`", + "begin": "^(?!\\G)", + "while": "^", + "name": "meta.stream.yaml", + "patterns": [ + { + "include": "#byte-order-mark" + }, + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G", + "while": "\\G", + "name": "meta.stream.yaml", + "patterns": [ + { + "include": "#byte-order-mark" + }, + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "directive-YAML": { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "(?=%YAML[ \t]+1\\.1(?=[\\x{85 2028 2029}\r\n\t ]))", + "end": "\\G(?=%(?!YAML[ \t]+1\\.1))", + "name": "meta.1.1.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "\\G(%)(YAML)([ \t]+)(1\\.1)", + "while": "\\G(?!---[\\x{85 2028 2029}\r\n\t ])", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.yaml.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "constant.numeric.yaml-version.yaml" + } + }, + "name": "meta.directives.yaml", + "patterns": [ + { + "include": "#directive-invalid" + }, + { + "include": "#directives" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G(?=---[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?!%)", + "patterns": [ + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#presentation-detail" + } + ] + }, + "directives": { + "comment": "https://yaml.org/spec/1.2.2/#68-directives", + "patterns": [ + { + "include": "source.yaml.1.3#directive-YAML" + }, + { + "include": "source.yaml.1.2#directive-YAML" + }, + { + "include": "source.yaml.1.1#directive-YAML" + }, + { + "include": "source.yaml.1.0#directive-YAML" + }, + { + "begin": "(?=%)", + "while": "\\G(?!%|---[\\x{85 2028 2029}\r\n\t ])", + "name": "meta.directives.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#682-tag-directives", + "begin": "\\G(%)(TAG)(?>([\t ]++)((!)(?>[0-9A-Za-z-]*+(!))?+))?+", + "end": "$", + "applyEndPatternLast": true, + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.tag.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "storage.type.tag-handle.yaml" + }, + "5": { + "name": "punctuation.definition.tag.begin.yaml" + }, + "6": { + "name": "punctuation.definition.tag.end.yaml" + }, + "comment": "https://yaml.org/spec/1.2.2/#rule-c-tag-handle" + }, + "patterns": [ + { + "comment": "technically the beginning should only validate against a valid uri scheme [A-Za-z][A-Za-z0-9.+-]*", + "begin": "\\G[\t ]++(?!#)", + "end": "(?=[\\x{85 2028 2029}\r\n\t ])", + "beginCaptures": { + "0": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "contentName": "support.type.tag-prefix.yaml", + "patterns": [ + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\\x{85 2028 2029}\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "\\G[,\\[\\]{}]", + "name": "invalid.illegal.character.uri.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\\x{85 2028 2029}\r\n\t a-zA-Z0-9-#;/?:@&=+$,_.!~*'()\\[\\]]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-reserved-directive", + "begin": "(%)([^ \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.other.yaml" + } + }, + "patterns": [ + { + "match": "\\G([\t ]++)([^ \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-name.yaml" + } + } + }, + { + "match": "([\t ]++)([^ \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-parameter.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "match": "\\G\\.{3}(?=[\\x{85 2028 2029}\r\n\t ])", + "name": "invalid.illegal.entity.other.document.end.yaml" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "directive-invalid": { + "patterns": [ + { + "match": "\\G\\.{3}(?=[\\x{85 2028 2029}\r\n\t ])", + "name": "invalid.illegal.entity.other.document.end.yaml" + }, + { + "begin": "\\G(%)(YAML)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "invalid.illegal.keyword.other.directive.yaml.yaml" + } + }, + "name": "meta.directive.yaml", + "patterns": [ + { + "match": "\\G([\t ]++|:)([0-9]++\\.[0-9]++)?+", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "constant.numeric.yaml-version.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "document": { + "comment": "https://yaml.org/spec/1.2.2/#91-documents", + "patterns": [ + { + "begin": "---(?=[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?!(?>\\.{3}|---)[\\x{85 2028 2029}\r\n\t ])", + "beginCaptures": { + "0": { + "name": "entity.other.document.begin.yaml" + } + }, + "name": "meta.document.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + { + "begin": "(?=\\.{3}[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?=[\t \\x{FEFF}]*+(?>#|$))", + "patterns": [ + { + "begin": "\\G\\.{3}", + "end": "$", + "beginCaptures": { + "0": { + "name": "entity.other.document.end.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#byte-order-mark" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G(?!%|[\t \\x{FEFF}]*+(?>#|$))", + "while": "\\G(?!(?>\\.{3}|---)[\\x{85 2028 2029}\r\n\t ])", + "name": "meta.document.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + } + ] + }, + "block-node": { + "patterns": [ + { + "include": "#block-sequence" + }, + { + "include": "#block-mapping" + }, + { + "include": "#block-scalar" + }, + { + "include": "#anchor-property" + }, + { + "include": "#tag-property" + }, + { + "include": "#alias" + }, + { + "begin": "(?=\"|')", + "while": "\\G", + "patterns": [ + { + "begin": "(?!\\G)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#double" + }, + { + "include": "#single" + } + ] + }, + { + "begin": "(?=\\[|{)", + "while": "\\G", + "patterns": [ + { + "include": "#block-mapping" + }, + { + "begin": "(?!\\G)(?![\r\n\t ])", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + } + ] + }, + { + "include": "#block-plain-out" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-mapping": { + "//": "The check for plain keys is expensive", + "begin": "(?=((?<=[-?:]) )?+)(?[!&*][^\\x{85 2028 2029}\r\n\t ]*+[\t ]++)*+)(?=(?>(?#Double Quote)\"(?>[^\\\\\"]++|\\\\.)*+\"|(?#Single Quote)'(?>[^']++|'')*+'|(?#Plain)(?>[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ]))(?>[^:#]++|:(?![\\x{85 2028 2029}\r\n\t ])|(?(\\1\\2)((?>[!&*][^\\x{85 2028 2029}\r\n\t ]*+[\t ]++)*+)((?>\t[\t ]*+)?+[^\\x{85 2028 2029}\r\n\t ?:\\-#!&*\"'\\[\\]{}0-9A-Za-z$()+./;<=\\\\^_~\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}])?+|( *+)([\t ]*+[^\\x{85 2028 2029}\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "comment": "May cause lag on long lines starting with a tag, anchor or alias", + "patterns": [ + { + "include": "#tag-property" + }, + { + "include": "#anchor-property" + }, + { + "include": "#alias" + }, + { + "include": "#presentation-detail" + } + ] + }, + "5": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "comment": "May cause lag on long lines starting with a tag, anchor or alias", + "patterns": [ + { + "include": "#tag-property" + }, + { + "include": "#anchor-property" + }, + { + "include": "#alias" + }, + { + "include": "#presentation-detail" + } + ] + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "4": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "5": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.mapping.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style (BLOCK-KEY)", + "begin": "\\G\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "include": "#double-escape" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + { + "include": "#block-key-plain" + }, + { + "include": "#block-map-value" + }, + { + "include": "#block-map-explicit" + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#rule-l+block-sequence", + "begin": "(?=((?<=[-?:]) )?+)(?(\\1\\2)(?!-[\\x{85 2028 2029}\r\n\t ])((?>\t[\t ]*+)?+[^\\x{85 2028 2029}\r\n\t #\\]}])?+|(?!\\1\\2)( *+)([\t ]*+[^\\x{85 2028 2029}\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.definition.block.sequence.item.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "4": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.block.sequence.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "block-map-explicit": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-explicit-key", + "begin": "(?=((?<=[-?:]) )?+)\\G( *+)(\\?)(?=[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?>(\\1\\2)(?![?:0-9A-Za-z$()+./;<=\\\\^_~\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{2028 2029 FEFF}]])((?>\t[\t ]*+)?+[^\\x{85 2028 2029}\r\n\t #\\-\\[\\]{}])?+|(?!\\1\\2)( *+)([\t ]*+[^\\x{85 2028 2029}\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.definition.map.key.yaml" + }, + "4": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "4": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.map.explicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-key-plain-out" + }, + { + "include": "#block-map-value" + }, + { + "include": "#block-node" + } + ] + }, + "block-map-value": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", + "begin": "(:)(?=[\\x{85 2028 2029}\r\n\t ])", + "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{2028 2029 FEFF}]]|-[^\\x{85 2028 2029}\r\n\t ])", + "beginCaptures": { + "1": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.map.value.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "block-key-plain": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (BLOCK-KEY)", + "begin": "\\G(?=[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ]))", + "end": "(?=[\t ]*+:[\\x{85 2028 2029}\r\n\t ]|(?>[\t ]++|\\G)#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": "\\G([\t ]++)(.)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "invalid.illegal.multiline-key.yaml" + } + } + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "block-scalar": { + "comment": "https://yaml.org/spec/1.2.2/#81-block-scalar-styles", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#8111-block-indentation-indicator", + "begin": "([\t ]*+)(?>(\\|)|(>))(?[+-])?+((1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)|(9))(?()|([+-]))?+", + "while": "\\G(?>(?>(?!\\6) |(?!\\7) {2}|(?!\\8) {3}|(?!\\9) {4}|(?!\\10) {5}|(?!\\11) {6}|(?!\\12) {7}|(?!\\13) {8}|(?!\\14) {9})| *+($|[^#]))", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "3": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + }, + "5": { + "name": "constant.numeric.indentation-indicator.yaml" + }, + "15": { + "name": "storage.modifier.chomping-indicator.yaml" + } + }, + "whileCaptures": { + "0": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "1": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "begin": "$", + "while": "\\G", + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + { + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-b-block-header", + "//": "Soooooooo many edge cases", + "begin": "([\t ]*+)(?>(\\|)|(>))([+-]?+)", + "while": "\\G", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "3": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-literal-content", + "begin": "$", + "while": "\\G", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "//": "Find the highest indented line", + "begin": "\\G( ++)$", + "while": "\\G(?>(\\1)$|(?!\\1)( *+)($|.))", + "captures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-b-nb-literal-next", + "//": [ + "Funky wrapper function", + "The `end` pattern clears the parent `\\G` anchor", + "Affectively forcing this rule to only match at most once", + "https://github.com/microsoft/vscode-textmate/issues/114" + ], + "begin": "\\G(?!$)(?=( *+))", + "end": "\\G(?!\\1)(?=[\t ]*+#)", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "begin": "\\G( *+)", + "while": "\\G(?>(\\1)|( *+)($|[^\t#]|[\t ]++[^#]))", + "captures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-chomped-empty", + "begin": "(?!\\G)(?=[\t ]*+#)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + { + "comment": "Header Comment", + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + } + ] + }, + "block-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-OUT)", + "begin": "(?=[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ]))", + "while": "\\G", + "patterns": [ + { + "begin": "\\G", + "end": "(?=(?>[\t ]++|\\G)#)", + "name": "string.unquoted.plain.out.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": ":(?=[\\x{85 2028 2029}\r\n\t ])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + { + "begin": "(?!\\G)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-node": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-seq-entry (FLOW-IN)", + "patterns": [ + { + "begin": "(?=\\[|{)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "begin": "(?!\\G)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + } + ] + }, + { + "include": "#anchor-property" + }, + { + "include": "#tag-property" + }, + { + "include": "#alias" + }, + { + "begin": "(?=\"|')", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "begin": "(?!\\G)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#double" + }, + { + "include": "#single" + } + ] + }, + { + "include": "#flow-plain-in" + }, + { + "include": "#presentation-detail" + } + ] + }, + "flow-mapping": { + "comment": "https://yaml.org/spec/1.2.2/#742-flow-mappings", + "begin": "{", + "end": "}", + "beginCaptures": { + "0": { + "name": "punctuation.definition.mapping.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.mapping.end.yaml" + } + }, + "name": "meta.flow.mapping.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-flow-map-entries", + "begin": "(?<={)\\G(?=[\\x{85 2028 2029}\r\n\t ,#])|,", + "end": "(?=[^\\x{85 2028 2029}\r\n\t ,#])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.mapping.yaml" + } + }, + "patterns": [ + { + "match": ",++", + "name": "invalid.illegal.separator.sequence.yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-map-key-mapping" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#741-flow-sequences", + "begin": "\\[", + "end": "]", + "beginCaptures": { + "0": { + "name": "punctuation.definition.sequence.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.sequence.end.yaml" + } + }, + "name": "meta.flow.sequence.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-flow-seq-entries", + "begin": "(?<=\\[)\\G(?=[\\x{85 2028 2029}\r\n\t ,#])|,", + "end": "(?=[^\\x{85 2028 2029}\r\n\t ,#])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.sequence.yaml" + } + }, + "patterns": [ + { + "match": ",++", + "name": "invalid.illegal.separator.sequence.yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-map-key-sequence" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-map-key-mapping": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-entry (FLOW-IN)", + "patterns": [ + { + "begin": "\\?(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\[\\]{}])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "name": "meta.flow.map.explicit.yaml", + "patterns": [ + { + "include": "#flow-map-key-mapping" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?=(?>[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ,\\[\\]{}])))", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#flow-key-plain-in" + }, + { + "match": ":(?=\\[|{)", + "name": "invalid.illegal.separator.map.yaml" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?=\"|')", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-map-key-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-entry (FLOW-IN)", + "patterns": [ + { + "begin": "\\?(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\[\\]{}])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "name": "meta.flow.map.explicit.yaml", + "patterns": [ + { + "include": "#flow-map-key-mapping" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?<=[\t ,\\[{]|^)(?=(?>[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))(?>[^:#,\\[\\]{}]++|:(?![\\x{85 2028 2029}\r\n\t ,\\[\\]{}])|(?\"(?>[^\\\\\"]++|\\\\.)*+\"|'(?>[^']++|'')*+')[\t ]*+:)", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-map-value-yaml": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-flow-map-separate-value (FLOW-IN)", + "begin": ":(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\]}])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.flow.pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + }, + "flow-map-value-json": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-flow-map-separate-value (FLOW-IN)", + "begin": "(?<=(?>[\"'\\]}]|^)[\t ]*+):", + "end": "(?=[,\\]}])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.flow.pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + }, + "flow-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-IN)", + "begin": "(?=[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "end": "(?=(?>[\t ]++|\\G)#|[\t ]*+[,\\[\\]{}])", + "name": "string.unquoted.plain.in.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-in" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": ":(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "flow-key-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (FLOW-OUT)", + "begin": "(?=[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}]|[?:-](?![\\x{85 2028 2029}\r\n\t ]))", + "end": "(?=[\t ]*+:[\\x{85 2028 2029}\r\n\t ]|[\t ]++#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "flow-key-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-implicit-yaml-key (FLOW-KEY)", + "begin": "\\G(?![\\x{85 2028 2029}\r\n\t #])", + "end": "(?=[\t ]*+(?>:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]|[,\\[\\]{}])|[\t ]++#)", + "name": "meta.flow.map.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-in" + }, + { + "include": "#non-printable" + } + ] + }, + "key-double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style", + "begin": "\\G\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "include": "#double-escape" + } + ] + }, + "key-single": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + "double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style", + "begin": "\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "string.quoted.double.yaml", + "patterns": [ + { + "match": "(?x[^\"]{2,0}|u[^\"]{4,0}|U[^\"]{8,0}|.)", + "name": "invalid.illegal.constant.character.escape.yaml" + } + ] + }, + "tag-implicit-plain-in": { + "comment": "https://yaml.org/type/index.html", + "patterns": [ + { + "match": "\\G(?>null|Null|NULL|~)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.null.yaml" + }, + { + "match": "\\G(?>true|True|TRUE|false|False|FALSE|y|Y|yes|Yes|YES|n|N|no|No|NO|on|On|ON|off|Off|OFF)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.boolean.yaml" + }, + { + "match": "\\G[-+]?+(0|[1-9][0-9_]*+)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.decimal.yaml" + }, + { + "match": "\\G[-+]?+0b[0-1_]++(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.binary.yaml" + }, + { + "match": "\\G[-+]?0[0-7_]++(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.octal.yaml" + }, + { + "match": "\\G[-+]?+0x[0-9a-fA-F_]++(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.hexadecimal.yaml" + }, + { + "match": "\\G[-+]?+[1-9][0-9_]*+(?>:[0-5]?[0-9])++(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.Sexagesimal.yaml" + }, + { + "match": "\\G[-+]?+(?>[0-9][0-9_]*+)?+\\.[0-9.]*+(?>[eE][-+][0-9]+)?+(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.decimal.yaml" + }, + { + "match": "\\G[-+]?+[0-9][0-9_]*+(?>:[0-5]?[0-9])++\\.[0-9_]*+(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.Sexagesimal.yaml" + }, + { + "match": "\\G[-+]?+\\.(?>inf|Inf|INF)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.inf.yaml" + }, + { + "match": "\\G\\.(?>nan|NaN|NAN)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.nan.yaml" + }, + { + "comment": "https://www.w3.org/TR/NOTE-datetime does not allow spaces, however https://yaml.org/type/timestamp.html does, but the provided regex doesn't match the TZD space in many of the YAML examples", + "match": "\\G(?>[0-9]{4}-[0-9]{2,1}-[0-9]{2,1}(?>T|t|[\t ]++)[0-9]{2,1}:[0-9]{2}:[0-9]{2}(?>\\.[0-9]*+)?+[\t ]*+(?>Z|[-+][0-9]{2,1}(?>:[0-9]{2})?+)?+|[0-9]{4}-[0-9]{2}-[0-9]{2})(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.timestamp.yaml" + }, + { + "match": "\\G<<(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.merge.yaml" + }, + { + "match": "\\G=(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.value.yaml" + }, + { + "match": "\\G(?>!|&|\\*)(?=[\t ]++#|[\t ]*+(?>[\\x{85 2028 2029}\r\n,\\]}]|:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]))", + "name": "constant.language.yaml.yaml" + } + ] + }, + "tag-implicit-plain-out": { + "comment": "https://yaml.org/type/index.html", + "patterns": [ + { + "match": "\\G(?>null|Null|NULL|~)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.null.yaml" + }, + { + "match": "\\G(?>true|True|TRUE|false|False|FALSE|yes|Yes|YES|y|Y|no|No|NO|n|N|on|On|ON|off|Off|OFF)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.boolean.yaml" + }, + { + "match": "\\G[-+]?+(0|[1-9][0-9_]*+)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.decimal.yaml" + }, + { + "match": "\\G[-+]?+0b[0-1_]++(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.binary.yaml" + }, + { + "match": "\\G[-+]?0[0-7_]++(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.octal.yaml" + }, + { + "match": "\\G[-+]?+0x[0-9a-fA-F_]++(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.hexadecimal.yaml" + }, + { + "match": "\\G[-+]?+[1-9][0-9_]*+(?>:[0-5]?[0-9])++(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.integer.Sexagesimal.yaml" + }, + { + "match": "\\G[-+]?+(?>[0-9][0-9_]*+)?+\\.[0-9.]*+(?>[eE][-+][0-9]+)?+(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.float.decimal.yaml" + }, + { + "match": "\\G[-+]?+[0-9][0-9_]*+(?>:[0-5]?[0-9])++\\.[0-9_]*+(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.float.Sexagesimal.yaml" + }, + { + "match": "\\G[-+]?+\\.(?>inf|Inf|INF)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.float.inf.yaml" + }, + { + "match": "\\G\\.(?>nan|NaN|NAN)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.float.nan.yaml" + }, + { + "comment": "https://www.w3.org/TR/NOTE-datetime does not allow spaces, however https://yaml.org/type/timestamp.html does, but the provided regex doesn't match the TZD space in many of the YAML examples", + "match": "\\G(?>[0-9]{4}-[0-9]{2,1}-[0-9]{2,1}(?>T|t|[\t ]++)[0-9]{2,1}:[0-9]{2}:[0-9]{2}(?>\\.[0-9]*+)?+[\t ]*+(?>Z|[-+][0-9]{2,1}(?>:[0-9]{2})?+)?+|[0-9]{4}-[0-9]{2}-[0-9]{2})(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.numeric.timestamp.yaml" + }, + { + "match": "\\G<<(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.merge.yaml" + }, + { + "match": "\\G=(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.value.yaml" + }, + { + "match": "\\G(?>!|&|\\*)(?=[\t ]++#|[\t ]*+(?>$|:[\\x{85 2028 2029}\r\n\t ]))", + "name": "constant.language.yaml.yaml" + } + ] + }, + "tag-property": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-tag-property", + "//": [ + "!", + "!!", + "!<>", + "!...", + "!!...", + "!<...>", + "!...!..." + ], + "patterns": [ + { + "match": "!(?=[\\x{85 2028 2029}\r\n\t ])", + "name": "storage.type.tag.non-specific.yaml punctuation.definition.tag.non-specific.yaml" + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-verbatim-tag", + "begin": "!<", + "end": ">", + "beginCaptures": { + "0": { + "name": "punctuation.definition.tag.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.yaml" + } + }, + "name": "storage.type.tag.verbatim.yaml", + "patterns": [ + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\\x{85 2028 2029}\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\\x{85 2028 2029}\r\n\t a-zA-Z0-9-#;/?:@&=+$,_.!~*'()\\[\\]%>]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-shorthand-tag", + "begin": "(?=!)", + "end": "(?=[\\x{85 2028 2029}\r\n\t ,\\[\\]{}])", + "name": "storage.type.tag.shorthand.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-secondary-tag-handle", + "match": "\\G!!", + "name": "punctuation.definition.tag.secondary.yaml" + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-secondary-tag-handle", + "match": "\\G(!)[0-9A-Za-z-]++(!)", + "captures": { + "1": { + "name": "punctuation.definition.tag.named.yaml" + }, + "2": { + "name": "punctuation.definition.tag.named.yaml" + } + } + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-primary-tag-handle", + "match": "\\G!", + "name": "punctuation.definition.tag.primary.yaml" + }, + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\\x{85 2028 2029}\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\\x{85 2028 2029}\r\n\t a-zA-Z0-9-#;/?:@&=+$,_.~*'()\\[\\]%]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + } + ] + }, + "anchor-property": { + "match": "(&)([^ \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)|(&)", + "captures": { + "0": { + "name": "keyword.control.flow.anchor.yaml" + }, + "1": { + "name": "punctuation.definition.anchor.yaml" + }, + "2": { + "name": "variable.other.anchor.yaml" + }, + "3": { + "name": "invalid.illegal.flow.anchor.yaml" + } + } + }, + "alias": { + "begin": "(\\*)([^ \\p{Cntrl}\\p{Surrogate}\\x{2028 2029 FFFE FFFF}]++)|(\\*)", + "end": "(?=:[\\x{85 2028 2029}\r\n\t ,\\[\\]{}]|[,\\[\\]{}])", + "captures": { + "0": { + "name": "keyword.control.flow.alias.yaml" + }, + "1": { + "name": "punctuation.definition.alias.yaml" + }, + "2": { + "name": "variable.other.alias.yaml" + }, + "3": { + "name": "invalid.illegal.flow.alias.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + "byte-order-mark": { + "comment": "", + "begin": "\\G", + "while": "\\G(?=[\\x{FEFF 85 2028 2029}\r\n\t ])", + "patterns": [ + { + "begin": "(?=#)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G\\x{FEFF}", + "while": "\\G", + "beginCaptures": { + "0": { + "name": "byte-order-mark.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#presentation-detail" + } + ] + }, + "presentation-detail": { + "patterns": [ + { + "match": "[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "comment": "https://yaml.org/spec/1.1/#id871136", + "match": "[\\x{85 2028 2029}\r\n]++", + "name": "punctuation.separator.line-break.yaml" + }, + { + "include": "#non-printable" + }, + { + "include": "#comment" + }, + { + "include": "#unknown" + } + ] + }, + "non-printable": { + "//": { + "85": "…", + "2028": "
", + "2029": "
", + "10000": "𐀀", + "A0": " ", + "D7FF": "퟿", + "E000": "", + "FFFD": "�", + "FEFF": "", + "FFFF": "￿", + "10FFFF": "􏿿" + }, + "match": "[^\t\n\r -~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]++", + "name": "invalid.illegal.non-printable.yaml" + }, + "comment": { + "comment": "Comments must be separated from other tokens by white space characters. `space`, `newline` or `carriage-return`. `#(.*)` causes performance issues", + "begin": "(?<=^|[\\x{FEFF 85 2028 2029} ])#", + "end": "[\\x{85 2028 2029}\r\n]", + "captures": { + "0": { + "name": "punctuation.definition.comment.yaml" + } + }, + "name": "comment.line.number-sign.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + "unknown": { + "match": ".[[^\\x{85}#\"':,\\[\\]{}]&&!-~\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]*+", + "name": "invalid.illegal.unrecognized.yaml markup.strikethrough" + } + } +} \ No newline at end of file diff --git a/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json new file mode 100644 index 00000000000..b2a921a5dd1 --- /dev/null +++ b/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json @@ -0,0 +1,1714 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/RedCMD/YAML-Syntax-Highlighter/blob/master/syntaxes/yaml-1.2.tmLanguage.json", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/287c71aeb0773759497822b5e5ce4bdc4d5ef2aa", + "name": "YAML 1.2", + "scopeName": "source.yaml.1.2", + "comment": "https://yaml.org/spec/1.2.2", + "patterns": [ + { + "include": "#stream" + } + ], + "repository": { + "stream": { + "patterns": [ + { + "comment": "allows me to just use `\\G` instead of the performance heavy `(^|\\G)`", + "begin": "^(?!\\G)", + "while": "^", + "name": "meta.stream.yaml", + "patterns": [ + { + "include": "#byte-order-mark" + }, + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G", + "while": "\\G", + "name": "meta.stream.yaml", + "patterns": [ + { + "include": "#byte-order-mark" + }, + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "directive-YAML": { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "(?=%YAML[\t ]+1\\.2(?=[\r\n\t ]))", + "end": "\\G(?=(?>\\.{3}|---)[\r\n\t ])", + "name": "meta.1.2.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "\\G(%)(YAML)([\t ]+)(1\\.2)", + "end": "\\G(?=---[\r\n\t ])", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.yaml.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "constant.numeric.yaml-version.yaml" + } + }, + "name": "meta.directives.yaml", + "patterns": [ + { + "include": "#directive-invalid" + }, + { + "include": "#directives" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#document" + } + ] + }, + "directives": { + "comment": "https://yaml.org/spec/1.2.2/#68-directives", + "patterns": [ + { + "include": "source.yaml.1.3#directive-YAML" + }, + { + "include": "source.yaml.1.2#directive-YAML" + }, + { + "include": "source.yaml.1.1#directive-YAML" + }, + { + "include": "source.yaml.1.0#directive-YAML" + }, + { + "begin": "(?=%)", + "while": "\\G(?!%|---[\r\n\t ])", + "name": "meta.directives.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#682-tag-directives", + "begin": "\\G(%)(TAG)(?>([\t ]++)((!)(?>[0-9A-Za-z-]*+(!))?+))?+", + "end": "$", + "applyEndPatternLast": true, + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.tag.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "storage.type.tag-handle.yaml" + }, + "5": { + "name": "punctuation.definition.tag.begin.yaml" + }, + "6": { + "name": "punctuation.definition.tag.end.yaml" + }, + "comment": "https://yaml.org/spec/1.2.2/#rule-c-tag-handle" + }, + "patterns": [ + { + "comment": "technically the beginning should only validate against a valid uri scheme [A-Za-z][A-Za-z0-9.+-]*", + "begin": "\\G[\t ]++(?!#)", + "end": "(?=[\r\n\t ])", + "beginCaptures": { + "0": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "contentName": "support.type.tag-prefix.yaml", + "patterns": [ + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "\\G[,\\[\\]{}]", + "name": "invalid.illegal.character.uri.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\r\n\t a-zA-Z0-9-#;/?:@&=+$,_.!~*'()\\[\\]]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-reserved-directive", + "begin": "(%)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.other.yaml" + } + }, + "patterns": [ + { + "match": "\\G([\t ]++)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-name.yaml" + } + } + }, + { + "match": "([\t ]++)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-parameter.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "match": "\\G\\.{3}(?=[\r\n\t ])", + "name": "invalid.illegal.entity.other.document.end.yaml" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "directive-invalid": { + "patterns": [ + { + "match": "\\G\\.{3}(?=[\r\n\t ])", + "name": "invalid.illegal.entity.other.document.end.yaml" + }, + { + "begin": "\\G(%)(YAML)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "invalid.illegal.keyword.other.directive.yaml.yaml" + } + }, + "name": "meta.directive.yaml", + "patterns": [ + { + "match": "\\G([\t ]++|:)([0-9]++\\.[0-9]++)?+", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "constant.numeric.yaml-version.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "document": { + "comment": "https://yaml.org/spec/1.2.2/#91-documents", + "patterns": [ + { + "begin": "---(?=[\r\n\t ])", + "while": "\\G(?!(?>\\.{3}|---)[\r\n\t ])", + "beginCaptures": { + "0": { + "name": "entity.other.document.begin.yaml" + } + }, + "name": "meta.document.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + { + "begin": "(?=\\.{3}[\r\n\t ])", + "while": "\\G(?=[\t \\x{FEFF}]*+(?>#|$))", + "patterns": [ + { + "begin": "\\G\\.{3}", + "end": "$", + "beginCaptures": { + "0": { + "name": "entity.other.document.end.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#byte-order-mark" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G(?!%|[\t \\x{FEFF}]*+(?>#|$))", + "while": "\\G(?!(?>\\.{3}|---)[\r\n\t ])", + "name": "meta.document.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + } + ] + }, + "block-node": { + "patterns": [ + { + "include": "#block-sequence" + }, + { + "include": "#block-mapping" + }, + { + "include": "#block-scalar" + }, + { + "include": "#anchor-property" + }, + { + "include": "#tag-property" + }, + { + "include": "#alias" + }, + { + "begin": "(?=\"|')", + "while": "\\G", + "patterns": [ + { + "begin": "(?!\\G)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#double" + }, + { + "include": "#single" + } + ] + }, + { + "begin": "(?=\\[|{)", + "while": "\\G", + "patterns": [ + { + "include": "#block-mapping" + }, + { + "begin": "(?!\\G)(?![\r\n\t ])", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + } + ] + }, + { + "include": "#block-plain-out" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-mapping": { + "//": "The check for plain keys is expensive", + "begin": "(?=((?<=[-?:]) )?+)(?((?>[!&*][^\r\n\t ]*+[\t ]++)*+)(?=(?>(?#Double Quote)\"(?>[^\\\\\"]++|\\\\.)*+\"|(?#Single Quote)'(?>[^']++|'')*+'|(?#Plain)(?>[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))(?>[^:#]++|:(?![\r\n\t ])|(?(\\1\\2)((?>[!&*][^\r\n\t ]*+[\t ]++)*+)((?>\t[\t ]*+)?+[^\r\n\t ?:\\-#!&*\"'\\[\\]{}0-9A-Za-z$()+./;<=\\\\^_~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}])?+|( *+)([\t ]*+[^\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "comment": "May cause lag on long lines starting with a tag, anchor or alias", + "patterns": [ + { + "include": "#tag-property" + }, + { + "include": "#anchor-property" + }, + { + "include": "#alias" + }, + { + "include": "#presentation-detail" + } + ] + }, + "5": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "comment": "May cause lag on long lines starting with a tag, anchor or alias", + "patterns": [ + { + "include": "#tag-property" + }, + { + "include": "#anchor-property" + }, + { + "include": "#alias" + }, + { + "include": "#presentation-detail" + } + ] + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "4": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "5": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.mapping.yaml", + "patterns": [ + { + "include": "#block-map-key-double" + }, + { + "include": "#block-map-key-single" + }, + { + "include": "#block-map-key-plain" + }, + { + "include": "#block-map-key-explicit" + }, + { + "include": "#block-map-value" + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#rule-l+block-sequence", + "begin": "(?=((?<=[-?:]) )?+)(?(\\1\\2)(?!-[\r\n\t ])((?>\t[\t ]*+)?+[^\r\n\t #\\]}])?+|(?!\\1\\2)( *+)([\t ]*+[^\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.definition.block.sequence.item.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "4": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.block.sequence.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "block-map-key-explicit": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-explicit-key", + "begin": "(?=((?<=[-?:]) )?+)\\G( *+)(\\?)(?=[\r\n\t ])", + "while": "\\G(?>(\\1\\2)(?![?:0-9A-Za-z$()+./;<=\\\\^_~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{FEFF}]])((?>\t[\t ]*+)?+[^\r\n\t #\\-\\[\\]{}])?+|(?!\\1\\2)( *+)([\t ]*+[^\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.definition.map.key.yaml" + }, + "4": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "4": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.map.explicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-key-plain-out" + }, + { + "include": "#block-map-value" + }, + { + "include": "#block-node" + } + ] + }, + "block-map-key-double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style (BLOCK-KEY)", + "begin": "\\G\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "include": "#double-escape" + } + ] + }, + "block-map-key-single": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + "block-map-key-plain": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (BLOCK-KEY)", + "begin": "\\G(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))", + "end": "(?=[\t ]*+:[\r\n\t ]|(?>[\t ]++|\\G)#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": "\\G([\t ]++)(.)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "invalid.illegal.multiline-key.yaml" + } + } + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "block-map-value": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", + "begin": ":(?=[\r\n\t ])", + "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{FEFF}]]|-[^\r\n\t ])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.map.value.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "block-scalar": { + "comment": "https://yaml.org/spec/1.2.2/#81-block-scalar-styles", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#8111-block-indentation-indicator", + "begin": "([\t ]*+)(?>(\\|)|(>))(?[+-])?+((1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)|(9))(?()|([+-]))?+", + "while": "\\G(?>(?>(?!\\6) |(?!\\7) {2}|(?!\\8) {3}|(?!\\9) {4}|(?!\\10) {5}|(?!\\11) {6}|(?!\\12) {7}|(?!\\13) {8}|(?!\\14) {9})| *+($|[^#]))", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "3": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + }, + "5": { + "name": "constant.numeric.indentation-indicator.yaml" + }, + "15": { + "name": "storage.modifier.chomping-indicator.yaml" + } + }, + "whileCaptures": { + "0": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "1": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "begin": "$", + "while": "\\G", + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + { + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-b-block-header", + "//": "Soooooooo many edge cases", + "begin": "([\t ]*+)(?>(\\|)|(>))([+-]?+)", + "while": "\\G", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "3": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-literal-content", + "begin": "$", + "while": "\\G", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "//": "Find the highest indented line", + "begin": "\\G( ++)$", + "while": "\\G(?>(\\1)$|(?!\\1)( *+)($|.))", + "captures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-b-nb-literal-next", + "//": [ + "Funky wrapper function", + "The `end` pattern clears the parent `\\G` anchor", + "Affectively forcing this rule to only match at most once", + "https://github.com/microsoft/vscode-textmate/issues/114" + ], + "begin": "\\G(?!$)(?=( *+))", + "end": "\\G(?!\\1)(?=[\t ]*+#)", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "begin": "\\G( *+)", + "while": "\\G(?>(\\1)|( *+)($|[^\t#]|[\t ]++[^#]))", + "captures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-chomped-empty", + "begin": "(?!\\G)(?=[\t ]*+#)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + { + "comment": "Header Comment", + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + } + ] + }, + "block-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-OUT)", + "begin": "(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))", + "while": "\\G", + "patterns": [ + { + "begin": "\\G", + "end": "(?=(?>[\t ]++|\\G)#)", + "name": "string.unquoted.plain.out.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": ":(?=[\r\n\t ])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + { + "begin": "(?!\\G)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-node": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-seq-entry (FLOW-IN)", + "patterns": [ + { + "begin": "(?=\\[|{)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "begin": "(?!\\G)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + } + ] + }, + { + "include": "#anchor-property" + }, + { + "include": "#tag-property" + }, + { + "include": "#alias" + }, + { + "begin": "(?=\"|')", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "begin": "(?!\\G)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#double" + }, + { + "include": "#single" + } + ] + }, + { + "include": "#flow-plain-in" + }, + { + "include": "#presentation-detail" + } + ] + }, + "flow-mapping": { + "comment": "https://yaml.org/spec/1.2.2/#742-flow-mappings", + "begin": "{", + "end": "}", + "beginCaptures": { + "0": { + "name": "punctuation.definition.mapping.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.mapping.end.yaml" + } + }, + "name": "meta.flow.mapping.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-flow-map-entries", + "begin": "(?<={)\\G(?=[\r\n\t ,#])|,", + "end": "(?=[^\r\n\t ,#])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.mapping.yaml" + } + }, + "patterns": [ + { + "match": ",++", + "name": "invalid.illegal.separator.sequence.yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping-map-key" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#741-flow-sequences", + "begin": "\\[", + "end": "]", + "beginCaptures": { + "0": { + "name": "punctuation.definition.sequence.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.sequence.end.yaml" + } + }, + "name": "meta.flow.sequence.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-flow-seq-entries", + "begin": "(?<=\\[)\\G(?=[\r\n\t ,#])|,", + "end": "(?=[^\r\n\t ,#])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.sequence.yaml" + } + }, + "patterns": [ + { + "match": ",++", + "name": "invalid.illegal.separator.sequence.yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-sequence-map-key" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-mapping-map-key": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-entry (FLOW-IN)", + "patterns": [ + { + "begin": "\\?(?=[\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\[\\]{}])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "name": "meta.flow.map.explicit.yaml", + "patterns": [ + { + "include": "#flow-mapping-map-key" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?=(?>[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ,\\[\\]{}])))", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#flow-key-plain-in" + }, + { + "match": ":(?=\\[|{)", + "name": "invalid.illegal.separator.map.yaml" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?=\"|')", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-sequence-map-key": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-entry (FLOW-IN)", + "patterns": [ + { + "begin": "\\?(?=[\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\[\\]{}])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "name": "meta.flow.map.explicit.yaml", + "patterns": [ + { + "include": "#flow-mapping-map-key" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?<=[\t ,\\[{]|^)(?=(?>[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ,\\[\\]{}]))(?>[^:#,\\[\\]{}]++|:(?![\r\n\t ,\\[\\]{}])|(?\"(?>[^\\\\\"]++|\\\\.)*+\"|'(?>[^']++|'')*+')[\t ]*+:)", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-map-value-yaml": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-flow-map-separate-value (FLOW-IN)", + "begin": ":(?=[\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\]}])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.flow.pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + }, + "flow-map-value-json": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-flow-map-separate-value (FLOW-IN)", + "begin": "(?<=(?>[\"'\\]}]|^)[\t ]*+):", + "end": "(?=[,\\]}])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.flow.pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + }, + "flow-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-IN)", + "begin": "(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ,\\[\\]{}]))", + "end": "(?=(?>[\t ]++|\\G)#|[\t ]*+[,\\[\\]{}])", + "name": "string.unquoted.plain.in.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-in" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": ":(?=[\r\n\t ,\\[\\]{}])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "flow-key-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (FLOW-OUT)", + "begin": "(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))", + "end": "(?=[\t ]*+:[\r\n\t ]|[\t ]++#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "flow-key-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-implicit-yaml-key (FLOW-KEY)", + "begin": "\\G(?![\r\n\t #])", + "end": "(?=[\t ]*+(?>:[\r\n\t ,\\[\\]{}]|[,\\[\\]{}])|[\t ]++#)", + "name": "meta.flow.map.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-in" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "key-double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style", + "begin": "\\G\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "include": "#double-escape" + } + ] + }, + "key-single": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + "double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style", + "begin": "\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "string.quoted.double.yaml", + "patterns": [ + { + "match": "(?x[^\"]{2,0}|u[^\"]{4,0}|U[^\"]{8,0}|.)", + "name": "invalid.illegal.constant.character.escape.yaml" + } + ] + }, + "tag-implicit-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#103-core-schema", + "patterns": [ + { + "match": "\\G(?>null|Null|NULL|~)(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.language.null.yaml" + }, + { + "match": "\\G(?>true|True|TRUE|false|False|FALSE)(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.language.boolean.yaml" + }, + { + "match": "\\G[+-]?+[0-9]++(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.decimal.yaml" + }, + { + "match": "\\G0o[0-7]++(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.octal.yaml" + }, + { + "match": "\\G0x[0-9a-fA-F]++(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.hexadecimal.yaml" + }, + { + "match": "\\G[+-]?+(?>\\.[0-9]++|[0-9]++(?>\\.[0-9]*+)?+)(?>[eE][+-]?+[0-9]++)?+(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.yaml" + }, + { + "match": "\\G[+-]?+\\.(?>inf|Inf|INF)(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.inf.yaml" + }, + { + "match": "\\G\\.(?>nan|NaN|NAN)(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.nan.yaml" + } + ] + }, + "tag-implicit-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#103-core-schema", + "patterns": [ + { + "match": "\\G(?>null|Null|NULL|~)(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.language.null.yaml" + }, + { + "match": "\\G(?>true|True|TRUE|false|False|FALSE)(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.language.boolean.yaml" + }, + { + "match": "\\G[+-]?+[0-9]++(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.integer.decimal.yaml" + }, + { + "match": "\\G0o[0-7]++(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.integer.octal.yaml" + }, + { + "match": "\\G0x[0-9a-fA-F]++(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.integer.hexadecimal.yaml" + }, + { + "match": "\\G[+-]?+(?>\\.[0-9]++|[0-9]++(?>\\.[0-9]*+)?+)(?>[eE][+-]?+[0-9]++)?+(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.float.yaml" + }, + { + "match": "\\G[+-]?+\\.(?>inf|Inf|INF)(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.float.inf.yaml" + }, + { + "match": "\\G\\.(?>nan|NaN|NAN)(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.float.nan.yaml" + } + ] + }, + "tag-property": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-tag-property", + "//": [ + "!", + "!!", + "!<>", + "!...", + "!!...", + "!<...>", + "!...!..." + ], + "patterns": [ + { + "match": "!(?=[\r\n\t ])", + "name": "storage.type.tag.non-specific.yaml punctuation.definition.tag.non-specific.yaml" + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-verbatim-tag", + "begin": "!<", + "end": ">", + "beginCaptures": { + "0": { + "name": "punctuation.definition.tag.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.yaml" + } + }, + "name": "storage.type.tag.verbatim.yaml", + "patterns": [ + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\r\n\t a-zA-Z0-9-#;/?:@&=+$,_.!~*'()\\[\\]%>]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-shorthand-tag", + "begin": "(?=!)", + "end": "(?=[\r\n\t ,\\[\\]{}])", + "name": "storage.type.tag.shorthand.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-secondary-tag-handle", + "match": "\\G!!", + "name": "punctuation.definition.tag.secondary.yaml" + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-secondary-tag-handle", + "match": "\\G(!)[0-9A-Za-z-]++(!)", + "captures": { + "1": { + "name": "punctuation.definition.tag.named.yaml" + }, + "2": { + "name": "punctuation.definition.tag.named.yaml" + } + } + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-primary-tag-handle", + "match": "\\G!", + "name": "punctuation.definition.tag.primary.yaml" + }, + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\r\n\t a-zA-Z0-9-#;/?:@&=+$_.~*'()%]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + } + ] + }, + "anchor-property": { + "match": "(&)([\\x{85}[^ ,\\[\\]{}\\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)|(&)", + "captures": { + "0": { + "name": "keyword.control.flow.anchor.yaml" + }, + "1": { + "name": "punctuation.definition.anchor.yaml" + }, + "2": { + "name": "variable.other.anchor.yaml" + }, + "3": { + "name": "invalid.illegal.flow.anchor.yaml" + } + } + }, + "alias": { + "begin": "(\\*)([\\x{85}[^ ,\\[\\]{}\\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)|(\\*)", + "end": "(?=:[\r\n\t ,\\[\\]{}]|[,\\[\\]{}])", + "captures": { + "0": { + "name": "keyword.control.flow.alias.yaml" + }, + "1": { + "name": "punctuation.definition.alias.yaml" + }, + "2": { + "name": "variable.other.alias.yaml" + }, + "3": { + "name": "invalid.illegal.flow.alias.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + "byte-order-mark": { + "comment": "", + "match": "\\G\\x{FEFF}++", + "name": "byte-order-mark.yaml" + }, + "presentation-detail": { + "patterns": [ + { + "match": "[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + }, + { + "include": "#comment" + }, + { + "include": "#unknown" + } + ] + }, + "non-printable": { + "//": { + "85": "…", + "10000": "𐀀", + "A0": " ", + "D7FF": "퟿", + "E000": "", + "FFFD": "�", + "FEFF": "", + "FFFF": "￿", + "10FFFF": "􏿿" + }, + "//match": "[\\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}&&[^\t\n\r\\x{85}]]++", + "match": "[^\t\n\r -~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]++", + "name": "invalid.illegal.non-printable.yaml" + }, + "comment": { + "comment": "Comments must be separated from other tokens by white space characters. `space`, `tab`, `newline` or `carriage-return`. `#(.*)` causes performance issues", + "begin": "(?<=[\\x{FEFF}\t ]|^)#", + "end": "\r|\n", + "captures": { + "0": { + "name": "punctuation.definition.comment.yaml" + } + }, + "name": "comment.line.number-sign.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + "unknown": { + "match": ".[[^\"':,\\[\\]{}]&&!-~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]*+", + "name": "invalid.illegal.unrecognized.yaml markup.strikethrough" + } + } +} \ No newline at end of file diff --git a/extensions/yaml/syntaxes/yaml-1.3.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.3.tmLanguage.json new file mode 100644 index 00000000000..56444fd9fa2 --- /dev/null +++ b/extensions/yaml/syntaxes/yaml-1.3.tmLanguage.json @@ -0,0 +1,1714 @@ +{ + "information_for_contributors": [ + "This file has been converted from https://github.com/RedCMD/YAML-Syntax-Highlighter/blob/master/syntaxes/yaml-1.3.tmLanguage.json", + "If you want to provide a fix or improvement, please create a pull request against the original repository.", + "Once accepted there, we are happy to receive an update request." + ], + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/287c71aeb0773759497822b5e5ce4bdc4d5ef2aa", + "name": "YAML 1.3", + "scopeName": "source.yaml.1.3", + "comment": "https://spec.yaml.io/main/spec/1.3.0/", + "patterns": [ + { + "include": "#stream" + } + ], + "repository": { + "stream": { + "patterns": [ + { + "comment": "allows me to just use `\\G` instead of the performance heavy `(^|\\G)`", + "begin": "^(?!\\G)", + "while": "^", + "name": "meta.stream.yaml", + "patterns": [ + { + "include": "#byte-order-mark" + }, + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G", + "while": "\\G", + "name": "meta.stream.yaml", + "patterns": [ + { + "include": "#byte-order-mark" + }, + { + "include": "#directives" + }, + { + "include": "#document" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "directive-YAML": { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "(?=%YAML[\t ]+1\\.3(?=[\r\n\t ]))", + "end": "\\G(?=(?>\\.{3}|---)[\r\n\t ])", + "name": "meta.1.3.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#681-yaml-directives", + "begin": "\\G(%)(YAML)([\t ]+)(1\\.3)", + "end": "\\G(?=---[\r\n\t ])", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.yaml.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "constant.numeric.yaml-version.yaml" + } + }, + "name": "meta.directives.yaml", + "patterns": [ + { + "include": "#directive-invalid" + }, + { + "include": "#directives" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#document" + } + ] + }, + "directives": { + "comment": "https://yaml.org/spec/1.2.2/#68-directives", + "patterns": [ + { + "include": "source.yaml.1.3#directive-YAML" + }, + { + "include": "source.yaml.1.2#directive-YAML" + }, + { + "include": "source.yaml.1.1#directive-YAML" + }, + { + "include": "source.yaml.1.0#directive-YAML" + }, + { + "begin": "(?=%)", + "while": "\\G(?!%|---[\r\n\t ])", + "name": "meta.directives.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#682-tag-directives", + "begin": "\\G(%)(TAG)(?>([\t ]++)((!)(?>[0-9A-Za-z-]*+(!))?+))?+", + "end": "$", + "applyEndPatternLast": true, + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.tag.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "name": "storage.type.tag-handle.yaml" + }, + "5": { + "name": "punctuation.definition.tag.begin.yaml" + }, + "6": { + "name": "punctuation.definition.tag.end.yaml" + }, + "comment": "https://yaml.org/spec/1.2.2/#rule-c-tag-handle" + }, + "patterns": [ + { + "comment": "technically the beginning should only validate against a valid uri scheme [A-Za-z][A-Za-z0-9.+-]*", + "begin": "\\G[\t ]++(?!#)", + "end": "(?=[\r\n\t ])", + "beginCaptures": { + "0": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "contentName": "support.type.tag-prefix.yaml", + "patterns": [ + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "\\G[,\\[\\]{}]", + "name": "invalid.illegal.character.uri.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\r\n\t a-zA-Z0-9-#;/?:@&=+$,_.!~*'()\\[\\]]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-reserved-directive", + "begin": "(%)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "keyword.other.directive.other.yaml" + } + }, + "patterns": [ + { + "match": "\\G([\t ]++)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-name.yaml" + } + } + }, + { + "match": "([\t ]++)([\\x{85}[^ \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "string.unquoted.directive-parameter.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "match": "\\G\\.{3}(?=[\r\n\t ])", + "name": "invalid.illegal.entity.other.document.end.yaml" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "directive-invalid": { + "patterns": [ + { + "match": "\\G\\.{3}(?=[\r\n\t ])", + "name": "invalid.illegal.entity.other.document.end.yaml" + }, + { + "begin": "\\G(%)(YAML)", + "end": "$", + "beginCaptures": { + "1": { + "name": "punctuation.definition.directive.begin.yaml" + }, + "2": { + "name": "invalid.illegal.keyword.other.directive.yaml.yaml" + } + }, + "name": "meta.directive.yaml", + "patterns": [ + { + "match": "\\G([\t ]++|:)([0-9]++\\.[0-9]++)?+", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "constant.numeric.yaml-version.yaml" + } + } + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "document": { + "comment": "https://yaml.org/spec/1.2.2/#91-documents", + "patterns": [ + { + "begin": "---(?=[\r\n\t ])", + "while": "\\G(?!(?>\\.{3}|---)[\r\n\t ])", + "beginCaptures": { + "0": { + "name": "entity.other.document.begin.yaml" + } + }, + "name": "meta.document.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + { + "begin": "(?=\\.{3}[\r\n\t ])", + "while": "\\G(?=[\t \\x{FEFF}]*+(?>#|$))", + "patterns": [ + { + "begin": "\\G\\.{3}", + "end": "$", + "beginCaptures": { + "0": { + "name": "entity.other.document.end.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#byte-order-mark" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "begin": "\\G(?!%|[\t \\x{FEFF}]*+(?>#|$))", + "while": "\\G(?!(?>\\.{3}|---)[\r\n\t ])", + "name": "meta.document.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + } + ] + }, + "block-node": { + "patterns": [ + { + "include": "#block-sequence" + }, + { + "include": "#block-mapping" + }, + { + "include": "#block-scalar" + }, + { + "include": "#anchor-property" + }, + { + "include": "#tag-property" + }, + { + "include": "#alias" + }, + { + "begin": "(?=\"|')", + "while": "\\G", + "patterns": [ + { + "begin": "(?!\\G)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#double" + }, + { + "include": "#single" + } + ] + }, + { + "begin": "(?=\\[|{)", + "while": "\\G", + "patterns": [ + { + "include": "#block-mapping" + }, + { + "begin": "(?!\\G)(?![\r\n\t ])", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + } + ] + }, + { + "include": "#block-plain-out" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-mapping": { + "//": "The check for plain keys is expensive", + "begin": "(?=((?<=[-?:]) )?+)(?((?>[!&*][^\r\n\t ]*+[\t ]++)*+)(?=(?>(?#Double Quote)\"(?>[^\\\\\"]++|\\\\.)*+\"|(?#Single Quote)'(?>[^']++|'')*+'|(?#Plain)(?>[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))(?>[^:#]++|:(?![\r\n\t ])|(?(\\1\\2)((?>[!&*][^\r\n\t ]*+[\t ]++)*+)((?>\t[\t ]*+)?+[^\r\n\t ?:\\-#!&*\"'\\[\\]{}0-9A-Za-z$()+./;<=\\\\^_~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}])?+|( *+)([\t ]*+[^\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.separator.yaml" + }, + "4": { + "comment": "May cause lag on long lines starting with a tag, anchor or alias", + "patterns": [ + { + "include": "#tag-property" + }, + { + "include": "#anchor-property" + }, + { + "include": "#alias" + }, + { + "include": "#presentation-detail" + } + ] + }, + "5": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "comment": "May cause lag on long lines starting with a tag, anchor or alias", + "patterns": [ + { + "include": "#tag-property" + }, + { + "include": "#anchor-property" + }, + { + "include": "#alias" + }, + { + "include": "#presentation-detail" + } + ] + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "4": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "5": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.mapping.yaml", + "patterns": [ + { + "include": "#block-map-key-double" + }, + { + "include": "#block-map-key-single" + }, + { + "include": "#block-map-key-plain" + }, + { + "include": "#block-map-key-explicit" + }, + { + "include": "#block-map-value" + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + }, + { + "include": "#presentation-detail" + } + ] + }, + "block-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#rule-l+block-sequence", + "begin": "(?=((?<=[-?:]) )?+)(?(\\1\\2)(?!-[\r\n\t ])((?>\t[\t ]*+)?+[^\r\n\t #\\]}])?+|(?!\\1\\2)( *+)([\t ]*+[^\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.definition.block.sequence.item.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "4": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.block.sequence.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "block-map-key-explicit": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-explicit-key", + "begin": "(?=((?<=[-?:]) )?+)\\G( *+)(\\?)(?=[\r\n\t ])", + "while": "\\G(?>(\\1\\2)(?![?:0-9A-Za-z$()+./;<=\\\\^_~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{FEFF}]])((?>\t[\t ]*+)?+[^\r\n\t #\\-\\[\\]{}])?+|(?!\\1\\2)( *+)([\t ]*+[^\r\n#])?+)", + "beginCaptures": { + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "punctuation.definition.map.key.yaml" + }, + "4": { + "name": "punctuation.whitespace.separator.yaml" + } + }, + "whileCaptures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "invalid.illegal.expected-indentation.yaml" + }, + "3": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "4": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.map.explicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-key-plain-out" + }, + { + "include": "#block-map-value" + }, + { + "include": "#block-node" + } + ] + }, + "block-map-key-double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style (BLOCK-KEY)", + "begin": "\\G\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "include": "#double-escape" + } + ] + }, + "block-map-key-single": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": ".[\t ]*+$", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + "block-map-key-plain": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (BLOCK-KEY)", + "begin": "\\G(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))", + "end": "(?=[\t ]*+:[\r\n\t ]|(?>[\t ]++|\\G)#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": "\\G([\t ]++)(.)", + "captures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "invalid.illegal.multiline-key.yaml" + } + } + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "block-map-value": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", + "begin": ":(?=[\r\n\t ])", + "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{FEFF}]]|-[^\r\n\t ])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.map.value.yaml", + "patterns": [ + { + "include": "#block-node" + } + ] + }, + "block-scalar": { + "comment": "https://yaml.org/spec/1.2.2/#81-block-scalar-styles", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#8111-block-indentation-indicator", + "begin": "([\t ]*+)(?>(\\|)|(>))(?[+-])?+((1)|(2)|(3)|(4)|(5)|(6)|(7)|(8)|(9))(?()|([+-]))?+", + "while": "\\G(?>(?>(?!\\6) |(?!\\7) {2}|(?!\\8) {3}|(?!\\9) {4}|(?!\\10) {5}|(?!\\11) {6}|(?!\\12) {7}|(?!\\13) {8}|(?!\\14) {9})| *+($|[^#]))", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "3": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + }, + "5": { + "name": "constant.numeric.indentation-indicator.yaml" + }, + "15": { + "name": "storage.modifier.chomping-indicator.yaml" + } + }, + "whileCaptures": { + "0": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "1": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "begin": "$", + "while": "\\G", + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + { + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-b-block-header", + "//": "Soooooooo many edge cases", + "begin": "([\t ]*+)(?>(\\|)|(>))([+-]?+)", + "while": "\\G", + "beginCaptures": { + "1": { + "name": "punctuation.whitespace.separator.yaml" + }, + "2": { + "name": "keyword.control.flow.block-scalar.literal.yaml" + }, + "3": { + "name": "keyword.control.flow.block-scalar.folded.yaml" + }, + "4": { + "name": "storage.modifier.chomping-indicator.yaml" + } + }, + "name": "meta.scalar.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-literal-content", + "begin": "$", + "while": "\\G", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "//": "Find the highest indented line", + "begin": "\\G( ++)$", + "while": "\\G(?>(\\1)$|(?!\\1)( *+)($|.))", + "captures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-b-nb-literal-next", + "//": [ + "Funky wrapper function", + "The `end` pattern clears the parent `\\G` anchor", + "Affectively forcing this rule to only match at most once", + "https://github.com/microsoft/vscode-textmate/issues/114" + ], + "begin": "\\G(?!$)(?=( *+))", + "end": "\\G(?!\\1)(?=[\t ]*+#)", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-nb-literal-text", + "begin": "\\G( *+)", + "while": "\\G(?>(\\1)|( *+)($|[^\t#]|[\t ]++[^#]))", + "captures": { + "1": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "2": { + "name": "punctuation.whitespace.indentation.yaml" + }, + "3": { + "name": "invalid.illegal.expected-indentation.yaml" + } + }, + "contentName": "string.unquoted.block.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-l-chomped-empty", + "begin": "(?!\\G)(?=[\t ]*+#)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + { + "comment": "Header Comment", + "begin": "\\G", + "end": "$", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + } + ] + }, + "block-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-OUT)", + "begin": "(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))", + "while": "\\G", + "patterns": [ + { + "begin": "\\G", + "end": "(?=(?>[\t ]++|\\G)#)", + "name": "string.unquoted.plain.out.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": ":(?=[\r\n\t ])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + { + "begin": "(?!\\G)", + "while": "\\G", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-node": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-seq-entry (FLOW-IN)", + "patterns": [ + { + "begin": "(?=\\[|{)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "begin": "(?!\\G)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping" + }, + { + "include": "#flow-sequence" + } + ] + }, + { + "include": "#anchor-property" + }, + { + "include": "#tag-property" + }, + { + "include": "#alias" + }, + { + "begin": "(?=\"|')", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "begin": "(?!\\G)", + "end": "(?=[:,\\]}])", + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#double" + }, + { + "include": "#single" + } + ] + }, + { + "include": "#flow-plain-in" + }, + { + "include": "#presentation-detail" + } + ] + }, + "flow-mapping": { + "comment": "https://yaml.org/spec/1.2.2/#742-flow-mappings", + "begin": "{", + "end": "}", + "beginCaptures": { + "0": { + "name": "punctuation.definition.mapping.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.mapping.end.yaml" + } + }, + "name": "meta.flow.mapping.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-flow-map-entries", + "begin": "(?<={)\\G(?=[\r\n\t ,#])|,", + "end": "(?=[^\r\n\t ,#])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.mapping.yaml" + } + }, + "patterns": [ + { + "match": ",++", + "name": "invalid.illegal.separator.sequence.yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-mapping-map-key" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-sequence": { + "comment": "https://yaml.org/spec/1.2.2/#741-flow-sequences", + "begin": "\\[", + "end": "]", + "beginCaptures": { + "0": { + "name": "punctuation.definition.sequence.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.sequence.end.yaml" + } + }, + "name": "meta.flow.sequence.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-flow-seq-entries", + "begin": "(?<=\\[)\\G(?=[\r\n\t ,#])|,", + "end": "(?=[^\r\n\t ,#])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.sequence.yaml" + } + }, + "patterns": [ + { + "match": ",++", + "name": "invalid.illegal.separator.sequence.yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "include": "#flow-sequence-map-key" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + "flow-mapping-map-key": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-entry (FLOW-IN)", + "patterns": [ + { + "begin": "\\?(?=[\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\[\\]{}])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "name": "meta.flow.map.explicit.yaml", + "patterns": [ + { + "include": "#flow-mapping-map-key" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?=(?>[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ,\\[\\]{}])))", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#flow-key-plain-in" + }, + { + "match": ":(?=\\[|{)", + "name": "invalid.illegal.separator.map.yaml" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#presentation-detail" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?=\"|')", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-sequence-map-key": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-entry (FLOW-IN)", + "patterns": [ + { + "begin": "\\?(?=[\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\[\\]{}])", + "beginCaptures": { + "0": { + "name": "punctuation.definition.map.key.yaml" + } + }, + "name": "meta.flow.map.explicit.yaml", + "patterns": [ + { + "include": "#flow-mapping-map-key" + }, + { + "include": "#flow-map-value-yaml" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#flow-node" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-flow-map-implicit-entry (FLOW-IN)", + "begin": "(?<=[\t ,\\[{]|^)(?=(?>[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ,\\[\\]{}]))(?>[^:#,\\[\\]{}]++|:(?![\r\n\t ,\\[\\]{}])|(?\"(?>[^\\\\\"]++|\\\\.)*+\"|'(?>[^']++|'')*+')[\t ]*+:)", + "end": "(?=[,\\[\\]{}])", + "name": "meta.flow.map.implicit.yaml", + "patterns": [ + { + "include": "#key-double" + }, + { + "include": "#key-single" + }, + { + "include": "#flow-map-value-json" + }, + { + "include": "#presentation-detail" + } + ] + } + ] + }, + "flow-map-value-yaml": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-flow-map-separate-value (FLOW-IN)", + "begin": ":(?=[\r\n\t ,\\[\\]{}])", + "end": "(?=[,\\]}])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.flow.pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + }, + "flow-map-value-json": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-flow-map-separate-value (FLOW-IN)", + "begin": "(?<=(?>[\"'\\]}]|^)[\t ]*+):", + "end": "(?=[,\\]}])", + "beginCaptures": { + "0": { + "name": "punctuation.separator.map.value.yaml" + } + }, + "name": "meta.flow.pair.value.yaml", + "patterns": [ + { + "include": "#flow-node" + } + ] + }, + "flow-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-multi-line (FLOW-IN)", + "begin": "(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ,\\[\\]{}]))", + "end": "(?=(?>[\t ]++|\\G)#|[\t ]*+[,\\[\\]{}])", + "name": "string.unquoted.plain.in.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-in" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": ":(?=[\r\n\t ,\\[\\]{}])", + "name": "invalid.illegal.multiline-key.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "flow-key-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-plain-one-line (FLOW-OUT)", + "begin": "(?=[\\x{85}[^-?:,\\[\\]{}#&*!|>'\"%@` \\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]|[?:-](?![\r\n\t ]))", + "end": "(?=[\t ]*+:[\r\n\t ]|[\t ]++#)", + "name": "meta.map.key.yaml string.unquoted.plain.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-out" + }, + { + "match": "\\G[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "[\t ]++$", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "flow-key-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#rule-ns-s-implicit-yaml-key (FLOW-KEY)", + "begin": "\\G(?![\r\n\t #])", + "end": "(?=[\t ]*+(?>:[\r\n\t ,\\[\\]{}]|[,\\[\\]{}])|[\t ]++#)", + "name": "meta.flow.map.key.yaml string.unquoted.plain.in.yaml entity.name.tag.yaml", + "patterns": [ + { + "include": "#tag-implicit-plain-in" + }, + { + "match": "\\x{FEFF}", + "name": "invalid.illegal.bom.yaml" + }, + { + "include": "#non-printable" + } + ] + }, + "key-double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style", + "begin": "\\G\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.double.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "include": "#double-escape" + } + ] + }, + "key-single": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-single-quoted (BLOCK-KEY)", + "begin": "\\G'", + "end": "'(?!')", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "meta.map.key.yaml string.quoted.single.yaml entity.name.tag.yaml", + "patterns": [ + { + "match": "[^\t -\\x{10FFFF}]++", + "name": "invalid.illegal.character.yaml" + }, + { + "match": "''", + "name": "constant.character.escape.single-quote.yaml" + } + ] + }, + "double": { + "comment": "https://yaml.org/spec/1.2.2/#double-quoted-style", + "begin": "\"", + "end": "\"", + "beginCaptures": { + "0": { + "name": "punctuation.definition.string.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.string.end.yaml" + } + }, + "name": "string.quoted.double.yaml", + "patterns": [ + { + "match": "(?x[^\"]{2,0}|u[^\"]{4,0}|U[^\"]{8,0}|.)", + "name": "invalid.illegal.constant.character.escape.yaml" + } + ] + }, + "tag-implicit-plain-in": { + "comment": "https://yaml.org/spec/1.2.2/#103-core-schema", + "patterns": [ + { + "match": "\\G(?>null|Null|NULL|~)(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.language.null.yaml" + }, + { + "match": "\\G(?>true|True|TRUE|false|False|FALSE)(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.language.boolean.yaml" + }, + { + "match": "\\G[+-]?+[0-9]++(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.decimal.yaml" + }, + { + "match": "\\G0o[0-7]++(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.octal.yaml" + }, + { + "match": "\\G0x[0-9a-fA-F]++(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.integer.hexadecimal.yaml" + }, + { + "match": "\\G[+-]?+(?>\\.[0-9]++|[0-9]++(?>\\.[0-9]*+)?+)(?>[eE][+-]?+[0-9]++)?+(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.yaml" + }, + { + "match": "\\G[+-]?+\\.(?>inf|Inf|INF)(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.inf.yaml" + }, + { + "match": "\\G\\.(?>nan|NaN|NAN)(?=[\t ]++#|[\t ]*+(?>[\r\n,\\]}]|:[\r\n\t ,\\[\\]{}]))", + "name": "constant.numeric.float.nan.yaml" + } + ] + }, + "tag-implicit-plain-out": { + "comment": "https://yaml.org/spec/1.2.2/#103-core-schema", + "patterns": [ + { + "match": "\\G(?>null|Null|NULL|~)(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.language.null.yaml" + }, + { + "match": "\\G(?>true|True|TRUE|false|False|FALSE)(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.language.boolean.yaml" + }, + { + "match": "\\G[+-]?+[0-9]++(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.integer.decimal.yaml" + }, + { + "match": "\\G0o[0-7]++(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.integer.octal.yaml" + }, + { + "match": "\\G0x[0-9a-fA-F]++(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.integer.hexadecimal.yaml" + }, + { + "match": "\\G[+-]?+(?>\\.[0-9]++|[0-9]++(?>\\.[0-9]*+)?+)(?>[eE][+-]?+[0-9]++)?+(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.float.yaml" + }, + { + "match": "\\G[+-]?+\\.(?>inf|Inf|INF)(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.float.inf.yaml" + }, + { + "match": "\\G\\.(?>nan|NaN|NAN)(?=[\t ]++#|[\t ]*+(?>$|:[\r\n\t ]))", + "name": "constant.numeric.float.nan.yaml" + } + ] + }, + "tag-property": { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-tag-property", + "//": [ + "!", + "!!", + "!<>", + "!...", + "!!...", + "!<...>", + "!...!..." + ], + "patterns": [ + { + "match": "!(?=[\r\n\t ])", + "name": "storage.type.tag.non-specific.yaml punctuation.definition.tag.non-specific.yaml" + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-verbatim-tag", + "begin": "!<", + "end": ">", + "beginCaptures": { + "0": { + "name": "punctuation.definition.tag.begin.yaml" + } + }, + "endCaptures": { + "0": { + "name": "punctuation.definition.tag.end.yaml" + } + }, + "name": "storage.type.tag.verbatim.yaml", + "patterns": [ + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\r\n\t a-zA-Z0-9-#;/?:@&=+$,_.!~*'()\\[\\]%>]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-ns-shorthand-tag", + "begin": "(?=!)", + "end": "(?=[\r\n\t ,\\[\\]{}])", + "name": "storage.type.tag.shorthand.yaml", + "patterns": [ + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-secondary-tag-handle", + "match": "\\G!!", + "name": "punctuation.definition.tag.secondary.yaml" + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-secondary-tag-handle", + "match": "\\G(!)[0-9A-Za-z-]++(!)", + "captures": { + "1": { + "name": "punctuation.definition.tag.named.yaml" + }, + "2": { + "name": "punctuation.definition.tag.named.yaml" + } + } + }, + { + "comment": "https://yaml.org/spec/1.2.2/#rule-c-primary-tag-handle", + "match": "\\G!", + "name": "punctuation.definition.tag.primary.yaml" + }, + { + "match": "%[0-9a-fA-F]{2}", + "name": "constant.character.escape.unicode.8-bit.yaml" + }, + { + "match": "%[^\r\n\t ]{2,0}", + "name": "invalid.illegal.constant.character.escape.unicode.8-bit.yaml" + }, + { + "include": "#non-printable" + }, + { + "match": "[^\r\n\t a-zA-Z0-9-#;/?:@&=+$_.~*'()%]++", + "name": "invalid.illegal.unrecognized.yaml" + } + ] + } + ] + }, + "anchor-property": { + "match": "(&)([\\x{85}[^ ,\\[\\]{}\\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)|(&)", + "captures": { + "0": { + "name": "keyword.control.flow.anchor.yaml" + }, + "1": { + "name": "punctuation.definition.anchor.yaml" + }, + "2": { + "name": "variable.other.anchor.yaml" + }, + "3": { + "name": "invalid.illegal.flow.anchor.yaml" + } + } + }, + "alias": { + "begin": "(\\*)([\\x{85}[^ ,\\[\\]{}\\p{Cntrl}\\p{Surrogate}\\x{FEFF FFFE FFFF}]]++)|(\\*)", + "end": "(?=:[\r\n\t ,\\[\\]{}]|[,\\[\\]{}])", + "captures": { + "0": { + "name": "keyword.control.flow.alias.yaml" + }, + "1": { + "name": "punctuation.definition.alias.yaml" + }, + "2": { + "name": "variable.other.alias.yaml" + }, + "3": { + "name": "invalid.illegal.flow.alias.yaml" + } + }, + "patterns": [ + { + "include": "#presentation-detail" + } + ] + }, + "byte-order-mark": { + "comment": "", + "match": "\\G\\x{FEFF}++", + "name": "byte-order-mark.yaml" + }, + "presentation-detail": { + "patterns": [ + { + "match": "[\t ]++", + "name": "punctuation.whitespace.separator.yaml" + }, + { + "include": "#non-printable" + }, + { + "include": "#comment" + }, + { + "include": "#unknown" + } + ] + }, + "non-printable": { + "//": { + "85": "…", + "10000": "𐀀", + "A0": " ", + "D7FF": "퟿", + "E000": "", + "FFFD": "�", + "FEFF": "", + "FFFF": "￿", + "10FFFF": "􏿿" + }, + "//match": "[\\p{Cntrl}\\p{Surrogate}\\x{FFFE FFFF}&&[^\t\n\r\\x{85}]]++", + "match": "[^\t\n\r -~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]++", + "name": "invalid.illegal.non-printable.yaml" + }, + "comment": { + "comment": "Comments must be separated from other tokens by white space characters. `space`, `tab`, `newline` or `carriage-return`. `#(.*)` causes performance issues", + "begin": "(?<=[\\x{FEFF}\t ]|^)#", + "end": "\r|\n", + "captures": { + "0": { + "name": "punctuation.definition.comment.yaml" + } + }, + "name": "comment.line.number-sign.yaml", + "patterns": [ + { + "include": "#non-printable" + } + ] + }, + "unknown": { + "match": ".[[^\"':,\\[\\]{}]&&!-~\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}]*+", + "name": "invalid.illegal.unrecognized.yaml markup.strikethrough" + } + } +} \ No newline at end of file diff --git a/extensions/yaml/syntaxes/yaml.tmLanguage.json b/extensions/yaml/syntaxes/yaml.tmLanguage.json index 447df713901..39d8e586442 100644 --- a/extensions/yaml/syntaxes/yaml.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml.tmLanguage.json @@ -1,621 +1,21 @@ { "information_for_contributors": [ - "This file has been converted from https://github.com/textmate/yaml.tmbundle/blob/master/Syntaxes/YAML.tmLanguage", + "This file has been converted from https://github.com/RedCMD/YAML-Syntax-Highlighter/blob/master/syntaxes/yaml.tmLanguage.json", "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/textmate/yaml.tmbundle/commit/e54ceae3b719506dba7e481a77cea4a8b576ae46", - "name": "YAML", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/5d2a15e2ee4bb9c2cc9a86a0b72aea8fa2aba1e1", + "name": "YAML Ain't Markup Language", "scopeName": "source.yaml", "patterns": [ { - "include": "#comment" - }, - { - "include": "#property" - }, - { - "include": "#directive" - }, - { - "match": "^---", - "name": "entity.other.document.begin.yaml" - }, - { - "match": "^\\.{3}", - "name": "entity.other.document.end.yaml" - }, - { - "include": "#node" + "comment": "Default to YAML version 1.2", + "include": "source.yaml.1.2" } ], "repository": { - "block-collection": { - "patterns": [ - { - "include": "#block-sequence" - }, - { - "include": "#block-mapping" - } - ] - }, - "block-mapping": { - "patterns": [ - { - "include": "#block-pair" - } - ] - }, - "block-node": { - "patterns": [ - { - "include": "#prototype" - }, - { - "include": "#block-scalar" - }, - { - "include": "#block-collection" - }, - { - "include": "#flow-scalar-plain-out" - }, - { - "include": "#flow-node" - } - ] - }, - "block-pair": { - "patterns": [ - { - "begin": "\\?", - "beginCaptures": { - "1": { - "name": "punctuation.definition.key-value.begin.yaml" - } - }, - "end": "(?=\\?)|^ *(:)|(:)", - "endCaptures": { - "1": { - "name": "punctuation.separator.key-value.mapping.yaml" - }, - "2": { - "name": "invalid.illegal.expected-newline.yaml" - } - }, - "name": "meta.block-mapping.yaml", - "patterns": [ - { - "include": "#block-node" - } - ] - }, - { - "begin": "(?x)\n (?=\n (?x:\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n )\n (\n [^\\s:]\n | : \\S\n | \\s+ (?![#\\s])\n )*\n \\s*\n :\n\t\t\t\t\t\t\t(\\s|$)\n )\n ", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", - "patterns": [ - { - "include": "#flow-scalar-plain-out-implicit-type" - }, - { - "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n ", - "beginCaptures": { - "0": { - "name": "entity.name.tag.yaml" - } - }, - "contentName": "entity.name.tag.yaml", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", - "name": "string.unquoted.plain.out.yaml" - } - ] - }, - { - "match": ":(?=\\s|$)", - "name": "punctuation.separator.key-value.mapping.yaml" - } - ] - }, - "block-scalar": { - "begin": "(?:(\\|)|(>))([1-9])?([-+])?(.*\\n?)", - "beginCaptures": { - "1": { - "name": "keyword.control.flow.block-scalar.literal.yaml" - }, - "2": { - "name": "keyword.control.flow.block-scalar.folded.yaml" - }, - "3": { - "name": "constant.numeric.indentation-indicator.yaml" - }, - "4": { - "name": "storage.modifier.chomping-indicator.yaml" - }, - "5": { - "patterns": [ - { - "include": "#comment" - }, - { - "match": ".+", - "name": "invalid.illegal.expected-comment-or-newline.yaml" - } - ] - } - }, - "end": "^(?=\\S)|(?!\\G)", - "patterns": [ - { - "begin": "^([ ]+)(?! )", - "end": "^(?!\\1|\\s*$)", - "name": "string.unquoted.block.yaml" - } - ] - }, - "block-sequence": { - "match": "(-)(?!\\S)", - "name": "punctuation.definition.block.sequence.item.yaml" - }, - "comment": { - "begin": "(?:(^[ \\t]*)|[ \\t]+)(?=#\\p{Print}*$)", - "beginCaptures": { - "1": { - "name": "punctuation.whitespace.comment.leading.yaml" - } - }, - "end": "(?!\\G)", - "patterns": [ - { - "begin": "#", - "beginCaptures": { - "0": { - "name": "punctuation.definition.comment.yaml" - } - }, - "end": "\\n", - "name": "comment.line.number-sign.yaml" - } - ] - }, - "directive": { - "begin": "^%", - "beginCaptures": { - "0": { - "name": "punctuation.definition.directive.begin.yaml" - } - }, - "end": "(?=$|[ \\t]+($|#))", - "name": "meta.directive.yaml", - "patterns": [ - { - "captures": { - "1": { - "name": "keyword.other.directive.yaml.yaml" - }, - "2": { - "name": "constant.numeric.yaml-version.yaml" - } - }, - "match": "\\G(YAML)[ \\t]+(\\d+\\.\\d+)" - }, - { - "captures": { - "1": { - "name": "keyword.other.directive.tag.yaml" - }, - "2": { - "name": "storage.type.tag-handle.yaml" - }, - "3": { - "name": "support.type.tag-prefix.yaml" - } - }, - "match": "(?x)\n \\G\n (TAG)\n (?:[ \\t]+\n ((?:!(?:[0-9A-Za-z\\-]*!)?))\n (?:[ \\t]+ (\n ! (?x: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )*\n | (?![,!\\[\\]{}]) (?x: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )+\n )\n )?\n )?\n " - }, - { - "captures": { - "1": { - "name": "support.other.directive.reserved.yaml" - }, - "2": { - "name": "string.unquoted.directive-name.yaml" - }, - "3": { - "name": "string.unquoted.directive-parameter.yaml" - } - }, - "match": "(?x) \\G (\\w+) (?:[ \\t]+ (\\w+) (?:[ \\t]+ (\\w+))? )?" - }, - { - "match": "\\S+", - "name": "invalid.illegal.unrecognized.yaml" - } - ] - }, - "flow-alias": { - "captures": { - "1": { - "name": "keyword.control.flow.alias.yaml" - }, - "2": { - "name": "punctuation.definition.alias.yaml" - }, - "3": { - "name": "variable.other.alias.yaml" - }, - "4": { - "name": "invalid.illegal.character.anchor.yaml" - } - }, - "match": "((\\*))([^\\s\\[\\]/{/},]+)([^\\s\\]},]\\S*)?" - }, - "flow-collection": { - "patterns": [ - { - "include": "#flow-sequence" - }, - { - "include": "#flow-mapping" - } - ] - }, - "flow-mapping": { - "begin": "\\{", - "beginCaptures": { - "0": { - "name": "punctuation.definition.mapping.begin.yaml" - } - }, - "end": "\\}", - "endCaptures": { - "0": { - "name": "punctuation.definition.mapping.end.yaml" - } - }, - "name": "meta.flow-mapping.yaml", - "patterns": [ - { - "include": "#prototype" - }, - { - "match": ",", - "name": "punctuation.separator.mapping.yaml" - }, - { - "include": "#flow-pair" - } - ] - }, - "flow-node": { - "patterns": [ - { - "include": "#prototype" - }, - { - "include": "#flow-alias" - }, - { - "include": "#flow-collection" - }, - { - "include": "#flow-scalar" - } - ] - }, - "flow-pair": { - "patterns": [ - { - "begin": "\\?", - "beginCaptures": { - "0": { - "name": "punctuation.definition.key-value.begin.yaml" - } - }, - "end": "(?=[},\\]])", - "name": "meta.flow-pair.explicit.yaml", - "patterns": [ - { - "include": "#prototype" - }, - { - "include": "#flow-pair" - }, - { - "include": "#flow-node" - }, - { - "begin": ":(?=\\s|$|[\\[\\]{},])", - "beginCaptures": { - "0": { - "name": "punctuation.separator.key-value.mapping.yaml" - } - }, - "end": "(?=[},\\]])", - "patterns": [ - { - "include": "#flow-value" - } - ] - } - ] - }, - { - "begin": "(?x)\n (?=\n (?:\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n )\n (\n [^\\s:[\\[\\]{},]]\n | : [^\\s[\\[\\]{},]]\n | \\s+ (?![#\\s])\n )*\n \\s*\n :\n\t\t\t\t\t\t\t(\\s|$)\n )\n ", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", - "name": "meta.flow-pair.key.yaml", - "patterns": [ - { - "include": "#flow-scalar-plain-in-implicit-type" - }, - { - "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n ", - "beginCaptures": { - "0": { - "name": "entity.name.tag.yaml" - } - }, - "contentName": "entity.name.tag.yaml", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", - "name": "string.unquoted.plain.in.yaml" - } - ] - }, - { - "include": "#flow-node" - }, - { - "begin": ":(?=\\s|$|[\\[\\]{},])", - "captures": { - "0": { - "name": "punctuation.separator.key-value.mapping.yaml" - } - }, - "end": "(?=[},\\]])", - "name": "meta.flow-pair.yaml", - "patterns": [ - { - "include": "#flow-value" - } - ] - } - ] - }, - "flow-scalar": { - "patterns": [ - { - "include": "#flow-scalar-double-quoted" - }, - { - "include": "#flow-scalar-single-quoted" - }, - { - "include": "#flow-scalar-plain-in" - } - ] - }, - "flow-scalar-double-quoted": { - "begin": "\"", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.yaml" - } - }, - "end": "\"", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.yaml" - } - }, - "name": "string.quoted.double.yaml", - "patterns": [ - { - "match": "\\\\([0abtnvfre \"/\\\\N_Lp]|x\\d\\d|u\\d{4}|U\\d{8})", - "name": "constant.character.escape.yaml" - }, - { - "match": "\\\\\\n", - "name": "constant.character.escape.double-quoted.newline.yaml" - } - ] - }, - "flow-scalar-plain-in": { - "patterns": [ - { - "include": "#flow-scalar-plain-in-implicit-type" - }, - { - "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] [^\\s[\\[\\]{},]]\n ", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n ", - "name": "string.unquoted.plain.in.yaml" - } - ] - }, - "flow-scalar-plain-in-implicit-type": { - "patterns": [ - { - "captures": { - "1": { - "name": "constant.language.null.yaml" - }, - "2": { - "name": "constant.language.boolean.yaml" - }, - "3": { - "name": "constant.numeric.integer.yaml" - }, - "4": { - "name": "constant.numeric.float.yaml" - }, - "5": { - "name": "constant.other.timestamp.yaml" - }, - "6": { - "name": "constant.language.value.yaml" - }, - "7": { - "name": "constant.language.merge.yaml" - } - }, - "match": "(?x)\n (?x:\n (null|Null|NULL|~)\n | (y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)\n | (\n (?:\n [-+]? 0b [0-1_]+ # (base 2)\n | [-+]? 0 [0-7_]+ # (base 8)\n | [-+]? (?: 0|[1-9][0-9_]*) # (base 10)\n | [-+]? 0x [0-9a-fA-F_]+ # (base 16)\n | [-+]? [1-9] [0-9_]* (?: :[0-5]?[0-9])+ # (base 60)\n )\n )\n | (\n (?x:\n [-+]? (?: [0-9] [0-9_]*)? \\. [0-9.]* (?: [eE] [-+] [0-9]+)? # (base 10)\n | [-+]? [0-9] [0-9_]* (?: :[0-5]?[0-9])+ \\. [0-9_]* # (base 60)\n | [-+]? \\. (?: inf|Inf|INF) # (infinity)\n | \\. (?: nan|NaN|NAN) # (not a number)\n )\n )\n | (\n (?x:\n \\d{4} - \\d{2} - \\d{2} # (y-m-d)\n | \\d{4} # (year)\n - \\d{1,2} # (month)\n - \\d{1,2} # (day)\n (?: [Tt] | [ \\t]+) \\d{1,2} # (hour)\n : \\d{2} # (minute)\n : \\d{2} # (second)\n (?: \\.\\d*)? # (fraction)\n (?:\n (?:[ \\t]*) Z\n | [-+] \\d{1,2} (?: :\\d{1,2})?\n )? # (time zone)\n )\n )\n | (=)\n | (<<)\n )\n (?:\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n | \\s* : [\\[\\]{},]\n | \\s* [\\[\\]{},]\n )\n )\n " - } - ] - }, - "flow-scalar-plain-out": { - "patterns": [ - { - "include": "#flow-scalar-plain-out-implicit-type" - }, - { - "begin": "(?x)\n [^\\s[-?:,\\[\\]{}#&*!|>'\"%@`]]\n | [?:-] \\S\n ", - "end": "(?x)\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n ", - "name": "string.unquoted.plain.out.yaml" - } - ] - }, - "flow-scalar-plain-out-implicit-type": { - "patterns": [ - { - "captures": { - "1": { - "name": "constant.language.null.yaml" - }, - "2": { - "name": "constant.language.boolean.yaml" - }, - "3": { - "name": "constant.numeric.integer.yaml" - }, - "4": { - "name": "constant.numeric.float.yaml" - }, - "5": { - "name": "constant.other.timestamp.yaml" - }, - "6": { - "name": "constant.language.value.yaml" - }, - "7": { - "name": "constant.language.merge.yaml" - } - }, - "match": "(?x)\n (?x:\n (null|Null|NULL|~)\n | (y|Y|yes|Yes|YES|n|N|no|No|NO|true|True|TRUE|false|False|FALSE|on|On|ON|off|Off|OFF)\n | (\n (?:\n [-+]? 0b [0-1_]+ # (base 2)\n | [-+]? 0 [0-7_]+ # (base 8)\n | [-+]? (?: 0|[1-9][0-9_]*) # (base 10)\n | [-+]? 0x [0-9a-fA-F_]+ # (base 16)\n | [-+]? [1-9] [0-9_]* (?: :[0-5]?[0-9])+ # (base 60)\n )\n )\n | (\n (?x:\n [-+]? (?: [0-9] [0-9_]*)? \\. [0-9.]* (?: [eE] [-+] [0-9]+)? # (base 10)\n | [-+]? [0-9] [0-9_]* (?: :[0-5]?[0-9])+ \\. [0-9_]* # (base 60)\n | [-+]? \\. (?: inf|Inf|INF) # (infinity)\n | \\. (?: nan|NaN|NAN) # (not a number)\n )\n )\n | (\n (?x:\n \\d{4} - \\d{2} - \\d{2} # (y-m-d)\n | \\d{4} # (year)\n - \\d{1,2} # (month)\n - \\d{1,2} # (day)\n (?: [Tt] | [ \\t]+) \\d{1,2} # (hour)\n : \\d{2} # (minute)\n : \\d{2} # (second)\n (?: \\.\\d*)? # (fraction)\n (?:\n (?:[ \\t]*) Z\n | [-+] \\d{1,2} (?: :\\d{1,2})?\n )? # (time zone)\n )\n )\n | (=)\n | (<<)\n )\n (?x:\n (?=\n \\s* $\n | \\s+ \\#\n | \\s* : (\\s|$)\n )\n )\n " - } - ] - }, - "flow-scalar-single-quoted": { - "begin": "'", - "beginCaptures": { - "0": { - "name": "punctuation.definition.string.begin.yaml" - } - }, - "end": "'(?!')", - "endCaptures": { - "0": { - "name": "punctuation.definition.string.end.yaml" - } - }, - "name": "string.quoted.single.yaml", - "patterns": [ - { - "match": "''", - "name": "constant.character.escape.single-quoted.yaml" - } - ] - }, - "flow-sequence": { - "begin": "\\[", - "beginCaptures": { - "0": { - "name": "punctuation.definition.sequence.begin.yaml" - } - }, - "end": "\\]", - "endCaptures": { - "0": { - "name": "punctuation.definition.sequence.end.yaml" - } - }, - "name": "meta.flow-sequence.yaml", - "patterns": [ - { - "include": "#prototype" - }, - { - "match": ",", - "name": "punctuation.separator.sequence.yaml" - }, - { - "include": "#flow-pair" - }, - { - "include": "#flow-node" - } - ] - }, - "flow-value": { - "patterns": [ - { - "begin": "\\G(?![},\\]])", - "end": "(?=[},\\]])", - "name": "meta.flow-pair.value.yaml", - "patterns": [ - { - "include": "#flow-node" - } - ] - } - ] - }, - "node": { - "patterns": [ - { - "include": "#block-node" - } - ] - }, - "property": { - "begin": "(?=!|&)", - "end": "(?!\\G)", - "name": "meta.property.yaml", - "patterns": [ - { - "captures": { - "1": { - "name": "keyword.control.property.anchor.yaml" - }, - "2": { - "name": "punctuation.definition.anchor.yaml" - }, - "3": { - "name": "entity.name.type.anchor.yaml" - }, - "4": { - "name": "invalid.illegal.character.anchor.yaml" - } - }, - "match": "\\G((&))([^\\s\\[\\]/{/},]+)(\\S+)?" - }, - { - "match": "(?x)\n \\G\n (?:\n ! < (?: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;/?:@&=+$,_.!~*'()\\[\\]] )+ >\n | (?:!(?:[0-9A-Za-z\\-]*!)?) (?: %[0-9A-Fa-f]{2} | [0-9A-Za-z\\-#;/?:@&=+$_.~*'()] )+\n | !\n )\n (?=\\ |\\t|$)\n ", - "name": "storage.type.tag-handle.yaml" - }, - { - "match": "\\S+", - "name": "invalid.illegal.tag-handle.yaml" - } - ] - }, - "prototype": { - "patterns": [ - { - "include": "#comment" - }, - { - "include": "#property" - } - ] + "parity": { + "comment": "Yes... That is right. Due to the changes with \\x2028, \\x2029, \\x85 and 'tags'. This is all the code I was able to reuse between all versions 1.3, 1.2, 1.1 and 1.0" } } } \ No newline at end of file From 502b5628b107a09671a7b8cc27e8609a9cbcc7b7 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 3 Jul 2024 04:08:45 -0700 Subject: [PATCH 0246/2222] Fix eslint rule suppressions in terminal/ Fixes #219708 --- .eslintrc.json | 3 ++- .../commandDetection/promptInputModel.test.ts | 13 +++++-------- .../chat/test/browser/terminalInitialHint.test.ts | 10 ++++++---- .../browser/terminalSuggestAddon.integrationTest.ts | 10 +++++----- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index db383e0b9b4..e9df5ec8a8c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -738,7 +738,8 @@ "vs/platform/*/~", "tas-client-umd", // node module allowed even in /common/ "@microsoft/1ds-core-js", // node module allowed even in /common/ - "@microsoft/1ds-post-js" // node module allowed even in /common/ + "@microsoft/1ds-post-js", // node module allowed even in /common/ + "@xterm/headless" // node module allowed even in /common/ ] }, { diff --git a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts index 18f410a147f..7d7e6849b8a 100644 --- a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts +++ b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts @@ -3,12 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// HACK: Ignore warnings, technically this requires browser/ but it's run on renderer.html anyway so -// it's fine in tests. Importing @xterm/headless appears to prevent `yarn test-browser` from running -// at all. -// eslint-disable-next-line local/code-import-patterns, local/code-amd-node-module -import { Terminal } from '@xterm/xterm'; - +import type { Terminal } from '@xterm/headless'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; import { PromptInputModel, type IPromptInputModelState } from 'vs/platform/terminal/common/capabilities/commandDetection/promptInputModel'; @@ -16,6 +11,7 @@ import { Emitter } from 'vs/base/common/event'; import type { ITerminalCommand } from 'vs/platform/terminal/common/capabilities/capabilities'; import { notDeepStrictEqual, strictEqual } from 'assert'; import { timeout } from 'vs/base/common/async'; +import { importAMDNodeModule } from 'vs/amdX'; suite('PromptInputModel', () => { const store = ensureNoDisposablesAreLeakedInTestSuite(); @@ -61,8 +57,9 @@ suite('PromptInputModel', () => { strictEqual(promptInputModel.cursorIndex, cursorIndex, `value=${promptInputModel.value}`); } - setup(() => { - xterm = store.add(new Terminal({ allowProposedApi: true })); + setup(async () => { + const TerminalCtor = (await importAMDNodeModule('@xterm/headless', 'lib-headless/xterm-headless.js')).Terminal; + xterm = store.add(new TerminalCtor({ allowProposedApi: true })); onCommandStart = store.add(new Emitter()); onCommandExecuted = store.add(new Emitter()); promptInputModel = store.add(new PromptInputModel(xterm, onCommandStart.event, onCommandExecuted.event, new NullLogService)); diff --git a/src/vs/workbench/contrib/terminalContrib/chat/test/browser/terminalInitialHint.test.ts b/src/vs/workbench/contrib/terminalContrib/chat/test/browser/terminalInitialHint.test.ts index d3a1a8a0b11..0558b023b16 100644 --- a/src/vs/workbench/contrib/terminalContrib/chat/test/browser/terminalInitialHint.test.ts +++ b/src/vs/workbench/contrib/terminalContrib/chat/test/browser/terminalInitialHint.test.ts @@ -2,8 +2,8 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// eslint-disable-next-line local/code-import-patterns, local/code-amd-node-module -import { Terminal } from '@xterm/xterm'; + +import type { Terminal } from '@xterm/xterm'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ShellIntegrationAddon } from 'vs/platform/terminal/common/xterm/shellIntegrationAddon'; import { workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; @@ -14,6 +14,7 @@ import { Emitter } from 'vs/base/common/event'; import { strictEqual } from 'assert'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ChatAgentLocation, IChatAgent } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { importAMDNodeModule } from 'vs/amdX'; // Test TerminalInitialHintAddon @@ -46,9 +47,10 @@ suite('Terminal Initial Hint Addon', () => { locations: [ChatAgentLocation.fromRaw('editor')], invoke: async () => { return {}; } }; - setup(() => { + setup(async () => { const instantiationService = workbenchInstantiationService({}, store); - xterm = store.add(new Terminal()); + const TerminalCtor = (await importAMDNodeModule('@xterm/xterm', 'lib/xterm.js')).Terminal; + xterm = store.add(new TerminalCtor()); const shellIntegrationAddon = store.add(new ShellIntegrationAddon('', true, undefined, new NullLogService)); initialHintAddon = store.add(instantiationService.createInstance(InitialHintAddon, shellIntegrationAddon.capabilities, onDidChangeAgents)); store.add(initialHintAddon.onDidRequestCreateHint(() => eventCount++)); diff --git a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalSuggestAddon.integrationTest.ts b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalSuggestAddon.integrationTest.ts index 90c2efa5e75..03e1ccd475b 100644 --- a/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalSuggestAddon.integrationTest.ts +++ b/src/vs/workbench/contrib/terminalContrib/suggest/test/browser/terminalSuggestAddon.integrationTest.ts @@ -3,9 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// eslint-disable-next-line local/code-import-patterns, local/code-amd-node-module -import { Terminal } from '@xterm/xterm'; - +import type { Terminal } from '@xterm/xterm'; import { strictEqual } from 'assert'; import { getActiveDocument } from 'vs/base/browser/dom'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; @@ -30,6 +28,7 @@ import { events as windows11_pwsh_namespace_completion } from 'vs/workbench/cont import { events as windows11_pwsh_type_before_prompt } from 'vs/workbench/contrib/terminalContrib/suggest/test/browser/recordings/windows11_pwsh_type_before_prompt'; import { events as windows11_pwsh_writehost_multiline_nav_up } from 'vs/workbench/contrib/terminalContrib/suggest/test/browser/recordings/windows11_pwsh_writehost_multiline_nav_up'; import { events as windows11_pwsh_writehost_multiline } from 'vs/workbench/contrib/terminalContrib/suggest/test/browser/recordings/windows11_pwsh_writehost_multiline'; +import { importAMDNodeModule } from 'vs/amdX'; const recordedTestCases: { name: string; events: RecordedSessionEvent[] }[] = [ { name: 'macos_bash_echo_simple', events: macos_bash_echo_simple as any as RecordedSessionEvent[] }, @@ -69,7 +68,7 @@ suite('Terminal Contrib Suggest Recordings', () => { let suggestWidgetVisibleContextKey: IContextKey; let suggestAddon: SuggestAddon; - setup(() => { + setup(async () => { const instantiationService = workbenchInstantiationService({ configurationService: () => new TestConfigurationService({ files: { autoSave: false }, @@ -84,7 +83,8 @@ suite('Terminal Contrib Suggest Recordings', () => { } }) }, store); - xterm = store.add(new Terminal({ allowProposedApi: true })); + const TerminalCtor = (await importAMDNodeModule('@xterm/xterm', 'lib/xterm.js')).Terminal; + xterm = store.add(new TerminalCtor({ allowProposedApi: true })); const shellIntegrationAddon = store.add(new ShellIntegrationAddon('', true, undefined, new NullLogService)); capabilities = shellIntegrationAddon.capabilities; suggestWidgetVisibleContextKey = TerminalContextKeys.suggestWidgetVisible.bindTo(instantiationService.get(IContextKeyService)); From db23b5c7c31fca98dcbc8590bcd82833e7e8f54a Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 3 Jul 2024 04:38:25 -0700 Subject: [PATCH 0247/2222] Fix @xterm/headless not working on browser (it's compiled for node) --- .eslintrc.json | 3 +-- .../capabilities/commandDetection/promptInputModel.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index e9df5ec8a8c..db383e0b9b4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -738,8 +738,7 @@ "vs/platform/*/~", "tas-client-umd", // node module allowed even in /common/ "@microsoft/1ds-core-js", // node module allowed even in /common/ - "@microsoft/1ds-post-js", // node module allowed even in /common/ - "@xterm/headless" // node module allowed even in /common/ + "@microsoft/1ds-post-js" // node module allowed even in /common/ ] }, { diff --git a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts index 7d7e6849b8a..9e015c72b27 100644 --- a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts +++ b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { Terminal } from '@xterm/headless'; +import type { Terminal } from '@xterm/xterm'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { NullLogService } from 'vs/platform/log/common/log'; import { PromptInputModel, type IPromptInputModelState } from 'vs/platform/terminal/common/capabilities/commandDetection/promptInputModel'; @@ -58,7 +58,7 @@ suite('PromptInputModel', () => { } setup(async () => { - const TerminalCtor = (await importAMDNodeModule('@xterm/headless', 'lib-headless/xterm-headless.js')).Terminal; + const TerminalCtor = (await importAMDNodeModule('@xterm/xterm', 'lib/xterm.js')).Terminal; xterm = store.add(new TerminalCtor({ allowProposedApi: true })); onCommandStart = store.add(new Emitter()); onCommandExecuted = store.add(new Emitter()); From cac98ea95ea9985f2e3d5513d5bc44d989e46c40 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 3 Jul 2024 15:30:38 +0200 Subject: [PATCH 0248/2222] #219720 Improve auto update extensions ux (#219859) * #219720 improve extension auto udpate experience * add detail --- .../abstractExtensionManagementService.ts | 18 + .../common/extensionManagement.ts | 1 + .../common/extensionManagementIpc.ts | 7 + .../node/extensionsWatcher.ts | 3 +- .../extensions/browser/extensionEditor.ts | 6 +- .../browser/extensions.contribution.ts | 118 +++--- .../extensions/browser/extensionsActions.ts | 33 +- .../extensions/browser/extensionsList.ts | 5 +- .../browser/extensionsWorkbenchService.ts | 386 +++++++++++------- .../contrib/extensions/common/extensions.ts | 3 +- .../extensionsWorkbenchService.test.ts | 218 ++++------ .../common/extensionManagementService.ts | 4 + .../test/browser/workbenchTestServices.ts | 1 + 13 files changed, 436 insertions(+), 367 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 284658e08d6..f3ae52b16a2 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -196,6 +196,24 @@ export abstract class AbstractExtensionManagementService extends Disposable impl this.participants.push(participant); } + async resetPinnedStateForAllUserExtensions(pinned: boolean): Promise { + try { + await this.joinAllSettled(this.userDataProfilesService.profiles.map( + async profile => { + const extensions = await this.getInstalled(ExtensionType.User, profile.extensionsResource); + await this.joinAllSettled(extensions.map( + async extension => { + if (extension.pinned !== pinned) { + await this.updateMetadata(extension, { pinned }, profile.extensionsResource); + } + })); + })); + } catch (error) { + this.logService.error('Error while resetting pinned state for all user extensions', getErrorMessage(error)); + throw error; + } + } + protected async installExtensions(extensions: InstallableExtension[]): Promise { const installExtensionResultsMap = new Map(); const installingExtensionsMap = new Map(); diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index 68f21988fc4..5786b57ea07 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -538,6 +538,7 @@ export interface IExtensionManagementService { getExtensionsControlManifest(): Promise; copyExtensions(fromProfileLocation: URI, toProfileLocation: URI): Promise; updateMetadata(local: ILocalExtension, metadata: Partial, profileLocation: URI): Promise; + resetPinnedStateForAllUserExtensions(pinned: boolean): Promise; download(extension: IGalleryExtension, operation: InstallOperation, donotVerifySignature: boolean): Promise; diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index 75cf3eb91ae..65e8735bd10 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -158,6 +158,9 @@ export class ExtensionManagementChannel implements IServerChannel { const e = await this.service.updateMetadata(transformIncomingExtension(args[0], uriTransformer), args[1], transformIncomingURI(args[2], uriTransformer)); return transformOutgoingExtension(e, uriTransformer); } + case 'resetPinnedStateForAllUserExtensions': { + return this.service.resetPinnedStateForAllUserExtensions(args[0]); + } case 'getExtensionsControlManifest': { return this.service.getExtensionsControlManifest(); } @@ -289,6 +292,10 @@ export class ExtensionManagementChannelClient extends Disposable implements IExt .then(extension => transformIncomingExtension(extension, null)); } + resetPinnedStateForAllUserExtensions(pinned: boolean): Promise { + return this.channel.call('resetPinnedStateForAllUserExtensions', [pinned]); + } + toggleAppliationScope(local: ILocalExtension, fromProfileLocation: URI): Promise { return this.channel.call('toggleAppliationScope', [local, fromProfileLocation]) .then(extension => transformIncomingExtension(extension, null)); diff --git a/src/vs/platform/extensionManagement/node/extensionsWatcher.ts b/src/vs/platform/extensionManagement/node/extensionsWatcher.ts index ba8e026b893..a56087b0b51 100644 --- a/src/vs/platform/extensionManagement/node/extensionsWatcher.ts +++ b/src/vs/platform/extensionManagement/node/extensionsWatcher.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { getErrorMessage } from 'vs/base/common/errors'; import { Emitter } from 'vs/base/common/event'; import { combinedDisposable, Disposable, DisposableMap } from 'vs/base/common/lifecycle'; import { ResourceSet } from 'vs/base/common/map'; @@ -40,7 +41,7 @@ export class ExtensionsWatcher extends Disposable { private readonly logService: ILogService, ) { super(); - this.initialize().then(null, error => logService.error(error)); + this.initialize().then(null, error => logService.error('Error while initializing Extensions Watcher', getErrorMessage(error))); } private async initialize(): Promise { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 5eacf022177..d3109d7f7d9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -72,7 +72,6 @@ import { UpdateAction, WebInstallAction, TogglePreReleaseExtensionAction, - ExtensionAction, } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { Delegate } from 'vs/workbench/contrib/extensions/browser/extensionsList'; import { ExtensionData, ExtensionsGridView, ExtensionsTree, getExtensions } from 'vs/workbench/contrib/extensions/browser/extensionsViewer'; @@ -334,8 +333,7 @@ export class ExtensionEditor extends EditorPane { const actions = [ this.instantiationService.createInstance(ExtensionRuntimeStateAction), this.instantiationService.createInstance(ExtensionStatusLabelAction), - this.instantiationService.createInstance(ButtonWithDropDownExtensionAction, 'extensions.updateActions', ExtensionAction.PROMINENT_LABEL_ACTION_CLASS, - [[this.instantiationService.createInstance(UpdateAction, true)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, true, [true, 'onlyEnabledExtensions'])]]), + this.instantiationService.createInstance(UpdateAction, true), this.instantiationService.createInstance(SetColorThemeAction), this.instantiationService.createInstance(SetFileIconThemeAction), this.instantiationService.createInstance(SetProductIconThemeAction), @@ -357,7 +355,7 @@ export class ExtensionEditor extends EditorPane { ] ]), this.instantiationService.createInstance(TogglePreReleaseExtensionAction), - this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, false, [false, 'onlySelectedExtensions']), + this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction), new ExtensionEditorManageExtensionAction(this.scopedContextKeyService || this.contextKeyService, this.instantiationService), ]; diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 33f961fc455..0daeda950dc 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -60,7 +60,6 @@ import { ExtensionEnablementWorkspaceTrustTransitionParticipant } from 'vs/workb import { clearSearchResultsIcon, configureRecommendedIcon, extensionsViewIcon, filterIcon, installWorkspaceRecommendedIcon, refreshIcon } from 'vs/workbench/contrib/extensions/browser/extensionsIcons'; import { EXTENSION_CATEGORIES } from 'vs/platform/extensions/common/extensions'; import { Disposable, DisposableStore, IDisposable, isDisposable } from 'vs/base/common/lifecycle'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { Query } from 'vs/workbench/contrib/extensions/common/extensionQuery'; @@ -79,6 +78,7 @@ import { CONTEXT_KEYBINDINGS_EDITOR } from 'vs/workbench/contrib/preferences/com import { DeprecatedExtensionsChecker } from 'vs/workbench/contrib/extensions/browser/deprecatedExtensionsChecker'; import { ProgressLocation } from 'vs/platform/progress/common/progress'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; +import { IConfigurationMigrationRegistry, Extensions as ConfigurationMigrationExtensions } from 'vs/workbench/common/configuration'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService, InstantiationType.Eager /* Auto updates extensions */); @@ -126,17 +126,15 @@ Registry.as(ConfigurationExtensions.Configuration) ...extensionsConfigurationNodeBase, properties: { 'extensions.autoUpdate': { - enum: [true, 'onlyEnabledExtensions', 'onlySelectedExtensions', false,], + enum: [true, 'onlyEnabledExtensions', false,], enumItemLabels: [ localize('all', "All Extensions"), localize('enabled', "Only Enabled Extensions"), - localize('selected', "Only Selected Extensions"), localize('none', "None"), ], enumDescriptions: [ - localize('extensions.autoUpdate.true', 'Download and install updates automatically for all extensions except for those updates are ignored.'), - localize('extensions.autoUpdate.enabled', 'Download and install updates automatically only for enabled extensions except for those updates are ignored. Disabled extensions are not updated automatically.'), - localize('extensions.autoUpdate.selected', 'Download and install updates automatically only for selected extensions.'), + localize('extensions.autoUpdate.true', 'Download and install updates automatically for all extensions.'), + localize('extensions.autoUpdate.enabled', 'Download and install updates automatically only for enabled extensions.'), localize('extensions.autoUpdate.false', 'Extensions are not automatically updated.'), ], description: localize('extensions.autoUpdate', "Controls the automatic update behavior of extensions. The updates are fetched from a Microsoft online service."), @@ -633,57 +631,38 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi } }); - const autoUpdateExtensionsSubMenu = new MenuId('autoUpdateExtensionsSubMenu'); - MenuRegistry.appendMenuItem(MenuId.ViewContainerTitle, { - submenu: autoUpdateExtensionsSubMenu, - title: localize('configure auto updating extensions', "Auto Update Extensions"), - when: ContextKeyExpr.and(ContextKeyExpr.equals('viewContainer', VIEWLET_ID), CONTEXT_HAS_GALLERY), - group: '1_updates', - order: 5, - }); - - this.registerExtensionAction({ - id: 'configureExtensionsAutoUpdate.all', - title: localize('configureExtensionsAutoUpdate.all', "All Extensions"), - toggled: ContextKeyExpr.and(ContextKeyExpr.has(`config.${AutoUpdateConfigurationKey}`), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions')), - menu: [{ - id: autoUpdateExtensionsSubMenu, - order: 1, - }], - run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, true) - }); - - this.registerExtensionAction({ - id: 'configureExtensionsAutoUpdate.enabled', - title: localize('configureExtensionsAutoUpdate.enabled', "Enabled Extensions"), - toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), - menu: [{ - id: autoUpdateExtensionsSubMenu, - order: 2, - }], - run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlyEnabledExtensions') - }); - + const enableAutoUpdateWhenCondition = ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false); this.registerExtensionAction({ - id: 'configureExtensionsAutoUpdate.selected', - title: localize('configureExtensionsAutoUpdate.selected', "Selected Extensions"), - toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), + id: 'workbench.extensions.action.enableAutoUpdate', + title: localize2('enableAutoUpdate', 'Enable Auto Update for All Extensions'), + category: ExtensionsLocalizedLabel, + precondition: enableAutoUpdateWhenCondition, menu: [{ - id: autoUpdateExtensionsSubMenu, - order: 2, + id: MenuId.ViewContainerTitle, + order: 5, + group: '1_updates', + when: enableAutoUpdateWhenCondition + }, { + id: MenuId.CommandPalette, }], - run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, 'onlySelectedExtensions') + run: (accessor: ServicesAccessor) => accessor.get(IExtensionsWorkbenchService).updateAutoUpdateValue(true) }); + const disableAutoUpdateWhenCondition = ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, false); this.registerExtensionAction({ - id: 'configureExtensionsAutoUpdate.none', - title: localize('configureExtensionsAutoUpdate.none', "None"), - toggled: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false), + id: 'workbench.extensions.action.disableAutoUpdate', + title: localize2('disableAutoUpdate', 'Disable Auto Update for All Extensions'), + precondition: disableAutoUpdateWhenCondition, + category: ExtensionsLocalizedLabel, menu: [{ - id: autoUpdateExtensionsSubMenu, - order: 3, + id: MenuId.ViewContainerTitle, + order: 5, + group: '1_updates', + when: disableAutoUpdateWhenCondition + }, { + id: MenuId.CommandPalette, }], - run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, false) + run: (accessor: ServicesAccessor) => accessor.get(IExtensionsWorkbenchService).updateAutoUpdateValue(false) }); this.registerExtensionAction({ @@ -722,24 +701,6 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi } }); - this.registerExtensionAction({ - id: 'workbench.extensions.action.disableAutoUpdate', - title: localize2('disableAutoUpdate', 'Disable Auto Update for All Extensions'), - category: ExtensionsLocalizedLabel, - f1: true, - precondition: CONTEXT_HAS_GALLERY, - run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, false) - }); - - this.registerExtensionAction({ - id: 'workbench.extensions.action.enableAutoUpdate', - title: localize2('enableAutoUpdate', 'Enable Auto Update for All Extensions'), - category: ExtensionsLocalizedLabel, - f1: true, - precondition: CONTEXT_HAS_GALLERY, - run: (accessor: ServicesAccessor) => accessor.get(IConfigurationService).updateValue(AutoUpdateConfigurationKey, true) - }); - this.registerExtensionAction({ id: 'workbench.extensions.action.enableAll', title: localize2('enableAll', 'Enable All Extensions'), @@ -1384,18 +1345,23 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi id: ToggleAutoUpdateForExtensionAction.ID, title: ToggleAutoUpdateForExtensionAction.LABEL, category: ExtensionsLocalizedLabel, + precondition: ContextKeyExpr.and(ContextKeyExpr.or(ContextKeyExpr.notEquals(`config.${AutoUpdateConfigurationKey}`, 'onlyEnabledExtensions'), ContextKeyExpr.equals('isExtensionEnabled', true)), ContextKeyExpr.not('extensionDisallowInstall')), menu: { id: MenuId.ExtensionContext, group: UPDATE_ACTIONS_GROUP, order: 1, - when: ContextKeyExpr.and(ContextKeyExpr.not('inExtensionEditor'), ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.or(ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false)),) + when: ContextKeyExpr.and( + ContextKeyExpr.not('inExtensionEditor'), + ContextKeyExpr.equals('extensionStatus', 'installed'), + ContextKeyExpr.not('isBuiltinExtension'), + ) }, run: async (accessor: ServicesAccessor, id: string) => { const instantiationService = accessor.get(IInstantiationService); const extensionWorkbenchService = accessor.get(IExtensionsWorkbenchService); const extension = extensionWorkbenchService.local.find(e => areSameExtensions(e.identifier, { id })); if (extension) { - const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, false, []); + const action = instantiationService.createInstance(ToggleAutoUpdateForExtensionAction); action.extension = extension; return action.run(); } @@ -1406,11 +1372,12 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi id: ToggleAutoUpdatesForPublisherAction.ID, title: { value: ToggleAutoUpdatesForPublisherAction.LABEL, original: 'Auto Update (Publisher)' }, category: ExtensionsLocalizedLabel, + precondition: ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false), menu: { id: MenuId.ExtensionContext, group: UPDATE_ACTIONS_GROUP, order: 2, - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension'), ContextKeyExpr.or(ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, 'onlySelectedExtensions'), ContextKeyExpr.equals(`config.${AutoUpdateConfigurationKey}`, false)),) + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'installed'), ContextKeyExpr.not('isBuiltinExtension')) }, run: async (accessor: ServicesAccessor, id: string) => { const instantiationService = accessor.get(IInstantiationService); @@ -1842,3 +1809,14 @@ if (isWeb) { // Running Extensions registerAction2(ShowRuntimeExtensionsAction); + +Registry.as(ConfigurationMigrationExtensions.ConfigurationMigration) + .registerConfigurationMigrations([{ + key: AutoUpdateConfigurationKey, + migrateFn: (value, accessor) => { + if (value === 'onlySelectedExtensions') { + return { value: false }; + } + return []; + } + }]); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index fccbb361feb..03c914611e9 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -12,7 +12,7 @@ import { Emitter, Event } from 'vs/base/common/event'; import * as json from 'vs/base/common/json'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { disposeIfDisposable } from 'vs/base/common/lifecycle'; -import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, UPDATE_ACTIONS_GROUP, AutoUpdateConfigurationKey, AutoUpdateConfigurationValue, ExtensionEditorTab, ExtensionRuntimeActionType, IExtensionArg } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, IExtensionContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, UPDATE_ACTIONS_GROUP, ExtensionEditorTab, ExtensionRuntimeActionType, IExtensionArg, AutoUpdateConfigurationKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { IGalleryExtension, IExtensionGalleryService, ILocalExtension, InstallOptions, InstallOperation, TargetPlatformToString, ExtensionManagementErrorCode } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IWorkbenchExtensionManagementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; @@ -943,11 +943,9 @@ export class ToggleAutoUpdateForExtensionAction extends ExtensionAction { private static readonly DisabledClass = `${ToggleAutoUpdateForExtensionAction.EnabledClass} hide`; constructor( - private readonly enableWhenOutdated: boolean, - private readonly enableWhenAutoUpdateValue: AutoUpdateConfigurationValue[], @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IConfigurationService configurationService: IConfigurationService, - ) { super(ToggleAutoUpdateForExtensionAction.ID, ToggleAutoUpdateForExtensionAction.LABEL.value, ToggleAutoUpdateForExtensionAction.DisabledClass); this._register(configurationService.onDidChangeConfiguration(e => { @@ -967,10 +965,10 @@ export class ToggleAutoUpdateForExtensionAction extends ExtensionAction { if (this.extension.isBuiltin) { return; } - if (this.enableWhenOutdated && (this.extension.state !== ExtensionState.Installed || !this.extension.outdated)) { + if (this.extension.deprecationInfo?.disallowInstall) { return; } - if (!this.enableWhenAutoUpdateValue.includes(this.extensionsWorkbenchService.getAutoUpdateValue())) { + if (this.extensionsWorkbenchService.getAutoUpdateValue() === 'onlyEnabledExtensions' && !this.extensionEnablementService.isEnabledEnablementState(this.extension.enablementState)) { return; } this.enabled = true; @@ -1128,6 +1126,7 @@ export class DropDownExtensionActionViewItem extends ActionViewItem { async function getContextMenuActionsGroups(extension: IExtension | undefined | null, contextKeyService: IContextKeyService, instantiationService: IInstantiationService): Promise<[string, Array][]> { return instantiationService.invokeFunction(async accessor => { const extensionsWorkbenchService = accessor.get(IExtensionsWorkbenchService); + const extensionEnablementService = accessor.get(IWorkbenchExtensionEnablementService); const menuService = accessor.get(IMenuService); const extensionRecommendationsService = accessor.get(IExtensionRecommendationsService); const extensionIgnoredRecommendationsService = accessor.get(IExtensionIgnoredRecommendationsService); @@ -1150,6 +1149,8 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n cksOverlay.push(['isExtensionRecommended', !!extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]]); cksOverlay.push(['isExtensionWorkspaceRecommended', extensionRecommendationsService.getAllRecommendationsWithReason()[extension.identifier.id.toLowerCase()]?.reasonId === ExtensionRecommendationReason.Workspace]); cksOverlay.push(['isUserIgnoredRecommendation', extensionIgnoredRecommendationsService.globalIgnoredRecommendations.some(e => e === extension.identifier.id.toLowerCase())]); + cksOverlay.push(['isExtensionPinned', extension.pinned]); + cksOverlay.push(['isExtensionEnabled', extensionEnablementService.isEnabledEnablementState(extension.enablementState)]); switch (extension.state) { case ExtensionState.Installing: cksOverlay.push(['extensionStatus', 'installing']); @@ -1325,6 +1326,14 @@ export class MenuItemExtensionAction extends ExtensionAction { super(action.id, action.label); } + override get enabled(): boolean { + return this.action.enabled; + } + + override set enabled(value: boolean) { + this.action.enabled = value; + } + update() { if (!this.extension) { return; @@ -1418,7 +1427,7 @@ export class TogglePreReleaseExtensionAction extends ExtensionAction { export class InstallAnotherVersionAction extends ExtensionAction { static readonly ID = 'workbench.extensions.action.install.anotherVersion'; - static readonly LABEL = localize('install another version', "Install Another Version..."); + static readonly LABEL = localize('install another version', "Install Specific Version..."); constructor( extension: IExtension | null, @@ -1461,7 +1470,6 @@ export class InstallAnotherVersionAction extends ExtensionAction { id: v.version, label: v.version, description: `${fromNow(new Date(Date.parse(v.date)), true)}${v.isPreReleaseVersion ? ` (${localize('pre-release', "pre-release")})` : ''}${v.version === this.extension?.local?.manifest.version ? ` (${localize('current', "current")})` : ''}`, - latest: i === 0, ariaLabel: `${v.isPreReleaseVersion ? 'Pre-Release version' : 'Release version'} ${v.version}`, isPreReleaseVersion: v.isPreReleaseVersion }; @@ -1476,14 +1484,9 @@ export class InstallAnotherVersionAction extends ExtensionAction { return; } try { - if (pick.latest) { - const [extension] = pick.id !== this.extension.version ? await this.extensionsWorkbenchService.getExtensions([{ id: this.extension.identifier.id, preRelease: pick.isPreReleaseVersion }], CancellationToken.None) : [this.extension]; - await this.extensionsWorkbenchService.install(extension ?? this.extension, { installPreReleaseVersion: pick.isPreReleaseVersion }); - } else { - await this.extensionsWorkbenchService.install(this.extension, { installPreReleaseVersion: pick.isPreReleaseVersion, version: pick.id }); - } + await this.extensionsWorkbenchService.install(this.extension, { installPreReleaseVersion: pick.isPreReleaseVersion, version: pick.id }); } catch (error) { - this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension, pick.latest ? this.extension.latestVersion : pick.id, InstallOperation.Install, error).run(); + this.instantiationService.createInstance(PromptExtensionInstallFailureAction, this.extension, pick.id, InstallOperation.Install, error).run(); } } return null; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index d74439e6c52..95c05e762e5 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -13,7 +13,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IPagedRenderer } from 'vs/base/browser/ui/list/listPaging'; import { Event } from 'vs/base/common/event'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService } from 'vs/workbench/contrib/extensions/common/extensions'; -import { ManageExtensionAction, ExtensionRuntimeStateAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ButtonWithDropDownExtensionAction, InstallDropdownAction, InstallingLabelAction, ButtonWithDropdownExtensionActionViewItem, DropDownExtensionAction, WebInstallAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction, ToggleAutoUpdateForExtensionAction, ExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; +import { ManageExtensionAction, ExtensionRuntimeStateAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ButtonWithDropDownExtensionAction, InstallDropdownAction, InstallingLabelAction, ButtonWithDropdownExtensionActionViewItem, DropDownExtensionAction, WebInstallAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionActivationStatusWidget, PreReleaseBookmarkWidget, extensionVerifiedPublisherIconColor, VerifiedPublisherWidget } from 'vs/workbench/contrib/extensions/browser/extensionsWidgets'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -126,8 +126,7 @@ export class Renderer implements IPagedRenderer { this.instantiationService.createInstance(ExtensionStatusLabelAction), this.instantiationService.createInstance(MigrateDeprecatedExtensionAction, true), this.instantiationService.createInstance(ExtensionRuntimeStateAction), - this.instantiationService.createInstance(ButtonWithDropDownExtensionAction, 'extensions.updateActions', ExtensionAction.PROMINENT_LABEL_ACTION_CLASS, - [[this.instantiationService.createInstance(UpdateAction, false)], [this.instantiationService.createInstance(ToggleAutoUpdateForExtensionAction, true, [true, 'onlyEnabledExtensions'])]]), + this.instantiationService.createInstance(UpdateAction, false), this.instantiationService.createInstance(InstallDropdownAction), this.instantiationService.createInstance(InstallingLabelAction), this.instantiationService.createInstance(SetLanguageAction), diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 826da4474fb..e4b62d95f4a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -519,6 +519,7 @@ ${this.description} } const EXTENSIONS_AUTO_UPDATE_KEY = 'extensions.autoUpdate'; +const EXTENSIONS_DONOT_AUTO_UPDATE_KEY = 'extensions.donotAutoUpdate'; class Extensions extends Disposable { @@ -991,14 +992,29 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension this.initializeAutoUpdate(); this.reportInstalledExtensionsTelemetry(); this._register(Event.debounce(this.onChange, () => undefined, 100)(() => this.reportProgressFromOtherSources())); - this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION, EXTENSIONS_AUTO_UPDATE_KEY, this._store)(e => this.onDidSelectedExtensionToAutoUpdateValueChange(false))); + this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION, EXTENSIONS_AUTO_UPDATE_KEY, this._store)(e => this.onDidSelectedExtensionToAutoUpdateValueChange())); + this._register(this.storageService.onDidChangeValue(StorageScope.APPLICATION, EXTENSIONS_DONOT_AUTO_UPDATE_KEY, this._store)(e => this.onDidSelectedExtensionToAutoUpdateValueChange())); } private initializeAutoUpdate(): void { + // Initialise Auto Update Value + let autoUpdateValue = this.getAutoUpdateValue(); + // Register listeners for auto updates this._register(this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(AutoUpdateConfigurationKey)) { - this.onDidAutoUpdateConfigurationChange(); + const wasAutoUpdateEnabled = autoUpdateValue !== false; + autoUpdateValue = this.getAutoUpdateValue(); + const isAutoUpdateEnabled = this.isAutoUpdateEnabled(); + if (wasAutoUpdateEnabled !== isAutoUpdateEnabled) { + this.setEnabledAutoUpdateExtensions([]); + this.setDisabledAutoUpdateExtensions([]); + this._onChange.fire(undefined); + this.extensionManagementService.resetPinnedStateForAllUserExtensions(!isAutoUpdateEnabled); + } + if (isAutoUpdateEnabled) { + this.eventuallyAutoUpdateExtensions(); + } } if (e.affectsConfiguration(AutoCheckUpdatesConfigurationKey)) { if (this.isAutoCheckUpdatesEnabled()) { @@ -1013,9 +1029,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension })); this._register(Event.debounce(this.onChange, () => undefined, 100)(() => this.hasOutdatedExtensionsContextKey.set(this.outdated.length > 0))); this._register(this.updateService.onStateChange(e => { - if (!this.isAutoUpdateEnabled()) { - return; - } if ((e.type === StateType.CheckingForUpdates && e.explicit) || e.type === StateType.AvailableForDownload || e.type === StateType.Downloaded) { this.eventuallyCheckForUpdates(true); } @@ -1043,6 +1056,36 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension })); } + private isAutoUpdateEnabled(): boolean { + return this.getAutoUpdateValue() !== false; + } + + getAutoUpdateValue(): AutoUpdateConfigurationValue { + const autoUpdate = this.configurationService.getValue(AutoUpdateConfigurationKey); + if (autoUpdate === 'onlySelectedExtensions') { + return false; + } + return isBoolean(autoUpdate) || autoUpdate === 'onlyEnabledExtensions' ? autoUpdate : true; + } + + async updateAutoUpdateValue(value: AutoUpdateConfigurationValue): Promise { + const wasEnabled = this.isAutoUpdateEnabled(); + const isEnabled = value !== false; + if (wasEnabled !== isEnabled) { + const result = await this.dialogService.confirm({ + title: nls.localize('confirmEnableDisableAutoUpdate', "Auto Update Extensions"), + message: isEnabled + ? nls.localize('confirmEnableAutoUpdate', "Do you want to enable auto update for all extensions?") + : nls.localize('confirmDisableAutoUpdate', "Do you want to disable auto update for all extensions?"), + detail: nls.localize('confirmEnableDisableAutoUpdateDetail', "This will reset the auto update settings you have set for individual extensions."), + }); + if (!result.confirmed) { + return; + } + } + await this.configurationService.updateValue(AutoUpdateConfigurationKey, value); + } + private readonly autoRestartListenerDisposable = this._register(new MutableDisposable()); private registerAutoRestartListener(): void { this.autoRestartListenerDisposable.value = undefined; @@ -1606,15 +1649,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return ExtensionState.Uninstalled; } - private async onDidAutoUpdateConfigurationChange(): Promise { - await this.updateExtensionsPinnedState(); - if (this.isAutoUpdateEnabled()) { - this.checkForUpdates(); - } else { - this.setSelectedExtensionsToAutoUpdate([]); - } - } - async checkForUpdates(onlyBuiltin?: boolean): Promise { if (!this.galleryService.isEnabled()) { return; @@ -1700,20 +1734,11 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return; } await Promise.allSettled(extensions.map(extensions => extensions.syncInstalledExtensionsWithGallery(gallery, this.getProductVersion()))); - if (this.isAutoUpdateEnabled()) { + if (this.outdated.length) { this.eventuallyAutoUpdateExtensions(); } } - getAutoUpdateValue(): AutoUpdateConfigurationValue { - const autoUpdate = this.configurationService.getValue(AutoUpdateConfigurationKey); - return isBoolean(autoUpdate) || autoUpdate === 'onlyEnabledExtensions' || autoUpdate === 'onlySelectedExtensions' ? autoUpdate : true; - } - - isAutoUpdateEnabled(): boolean { - return this.getAutoUpdateValue() !== false; - } - private isAutoCheckUpdatesEnabled(): boolean { return this.configurationService.getValue(AutoCheckUpdatesConfigurationKey); } @@ -1721,7 +1746,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension private eventuallyCheckForUpdates(immediate = false): void { this.updatesCheckDelayer.cancel(); this.updatesCheckDelayer.trigger(async () => { - if (this.isAutoUpdateEnabled() || this.isAutoCheckUpdatesEnabled()) { + if (this.isAutoCheckUpdatesEnabled()) { await this.checkForUpdates(); } this.eventuallyCheckForUpdates(); @@ -1762,10 +1787,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } private async autoUpdateExtensions(): Promise { - if (!this.isAutoUpdateEnabled()) { - return; - } - const toUpdate = this.outdated.filter(e => !e.local?.pinned && this.shouldAutoUpdateExtension(e)); if (!toUpdate.length) { return; @@ -1798,32 +1819,43 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return undefined; } - private async updateExtensionsPinnedState(): Promise { - await Promise.all(this.installed.map(async e => { - if (e.isBuiltin) { - return; + private shouldAutoUpdateExtension(extension: IExtension): boolean { + if (extension.deprecationInfo?.disallowInstall) { + return false; + } + + const autoUpdateValue = this.getAutoUpdateValue(); + + if (autoUpdateValue === false) { + const extensionsToAutoUpdate = this.getEnabledAutoUpdateExtensions(); + const extensionId = extension.identifier.id.toLowerCase(); + if (extensionsToAutoUpdate.includes(extensionId)) { + return true; } - const shouldBePinned = !this.shouldAutoUpdateExtension(e); - if (e.local && e.local.pinned !== shouldBePinned) { - await this.extensionManagementService.updateMetadata(e.local, { pinned: shouldBePinned }); + if (this.isAutoUpdateEnabledForPublisher(extension.publisher) && !extensionsToAutoUpdate.includes(`-${extensionId}`)) { + return true; } - })); - } + return false; + } - private shouldAutoUpdateExtension(extension: IExtension): boolean { - const autoUpdate = this.getAutoUpdateValue(); - if (isBoolean(autoUpdate)) { - return autoUpdate; + if (extension.pinned) { + return false; + } + + const disabledAutoUpdateExtensions = this.getDisabledAutoUpdateExtensions(); + if (disabledAutoUpdateExtensions.includes(extension.identifier.id.toLowerCase())) { + return false; } - if (autoUpdate === 'onlyEnabledExtensions') { + if (autoUpdateValue === true) { + return true; + } + + if (autoUpdateValue === 'onlyEnabledExtensions') { return this.extensionEnablementService.isEnabledEnablementState(extension.enablementState); } - const extensionsToAutoUpdate = this.getSelectedExtensionsToAutoUpdate(); - const extensionId = extension.identifier.id.toLowerCase(); - return extensionsToAutoUpdate.includes(extensionId) || - (!extensionsToAutoUpdate.includes(`-${extensionId}`) && this.isAutoUpdateEnabledForPublisher(extension.publisher)); + return false; } isAutoUpdateEnabledFor(extensionOrPublisher: IExtension | string): boolean { @@ -1831,16 +1863,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (EXTENSION_IDENTIFIER_REGEX.test(extensionOrPublisher)) { throw new Error('Expected publisher string, found extension identifier'); } - const autoUpdate = this.getAutoUpdateValue(); - if (isBoolean(autoUpdate)) { - return autoUpdate; - } - if (autoUpdate === 'onlyEnabledExtensions') { - return false; + if (this.isAutoUpdateEnabled()) { + return true; } return this.isAutoUpdateEnabledForPublisher(extensionOrPublisher); } - return !extensionOrPublisher.local?.pinned && this.shouldAutoUpdateExtension(extensionOrPublisher); + return this.shouldAutoUpdateExtension(extensionOrPublisher); } private isAutoUpdateEnabledForPublisher(publisher: string): boolean { @@ -1849,101 +1877,135 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } async updateAutoUpdateEnablementFor(extensionOrPublisher: IExtension | string, enable: boolean): Promise { - const autoUpdateValue = this.getAutoUpdateValue(); - - if (autoUpdateValue === true || autoUpdateValue === 'onlyEnabledExtensions') { + if (this.isAutoUpdateEnabled()) { if (isString(extensionOrPublisher)) { throw new Error('Expected extension, found publisher string'); } if (!extensionOrPublisher.local) { throw new Error('Only installed extensions can be pinned'); } - await this.extensionManagementService.updateMetadata(extensionOrPublisher.local, { pinned: !enable }); - if (enable) { - this.eventuallyAutoUpdateExtensions(); - } - return; - } - if (autoUpdateValue === false && enable) { - await this.configurationService.updateValue(AutoUpdateConfigurationKey, 'onlySelectedExtensions'); - } - - let update = false; - const autoUpdateExtensions = this.getSelectedExtensionsToAutoUpdate(); - if (isString(extensionOrPublisher)) { - if (EXTENSION_IDENTIFIER_REGEX.test(extensionOrPublisher)) { - throw new Error('Expected publisher string, found extension identifier'); + const disabledAutoUpdateExtensions = this.getDisabledAutoUpdateExtensions(); + const extensionId = extensionOrPublisher.identifier.id.toLowerCase(); + const extensionIndex = disabledAutoUpdateExtensions.indexOf(extensionId); + if (enable) { + if (extensionIndex !== -1) { + disabledAutoUpdateExtensions.splice(extensionIndex, 1); + } } - extensionOrPublisher = extensionOrPublisher.toLowerCase(); - if (this.isAutoUpdateEnabledFor(extensionOrPublisher) !== enable) { - update = true; - if (enable) { - autoUpdateExtensions.push(extensionOrPublisher); - } else { - if (autoUpdateExtensions.includes(extensionOrPublisher)) { - autoUpdateExtensions.splice(autoUpdateExtensions.indexOf(extensionOrPublisher), 1); - } + else { + if (extensionIndex === -1) { + disabledAutoUpdateExtensions.push(extensionId); } } - } else { - const extensionId = extensionOrPublisher.identifier.id.toLowerCase(); - const enableAutoUpdatesForPublisher = this.isAutoUpdateEnabledFor(extensionOrPublisher.publisher.toLowerCase()); - const enableAutoUpdatesForExtension = autoUpdateExtensions.includes(extensionId); - const disableAutoUpdatesForExtension = autoUpdateExtensions.includes(`-${extensionId}`); + this.setDisabledAutoUpdateExtensions(disabledAutoUpdateExtensions); + if (enable && extensionOrPublisher.pinned) { + await this.extensionManagementService.updateMetadata(extensionOrPublisher.local, { pinned: false }); + } + this._onChange.fire(extensionOrPublisher); + } - if (enable) { - if (disableAutoUpdatesForExtension) { - autoUpdateExtensions.splice(autoUpdateExtensions.indexOf(`-${extensionId}`), 1); - update = true; + else { + const enabledAutoUpdateExtensions = this.getEnabledAutoUpdateExtensions(); + if (isString(extensionOrPublisher)) { + if (EXTENSION_IDENTIFIER_REGEX.test(extensionOrPublisher)) { + throw new Error('Expected publisher string, found extension identifier'); } - if (enableAutoUpdatesForPublisher) { - if (enableAutoUpdatesForExtension) { - autoUpdateExtensions.splice(autoUpdateExtensions.indexOf(extensionId), 1); - update = true; - } - } else { - if (!enableAutoUpdatesForExtension) { - autoUpdateExtensions.push(extensionId); - update = true; + extensionOrPublisher = extensionOrPublisher.toLowerCase(); + if (this.isAutoUpdateEnabledFor(extensionOrPublisher) !== enable) { + if (enable) { + enabledAutoUpdateExtensions.push(extensionOrPublisher); + } else { + if (enabledAutoUpdateExtensions.includes(extensionOrPublisher)) { + enabledAutoUpdateExtensions.splice(enabledAutoUpdateExtensions.indexOf(extensionOrPublisher), 1); + } } } - if (extensionOrPublisher.local?.pinned) { - await this.extensionManagementService.updateMetadata(extensionOrPublisher.local, { pinned: false }); + this.setEnabledAutoUpdateExtensions(enabledAutoUpdateExtensions); + for (const e of this.installed) { + if (e.publisher.toLowerCase() === extensionOrPublisher) { + this._onChange.fire(e); + } } - } - // Disable Auto Updates - else { - if (enableAutoUpdatesForExtension) { - autoUpdateExtensions.splice(autoUpdateExtensions.indexOf(extensionId), 1); - update = true; + } else { + const extensionId = extensionOrPublisher.identifier.id.toLowerCase(); + const enableAutoUpdatesForPublisher = this.isAutoUpdateEnabledFor(extensionOrPublisher.publisher.toLowerCase()); + const enableAutoUpdatesForExtension = enabledAutoUpdateExtensions.includes(extensionId); + const disableAutoUpdatesForExtension = enabledAutoUpdateExtensions.includes(`-${extensionId}`); + + if (enable) { + if (disableAutoUpdatesForExtension) { + enabledAutoUpdateExtensions.splice(enabledAutoUpdateExtensions.indexOf(`-${extensionId}`), 1); + } + if (enableAutoUpdatesForPublisher) { + if (enableAutoUpdatesForExtension) { + enabledAutoUpdateExtensions.splice(enabledAutoUpdateExtensions.indexOf(extensionId), 1); + } + } else { + if (!enableAutoUpdatesForExtension) { + enabledAutoUpdateExtensions.push(extensionId); + } + } } - if (enableAutoUpdatesForPublisher) { - if (!disableAutoUpdatesForExtension) { - autoUpdateExtensions.push(`-${extensionId}`); - update = true; + // Disable Auto Updates + else { + if (enableAutoUpdatesForExtension) { + enabledAutoUpdateExtensions.splice(enabledAutoUpdateExtensions.indexOf(extensionId), 1); } - } else { - if (disableAutoUpdatesForExtension) { - autoUpdateExtensions.splice(autoUpdateExtensions.indexOf(`-${extensionId}`), 1); - update = true; + if (enableAutoUpdatesForPublisher) { + if (!disableAutoUpdatesForExtension) { + enabledAutoUpdateExtensions.push(`-${extensionId}`); + } + } else { + if (disableAutoUpdatesForExtension) { + enabledAutoUpdateExtensions.splice(enabledAutoUpdateExtensions.indexOf(`-${extensionId}`), 1); + } } } + this.setEnabledAutoUpdateExtensions(enabledAutoUpdateExtensions); + this._onChange.fire(extensionOrPublisher); } } - if (update) { - this.setSelectedExtensionsToAutoUpdate(autoUpdateExtensions); - await this.onDidSelectedExtensionToAutoUpdateValueChange(true); - if (autoUpdateValue === 'onlySelectedExtensions' && autoUpdateExtensions.length === 0) { - await this.configurationService.updateValue(AutoUpdateConfigurationKey, false); - } + + if (enable) { + this.autoUpdateExtensions(); } } - private async onDidSelectedExtensionToAutoUpdateValueChange(forceUpdate: boolean): Promise { - if (forceUpdate || this.selectedExtensionsToAutoUpdateValue !== this.getSelectedExtensionsToAutoUpdateValue() /* This checks if current window changed the value or not */) { - await this.updateExtensionsPinnedState(); - this.eventuallyAutoUpdateExtensions(); + private onDidSelectedExtensionToAutoUpdateValueChange(): void { + if ( + this.enabledAuotUpdateExtensionsValue !== this.getEnabledAutoUpdateExtensionsValue() /* This checks if current window changed the value or not */ + || this.disabledAutoUpdateExtensionsValue !== this.getDisabledAutoUpdateExtensionsValue() /* This checks if current window changed the value or not */ + ) { + const userExtensions = this.installed.filter(e => !e.isBuiltin); + const groupBy = (extensions: IExtension[]): IExtension[][] => { + const shouldAutoUpdate: IExtension[] = []; + const shouldNotAutoUpdate: IExtension[] = []; + for (const extension of extensions) { + if (this.shouldAutoUpdateExtension(extension)) { + shouldAutoUpdate.push(extension); + } else { + shouldNotAutoUpdate.push(extension); + } + } + return [shouldAutoUpdate, shouldNotAutoUpdate]; + }; + + const [wasShouldAutoUpdate, wasShouldNotAutoUpdate] = groupBy(userExtensions); + this._enabledAutoUpdateExtensionsValue = undefined; + this._disabledAutoUpdateExtensionsValue = undefined; + const [shouldAutoUpdate, shouldNotAutoUpdate] = groupBy(userExtensions); + + for (const e of wasShouldAutoUpdate ?? []) { + if (shouldNotAutoUpdate?.includes(e)) { + this._onChange.fire(e); + } + } + for (const e of wasShouldNotAutoUpdate ?? []) { + if (shouldAutoUpdate?.includes(e)) { + this._onChange.fire(e); + } + } } } @@ -2084,10 +2146,6 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension throw new Error(nls.localize('unknown', "Unable to install extension")); } - if (installOptions.version) { - await this.updateAutoUpdateEnablementFor(extension, false); - } - if (installOptions.enable) { if (extension.enablementState === EnablementState.DisabledWorkspace || extension.enablementState === EnablementState.DisabledGlobally) { if (installOptions.justification) { @@ -2571,12 +2629,12 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } private getPublishersToAutoUpdate(): string[] { - return this.getSelectedExtensionsToAutoUpdate().filter(id => !EXTENSION_IDENTIFIER_REGEX.test(id)); + return this.getEnabledAutoUpdateExtensions().filter(id => !EXTENSION_IDENTIFIER_REGEX.test(id)); } - getSelectedExtensionsToAutoUpdate(): string[] { + getEnabledAutoUpdateExtensions(): string[] { try { - const parsedValue = JSON.parse(this.selectedExtensionsToAutoUpdateValue); + const parsedValue = JSON.parse(this.enabledAuotUpdateExtensionsValue); if (Array.isArray(parsedValue)) { return parsedValue; } @@ -2584,32 +2642,70 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return []; } - private setSelectedExtensionsToAutoUpdate(selectedExtensionsToAutoUpdate: string[]): void { - this.selectedExtensionsToAutoUpdateValue = JSON.stringify(selectedExtensionsToAutoUpdate); + private setEnabledAutoUpdateExtensions(enabledAutoUpdateExtensions: string[]): void { + this.enabledAuotUpdateExtensionsValue = JSON.stringify(enabledAutoUpdateExtensions); } - private _selectedExtensionsToAutoUpdateValue: string | undefined; - private get selectedExtensionsToAutoUpdateValue(): string { - if (!this._selectedExtensionsToAutoUpdateValue) { - this._selectedExtensionsToAutoUpdateValue = this.getSelectedExtensionsToAutoUpdateValue(); + private _enabledAutoUpdateExtensionsValue: string | undefined; + private get enabledAuotUpdateExtensionsValue(): string { + if (!this._enabledAutoUpdateExtensionsValue) { + this._enabledAutoUpdateExtensionsValue = this.getEnabledAutoUpdateExtensionsValue(); } - return this._selectedExtensionsToAutoUpdateValue; + return this._enabledAutoUpdateExtensionsValue; } - private set selectedExtensionsToAutoUpdateValue(placeholderViewContainesValue: string) { - if (this.selectedExtensionsToAutoUpdateValue !== placeholderViewContainesValue) { - this._selectedExtensionsToAutoUpdateValue = placeholderViewContainesValue; - this.setSelectedExtensionsToAutoUpdateValue(placeholderViewContainesValue); + private set enabledAuotUpdateExtensionsValue(enabledAuotUpdateExtensionsValue: string) { + if (this.enabledAuotUpdateExtensionsValue !== enabledAuotUpdateExtensionsValue) { + this._enabledAutoUpdateExtensionsValue = enabledAuotUpdateExtensionsValue; + this.setEnabledAutoUpdateExtensionsValue(enabledAuotUpdateExtensionsValue); } } - private getSelectedExtensionsToAutoUpdateValue(): string { + private getEnabledAutoUpdateExtensionsValue(): string { return this.storageService.get(EXTENSIONS_AUTO_UPDATE_KEY, StorageScope.APPLICATION, '[]'); } - private setSelectedExtensionsToAutoUpdateValue(value: string): void { + private setEnabledAutoUpdateExtensionsValue(value: string): void { this.storageService.store(EXTENSIONS_AUTO_UPDATE_KEY, value, StorageScope.APPLICATION, StorageTarget.USER); } + getDisabledAutoUpdateExtensions(): string[] { + try { + const parsedValue = JSON.parse(this.disabledAutoUpdateExtensionsValue); + if (Array.isArray(parsedValue)) { + return parsedValue; + } + } catch (e) { /* Ignore */ } + return []; + } + + private setDisabledAutoUpdateExtensions(disabledAutoUpdateExtensions: string[]): void { + this.disabledAutoUpdateExtensionsValue = JSON.stringify(disabledAutoUpdateExtensions); + } + + private _disabledAutoUpdateExtensionsValue: string | undefined; + private get disabledAutoUpdateExtensionsValue(): string { + if (!this._disabledAutoUpdateExtensionsValue) { + this._disabledAutoUpdateExtensionsValue = this.getDisabledAutoUpdateExtensionsValue(); + } + + return this._disabledAutoUpdateExtensionsValue; + } + + private set disabledAutoUpdateExtensionsValue(disabledAutoUpdateExtensionsValue: string) { + if (this.disabledAutoUpdateExtensionsValue !== disabledAutoUpdateExtensionsValue) { + this._disabledAutoUpdateExtensionsValue = disabledAutoUpdateExtensionsValue; + this.setDisabledAutoUpdateExtensionsValue(disabledAutoUpdateExtensionsValue); + } + } + + private getDisabledAutoUpdateExtensionsValue(): string { + return this.storageService.get(EXTENSIONS_DONOT_AUTO_UPDATE_KEY, StorageScope.APPLICATION, '[]'); + } + + private setDisabledAutoUpdateExtensionsValue(value: string): void { + this.storageService.store(EXTENSIONS_DONOT_AUTO_UPDATE_KEY, value, StorageScope.APPLICATION, StorageTarget.USER); + } + } diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index bba7f851350..c619e3a0b57 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -62,6 +62,7 @@ export interface IExtension { readonly publisherUrl?: URI; readonly publisherDomain?: { link: string; verified: boolean }; readonly publisherSponsorLink?: URI; + readonly pinned: boolean; readonly version: string; readonly latestVersion: string; readonly preRelease: boolean; @@ -138,7 +139,7 @@ export interface IExtensionsWorkbenchService { isAutoUpdateEnabledFor(extensionOrPublisher: IExtension | string): boolean; updateAutoUpdateEnablementFor(extensionOrPublisher: IExtension | string, enable: boolean): Promise; open(extension: IExtension | string, options?: IExtensionEditorOptions): Promise; - isAutoUpdateEnabled(): boolean; + updateAutoUpdateValue(value: AutoUpdateConfigurationValue): Promise; getAutoUpdateValue(): AutoUpdateConfigurationValue; checkForUpdates(): Promise; getExtensionStatus(extension: IExtension): IExtensionsStatus | undefined; diff --git a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts index 95f83c91d4a..eee8b207a29 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-sandbox/extensionsWorkbenchService.test.ts @@ -25,7 +25,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { TestExtensionTipsService, TestSharedProcessService } from 'vs/workbench/test/electron-sandbox/workbenchTestServices'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { ConfigurationTarget, IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { IProgressService } from 'vs/platform/progress/common/progress'; import { ProgressService } from 'vs/workbench/services/progress/browser/progressService'; @@ -57,6 +57,7 @@ import { FileService } from 'vs/platform/files/common/fileService'; import { UserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfileService'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { toUserDataProfile } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; suite('ExtensionsWorkbenchServiceTest', () => { @@ -111,7 +112,8 @@ suite('ExtensionsWorkbenchServiceTest', () => { return local; }, async canInstall() { return true; }, - getTargetPlatform: async () => getTargetPlatform(platform, arch) + getTargetPlatform: async () => getTargetPlatform(platform, arch), + async resetPinnedStateForAllUserExtensions(pinned: boolean) { } }); instantiationService.stub(IExtensionManagementServerService, anExtensionManagementServerService({ @@ -1431,10 +1433,35 @@ suite('ExtensionsWorkbenchServiceTest', () => { await testObject.updateAutoUpdateEnablementFor(testObject.local[0], false); - assert.strictEqual(testObject.local[0].local?.pinned, true); + assert.strictEqual(testObject.local[0].local?.pinned, undefined); assert.strictEqual(testObject.local[1].local?.pinned, undefined); - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), []); + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), []); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), ['pub.a']); + }); + + test('Test disable autoupdate for extension when auto update is enabled for enabled extensions', async () => { + stubConfiguration('onlyEnabledExtensions'); + + const extension1 = aLocalExtension('a'); + const extension2 = aLocalExtension('b'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2]); + instantiationService.stub(IExtensionManagementService, 'updateMetadata', (local: Mutable, metadata: Partial) => { + local.pinned = !!metadata.pinned; + return local; + }); + testObject = await aWorkbenchService(); + + assert.strictEqual(testObject.local[0].local?.pinned, undefined); + assert.strictEqual(testObject.local[1].local?.pinned, undefined); + + await testObject.updateAutoUpdateEnablementFor(testObject.local[0], false); + + assert.strictEqual(testObject.local[0].local?.pinned, undefined); + assert.strictEqual(testObject.local[1].local?.pinned, undefined); + + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), []); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), ['pub.a']); }); test('Test enable autoupdate for extension when auto update is enabled for all', async () => { @@ -1453,10 +1480,33 @@ suite('ExtensionsWorkbenchServiceTest', () => { await testObject.updateAutoUpdateEnablementFor(testObject.local[0], false); await testObject.updateAutoUpdateEnablementFor(testObject.local[0], true); + assert.strictEqual(testObject.local[0].local?.pinned, undefined); + assert.strictEqual(testObject.local[1].local?.pinned, undefined); + + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), []); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), []); + }); + + test('Test enable autoupdate for pinned extension when auto update is enabled', async () => { + const extension1 = aLocalExtension('a', undefined, { pinned: true }); + const extension2 = aLocalExtension('b'); + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2]); + instantiationService.stub(IExtensionManagementService, 'updateMetadata', (local: Mutable, metadata: Partial) => { + local.pinned = !!metadata.pinned; + return local; + }); + testObject = await aWorkbenchService(); + + assert.strictEqual(testObject.local[0].local?.pinned, true); + assert.strictEqual(testObject.local[1].local?.pinned, undefined); + + await testObject.updateAutoUpdateEnablementFor(testObject.local[0], true); + assert.strictEqual(testObject.local[0].local?.pinned, false); assert.strictEqual(testObject.local[1].local?.pinned, undefined); - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), []); + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), []); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), []); }); test('Test updateAutoUpdateEnablementFor throws error when auto update is disabled', async () => { @@ -1489,46 +1539,6 @@ suite('ExtensionsWorkbenchServiceTest', () => { } }); - test('Test updateAutoUpdateEnablementFor throws error for extension id when auto update mode is onlySelectedExtensions', async () => { - stubConfiguration('onlySelectedExtensions'); - - const extension1 = aLocalExtension('a'); - const extension2 = aLocalExtension('b'); - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2]); - testObject = await aWorkbenchService(); - - try { - await testObject.updateAutoUpdateEnablementFor(testObject.local[0].identifier.id, true); - assert.fail('error expected'); - } catch (error) { - // expected - } - }); - - test('Test enable autoupdate for extension when auto update is set to onlySelectedExtensions', async () => { - stubConfiguration('onlySelectedExtensions'); - - const extension1 = aLocalExtension('a', undefined, { pinned: true }); - const extension2 = aLocalExtension('b', undefined, { pinned: true }); - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2]); - instantiationService.stub(IExtensionManagementService, 'updateMetadata', (local: Mutable, metadata: Partial) => { - local.pinned = !!metadata.pinned; - return local; - }); - testObject = await aWorkbenchService(); - - assert.strictEqual(testObject.local[0].local?.pinned, true); - assert.strictEqual(testObject.local[1].local?.pinned, true); - - await testObject.updateAutoUpdateEnablementFor(testObject.local[0], true); - - assert.strictEqual(testObject.local[0].local?.pinned, false); - assert.strictEqual(testObject.local[1].local?.pinned, true); - - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), ['pub.a']); - assert.equal(instantiationService.get(IConfigurationService).getValue(AutoUpdateConfigurationKey), 'onlySelectedExtensions'); - }); - test('Test enable autoupdate for extension when auto update is disabled', async () => { stubConfiguration(false); @@ -1546,15 +1556,17 @@ suite('ExtensionsWorkbenchServiceTest', () => { await testObject.updateAutoUpdateEnablementFor(testObject.local[0], true); - assert.strictEqual(testObject.local[0].local?.pinned, false); + assert.strictEqual(testObject.local[0].local?.pinned, true); assert.strictEqual(testObject.local[1].local?.pinned, true); - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), ['pub.a']); - assert.equal(instantiationService.get(IConfigurationService).getValue(AutoUpdateConfigurationKey), 'onlySelectedExtensions'); + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), ['pub.a']); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), []); }); - test('Test disable autoupdate for extension when auto update is set to onlySelectedExtensions', async () => { - stubConfiguration('onlySelectedExtensions'); + test('Test reset autoupdate extensions state when auto update is disabled', async () => { + instantiationService.stub(IDialogService, { + confirm: () => Promise.resolve({ confirmed: true }) + }); const extension1 = aLocalExtension('a', undefined, { pinned: true }); const extension2 = aLocalExtension('b', undefined, { pinned: true }); @@ -1565,101 +1577,41 @@ suite('ExtensionsWorkbenchServiceTest', () => { }); testObject = await aWorkbenchService(); - await testObject.updateAutoUpdateEnablementFor(testObject.local[0], true); await testObject.updateAutoUpdateEnablementFor(testObject.local[0], false); - assert.strictEqual(testObject.local[0].local?.pinned, true); - assert.strictEqual(testObject.local[1].local?.pinned, true); - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), []); - assert.equal(instantiationService.get(IConfigurationService).getValue(AutoUpdateConfigurationKey), false); - }); + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), []); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), ['pub.a']); - test('Test enable auto update for publisher when auto update mode is onlySelectedExtensions', async () => { - stubConfiguration('onlySelectedExtensions'); + await testObject.updateAutoUpdateValue(false); - const extension1 = aLocalExtension('a', undefined, { pinned: true }); - const extension2 = aLocalExtension('b', undefined, { pinned: true }); - const extension3 = aLocalExtension('a', { publisher: 'pub2' }, { pinned: true }); - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2, extension3]); - instantiationService.stub(IExtensionManagementService, 'updateMetadata', (local: ILocalExtension, metadata: Partial) => { - local.pinned = !!metadata.pinned; - return local; - }); - testObject = await aWorkbenchService(); - - await testObject.updateAutoUpdateEnablementFor(testObject.local[0].publisher, true); - - assert.strictEqual(testObject.local[0].local?.pinned, false); - assert.strictEqual(testObject.local[1].local?.pinned, false); - assert.strictEqual(testObject.local[2].local?.pinned, true); - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), ['pub']); + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), []); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), []); }); - test('Test disable auto update for publisher when auto update mode is onlySelectedExtensions', async () => { - stubConfiguration('onlySelectedExtensions'); - - const extension1 = aLocalExtension('a', undefined, { pinned: true }); - const extension2 = aLocalExtension('b', undefined, { pinned: true }); - const extension3 = aLocalExtension('a', { publisher: 'pub2' }, { pinned: true }); - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2, extension3]); - instantiationService.stub(IExtensionManagementService, 'updateMetadata', (local: ILocalExtension, metadata: Partial) => { - local.pinned = !!metadata.pinned; - return local; + test('Test reset autoupdate extensions state when auto update is enabled', async () => { + stubConfiguration(false); + instantiationService.stub(IDialogService, { + confirm: () => Promise.resolve({ confirmed: true }) }); - testObject = await aWorkbenchService(); - - await testObject.updateAutoUpdateEnablementFor(testObject.local[0].publisher, true); - await testObject.updateAutoUpdateEnablementFor(testObject.local[0].publisher, false); - - assert.strictEqual(testObject.local[0].local?.pinned, true); - assert.strictEqual(testObject.local[1].local?.pinned, true); - assert.strictEqual(testObject.local[2].local?.pinned, true); - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), []); - }); - - test('Test disable auto update for an extension when auto update for publisher is enabled and update mode is onlySelectedExtensions', async () => { - stubConfiguration('onlySelectedExtensions'); const extension1 = aLocalExtension('a', undefined, { pinned: true }); const extension2 = aLocalExtension('b', undefined, { pinned: true }); - const extension3 = aLocalExtension('a', { publisher: 'pub2' }, { pinned: true }); - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2, extension3]); - instantiationService.stub(IExtensionManagementService, 'updateMetadata', (local: ILocalExtension, metadata: Partial) => { + instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2]); + instantiationService.stub(IExtensionManagementService, 'updateMetadata', (local: Mutable, metadata: Partial) => { local.pinned = !!metadata.pinned; return local; }); testObject = await aWorkbenchService(); - await testObject.updateAutoUpdateEnablementFor(testObject.local[0].publisher, true); - await testObject.updateAutoUpdateEnablementFor(testObject.local[0], false); - - assert.strictEqual(testObject.local[0].local?.pinned, true); - assert.strictEqual(testObject.local[1].local?.pinned, false); - assert.strictEqual(testObject.local[2].local?.pinned, true); - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), ['pub', '-pub.a']); - }); + await testObject.updateAutoUpdateEnablementFor(testObject.local[0], true); - test('Test enable auto update for an extension when auto updates is enabled for publisher and disabled for extension and update mode is onlySelectedExtensions', async () => { - stubConfiguration('onlySelectedExtensions'); + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), ['pub.a']); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), []); - const extension1 = aLocalExtension('a', undefined, { pinned: true }); - const extension2 = aLocalExtension('b', undefined, { pinned: true }); - const extension3 = aLocalExtension('a', { publisher: 'pub2' }, { pinned: true }); - instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', [extension1, extension2, extension3]); - instantiationService.stub(IExtensionManagementService, 'updateMetadata', (local: ILocalExtension, metadata: Partial) => { - local.pinned = !!metadata.pinned; - return local; - }); - testObject = await aWorkbenchService(); + await testObject.updateAutoUpdateValue(true); - await testObject.updateAutoUpdateEnablementFor(testObject.local[0].publisher, true); - await testObject.updateAutoUpdateEnablementFor(testObject.local[0], false); - await testObject.updateAutoUpdateEnablementFor(testObject.local[0], true); - - assert.strictEqual(testObject.local[0].local?.pinned, false); - assert.strictEqual(testObject.local[1].local?.pinned, false); - assert.strictEqual(testObject.local[2].local?.pinned, true); - assert.deepStrictEqual(testObject.getSelectedExtensionsToAutoUpdate(), ['pub']); + assert.deepStrictEqual(testObject.getEnabledAutoUpdateExtensions(), []); + assert.deepStrictEqual(testObject.getDisabledAutoUpdateExtensions(), []); }); async function aWorkbenchService(): Promise { @@ -1673,13 +1625,22 @@ suite('ExtensionsWorkbenchServiceTest', () => { [AutoUpdateConfigurationKey]: autoUpdateValue ?? true, [AutoCheckUpdatesConfigurationKey]: autoCheckUpdatesValue ?? true }; + const emitter = disposableStore.add(new Emitter()); instantiationService.stub(IConfigurationService, { - onDidChangeConfiguration: () => { return undefined!; }, + onDidChangeConfiguration: emitter.event, getValue: (key?: any) => { return key ? values[key] : undefined; }, updateValue: async (key: string, value: any) => { values[key] = value; + emitter.fire({ + affectedKeys: new Set([key]), + source: ConfigurationTarget.USER, + change: { keys: [], overrides: [] }, + affectsConfiguration(configuration, overrides) { + return true; + }, + }); } }); } @@ -1752,6 +1713,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { }, getTargetPlatform: async () => getTargetPlatform(platform, arch), async getExtensionsControlManifest() { return { malicious: [], deprecated: {}, search: [] }; }, + async resetPinnedStateForAllUserExtensions(pinned: boolean) { } }; } }); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index 68d30c40d35..b4d570c2015 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -226,6 +226,10 @@ export class ExtensionManagementService extends Disposable implements IWorkbench return Promise.reject(`Invalid location ${extension.location.toString()}`); } + async resetPinnedStateForAllUserExtensions(pinned: boolean): Promise { + await Promise.allSettled(this.servers.map(server => server.extensionManagementService.resetPinnedStateForAllUserExtensions(pinned))); + } + zip(extension: ILocalExtension): Promise { const server = this.getServer(extension); if (server) { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 46cb0e01277..a451ea2afb5 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -2205,6 +2205,7 @@ export class TestWorkbenchExtensionManagementService implements IWorkbenchExtens getInstalledWorkspaceExtensions(): Promise { throw new Error('Method not implemented.'); } installResourceExtension(): Promise { throw new Error('Method not implemented.'); } getExtensions(): Promise { throw new Error('Method not implemented.'); } + resetPinnedStateForAllUserExtensions(pinned: boolean): Promise { throw new Error('Method not implemented.'); } } export class TestUserDataProfileService implements IUserDataProfileService { From a238e1548597785afd08fa11469c95a4ce827146 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 3 Jul 2024 16:11:57 +0200 Subject: [PATCH 0249/2222] Git - fix timing issue with branch picker (#219864) --- extensions/git/src/commands.ts | 38 ++++++++++++++++++---------------- 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index fb53eee2976..62d35d7af92 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2516,9 +2516,26 @@ export class CommandCenter { : l10n.t('Select a branch or tag to checkout'); quickPick.show(); - picks.push(... await createCheckoutItems(repository, opts?.detached)); - quickPick.items = [...commands, ...picks]; + + const setQuickPickItems = () => { + switch (true) { + case quickPick.value === '': + quickPick.items = [...commands, ...picks]; + break; + case commands.length === 0: + quickPick.items = picks; + break; + case picks.length === 0: + quickPick.items = commands; + break; + default: + quickPick.items = [...picks, { label: '', kind: QuickPickItemKind.Separator }, ...commands]; + break; + } + }; + + setQuickPickItems(); quickPick.busy = false; const choice = await new Promise(c => { @@ -2533,22 +2550,7 @@ export class CommandCenter { c(undefined); }))); - disposables.push(quickPick.onDidChangeValue(value => { - switch (true) { - case value === '': - quickPick.items = [...commands, ...picks]; - break; - case commands.length === 0: - quickPick.items = picks; - break; - case picks.length === 0: - quickPick.items = commands; - break; - default: - quickPick.items = [...picks, { label: '', kind: QuickPickItemKind.Separator }, ...commands]; - break; - } - })); + disposables.push(quickPick.onDidChangeValue(() => setQuickPickItems())); }); dispose(disposables); From 91bad301f1be9cafaa0004b2f42d0bce458e7417 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 3 Jul 2024 16:46:35 +0200 Subject: [PATCH 0250/2222] update `test-web` to 0.0.56 (#219866) --- package.json | 2 +- yarn.lock | 232 ++++++++++++++++++++++++++++++++++++--------------- 2 files changed, 165 insertions(+), 69 deletions(-) diff --git a/package.json b/package.json index 8e5b8e35bed..0be301683e5 100644 --- a/package.json +++ b/package.json @@ -136,7 +136,7 @@ "@vscode/telemetry-extractor": "^1.10.2", "@vscode/test-cli": "^0.0.6", "@vscode/test-electron": "^2.3.8", - "@vscode/test-web": "^0.0.50", + "@vscode/test-web": "^0.0.56", "@vscode/v8-heap-parser": "^0.1.0", "@vscode/vscode-perf": "^0.0.14", "ansi-colors": "^3.2.3", diff --git a/yarn.lock b/yarn.lock index 3ed17cd0af6..9f322a5992f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -902,12 +902,12 @@ resolved "https://registry.yarnpkg.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz#a77ea742fab25775145434eb1d2328cf5013ac33" integrity sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg== -"@playwright/browser-chromium@^1.40.1": - version "1.40.1" - resolved "https://registry.yarnpkg.com/@playwright/browser-chromium/-/browser-chromium-1.40.1.tgz#74f3a213f082798c838f2ef1405c5e9cfeeebc99" - integrity sha512-uNmjHYXBTYTfJkf89D6zVUcesCFzZ/yjkPj8FvBJQ6yf3na/j1rcjVNzx0PzOAGcWKioB/rnWRBi7b5ojOdCHA== +"@playwright/browser-chromium@^1.45.0": + version "1.45.1" + resolved "https://registry.yarnpkg.com/@playwright/browser-chromium/-/browser-chromium-1.45.1.tgz#7c1a69dfe9969df6271e8174f9c5dd43dee9a2a3" + integrity sha512-Jcix36xOagtda46bfn0CcdnygrcFT7FfwLhF1Y8JkHZTKPgfiku695+F0+CWabL4sU9nBG8sT5l7dmgkMgAJgw== dependencies: - playwright-core "1.40.1" + playwright-core "1.45.1" "@playwright/test@^1.45.0": version "1.45.0" @@ -1649,24 +1649,25 @@ jszip "^3.10.1" semver "^7.5.2" -"@vscode/test-web@^0.0.50": - version "0.0.50" - resolved "https://registry.yarnpkg.com/@vscode/test-web/-/test-web-0.0.50.tgz#fe6a9f5a50f3d8fd9379593a44173696ea75c20c" - integrity sha512-aFcwTyA3qirjfr6f5oKEV/07MbsbNeJEqElIh/ceNPEnJIyKYQeffbU7Cia9XtTKk1Ue1qMqwhdIO8OnUa9QBg== +"@vscode/test-web@^0.0.56": + version "0.0.56" + resolved "https://registry.yarnpkg.com/@vscode/test-web/-/test-web-0.0.56.tgz#963b6e1bfa6d5b4b600083e328ee8d28890108eb" + integrity sha512-lR688n+D6A9odw+IZ5cU8CYr2YXLB61bGgyZpPVJe/sJy4/DYX5CAxPb7Wj9ZMYL41CTvWq5DeXtfCjlabPcYA== dependencies: "@koa/cors" "^5.0.0" "@koa/router" "^12.0.1" - "@playwright/browser-chromium" "^1.40.1" + "@playwright/browser-chromium" "^1.45.0" + glob "^10.4.2" gunzip-maybe "^1.4.2" - http-proxy-agent "^7.0.0" - https-proxy-agent "^7.0.2" - koa "^2.14.2" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.4" + koa "^2.15.3" koa-morgan "^1.0.1" koa-mount "^4.0.0" koa-static "^5.0.0" minimist "^1.2.8" - playwright "^1.40.1" - tar-fs "^3.0.4" + playwright "^1.45.0" + tar-fs "^3.0.6" vscode-uri "^3.0.8" "@vscode/v8-heap-parser@^0.1.0": @@ -2505,6 +2506,39 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== +bare-events@^2.0.0, bare-events@^2.2.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/bare-events/-/bare-events-2.4.2.tgz#3140cca7a0e11d49b3edc5041ab560659fd8e1f8" + integrity sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q== + +bare-fs@^2.1.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/bare-fs/-/bare-fs-2.3.1.tgz#cdbd63dac7a552dfb2b87d18c822298d1efd213d" + integrity sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA== + dependencies: + bare-events "^2.0.0" + bare-path "^2.0.0" + bare-stream "^2.0.0" + +bare-os@^2.1.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/bare-os/-/bare-os-2.4.0.tgz#5de5e3ba7704f459c9656629edca7cc736e06608" + integrity sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg== + +bare-path@^2.0.0, bare-path@^2.1.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/bare-path/-/bare-path-2.1.3.tgz#594104c829ef660e43b5589ec8daef7df6cedb3e" + integrity sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA== + dependencies: + bare-os "^2.1.0" + +bare-stream@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/bare-stream/-/bare-stream-2.1.3.tgz#070b69919963a437cc9e20554ede079ce0a129b2" + integrity sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ== + dependencies: + streamx "^2.18.0" + base64-js@^1.3.1, base64-js@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" @@ -3265,10 +3299,10 @@ cookie@^0.5.0: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.5.0.tgz#d1f5d71adec6558c58f389987c366aa47e994f8b" integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw== -cookies@~0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" - integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== +cookies@~0.9.0: + version "0.9.1" + resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.9.1.tgz#3ffed6f60bb4fb5f146feeedba50acc418af67e3" + integrity sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw== dependencies: depd "~2.0.0" keygrip "~1.1.0" @@ -4459,7 +4493,7 @@ fast-fifo@^1.1.0: resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.2.0.tgz#2ee038da2468e8623066dee96958b0c1763aa55a" integrity sha512-NcvQXt7Cky1cNau15FWy64IjuO8X0JijhTBBrJj1YlxlDfRkJXNaK9RFUjwpfDPzMdv7wB38jr53l9tkNLxnWg== -fast-fifo@^1.2.0: +fast-fifo@^1.2.0, fast-fifo@^1.3.2: version "1.3.2" resolved "https://registry.yarnpkg.com/fast-fifo/-/fast-fifo-1.3.2.tgz#286e31de96eb96d38a97899815740ba2a4f3640c" integrity sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ== @@ -4977,6 +5011,18 @@ glob@^10.0.0, glob@^10.3.10: minipass "^5.0.0 || ^6.0.2 || ^7.0.0" path-scurry "^1.10.1" +glob@^10.4.2: + version "10.4.2" + resolved "https://registry.yarnpkg.com/glob/-/glob-10.4.2.tgz#bed6b95dade5c1f80b4434daced233aee76160e5" + integrity sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w== + dependencies: + foreground-child "^3.1.0" + jackspeak "^3.1.2" + minimatch "^9.0.4" + minipass "^7.1.2" + package-json-from-dist "^1.0.0" + path-scurry "^1.11.1" + glob@^5.0.13: version "5.0.15" resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" @@ -5554,6 +5600,14 @@ http-proxy-agent@^7.0.0: agent-base "^7.1.0" debug "^4.3.4" +http-proxy-agent@^7.0.2: + version "7.0.2" + resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz#9a8b1f246866c028509486585f62b8f2c18c270e" + integrity sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig== + dependencies: + agent-base "^7.1.0" + debug "^4.3.4" + http2-wrapper@^1.0.0-beta.5.2: version "1.0.3" resolved "https://registry.yarnpkg.com/http2-wrapper/-/http2-wrapper-1.0.3.tgz#b8f55e0c1f25d4ebd08b3b0c2c079f9590800b3d" @@ -5586,6 +5640,14 @@ https-proxy-agent@^7.0.2: agent-base "^7.0.2" debug "4" +https-proxy-agent@^7.0.4: + version "7.0.5" + resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz#9e8b5013873299e11fab6fd548405da2d6c602b2" + integrity sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw== + dependencies: + agent-base "^7.0.2" + debug "4" + husky@^0.13.1: version "0.13.4" resolved "https://registry.yarnpkg.com/husky/-/husky-0.13.4.tgz#48785c5028de3452a51c48c12c4f94b2124a1407" @@ -6175,6 +6237,15 @@ jackspeak@^2.3.5: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jackspeak@^3.1.2: + version "3.4.0" + resolved "https://registry.yarnpkg.com/jackspeak/-/jackspeak-3.4.0.tgz#a75763ff36ad778ede6a156d8ee8b124de445b4a" + integrity sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw== + dependencies: + "@isaacs/cliui" "^8.0.2" + optionalDependencies: + "@pkgjs/parseargs" "^0.11.0" + jest-worker@^27.0.2: version "27.0.6" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.0.6.tgz#a5fdb1e14ad34eb228cfe162d9f729cdbfa28aed" @@ -6432,16 +6503,16 @@ koa-static@^5.0.0: debug "^3.1.0" koa-send "^5.0.0" -koa@^2.14.2: - version "2.14.2" - resolved "https://registry.yarnpkg.com/koa/-/koa-2.14.2.tgz#a57f925c03931c2b4d94b19d2ebf76d3244863fc" - integrity sha512-VFI2bpJaodz6P7x2uyLiX6RLYpZmOJqNmoCst/Yyd7hQlszyPwG/I9CQJ63nOtKSxpt5M7NH67V6nJL2BwCl7g== +koa@^2.15.3: + version "2.15.3" + resolved "https://registry.yarnpkg.com/koa/-/koa-2.15.3.tgz#062809266ee75ce0c75f6510a005b0e38f8c519a" + integrity sha512-j/8tY9j5t+GVMLeioLaxweJiKUayFhlGqNTzf2ZGwL0ZCQijd2RLHK0SLW5Tsko8YyyqCZC2cojIb0/s62qTAg== dependencies: accepts "^1.3.5" cache-content-type "^1.0.0" content-disposition "~0.5.2" content-type "^1.0.4" - cookies "~0.8.0" + cookies "~0.9.0" debug "^4.3.2" delegates "^1.0.0" depd "^2.0.0" @@ -6666,6 +6737,11 @@ lowercase-keys@^2.0.0: resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" integrity sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA== +lru-cache@^10.2.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.3.0.tgz#4a4aaf10c84658ab70f79a85a9a3f1e1fb11196b" + integrity sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ== + lru-cache@^4.1.3: version "4.1.5" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" @@ -7006,6 +7082,13 @@ minimatch@^7.4.3: dependencies: brace-expansion "^2.0.1" +minimatch@^9.0.4: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: version "1.2.7" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.7.tgz#daa1c4d91f507390437c6a8bc01078e7000c4d18" @@ -7033,6 +7116,11 @@ minipass@^5.0.0: resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.0.4.tgz#dbce03740f50a4786ba994c1fb908844d27b038c" integrity sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ== +minipass@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/minipass/-/minipass-7.1.2.tgz#93a9626ce5e5e66bd4db86849e7515e92340a707" + integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw== + minizlib@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931" @@ -7684,6 +7772,11 @@ p-try@^2.0.0: resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== +package-json-from-dist@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz#e501cd3094b278495eb4258d4c9f6d5ac3019f00" + integrity sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw== + pako@~0.2.0: version "0.2.9" resolved "https://registry.yarnpkg.com/pako/-/pako-0.2.9.tgz#f3f7522f4ef782348da8161bad9ecfd51bf83a75" @@ -7817,6 +7910,14 @@ path-scurry@^1.10.1: lru-cache "^9.1.1 || ^10.0.0" minipass "^5.0.0 || ^6.0.2 || ^7.0.0" +path-scurry@^1.11.1: + version "1.11.1" + resolved "https://registry.yarnpkg.com/path-scurry/-/path-scurry-1.11.1.tgz#7960a668888594a0720b12a911d1a742ab9f11d2" + integrity sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA== + dependencies: + lru-cache "^10.2.0" + minipass "^5.0.0 || ^6.0.2 || ^7.0.0" + path-to-regexp@^1.7.0: version "1.8.0" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-1.8.0.tgz#887b3ba9d84393e87a0a0b9f4cb756198b53548a" @@ -7935,16 +8036,16 @@ playwright-core@1.30.0: resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.30.0.tgz#de987cea2e86669e3b85732d230c277771873285" integrity sha512-7AnRmTCf+GVYhHbLJsGUtskWTE33SwMZkybJ0v6rqR1boxq2x36U7p1vDRV7HO2IwTZgmycracLxPEJI49wu4g== -playwright-core@1.40.1: - version "1.40.1" - resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.40.1.tgz#442d15e86866a87d90d07af528e0afabe4c75c05" - integrity sha512-+hkOycxPiV534c4HhpfX6yrlawqVUzITRKwHAmYfmsVreltEl6fAZJ3DPfLMOODw0H3s1Itd6MDCWmP1fl/QvQ== - playwright-core@1.45.0: version "1.45.0" resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.45.0.tgz#5741a670b7c9060ce06852c0051d84736fb94edc" integrity sha512-lZmHlFQ0VYSpAs43dRq1/nJ9G/6SiTI7VPqidld9TDefL9tX87bTKExWZZUF5PeRyqtXqd8fQi2qmfIedkwsNQ== +playwright-core@1.45.1: + version "1.45.1" + resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.45.1.tgz#549a2701556b58245cc75263f9fc2795c1158dc1" + integrity sha512-LF4CUUtrUu2TCpDw4mcrAIuYrEjVDfT1cHbJMfwnE2+1b8PZcFzPNgvZCvq2JfQ4aTjRCCHw5EJ2tmr2NSzdPg== + playwright@1.45.0: version "1.45.0" resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.45.0.tgz#400c709c64438690f13705cb9c88ef93089c5c27" @@ -7961,12 +8062,12 @@ playwright@^1.29.2: dependencies: playwright-core "1.30.0" -playwright@^1.40.1: - version "1.40.1" - resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.40.1.tgz#a11bf8dca15be5a194851dbbf3df235b9f53d7ae" - integrity sha512-2eHI7IioIpQ0bS1Ovg/HszsN/XKNwEG1kbzSDDmADpclKc7CyqkHw7Mg2JCz/bbCxg25QUPcjksoMW7JcIFQmw== +playwright@^1.45.0: + version "1.45.1" + resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.45.1.tgz#aaa6b0d6db14796b599d80c6679e63444e942534" + integrity sha512-Hjrgae4kpSQBr98nhCj3IScxVeVUixqj+5oyif8TdIn2opTCPEzqAqNMeK42i3cWDCVu9MI+ZsGWw+gVR4ISBg== dependencies: - playwright-core "1.40.1" + playwright-core "1.45.1" optionalDependencies: fsevents "2.3.2" @@ -9355,7 +9456,18 @@ streamx@^2.15.0: fast-fifo "^1.1.0" queue-tick "^1.0.1" -"string-width-cjs@npm:string-width@^4.2.0": +streamx@^2.18.0: + version "2.18.0" + resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.18.0.tgz#5bc1a51eb412a667ebfdcd4e6cf6a6fc65721ac7" + integrity sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ== + dependencies: + fast-fifo "^1.3.2" + queue-tick "^1.0.1" + text-decoder "^1.1.0" + optionalDependencies: + bare-events "^2.2.0" + +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9390,15 +9502,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -9452,7 +9555,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9480,13 +9583,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -9648,14 +9744,16 @@ tar-fs@^2.0.0: pump "^3.0.0" tar-stream "^2.1.4" -tar-fs@^3.0.4: - version "3.0.4" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.4.tgz#a21dc60a2d5d9f55e0089ccd78124f1d3771dbbf" - integrity sha512-5AFQU8b9qLfZCX9zp2duONhPmZv0hGYiBPJsyUdqMjzq/mqVpy/rEUSeHk1+YitmxugaptgBh5oDGU3VsAJq4w== +tar-fs@^3.0.6: + version "3.0.6" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-3.0.6.tgz#eaccd3a67d5672f09ca8e8f9c3d2b89fa173f217" + integrity sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w== dependencies: - mkdirp-classic "^0.5.2" pump "^3.0.0" tar-stream "^3.1.5" + optionalDependencies: + bare-fs "^2.1.1" + bare-path "^2.1.0" tar-stream@^2.1.4: version "2.2.0" @@ -9768,6 +9866,13 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" +text-decoder@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/text-decoder/-/text-decoder-1.1.0.tgz#3379e728fcf4d3893ec1aea35e8c2cac215ef190" + integrity sha512-TmLJNj6UgX8xcUZo4UDStGQtDiTzF7BzWlzn9g7UWrjkpHr5uJTK1ld16wZ3LXb2vb6jH8qU89dW5whuMdXYdw== + dependencies: + b4a "^1.6.4" + text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" @@ -10642,7 +10747,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10677,15 +10782,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 336250bb6e7ef3668d5bdfe84798ea0db908c71f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 3 Jul 2024 17:38:52 +0200 Subject: [PATCH 0251/2222] fix #210363 (#219869) --- .../common/configurationRegistry.ts | 33 ++++++++++++++ .../test/common/extensionNls.test.ts | 9 ++-- .../platform/extensions/common/extensions.ts | 15 +------ .../userDataSync/common/settingsSync.ts | 34 +++++++++++--- .../userDataSync/common/userDataSync.ts | 41 ++++++++++++++--- .../test/common/userDataSyncClient.ts | 2 +- .../api/common/configurationExtensionPoint.ts | 44 +++++-------------- .../userDataSync/common/userDataSyncUtil.ts | 4 +- 8 files changed, 115 insertions(+), 67 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 322e807f0a7..98ab6dc12d8 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -888,3 +888,36 @@ export function getScopes(): [string, ConfigurationScope | undefined][] { scopes.push(['task', ConfigurationScope.RESOURCE]); return scopes; } + +export function getAllConfigurationProperties(configurationNode: IConfigurationNode[]): IStringDictionary { + const result: IStringDictionary = {}; + for (const configuration of configurationNode) { + const properties = configuration.properties; + if (types.isObject(properties)) { + for (const key in properties) { + result[key] = properties[key]; + } + } + if (configuration.allOf) { + Object.assign(result, getAllConfigurationProperties(configuration.allOf)); + } + } + return result; +} + +export function parseScope(scope: string): ConfigurationScope { + switch (scope) { + case 'application': + return ConfigurationScope.APPLICATION; + case 'machine': + return ConfigurationScope.MACHINE; + case 'resource': + return ConfigurationScope.RESOURCE; + case 'machine-overridable': + return ConfigurationScope.MACHINE_OVERRIDABLE; + case 'language-overridable': + return ConfigurationScope.LANGUAGE_OVERRIDABLE; + default: + return ConfigurationScope.WINDOW; + } +} diff --git a/src/vs/platform/extensionManagement/test/common/extensionNls.test.ts b/src/vs/platform/extensionManagement/test/common/extensionNls.test.ts index e23d69b9371..64aad4fbd41 100644 --- a/src/vs/platform/extensionManagement/test/common/extensionNls.test.ts +++ b/src/vs/platform/extensionManagement/test/common/extensionNls.test.ts @@ -7,8 +7,9 @@ import assert from 'assert'; import { deepClone } from 'vs/base/common/objects'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { ILocalizedString } from 'vs/platform/action/common/action'; +import { IConfigurationNode } from 'vs/platform/configuration/common/configurationRegistry'; import { localizeManifest } from 'vs/platform/extensionManagement/common/extensionNls'; -import { IExtensionManifest, IConfiguration } from 'vs/platform/extensions/common/extensions'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { NullLogger } from 'vs/platform/log/common/log'; const manifest: IExtensionManifest = { @@ -62,7 +63,7 @@ suite('Localize Manifest', () => { assert.strictEqual(localizedManifest.contributes?.commands?.[0].title, 'Test Command'); assert.strictEqual(localizedManifest.contributes?.commands?.[0].category, 'Test Category'); assert.strictEqual(localizedManifest.contributes?.authentication?.[0].label, 'Test Authentication'); - assert.strictEqual((localizedManifest.contributes?.configuration as IConfiguration).title, 'Test Configuration'); + assert.strictEqual((localizedManifest.contributes?.configuration as IConfigurationNode).title, 'Test Configuration'); }); test('replaces template strings with fallback if not found in translations', function () { @@ -81,7 +82,7 @@ suite('Localize Manifest', () => { assert.strictEqual(localizedManifest.contributes?.commands?.[0].title, 'Test Command'); assert.strictEqual(localizedManifest.contributes?.commands?.[0].category, 'Test Category'); assert.strictEqual(localizedManifest.contributes?.authentication?.[0].label, 'Test Authentication'); - assert.strictEqual((localizedManifest.contributes?.configuration as IConfiguration).title, 'Test Configuration'); + assert.strictEqual((localizedManifest.contributes?.configuration as IConfigurationNode).title, 'Test Configuration'); }); test('replaces template strings - command title & categories become ILocalizedString', function () { @@ -111,7 +112,7 @@ suite('Localize Manifest', () => { // Everything else stays as a string. assert.strictEqual(localizedManifest.contributes?.authentication?.[0].label, 'Testauthentifizierung'); - assert.strictEqual((localizedManifest.contributes?.configuration as IConfiguration).title, 'Testkonfiguration'); + assert.strictEqual((localizedManifest.contributes?.configuration as IConfigurationNode).title, 'Testkonfiguration'); }); test('replaces template strings - is best effort #164630', function () { diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index 4dd080da3ae..d0b69021dc2 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -21,19 +21,6 @@ export interface ICommand { category?: string | ILocalizedString; } -export interface IConfigurationProperty { - description: string; - type: string | string[]; - default?: any; -} - -export interface IConfiguration { - id?: string; - order?: number; - title?: string; - properties: { [key: string]: IConfigurationProperty }; -} - export interface IDebugger { label?: string; type: string; @@ -182,7 +169,7 @@ export interface ILocalizationContribution { export interface IExtensionContributions { commands?: ICommand[]; - configuration?: IConfiguration | IConfiguration[]; + configuration?: any; debuggers?: IDebugger[]; grammars?: IGrammar[]; jsonValidation?: IJSONValidation[]; diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index b1c7523e8ef..c9499d33e9e 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { distinct } from 'vs/base/common/arrays'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; @@ -12,6 +13,7 @@ import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configur import { ConfigurationModelParser } from 'vs/platform/configuration/common/configurationModels'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { FileOperationError, FileOperationResult, IFileService } from 'vs/platform/files/common/files'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -19,7 +21,7 @@ import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity' import { IUserDataProfile, IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; import { AbstractInitializer, AbstractJsonFileSynchroniser, IAcceptResult, IFileResourcePreview, IMergeResult } from 'vs/platform/userDataSync/common/abstractSynchronizer'; import { getIgnoredSettings, isEmpty, merge, updateIgnoredSettings } from 'vs/platform/userDataSync/common/settingsMerge'; -import { Change, IRemoteUserData, IUserDataSyncLocalStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, IUserDataSyncUtilService, SyncResource, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_CONFIGURATION_SCOPE, USER_DATA_SYNC_SCHEME, IUserDataResourceManifest } from 'vs/platform/userDataSync/common/userDataSync'; +import { Change, IRemoteUserData, IUserDataSyncLocalStoreService, IUserDataSyncConfiguration, IUserDataSynchroniser, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncStoreService, IUserDataSyncUtilService, SyncResource, UserDataSyncError, UserDataSyncErrorCode, USER_DATA_SYNC_CONFIGURATION_SCOPE, USER_DATA_SYNC_SCHEME, IUserDataResourceManifest, getIgnoredSettingsForExtension } from 'vs/platform/userDataSync/common/userDataSync'; interface ISettingsResourcePreview extends IFileResourcePreview { previewResult: IMergeResult; @@ -51,7 +53,7 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement readonly acceptedResource: URI = this.previewResource.with({ scheme: USER_DATA_SYNC_SCHEME, authority: 'accepted' }); constructor( - profile: IUserDataProfile, + private readonly profile: IUserDataProfile, collection: string | undefined, @IFileService fileService: IFileService, @IEnvironmentService environmentService: IEnvironmentService, @@ -315,21 +317,39 @@ export class SettingsSynchroniser extends AbstractJsonFileSynchroniser implement return { settings }; } - private _defaultIgnoredSettings: Promise | undefined = undefined; + private coreIgnoredSettings: Promise | undefined = undefined; + private systemExtensionsIgnoredSettings: Promise | undefined = undefined; + private userExtensionsIgnoredSettings: Promise | undefined = undefined; private async getIgnoredSettings(content?: string): Promise { - if (!this._defaultIgnoredSettings) { - this._defaultIgnoredSettings = this.userDataSyncUtilService.resolveDefaultIgnoredSettings(); + if (!this.coreIgnoredSettings) { + this.coreIgnoredSettings = this.userDataSyncUtilService.resolveDefaultCoreIgnoredSettings(); + } + if (!this.systemExtensionsIgnoredSettings) { + this.systemExtensionsIgnoredSettings = this.getIgnoredSettingForSystemExtensions(); + } + if (!this.userExtensionsIgnoredSettings) { + this.userExtensionsIgnoredSettings = this.getIgnoredSettingForUserExtensions(); const disposable = this._register(Event.any( Event.filter(this.extensionManagementService.onDidInstallExtensions, (e => e.some(({ local }) => !!local))), Event.filter(this.extensionManagementService.onDidUninstallExtension, (e => !e.error)))(() => { disposable.dispose(); - this._defaultIgnoredSettings = undefined; + this.userExtensionsIgnoredSettings = undefined; })); } - const defaultIgnoredSettings = await this._defaultIgnoredSettings; + const defaultIgnoredSettings = (await Promise.all([this.coreIgnoredSettings, this.systemExtensionsIgnoredSettings, this.userExtensionsIgnoredSettings])).flat(); return getIgnoredSettings(defaultIgnoredSettings, this.configurationService, content); } + private async getIgnoredSettingForSystemExtensions(): Promise { + const systemExtensions = await this.extensionManagementService.getInstalled(ExtensionType.System); + return distinct(systemExtensions.map(e => getIgnoredSettingsForExtension(e.manifest)).flat()); + } + + private async getIgnoredSettingForUserExtensions(): Promise { + const userExtensions = await this.extensionManagementService.getInstalled(ExtensionType.User, this.profile.extensionsResource); + return distinct(userExtensions.map(e => getIgnoredSettingsForExtension(e.manifest)).flat()); + } + private validateContent(content: string): void { if (this.hasErrors(content, false)) { throw new UserDataSyncError(localize('errorInvalidSettings', "Unable to sync settings as there are errors/warning in settings file."), UserDataSyncErrorCode.LocalInvalidContent, this.resource); diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 084755420cb..ed81c9196e8 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -15,9 +15,10 @@ import { isObject, isString } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IHeaders } from 'vs/base/parts/request/common/request'; import { localize } from 'vs/nls'; -import { allSettings, ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { allSettings, ConfigurationScope, Extensions as ConfigurationExtensions, IConfigurationRegistry, IRegisteredConfigurationPropertySchema, getAllConfigurationProperties, parseScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { EXTENSION_IDENTIFIER_PATTERN, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Extensions as JSONExtensions, IJSONContributionRegistry } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { ILogService } from 'vs/platform/log/common/log'; @@ -30,12 +31,40 @@ export function getDisallowedIgnoredSettings(): string[] { return Object.keys(allSettings).filter(setting => !!allSettings[setting].disallowSyncIgnore); } -export function getDefaultIgnoredSettings(): string[] { +export function getDefaultIgnoredSettings(excludeExtensions: boolean = false): string[] { const allSettings = Registry.as(ConfigurationExtensions.Configuration).getConfigurationProperties(); - const ignoreSyncSettings = Object.keys(allSettings).filter(setting => !!allSettings[setting].ignoreSync); - const machineSettings = Object.keys(allSettings).filter(setting => allSettings[setting].scope === ConfigurationScope.MACHINE || allSettings[setting].scope === ConfigurationScope.MACHINE_OVERRIDABLE); + const ignoredSettings = getIgnoredSettings(allSettings, excludeExtensions); const disallowedSettings = getDisallowedIgnoredSettings(); - return distinct([...ignoreSyncSettings, ...machineSettings, ...disallowedSettings]); + return distinct([...ignoredSettings, ...disallowedSettings]); +} + +export function getIgnoredSettingsForExtension(manifest: IExtensionManifest): string[] { + if (!manifest.contributes?.configuration) { + return []; + } + const configurations = Array.isArray(manifest.contributes.configuration) ? manifest.contributes.configuration : [manifest.contributes.configuration]; + if (!configurations.length) { + return []; + } + const properties = getAllConfigurationProperties(configurations); + return getIgnoredSettings(properties, false); +} + +function getIgnoredSettings(properties: IStringDictionary, excludeExtensions: boolean): string[] { + const ignoredSettings = new Set(); + for (const key in properties) { + if (excludeExtensions && !!properties[key].source) { + continue; + } + const scope = isString(properties[key].scope) ? parseScope(properties[key].scope) : properties[key].scope; + if (properties[key].ignoreSync + || scope === ConfigurationScope.MACHINE + || scope === ConfigurationScope.MACHINE_OVERRIDABLE + ) { + ignoredSettings.add(key); + } + } + return [...ignoredSettings.values()]; } export const USER_DATA_SYNC_CONFIGURATION_SCOPE = 'settingsSync'; @@ -591,7 +620,7 @@ export interface IUserDataSyncUtilService { readonly _serviceBrand: undefined; resolveUserBindings(userbindings: string[]): Promise>; resolveFormattingOptions(resource: URI): Promise; - resolveDefaultIgnoredSettings(): Promise; + resolveDefaultCoreIgnoredSettings(): Promise; } export const IUserDataSyncLogService = createDecorator('IUserDataSyncLogService'); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 3a035f63221..ea8260323a9 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -355,7 +355,7 @@ export class TestUserDataSyncUtilService implements IUserDataSyncUtilService { _serviceBrand: any; - async resolveDefaultIgnoredSettings(): Promise { + async resolveDefaultCoreIgnoredSettings(): Promise { return getDefaultIgnoredSettings(); } diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index 496651fe769..b5a56b231e8 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -8,7 +8,7 @@ import * as objects from 'vs/base/common/objects'; import { Registry } from 'vs/platform/registry/common/platform'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { ExtensionsRegistry, IExtensionPointUser } from 'vs/workbench/services/extensions/common/extensionsRegistry'; -import { IConfigurationNode, IConfigurationRegistry, Extensions, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_REGEX, IConfigurationDefaults, configurationDefaultsSchemaId, IConfigurationDelta, getDefaultValue } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationNode, IConfigurationRegistry, Extensions, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_REGEX, IConfigurationDefaults, configurationDefaultsSchemaId, IConfigurationDelta, getDefaultValue, getAllConfigurationProperties, parseScope } from 'vs/platform/configuration/common/configurationRegistry'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/platform/jsonschemas/common/jsonContributionRegistry'; import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId } from 'vs/workbench/services/configuration/common/configuration'; import { isObject, isUndefined } from 'vs/base/common/types'; @@ -210,8 +210,7 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => { const seenProperties = new Set(); - function handleConfiguration(node: IConfigurationNode, extension: IExtensionPointUser): IConfigurationNode[] { - const configurations: IConfigurationNode[] = []; + function handleConfiguration(node: IConfigurationNode, extension: IExtensionPointUser): IConfigurationNode { const configuration = objects.deepClone(node); if (configuration.title && (typeof configuration.title !== 'string')) { @@ -224,8 +223,7 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => { configuration.extensionInfo = { id: extension.description.identifier.value, displayName: extension.description.displayName }; configuration.restrictedProperties = extension.description.capabilities?.untrustedWorkspaces?.supported === 'limited' ? extension.description.capabilities?.untrustedWorkspaces.restrictedConfigurations : undefined; configuration.title = configuration.title || extension.description.displayName || extension.description.identifier.value; - configurations.push(configuration); - return configurations; + return configuration; } function validateProperties(configuration: IConfigurationNode, extension: IExtensionPointUser): void { @@ -254,23 +252,7 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => { continue; } seenProperties.add(key); - if (propertyConfiguration.scope) { - if (propertyConfiguration.scope.toString() === 'application') { - propertyConfiguration.scope = ConfigurationScope.APPLICATION; - } else if (propertyConfiguration.scope.toString() === 'machine') { - propertyConfiguration.scope = ConfigurationScope.MACHINE; - } else if (propertyConfiguration.scope.toString() === 'resource') { - propertyConfiguration.scope = ConfigurationScope.RESOURCE; - } else if (propertyConfiguration.scope.toString() === 'machine-overridable') { - propertyConfiguration.scope = ConfigurationScope.MACHINE_OVERRIDABLE; - } else if (propertyConfiguration.scope.toString() === 'language-overridable') { - propertyConfiguration.scope = ConfigurationScope.LANGUAGE_OVERRIDABLE; - } else { - propertyConfiguration.scope = ConfigurationScope.WINDOW; - } - } else { - propertyConfiguration.scope = ConfigurationScope.WINDOW; - } + propertyConfiguration.scope = propertyConfiguration.scope ? parseScope(propertyConfiguration.scope.toString()) : ConfigurationScope.WINDOW; } } const subNodes = configuration.allOf; @@ -288,9 +270,9 @@ configurationExtPoint.setHandler((extensions, { added, removed }) => { const configurations: IConfigurationNode[] = []; const value = extension.value; if (Array.isArray(value)) { - value.forEach(v => configurations.push(...handleConfiguration(v, extension))); + value.forEach(v => configurations.push(handleConfiguration(v, extension))); } else { - configurations.push(...handleConfiguration(value, extension)); + configurations.push(handleConfiguration(value, extension)); } extensionConfigurations.set(extension.description.identifier, configurations); addedConfigurations.push(...configurations); @@ -400,15 +382,11 @@ class SettingsTableRenderer extends Disposable implements IExtensionFeatureTable } render(manifest: IExtensionManifest): IRenderedData { - const configuration = manifest.contributes?.configuration; - let properties: any = {}; - if (Array.isArray(configuration)) { - configuration.forEach(config => { - properties = { ...properties, ...config.properties }; - }); - } else if (configuration) { - properties = configuration.properties; - } + const configuration: IConfigurationNode[] = manifest.contributes?.configuration + ? Array.isArray(manifest.contributes.configuration) ? manifest.contributes.configuration : [manifest.contributes.configuration] + : []; + + const properties = getAllConfigurationProperties(configuration); const contrib = properties ? Object.keys(properties) : []; const headers = [nls.localize('setting name', "ID"), nls.localize('description', "Description"), nls.localize('default', "Default")]; diff --git a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts index 45329df40c4..cd7f253b913 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSyncUtil.ts @@ -23,8 +23,8 @@ class UserDataSyncUtilService implements IUserDataSyncUtilService { @ITextResourceConfigurationService private readonly textResourceConfigurationService: ITextResourceConfigurationService, ) { } - async resolveDefaultIgnoredSettings(): Promise { - return getDefaultIgnoredSettings(); + async resolveDefaultCoreIgnoredSettings(): Promise { + return getDefaultIgnoredSettings(true); } async resolveUserBindings(userBindings: string[]): Promise> { From eb8142459e95dc541c5b1eead438d4b60100ca52 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 3 Jul 2024 17:39:59 +0200 Subject: [PATCH 0252/2222] feedback (#219868) --- .../contrib/extensions/browser/extensionsWorkbenchService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index e4b62d95f4a..9c34630f65b 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -1077,7 +1077,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension message: isEnabled ? nls.localize('confirmEnableAutoUpdate', "Do you want to enable auto update for all extensions?") : nls.localize('confirmDisableAutoUpdate', "Do you want to disable auto update for all extensions?"), - detail: nls.localize('confirmEnableDisableAutoUpdateDetail', "This will reset the auto update settings you have set for individual extensions."), + detail: nls.localize('confirmEnableDisableAutoUpdateDetail', "This will reset any auto update settings you have set for individual extensions."), }); if (!result.confirmed) { return; From 7a580b79e50d100baf1ae82516d9e00cf9c62dda Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 3 Jul 2024 09:08:38 -0700 Subject: [PATCH 0253/2222] Pick up latest TS for building VS Code (#219870) --- package.json | 2 +- yarn.lock | 39 ++++++++++++++++++++++++++++++++------- 2 files changed, 33 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 0be301683e5..92e269b5a3f 100644 --- a/package.json +++ b/package.json @@ -207,7 +207,7 @@ "ts-loader": "^9.4.2", "ts-node": "^10.9.1", "tsec": "0.2.7", - "typescript": "^5.6.0-dev.20240618", + "typescript": "^5.6.0-dev.20240703", "util": "^0.12.4", "vscode-nls-dev": "^3.3.1", "webpack": "^5.91.0", diff --git a/yarn.lock b/yarn.lock index 9f322a5992f..466eaf63f25 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9467,7 +9467,7 @@ streamx@^2.18.0: optionalDependencies: bare-events "^2.2.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9502,6 +9502,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -9555,7 +9564,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9583,6 +9592,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -10194,10 +10210,10 @@ typescript@^4.7.4: resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.8.4.tgz#c464abca159669597be5f96b8943500b238e60e6" integrity sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ== -typescript@^5.6.0-dev.20240618: - version "5.6.0-dev.20240618" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.0-dev.20240618.tgz#5ce1d67e5c9e36585349916a85a3f3d8cc806168" - integrity sha512-nUnATyFjcoenJB7S5oPGea2s0dd8MVl+2NisBLm7E+zpXkX0KSLy8Y7aFNSQ+r1Hs/MrKfSlV4O8yiQpCuOqrQ== +typescript@^5.6.0-dev.20240703: + version "5.6.0-dev.20240703" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.6.0-dev.20240703.tgz#4b1fad9790ff4827a9210c6943b3039b53a739da" + integrity sha512-AAOGWtykhMpxB4l+5CwojT2aBVAszalz9guzYaZMavmKHWxm3HciR+cIcKqDgR22hR7fPBJHtOti7uFCo4mt4A== typical@^4.0.0: version "4.0.0" @@ -10747,7 +10763,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10782,6 +10798,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From e6a828c5957959b7a18de37a567d850880101641 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Wed, 3 Jul 2024 20:54:43 +0200 Subject: [PATCH 0254/2222] Fix #219705 (#219881) --- .../browser/userDataProfilesEditorModel.ts | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts index a2e56bc5c7b..237d9142b36 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditorModel.ts @@ -394,6 +394,9 @@ export class NewProfileElement extends AbstractUserDataProfileElement { private templatePromise: CancelablePromise | undefined; private template: IUserDataProfileTemplate | null = null; + private defaultName: string; + private defaultIcon: string | undefined; + constructor( name: string, copyFrom: URI | IUserDataProfile | undefined, @@ -417,6 +420,7 @@ export class NewProfileElement extends AbstractUserDataProfileElement { commandService, instantiationService, ); + this.defaultName = name; this._copyFrom = copyFrom; this._copyFlags = this.getCopyFlagsFrom(copyFrom); this.initialize(); @@ -473,8 +477,12 @@ export class NewProfileElement extends AbstractUserDataProfileElement { if (this.copyFrom instanceof URI) { await this.resolveTemplate(this.copyFrom); if (this.template) { - this.name = this.template.name ?? ''; - this.icon = this.template.icon; + if (this.defaultName === this.name) { + this.name = this.defaultName = this.template.name ?? ''; + } + if (this.defaultIcon === this.icon) { + this.icon = this.defaultIcon = this.template.icon; + } this.setCopyFlag(ProfileResourceType.Settings, !!this.template.settings); this.setCopyFlag(ProfileResourceType.Keybindings, !!this.template.keybindings); this.setCopyFlag(ProfileResourceType.Tasks, !!this.template.tasks); @@ -485,8 +493,12 @@ export class NewProfileElement extends AbstractUserDataProfileElement { } if (isUserDataProfile(this.copyFrom)) { - this.name = `${this.copyFrom.name} (Copy)`; - this.icon = this.copyFrom.icon; + if (this.defaultName === this.name) { + this.name = this.defaultName = localize('copy from', "{0} (Copy)", this.copyFrom.name); + } + if (this.defaultIcon === this.icon) { + this.icon = this.defaultIcon = this.copyFrom.icon; + } this.setCopyFlag(ProfileResourceType.Settings, true); this.setCopyFlag(ProfileResourceType.Keybindings, true); this.setCopyFlag(ProfileResourceType.Tasks, true); @@ -495,8 +507,12 @@ export class NewProfileElement extends AbstractUserDataProfileElement { return; } - this.name = localize('untitled', "Untitled"); - this.icon = undefined; + if (this.defaultName === this.name) { + this.name = this.defaultName = localize('untitled', "Untitled"); + } + if (this.defaultIcon === this.icon) { + this.icon = this.defaultIcon = undefined; + } this.setCopyFlag(ProfileResourceType.Settings, false); this.setCopyFlag(ProfileResourceType.Keybindings, false); this.setCopyFlag(ProfileResourceType.Tasks, false); From a101882bb6134803c7062e07fe57b463e11399f6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 3 Jul 2024 21:08:55 +0200 Subject: [PATCH 0255/2222] select a language option is not working (#219876) (#219879) --- .../browser/emptyTextEditorHint/emptyTextEditorHint.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts index 327554fd781..952a32eac79 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts @@ -348,7 +348,7 @@ class EmptyTextEditorHintContentWidget implements IContentWidget { id: ChangeLanguageAction.ID, from: 'hint' }); - await this.commandService.executeCommand(ChangeLanguageAction.ID, { from: 'hint' }); + await this.commandService.executeCommand(ChangeLanguageAction.ID); this.editor.focus(); }; From 3581a121d1c05455c5bed4e79973c75ef8b9c6de Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 3 Jul 2024 22:44:46 +0200 Subject: [PATCH 0256/2222] compress schemas by using $refs for duplicates (#219883) * compress schema * add tests * remove unused import * polish --- src/vs/base/common/jsonSchema.ts | 147 +++++ src/vs/base/test/common/jsonSchema.test.ts | 574 ++++++++++++++++++ .../common/preferencesContribution.ts | 21 +- 3 files changed, 739 insertions(+), 3 deletions(-) create mode 100644 src/vs/base/test/common/jsonSchema.test.ts diff --git a/src/vs/base/common/jsonSchema.ts b/src/vs/base/common/jsonSchema.ts index 4216b0e5c0d..fbf6d9813fa 100644 --- a/src/vs/base/common/jsonSchema.ts +++ b/src/vs/base/common/jsonSchema.ts @@ -118,3 +118,150 @@ export type SchemaToType = T extends { type: 'string' } : T extends { type: 'array'; items: infer I } ? Array> : never; + +interface Equals { schemas: IJSONSchema[]; id?: string } + +export function getCompressedContent(schema: IJSONSchema): string { + let hasDups = false; + + + // visit all schema nodes and collect the ones that are equal + const equalsByString = new Map(); + const nodeToEquals = new Map(); + const visitSchemas = (next: IJSONSchema) => { + if (schema === next) { + return true; + } + const val = JSON.stringify(next); + if (val.length < 30) { + // the $ref takes around 25 chars, so we don't save anything + return true; + } + const eq = equalsByString.get(val); + if (!eq) { + const newEq = { schemas: [next] }; + equalsByString.set(val, newEq); + nodeToEquals.set(next, newEq); + return true; + } + eq.schemas.push(next); + nodeToEquals.set(next, eq); + hasDups = true; + return false; + }; + traverseNodes(schema, visitSchemas); + equalsByString.clear(); + + if (!hasDups) { + return JSON.stringify(schema); + } + + let defNodeName = '$defs'; + while (schema.hasOwnProperty(defNodeName)) { + defNodeName += '_'; + } + + // used to collect all schemas that are later put in `$defs`. The index in the array is the id of the schema. + const definitions: IJSONSchema[] = []; + + function stringify(root: IJSONSchema): string { + return JSON.stringify(root, (_key: string, value: any) => { + if (value !== root) { + const eq = nodeToEquals.get(value); + if (eq && eq.schemas.length > 1) { + if (!eq.id) { + eq.id = `_${definitions.length}`; + definitions.push(eq.schemas[0]); + } + return { $ref: `#/${defNodeName}/${eq.id}` }; + } + } + return value; + }); + } + + // stringify the schema and replace duplicate subtrees with $ref + // this will add new items to the definitions array + const str = stringify(schema); + + // now stringify the definitions. Each invication of stringify cann add new items to the definitions array, so the length can grow while we iterate + const defStrings: string[] = []; + for (let i = 0; i < definitions.length; i++) { + defStrings.push(`"_${i}":${stringify(definitions[i])}`); + } + if (defStrings.length) { + return `${str.substring(0, str.length - 1)},"${defNodeName}":{${defStrings.join(',')}}}`; + } + return str; +} + +type IJSONSchemaRef = IJSONSchema | boolean; + +function isObject(thing: any): thing is object { + return typeof thing === 'object' && thing !== null; +} + +/* + * Traverse a JSON schema and visit each schema node +*/ +function traverseNodes(root: IJSONSchema, visit: (schema: IJSONSchema) => boolean) { + if (!root || typeof root !== 'object') { + return; + } + const collectEntries = (...entries: (IJSONSchemaRef | undefined)[]) => { + for (const entry of entries) { + if (isObject(entry)) { + toWalk.push(entry); + } + } + }; + const collectMapEntries = (...maps: (IJSONSchemaMap | undefined)[]) => { + for (const map of maps) { + if (isObject(map)) { + for (const key in map) { + const entry = map[key]; + if (isObject(entry)) { + toWalk.push(entry); + } + } + } + } + }; + const collectArrayEntries = (...arrays: (IJSONSchemaRef[] | undefined)[]) => { + for (const array of arrays) { + if (Array.isArray(array)) { + for (const entry of array) { + if (isObject(entry)) { + toWalk.push(entry); + } + } + } + } + }; + const collectEntryOrArrayEntries = (items: (IJSONSchemaRef[] | IJSONSchemaRef | undefined)) => { + if (Array.isArray(items)) { + for (const entry of items) { + if (isObject(entry)) { + toWalk.push(entry); + } + } + } else if (isObject(items)) { + toWalk.push(items); + } + }; + + const toWalk: IJSONSchema[] = [root]; + + let next = toWalk.pop(); + while (next) { + const visitChildern = visit(next); + if (visitChildern) { + collectEntries(next.additionalItems, next.additionalProperties, next.not, next.contains, next.propertyNames, next.if, next.then, next.else, next.unevaluatedItems, next.unevaluatedProperties); + collectMapEntries(next.definitions, next.$defs, next.properties, next.patternProperties, next.dependencies, next.dependentSchemas); + collectArrayEntries(next.anyOf, next.allOf, next.oneOf, next.prefixItems); + collectEntryOrArrayEntries(next.items); + } + next = toWalk.pop(); + } +} + diff --git a/src/vs/base/test/common/jsonSchema.test.ts b/src/vs/base/test/common/jsonSchema.test.ts new file mode 100644 index 00000000000..f47e1790143 --- /dev/null +++ b/src/vs/base/test/common/jsonSchema.test.ts @@ -0,0 +1,574 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import assert from 'assert'; +import { getCompressedContent, IJSONSchema } from 'vs/base/common/jsonSchema'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; + +suite('JSON Schema', () => { + + ensureNoDisposablesAreLeakedInTestSuite(); + + test('getCompressedContent 1', () => { + + const schema: IJSONSchema = { + type: 'object', + properties: { + a: { + type: 'object', + description: 'a', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + }, + e: { + type: 'object', + description: 'e', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + } + } + }; + + const expected: IJSONSchema = { + type: 'object', + properties: { + a: { + type: 'object', + description: 'a', + properties: { + b: { + $ref: '#/$defs/_0' + } + } + }, + e: { + type: 'object', + description: 'e', + properties: { + b: { + $ref: '#/$defs/_0' + } + } + } + }, + $defs: { + "_0": { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + + }; + + assert.deepEqual(getCompressedContent(schema), JSON.stringify(expected)); + }); + + test('getCompressedContent 2', () => { + + const schema: IJSONSchema = { + type: 'object', + properties: { + a: { + type: 'object', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + }, + e: { + type: 'object', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + } + } + }; + + const expected: IJSONSchema = { + type: 'object', + properties: { + a: { + $ref: '#/$defs/_0' + + }, + e: { + $ref: '#/$defs/_0' + } + }, + $defs: { + "_0": { + type: 'object', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + } + } + + }; + + assert.deepEqual(getCompressedContent(schema), JSON.stringify(expected)); + }); + + test('getCompressedContent 3', () => { + + + const schema: IJSONSchema = { + type: 'object', + properties: { + a: { + type: 'object', + oneOf: [ + { + allOf: [ + { + properties: { + name: { + type: 'string' + }, + description: { + type: 'string' + } + } + }, + { + properties: { + street: { + type: 'string' + }, + } + } + ] + }, + { + allOf: [ + { + properties: { + name: { + type: 'string' + }, + description: { + type: 'string' + } + } + }, + { + properties: { + river: { + type: 'string' + }, + } + } + ] + }, + { + allOf: [ + { + properties: { + name: { + type: 'string' + }, + description: { + type: 'string' + } + } + }, + { + properties: { + mountain: { + type: 'string' + }, + } + } + ] + } + ] + }, + b: { + type: 'object', + properties: { + street: { + properties: { + street: { + type: 'string' + } + } + } + } + } + } + }; + + const expected: IJSONSchema = { + "type": "object", + "properties": { + "a": { + "type": "object", + "oneOf": [ + { + "allOf": [ + { + "$ref": "#/$defs/_0" + }, + { + "$ref": "#/$defs/_1" + } + ] + }, + { + "allOf": [ + { + "$ref": "#/$defs/_0" + }, + { + "properties": { + "river": { + "type": "string" + } + } + } + ] + }, + { + "allOf": [ + { + "$ref": "#/$defs/_0" + }, + { + "properties": { + "mountain": { + "type": "string" + } + } + } + ] + } + ] + }, + "b": { + "type": "object", + "properties": { + "street": { + "$ref": "#/$defs/_1" + } + } + } + }, + "$defs": { + "_0": { + "properties": { + "name": { + "type": "string" + }, + "description": { + "type": "string" + } + } + }, + "_1": { + "properties": { + "street": { + "type": "string" + } + } + } + } + }; + + const actual = getCompressedContent(schema); + assert.deepEqual(actual, JSON.stringify(expected)); + }); + + test('getCompressedContent 4', () => { + + const schema: IJSONSchema = { + type: 'object', + properties: { + a: { + type: 'object', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + }, + e: { + type: 'object', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + }, + f: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + }; + + const expected: IJSONSchema = { + type: 'object', + properties: { + a: { + $ref: '#/$defs/_0' + }, + e: { + $ref: '#/$defs/_0' + }, + f: { + $ref: '#/$defs/_1' + } + }, + $defs: { + "_0": { + type: 'object', + properties: { + b: { + type: 'object', + properties: { + c: { + $ref: '#/$defs/_1' + } + } + } + } + }, + "_1": { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + + }; + + assert.deepEqual(getCompressedContent(schema), JSON.stringify(expected)); + }); + + test('getCompressedContent 5', () => { + + const schema: IJSONSchema = { + type: 'object', + properties: { + a: { + type: 'array', + items: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + }, + e: { + type: 'array', + items: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + }, + f: { + type: 'object', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + }, + g: { + type: 'object', + properties: { + b: { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + } + } + }; + + const expected: IJSONSchema = { + type: 'object', + properties: { + a: { + $ref: '#/$defs/_0' + }, + e: { + $ref: '#/$defs/_0' + }, + f: { + $ref: '#/$defs/_1' + }, + g: { + $ref: '#/$defs/_1' + } + }, + $defs: { + "_0": { + type: 'array', + items: { + $ref: '#/$defs/_2' + } + }, + "_1": { + type: 'object', + properties: { + b: { + $ref: '#/$defs/_2' + } + } + }, + "_2": { + type: 'object', + properties: { + c: { + type: 'object', + properties: { + d: { + type: 'string' + } + } + } + } + } + } + + }; + + assert.deepEqual(getCompressedContent(schema), JSON.stringify(expected)); + }); + + +}); diff --git a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts index 877d449605c..958b8443d70 100644 --- a/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts +++ b/src/vs/workbench/contrib/preferences/common/preferencesContribution.ts @@ -24,6 +24,8 @@ import { RegisteredEditorPriority, IEditorResolverService } from 'vs/workbench/s import { ITextEditorService } from 'vs/workbench/services/textfile/common/textEditorService'; import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IPreferencesService, USE_SPLIT_JSON_SETTING } from 'vs/workbench/services/preferences/common/preferences'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; +import { getCompressedContent, IJSONSchema } from 'vs/base/common/jsonSchema'; +import { ILogService, LogLevel } from 'vs/platform/log/common/log'; const schemaRegistry = Registry.as(JSONContributionRegistry.Extensions.JSONContribution); @@ -43,7 +45,8 @@ export class PreferencesContribution implements IWorkbenchContribution { @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @IEditorResolverService private readonly editorResolverService: IEditorResolverService, - @ITextEditorService private readonly textEditorService: ITextEditorService + @ITextEditorService private readonly textEditorService: ITextEditorService, + @ILogService private readonly logService: ILogService, ) { this.settingsListener = this.configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(USE_SPLIT_JSON_SETTING) || e.affectsConfiguration(DEFAULT_SETTINGS_EDITOR_SETTING)) { @@ -120,26 +123,38 @@ export class PreferencesContribution implements IWorkbenchContribution { private getSchemaModel(uri: URI): ITextModel { let schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()] ?? {} /* Use empty schema if not yet registered */; - const modelContent = JSON.stringify(schema); + const modelContent = this.getSchemaContent(uri, schema); const languageSelection = this.languageService.createById('jsonc'); const model = this.modelService.createModel(modelContent, languageSelection, uri); const disposables = new DisposableStore(); disposables.add(schemaRegistry.onDidChangeSchema(schemaUri => { if (schemaUri === uri.toString()) { schema = schemaRegistry.getSchemaContributions().schemas[uri.toString()]; - model.setValue(JSON.stringify(schema)); + model.setValue(this.getSchemaContent(uri, schema)); } })); disposables.add(model.onWillDispose(() => disposables.dispose())); return model; } + private getSchemaContent(uri: URI, schema: IJSONSchema): string { + const startTime = Date.now(); + const content = getCompressedContent(schema); + if (this.logService.getLevel() === LogLevel.Debug) { + const endTime = Date.now(); + const uncompressed = JSON.stringify(schema); + this.logService.debug(`${uri.path}: ${uncompressed.length} -> ${content.length} (${Math.round((uncompressed.length - content.length) / uncompressed.length * 100)}%) Took ${endTime - startTime}ms`); + } + return content; + } + dispose(): void { dispose(this.editorOpeningListener); dispose(this.settingsListener); } } + const registry = Registry.as(Extensions.Configuration); registry.registerConfiguration({ ...workbenchConfigurationNodeBase, From edd91bc5e08bc3e380d9d900b42c13a3a36bb01b Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Wed, 3 Jul 2024 15:12:40 -0700 Subject: [PATCH 0257/2222] Update classifier workflows to use AzureLogin task (#219895) --- .../workflows/deep-classifier-assign-monitor.yml | 1 - .github/workflows/deep-classifier-runner.yml | 2 -- .github/workflows/deep-classifier-scraper.yml | 13 +++++++++++-- .../workflows/deep-classifier-unassign-monitor.yml | 1 - 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/.github/workflows/deep-classifier-assign-monitor.yml b/.github/workflows/deep-classifier-assign-monitor.yml index cfd9abc374a..a61f9cfb137 100644 --- a/.github/workflows/deep-classifier-assign-monitor.yml +++ b/.github/workflows/deep-classifier-assign-monitor.yml @@ -21,4 +21,3 @@ jobs: with: botName: VSCodeTriageBot token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} diff --git a/.github/workflows/deep-classifier-runner.yml b/.github/workflows/deep-classifier-runner.yml index 81fd3516751..7145de06db5 100644 --- a/.github/workflows/deep-classifier-runner.yml +++ b/.github/workflows/deep-classifier-runner.yml @@ -40,9 +40,7 @@ jobs: excludeLabels: feature-request|testplan-item configPath: classifier blobContainerName: vscode-issue-classifier - blobStorageKey: ${{secrets.AZURE_BLOB_STORAGE_CONNECTION_STRING}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} - name: Set up Python 3.7 uses: actions/setup-python@v5 with: diff --git a/.github/workflows/deep-classifier-scraper.yml b/.github/workflows/deep-classifier-scraper.yml index e21061549d9..e663372fad0 100644 --- a/.github/workflows/deep-classifier-scraper.yml +++ b/.github/workflows/deep-classifier-scraper.yml @@ -1,4 +1,9 @@ name: "Deep Classifier: Scraper" + +permissions: + id-token: write + contents: read + on: schedule: - cron: 0 0 15 * * # 15th of the month @@ -9,7 +14,13 @@ on: jobs: main: runs-on: ubuntu-latest + environment: main steps: + - uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + allow-no-subscriptions: true - name: Checkout Actions uses: actions/checkout@v4 with: @@ -25,6 +36,4 @@ jobs: uses: ./actions/classifier-deep/train/fetch-issues with: blobContainerName: vscode-issue-classifier - blobStorageKey: ${{secrets.AZURE_BLOB_STORAGE_CONNECTION_STRING}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} diff --git a/.github/workflows/deep-classifier-unassign-monitor.yml b/.github/workflows/deep-classifier-unassign-monitor.yml index d0e14e936c2..52ac0d3ddcd 100644 --- a/.github/workflows/deep-classifier-unassign-monitor.yml +++ b/.github/workflows/deep-classifier-unassign-monitor.yml @@ -21,4 +21,3 @@ jobs: with: botName: VSCodeTriageBot token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} From 2f0b553214a7bb5fd79b4fb06ba8efe21e421275 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 3 Jul 2024 15:34:31 -0700 Subject: [PATCH 0258/2222] Iron out event firing by leveraging observables (#219897) I think this reduces redundant event firing by quite a bit. I hope @hediet would be proud --- .../platform/quickinput/browser/quickInput.ts | 3 - .../browser/quickInputController.ts | 1 - .../quickinput/browser/quickInputTree.ts | 103 ++++++++---------- 3 files changed, 43 insertions(+), 64 deletions(-) diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 4c6dfbe6b7d..74414a631a1 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -1036,9 +1036,6 @@ export class QuickPick extends QuickInput implements I // We want focus to exist in the list if there are items so that space can be used to toggle this.ui.list.shouldLoop = !this.canSelectMany; this.ui.list.filter(this.filterValue(this.ui.inputBox.value)); - this.ui.checkAll.checked = this.ui.list.getAllVisibleChecked(); - this.ui.visibleCount.setCount(this.ui.list.getVisibleCount()); - this.ui.count.setCount(this.ui.list.getCheckedCount()); switch (this._itemActivation) { case ItemActivation.NONE: this._itemActivation = ItemActivation.FIRST; // only valid once, then unset diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index 626b74e8fa5..41fad6343ee 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -580,7 +580,6 @@ export class QuickInputController extends Disposable { ui.count.setCount(0); dom.reset(ui.message); ui.progressBar.stop(); - ui.list.setElements([]); ui.list.matchOnDescription = false; ui.list.matchOnDetail = false; ui.list.matchOnLabel = true; diff --git a/src/vs/platform/quickinput/browser/quickInputTree.ts b/src/vs/platform/quickinput/browser/quickInputTree.ts index c65ad7ef4d7..8d1a046678b 100644 --- a/src/vs/platform/quickinput/browser/quickInputTree.ts +++ b/src/vs/platform/quickinput/browser/quickInputTree.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { Emitter, Event, IValueWithChangeEvent } from 'vs/base/common/event'; +import { Emitter, Event, EventBufferer, IValueWithChangeEvent } from 'vs/base/common/event'; import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { IObjectTreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; @@ -39,6 +39,8 @@ import { isCancellationError } from 'vs/base/common/errors'; import type { IHoverWidget, IManagedHoverTooltipMarkdownString } from 'vs/base/browser/ui/hover/hover'; import { QuickPickFocus } from '../common/quickInput'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { observableValue, observableValueOpts } from 'vs/base/common/observable'; +import { equals } from 'vs/base/common/arrays'; const $ = dom.$; @@ -668,17 +670,17 @@ export class QuickInputTree extends Disposable { */ readonly onLeave: Event = this._onLeave.event; - private readonly _onChangedAllVisibleChecked = new Emitter(); - onChangedAllVisibleChecked: Event = this._onChangedAllVisibleChecked.event; + private readonly _visibleCountObservable = observableValue('VisibleCount', 0); + onChangedVisibleCount: Event = Event.fromObservable(this._visibleCountObservable, this._store); - private readonly _onChangedCheckedCount = new Emitter(); - onChangedCheckedCount: Event = this._onChangedCheckedCount.event; + private readonly _allVisibleCheckedObservable = observableValue('AllVisibleChecked', false); + onChangedAllVisibleChecked: Event = Event.fromObservable(this._allVisibleCheckedObservable, this._store); - private readonly _onChangedVisibleCount = new Emitter(); - onChangedVisibleCount: Event = this._onChangedVisibleCount.event; + private readonly _checkedCountObservable = observableValue('CheckedCount', 0); + onChangedCheckedCount: Event = Event.fromObservable(this._checkedCountObservable, this._store); - private readonly _onChangedCheckedElements = new Emitter(); - onChangedCheckedElements: Event = this._onChangedCheckedElements.event; + private readonly _checkedElementsObservable = observableValueOpts({ equalsFn: equals }, new Array()); + onChangedCheckedElements: Event = Event.fromObservable(this._checkedElementsObservable, this._store); private readonly _onButtonTriggered = new Emitter>(); onButtonTriggered = this._onButtonTriggered.event; @@ -686,21 +688,21 @@ export class QuickInputTree extends Disposable { private readonly _onSeparatorButtonTriggered = new Emitter(); onSeparatorButtonTriggered = this._onSeparatorButtonTriggered.event; + private readonly _elementChecked = new Emitter<{ element: IQuickPickElement; checked: boolean }>(); + private readonly _elementCheckedEventBufferer = new EventBufferer(); + + private _hasCheckboxes = false; + private readonly _container: HTMLElement; private readonly _tree: WorkbenchObjectTree; private readonly _separatorRenderer: QuickPickSeparatorElementRenderer; private readonly _itemRenderer: QuickPickItemElementRenderer; - private readonly _elementChecked = new Emitter<{ element: IQuickPickElement; checked: boolean }>(); private _inputElements = new Array(); private _elementTree = new Array(); private _itemElements = new Array(); // Elements that apply to the current set of elements private readonly _elementDisposable = this._register(new DisposableStore()); private _lastHover: IHoverWidget | undefined; - // This is used to prevent setting the checked state of a single element from firing the checked events - // so that we can batch them together. This can probably be improved by handling events differently, - // but this works for now. An observable would probably be ideal for this. - private _shouldFireCheckedEvents = true; constructor( private parent: HTMLElement, @@ -842,6 +844,7 @@ export class QuickInputTree extends Disposable { this._registerOnKeyDown(); this._registerOnContainerClick(); this._registerOnMouseMiddleClick(); + this._registerOnTreeModelChanged(); this._registerOnElementChecked(); this._registerOnContextMenu(); this._registerHoverListeners(); @@ -879,8 +882,19 @@ export class QuickInputTree extends Disposable { })); } + private _registerOnTreeModelChanged() { + this._register(this._tree.onDidChangeModel(() => { + const visibleCount = this._itemElements.filter(e => !e.hidden).length; + this._visibleCountObservable.set(visibleCount, undefined); + if (this._hasCheckboxes) { + this._fireCheckedEvents(); + } + })); + } + private _registerOnElementChecked() { - this._register(this._elementChecked.event(_ => this._fireCheckedEvents())); + // Only fire the last event when buffered + this._register(this._elementCheckedEventBufferer.wrapEvent(this._elementChecked.event, (_, e) => e)(_ => this._fireCheckedEvents())); } private _registerOnContextMenu() { @@ -1015,37 +1029,21 @@ export class QuickInputTree extends Disposable { //#region public methods - getAllVisibleChecked() { - return this._allVisibleChecked(this._itemElements, false); - } - - getCheckedCount() { - return this._itemElements.filter(element => element.checked).length; - } - - getVisibleCount() { - return this._itemElements.filter(e => !e.hidden).length; - } - setAllVisibleChecked(checked: boolean) { - try { - this._shouldFireCheckedEvents = false; + this._elementCheckedEventBufferer.bufferEvents(() => { this._itemElements.forEach(element => { if (!element.hidden && !element.checkboxDisabled) { - // Would fire an event if we didn't have the flag set + // Would fire an event if we didn't beffer the events element.checked = checked; } }); - } finally { - this._shouldFireCheckedEvents = true; - this._fireCheckedEvents(); - } + }); } setElements(inputElements: QuickPickItem[]): void { this._elementDisposable.clear(); this._inputElements = inputElements; - const hasCheckbox = this.parent.classList.contains('show-checkboxes'); + this._hasCheckboxes = this.parent.classList.contains('show-checkboxes'); let currentSeparatorElement: QuickPickSeparatorElement | undefined; this._itemElements = new Array(); this._elementTree = inputElements.reduce((result, item, index) => { @@ -1071,7 +1069,7 @@ export class QuickInputTree extends Disposable { } const qpi = new QuickPickItemElement( index, - hasCheckbox, + this._hasCheckboxes, (event: IQuickPickItemButtonEvent) => this.fireButtonTriggered(event), this._elementChecked, item, @@ -1115,7 +1113,6 @@ export class QuickInputTree extends Disposable { } } this._tree.setChildren(null, elements); - this._onChangedVisibleCount.fire(visibleCount); // Accessibility hack, unfortunately on next tick // https://github.com/microsoft/vscode/issues/211976 @@ -1177,20 +1174,16 @@ export class QuickInputTree extends Disposable { } setCheckedElements(items: IQuickPickItem[]) { - try { - this._shouldFireCheckedEvents = false; + this._elementCheckedEventBufferer.bufferEvents(() => { const checked = new Set(); for (const item of items) { checked.add(item); } for (const element of this._itemElements) { - // Would fire an event if we didn't have the flag set + // Would fire an event if we didn't beffer the events element.checked = checked.has(element.item); } - } finally { - this._shouldFireCheckedEvents = true; - this._fireCheckedEvents(); - } + }); } focus(what: QuickPickFocus): void { @@ -1500,16 +1493,11 @@ export class QuickInputTree extends Disposable { } this._tree.setChildren(null, elements); this._tree.layout(); - - this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked()); - this._onChangedVisibleCount.fire(shownElements.length); - return true; } toggleCheckbox() { - try { - this._shouldFireCheckedEvents = false; + this._elementCheckedEventBufferer.bufferEvents(() => { const elements = this._tree.getFocus().filter((e): e is QuickPickItemElement => e instanceof QuickPickItemElement); const allChecked = this._allVisibleChecked(elements); for (const element of elements) { @@ -1518,10 +1506,7 @@ export class QuickInputTree extends Disposable { element.checked = !allChecked; } } - } finally { - this._shouldFireCheckedEvents = true; - this._fireCheckedEvents(); - } + }); } display(display: boolean) { @@ -1581,12 +1566,10 @@ export class QuickInputTree extends Disposable { } private _fireCheckedEvents() { - if (!this._shouldFireCheckedEvents) { - return; - } - this._onChangedAllVisibleChecked.fire(this.getAllVisibleChecked()); - this._onChangedCheckedCount.fire(this.getCheckedCount()); - this._onChangedCheckedElements.fire(this.getCheckedElements()); + this._allVisibleCheckedObservable.set(this._allVisibleChecked(this._itemElements, false), undefined); + const checkedCount = this._itemElements.filter(element => element.checked).length; + this._checkedCountObservable.set(checkedCount, undefined); + this._checkedElementsObservable.set(this.getCheckedElements(), undefined); } private fireButtonTriggered(event: IQuickPickItemButtonEvent) { From 9469c82f9fecf51745902bba3af45fe09fe80c62 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 3 Jul 2024 16:12:04 -0700 Subject: [PATCH 0259/2222] Don't allow insiders users to report issues with TS plugins enabled (#219902) Seeing far too many users not reading the warning/issue text before reporting issues. These reports are rarely actionable --- .../typescript-language-features/src/typescriptServiceClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index da6408b827b..2435f7725a9 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -673,7 +673,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType if (!this._isPromptingAfterCrash) { if (this.pluginManager.plugins.length) { prompt = vscode.window.showWarningMessage( - vscode.l10n.t("The JS/TS language service crashed.\nThis may be caused by a plugin contributed by one of these extensions: {0}.\nPlease try disabling these extensions before filing an issue against VS Code.", pluginExtensionList), reportIssueItem); + vscode.l10n.t("The JS/TS language service crashed.\nThis may be caused by a plugin contributed by one of these extensions: {0}.\nPlease try disabling these extensions before filing an issue against VS Code.", pluginExtensionList)); } else { prompt = vscode.window.showWarningMessage( vscode.l10n.t("The JS/TS language service crashed."), From 422b24d7aa4c97a8312822b32af008387dad45e0 Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Wed, 3 Jul 2024 16:17:56 -0700 Subject: [PATCH 0260/2222] Fix hover over non-ascii variable names (#219903) Fix #216610 --- src/vs/workbench/contrib/debug/common/debugUtils.ts | 2 +- src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/debug/common/debugUtils.ts b/src/vs/workbench/contrib/debug/common/debugUtils.ts index acf1e467817..1b193bd9d5d 100644 --- a/src/vs/workbench/contrib/debug/common/debugUtils.ts +++ b/src/vs/workbench/contrib/debug/common/debugUtils.ts @@ -102,7 +102,7 @@ export function getExactExpressionStartAndEnd(lineContent: string, looseStart: n // If there are non-word characters after the cursor, we want to truncate the expression then. // For example in expression 'a.b.c.d', if the focus was under 'b', 'a.b' would be evaluated. if (matchingExpression) { - const subExpression: RegExp = /\w+/g; + const subExpression: RegExp = /(\w|\p{L})+/gu; let subExpressionResult: RegExpExecArray | null = null; while (subExpressionResult = subExpression.exec(matchingExpression)) { const subEnd = subExpressionResult.index + 1 + startOffset + subExpressionResult[0].length; diff --git a/src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts b/src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts index c57bb374b3d..d6b033ff16f 100644 --- a/src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/debugUtils.test.ts @@ -41,6 +41,9 @@ suite('Debug - Utils', () => { assert.deepStrictEqual(getExactExpressionStartAndEnd('var t = a.b;c.d.name', 16, 20), { start: 13, end: 20 }); assert.deepStrictEqual(getExactExpressionStartAndEnd('var t = a.b.c-d.name', 16, 20), { start: 15, end: 20 }); + + assert.deepStrictEqual(getExactExpressionStartAndEnd('var aøñéå文 = a.b.c-d.name', 5, 5), { start: 5, end: 10 }); + assert.deepStrictEqual(getExactExpressionStartAndEnd('aøñéå文.aøñéå文.aøñéå文', 9, 9), { start: 1, end: 13 }); }); test('config presentation', () => { From ee8937a60487747e544cd0f3af58384376b6a07c Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Wed, 3 Jul 2024 17:02:21 -0700 Subject: [PATCH 0261/2222] Rename snippet placeholder (#219904) Fixes #214760 --- extensions/javascript/snippets/javascript.code-snippets | 2 +- .../typescript-basics/snippets/typescript.code-snippets | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/extensions/javascript/snippets/javascript.code-snippets b/extensions/javascript/snippets/javascript.code-snippets index 9448fd140d9..ddf9a13b731 100644 --- a/extensions/javascript/snippets/javascript.code-snippets +++ b/extensions/javascript/snippets/javascript.code-snippets @@ -46,7 +46,7 @@ "For-Of Loop": { "prefix": "forof", "body": [ - "for (const ${1:iterator} of ${2:object}) {", + "for (const ${1:element} of ${2:object}) {", "\t$TM_SELECTED_TEXT$0", "}" ], diff --git a/extensions/typescript-basics/snippets/typescript.code-snippets b/extensions/typescript-basics/snippets/typescript.code-snippets index 35b2aa1711c..9d6dadcc2e6 100644 --- a/extensions/typescript-basics/snippets/typescript.code-snippets +++ b/extensions/typescript-basics/snippets/typescript.code-snippets @@ -163,7 +163,7 @@ "For-Of Loop": { "prefix": "forof", "body": [ - "for (const ${1:iterator} of ${2:object}) {", + "for (const ${1:element} of ${2:object}) {", "\t$TM_SELECTED_TEXT$0", "}" ], @@ -172,7 +172,7 @@ "For-Await-Of Loop": { "prefix": "forawaitof", "body": [ - "for await (const ${1:iterator} of ${2:object}) {", + "for await (const ${1:element} of ${2:object}) {", "\t$TM_SELECTED_TEXT$0", "}" ], From 34f8428e4b0e3bf822f4b9e8b4e3e0cab69ef2ce Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Wed, 3 Jul 2024 20:33:11 -0700 Subject: [PATCH 0262/2222] Misc cleanup in QuickInputTree (#219914) * dispose some Event.maps * remove a bunch of dead code (functions that are no longer used) * stylistic changes * have `QuickPickFocus.Second`, `QuickPickFocus.Last`, `QuickPickFocus.NextSeparator` use the tree itself instead of `this._itemElements` --- .../browser/quickInputController.ts | 6 +- .../quickinput/browser/quickInputTree.ts | 154 +++++++----------- 2 files changed, 66 insertions(+), 94 deletions(-) diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index 41fad6343ee..8999c5e84ab 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -275,7 +275,7 @@ export class QuickInputController extends Disposable { } else { selectors.push('input[type=text]'); } - if (this.getUI().list.isDisplayed()) { + if (this.getUI().list.displayed) { selectors.push('.monaco-list'); } // focus links if there are any @@ -614,7 +614,7 @@ export class QuickInputController extends Disposable { ui.customButtonContainer.style.display = visibilities.customButton ? '' : 'none'; ui.message.style.display = visibilities.message ? '' : 'none'; ui.progressBar.getContainer().style.display = visibilities.progressBar ? '' : 'none'; - ui.list.display(!!visibilities.list); + ui.list.displayed = !!visibilities.list; ui.container.classList.toggle('show-checkboxes', !!visibilities.checkBox); ui.container.classList.toggle('hidden-input', !visibilities.inputBox && !visibilities.description); this.updateLayout(); // TODO @@ -683,7 +683,7 @@ export class QuickInputController extends Disposable { } navigate(next: boolean, quickNavigate?: IQuickNavigateConfiguration) { - if (this.isVisible() && this.getUI().list.isDisplayed()) { + if (this.isVisible() && this.getUI().list.displayed) { this.getUI().list.focus(next ? QuickPickFocus.Next : QuickPickFocus.Previous); if (quickNavigate && this.controller instanceof QuickPick) { this.controller.quickNavigate = quickNavigate; diff --git a/src/vs/platform/quickinput/browser/quickInputTree.ts b/src/vs/platform/quickinput/browser/quickInputTree.ts index 8d1a046678b..83976c15065 100644 --- a/src/vs/platform/quickinput/browser/quickInputTree.ts +++ b/src/vs/platform/quickinput/browser/quickInputTree.ts @@ -658,6 +658,8 @@ class QuickPickSeparatorElementRenderer extends BaseQuickInputListRenderer(); /** * Event that is fired when the tree receives a keydown. @@ -691,6 +693,8 @@ export class QuickInputTree extends Disposable { private readonly _elementChecked = new Emitter<{ element: IQuickPickElement; checked: boolean }>(); private readonly _elementCheckedEventBufferer = new EventBufferer(); + //#endregion + private _hasCheckboxes = false; private readonly _container: HTMLElement; @@ -745,7 +749,8 @@ export class QuickInputTree extends Disposable { get onDidChangeFocus() { return Event.map( this._tree.onDidChangeFocus, - e => e.elements.filter((e): e is QuickPickItemElement => e instanceof QuickPickItemElement).map(e => e.item) + e => e.elements.filter((e): e is QuickPickItemElement => e instanceof QuickPickItemElement).map(e => e.item), + this._store ); } @@ -756,10 +761,19 @@ export class QuickInputTree extends Disposable { e => ({ items: e.elements.filter((e): e is QuickPickItemElement => e instanceof QuickPickItemElement).map(e => e.item), event: e.browserEvent - }) + }), + this._store ); } + get displayed() { + return this._container.style.display !== 'none'; + } + + set displayed(value: boolean) { + this._container.style.display = value ? '' : 'none'; + } + get scrollTop() { return this._tree.scrollTop; } @@ -887,14 +901,14 @@ export class QuickInputTree extends Disposable { const visibleCount = this._itemElements.filter(e => !e.hidden).length; this._visibleCountObservable.set(visibleCount, undefined); if (this._hasCheckboxes) { - this._fireCheckedEvents(); + this._updateCheckedObservables(); } })); } private _registerOnElementChecked() { // Only fire the last event when buffered - this._register(this._elementCheckedEventBufferer.wrapEvent(this._elementChecked.event, (_, e) => e)(_ => this._fireCheckedEvents())); + this._register(this._elementCheckedEventBufferer.wrapEvent(this._elementChecked.event, (_, e) => e)(_ => this._updateCheckedObservables())); } private _registerOnContextMenu() { @@ -1055,7 +1069,7 @@ export class QuickInputTree extends Disposable { } currentSeparatorElement = new QuickPickSeparatorElement( index, - (event: IQuickPickSeparatorButtonEvent) => this.fireSeparatorButtonTriggered(event), + e => this._onSeparatorButtonTriggered.fire(e), item ); element = currentSeparatorElement; @@ -1070,7 +1084,7 @@ export class QuickInputTree extends Disposable { const qpi = new QuickPickItemElement( index, this._hasCheckboxes, - (event: IQuickPickItemButtonEvent) => this.fireButtonTriggered(event), + e => this._onButtonTriggered.fire(e), this._elementChecked, item, separator, @@ -1088,31 +1102,7 @@ export class QuickInputTree extends Disposable { return result; }, new Array()); - const elements = new Array>(); - let visibleCount = 0; - for (const element of this._elementTree) { - if (element instanceof QuickPickSeparatorElement) { - elements.push({ - element, - collapsible: false, - collapsed: false, - children: element.children.map(e => ({ - element: e, - collapsible: false, - collapsed: false, - })), - }); - visibleCount += element.children.length + 1; // +1 for the separator itself; - } else { - elements.push({ - element, - collapsible: false, - collapsed: false, - }); - visibleCount++; - } - } - this._tree.setChildren(null, elements); + this._setElementsToTree(this._elementTree); // Accessibility hack, unfortunately on next tick // https://github.com/microsoft/vscode/issues/211976 @@ -1129,17 +1119,6 @@ export class QuickInputTree extends Disposable { } } - getElementsCount(): number { - return this._inputElements.length; - } - - getFocusedElements() { - return this._tree.getFocus() - .filter((e): e is IQuickPickElement => !!e) - .map(e => e.item) - .filter((e): e is IQuickPickItem => !!e); - } - setFocusedElements(items: IQuickPickItem[]) { const elements = items.map(item => this._itemElements.find(e => e.item === item)) .filter((e): e is QuickPickItemElement => !!e); @@ -1156,12 +1135,6 @@ export class QuickInputTree extends Disposable { return this._tree.getHTMLElement().getAttribute('aria-activedescendant'); } - getSelectedElements() { - return this._tree.getSelection() - .filter((e): e is IQuickPickElement => !!e && !!(e as QuickPickItemElement).item) - .map(e => e.item); - } - setSelectedElements(items: IQuickPickItem[]) { const elements = items.map(item => this._itemElements.find(e => e.item === item)) .filter((e): e is QuickPickItemElement => !!e); @@ -1200,13 +1173,24 @@ export class QuickInputTree extends Disposable { this._tree.scrollTop = 0; this._tree.focusFirst(undefined, (e) => e.element instanceof QuickPickItemElement); break; - case QuickPickFocus.Second: + case QuickPickFocus.Second: { this._tree.scrollTop = 0; - this._tree.setFocus([this._itemElements[1]]); + let isSecondItem = false; + this._tree.focusFirst(undefined, (e) => { + if (!(e.element instanceof QuickPickItemElement)) { + return false; + } + if (isSecondItem) { + return true; + } + isSecondItem = !isSecondItem; + return false; + }); break; + } case QuickPickFocus.Last: this._tree.scrollTop = this._tree.scrollHeight; - this._tree.setFocus([this._itemElements[this._itemElements.length - 1]]); + this._tree.focusLast(undefined, (e) => e.element instanceof QuickPickItemElement); break; case QuickPickFocus.Next: { const prevFocus = this._tree.getFocus(); @@ -1308,7 +1292,7 @@ export class QuickInputTree extends Disposable { // If we didn't move, then we should just move to the end // of the list. this._tree.scrollTop = this._tree.scrollHeight; - this._tree.setFocus([this._itemElements[this._itemElements.length - 1]]); + this._tree.focusLast(undefined, (e) => e.element instanceof QuickPickItemElement); } break; } @@ -1470,28 +1454,7 @@ export class QuickInputTree extends Disposable { return result; }, new Array()); - const elements = new Array>(); - for (const element of finalElements) { - if (element instanceof QuickPickSeparatorElement) { - elements.push({ - element, - collapsible: false, - collapsed: false, - children: element.children.map(e => ({ - element: e, - collapsible: false, - collapsed: false, - })), - }); - } else { - elements.push({ - element, - collapsible: false, - collapsed: false, - }); - } - } - this._tree.setChildren(null, elements); + this._setElementsToTree(finalElements); this._tree.layout(); return true; } @@ -1509,14 +1472,6 @@ export class QuickInputTree extends Disposable { }); } - display(display: boolean) { - this._container.style.display = display ? '' : 'none'; - } - - isDisplayed() { - return this._container.style.display !== 'none'; - } - style(styles: IListStyles) { this._tree.style(styles); } @@ -1551,6 +1506,31 @@ export class QuickInputTree extends Disposable { //#region private methods + private _setElementsToTree(elements: IQuickPickElement[]) { + const treeElements = new Array>(); + for (const element of elements) { + if (element instanceof QuickPickSeparatorElement) { + treeElements.push({ + element, + collapsible: false, + collapsed: false, + children: element.children.map(e => ({ + element: e, + collapsible: false, + collapsed: false, + })), + }); + } else { + treeElements.push({ + element, + collapsible: false, + collapsed: false, + }); + } + } + this._tree.setChildren(null, treeElements); + } + private _allVisibleChecked(elements: QuickPickItemElement[], whenNoneVisible = true) { for (let i = 0, n = elements.length; i < n; i++) { const element = elements[i]; @@ -1565,21 +1545,13 @@ export class QuickInputTree extends Disposable { return whenNoneVisible; } - private _fireCheckedEvents() { + private _updateCheckedObservables() { this._allVisibleCheckedObservable.set(this._allVisibleChecked(this._itemElements, false), undefined); const checkedCount = this._itemElements.filter(element => element.checked).length; this._checkedCountObservable.set(checkedCount, undefined); this._checkedElementsObservable.set(this.getCheckedElements(), undefined); } - private fireButtonTriggered(event: IQuickPickItemButtonEvent) { - this._onButtonTriggered.fire(event); - } - - private fireSeparatorButtonTriggered(event: IQuickPickSeparatorButtonEvent) { - this._onSeparatorButtonTriggered.fire(event); - } - /** * Disposes of the hover and shows a new one for the given index if it has a tooltip. * @param element The element to show the hover for From 82c54248fd1373da9f4b4c3606d9778f0d08fc1a Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 4 Jul 2024 07:59:10 +0200 Subject: [PATCH 0263/2222] perf - inline `package.json` and `product.json` (#219841) --- build/darwin/create-universal-app.js | 1 - build/darwin/create-universal-app.ts | 1 - build/gulpfile.reh.js | 41 ++++++++++++++------ build/gulpfile.vscode.js | 43 ++++++++++++++------- build/gulpfile.vscode.web.js | 6 +-- build/lib/date.js | 24 ++++++++++++ build/lib/date.ts | 25 +++++++++++++ build/lib/inlineMeta.js | 46 +++++++++++++++++++++++ build/lib/inlineMeta.ts | 56 ++++++++++++++++++++++++++++ src/bootstrap-amd.js | 7 ++-- src/bootstrap-meta.js | 28 ++++++++++++++ src/cli.js | 8 +--- src/main.js | 5 +-- src/server-cli.js | 8 +--- src/server-main.js | 5 +-- 15 files changed, 248 insertions(+), 56 deletions(-) create mode 100644 build/lib/date.js create mode 100644 build/lib/date.ts create mode 100644 build/lib/inlineMeta.js create mode 100644 build/lib/inlineMeta.ts create mode 100644 src/bootstrap-meta.js diff --git a/build/darwin/create-universal-app.js b/build/darwin/create-universal-app.js index 7da8e55c908..85d27273861 100644 --- a/build/darwin/create-universal-app.js +++ b/build/darwin/create-universal-app.js @@ -28,7 +28,6 @@ async function main(buildDir) { x64AsarPath, arm64AsarPath, filesToSkip: [ - 'product.json', 'Credits.rtf', 'CodeResources', 'fsevents.node', diff --git a/build/darwin/create-universal-app.ts b/build/darwin/create-universal-app.ts index ffba8952cd8..04eb3a11e20 100644 --- a/build/darwin/create-universal-app.ts +++ b/build/darwin/create-universal-app.ts @@ -32,7 +32,6 @@ async function main(buildDir?: string) { x64AsarPath, arm64AsarPath, filesToSkip: [ - 'product.json', 'Credits.rtf', 'CodeResources', 'fsevents.node', diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index b6030c6102e..92d41b18485 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -12,11 +12,13 @@ const util = require('./lib/util'); const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const optimize = require('./lib/optimize'); +const { inlineMeta } = require('./lib/inlineMeta'); const product = require('../product.json'); const rename = require('gulp-rename'); const replace = require('gulp-replace'); const filter = require('gulp-filter'); const { getProductionDependencies } = require('./lib/dependencies'); +const { date } = require('./lib/date'); const vfs = require('vinyl-fs'); const packageJson = require('../package.json'); const flatmap = require('gulp-flatmap'); @@ -115,6 +117,12 @@ const serverWithWebEntryPoints = [ ...vscodeWebEntryPoints ]; +const commonJSEntryPoints = [ + 'out-build/server-main.js', + 'out-build/server-cli.js', + 'out-build/bootstrap-fork.js', +]; + function getNodeVersion() { const yarnrc = fs.readFileSync(path.join(REPO_ROOT, 'remote', '.yarnrc'), 'utf8'); const nodeVersion = /^target "(.*)"$/m.exec(yarnrc)[1]; @@ -180,7 +188,6 @@ if (defaultNodeTask) { function nodejs(platform, arch) { const { fetchUrls, fetchGithub } = require('./lib/fetch'); const untar = require('gulp-untar'); - const crypto = require('crypto'); if (arch === 'armhf') { arch = 'armv7l'; @@ -286,13 +293,22 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa } const name = product.nameShort; - const packageJsonStream = gulp.src(['remote/package.json'], { base: 'remote' }) - .pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined })); - const date = new Date().toISOString(); + let packageJsonContents; + const packageJsonStream = gulp.src(['remote/package.json'], { base: 'remote' }) + .pipe(json({ name, version, dependencies: undefined, optionalDependencies: undefined })) + .pipe(es.through(function (file) { + packageJsonContents = file.contents.toString(); + this.emit('data', file); + })); + let productJsonContents; const productJsonStream = gulp.src(['product.json'], { base: '.' }) - .pipe(json({ commit, date, version })); + .pipe(json({ commit, date, version })) + .pipe(es.through(function (file) { + productJsonContents = file.contents.toString(); + this.emit('data', file); + })); const license = gulp.src(['remote/LICENSE'], { base: 'remote', allowEmpty: true }); @@ -385,6 +401,12 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa ); } + result = inlineMeta(result, { + targetPaths: commonJSEntryPoints, + packageJsonFn: () => packageJsonContents, + productJsonFn: () => productJsonContents + }); + return result.pipe(vfs.dest(destination)); }; } @@ -416,17 +438,14 @@ function tweakProductForServerWeb(product) { }, commonJS: { src: 'out-build', - entryPoints: [ - 'out-build/server-main.js', - 'out-build/server-cli.js', - 'out-build/bootstrap-fork.js', - ], + entryPoints: commonJSEntryPoints, platform: 'node', external: [ 'minimist', - // TODO: we cannot inline `product.json` because + // We cannot inline `product.json` from here because // it is being changed during build time at a later // point in time (such as `checksums`) + // We have a manual step to inline these later. '../product.json', '../package.json' ] diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 5861cb5067a..8923954e12e 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -6,10 +6,7 @@ 'use strict'; const gulp = require('gulp'); -const merge = require('gulp-merge-json'); const fs = require('fs'); -const os = require('os'); -const cp = require('child_process'); const path = require('path'); const es = require('event-stream'); const vfs = require('vinyl-fs'); @@ -18,9 +15,11 @@ const replace = require('gulp-replace'); const filter = require('gulp-filter'); const util = require('./lib/util'); const { getVersion } = require('./lib/getVersion'); +const { date } = require('./lib/date'); const task = require('./lib/task'); const buildfile = require('../src/buildfile'); const optimize = require('./lib/optimize'); +const { inlineMeta } = require('./lib/inlineMeta'); const root = path.dirname(__dirname); const commit = getVersion(root); const packageJson = require('../package.json'); @@ -85,6 +84,12 @@ const windowBootstrapFiles = [ 'out-build/bootstrap-window.js' ]; +const commonJSEntryPoints = [ + 'out-build/main.js', + 'out-build/cli.js', + 'out-build/bootstrap-fork.js' +]; + const optimizeVSCodeTask = task.define('optimize-vscode', task.series( util.rimraf('out-vscode'), // Optimize: bundles source files automatically based on @@ -103,19 +108,16 @@ const optimizeVSCodeTask = task.define('optimize-vscode', task.series( }, commonJS: { src: 'out-build', - entryPoints: [ - 'out-build/main.js', - 'out-build/cli.js', - 'out-build/bootstrap-fork.js', - ], + entryPoints: commonJSEntryPoints, platform: 'node', external: [ 'electron', 'minimist', 'original-fs', - // TODO: we cannot inline `product.json` because + // We cannot inline `product.json` from here because // it is being changed during build time at a later // point in time (such as `checksums`) + // We have a manual step to inline these later. '../product.json', '../package.json', ] @@ -247,14 +249,21 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op packageJsonUpdates.desktopName = `${product.applicationName}-url-handler.desktop`; } + let packageJsonContents; const packageJsonStream = gulp.src(['package.json'], { base: '.' }) - .pipe(json(packageJsonUpdates)); - - const date = new Date().toISOString(); - const productJsonUpdate = { commit, date, checksums, version }; + .pipe(json(packageJsonUpdates)) + .pipe(es.through(function (file) { + packageJsonContents = file.contents.toString(); + this.emit('data', file); + })); + let productJsonContents; const productJsonStream = gulp.src(['product.json'], { base: '.' }) - .pipe(json(productJsonUpdate)); + .pipe(json({ commit, date, checksums, version })) + .pipe(es.through(function (file) { + productJsonContents = file.contents.toString(); + this.emit('data', file); + })); const license = gulp.src([product.licenseFileName, 'ThirdPartyNotices.txt', 'licenses/**'], { base: '.', allowEmpty: true }); @@ -387,6 +396,12 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op .pipe(rename('bin/' + product.applicationName))); } + result = inlineMeta(result, { + targetPaths: commonJSEntryPoints, + packageJsonFn: () => packageJsonContents, + productJsonFn: () => productJsonContents + }); + return result.pipe(vfs.dest(destination)); }; } diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 4480f5e1046..fe5adb45daf 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -12,12 +12,12 @@ const util = require('./lib/util'); const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const optimize = require('./lib/optimize'); +const { date } = require('./lib/date'); const product = require('../product.json'); const rename = require('gulp-rename'); const filter = require('gulp-filter'); const { getProductionDependencies } = require('./lib/dependencies'); const vfs = require('vinyl-fs'); -const replace = require('gulp-replace'); const packageJson = require('../package.json'); const { compileBuildTask } = require('./gulpfile.compile'); const extensions = require('./lib/extensions'); @@ -76,8 +76,6 @@ const vscodeWebEntryPoints = [ ].flat(); exports.vscodeWebEntryPoints = vscodeWebEntryPoints; -const buildDate = new Date().toISOString(); - /** * @param {object} product The parsed product.json file contents */ @@ -93,7 +91,7 @@ const createVSCodeWebProductConfigurationPatcher = (product) => { ...product, version, commit, - date: buildDate + date }); return content.replace('/*BUILD->INSERT_PRODUCT_CONFIGURATION*/', () => productConfiguration.substr(1, productConfiguration.length - 2) /* without { and }*/); } diff --git a/build/lib/date.js b/build/lib/date.js new file mode 100644 index 00000000000..a472e67eed4 --- /dev/null +++ b/build/lib/date.js @@ -0,0 +1,24 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.date = void 0; +function getRoundedBuildDate() { + const now = new Date(); + const minutes = now.getMinutes(); + if (minutes >= 30) { + now.setHours(now.getHours() + 1); + } + now.setMinutes(0, 0, 0); + return now; +} +/** + * An attempt to produce a stable date for the build that can be + * used across processes and build steps that run in parallel almost + * at the same time. The current time is rounded up or down to the + * closest hour. + */ +exports.date = getRoundedBuildDate(); +//# sourceMappingURL=date.js.map \ No newline at end of file diff --git a/build/lib/date.ts b/build/lib/date.ts new file mode 100644 index 00000000000..d1f1601e1c9 --- /dev/null +++ b/build/lib/date.ts @@ -0,0 +1,25 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +function getRoundedBuildDate() { + const now = new Date(); + + const minutes = now.getMinutes(); + if (minutes >= 30) { + now.setHours(now.getHours() + 1); + } + + now.setMinutes(0, 0, 0); + + return now; +} + +/** + * An attempt to produce a stable date for the build that can be + * used across processes and build steps that run in parallel almost + * at the same time. The current time is rounded up or down to the + * closest hour. + */ +export const date = getRoundedBuildDate(); diff --git a/build/lib/inlineMeta.js b/build/lib/inlineMeta.js new file mode 100644 index 00000000000..adcbfa9587e --- /dev/null +++ b/build/lib/inlineMeta.js @@ -0,0 +1,46 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +Object.defineProperty(exports, "__esModule", { value: true }); +exports.inlineMeta = inlineMeta; +const es = require("event-stream"); +const path_1 = require("path"); +const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; +const productJsonMarkerId = 'BUILD_INSERT_PRODUCT_CONFIGURATION'; +function inlineMeta(result, ctx) { + return result.pipe(es.through(function (file) { + if (matchesFile(file, ctx)) { + let content = file.contents.toString(); + let markerFound = false; + const packageMarker = `${packageJsonMarkerId}:"${packageJsonMarkerId}"`; // this needs to be the format after esbuild has processed the file (e.g. double quotes) + if (content.includes(packageMarker)) { + content = content.replace(packageMarker, JSON.stringify(JSON.parse(ctx.packageJsonFn())).slice(1, -1) /* trim braces */); + markerFound = true; + } + const productMarker = `${productJsonMarkerId}:"${productJsonMarkerId}"`; // this needs to be the format after esbuild has processed the file (e.g. double quotes) + if (content.includes(productMarker)) { + content = content.replace(productMarker, JSON.stringify(JSON.parse(ctx.productJsonFn())).slice(1, -1) /* trim braces */); + markerFound = true; + } + if (markerFound) { + file.contents = Buffer.from(content); + } + else if (content.includes(packageJsonMarkerId) || content.includes(productJsonMarkerId)) { + this.emit('error', new Error(`Unable to inline metadata because expected markers where not found in ${file.basename}.`)); + return; + } + } + this.emit('data', file); + })); +} +function matchesFile(file, ctx) { + for (const targetPath of ctx.targetPaths) { + if (file.basename === (0, path_1.basename)(targetPath)) { // TODO would be nicer to figure out root relative path to not match on false positives + return true; + } + } + return false; +} +//# sourceMappingURL=inlineMeta.js.map \ No newline at end of file diff --git a/build/lib/inlineMeta.ts b/build/lib/inlineMeta.ts new file mode 100644 index 00000000000..8f4c97d6635 --- /dev/null +++ b/build/lib/inlineMeta.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as es from 'event-stream'; +import { basename } from 'path'; +import * as File from 'vinyl'; + +export interface IInlineMetaContext { + readonly targetPaths: string[]; + readonly packageJsonFn: () => string; + readonly productJsonFn: () => string; +} + +const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; +const productJsonMarkerId = 'BUILD_INSERT_PRODUCT_CONFIGURATION'; + +export function inlineMeta(result: NodeJS.ReadWriteStream, ctx: IInlineMetaContext): NodeJS.ReadWriteStream { + return result.pipe(es.through(function (file: File) { + if (matchesFile(file, ctx)) { + let content = file.contents.toString(); + let markerFound = false; + + const packageMarker = `${packageJsonMarkerId}:"${packageJsonMarkerId}"`; // this needs to be the format after esbuild has processed the file (e.g. double quotes) + if (content.includes(packageMarker)) { + content = content.replace(packageMarker, JSON.stringify(JSON.parse(ctx.packageJsonFn())).slice(1, -1) /* trim braces */); + markerFound = true; + } + + const productMarker = `${productJsonMarkerId}:"${productJsonMarkerId}"`; // this needs to be the format after esbuild has processed the file (e.g. double quotes) + if (content.includes(productMarker)) { + content = content.replace(productMarker, JSON.stringify(JSON.parse(ctx.productJsonFn())).slice(1, -1) /* trim braces */); + markerFound = true; + } + + if (markerFound) { + file.contents = Buffer.from(content); + } else if (content.includes(packageJsonMarkerId) || content.includes(productJsonMarkerId)) { + this.emit('error', new Error(`Unable to inline metadata because expected markers where not found in ${file.basename}.`)); + return; + } + } + + this.emit('data', file); + })); +} + +function matchesFile(file: File, ctx: IInlineMetaContext): boolean { + for (const targetPath of ctx.targetPaths) { + if (file.basename === basename(targetPath)) { // TODO would be nicer to figure out root relative path to not match on false positives + return true; + } + } + return false; +} diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index aba8ee48fb6..27d15eb76d5 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -8,6 +8,7 @@ /** * @typedef {import('./vs/nls').INLSConfiguration} INLSConfiguration + * @import { IProductConfiguration } from './vs/base/common/product' */ // Store the node.js require function in a variable @@ -19,8 +20,8 @@ const nodeRequire = require; globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target, mod) => nodeRequire(String(mod)) }); // VSCODE_GLOBALS: package/product.json -/** @type Record */ -globalThis._VSCODE_PRODUCT_JSON = require('../product.json'); +/** @type Partial */ +globalThis._VSCODE_PRODUCT_JSON = require('./bootstrap-meta').product; if (process.env['VSCODE_DEV']) { // Patch product overrides when running out of sources try { @@ -29,7 +30,7 @@ if (process.env['VSCODE_DEV']) { globalThis._VSCODE_PRODUCT_JSON = Object.assign(globalThis._VSCODE_PRODUCT_JSON, overrides); } catch (error) { /* ignore */ } } -globalThis._VSCODE_PACKAGE_JSON = require('../package.json'); +globalThis._VSCODE_PACKAGE_JSON = require('./bootstrap-meta').pkg; // @ts-ignore const loader = require('./vs/loader'); diff --git a/src/bootstrap-meta.js b/src/bootstrap-meta.js new file mode 100644 index 00000000000..7924b77eec8 --- /dev/null +++ b/src/bootstrap-meta.js @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +//@ts-check +'use strict'; + +/** + * @import { IProductConfiguration } from './vs/base/common/product' + */ + +/** @type Partial & { BUILD_INSERT_PRODUCT_CONFIGURATION?: string } */ +let product = { BUILD_INSERT_PRODUCT_CONFIGURATION: 'BUILD_INSERT_PRODUCT_CONFIGURATION' }; // DO NOT MODIFY, PATCHED DURING BUILD +if (product['BUILD_INSERT_PRODUCT_CONFIGURATION']) { + // @ts-ignore + product = require('../product.json'); // Running out of sources +} + +/** @type object & { BUILD_INSERT_PACKAGE_CONFIGURATION?: string } */ +let pkg = { BUILD_INSERT_PACKAGE_CONFIGURATION: 'BUILD_INSERT_PACKAGE_CONFIGURATION' }; // DO NOT MODIFY, PATCHED DURING BUILD +if (pkg['BUILD_INSERT_PACKAGE_CONFIGURATION']) { + // @ts-ignore + pkg = require('../package.json'); // Running out of sources +} + +exports.product = product; +exports.pkg = pkg; diff --git a/src/cli.js b/src/cli.js index 686f7ab80c3..8191425eff9 100644 --- a/src/cli.js +++ b/src/cli.js @@ -6,10 +6,6 @@ //@ts-check 'use strict'; -/** - * @import { IProductConfiguration } from './vs/base/common/product' - */ - // Delete `VSCODE_CWD` very early even before // importing bootstrap files. We have seen // reports where `code .` would use the wrong @@ -20,9 +16,7 @@ delete process.env['VSCODE_CWD']; const bootstrap = require('./bootstrap'); const bootstrapNode = require('./bootstrap-node'); -/** @type {Partial} */ -// @ts-ignore -const product = require('../product.json'); +const product = require('./bootstrap-meta').product; const { resolveNLSConfiguration } = require('./vs/base/node/nls'); async function start() { diff --git a/src/main.js b/src/main.js index a0b8f09fff3..be7cd98afaf 100644 --- a/src/main.js +++ b/src/main.js @@ -7,7 +7,6 @@ 'use strict'; /** - * @import { IProductConfiguration } from './vs/base/common/product' * @import { INLSConfiguration } from './vs/nls' * @import { NativeParsedArgs } from './vs/platform/environment/common/argv' */ @@ -23,9 +22,7 @@ const bootstrapNode = require('./bootstrap-node'); const { getUserDataPath } = require('./vs/platform/environment/node/userDataPath'); const { parse } = require('./vs/base/common/jsonc'); const { getUNCHost, addUNCHostToAllowlist } = require('./vs/base/node/unc'); -/** @type {Partial} */ -// @ts-ignore -const product = require('../product.json'); +const product = require('./bootstrap-meta').product; const { app, protocol, crashReporter, Menu } = require('electron'); // Enable portable support diff --git a/src/server-cli.js b/src/server-cli.js index 3479ae3f1d6..aa041a66251 100644 --- a/src/server-cli.js +++ b/src/server-cli.js @@ -6,14 +6,8 @@ // @ts-check 'use strict'; -/** - * @import { IProductConfiguration } from './vs/base/common/product' - */ - const path = require('path'); -/** @type {Partial} */ -// @ts-ignore -const product = require('../product.json'); +const product = require('./bootstrap-meta').product; const { resolveNLSConfiguration } = require('./vs/base/node/nls'); async function start() { diff --git a/src/server-main.js b/src/server-main.js index 18f700a404f..e5a90596962 100644 --- a/src/server-main.js +++ b/src/server-main.js @@ -7,7 +7,6 @@ 'use strict'; /** - * @import { IProductConfiguration } from './vs/base/common/product' * @import { INLSConfiguration } from './vs/nls' */ @@ -17,9 +16,7 @@ const perf = require('./vs/base/common/performance'); const performance = require('perf_hooks').performance; -/** @type {Partial} */ -// @ts-ignore -const product = require('../product.json'); +const product = require('./bootstrap-meta').product; const readline = require('readline'); const http = require('http'); const { resolveNLSConfiguration } = require('./vs/base/node/nls'); From 42f6c855560f4c2c4e0cb2074b51c4223b7be4a3 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 4 Jul 2024 08:47:46 +0200 Subject: [PATCH 0264/2222] web - include `nls.messages.js` in standalone build (#219919) --- build/gulpfile.reh.js | 3 --- build/gulpfile.vscode.web.js | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 92d41b18485..07bf9ff825f 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -78,9 +78,6 @@ const serverWithWebResources = [ // Include all of server... ...serverResources, - // NLS - 'out-build/nls.messages.js', - // ...and all of web ...vscodeWebResourceIncludes ]; diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index fe5adb45daf..ad01b6cfb42 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -38,6 +38,9 @@ const vscodeWebResourceIncludes = [ 'out-build/vs/base/browser/ui/codicons/codicon/**/*.ttf', 'out-build/vs/**/markdown.css', + // NLS + 'out-build/nls.messages.js', + // Webview 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', 'out-build/vs/workbench/contrib/webview/browser/pre/*.html', From d69a8fb36d408b6bd89197668ba3b039d5f9bb91 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Thu, 4 Jul 2024 00:12:15 -0700 Subject: [PATCH 0265/2222] Align JS and TS snippets (#219917) Mostly should be the same with a few small differences --- .../snippets/javascript.code-snippets | 144 +++++++++++++----- .../snippets/typescript.code-snippets | 9 ++ 2 files changed, 112 insertions(+), 41 deletions(-) diff --git a/extensions/javascript/snippets/javascript.code-snippets b/extensions/javascript/snippets/javascript.code-snippets index ddf9a13b731..5bf6aa5edee 100644 --- a/extensions/javascript/snippets/javascript.code-snippets +++ b/extensions/javascript/snippets/javascript.code-snippets @@ -1,16 +1,79 @@ { - "define module": { - "prefix": "define", - "body": [ - "define([", - "\t'require',", - "\t'${1:dependency}'", - "], function(require, ${2:factory}) {", - "\t'use strict';", + "Constructor": { + "prefix": "ctor", + "body": [ + "/**", + " *", + " */", + "constructor() {", + "\tsuper();", "\t$0", - "});" + "}" + ], + "description": "Constructor" + }, + "Class Definition": { + "prefix": "class", + "isFileTemplate": true, + "body": [ + "class ${1:name} {", + "\tconstructor(${2:parameters}) {", + "\t\t$0", + "\t}", + "}" + ], + "description": "Class Definition" + }, + "Method Definition": { + "prefix": "method", + "body": [ + "/**", + " * ", + " */", + "${1:name}() {", + "\t$0", + "}" + ], + "description": "Method Definition" + }, + "Import Statement": { + "prefix": "import", + "body": [ + "import { $0 } from \"${1:module}\";" + ], + "description": "Import external module" + }, + "Log to the console": { + "prefix": "log", + "body": [ + "console.log($1);", + "$0" + ], + "description": "Log to the console" + }, + "Log warning to console": { + "prefix": "warn", + "body": [ + "console.warn($1);", + "$0" + ], + "description": "Log warning to the console" + }, + "Log error to console": { + "prefix": "error", + "body": [ + "console.error($1);", + "$0" + ], + "description": "Log error to the console" + }, + "Throw Exception": { + "prefix": "throw", + "body": [ + "throw new Error(\"$1\");", + "$0" ], - "description": "define module" + "description": "Throw Exception" }, "For Loop": { "prefix": "for", @@ -22,20 +85,20 @@ ], "description": "For Loop" }, - "For-Each Loop": { - "prefix": "foreach", + "For-Each Loop using =>": { + "prefix": "foreach =>", "body": [ "${1:array}.forEach(${2:element} => {", "\t$TM_SELECTED_TEXT$0", "});" ], - "description": "For-Each Loop" + "description": "For-Each Loop using =>" }, "For-In Loop": { "prefix": "forin", "body": [ "for (const ${1:key} in ${2:object}) {", - "\tif (Object.hasOwnProperty.call(${2:object}, ${1:key})) {", + "\tif (Object.prototype.hasOwnProperty.call(${2:object}, ${1:key})) {", "\t\tconst ${3:element} = ${2:object}[${1:key}];", "\t\t$TM_SELECTED_TEXT$0", "\t}", @@ -52,6 +115,15 @@ ], "description": "For-Of Loop" }, + "For-Await-Of Loop": { + "prefix": "forawaitof", + "body": [ + "for await (const ${1:element} of ${2:object}) {", + "\t$TM_SELECTED_TEXT$0", + "}" + ], + "description": "For-Await-Of Loop" + }, "Function Statement": { "prefix": "function", "body": [ @@ -149,13 +221,6 @@ ], "description": "Set Interval Function" }, - "Import Statement": { - "prefix": "import", - "body": [ - "import { $0 } from \"${1:module}\";" - ], - "description": "Import external module" - }, "Region Start": { "prefix": "#region", "body": [ @@ -170,34 +235,31 @@ ], "description": "Folding Region End" }, - "Log to the console": { - "prefix": "log", - "body": [ - "console.log($1);" - ], - "description": "Log to the console" - }, - "Log warning to console": { - "prefix": "warn", + "new Promise": { + "prefix": "newpromise", "body": [ - "console.warn($1);" + "new Promise((resolve, reject) => {", + "\t$TM_SELECTED_TEXT$0", + "})" ], - "description": "Log warning to the console" + "description": "Create a new Promise" }, - "Log error to console": { - "prefix": "error", + "Async Function Statement": { + "prefix": "async function", "body": [ - "console.error($1);" + "async function ${1:name}(${2:params}) {", + "\t$TM_SELECTED_TEXT$0", + "}" ], - "description": "Log error to the console" + "description": "Async Function Statement" }, - "new Promise": { - "prefix": "newpromise", + "Async Function Expression": { + "prefix": "async arrow function", "body": [ - "new Promise((resolve, reject) => {", + "async (${1:params}) => {", "\t$TM_SELECTED_TEXT$0", - "})" + "}" ], - "description": "Create a new Promise" + "description": "Async Function Expression" } } diff --git a/extensions/typescript-basics/snippets/typescript.code-snippets b/extensions/typescript-basics/snippets/typescript.code-snippets index 9d6dadcc2e6..9ed695795eb 100644 --- a/extensions/typescript-basics/snippets/typescript.code-snippets +++ b/extensions/typescript-basics/snippets/typescript.code-snippets @@ -266,6 +266,15 @@ ], "description": "Set Timeout Function" }, + "Set Interval Function": { + "prefix": "setinterval", + "body": [ + "setInterval(() => {", + "\t$TM_SELECTED_TEXT$0", + "}, ${1:interval});" + ], + "description": "Set Interval Function" + }, "Region Start": { "prefix": "#region", "body": [ From 859a0942630b74c73da4929a32e9f280f8c3b94a Mon Sep 17 00:00:00 2001 From: John Murray Date: Thu, 4 Jul 2024 09:33:30 +0100 Subject: [PATCH 0266/2222] Hide Local History commands when `"workbench.localHistory.enabled": false` (#212936) --- .../localHistory/browser/localHistoryCommands.ts | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/contrib/localHistory/browser/localHistoryCommands.ts b/src/vs/workbench/contrib/localHistory/browser/localHistoryCommands.ts index c768ab602f0..098357445d0 100644 --- a/src/vs/workbench/contrib/localHistory/browser/localHistoryCommands.ts +++ b/src/vs/workbench/contrib/localHistory/browser/localHistoryCommands.ts @@ -33,6 +33,7 @@ import { getLocalHistoryDateFormatter, LOCAL_HISTORY_ICON_RESTORE, LOCAL_HISTORY import { IPathService } from 'vs/workbench/services/path/common/pathService'; const LOCAL_HISTORY_CATEGORY = localize2('localHistory.category', 'Local History'); +const CTX_LOCAL_HISTORY_ENABLED = ContextKeyExpr.has('config.workbench.localHistory.enabled'); export interface ITimelineCommandArgument { uri: URI; @@ -316,7 +317,8 @@ registerAction2(class extends Action2 { id: 'workbench.action.localHistory.restoreViaPicker', title: localize2('localHistory.restoreViaPicker', 'Find Entry to Restore'), f1: true, - category: LOCAL_HISTORY_CATEGORY + category: LOCAL_HISTORY_CATEGORY, + precondition: CTX_LOCAL_HISTORY_ENABLED }); } async run(accessor: ServicesAccessor): Promise { @@ -402,7 +404,7 @@ registerAction2(class extends Action2 { } }); -MenuRegistry.appendMenuItem(MenuId.TimelineTitle, { command: { id: 'workbench.action.localHistory.restoreViaPicker', title: localize2('localHistory.restoreViaPickerMenu', 'Local History: Find Entry to Restore...') }, group: 'submenu', order: 1 }); +MenuRegistry.appendMenuItem(MenuId.TimelineTitle, { command: { id: 'workbench.action.localHistory.restoreViaPicker', title: localize2('localHistory.restoreViaPickerMenu', 'Local History: Find Entry to Restore...') }, group: 'submenu', order: 1, when: CTX_LOCAL_HISTORY_ENABLED }); //#endregion @@ -499,7 +501,8 @@ registerAction2(class extends Action2 { id: 'workbench.action.localHistory.deleteAll', title: localize2('localHistory.deleteAll', 'Delete All'), f1: true, - category: LOCAL_HISTORY_CATEGORY + category: LOCAL_HISTORY_CATEGORY, + precondition: CTX_LOCAL_HISTORY_ENABLED }); } async run(accessor: ServicesAccessor): Promise { @@ -534,7 +537,7 @@ registerAction2(class extends Action2 { title: localize2('localHistory.create', 'Create Entry'), f1: true, category: LOCAL_HISTORY_CATEGORY, - precondition: ActiveEditorContext + precondition: ContextKeyExpr.and(CTX_LOCAL_HISTORY_ENABLED, ActiveEditorContext) }); } async run(accessor: ServicesAccessor): Promise { From fe159c649e029cea4d34d9dac841ab5e9e373389 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 4 Jul 2024 11:02:31 +0200 Subject: [PATCH 0267/2222] show original enabled api proposals in the editor (#219928) --- .../extensionManagement/common/extensionsScannerService.ts | 1 + src/vs/platform/extensions/common/extensions.ts | 1 + .../services/extensions/common/extensionsProposedApi.ts | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index 55073608fa9..57831802fe4 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -674,6 +674,7 @@ class ExtensionsScanner extends Disposable { extension = this.validate(extension, input); } if (manifest.enabledApiProposals && (!this.environmentService.isBuilt || this.extensionsEnabledWithApiProposalVersion.includes(id.toLowerCase()))) { + manifest.originalEnabledApiProposals = manifest.enabledApiProposals; manifest.enabledApiProposals = parseEnabledApiProposalNames([...manifest.enabledApiProposals]); } return extension; diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index d0b69021dc2..822260bdc2f 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -271,6 +271,7 @@ export interface IRelaxedExtensionManifest { contributes?: IExtensionContributions; repository?: { url: string }; bugs?: { url: string }; + originalEnabledApiProposals?: readonly string[]; enabledApiProposals?: readonly string[]; api?: string; scripts?: { [key: string]: string }; diff --git a/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts b/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts index 76d560e77b1..8133ef3e43a 100644 --- a/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts +++ b/src/vs/workbench/services/extensions/common/extensionsProposedApi.ts @@ -120,11 +120,11 @@ class ApiProposalsMarkdowneRenderer extends Disposable implements IExtensionFeat readonly type = 'markdown'; shouldRender(manifest: IExtensionManifest): boolean { - return !!manifest.enabledApiProposals?.length; + return !!manifest.originalEnabledApiProposals?.length || !!manifest.enabledApiProposals?.length; } render(manifest: IExtensionManifest): IRenderedData { - const enabledApiProposals = manifest.enabledApiProposals || []; + const enabledApiProposals = manifest.originalEnabledApiProposals ?? manifest.enabledApiProposals ?? []; const data = new MarkdownString(); if (enabledApiProposals.length) { for (const proposal of enabledApiProposals) { From 633fbed04eb5263d36f9d36f0e912ff73180ef31 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 4 Jul 2024 11:08:07 +0200 Subject: [PATCH 0268/2222] update description (#219929) --- .../contrib/extensions/browser/extensionsWorkbenchService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 9c34630f65b..78abd2bffab 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -969,7 +969,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension properties: { [AutoRestartConfigurationKey]: { type: 'boolean', - description: nls.localize('autoRestart', "If activated, extensions will automatically restart following an update if the window is not in focus."), + description: nls.localize('autoRestart', "If activated, extensions will automatically restart following an update if the window is not in focus. There can be a data loss if you have open Notebooks or Custom Editors."), default: false, } } From 9c88ad7ebd4eff4ead004aee2b98630158f53b5f Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 4 Jul 2024 11:27:17 +0200 Subject: [PATCH 0269/2222] fix #219932 (#219933) --- .../extensionManagement/common/extensionManagementCLI.ts | 2 +- .../contrib/extensions/browser/extensions.contribution.ts | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts index 17c76f6fe70..3506d94d5c9 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementCLI.ts @@ -253,7 +253,7 @@ export class ExtensionManagementCLI { const valid = await this.validateVSIX(manifest, force, installOptions.profileLocation, installedExtensions); if (valid) { try { - await this.extensionManagementService.install(vsix, installOptions); + await this.extensionManagementService.install(vsix, { ...installOptions, installGivenVersion: true }); this.logger.info(localize('successVsixInstall', "Extension '{0}' was successfully installed.", basename(vsix))); } catch (error) { if (isCancellationError(error)) { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 0daeda950dc..697fafb73d8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -376,7 +376,7 @@ CommandsRegistry.registerCommand({ } } else { const vsix = URI.revive(arg); - await extensionsWorkbenchService.install(vsix, { installOnlyNewlyAddedFromExtensionPack: options?.installOnlyNewlyAddedFromExtensionPackVSIX }); + await extensionsWorkbenchService.install(vsix, { installOnlyNewlyAddedFromExtensionPack: options?.installOnlyNewlyAddedFromExtensionPackVSIX, installGivenVersion: true }); } } catch (e) { onUnexpectedError(e); @@ -818,7 +818,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi const notificationService = accessor.get(INotificationService); const vsixs = Array.isArray(resources) ? resources : [resources]; - const result = await Promise.allSettled(vsixs.map(async (vsix) => await extensionsWorkbenchService.install(vsix))); + const result = await Promise.allSettled(vsixs.map(async (vsix) => await extensionsWorkbenchService.install(vsix, { installGivenVersion: true }))); let error: Error | undefined, requireReload = false, requireRestart = false; for (const r of result) { if (r.status === 'rejected') { From acfda1578a3c486be75636963047e3057dd2b84a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 4 Jul 2024 11:54:03 +0200 Subject: [PATCH 0270/2222] fix #218061 (#219936) --- .../userDataSync/common/userDataSyncLocalStoreService.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/userDataSync/common/userDataSyncLocalStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncLocalStoreService.ts index 28d2400cfee..8c505b3aec7 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncLocalStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncLocalStoreService.ts @@ -53,7 +53,7 @@ export class UserDataSyncLocalStoreService extends Disposable implements IUserDa if (stat.children) { for (const child of stat.children) { - if (child.isDirectory && !this.userDataProfilesService.profiles.some(profile => profile.id === child.name)) { + if (child.isDirectory && !ALL_SYNC_RESOURCES.includes(child.name) && !this.userDataProfilesService.profiles.some(profile => profile.id === child.name)) { try { this.logService.info('Deleting non existing profile from backup', child.resource.path); await this.fileService.del(child.resource, { recursive: true }); From bf6ba5953fd1b98465c8bc1e97a9cfa0eae6211a Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 4 Jul 2024 12:39:17 +0200 Subject: [PATCH 0271/2222] do not reset local state when resource enablement has changed (#219938) --- src/vs/platform/userDataSync/common/userDataSyncService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index 856003580d0..a11d2cb04cc 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -702,8 +702,7 @@ class ProfileSynchronizer extends Disposable { const [[synchronizer, , disposable]] = this._enabled.splice(index, 1); disposable.dispose(); this.updateStatus(); - Promise.allSettled([synchronizer.stop(), synchronizer.resetLocal()]) - .then(null, error => this.logService.error(error)); + synchronizer.stop().then(null, error => this.logService.error(error)); } } From d9be968b0b2875e8e97de3b06f6e5628836fc99e Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 4 Jul 2024 12:49:57 +0200 Subject: [PATCH 0272/2222] Simply user snippets and tasks labels in all menus (#219939) --- .../contrib/snippets/browser/commands/configureSnippets.ts | 6 +++--- src/vs/workbench/contrib/tasks/browser/task.contribution.ts | 2 +- .../userDataProfile/browser/userDataProfilesEditor.ts | 4 ++-- .../services/userDataProfile/browser/tasksResource.ts | 2 +- .../browser/userDataProfileImportExportService.ts | 4 ++-- .../workbench/services/userDataSync/common/userDataSync.ts | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts index f0ea01793f2..198ec6d581b 100644 --- a/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts +++ b/src/vs/workbench/contrib/snippets/browser/commands/configureSnippets.ts @@ -227,10 +227,10 @@ export class ConfigureSnippetsAction extends SnippetsAction { constructor() { super({ id: 'workbench.action.openSnippets', - title: nls.localize2('openSnippet.label', "Configure User Snippets"), + title: nls.localize2('openSnippet.label', "Configure Snippets"), shortTitle: { - ...nls.localize2('userSnippets', "User Snippets"), - mnemonicTitle: nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "User &&Snippets"), + ...nls.localize2('userSnippets', "Snippets"), + mnemonicTitle: nls.localize({ key: 'miOpenSnippets', comment: ['&& denotes a mnemonic'] }, "&&Snippets"), }, f1: true, menu: [ diff --git a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts index ac6fdcba385..88059901e64 100644 --- a/src/vs/workbench/contrib/tasks/browser/task.contribution.ts +++ b/src/vs/workbench/contrib/tasks/browser/task.contribution.ts @@ -347,7 +347,7 @@ class UserTasksGlobalActionContribution extends Disposable implements IWorkbench private registerActions() { const id = 'workbench.action.tasks.openUserTasks'; - const title = nls.localize('userTasks', "User Tasks"); + const title = nls.localize('tasks', "Tasks"); this._register(MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { command: { id, diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts index b84e1af173b..f1075952370 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfilesEditor.ts @@ -937,9 +937,9 @@ class AbstractProfileResourceTreeRenderer extends Disposable { case ProfileResourceType.Keybindings: return localize('keybindings', "Keyboard Shortcuts"); case ProfileResourceType.Snippets: - return localize('snippets', "User Snippets"); + return localize('snippets', "Snippets"); case ProfileResourceType.Tasks: - return localize('tasks', "User Tasks"); + return localize('tasks', "Tasks"); case ProfileResourceType.Extensions: return localize('extensions', "Extensions"); } diff --git a/src/vs/workbench/services/userDataProfile/browser/tasksResource.ts b/src/vs/workbench/services/userDataProfile/browser/tasksResource.ts index 510e58bcfe0..c5ed415786a 100644 --- a/src/vs/workbench/services/userDataProfile/browser/tasksResource.ts +++ b/src/vs/workbench/services/userDataProfile/browser/tasksResource.ts @@ -84,7 +84,7 @@ export class TasksResourceTreeItem implements IProfileResourceTreeItem { readonly type = ProfileResourceType.Tasks; readonly handle = ProfileResourceType.Tasks; - readonly label = { label: localize('tasks', "User Tasks") }; + readonly label = { label: localize('tasks', "Tasks") }; readonly collapsibleState = TreeItemCollapsibleState.Expanded; checkbox: ITreeItemCheckboxState | undefined; diff --git a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts index 6a28cadf448..7c5bab40b87 100644 --- a/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts +++ b/src/vs/workbench/services/userDataProfile/browser/userDataProfileImportExportService.ts @@ -388,8 +388,8 @@ export class UserDataProfileImportExportService extends Disposable implements IU const settings: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Settings, label: localize('settings', "Settings"), picked: !profile?.useDefaultFlags?.settings }; const keybindings: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Keybindings, label: localize('keybindings', "Keyboard Shortcuts"), picked: !profile?.useDefaultFlags?.keybindings }; - const snippets: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Snippets, label: localize('snippets', "User Snippets"), picked: !profile?.useDefaultFlags?.snippets }; - const tasks: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Tasks, label: localize('tasks', "User Tasks"), picked: !profile?.useDefaultFlags?.tasks }; + const snippets: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Snippets, label: localize('snippets', "Snippets"), picked: !profile?.useDefaultFlags?.snippets }; + const tasks: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Tasks, label: localize('tasks', "Tasks"), picked: !profile?.useDefaultFlags?.tasks }; const extensions: IQuickPickItem & { id: ProfileResourceType } = { id: ProfileResourceType.Extensions, label: localize('extensions', "Extensions"), picked: !profile?.useDefaultFlags?.extensions }; const resources = [settings, keybindings, snippets, tasks, extensions]; diff --git a/src/vs/workbench/services/userDataSync/common/userDataSync.ts b/src/vs/workbench/services/userDataSync/common/userDataSync.ts index 9aa88c9e1d0..759011c2549 100644 --- a/src/vs/workbench/services/userDataSync/common/userDataSync.ts +++ b/src/vs/workbench/services/userDataSync/common/userDataSync.ts @@ -55,8 +55,8 @@ export function getSyncAreaLabel(source: SyncResource): string { switch (source) { case SyncResource.Settings: return localize('settings', "Settings"); case SyncResource.Keybindings: return localize('keybindings', "Keyboard Shortcuts"); - case SyncResource.Snippets: return localize('snippets', "User Snippets"); - case SyncResource.Tasks: return localize('tasks', "User Tasks"); + case SyncResource.Snippets: return localize('snippets', "Snippets"); + case SyncResource.Tasks: return localize('tasks', "Tasks"); case SyncResource.Extensions: return localize('extensions', "Extensions"); case SyncResource.GlobalState: return localize('ui state label', "UI State"); case SyncResource.Profiles: return localize('profiles', "Profiles"); From f94698b3ddd9565e743c9d2b9bd3123081d468a0 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 4 Jul 2024 12:56:55 +0200 Subject: [PATCH 0273/2222] Git - do not show progress while running Config and GetBranch operations (#219940) --- extensions/git/src/operation.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/extensions/git/src/operation.ts b/extensions/git/src/operation.ts index 223f1945b02..75f9405b3e3 100644 --- a/extensions/git/src/operation.ts +++ b/extensions/git/src/operation.ts @@ -141,7 +141,7 @@ export const Operation = { CheckoutTracking: (refLabel: string) => ({ kind: OperationKind.CheckoutTracking, blocking: true, readOnly: false, remote: false, retry: false, showProgress: true, refLabel } as CheckoutTrackingOperation), Clean: (showProgress: boolean) => ({ kind: OperationKind.Clean, blocking: false, readOnly: false, remote: false, retry: false, showProgress } as CleanOperation), Commit: { kind: OperationKind.Commit, blocking: true, readOnly: false, remote: false, retry: false, showProgress: true } as CommitOperation, - Config: (readOnly: boolean) => ({ kind: OperationKind.Config, blocking: false, readOnly, remote: false, retry: false, showProgress: true } as ConfigOperation), + Config: (readOnly: boolean) => ({ kind: OperationKind.Config, blocking: false, readOnly, remote: false, retry: false, showProgress: false } as ConfigOperation), DeleteBranch: { kind: OperationKind.DeleteBranch, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteBranchOperation, DeleteRef: { kind: OperationKind.DeleteRef, blocking: false, readOnly: false, remote: false, retry: false, showProgress: true } as DeleteRefOperation, DeleteRemoteTag: { kind: OperationKind.DeleteRemoteTag, blocking: false, readOnly: false, remote: true, retry: false, showProgress: true } as DeleteRemoteTagOperation, @@ -149,7 +149,7 @@ export const Operation = { Diff: { kind: OperationKind.Diff, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as DiffOperation, Fetch: (showProgress: boolean) => ({ kind: OperationKind.Fetch, blocking: false, readOnly: false, remote: true, retry: true, showProgress } as FetchOperation), FindTrackingBranches: { kind: OperationKind.FindTrackingBranches, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as FindTrackingBranchesOperation, - GetBranch: { kind: OperationKind.GetBranch, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetBranchOperation, + GetBranch: { kind: OperationKind.GetBranch, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetBranchOperation, GetBranches: { kind: OperationKind.GetBranches, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetBranchesOperation, GetCommitTemplate: { kind: OperationKind.GetCommitTemplate, blocking: false, readOnly: true, remote: false, retry: false, showProgress: true } as GetCommitTemplateOperation, GetObjectDetails: { kind: OperationKind.GetObjectDetails, blocking: false, readOnly: true, remote: false, retry: false, showProgress: false } as GetObjectDetailsOperation, From 58d9d8122825059596f072b95444e6df90d89ae7 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Thu, 4 Jul 2024 15:47:00 +0200 Subject: [PATCH 0274/2222] build - use ISO string of date as before (#219961) --- build/lib/date.js | 2 +- build/lib/date.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/lib/date.js b/build/lib/date.js index a472e67eed4..a3e2554c016 100644 --- a/build/lib/date.js +++ b/build/lib/date.js @@ -20,5 +20,5 @@ function getRoundedBuildDate() { * at the same time. The current time is rounded up or down to the * closest hour. */ -exports.date = getRoundedBuildDate(); +exports.date = getRoundedBuildDate().toISOString(); //# sourceMappingURL=date.js.map \ No newline at end of file diff --git a/build/lib/date.ts b/build/lib/date.ts index d1f1601e1c9..a68a4caf3df 100644 --- a/build/lib/date.ts +++ b/build/lib/date.ts @@ -22,4 +22,4 @@ function getRoundedBuildDate() { * at the same time. The current time is rounded up or down to the * closest hour. */ -export const date = getRoundedBuildDate(); +export const date = getRoundedBuildDate().toISOString(); From e852c335aac2aab40baf3931c391668f2dd1b202 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 4 Jul 2024 16:46:16 +0200 Subject: [PATCH 0275/2222] disable disable-eslint (#219967) --- src/vs/workbench/services/timer/browser/timerService.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/vs/workbench/services/timer/browser/timerService.ts b/src/vs/workbench/services/timer/browser/timerService.ts index df14ee528cc..486180f41f8 100644 --- a/src/vs/workbench/services/timer/browser/timerService.ts +++ b/src/vs/workbench/services/timer/browser/timerService.ts @@ -566,8 +566,7 @@ export abstract class AbstractTimerService implements ITimerService { const t1 = performance.now(); fib(24); const value = Math.round(performance.now() - t1); - // eslint-disable-next-line no-restricted-globals - postMessage({ value: tooSlow ? -1 : value }); + self.postMessage({ value: tooSlow ? -1 : value }); }).toString(); From 83c722d9a1ed9a2f05331d9e0671459c84bb3410 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Thu, 4 Jul 2024 17:12:06 +0200 Subject: [PATCH 0276/2222] =?UTF-8?q?Git=20-=20=F0=9F=92=84=20better=20err?= =?UTF-8?q?or=20handling=20and=20clean-up=20logging=20(#219975)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/main.ts | 16 +- extensions/git/src/model.ts | 202 ++++++++++++++------------ extensions/git/src/protocolHandler.ts | 14 +- 3 files changed, 126 insertions(+), 106 deletions(-) diff --git a/extensions/git/src/main.ts b/extensions/git/src/main.ts index c2d9b974be7..b3f6b6466fe 100644 --- a/extensions/git/src/main.ts +++ b/extensions/git/src/main.ts @@ -48,7 +48,7 @@ async function createModel(context: ExtensionContext, logger: LogOutputChannel, } const info = await findGit(pathHints, gitPath => { - logger.info(l10n.t('Validating found git in: "{0}"', gitPath)); + logger.info(l10n.t('[main] Validating found git in: "{0}"', gitPath)); if (excludes.length === 0) { return true; } @@ -56,7 +56,7 @@ async function createModel(context: ExtensionContext, logger: LogOutputChannel, const normalized = path.normalize(gitPath).replace(/[\r\n]+$/, ''); const skip = excludes.some(e => normalized.startsWith(e)); if (skip) { - logger.info(l10n.t('Skipped found git in: "{0}"', gitPath)); + logger.info(l10n.t('[main] Skipped found git in: "{0}"', gitPath)); } return !skip; }, logger); @@ -66,7 +66,7 @@ async function createModel(context: ExtensionContext, logger: LogOutputChannel, try { ipcServer = await createIPCServer(context.storagePath); } catch (err) { - logger.error(`Failed to create git IPC: ${err}`); + logger.error(`[main] Failed to create git IPC: ${err}`); } const askpass = new Askpass(ipcServer); @@ -79,7 +79,7 @@ async function createModel(context: ExtensionContext, logger: LogOutputChannel, const terminalEnvironmentManager = new TerminalEnvironmentManager(context, [askpass, gitEditor, ipcServer]); disposables.push(terminalEnvironmentManager); - logger.info(l10n.t('Using git "{0}" from "{1}"', info.version, info.path)); + logger.info(l10n.t('[main] Using git "{0}" from "{1}"', info.version, info.path)); const git = new Git({ gitPath: info.path, @@ -187,7 +187,7 @@ export async function _activate(context: ExtensionContext): Promise { - logger.appendLine(l10n.t('Log level: {0}', LogLevel[logLevel])); + logger.appendLine(l10n.t('[main] Log level: {0}', LogLevel[logLevel])); }; disposables.push(logger.onDidChangeLogLevel(onDidChangeLogLevel)); onDidChangeLogLevel(logger.logLevel); @@ -212,13 +212,13 @@ export async function _activate(context: ExtensionContext): Promise { + this.logger.info('[Model] Initial repository scan started'); + const config = workspace.getConfiguration('git'); const autoRepositoryDetection = config.get('autoRepositoryDetection'); const parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt'); + this.logger.trace(`[Model] Settings: autoRepositoryDetection=${autoRepositoryDetection}, openRepositoryInParentFolders=${parentRepositoryConfig}`); + // Initial repository scan function const initialScanFn = () => Promise.all([ this.onDidChangeWorkspaceFolders({ added: workspace.workspaceFolders || [], removed: [] }), @@ -321,6 +325,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } */ this.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length }); + this.logger.info(`[Model] Initial repository scan completed - repositories(${this.repositories.length}), closed repositories (${this.closedRepositories.length}), parent repositories (${this.parentRepositories.length}), unsafe repositories (${this.unsafeRepositories.length})`); } /** @@ -329,47 +334,51 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi * the git.repositoryScanMaxDepth setting. */ private async scanWorkspaceFolders(): Promise { - const config = workspace.getConfiguration('git'); - const autoRepositoryDetection = config.get('autoRepositoryDetection'); - this.logger.trace(`[swsf] Scan workspace sub folders. autoRepositoryDetection=${autoRepositoryDetection}`); + try { + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); - if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') { - return; - } + if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'subFolders') { + return; + } - await Promise.all((workspace.workspaceFolders || []).map(async folder => { - const root = folder.uri.fsPath; - this.logger.trace(`[swsf] Workspace folder: ${root}`); + await Promise.all((workspace.workspaceFolders || []).map(async folder => { + const root = folder.uri.fsPath; + this.logger.trace(`[Model] Workspace folder: ${root}`); - // Workspace folder children - const repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1); - const repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []); + // Workspace folder children + const repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1); + const repositoryScanIgnoredFolders = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanIgnoredFolders', []); - const subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders)); + const subfolders = new Set(await this.traverseWorkspaceFolder(root, repositoryScanMaxDepth, repositoryScanIgnoredFolders)); - // Repository scan folders - const scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || []; - this.logger.trace(`[swsf] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`); + // Repository scan folders + const scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || []; + this.logger.trace(`[Model] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`); - for (const scanPath of scanPaths) { - if (scanPath === '.git') { - this.logger.trace('[swsf] \'.git\' not supported in \'git.scanRepositories\' setting.'); - continue; - } + for (const scanPath of scanPaths) { + if (scanPath === '.git') { + this.logger.trace('[Model] \'.git\' not supported in \'git.scanRepositories\' setting.'); + continue; + } - if (path.isAbsolute(scanPath)) { - const notSupportedMessage = l10n.t('Absolute paths not supported in "git.scanRepositories" setting.'); - this.logger.warn(notSupportedMessage); - console.warn(notSupportedMessage); - continue; - } + if (path.isAbsolute(scanPath)) { + const notSupportedMessage = l10n.t('Absolute paths not supported in "git.scanRepositories" setting.'); + this.logger.warn(`[Model] ${notSupportedMessage}`); + console.warn(notSupportedMessage); + continue; + } - subfolders.add(path.join(root, scanPath)); - } + subfolders.add(path.join(root, scanPath)); + } - this.logger.trace(`[swsf] Workspace scan sub folders: [${[...subfolders].join(', ')}]`); - await Promise.all([...subfolders].map(f => this.openRepository(f))); - })); + this.logger.trace(`[Model] Workspace scan sub folders: [${[...subfolders].join(', ')}]`); + await Promise.all([...subfolders].map(f => this.openRepository(f))); + })); + } + catch (err) { + this.logger.warn(`[Model] scanWorkspaceFolders: ${err}`); + } } private async traverseWorkspaceFolder(workspaceFolder: string, maxDepth: number, repositoryScanIgnoredFolders: string[]): Promise { @@ -388,7 +397,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } } catch (err) { - this.logger.warn(`[swsf] Unable to read folder '${currentFolder.path}': ${err}`); + this.logger.warn(`[Model] Unable to read workspace folder '${currentFolder.path}': ${err}`); continue; } @@ -434,23 +443,28 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } private async onDidChangeWorkspaceFolders({ added, removed }: WorkspaceFoldersChangeEvent): Promise { - const possibleRepositoryFolders = added - .filter(folder => !this.getOpenRepository(folder.uri)); - - const activeRepositoriesList = window.visibleTextEditors - .map(editor => this.getRepository(editor.document.uri)) - .filter(repository => !!repository) as Repository[]; - - const activeRepositories = new Set(activeRepositoriesList); - const openRepositoriesToDispose = removed - .map(folder => this.getOpenRepository(folder.uri)) - .filter(r => !!r) - .filter(r => !activeRepositories.has(r!.repository)) - .filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[]; - - openRepositoriesToDispose.forEach(r => r.dispose()); - this.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); - await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath))); + try { + const possibleRepositoryFolders = added + .filter(folder => !this.getOpenRepository(folder.uri)); + + const activeRepositoriesList = window.visibleTextEditors + .map(editor => this.getRepository(editor.document.uri)) + .filter(repository => !!repository) as Repository[]; + + const activeRepositories = new Set(activeRepositoriesList); + const openRepositoriesToDispose = removed + .map(folder => this.getOpenRepository(folder.uri)) + .filter(r => !!r) + .filter(r => !activeRepositories.has(r!.repository)) + .filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[]; + + openRepositoriesToDispose.forEach(r => r.dispose()); + this.logger.trace(`[Model] Workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); + await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath))); + } + catch (err) { + this.logger.warn(`[Model] onDidChangeWorkspaceFolders: ${err}`); + } } private onDidChangeConfiguration(): void { @@ -463,50 +477,54 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi .filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true) .map(({ repository }) => repository); - this.logger.trace(`[swf] Scan workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); + this.logger.trace(`[Model] Workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath)); openRepositoriesToDispose.forEach(r => r.dispose()); } private async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise { - if (!workspace.isTrusted) { - this.logger.trace('[svte] Workspace is not trusted.'); - return; - } - - const config = workspace.getConfiguration('git'); - const autoRepositoryDetection = config.get('autoRepositoryDetection'); - this.logger.trace(`[svte] Scan visible text editors. autoRepositoryDetection=${autoRepositoryDetection}`); - - if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') { - return; - } - - await Promise.all(editors.map(async editor => { - const uri = editor.document.uri; - - if (uri.scheme !== 'file') { + try { + if (!workspace.isTrusted) { + this.logger.trace('[Model] Workspace is not trusted.'); return; } - const repository = this.getRepository(uri); + const config = workspace.getConfiguration('git'); + const autoRepositoryDetection = config.get('autoRepositoryDetection'); - if (repository) { - this.logger.trace(`[svte] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`); + if (autoRepositoryDetection !== true && autoRepositoryDetection !== 'openEditors') { return; } - this.logger.trace(`[svte] Open repository for editor resource ${uri.fsPath}`); - await this.openRepository(path.dirname(uri.fsPath)); - })); + await Promise.all(editors.map(async editor => { + const uri = editor.document.uri; + + if (uri.scheme !== 'file') { + return; + } + + const repository = this.getRepository(uri); + + if (repository) { + this.logger.trace(`[Model] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`); + return; + } + + this.logger.trace(`[Model] Open repository for editor resource ${uri.fsPath}`); + await this.openRepository(path.dirname(uri.fsPath)); + })); + } + catch (err) { + this.logger.warn(`[Model] onDidChangeVisibleTextEditors: ${err}`); + } } @sequentialize async openRepository(repoPath: string, openIfClosed = false): Promise { - this.logger.trace(`Opening repository: ${repoPath}`); + this.logger.trace(`[Model] Opening repository: ${repoPath}`); const existingRepository = await this.getRepositoryExact(repoPath); if (existingRepository) { - this.logger.trace(`Repository for path ${repoPath} already exists: ${existingRepository.root}`); + this.logger.trace(`[Model] Repository for path ${repoPath} already exists: ${existingRepository.root}`); return; } @@ -514,7 +532,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi const enabled = config.get('enabled') === true; if (!enabled) { - this.logger.trace('Git is not enabled'); + this.logger.trace('[Model] Git is not enabled'); return; } @@ -524,7 +542,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi fs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK); const result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']); if (result.stderr.trim() === '' && result.stdout.trim() === '') { - this.logger.trace(`Bare repository: ${repoPath}`); + this.logger.trace(`[Model] Bare repository: ${repoPath}`); return; } } catch { @@ -534,16 +552,16 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi try { const { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath); - this.logger.trace(`Repository root for path ${repoPath} is: ${repositoryRoot}`); + this.logger.trace(`[Model] Repository root for path ${repoPath} is: ${repositoryRoot}`); const existingRepository = await this.getRepositoryExact(repositoryRoot); if (existingRepository) { - this.logger.trace(`Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`); + this.logger.trace(`[Model] Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`); return; } if (this.shouldRepositoryBeIgnored(repositoryRoot)) { - this.logger.trace(`Repository for path ${repositoryRoot} is ignored`); + this.logger.trace(`[Model] Repository for path ${repositoryRoot} is ignored`); return; } @@ -552,7 +570,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi if (parentRepositoryConfig !== 'always' && this.globalState.get(`parentRepository:${repositoryRoot}`) !== true) { const isRepositoryOutsideWorkspace = await this.isRepositoryOutsideWorkspace(repositoryRoot); if (isRepositoryOutsideWorkspace) { - this.logger.trace(`Repository in parent folder: ${repositoryRoot}`); + this.logger.trace(`[Model] Repository in parent folder: ${repositoryRoot}`); if (!this._parentRepositoriesManager.hasRepository(repositoryRoot)) { // Show a notification if the parent repository is opened after the initial scan @@ -569,7 +587,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi // Handle unsafe repositories if (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) { - this.logger.trace(`Unsafe repository: ${repositoryRoot}`); + this.logger.trace(`[Model] Unsafe repository: ${repositoryRoot}`); // Show a notification if the unsafe repository is opened after the initial scan if (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) { @@ -583,7 +601,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi // Handle repositories that were closed by the user if (!openIfClosed && this._closedRepositoriesManager.isRepositoryClosed(repositoryRoot)) { - this.logger.trace(`Repository for path ${repositoryRoot} is closed`); + this.logger.trace(`[Model] Repository for path ${repositoryRoot} is closed`); return; } @@ -594,12 +612,14 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi this.open(repository); this._closedRepositoriesManager.deleteRepository(repository.root); + this.logger.info(`[Model] Opened repository: ${repository.root}`); + // Do not await this, we want SCM // to know about the repo asap repository.status(); } catch (err) { // noop - this.logger.trace(`Opening repository for path='${repoPath}' failed; ex=${err}`); + this.logger.trace(`[Model] Opening repository for path='${repoPath}' failed; ex=${err}`); } } @@ -631,7 +651,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi const repositoryRootRealPath = await fs.promises.realpath(repositoryRoot); return !pathEquals(repositoryRoot, repositoryRootRealPath) ? repositoryRootRealPath : undefined; } catch (err) { - this.logger.warn(`Failed to get repository realpath for: "${repositoryRoot}". ${err}`); + this.logger.warn(`[Model] Failed to get repository realpath for "${repositoryRoot}": ${err}`); return undefined; } } @@ -658,7 +678,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } private open(repository: Repository): void { - this.logger.info(`Open repository: ${repository.root}`); + this.logger.trace(`[Model] Open repository: ${repository.root}`); const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed); const disappearListener = onDidDisappearRepository(() => dispose()); @@ -675,7 +695,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi const checkForSubmodules = () => { if (!shouldDetectSubmodules) { - this.logger.trace('Automatic detection of git submodules is not enabled.'); + this.logger.trace('[Model] Automatic detection of git submodules is not enabled.'); return; } @@ -750,7 +770,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi return; } - this.logger.info(`Close repository: ${repository.root}`); + this.logger.info(`[Model] Close repository: ${repository.root}`); this._closedRepositoriesManager.addRepository(openRepository.repository.root); openRepository.dispose(); @@ -803,7 +823,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi return openRepositoryRealPath?.repository; } catch (err) { - this.logger.warn(`Failed to get repository realpath for: "${repoPath}". ${err}`); + this.logger.warn(`[Model] Failed to get repository realpath for: "${repoPath}". ${err}`); return undefined; } } @@ -989,7 +1009,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi this._workspaceFolders.set(workspaceFolder.uri.fsPath, result); } catch (err) { // noop - Workspace folder does not exist - this.logger.trace(`Failed to resolve workspace folder: "${workspaceFolder.uri.fsPath}". ${err}`); + this.logger.trace(`[Model] Failed to resolve workspace folder "${workspaceFolder.uri.fsPath}": ${err}`); } } diff --git a/extensions/git/src/protocolHandler.ts b/extensions/git/src/protocolHandler.ts index dc73fe39965..b65eac01a68 100644 --- a/extensions/git/src/protocolHandler.ts +++ b/extensions/git/src/protocolHandler.ts @@ -22,7 +22,7 @@ export class GitProtocolHandler implements UriHandler { } handleUri(uri: Uri): void { - this.logger.info(`GitProtocolHandler.handleUri(${uri.toString()})`); + this.logger.info(`[GitProtocolHandler] handleUri(${uri.toString()})`); switch (uri.path) { case '/clone': this.clone(uri); @@ -34,17 +34,17 @@ export class GitProtocolHandler implements UriHandler { const ref = data.ref; if (!data.url) { - this.logger.warn('Failed to open URI:' + uri.toString()); + this.logger.warn('[GitProtocolHandler] Failed to open URI:' + uri.toString()); return; } if (Array.isArray(data.url) && data.url.length === 0) { - this.logger.warn('Failed to open URI:' + uri.toString()); + this.logger.warn('[GitProtocolHandler] Failed to open URI:' + uri.toString()); return; } if (ref !== undefined && typeof ref !== 'string') { - this.logger.warn('Failed to open URI due to multiple references:' + uri.toString()); + this.logger.warn('[GitProtocolHandler] Failed to open URI due to multiple references:' + uri.toString()); return; } @@ -69,12 +69,12 @@ export class GitProtocolHandler implements UriHandler { } } catch (ex) { - this.logger.warn('Invalid URI:' + uri.toString()); + this.logger.warn('[GitProtocolHandler] Invalid URI:' + uri.toString()); return; } if (!(await commands.getCommands(true)).includes('git.clone')) { - this.logger.error('Could not complete git clone operation as git installation was not found.'); + this.logger.error('[GitProtocolHandler] Could not complete git clone operation as git installation was not found.'); const errorMessage = l10n.t('Could not clone your repository as Git is not installed.'); const downloadGit = l10n.t('Download Git'); @@ -86,7 +86,7 @@ export class GitProtocolHandler implements UriHandler { return; } else { const cloneTarget = cloneUri.toString(true); - this.logger.info(`Executing git.clone for ${cloneTarget}`); + this.logger.info(`[GitProtocolHandler] Executing git.clone for ${cloneTarget}`); commands.executeCommand('git.clone', cloneTarget, undefined, { ref: ref }); } } From 3ce0d742a353b0df2e867b4d4fd531bba4a77395 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Thu, 4 Jul 2024 17:22:57 +0200 Subject: [PATCH 0277/2222] fix #219977 (#219978) --- .../common/configurationRegistry.ts | 32 ++++++++++++------- .../test/common/configurationRegistry.test.ts | 22 +++++++++++-- .../test/common/configurations.test.ts | 4 +-- .../api/common/configurationExtensionPoint.ts | 8 ++++- .../debug/browser/debug.contribution.ts | 3 +- .../browser/configurationService.ts | 18 ++++++++--- 6 files changed, 66 insertions(+), 21 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 98ab6dc12d8..2a93b106815 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -191,6 +191,11 @@ export interface IConfigurationPropertySchema extends IJSONSchema { */ disallowSyncIgnore?: boolean; + /** + * Disallow extensions to contribute configuration default value for this setting. + */ + disallowConfigurationDefault?: boolean; + /** * Labels for enumeration items */ @@ -233,12 +238,11 @@ export interface IConfigurationNode { restrictedProperties?: string[]; } -export type ConfigurationDefaultSource = IExtensionInfo | string; -export type ConfigurationDefaultValueSource = ConfigurationDefaultSource | Map; +export type ConfigurationDefaultValueSource = IExtensionInfo | Map; export interface IConfigurationDefaults { overrides: IStringDictionary; - source?: ConfigurationDefaultSource; + source?: IExtensionInfo; } export type IRegisteredConfigurationPropertySchema = IConfigurationPropertySchema & { @@ -369,7 +373,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { if (source) { let objectConfigurationSources = valuesSources.get(configuration); if (!objectConfigurationSources) { - objectConfigurationSources = new Map(); + objectConfigurationSources = new Map(); valuesSources.set(configuration, objectConfigurationSources); } if (!(objectConfigurationSources instanceof Map)) { @@ -398,7 +402,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for the {0} language.", plainKey), $ref: resourceLanguageSettingsSchemaId, defaultDefaultValue: defaultValue, - source: types.isString(source) ? undefined : source, + source, defaultValueSource: source }; overrideIdentifiers.push(...overrideIdentifiersFromKey(key)); @@ -425,7 +429,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { newDefaultValue = { ...existingDefaultValue, ...newDefaultValue }; - newDefaultValueSource = existingDefaultOverride?.source ?? new Map(); + newDefaultValueSource = existingDefaultOverride?.source ?? new Map(); if (!(newDefaultValueSource instanceof Map)) { console.error('defaultValueSource is not a Map'); continue; @@ -464,7 +468,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { for (const { overrides, source } of defaultConfigurations) { for (const key in overrides) { - const id = types.isString(source) ? source : source?.id; + const id = source?.id; const configurationDefaultsOverride = this.configurationDefaultsOverrides.get(key); if (!configurationDefaultsOverride) { @@ -476,7 +480,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { const overrideValue = overrides[key][configuration]; if (types.isObject(overrideValue)) { - const configurationSource = configurationDefaultsOverride.valuesSources?.get(configuration) as Map | undefined; + const configurationSource = configurationDefaultsOverride.valuesSources?.get(configuration) as Map | undefined; for (const overrideObjectKey of Object.keys(overrideValue)) { const keySource = configurationSource?.get(overrideObjectKey); @@ -529,7 +533,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { } // Otherwise, remove the default value if the source matches else { - const configurationDefaultsOverrideSourceId = types.isString(configurationDefaultsOverride.source) ? configurationDefaultsOverride.source : configurationDefaultsOverride.source?.id; + const configurationDefaultsOverrideSourceId = configurationDefaultsOverride.source?.id; if (id !== configurationDefaultsOverrideSourceId) { continue; // Another source is overriding this default value } @@ -802,8 +806,14 @@ class ConfigurationRegistry implements IConfigurationRegistry { private updatePropertyDefaultValue(key: string, property: IRegisteredConfigurationPropertySchema): void { const configurationdefaultOverride = this.configurationDefaultsOverrides.get(key); - let defaultValue = configurationdefaultOverride?.value; - let defaultSource = configurationdefaultOverride?.source; + let defaultValue = undefined; + let defaultSource = undefined; + if (configurationdefaultOverride + && (!property.disallowConfigurationDefault || !configurationdefaultOverride.source) // Prevent overriding the default value if the property is disallowed to be overridden by configuration defaults from extensions + ) { + defaultValue = configurationdefaultOverride.value; + defaultSource = configurationdefaultOverride.source; + } if (types.isUndefined(defaultValue)) { defaultValue = property.defaultDefaultValue; defaultSource = undefined; diff --git a/src/vs/platform/configuration/test/common/configurationRegistry.test.ts b/src/vs/platform/configuration/test/common/configurationRegistry.test.ts index 24dc735cd11..ec7fcb5e8b6 100644 --- a/src/vs/platform/configuration/test/common/configurationRegistry.test.ts +++ b/src/vs/platform/configuration/test/common/configurationRegistry.test.ts @@ -31,6 +31,24 @@ suite('ConfigurationRegistry', () => { assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { a: 2, c: 3 }); }); + test('configuration override defaults - prevent overriding default value', async () => { + configurationRegistry.registerConfiguration({ + 'id': '_test_default', + 'type': 'object', + 'properties': { + 'config.preventDefaultValueOverride': { + 'type': 'object', + default: { a: 0 }, + 'disallowConfigurationDefault': true + } + } + }); + + configurationRegistry.registerDefaultConfigurations([{ overrides: { 'config.preventDefaultValueOverride': { a: 1, b: 2 } } }]); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config.preventDefaultValueOverride'].default, { a: 0 }); + }); + test('configuration override defaults - merges defaults', async () => { configurationRegistry.registerDefaultConfigurations([{ overrides: { '[lang]': { a: 1, b: 2 } } }]); configurationRegistry.registerDefaultConfigurations([{ overrides: { '[lang]': { a: 2, c: 3 } } }]); @@ -91,8 +109,8 @@ suite('ConfigurationRegistry', () => { } }); - const overrides1 = [{ overrides: { 'config': { a: 1, b: 2 } }, source: 'source1' }]; - const overrides2 = [{ overrides: { 'config': { a: 2, c: 3 } }, source: 'source2' }]; + const overrides1 = [{ overrides: { 'config': { a: 1, b: 2 } }, source: { id: 'source1', displayName: 'source1' } }]; + const overrides2 = [{ overrides: { 'config': { a: 2, c: 3 } }, source: { id: 'source2', displayName: 'source2' } }]; configurationRegistry.registerDefaultConfigurations(overrides1); configurationRegistry.registerDefaultConfigurations(overrides2); diff --git a/src/vs/platform/configuration/test/common/configurations.test.ts b/src/vs/platform/configuration/test/common/configurations.test.ts index eb02aec4f9a..63f8d33bfee 100644 --- a/src/vs/platform/configuration/test/common/configurations.test.ts +++ b/src/vs/platform/configuration/test/common/configurations.test.ts @@ -386,7 +386,7 @@ suite('DefaultConfiguration', () => { } } }, - source: 'source1' + source: { id: 'source1', displayName: 'source1' } }; const node2 = { @@ -398,7 +398,7 @@ suite('DefaultConfiguration', () => { } } }, - source: 'source2' + source: { id: 'source2', displayName: 'source2' } }; configurationRegistry.registerDefaultConfigurations([node1]); configurationRegistry.registerDefaultConfigurations([node2]); diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index b5a56b231e8..8b7c408c766 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -160,11 +160,17 @@ defaultConfigurationExtPoint.setHandler((extensions, { added, removed }) => { const addedDefaultConfigurations = added.map(extension => { const overrides: IStringDictionary = objects.deepClone(extension.value); for (const key of Object.keys(overrides)) { + const registeredPropertyScheme = registeredProperties[key]; + if (registeredPropertyScheme?.disallowConfigurationDefault) { + extension.collector.warn(nls.localize('config.property.preventDefaultConfiguration.warning', "Cannot register configuration defaults for '{0}'. This setting does not allow contributing configuration defaults.", key)); + delete overrides[key]; + continue; + } if (!OVERRIDE_PROPERTY_REGEX.test(key)) { - const registeredPropertyScheme = registeredProperties[key]; if (registeredPropertyScheme?.scope && !allowedScopes.includes(registeredPropertyScheme.scope)) { extension.collector.warn(nls.localize('config.property.defaultConfiguration.warning', "Cannot register configuration defaults for '{0}'. Only defaults for machine-overridable, window, resource and language overridable scoped settings are supported.", key)); delete overrides[key]; + continue; } } } diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index 6806be01e94..b85da3db794 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -560,7 +560,8 @@ configurationRegistry.registerConfiguration({ type: 'object', description: nls.localize({ comment: ['This is the description for a setting'], key: 'launch' }, "Global debug launch configuration. Should be used as an alternative to 'launch.json' that is shared across workspaces."), default: { configurations: [], compounds: [] }, - $ref: launchSchemaId + $ref: launchSchemaId, + disallowConfigurationDefault: true }, 'debug.focusWindowOnBreak': { type: 'boolean', diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index b44f1e12235..c1bac6f8691 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -1260,9 +1260,9 @@ class RegisterConfigurationSchemasContribution extends Disposable implements IWo type: 'object', description: localize('configurationDefaults.description', 'Contribute defaults for configurations'), properties: Object.assign({}, - machineOverridableSettings.properties, - windowSettings.properties, - resourceSettings.properties + this.filterDefaultOverridableProperties(machineOverridableSettings.properties), + this.filterDefaultOverridableProperties(windowSettings.properties), + this.filterDefaultOverridableProperties(resourceSettings.properties) ), patternProperties: { [OVERRIDE_PROPERTY_PATTERN]: { @@ -1316,6 +1316,16 @@ class RegisterConfigurationSchemasContribution extends Disposable implements IWo }); return result; } + + private filterDefaultOverridableProperties(properties: IStringDictionary): IStringDictionary { + const result: IStringDictionary = {}; + Object.entries(properties).forEach(([key, value]) => { + if (!value.disallowConfigurationDefault) { + result[key] = value; + } + }); + return result; + } } class ResetConfigurationDefaultsOverridesCache extends Disposable implements IWorkbenchContribution { @@ -1365,7 +1375,7 @@ class UpdateExperimentalSettingsDefaults extends Disposable implements IWorkbenc } catch (error) {/*ignore */ } } if (Object.keys(overrides).length) { - this.configurationRegistry.registerDefaultConfigurations([{ overrides, source: localize('experimental', "Experiments") }]); + this.configurationRegistry.registerDefaultConfigurations([{ overrides }]); } } } From 6864b950ef0483bf4a2d85fd83559c76b26ec1e0 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 4 Jul 2024 08:40:48 -0700 Subject: [PATCH 0278/2222] Skip flaky terminal tabs test Part of #216564 --- test/smoke/src/areas/terminal/terminal-tabs.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/smoke/src/areas/terminal/terminal-tabs.test.ts b/test/smoke/src/areas/terminal/terminal-tabs.test.ts index cbe672bbc1f..4ff156c6069 100644 --- a/test/smoke/src/areas/terminal/terminal-tabs.test.ts +++ b/test/smoke/src/areas/terminal/terminal-tabs.test.ts @@ -36,7 +36,8 @@ export function setup() { await terminal.assertSingleTab({ name }); }); - it('should reset the tab name to the default value when no name is provided', async () => { + // TODO: Flaky https://github.com/microsoft/vscode/issues/216564 + it.skip('should reset the tab name to the default value when no name is provided', async () => { await terminal.createTerminal(); const defaultName = await terminal.getSingleTabName(); const name = 'my terminal name'; From 1cdc3cdcb3cdc56e47e72afaef955e31ad300fa6 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 4 Jul 2024 08:55:58 -0700 Subject: [PATCH 0279/2222] Always make room in terminal gutter even is shell integration is off Fixes #219970 --- .../contrib/terminal/browser/media/terminal.css | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index c3ee3fb42d7..b531e31130f 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -73,8 +73,8 @@ } .monaco-workbench .xterm { - /* All terminals have at least 10px left/right edge padding and 2 padding on the bottom (so underscores on last line are visible */ - padding: 0 10px 2px; + /* All terminals have at least 20px left, 10px right edge padding and 2 padding on the bottom (so underscores on last line are visible) */ + padding: 0 10px 2px 20px; } .monaco-workbench .terminal-editor .xterm, @@ -130,15 +130,6 @@ .xterm.xterm-cursor-pointer .xterm-screen { cursor: pointer; } .xterm.column-select.focus .xterm-screen { cursor: crosshair; } -.monaco-workbench .terminal-editor .xterm { - padding-left: 20px !important; -} - -.monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:first-child .xterm, -.integrated-terminal.shell-integration .xterm { - padding-left: 20px !important; -} - .monaco-workbench .terminal-editor .terminal-group .monaco-split-view2.horizontal .split-view-view:last-child .xterm, .monaco-workbench .pane-body.integrated-terminal .terminal-group .monaco-split-view2.horizontal .split-view-view:last-child .xterm { padding-right: 20px; From 60c991e9ef45f94c84a1b9ada4557da40077cf74 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Thu, 4 Jul 2024 09:01:56 -0700 Subject: [PATCH 0280/2222] xterm@5.6.0-beta.36 Fixes #219979 --- package.json | 16 +++--- remote/package.json | 16 +++--- remote/web/package.json | 14 +++--- remote/web/yarn.lock | 66 ++++++++++++------------- remote/yarn.lock | 76 ++++++++++++++-------------- yarn.lock | 107 +++++++++++++++------------------------- 6 files changed, 135 insertions(+), 160 deletions(-) diff --git a/package.json b/package.json index 92e269b5a3f..bfec4be89e0 100644 --- a/package.json +++ b/package.json @@ -82,14 +82,14 @@ "@vscode/windows-mutex": "^0.5.0", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "0.2.0-beta.4", - "@xterm/addon-image": "0.9.0-beta.21", - "@xterm/addon-search": "0.16.0-beta.21", - "@xterm/addon-serialize": "0.14.0-beta.21", - "@xterm/addon-unicode11": "0.9.0-beta.21", - "@xterm/addon-webgl": "0.19.0-beta.21", - "@xterm/headless": "5.6.0-beta.21", - "@xterm/xterm": "5.6.0-beta.21", + "@xterm/addon-clipboard": "0.2.0-beta.19", + "@xterm/addon-image": "0.9.0-beta.36", + "@xterm/addon-search": "0.16.0-beta.36", + "@xterm/addon-serialize": "0.14.0-beta.36", + "@xterm/addon-unicode11": "0.9.0-beta.36", + "@xterm/addon-webgl": "0.19.0-beta.36", + "@xterm/headless": "5.6.0-beta.36", + "@xterm/xterm": "5.6.0-beta.36", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.2", diff --git a/remote/package.json b/remote/package.json index 5b0e80182ea..e39eb2215d2 100644 --- a/remote/package.json +++ b/remote/package.json @@ -14,14 +14,14 @@ "@vscode/vscode-languagedetection": "1.0.21", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "0.2.0-beta.4", - "@xterm/addon-image": "0.9.0-beta.21", - "@xterm/addon-search": "0.16.0-beta.21", - "@xterm/addon-serialize": "0.14.0-beta.21", - "@xterm/addon-unicode11": "0.9.0-beta.21", - "@xterm/addon-webgl": "0.19.0-beta.21", - "@xterm/headless": "5.6.0-beta.21", - "@xterm/xterm": "5.6.0-beta.21", + "@xterm/addon-clipboard": "0.2.0-beta.19", + "@xterm/addon-image": "0.9.0-beta.36", + "@xterm/addon-search": "0.16.0-beta.36", + "@xterm/addon-serialize": "0.14.0-beta.36", + "@xterm/addon-unicode11": "0.9.0-beta.36", + "@xterm/addon-webgl": "0.19.0-beta.36", + "@xterm/headless": "5.6.0-beta.36", + "@xterm/xterm": "5.6.0-beta.36", "cookie": "^0.4.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", diff --git a/remote/web/package.json b/remote/web/package.json index a6c63532460..320980c9761 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -7,13 +7,13 @@ "@microsoft/1ds-post-js": "^3.2.13", "@vscode/iconv-lite-umd": "0.7.0", "@vscode/vscode-languagedetection": "1.0.21", - "@xterm/addon-clipboard": "0.2.0-beta.4", - "@xterm/addon-image": "0.9.0-beta.21", - "@xterm/addon-search": "0.16.0-beta.21", - "@xterm/addon-serialize": "0.14.0-beta.21", - "@xterm/addon-unicode11": "0.9.0-beta.21", - "@xterm/addon-webgl": "0.19.0-beta.21", - "@xterm/xterm": "5.6.0-beta.21", + "@xterm/addon-clipboard": "0.2.0-beta.19", + "@xterm/addon-image": "0.9.0-beta.36", + "@xterm/addon-search": "0.16.0-beta.36", + "@xterm/addon-serialize": "0.14.0-beta.36", + "@xterm/addon-unicode11": "0.9.0-beta.36", + "@xterm/addon-webgl": "0.19.0-beta.36", + "@xterm/xterm": "5.6.0-beta.36", "jschardet": "3.1.2", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 3a330eae061..1eb4c160597 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -48,42 +48,42 @@ resolved "https://registry.yarnpkg.com/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz#89b48f293f6aa3341bb888c1118d16ff13b032d3" integrity sha512-zSUH9HYCw5qsCtd7b31yqkpaCU6jhtkKLkvOOA8yTrIRfBSOFb8PPhgmMicD7B/m+t4PwOJXzU1XDtrM9Fd3/g== -"@xterm/addon-clipboard@0.2.0-beta.4": - version "0.2.0-beta.4" - resolved "https://registry.yarnpkg.com/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.4.tgz#9911baaebfbc07a698ae62366a596bfdeac8fa7e" - integrity sha512-p2KGTFUDK4YFthCgfsv2wT66JDTZPcIuoWeDT+TmSFbS1smDPTMCyM/rDDkGY+duHRcQsIMVzGC+2NRb/exX6A== +"@xterm/addon-clipboard@0.2.0-beta.19": + version "0.2.0-beta.19" + resolved "https://registry.yarnpkg.com/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.19.tgz#da2ea7a0d6e51383d4a21cbb04fb7fbd9db7d853" + integrity sha512-A/NxJQoOq21kE1ykZ07Cw3IxD5cQFxba1iMxnSFvWGVC71ZdHGwUveLeY8nHWEL8PfLsZxAgIzlMTfWgfkQ+CA== dependencies: js-base64 "^3.7.5" -"@xterm/addon-image@0.9.0-beta.21": - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.21.tgz#64fe50ee623f3e518574e1cbbe649cc0c0d60265" - integrity sha512-kTArrrS7K5+WYTTO8Ktt1aYxKTO4/jUm3KmyvPVjf9iw7OhLtG9mU+X9dXo56DTAqmbIUfJgY3OQbWffcyNk7w== - -"@xterm/addon-search@0.16.0-beta.21": - version "0.16.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.16.0-beta.21.tgz#b8a20e83c1ff24afa675c3723244b2068255688d" - integrity sha512-RVn8yRx+w6R7abWiIttyAR0+Myh+XCYOLAkwco3iIYgzlztmox3Qp6YNzWJj0G8iwSvzxaSu7Fbjbb2PXTOSIg== - -"@xterm/addon-serialize@0.14.0-beta.21": - version "0.14.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.21.tgz#a074c5fdd2105c07574e6848babefef2905d84cb" - integrity sha512-Eg1QT2WG0pAIV+RrPv921+dVQvQqMhoFv2DQfMYDcqNbD2mTvIbX/ecEMb1bmn3WI0jNNomQ8UHZRFNRbDA+BA== - -"@xterm/addon-unicode11@0.9.0-beta.21": - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.21.tgz#dc843df701e518bc459e77dcd4fd65fe49adbb4b" - integrity sha512-IiHYZ+88m5MCoAyOHWQ4xXzecOh6FsDDr8lZpJktbFHyzYjBlIDQ6z9cJg+3ApApfo5Xosnmzjs27kf7wG2L0w== - -"@xterm/addon-webgl@0.19.0-beta.21": - version "0.19.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.21.tgz#68b92a47bf6768babd57bfbaf3ac97a7c670d8df" - integrity sha512-YV8Aaxp4QokXXehSCJ7NvudZKPDyBiXv4HqENqDpQllCj4hOWC5xJYSoFoPtu5+UhlzfqqvYRX/Il7QegPFPDg== - -"@xterm/xterm@5.6.0-beta.21": - version "5.6.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.6.0-beta.21.tgz#87a4e45752e5708cffc5c583d7f15e107313eb4e" - integrity sha512-1tLJaGudNSg1hEC+ZwUU7PiUvzURzKB5v1IRaJdmZK81ZCxvEF6Qfo281pTZsZFnv2iOWqFEC0C5uRmBXLm0lQ== +"@xterm/addon-image@0.9.0-beta.36": + version "0.9.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.36.tgz#79024103c48f4e401ca15afe49fad4f3834c023c" + integrity sha512-m8c5OfJBzPYfv90mSgc0bX/P+qUsgczVajHW+kE59UoC311ng13IlCg6a4bJHb2EHqGsq19fIrYCn6+JsMdRsQ== + +"@xterm/addon-search@0.16.0-beta.36": + version "0.16.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.16.0-beta.36.tgz#22deda3250552f24de05f8112299d15f3fe90f01" + integrity sha512-lN66vYpKvNBxbvtJXLbuidirirmIzySXnl8JvarcrDaw4HlqluOvvjEdVYKofWV5ZGSaPfIAijwJW1f0KjUhJw== + +"@xterm/addon-serialize@0.14.0-beta.36": + version "0.14.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.36.tgz#1407c13fe1bd869ad4f26e7b7da4e7fa87442021" + integrity sha512-6KpzHlQIuHakPv70dKhQp8f6e9hk4q1fNuuTD1rEzDg8DeKRfUDjorw1vPkKTB/DD+3zaMUBtg7DFVVEi+/+Cw== + +"@xterm/addon-unicode11@0.9.0-beta.36": + version "0.9.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.36.tgz#158dcdd707a466958a256a960e5d9a967a97a9dc" + integrity sha512-BKP2ml0fYOHnfaTp0LorSluNXjHRSEwf3yrD3K6jEZfYTBePhee1TAxOdNH/TdqwNYZYaYHaK87A5mSuYpKPBQ== + +"@xterm/addon-webgl@0.19.0-beta.36": + version "0.19.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.36.tgz#8926a0434e5ce74eee12a965c06cd5f601391f18" + integrity sha512-bJA1enVNlIMRkBU9i7i8qX26Zs2/CrGedREW5WI0NZUAn0IHlatWlj3aOfTuI2MYWUPGE8ul30PyipYP6P+fmA== + +"@xterm/xterm@5.6.0-beta.36": + version "5.6.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.6.0-beta.36.tgz#fd0fd598b67e3bcba61a59bb1a33b131ad86eea3" + integrity sha512-YtFKQIggbvV2brWifksZAtLi447j0DFdoSRoq4vQi/N7KFC0pguGdG3YzYkDOyqoeLMPu569e2b5oevMe6d2aQ== js-base64@^3.7.5: version "3.7.7" diff --git a/remote/yarn.lock b/remote/yarn.lock index 54318a1a762..9b0e1ec44b5 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -122,47 +122,47 @@ resolved "https://registry.yarnpkg.com/@vscode/windows-registry/-/windows-registry-1.1.0.tgz#03dace7c29c46f658588b9885b9580e453ad21f9" integrity sha512-5AZzuWJpGscyiMOed0IuyEwt6iKmV5Us7zuwCDCFYMIq7tsvooO9BUiciywsvuthGz6UG4LSpeDeCxvgMVhnIw== -"@xterm/addon-clipboard@0.2.0-beta.4": - version "0.2.0-beta.4" - resolved "https://registry.yarnpkg.com/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.4.tgz#9911baaebfbc07a698ae62366a596bfdeac8fa7e" - integrity sha512-p2KGTFUDK4YFthCgfsv2wT66JDTZPcIuoWeDT+TmSFbS1smDPTMCyM/rDDkGY+duHRcQsIMVzGC+2NRb/exX6A== +"@xterm/addon-clipboard@0.2.0-beta.19": + version "0.2.0-beta.19" + resolved "https://registry.yarnpkg.com/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.19.tgz#da2ea7a0d6e51383d4a21cbb04fb7fbd9db7d853" + integrity sha512-A/NxJQoOq21kE1ykZ07Cw3IxD5cQFxba1iMxnSFvWGVC71ZdHGwUveLeY8nHWEL8PfLsZxAgIzlMTfWgfkQ+CA== dependencies: js-base64 "^3.7.5" -"@xterm/addon-image@0.9.0-beta.21": - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.21.tgz#64fe50ee623f3e518574e1cbbe649cc0c0d60265" - integrity sha512-kTArrrS7K5+WYTTO8Ktt1aYxKTO4/jUm3KmyvPVjf9iw7OhLtG9mU+X9dXo56DTAqmbIUfJgY3OQbWffcyNk7w== - -"@xterm/addon-search@0.16.0-beta.21": - version "0.16.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.16.0-beta.21.tgz#b8a20e83c1ff24afa675c3723244b2068255688d" - integrity sha512-RVn8yRx+w6R7abWiIttyAR0+Myh+XCYOLAkwco3iIYgzlztmox3Qp6YNzWJj0G8iwSvzxaSu7Fbjbb2PXTOSIg== - -"@xterm/addon-serialize@0.14.0-beta.21": - version "0.14.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.21.tgz#a074c5fdd2105c07574e6848babefef2905d84cb" - integrity sha512-Eg1QT2WG0pAIV+RrPv921+dVQvQqMhoFv2DQfMYDcqNbD2mTvIbX/ecEMb1bmn3WI0jNNomQ8UHZRFNRbDA+BA== - -"@xterm/addon-unicode11@0.9.0-beta.21": - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.21.tgz#dc843df701e518bc459e77dcd4fd65fe49adbb4b" - integrity sha512-IiHYZ+88m5MCoAyOHWQ4xXzecOh6FsDDr8lZpJktbFHyzYjBlIDQ6z9cJg+3ApApfo5Xosnmzjs27kf7wG2L0w== - -"@xterm/addon-webgl@0.19.0-beta.21": - version "0.19.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.21.tgz#68b92a47bf6768babd57bfbaf3ac97a7c670d8df" - integrity sha512-YV8Aaxp4QokXXehSCJ7NvudZKPDyBiXv4HqENqDpQllCj4hOWC5xJYSoFoPtu5+UhlzfqqvYRX/Il7QegPFPDg== - -"@xterm/headless@5.6.0-beta.21": - version "5.6.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/headless/-/headless-5.6.0-beta.21.tgz#110fa33b59f4bf2d1de188e318bb944c8d774e97" - integrity sha512-RtKsv7KZb/ee8hwkvMNYuUofDoBR/KWUjoB5mo10C+dHyDJcMYiG2k48cAvcaJRjPH721iOELORKQk3NAlowkg== - -"@xterm/xterm@5.6.0-beta.21": - version "5.6.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.6.0-beta.21.tgz#87a4e45752e5708cffc5c583d7f15e107313eb4e" - integrity sha512-1tLJaGudNSg1hEC+ZwUU7PiUvzURzKB5v1IRaJdmZK81ZCxvEF6Qfo281pTZsZFnv2iOWqFEC0C5uRmBXLm0lQ== +"@xterm/addon-image@0.9.0-beta.36": + version "0.9.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.36.tgz#79024103c48f4e401ca15afe49fad4f3834c023c" + integrity sha512-m8c5OfJBzPYfv90mSgc0bX/P+qUsgczVajHW+kE59UoC311ng13IlCg6a4bJHb2EHqGsq19fIrYCn6+JsMdRsQ== + +"@xterm/addon-search@0.16.0-beta.36": + version "0.16.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.16.0-beta.36.tgz#22deda3250552f24de05f8112299d15f3fe90f01" + integrity sha512-lN66vYpKvNBxbvtJXLbuidirirmIzySXnl8JvarcrDaw4HlqluOvvjEdVYKofWV5ZGSaPfIAijwJW1f0KjUhJw== + +"@xterm/addon-serialize@0.14.0-beta.36": + version "0.14.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.36.tgz#1407c13fe1bd869ad4f26e7b7da4e7fa87442021" + integrity sha512-6KpzHlQIuHakPv70dKhQp8f6e9hk4q1fNuuTD1rEzDg8DeKRfUDjorw1vPkKTB/DD+3zaMUBtg7DFVVEi+/+Cw== + +"@xterm/addon-unicode11@0.9.0-beta.36": + version "0.9.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.36.tgz#158dcdd707a466958a256a960e5d9a967a97a9dc" + integrity sha512-BKP2ml0fYOHnfaTp0LorSluNXjHRSEwf3yrD3K6jEZfYTBePhee1TAxOdNH/TdqwNYZYaYHaK87A5mSuYpKPBQ== + +"@xterm/addon-webgl@0.19.0-beta.36": + version "0.19.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.36.tgz#8926a0434e5ce74eee12a965c06cd5f601391f18" + integrity sha512-bJA1enVNlIMRkBU9i7i8qX26Zs2/CrGedREW5WI0NZUAn0IHlatWlj3aOfTuI2MYWUPGE8ul30PyipYP6P+fmA== + +"@xterm/headless@5.6.0-beta.36": + version "5.6.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/headless/-/headless-5.6.0-beta.36.tgz#cf3e690024019eac2e22d87e0e9f04da6e99cfa9" + integrity sha512-X0Te4ssxcVZ3/YlYEjzN+4w5e4f3Ni/kdjBUKoyZSRpA1+Er54HC/I3t1jc4amqI9xysnVwhq+Ey+LjygIfALw== + +"@xterm/xterm@5.6.0-beta.36": + version "5.6.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.6.0-beta.36.tgz#fd0fd598b67e3bcba61a59bb1a33b131ad86eea3" + integrity sha512-YtFKQIggbvV2brWifksZAtLi447j0DFdoSRoq4vQi/N7KFC0pguGdG3YzYkDOyqoeLMPu569e2b5oevMe6d2aQ== agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" diff --git a/yarn.lock b/yarn.lock index 466eaf63f25..d3ccc9b18cb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1976,47 +1976,47 @@ resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== -"@xterm/addon-clipboard@0.2.0-beta.4": - version "0.2.0-beta.4" - resolved "https://registry.yarnpkg.com/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.4.tgz#9911baaebfbc07a698ae62366a596bfdeac8fa7e" - integrity sha512-p2KGTFUDK4YFthCgfsv2wT66JDTZPcIuoWeDT+TmSFbS1smDPTMCyM/rDDkGY+duHRcQsIMVzGC+2NRb/exX6A== +"@xterm/addon-clipboard@0.2.0-beta.19": + version "0.2.0-beta.19" + resolved "https://registry.yarnpkg.com/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.19.tgz#da2ea7a0d6e51383d4a21cbb04fb7fbd9db7d853" + integrity sha512-A/NxJQoOq21kE1ykZ07Cw3IxD5cQFxba1iMxnSFvWGVC71ZdHGwUveLeY8nHWEL8PfLsZxAgIzlMTfWgfkQ+CA== dependencies: js-base64 "^3.7.5" -"@xterm/addon-image@0.9.0-beta.21": - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.21.tgz#64fe50ee623f3e518574e1cbbe649cc0c0d60265" - integrity sha512-kTArrrS7K5+WYTTO8Ktt1aYxKTO4/jUm3KmyvPVjf9iw7OhLtG9mU+X9dXo56DTAqmbIUfJgY3OQbWffcyNk7w== - -"@xterm/addon-search@0.16.0-beta.21": - version "0.16.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.16.0-beta.21.tgz#b8a20e83c1ff24afa675c3723244b2068255688d" - integrity sha512-RVn8yRx+w6R7abWiIttyAR0+Myh+XCYOLAkwco3iIYgzlztmox3Qp6YNzWJj0G8iwSvzxaSu7Fbjbb2PXTOSIg== - -"@xterm/addon-serialize@0.14.0-beta.21": - version "0.14.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.21.tgz#a074c5fdd2105c07574e6848babefef2905d84cb" - integrity sha512-Eg1QT2WG0pAIV+RrPv921+dVQvQqMhoFv2DQfMYDcqNbD2mTvIbX/ecEMb1bmn3WI0jNNomQ8UHZRFNRbDA+BA== - -"@xterm/addon-unicode11@0.9.0-beta.21": - version "0.9.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.21.tgz#dc843df701e518bc459e77dcd4fd65fe49adbb4b" - integrity sha512-IiHYZ+88m5MCoAyOHWQ4xXzecOh6FsDDr8lZpJktbFHyzYjBlIDQ6z9cJg+3ApApfo5Xosnmzjs27kf7wG2L0w== - -"@xterm/addon-webgl@0.19.0-beta.21": - version "0.19.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.21.tgz#68b92a47bf6768babd57bfbaf3ac97a7c670d8df" - integrity sha512-YV8Aaxp4QokXXehSCJ7NvudZKPDyBiXv4HqENqDpQllCj4hOWC5xJYSoFoPtu5+UhlzfqqvYRX/Il7QegPFPDg== - -"@xterm/headless@5.6.0-beta.21": - version "5.6.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/headless/-/headless-5.6.0-beta.21.tgz#110fa33b59f4bf2d1de188e318bb944c8d774e97" - integrity sha512-RtKsv7KZb/ee8hwkvMNYuUofDoBR/KWUjoB5mo10C+dHyDJcMYiG2k48cAvcaJRjPH721iOELORKQk3NAlowkg== - -"@xterm/xterm@5.6.0-beta.21": - version "5.6.0-beta.21" - resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.6.0-beta.21.tgz#87a4e45752e5708cffc5c583d7f15e107313eb4e" - integrity sha512-1tLJaGudNSg1hEC+ZwUU7PiUvzURzKB5v1IRaJdmZK81ZCxvEF6Qfo281pTZsZFnv2iOWqFEC0C5uRmBXLm0lQ== +"@xterm/addon-image@0.9.0-beta.36": + version "0.9.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-image/-/addon-image-0.9.0-beta.36.tgz#79024103c48f4e401ca15afe49fad4f3834c023c" + integrity sha512-m8c5OfJBzPYfv90mSgc0bX/P+qUsgczVajHW+kE59UoC311ng13IlCg6a4bJHb2EHqGsq19fIrYCn6+JsMdRsQ== + +"@xterm/addon-search@0.16.0-beta.36": + version "0.16.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-search/-/addon-search-0.16.0-beta.36.tgz#22deda3250552f24de05f8112299d15f3fe90f01" + integrity sha512-lN66vYpKvNBxbvtJXLbuidirirmIzySXnl8JvarcrDaw4HlqluOvvjEdVYKofWV5ZGSaPfIAijwJW1f0KjUhJw== + +"@xterm/addon-serialize@0.14.0-beta.36": + version "0.14.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.36.tgz#1407c13fe1bd869ad4f26e7b7da4e7fa87442021" + integrity sha512-6KpzHlQIuHakPv70dKhQp8f6e9hk4q1fNuuTD1rEzDg8DeKRfUDjorw1vPkKTB/DD+3zaMUBtg7DFVVEi+/+Cw== + +"@xterm/addon-unicode11@0.9.0-beta.36": + version "0.9.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.36.tgz#158dcdd707a466958a256a960e5d9a967a97a9dc" + integrity sha512-BKP2ml0fYOHnfaTp0LorSluNXjHRSEwf3yrD3K6jEZfYTBePhee1TAxOdNH/TdqwNYZYaYHaK87A5mSuYpKPBQ== + +"@xterm/addon-webgl@0.19.0-beta.36": + version "0.19.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.36.tgz#8926a0434e5ce74eee12a965c06cd5f601391f18" + integrity sha512-bJA1enVNlIMRkBU9i7i8qX26Zs2/CrGedREW5WI0NZUAn0IHlatWlj3aOfTuI2MYWUPGE8ul30PyipYP6P+fmA== + +"@xterm/headless@5.6.0-beta.36": + version "5.6.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/headless/-/headless-5.6.0-beta.36.tgz#cf3e690024019eac2e22d87e0e9f04da6e99cfa9" + integrity sha512-X0Te4ssxcVZ3/YlYEjzN+4w5e4f3Ni/kdjBUKoyZSRpA1+Er54HC/I3t1jc4amqI9xysnVwhq+Ey+LjygIfALw== + +"@xterm/xterm@5.6.0-beta.36": + version "5.6.0-beta.36" + resolved "https://registry.yarnpkg.com/@xterm/xterm/-/xterm-5.6.0-beta.36.tgz#fd0fd598b67e3bcba61a59bb1a33b131ad86eea3" + integrity sha512-YtFKQIggbvV2brWifksZAtLi447j0DFdoSRoq4vQi/N7KFC0pguGdG3YzYkDOyqoeLMPu569e2b5oevMe6d2aQ== "@xtuc/ieee754@^1.2.0": version "1.2.0" @@ -9467,7 +9467,7 @@ streamx@^2.18.0: optionalDependencies: bare-events "^2.2.0" -"string-width-cjs@npm:string-width@^4.2.0": +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9502,15 +9502,6 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -9564,7 +9555,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9592,13 +9583,6 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" -strip-ansi@^6.0.0, strip-ansi@^6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -10763,7 +10747,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10798,15 +10782,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From d8ddc28be0e1da789d0a59580af4d50fde07d55d Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Thu, 4 Jul 2024 09:37:08 -0700 Subject: [PATCH 0281/2222] cli: honor --connection-token-file in serve-web (#219041) net: gracefully disconnect better from remote This adds in a step to gracefully disconnect (send a "disconnect" message and then await the flush) before closing the workbench. In some cases, like Remote - SSH, sending messages is more async than others. In the exec server connection we handle a `zlib.flate` stream to compress data to and from the VS Code server, and its API is asynchronous, so this lets us ensure the stream is drained before giving the go-ahead to close up shop This lifecycle phase is a little awkward and I ended it putting it directly in the lifecycle service: we don't want to do this in `onWillShutdown` because some contributions want to save data to the remote workspace, and if shutdown is vetoed it would leave a broken state. But `onDidShutdown` is synchronous and already depended upon by several other points, and changing that also felt a bit risky. cc @alexdima Refs #211462, will require some small adoption in Remote - SSH to close. --- .../lifecycle/common/lifecycleService.ts | 8 +++---- .../electron-sandbox/lifecycleService.ts | 24 +++++++++++++++++-- .../remote/browser/remoteAgentService.ts | 2 +- .../common/abstractRemoteAgentService.ts | 22 ++++++++--------- 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/services/lifecycle/common/lifecycleService.ts b/src/vs/workbench/services/lifecycle/common/lifecycleService.ts index 09eee478155..92006c0a577 100644 --- a/src/vs/workbench/services/lifecycle/common/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/common/lifecycleService.ts @@ -3,13 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from 'vs/base/common/event'; import { Barrier } from 'vs/base/common/async'; +import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { ILifecycleService, WillShutdownEvent, StartupKind, LifecyclePhase, LifecyclePhaseToString, ShutdownReason, BeforeShutdownErrorEvent, InternalBeforeShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; -import { ILogService } from 'vs/platform/log/common/log'; import { mark } from 'vs/base/common/performance'; +import { ILogService } from 'vs/platform/log/common/log'; import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from 'vs/platform/storage/common/storage'; +import { BeforeShutdownErrorEvent, ILifecycleService, InternalBeforeShutdownEvent, LifecyclePhase, LifecyclePhaseToString, ShutdownReason, StartupKind, WillShutdownEvent } from 'vs/workbench/services/lifecycle/common/lifecycle'; export abstract class AbstractLifecycleService extends Disposable implements ILifecycleService { @@ -44,7 +44,7 @@ export abstract class AbstractLifecycleService extends Disposable implements ILi constructor( @ILogService protected readonly logService: ILogService, - @IStorageService protected readonly storageService: IStorageService + @IStorageService protected readonly storageService: IStorageService, ) { super(); diff --git a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts index 0434e6ded59..90da4ac5afa 100644 --- a/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts +++ b/src/vs/workbench/services/lifecycle/electron-sandbox/lifecycleService.ts @@ -11,19 +11,22 @@ import { ILogService } from 'vs/platform/log/common/log'; import { AbstractLifecycleService } from 'vs/workbench/services/lifecycle/common/lifecycleService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { INativeHostService } from 'vs/platform/native/common/native'; -import { Promises, disposableTimeout, raceCancellation } from 'vs/base/common/async'; +import { Promises, disposableTimeout, raceCancellation, timeout } from 'vs/base/common/async'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; export class NativeLifecycleService extends AbstractLifecycleService { private static readonly BEFORE_SHUTDOWN_WARNING_DELAY = 5000; private static readonly WILL_SHUTDOWN_WARNING_DELAY = 800; + private static readonly MAX_GRACEFUL_REMOTE_DISCONNECT_TIME = 3000; constructor( @INativeHostService private readonly nativeHostService: INativeHostService, @IStorageService storageService: IStorageService, - @ILogService logService: ILogService + @ILogService logService: ILogService, + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, ) { super(logService, storageService); @@ -66,6 +69,10 @@ export class NativeLifecycleService extends AbstractLifecycleService { // trigger onWillShutdown events and joining await this.handleWillShutdown(reply.reason); + // now that all services have stored their data, it's safe to terminate + // the remote connection gracefully before synchronously saying we're shut down + await this.handleRemoteAgentDisconnect(); + // trigger onDidShutdown event now that we know we will quit this._onDidShutdown.fire(); @@ -74,6 +81,19 @@ export class NativeLifecycleService extends AbstractLifecycleService { }); } + private async handleRemoteAgentDisconnect(): Promise { + const longRunningWarning = disposableTimeout(() => { + this.logService.warn(`[lifecycle] the remote agent is taking a long time to disconnect, waiting...`); + }, NativeLifecycleService.BEFORE_SHUTDOWN_WARNING_DELAY); + + await Promise.race([ + this.remoteAgentService.endConnection(), + timeout(NativeLifecycleService.MAX_GRACEFUL_REMOTE_DISCONNECT_TIME), + ]); + + longRunningWarning.dispose(); + } + protected async handleBeforeShutdown(reason: ShutdownReason): Promise { const logService = this.logService; diff --git a/src/vs/workbench/services/remote/browser/remoteAgentService.ts b/src/vs/workbench/services/remote/browser/remoteAgentService.ts index ee12dd5b070..b28ab9c941b 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentService.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentService.ts @@ -27,7 +27,7 @@ export class RemoteAgentService extends AbstractRemoteAgentService implements IR @IProductService productService: IProductService, @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, @ISignService signService: ISignService, - @ILogService logService: ILogService + @ILogService logService: ILogService, ) { super(remoteSocketFactoryService, userDataProfileService, environmentService, productService, remoteAuthorityResolverService, signService, logService); } diff --git a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts index fe802daf50b..914c0a91081 100644 --- a/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts +++ b/src/vs/workbench/services/remote/common/abstractRemoteAgentService.ts @@ -3,23 +3,23 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IChannel, IServerChannel, getDelayedChannel, IPCLogger } from 'vs/base/parts/ipc/common/ipc'; +import { getDelayedChannel, IChannel, IPCLogger, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client } from 'vs/base/parts/ipc/common/ipc.net'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IDiagnosticInfo, IDiagnosticInfoOptions } from 'vs/platform/diagnostics/common/diagnostics'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IProductService } from 'vs/platform/product/common/productService'; import { connectRemoteAgentManagement, IConnectionOptions, ManagementPersistentConnection, PersistentConnectionEvent } from 'vs/platform/remote/common/remoteAgentConnection'; -import { IExtensionHostExitInfo, IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IRemoteAgentEnvironment, RemoteAgentConnectionContext } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; -import { RemoteAgentConnectionContext, IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; -import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/remote/common/remoteAgentEnvironmentChannel'; -import { IDiagnosticInfoOptions, IDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; -import { Emitter } from 'vs/base/common/event'; +import { IRemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; import { ISignService } from 'vs/platform/sign/common/sign'; -import { ILogService } from 'vs/platform/log/common/log'; import { ITelemetryData, TelemetryLevel } from 'vs/platform/telemetry/common/telemetry'; -import { IProductService } from 'vs/platform/product/common/productService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { RemoteExtensionEnvironmentChannelClient } from 'vs/workbench/services/remote/common/remoteAgentEnvironmentChannel'; +import { IExtensionHostExitInfo, IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; -import { IRemoteSocketFactoryService } from 'vs/platform/remote/common/remoteSocketFactoryService'; export abstract class AbstractRemoteAgentService extends Disposable implements IRemoteAgentService { @@ -35,7 +35,7 @@ export abstract class AbstractRemoteAgentService extends Disposable implements I @IProductService productService: IProductService, @IRemoteAuthorityResolverService private readonly _remoteAuthorityResolverService: IRemoteAuthorityResolverService, @ISignService signService: ISignService, - @ILogService logService: ILogService + @ILogService logService: ILogService, ) { super(); if (this._environmentService.remoteAuthority) { From 63e93de60858653c647c17f9e30b41abf2dac4a3 Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 5 Jul 2024 02:00:07 +0900 Subject: [PATCH 0282/2222] fix: use xcode >= 15.1 to address broken builds on macOS <= 12 (#219991) --- build/.cachesalt | 2 +- build/azure-pipelines/darwin/product-build-darwin.yml | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/build/.cachesalt b/build/.cachesalt index 4a19f329eb1..d7d415d3213 100644 --- a/build/.cachesalt +++ b/build/.cachesalt @@ -1 +1 @@ -2024-05-25T03:29:59.419Z +2024-07-04T16:31:11.121Z diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 8b4bda1c6a2..11f69d735ac 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -78,6 +78,14 @@ steps: - script: | set -e + # Refs https://github.com/microsoft/vscode/issues/219893#issuecomment-2209313109 + sudo xcode-select --switch /Applications/Xcode_15.2.app + condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + displayName: Switch to Xcode >= 15.1 + + - script: | + set -e + c++ --version python3 -m pip install setuptools for i in {1..5}; do # try 5 times From 6eaf6487a4d8301b981036bfa53976546eb6694f Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Thu, 4 Jul 2024 19:28:48 +0200 Subject: [PATCH 0283/2222] make sure to reset known embeddings models (#219994) --- src/vs/workbench/api/common/extHostEmbedding.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/api/common/extHostEmbedding.ts b/src/vs/workbench/api/common/extHostEmbedding.ts index f99cd387c62..4c712aee9e8 100644 --- a/src/vs/workbench/api/common/extHostEmbedding.ts +++ b/src/vs/workbench/api/common/extHostEmbedding.ts @@ -39,6 +39,7 @@ export class ExtHostEmbeddings implements ExtHostEmbeddingsShape { this._provider.set(handle, { id: embeddingsModel, provider }); return toDisposable(() => { + this._allKnownModels.delete(embeddingsModel); this._proxy.$unregisterEmbeddingProvider(handle); this._provider.delete(handle); }); From 82104a3a6cf8713b81e5bbd97960dbf5c82a816a Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Thu, 4 Jul 2024 23:30:30 +0200 Subject: [PATCH 0284/2222] Proxy Basic Auth --- src/vs/code/electron-main/app.ts | 7 +- src/vs/platform/native/common/native.ts | 2 + .../native}/electron-main/auth.ts | 137 ++++++++++-------- .../electron-main/nativeHostMainService.ts | 9 +- .../request/browser/requestService.ts | 6 +- src/vs/platform/request/common/request.ts | 16 ++ src/vs/platform/request/common/requestIpc.ts | 7 +- .../platform/request/node/requestService.ts | 6 +- .../test/common/userDataSyncClient.ts | 3 +- .../common/userDataSyncStoreService.test.ts | 1 + .../api/browser/mainThreadWorkspace.ts | 6 +- .../workbench/api/common/extHost.protocol.ts | 2 + .../workbench/api/common/extHostWorkspace.ts | 6 + src/vs/workbench/api/node/proxyResolver.ts | 30 +++- .../electron-sandbox/requestService.ts | 6 +- .../electron-sandbox/workbenchTestServices.ts | 2 + 16 files changed, 173 insertions(+), 73 deletions(-) rename src/vs/{code => platform/native}/electron-main/auth.ts (64%) diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 890691d24ef..ca405dd725d 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -25,7 +25,7 @@ import { getDelayedChannel, ProxyChannel, StaticRouter } from 'vs/base/parts/ipc import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc.electron'; import { Client as MessagePortClient } from 'vs/base/parts/ipc/electron-main/ipc.mp'; import { Server as NodeIPCServer } from 'vs/base/parts/ipc/node/ipc.net'; -import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; +import { IProxyAuthService, ProxyAuthService } from 'vs/platform/native/electron-main/auth'; import { localize } from 'vs/nls'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; @@ -586,7 +586,7 @@ export class CodeApplication extends Disposable { const appInstantiationService = await this.initServices(machineId, sqmId, devDeviceId, sharedProcessReady); // Auth Handler - this._register(appInstantiationService.createInstance(ProxyAuthHandler)); + appInstantiationService.invokeFunction(accessor => accessor.get(IProxyAuthService)); // Transient profiles handler this._register(appInstantiationService.createInstance(UserDataProfilesHandler)); @@ -1094,6 +1094,9 @@ export class CodeApplication extends Disposable { // Utility Process Worker services.set(IUtilityProcessWorkerMainService, new SyncDescriptor(UtilityProcessWorkerMainService, undefined, true)); + // Proxy Auth + services.set(IProxyAuthService, new SyncDescriptor(ProxyAuthService)); + // Init services that require it await Promises.settled([ backupMainService.initialize(), diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 4c035680fb7..9489592a766 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -11,6 +11,7 @@ import { ISerializableCommandAction } from 'vs/platform/action/common/action'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/common/dialogs'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IV8Profile } from 'vs/platform/profiling/common/profiling'; +import { AuthInfo, Credentials } from 'vs/platform/request/common/request'; import { IPartsSplash } from 'vs/platform/theme/common/themeService'; import { IColorScheme, IOpenedAuxiliaryWindow, IOpenedMainWindow, IOpenEmptyWindowOptions, IOpenWindowOptions, IPoint, IRectangle, IWindowOpenable } from 'vs/platform/window/common/window'; @@ -184,6 +185,7 @@ export interface ICommonNativeHostService { // Connectivity resolveProxy(url: string): Promise; + lookupAuthorization(authInfo: AuthInfo): Promise; loadCertificates(): Promise; findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise; diff --git a/src/vs/code/electron-main/auth.ts b/src/vs/platform/native/electron-main/auth.ts similarity index 64% rename from src/vs/code/electron-main/auth.ts rename to src/vs/platform/native/electron-main/auth.ts index bf2a99f601c..4d6bd8b5ffa 100644 --- a/src/vs/code/electron-main/auth.ts +++ b/src/vs/platform/native/electron-main/auth.ts @@ -3,14 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, AuthenticationResponseDetails, AuthInfo, Event as ElectronEvent, WebContents } from 'electron'; +import { app, AuthenticationResponseDetails, AuthInfo as ElectronAuthInfo, Event as ElectronEvent } from 'electron'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; import { generateUuid } from 'vs/base/common/uuid'; import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; +import { AuthInfo, Credentials } from 'vs/platform/request/common/request'; import { StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IApplicationStorageMainService } from 'vs/platform/storage/electron-main/storageMainService'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; @@ -20,49 +22,29 @@ interface ElectronAuthenticationResponseDetails extends AuthenticationResponseDe } type LoginEvent = { - event: ElectronEvent; + event?: ElectronEvent; authInfo: AuthInfo; - req: ElectronAuthenticationResponseDetails; - - callback: (username?: string, password?: string) => void; + callback?: (username?: string, password?: string) => void; }; -type Credentials = { - username: string; - password: string; -}; +export const IProxyAuthService = createDecorator('proxyAuthService'); -enum ProxyAuthState { - - /** - * Initial state: we will try to use stored credentials - * first to reply to the auth challenge. - */ - Initial = 1, - - /** - * We used stored credentials and are still challenged, - * so we will show a login dialog next. - */ - StoredCredentialsUsed, - - /** - * Finally, if we showed a login dialog already, we will - * not show any more login dialogs until restart to reduce - * the UI noise. - */ - LoginDialogShown +export interface IProxyAuthService { + lookupAuthorization(authInfo: AuthInfo): Promise; } -export class ProxyAuthHandler extends Disposable { +export class ProxyAuthService extends Disposable implements IProxyAuthService { + + declare readonly _serviceBrand: undefined; private readonly PROXY_CREDENTIALS_SERVICE_KEY = 'proxy-credentials://'; - private pendingProxyResolve: Promise | undefined = undefined; + private pendingProxyResolves = new Map>(); + private currentDialog: Promise | undefined = undefined; - private state = ProxyAuthState.Initial; + private cancelledAuthInfoHashes = new Set(); - private sessionCredentials: Credentials | undefined = undefined; + private sessionCredentials = new Map(); constructor( @ILogService private readonly logService: ILogService, @@ -76,39 +58,45 @@ export class ProxyAuthHandler extends Disposable { } private registerListeners(): void { - const onLogin = Event.fromNodeEventEmitter(app, 'login', (event: ElectronEvent, webContents: WebContents, req: ElectronAuthenticationResponseDetails, authInfo: AuthInfo, callback) => ({ event, webContents, req, authInfo, callback })); + const onLogin = Event.fromNodeEventEmitter(app, 'login', (event: ElectronEvent, req: ElectronAuthenticationResponseDetails, authInfo: ElectronAuthInfo, callback) => ({ event, authInfo: { ...authInfo, attempt: req.firstAuthAttempt ? 1 : 2 }, callback } satisfies LoginEvent)); this._register(onLogin(this.onLogin, this)); } - private async onLogin({ event, authInfo, req, callback }: LoginEvent): Promise { + async lookupAuthorization(authInfo: AuthInfo): Promise { + return this.onLogin({ authInfo }); + } + + private async onLogin({ event, authInfo, callback }: LoginEvent): Promise { if (!authInfo.isProxy) { return; // only for proxy } - if (!this.pendingProxyResolve && this.state === ProxyAuthState.LoginDialogShown && req.firstAuthAttempt) { - this.logService.trace('auth#onLogin (proxy) - exit - proxy dialog already shown'); - - return; // only one dialog per session at max (except when firstAuthAttempt: false which indicates a login problem) - } - // Signal we handle this event on our own, otherwise // Electron will ignore our provided credentials. - event.preventDefault(); + event?.preventDefault(); + + // Compute a hash over the authentication info to be used + // with the credentials store to return the right credentials + // given the properties of the auth request + // (see https://github.com/microsoft/vscode/issues/109497) + const authInfoHash = String(hash({ scheme: authInfo.scheme, host: authInfo.host, port: authInfo.port })); let credentials: Credentials | undefined = undefined; - if (!this.pendingProxyResolve) { + let pendingProxyResolve = this.pendingProxyResolves.get(authInfoHash); + if (!pendingProxyResolve) { this.logService.trace('auth#onLogin (proxy) - no pending proxy handling found, starting new'); - this.pendingProxyResolve = this.resolveProxyCredentials(authInfo); + pendingProxyResolve = this.resolveProxyCredentials(authInfo, authInfoHash); + this.pendingProxyResolves.set(authInfoHash, pendingProxyResolve); try { - credentials = await this.pendingProxyResolve; + credentials = await pendingProxyResolve; } finally { - this.pendingProxyResolve = undefined; + this.pendingProxyResolves.delete(authInfoHash); } } else { this.logService.trace('auth#onLogin (proxy) - pending proxy handling found'); - credentials = await this.pendingProxyResolve; + credentials = await pendingProxyResolve; } // According to Electron docs, it is fine to call back without @@ -118,14 +106,15 @@ export class ProxyAuthHandler extends Disposable { // > If `callback` is called without a username or password, the authentication // > request will be cancelled and the authentication error will be returned to the // > page. - callback(credentials?.username, credentials?.password); + callback?.(credentials?.username, credentials?.password); + return credentials; } - private async resolveProxyCredentials(authInfo: AuthInfo): Promise { + private async resolveProxyCredentials(authInfo: AuthInfo, authInfoHash: string): Promise { this.logService.trace('auth#resolveProxyCredentials (proxy) - enter'); try { - const credentials = await this.doResolveProxyCredentials(authInfo); + const credentials = await this.doResolveProxyCredentials(authInfo, authInfoHash); if (credentials) { this.logService.trace('auth#resolveProxyCredentials (proxy) - got credentials'); @@ -140,14 +129,18 @@ export class ProxyAuthHandler extends Disposable { return undefined; } - private async doResolveProxyCredentials(authInfo: AuthInfo): Promise { + private async doResolveProxyCredentials(authInfo: AuthInfo, authInfoHash: string): Promise { this.logService.trace('auth#doResolveProxyCredentials - enter', authInfo); - // Compute a hash over the authentication info to be used - // with the credentials store to return the right credentials - // given the properties of the auth request - // (see https://github.com/microsoft/vscode/issues/109497) - const authInfoHash = String(hash({ scheme: authInfo.scheme, host: authInfo.host, port: authInfo.port })); + // Reply with session credentials unless we used them already. + // In that case we need to show a login dialog again because + // they seem invalid. + if (authInfo.attempt === 1 && this.sessionCredentials.has(authInfoHash)) { + this.logService.trace('auth#doResolveProxyCredentials (proxy) - exit - found session credentials to use'); + + const { username, password } = this.sessionCredentials.get(authInfoHash)!; + return { username, password }; + } let storedUsername: string | undefined; let storedPassword: string | undefined; @@ -166,13 +159,32 @@ export class ProxyAuthHandler extends Disposable { // Reply with stored credentials unless we used them already. // In that case we need to show a login dialog again because // they seem invalid. - if (this.state !== ProxyAuthState.StoredCredentialsUsed && typeof storedUsername === 'string' && typeof storedPassword === 'string') { + if (authInfo.attempt === 1 && typeof storedUsername === 'string' && typeof storedPassword === 'string') { this.logService.trace('auth#doResolveProxyCredentials (proxy) - exit - found stored credentials to use'); - this.state = ProxyAuthState.StoredCredentialsUsed; + this.sessionCredentials.set(authInfoHash, { username: storedUsername, password: storedPassword }); return { username: storedUsername, password: storedPassword }; } + const previousDialog = this.currentDialog; + const currentDialog = this.currentDialog = (async () => { + await previousDialog; + const credentials = await this.showProxyCredentialsDialog(authInfo, authInfoHash, storedUsername, storedPassword); + if (this.currentDialog === currentDialog!) { + this.currentDialog = undefined; + } + return credentials; + })(); + return currentDialog; + } + + private async showProxyCredentialsDialog(authInfo: AuthInfo, authInfoHash: string, storedUsername: string | undefined, storedPassword: string | undefined): Promise { + if (this.cancelledAuthInfoHashes.has(authInfoHash)) { + this.logService.trace('auth#doResolveProxyCredentials (proxy) - exit - login dialog was cancelled before, not showing again'); + + return undefined; + } + // Find suitable window to show dialog: prefer to show it in the // active window because any other network request will wait on // the credentials and we want the user to present the dialog. @@ -186,14 +198,14 @@ export class ProxyAuthHandler extends Disposable { this.logService.trace(`auth#doResolveProxyCredentials (proxy) - asking window ${window.id} to handle proxy login`); // Open proxy dialog + const sessionCredentials = this.sessionCredentials.get(authInfoHash); const payload = { authInfo, - username: this.sessionCredentials?.username ?? storedUsername, // prefer to show already used username (if any) over stored - password: this.sessionCredentials?.password ?? storedPassword, // prefer to show already used password (if any) over stored + username: sessionCredentials?.username ?? storedUsername, // prefer to show already used username (if any) over stored + password: sessionCredentials?.password ?? storedPassword, // prefer to show already used password (if any) over stored replyChannel: `vscode:proxyAuthResponse:${generateUuid()}` }; window.sendWhenReady('vscode:openProxyAuthenticationDialog', CancellationToken.None, payload); - this.state = ProxyAuthState.LoginDialogShown; // Handle reply const loginDialogCredentials = await new Promise(resolve => { @@ -229,6 +241,7 @@ export class ProxyAuthHandler extends Disposable { // We did not get any credentials from the window (e.g. cancelled) else { + this.cancelledAuthInfoHashes.add(authInfoHash); resolve(undefined); } } @@ -240,7 +253,7 @@ export class ProxyAuthHandler extends Disposable { // Remember credentials for the session in case // the credentials are wrong and we show the dialog // again - this.sessionCredentials = loginDialogCredentials; + this.sessionCredentials.set(authInfoHash, loginDialogCredentials); return loginDialogCredentials; } diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 2718d0fd7bd..dad4087ec75 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -44,6 +44,8 @@ import { IV8Profile } from 'vs/platform/profiling/common/profiling'; import { IAuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow'; import { CancellationError } from 'vs/base/common/errors'; +import { IProxyAuthService } from 'vs/platform/native/electron-main/auth'; +import { AuthInfo, Credentials } from 'vs/platform/request/common/request'; export interface INativeHostMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -62,7 +64,8 @@ export class NativeHostMainService extends Disposable implements INativeHostMain @ILogService private readonly logService: ILogService, @IProductService private readonly productService: IProductService, @IThemeMainService private readonly themeMainService: IThemeMainService, - @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService + @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, + @IProxyAuthService private readonly proxyAuthService: IProxyAuthService ) { super(); } @@ -772,6 +775,10 @@ export class NativeHostMainService extends Disposable implements INativeHostMain return session?.resolveProxy(url); } + async lookupAuthorization(_windowId: number | undefined, authInfo: AuthInfo): Promise { + return this.proxyAuthService.lookupAuthorization(authInfo); + } + async loadCertificates(_windowId: number | undefined): Promise { const proxyAgent = await import('@vscode/proxy-agent'); return proxyAgent.loadSystemCertificates({ log: this.logService }); diff --git a/src/vs/platform/request/browser/requestService.ts b/src/vs/platform/request/browser/requestService.ts index f0fc3d68790..70f4e65bcbc 100644 --- a/src/vs/platform/request/browser/requestService.ts +++ b/src/vs/platform/request/browser/requestService.ts @@ -8,7 +8,7 @@ import { request } from 'vs/base/parts/request/browser/request'; import { IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILoggerService } from 'vs/platform/log/common/log'; -import { AbstractRequestService, IRequestService } from 'vs/platform/request/common/request'; +import { AbstractRequestService, AuthInfo, Credentials, IRequestService } from 'vs/platform/request/common/request'; /** * This service exposes the `request` API, while using the global @@ -36,6 +36,10 @@ export class RequestService extends AbstractRequestService implements IRequestSe return undefined; // not implemented in the web } + async lookupAuthorization(authInfo: AuthInfo): Promise { + return undefined; // not implemented in the web + } + async loadCertificates(): Promise { return []; // not implemented in the web } diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 06265628b94..e8574d11f7f 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -16,12 +16,27 @@ import { Registry } from 'vs/platform/registry/common/platform'; export const IRequestService = createDecorator('requestService'); +export interface AuthInfo { + isProxy: boolean; + scheme: string; + host: string; + port: number; + realm: string; + attempt: number; +} + +export interface Credentials { + username: string; + password: string; +} + export interface IRequestService { readonly _serviceBrand: undefined; request(options: IRequestOptions, token: CancellationToken): Promise; resolveProxy(url: string): Promise; + lookupAuthorization(authInfo: AuthInfo): Promise; loadCertificates(): Promise; } @@ -80,6 +95,7 @@ export abstract class AbstractRequestService extends Disposable implements IRequ abstract request(options: IRequestOptions, token: CancellationToken): Promise; abstract resolveProxy(url: string): Promise; + abstract lookupAuthorization(authInfo: AuthInfo): Promise; abstract loadCertificates(): Promise; } diff --git a/src/vs/platform/request/common/requestIpc.ts b/src/vs/platform/request/common/requestIpc.ts index 421106ebff6..b489a52dacc 100644 --- a/src/vs/platform/request/common/requestIpc.ts +++ b/src/vs/platform/request/common/requestIpc.ts @@ -8,7 +8,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { IHeaders, IRequestContext, IRequestOptions } from 'vs/base/parts/request/common/request'; -import { IRequestService } from 'vs/platform/request/common/request'; +import { AuthInfo, Credentials, IRequestService } from 'vs/platform/request/common/request'; type RequestResponse = [ { @@ -34,6 +34,7 @@ export class RequestChannel implements IServerChannel { return [{ statusCode: res.statusCode, headers: res.headers }, buffer]; }); case 'resolveProxy': return this.service.resolveProxy(args[0]); + case 'lookupAuthorization': return this.service.lookupAuthorization(args[0]); case 'loadCertificates': return this.service.loadCertificates(); } throw new Error('Invalid call'); @@ -55,6 +56,10 @@ export class RequestChannelClient implements IRequestService { return this.channel.call('resolveProxy', [url]); } + async lookupAuthorization(authInfo: AuthInfo): Promise { + return this.channel.call<{ username: string; password: string } | undefined>('lookupAuthorization', [authInfo]); + } + async loadCertificates(): Promise { return this.channel.call('loadCertificates'); } diff --git a/src/vs/platform/request/node/requestService.ts b/src/vs/platform/request/node/requestService.ts index 23f8f0d44c8..dd2165c1ce3 100644 --- a/src/vs/platform/request/node/requestService.ts +++ b/src/vs/platform/request/node/requestService.ts @@ -17,7 +17,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { getResolvedShellEnv } from 'vs/platform/shell/node/shellEnv'; import { ILogService, ILoggerService } from 'vs/platform/log/common/log'; -import { AbstractRequestService, IRequestService } from 'vs/platform/request/common/request'; +import { AbstractRequestService, AuthInfo, Credentials, IRequestService } from 'vs/platform/request/common/request'; import { Agent, getProxyAgent } from 'vs/platform/request/node/proxy'; import { createGunzip } from 'zlib'; @@ -110,6 +110,10 @@ export class RequestService extends AbstractRequestService implements IRequestSe return undefined; // currently not implemented in node } + async lookupAuthorization(authInfo: AuthInfo): Promise { + return undefined; // currently not implemented in node + } + async loadCertificates(): Promise { const proxyAgent = await import('@vscode/proxy-agent'); return proxyAgent.loadSystemCertificates({ log: this.logService }); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index ea8260323a9..3cd97bda585 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -26,7 +26,7 @@ import { TestInstantiationService } from 'vs/platform/instantiation/test/common/ import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import product from 'vs/platform/product/common/product'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IRequestService } from 'vs/platform/request/common/request'; +import { AuthInfo, Credentials, IRequestService } from 'vs/platform/request/common/request'; import { InMemoryStorageService, IStorageService } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService } from 'vs/platform/telemetry/common/telemetryUtils'; @@ -188,6 +188,7 @@ export class UserDataSyncTestServer implements IRequestService { constructor(private readonly rateLimit = Number.MAX_SAFE_INTEGER, private readonly retryAfter?: number) { } async resolveProxy(url: string): Promise { return url; } + async lookupAuthorization(authInfo: AuthInfo): Promise { return undefined; } async loadCertificates(): Promise { return []; } async request(options: IRequestOptions, token: CancellationToken): Promise { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts index 2a69d9d3ebf..03f68a73da9 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncStoreService.test.ts @@ -414,6 +414,7 @@ suite('UserDataSyncRequestsSession', () => { _serviceBrand: undefined, async request() { return { res: { headers: {} }, stream: newWriteableBufferStream() }; }, async resolveProxy() { return undefined; }, + async lookupAuthorization() { return undefined; }, async loadCertificates() { return []; } }; diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index 180932b6d6d..09ff17ad867 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -14,7 +14,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { INotificationService } from 'vs/platform/notification/common/notification'; -import { IRequestService } from 'vs/platform/request/common/request'; +import { AuthInfo, Credentials, IRequestService } from 'vs/platform/request/common/request'; import { WorkspaceTrustRequestOptions, IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust'; import { IWorkspace, IWorkspaceContextService, WorkbenchState, isUntitledWorkspace, WorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; @@ -223,6 +223,10 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return this._requestService.resolveProxy(url); } + $lookupAuthorization(authInfo: AuthInfo): Promise { + return this._requestService.lookupAuthorization(authInfo); + } + $loadCertificates(): Promise { return this._requestService.loadCertificates(); } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index b1dcc4981a3..7bbab68a618 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -82,6 +82,7 @@ import { IFileQueryBuilderOptions, ITextQueryBuilderOptions } from 'vs/workbench import * as search from 'vs/workbench/services/search/common/search'; import { ISaveProfileResult } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import type { TerminalShellExecutionCommandLineConfidence } from 'vscode'; +import { AuthInfo, Credentials } from 'vs/platform/request/common/request'; export interface IWorkspaceData extends IStaticWorkspaceData { folders: { uri: UriComponents; name: string; index: number }[]; @@ -1385,6 +1386,7 @@ export interface MainThreadWorkspaceShape extends IDisposable { $saveAll(includeUntitled?: boolean): Promise; $updateWorkspaceFolders(extensionName: string, index: number, deleteCount: number, workspaceFoldersToAdd: { uri: UriComponents; name?: string }[]): Promise; $resolveProxy(url: string): Promise; + $lookupAuthorization(authInfo: AuthInfo): Promise; $loadCertificates(): Promise; $requestWorkspaceTrust(options?: WorkspaceTrustRequestOptions): Promise; $registerEditSessionIdentityProvider(handle: number, scheme: string): void; diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 5b3cd328aaa..61c5b48357c 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -33,12 +33,14 @@ import { IRawFileMatch2, ITextSearchResult, resultIsMatch } from 'vs/workbench/s import * as vscode from 'vscode'; import { ExtHostWorkspaceShape, IRelativePatternDto, IWorkspaceData, MainContext, MainThreadMessageOptions, MainThreadMessageServiceShape, MainThreadWorkspaceShape } from './extHost.protocol'; import { revive } from 'vs/base/common/marshalling'; +import { AuthInfo, Credentials } from 'vs/platform/request/common/request'; export interface IExtHostWorkspaceProvider { getWorkspaceFolder2(uri: vscode.Uri, resolveParent?: boolean): Promise; resolveWorkspaceFolder(uri: vscode.Uri): Promise; getWorkspaceFolders2(): Promise; resolveProxy(url: string): Promise; + lookupAuthorization(authInfo: AuthInfo): Promise; loadCertificates(): Promise; } @@ -626,6 +628,10 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac return this._proxy.$resolveProxy(url); } + lookupAuthorization(authInfo: AuthInfo): Promise { + return this._proxy.$lookupAuthorization(authInfo); + } + loadCertificates(): Promise { return this._proxy.$loadCertificates(); } diff --git a/src/vs/workbench/api/node/proxyResolver.ts b/src/vs/workbench/api/node/proxyResolver.ts index ac373f9739b..e39d10af2d3 100644 --- a/src/vs/workbench/api/node/proxyResolver.ts +++ b/src/vs/workbench/api/node/proxyResolver.ts @@ -17,6 +17,7 @@ import { URI } from 'vs/base/common/uri'; import { ILogService, LogLevel as LogServiceLevel } from 'vs/platform/log/common/log'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { LogLevel, createHttpPatch, createProxyResolver, createTlsPatch, ProxySupportSetting, ProxyAgentParams, createNetPatch, loadSystemCertificates } from '@vscode/proxy-agent'; +import { AuthInfo } from 'vs/platform/request/common/request'; const systemCertificatesV2Default = false; @@ -32,7 +33,7 @@ export function connectProxyResolver( const doUseHostProxy = typeof useHostProxy === 'boolean' ? useHostProxy : !initData.remote.isRemote; const params: ProxyAgentParams = { resolveProxy: url => extHostWorkspace.resolveProxy(url), - lookupProxyAuthorization: lookupProxyAuthorization.bind(undefined, extHostLogService, mainThreadTelemetry, configProvider, {}, initData.remote.isRemote), + lookupProxyAuthorization: lookupProxyAuthorization.bind(undefined, extHostWorkspace, extHostLogService, mainThreadTelemetry, configProvider, {}, initData.remote.isRemote), getProxyURL: () => configProvider.getConfiguration('http').get('proxy'), getProxySupport: () => configProvider.getConfiguration('http').get('proxySupport') || 'off', getNoProxyConfig: () => configProvider.getConfiguration('http').get('noProxy') || [], @@ -140,6 +141,7 @@ function configureModuleLoading(extensionService: ExtHostExtensionService, looku } async function lookupProxyAuthorization( + extHostWorkspace: IExtHostWorkspaceProvider, extHostLogService: ILogService, mainThreadTelemetry: MainThreadTelemetryShape, configProvider: ExtHostConfigProvider, @@ -147,7 +149,7 @@ async function lookupProxyAuthorization( isRemote: boolean, proxyURL: string, proxyAuthenticate: string | string[] | undefined, - state: { kerberosRequested?: boolean } + state: { kerberosRequested?: boolean; basicAuthAttempt?: number } ): Promise { const cached = proxyAuthenticateCache[proxyURL]; if (proxyAuthenticate) { @@ -171,6 +173,30 @@ async function lookupProxyAuthorization( } catch (err) { extHostLogService.error('ProxyResolver#lookupProxyAuthorization Kerberos authentication failed', err); } + } else { + const header = authenticate.find(a => /^Basic( |$)/i.test(a)); + if (header) { + try { + state.basicAuthAttempt = (state.basicAuthAttempt || 0) + 1; + const realm = / realm="([^"]+)"/i.exec(header)?.[1]; + extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication lookup', `proxyURL:${proxyURL}`, `realm:${realm}`); + const url = new URL(proxyURL); + const authInfo: AuthInfo = { + scheme: 'basic', + host: url.hostname, + port: Number(url.port), + realm: realm || '', + isProxy: true, + attempt: state.basicAuthAttempt, + }; + const credentials = await extHostWorkspace.lookupAuthorization(authInfo); + if (credentials) { + return 'Basic ' + Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64'); + } + } catch (err) { + extHostLogService.error('ProxyResolver#lookupProxyAuthorization Kerberos authentication failed', err); + } + } } return undefined; } diff --git a/src/vs/workbench/services/request/electron-sandbox/requestService.ts b/src/vs/workbench/services/request/electron-sandbox/requestService.ts index 0a249b6eaaa..a0597f10ee3 100644 --- a/src/vs/workbench/services/request/electron-sandbox/requestService.ts +++ b/src/vs/workbench/services/request/electron-sandbox/requestService.ts @@ -7,7 +7,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ILoggerService } from 'vs/platform/log/common/log'; import { RequestService } from 'vs/platform/request/browser/requestService'; import { InstantiationType, registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IRequestService } from 'vs/platform/request/common/request'; +import { AuthInfo, Credentials, IRequestService } from 'vs/platform/request/common/request'; import { INativeHostService } from 'vs/platform/native/common/native'; export class NativeRequestService extends RequestService { @@ -24,6 +24,10 @@ export class NativeRequestService extends RequestService { return this.nativeHostService.resolveProxy(url); } + override async lookupAuthorization(authInfo: AuthInfo): Promise { + return this.nativeHostService.lookupAuthorization(authInfo); + } + override async loadCertificates(): Promise { return this.nativeHostService.loadCertificates(); } diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index 45f6d6d98f4..3864abba55b 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -48,6 +48,7 @@ import { NativeWorkingCopyBackupService } from 'vs/workbench/services/workingCop import { CancellationToken } from 'vs/base/common/cancellation'; import { UriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentityService'; import { UserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { AuthInfo, Credentials } from 'vs/platform/request/common/request'; export class TestSharedProcessService implements ISharedProcessService { @@ -144,6 +145,7 @@ export class TestNativeHostService implements INativeHostService { async openDevTools(options?: Partial & INativeHostOptions | undefined): Promise { } async toggleDevTools(): Promise { } async resolveProxy(url: string): Promise { return undefined; } + async lookupAuthorization(authInfo: AuthInfo): Promise { return undefined; } async loadCertificates(): Promise { return []; } async findFreePort(startPort: number, giveUpAfter: number, timeout: number, stride?: number): Promise { return -1; } async readClipboardText(type?: 'selection' | 'clipboard' | undefined): Promise { return ''; } From 30a5144f2dd900486db702247ea6b7350a193be4 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 5 Jul 2024 09:29:33 +0200 Subject: [PATCH 0285/2222] disable failing test for FF (#220033) re https://github.com/microsoft/vscode/issues/219843 --- .../test/browser/wordOperations.test.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/contrib/wordOperations/test/browser/wordOperations.test.ts b/src/vs/editor/contrib/wordOperations/test/browser/wordOperations.test.ts index 51cdccc4fd3..d90d44fd2d6 100644 --- a/src/vs/editor/contrib/wordOperations/test/browser/wordOperations.test.ts +++ b/src/vs/editor/contrib/wordOperations/test/browser/wordOperations.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { isFirefox } from 'vs/base/common/platform'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { CoreEditingCommands } from 'vs/editor/browser/coreCommands'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -179,7 +180,11 @@ suite('WordOperations', () => { assert.deepStrictEqual(actual, EXPECTED); }); - test('cursorWordLeft - Recognize words', () => { + test('cursorWordLeft - Recognize words', function () { + if (isFirefox) { + // https://github.com/microsoft/vscode/issues/219843 + return this.skip(); + } const EXPECTED = [ '|/* |これ|は|テスト|です |/*', ].join('\n'); @@ -399,7 +404,11 @@ suite('WordOperations', () => { assert.deepStrictEqual(actual, EXPECTED); }); - test('cursorWordRight - Recognize words', () => { + test('cursorWordRight - Recognize words', function () { + if (isFirefox) { + // https://github.com/microsoft/vscode/issues/219843 + return this.skip(); + } const EXPECTED = [ '/*| これ|は|テスト|です|/*|', ].join('\n'); From 6fdeabf40b3774c7fb0514cab66c03d549df89b3 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 5 Jul 2024 09:32:00 +0200 Subject: [PATCH 0286/2222] Fix argument order (#220034) --- src/vs/platform/native/electron-main/auth.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/native/electron-main/auth.ts b/src/vs/platform/native/electron-main/auth.ts index 4d6bd8b5ffa..953a74ec158 100644 --- a/src/vs/platform/native/electron-main/auth.ts +++ b/src/vs/platform/native/electron-main/auth.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, AuthenticationResponseDetails, AuthInfo as ElectronAuthInfo, Event as ElectronEvent } from 'electron'; +import { app, AuthenticationResponseDetails, AuthInfo as ElectronAuthInfo, Event as ElectronEvent, WebContents } from 'electron'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; @@ -58,7 +58,7 @@ export class ProxyAuthService extends Disposable implements IProxyAuthService { } private registerListeners(): void { - const onLogin = Event.fromNodeEventEmitter(app, 'login', (event: ElectronEvent, req: ElectronAuthenticationResponseDetails, authInfo: ElectronAuthInfo, callback) => ({ event, authInfo: { ...authInfo, attempt: req.firstAuthAttempt ? 1 : 2 }, callback } satisfies LoginEvent)); + const onLogin = Event.fromNodeEventEmitter(app, 'login', (event: ElectronEvent, _webContents: WebContents, req: ElectronAuthenticationResponseDetails, authInfo: ElectronAuthInfo, callback) => ({ event, authInfo: { ...authInfo, attempt: req.firstAuthAttempt ? 1 : 2 }, callback } satisfies LoginEvent)); this._register(onLogin(this.onLogin, this)); } From 2d1ad358919d1f63c43a517e6f17c15942f9dd29 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 5 Jul 2024 09:52:44 +0200 Subject: [PATCH 0287/2222] Fix editor commands context (#219971) * editor commands debt * :lipstick: * :lipstick: * :lipstick: * reorder context correctly * :lipstick: * :lipstick: --- .../browser/parts/editor/editorActions.ts | 37 +- .../browser/parts/editor/editorCommands.ts | 501 ++++++------------ .../parts/editor/editorCommandsContext.ts | 200 +++++++ .../contrib/files/browser/fileCommands.ts | 9 +- .../multiDiffEditor/browser/actions.ts | 24 +- .../browser/controller/coreActions.ts | 7 +- 6 files changed, 420 insertions(+), 358 deletions(-) create mode 100644 src/vs/workbench/browser/parts/editor/editorCommandsContext.ts diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 63e2103bcee..eb9d1534e11 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -13,7 +13,7 @@ import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/bro import { GoFilter, IHistoryService } from 'vs/workbench/services/history/common/history'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR, resolveCommandsContext, getCommandsContext, TOGGLE_MAXIMIZE_EDITOR_GROUP, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID as NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID, resolveEditorsContext, getEditorsContext } from 'vs/workbench/browser/parts/editor/editorCommands'; +import { CLOSE_EDITOR_COMMAND_ID, MOVE_ACTIVE_EDITOR_COMMAND_ID, ActiveEditorMoveCopyArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, UNPIN_EDITOR_COMMAND_ID, COPY_ACTIVE_EDITOR_COMMAND_ID, SPLIT_EDITOR, TOGGLE_MAXIMIZE_EDITOR_GROUP, MOVE_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_INTO_NEW_WINDOW_COMMAND_ID, MOVE_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, COPY_EDITOR_GROUP_INTO_NEW_WINDOW_COMMAND_ID, NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID as NEW_EMPTY_EDITOR_WINDOW_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; import { IEditorGroupsService, IEditorGroup, GroupsArrangement, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder, MergeGroupMode } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -34,10 +34,10 @@ import { IKeybindingRule, KeybindingWeight } from 'vs/platform/keybinding/common import { ILogService } from 'vs/platform/log/common/log'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { ActiveEditorAvailableEditorIdsContext, ActiveEditorContext, ActiveEditorGroupEmptyContext, AuxiliaryBarVisibleContext, EditorPartMaximizedEditorGroupContext, EditorPartMultipleEditorGroupsContext, IsAuxiliaryWindowFocusedContext, MultipleEditorGroupsContext, SideBarVisibleContext } from 'vs/workbench/common/contextkeys'; -import { URI } from 'vs/base/common/uri'; import { getActiveDocument } from 'vs/base/browser/dom'; import { ICommandActionTitle } from 'vs/platform/action/common/action'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { resolveCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommandsContext'; class ExecuteCommandAction extends Action2 { @@ -62,12 +62,14 @@ abstract class AbstractSplitEditorAction extends Action2 { return preferredSideBySideGroupDirection(configurationService); } - override async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { + override async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { const editorGroupService = accessor.get(IEditorGroupsService); const configurationService = accessor.get(IConfigurationService); - const commandContext = getCommandsContext(accessor, resourceOrContext, context); - splitEditor(editorGroupService, this.getDirection(configurationService), commandContext ? [commandContext] : undefined); + const direction = this.getDirection(configurationService); + const commandContext = resolveCommandsContext(accessor, args); + + splitEditor(editorGroupService, direction, commandContext); } } @@ -1169,11 +1171,13 @@ export class ToggleMaximizeEditorGroupAction extends Action2 { }); } - override async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { + override async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { const editorGroupsService = accessor.get(IEditorGroupsService); - const { group } = resolveCommandsContext(editorGroupsService, getCommandsContext(accessor, resourceOrContext, context)); - editorGroupsService.toggleMaximizeGroup(group); + const resolvedContext = resolveCommandsContext(accessor, args); + if (resolvedContext.groupedEditors.length) { + editorGroupsService.toggleMaximizeGroup(resolvedContext.groupedEditors[0].group); + } } } @@ -2538,21 +2542,24 @@ abstract class BaseMoveCopyEditorToNewWindowAction extends Action2 { }); } - override async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) { + override async run(accessor: ServicesAccessor, ...args: unknown[]) { const editorGroupService = accessor.get(IEditorGroupsService); - const editorsContext = resolveEditorsContext(getEditorsContext(accessor, resourceOrContext, context)); - if (editorsContext.length === 0) { + const resolvedContext = resolveCommandsContext(accessor, args); + if (!resolvedContext.groupedEditors.length) { return; } const auxiliaryEditorPart = await editorGroupService.createAuxiliaryEditorPart(); - const sourceGroup = editorsContext[0].group; // only single group supported for move/copy for now - const sourceEditors = editorsContext.filter(({ group }) => group === sourceGroup); + // only single group supported for move/copy for now + const { group, editors } = resolvedContext.groupedEditors[0]; + const options = { preserveFocus: resolvedContext.preserveFocus }; + const editorsWithOptions = editors.map(editor => ({ editor, options })); + if (this.move) { - sourceGroup.moveEditors(sourceEditors, auxiliaryEditorPart.activeGroup); + group.moveEditors(editorsWithOptions, auxiliaryEditorPart.activeGroup); } else { - sourceGroup.copyEditors(sourceEditors, auxiliaryEditorPart.activeGroup); + group.copyEditors(editorsWithOptions, auxiliaryEditorPart.activeGroup); } auxiliaryEditorPart.activeGroup.focus(); diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index a8ba531496e..ce629242565 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -3,13 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { getActiveElement } from 'vs/base/browser/dom'; -import { List } from 'vs/base/browser/ui/list/listWidget'; -import { coalesce, distinct } from 'vs/base/common/arrays'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Schemas, matchesScheme } from 'vs/base/common/network'; -import { extname, isEqual } from 'vs/base/common/resources'; +import { extname } from 'vs/base/common/resources'; import { isNumber, isObject, isString, isUndefined } from 'vs/base/common/types'; import { URI, UriComponents } from 'vs/base/common/uri'; import { isDiffEditor } from 'vs/editor/browser/editorBrowser'; @@ -23,7 +20,7 @@ import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { EditorResolution, IEditorOptions, IResourceEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeybindingWeight, KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { IListService, IOpenEvent } from 'vs/platform/list/browser/listService'; +import { IOpenEvent } from 'vs/platform/list/browser/listService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -31,17 +28,18 @@ import { ActiveGroupEditorsByMostRecentlyUsedQuickAccess } from 'vs/workbench/br import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor'; import { ActiveEditorCanSplitInGroupContext, ActiveEditorGroupEmptyContext, ActiveEditorGroupLockedContext, ActiveEditorStickyContext, MultipleEditorGroupsContext, SideBySideEditorActiveContext, TextCompareEditorActiveContext } from 'vs/workbench/common/contextkeys'; -import { CloseDirection, EditorInputCapabilities, EditorsOrder, IEditorCommandsContext, IEditorIdentifier, IResourceDiffEditorInput, IUntitledTextResourceEditorInput, IVisibleEditorPane, isEditorCommandsContext, isEditorIdentifier, isEditorInputWithOptionsAndGroup } from 'vs/workbench/common/editor'; +import { CloseDirection, EditorInputCapabilities, EditorsOrder, IResourceDiffEditorInput, IUntitledTextResourceEditorInput, IVisibleEditorPane, isEditorInputWithOptionsAndGroup } from 'vs/workbench/common/editor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput'; import { EditorGroupColumn, columnToEditorGroup } from 'vs/workbench/services/editor/common/editorGroupColumn'; -import { EditorGroupLayout, GroupDirection, GroupLocation, GroupsOrder, IEditorGroup, IEditorGroupsService, IEditorReplacement, isEditorGroup, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { EditorGroupLayout, GroupDirection, GroupLocation, GroupsOrder, IEditorGroup, IEditorGroupsService, IEditorReplacement, preferredSideBySideGroupDirection } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorResolverService } from 'vs/workbench/services/editor/common/editorResolverService'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IUntitledTextEditorService } from 'vs/workbench/services/untitled/common/untitledTextEditorService'; import { DIFF_FOCUS_OTHER_SIDE, DIFF_FOCUS_PRIMARY_SIDE, DIFF_FOCUS_SECONDARY_SIDE, DIFF_OPEN_SIDE, registerDiffEditorCommands } from './diffEditorCommands'; +import { IResolvedEditorCommandsContext, resolveCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommandsContext'; export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors'; export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup'; @@ -656,50 +654,25 @@ function registerFocusEditorGroupAtIndexCommands(): void { } } -export function splitEditor(editorGroupService: IEditorGroupsService, direction: GroupDirection, contexts?: IEditorCommandsContext[]): void { - let newGroup: IEditorGroup | undefined; - let sourceGroup: IEditorGroup | undefined; - - for (const context of contexts ?? [undefined]) { - let currentGroup: IEditorGroup | undefined; - - if (context) { - currentGroup = editorGroupService.getGroup(context.groupId); - } else { - currentGroup = editorGroupService.activeGroup; - } - - if (!currentGroup) { - continue; - } - - if (!sourceGroup) { - sourceGroup = currentGroup; - } else if (sourceGroup.id !== currentGroup.id) { - continue; // Only support splitting from the same group - } +export function splitEditor(editorGroupService: IEditorGroupsService, direction: GroupDirection, resolvedContext: IResolvedEditorCommandsContext): void { + if (!resolvedContext.groupedEditors.length) { + return; + } - // Add group - if (!newGroup) { - newGroup = editorGroupService.addGroup(currentGroup, direction); - } + // Only support splitting from one source group + const { group, editors } = resolvedContext.groupedEditors[0]; + const preserveFocus = resolvedContext.preserveFocus; + const newGroup = editorGroupService.addGroup(group, direction); + for (const editorToCopy of editors) { // Split editor (if it can be split) - let editorToCopy: EditorInput | undefined; - if (context && typeof context.editorIndex === 'number') { - editorToCopy = currentGroup.getEditorByIndex(context.editorIndex); - } else { - editorToCopy = currentGroup.activeEditor ?? undefined; - } - - // Copy the editor to the new group, else create an empty group if (editorToCopy && !editorToCopy.hasCapability(EditorInputCapabilities.Singleton)) { - currentGroup.copyEditor(editorToCopy, newGroup, { preserveFocus: context?.preserveFocus }); + group.copyEditor(editorToCopy, newGroup, { preserveFocus }); } } // Focus - newGroup?.focus(); + newGroup.focus(); } function registerSplitEditorCommands() { @@ -709,9 +682,9 @@ function registerSplitEditorCommands() { { id: SPLIT_EDITOR_LEFT, direction: GroupDirection.LEFT }, { id: SPLIT_EDITOR_RIGHT, direction: GroupDirection.RIGHT } ].forEach(({ id, direction }) => { - CommandsRegistry.registerCommand(id, function (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) { - const { editors } = getEditorsContext(accessor, resourceOrContext, context); - splitEditor(accessor.get(IEditorGroupsService), direction, editors); + CommandsRegistry.registerCommand(id, function (accessor, ...args) { + const resolvedContext = resolveCommandsContext(accessor, args); + splitEditor(accessor.get(IEditorGroupsService), direction, resolvedContext); }); }); } @@ -721,14 +694,14 @@ function registerCloseEditorCommands() { // A special handler for "Close Editor" depending on context // - keybindining: do not close sticky editors, rather open the next non-sticky editor // - menu: always close editor, even sticky ones - function closeEditorHandler(accessor: ServicesAccessor, forceCloseStickyEditors: boolean, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { + function closeEditorHandler(accessor: ServicesAccessor, forceCloseStickyEditors: boolean, ...args: unknown[]): Promise { const editorGroupsService = accessor.get(IEditorGroupsService); const editorService = accessor.get(IEditorService); let keepStickyEditors: boolean | undefined = undefined; if (forceCloseStickyEditors) { keepStickyEditors = false; // explicitly close sticky editors - } else if (resourceOrContext || context) { + } else if (args.length) { keepStickyEditors = false; // we have a context, as such this command was used e.g. from the tab context menu } else { keepStickyEditors = editorGroupsService.partOptions.preventPinnedEditorClose === 'keyboard' || editorGroupsService.partOptions.preventPinnedEditorClose === 'keyboardAndMouse'; // respect setting otherwise @@ -756,17 +729,12 @@ function registerCloseEditorCommands() { } // With context: proceed to close editors as instructed - const { editors, groups } = getEditorsContext(accessor, resourceOrContext, context); - - return Promise.all(groups.map(async group => { - if (group) { - const editorsToClose = coalesce(editors - .filter(editor => editor.groupId === group.id) - .map(editor => typeof editor.editorIndex === 'number' ? group.getEditorByIndex(editor.editorIndex) : group.activeEditor)) - .filter(editor => !keepStickyEditors || !group.isSticky(editor)); + const resolvedContext = resolveCommandsContext(accessor, args); + const preserveFocus = resolvedContext.preserveFocus; - await group.closeEditors(editorsToClose, { preserveFocus: context?.preserveFocus }); - } + return Promise.all(resolvedContext.groupedEditors.map(async ({ group, editors }) => { + const editorsToClose = editors.filter(editor => !keepStickyEditors || !group.isSticky(editor)); + await group.closeEditors(editorsToClose, { preserveFocus }); })); } @@ -776,13 +744,13 @@ function registerCloseEditorCommands() { when: undefined, primary: KeyMod.CtrlCmd | KeyCode.KeyW, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KeyW] }, - handler: (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - return closeEditorHandler(accessor, false, resourceOrContext, context); + handler: (accessor, ...args: unknown[]) => { + return closeEditorHandler(accessor, false, args); } }); - CommandsRegistry.registerCommand(CLOSE_PINNED_EDITOR_COMMAND_ID, (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - return closeEditorHandler(accessor, true /* force close pinned editors */, resourceOrContext, context); + CommandsRegistry.registerCommand(CLOSE_PINNED_EDITOR_COMMAND_ID, (accessor, ...args: unknown[]) => { + return closeEditorHandler(accessor, true /* force close pinned editors */, args); }); KeybindingsRegistry.registerCommandAndKeybindingRule({ @@ -790,12 +758,10 @@ function registerCloseEditorCommands() { weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyW), - handler: (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - return Promise.all(getEditorsContext(accessor, resourceOrContext, context).groups.map(async group => { - if (group) { - await group.closeAllEditors({ excludeSticky: true }); - return; - } + handler: (accessor, ...args: unknown[]) => { + const resolvedContext = resolveCommandsContext(accessor, args); + return Promise.all(resolvedContext.groupedEditors.map(async ({ group }) => { + await group.closeAllEditors({ excludeSticky: true }); })); } }); @@ -806,19 +772,12 @@ function registerCloseEditorCommands() { when: ContextKeyExpr.and(ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext), primary: KeyMod.CtrlCmd | KeyCode.KeyW, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KeyW] }, - handler: (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { + handler: (accessor, ...args: unknown[]) => { const editorGroupService = accessor.get(IEditorGroupsService); - const commandsContext = getCommandsContext(accessor, resourceOrContext, context); - - let group: IEditorGroup | undefined; - if (commandsContext && typeof commandsContext.groupId === 'number') { - group = editorGroupService.getGroup(commandsContext.groupId); - } else { - group = editorGroupService.activeGroup; - } + const commandsContext = resolveCommandsContext(accessor, args); - if (group) { - editorGroupService.removeGroup(group); + if (commandsContext.groupedEditors.length) { + editorGroupService.removeGroup(commandsContext.groupedEditors[0].group); } } }); @@ -828,11 +787,10 @@ function registerCloseEditorCommands() { weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.KeyU), - handler: (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - return Promise.all(getEditorsContext(accessor, resourceOrContext, context).groups.map(async group => { - if (group) { - await group.closeEditors({ savedOnly: true, excludeSticky: true }, { preserveFocus: context?.preserveFocus }); - } + handler: (accessor, ...args: unknown[]) => { + const resolvedContext = resolveCommandsContext(accessor, args); + return Promise.all(resolvedContext.groupedEditors.map(async ({ group }) => { + await group.closeEditors({ savedOnly: true, excludeSticky: true }, { preserveFocus: resolvedContext.preserveFocus }); })); } }); @@ -843,24 +801,19 @@ function registerCloseEditorCommands() { when: undefined, primary: undefined, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyT }, - handler: (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - const { editors, groups } = getEditorsContext(accessor, resourceOrContext, context); - return Promise.all(groups.map(async group => { - if (group) { - const editorsToKeep = editors - .filter(editor => editor.groupId === group.id) - .map(editor => typeof editor.editorIndex === 'number' ? group.getEditorByIndex(editor.editorIndex) : group.activeEditor); - - const editorsToClose = group.getEditors(EditorsOrder.SEQUENTIAL, { excludeSticky: true }).filter(editor => !editorsToKeep.includes(editor)); - - for (const editorToKeep of editorsToKeep) { - if (editorToKeep) { - group.pinEditor(editorToKeep); - } - } + handler: (accessor, ...args: unknown[]) => { + const resolvedContext = resolveCommandsContext(accessor, args); - await group.closeEditors(editorsToClose, { preserveFocus: context?.preserveFocus }); + return Promise.all(resolvedContext.groupedEditors.map(async ({ group, editors }) => { + const editorsToClose = group.getEditors(EditorsOrder.SEQUENTIAL, { excludeSticky: true }).filter(editor => !editors.includes(editor)); + + for (const editorToKeep of editors) { + if (editorToKeep) { + group.pinEditor(editorToKeep); + } } + + await group.closeEditors(editorsToClose, { preserveFocus: resolvedContext.preserveFocus }); })); } }); @@ -870,16 +823,15 @@ function registerCloseEditorCommands() { weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: undefined, - handler: async (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - const editorGroupService = accessor.get(IEditorGroupsService); - - const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(accessor, resourceOrContext, context)); - if (group && editor) { + handler: async (accessor, ...args: unknown[]) => { + const resolvedContext = resolveCommandsContext(accessor, args); + if (resolvedContext.groupedEditors.length) { + const { group, editors } = resolvedContext.groupedEditors[0]; if (group.activeEditor) { group.pinEditor(group.activeEditor); } - await group.closeEditors({ direction: CloseDirection.RIGHT, except: editor, excludeSticky: true }, { preserveFocus: context?.preserveFocus }); + await group.closeEditors({ direction: CloseDirection.RIGHT, except: editors[0], excludeSticky: true }, { preserveFocus: resolvedContext.preserveFocus }); } } }); @@ -889,62 +841,64 @@ function registerCloseEditorCommands() { weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: undefined, - handler: async (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { + handler: async (accessor, ...args: unknown[]) => { const editorService = accessor.get(IEditorService); const editorResolverService = accessor.get(IEditorResolverService); const telemetryService = accessor.get(ITelemetryService); - const editorsAndGroup = resolveEditorsContext(getEditorsContext(accessor, resourceOrContext, context)); + const resolvedContext = resolveCommandsContext(accessor, args); const editorReplacements = new Map(); - for (const { editor, group } of editorsAndGroup) { - const untypedEditor = editor.toUntyped(); - if (!untypedEditor) { - return; // Resolver can only resolve untyped editors - } + for (const { group, editors } of resolvedContext.groupedEditors) { + for (const editor of editors) { + const untypedEditor = editor.toUntyped(); + if (!untypedEditor) { + return; // Resolver can only resolve untyped editors + } - untypedEditor.options = { ...editorService.activeEditorPane?.options, override: EditorResolution.PICK }; - const resolvedEditor = await editorResolverService.resolveEditor(untypedEditor, group); - if (!isEditorInputWithOptionsAndGroup(resolvedEditor)) { - return; - } + untypedEditor.options = { ...editorService.activeEditorPane?.options, override: EditorResolution.PICK }; + const resolvedEditor = await editorResolverService.resolveEditor(untypedEditor, group); + if (!isEditorInputWithOptionsAndGroup(resolvedEditor)) { + return; + } - let editorReplacementsInGroup = editorReplacements.get(group); - if (!editorReplacementsInGroup) { - editorReplacementsInGroup = []; - editorReplacements.set(group, editorReplacementsInGroup); - } + let editorReplacementsInGroup = editorReplacements.get(group); + if (!editorReplacementsInGroup) { + editorReplacementsInGroup = []; + editorReplacements.set(group, editorReplacementsInGroup); + } - editorReplacementsInGroup.push({ - editor: editor, - replacement: resolvedEditor.editor, - forceReplaceDirty: editor.resource?.scheme === Schemas.untitled, - options: resolvedEditor.options - }); - - // Telemetry - type WorkbenchEditorReopenClassification = { - owner: 'rebornix'; - comment: 'Identify how a document is reopened'; - scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File system provider scheme for the resource' }; - ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension for the resource' }; - from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The editor view type the resource is switched from' }; - to: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The editor view type the resource is switched to' }; - }; - - type WorkbenchEditorReopenEvent = { - scheme: string; - ext: string; - from: string; - to: string; - }; - - telemetryService.publicLog2('workbenchEditorReopen', { - scheme: editor.resource?.scheme ?? '', - ext: editor.resource ? extname(editor.resource) : '', - from: editor.editorId ?? '', - to: resolvedEditor.editor.editorId ?? '' - }); + editorReplacementsInGroup.push({ + editor: editor, + replacement: resolvedEditor.editor, + forceReplaceDirty: editor.resource?.scheme === Schemas.untitled, + options: resolvedEditor.options + }); + + // Telemetry + type WorkbenchEditorReopenClassification = { + owner: 'rebornix'; + comment: 'Identify how a document is reopened'; + scheme: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File system provider scheme for the resource' }; + ext: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'File extension for the resource' }; + from: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The editor view type the resource is switched from' }; + to: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The editor view type the resource is switched to' }; + }; + + type WorkbenchEditorReopenEvent = { + scheme: string; + ext: string; + from: string; + to: string; + }; + + telemetryService.publicLog2('workbenchEditorReopen', { + scheme: editor.resource?.scheme ?? '', + ext: editor.resource ? extname(editor.resource) : '', + from: editor.editorId ?? '', + to: resolvedEditor.editor.editorId ?? '' + }); + } } // Replace editor with resolved one and make active @@ -955,11 +909,12 @@ function registerCloseEditorCommands() { } }); - CommandsRegistry.registerCommand(CLOSE_EDITORS_AND_GROUP_COMMAND_ID, async (accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { + CommandsRegistry.registerCommand(CLOSE_EDITORS_AND_GROUP_COMMAND_ID, async (accessor: ServicesAccessor, ...args: unknown[]) => { const editorGroupService = accessor.get(IEditorGroupsService); - const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(accessor, resourceOrContext, context)); - if (group) { + const resolvedContext = resolveCommandsContext(accessor, args); + if (resolvedContext.groupedEditors.length) { + const { group } = resolvedContext.groupedEditors[0]; await group.closeAllEditors(); if (group.count === 0 && editorGroupService.getGroup(group.id) /* could be gone by now */) { @@ -1002,11 +957,15 @@ function registerFocusEditorGroupWihoutWrapCommands(): void { function registerSplitEditorInGroupCommands(): void { - async function splitEditorInGroup(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - const editorGroupService = accessor.get(IEditorGroupsService); + async function splitEditorInGroup(accessor: ServicesAccessor, resolvedContext: IResolvedEditorCommandsContext): Promise { const instantiationService = accessor.get(IInstantiationService); - const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(accessor, resourceOrContext, context)); + if (!resolvedContext.groupedEditors.length) { + return; + } + + const { group, editors } = resolvedContext.groupedEditors[0]; + const editor = editors[0]; if (!editor) { return; } @@ -1033,15 +992,22 @@ function registerSplitEditorInGroupCommands(): void { } }); } - run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - return splitEditorInGroup(accessor, resourceOrContext, context); + run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + return splitEditorInGroup(accessor, resolveCommandsContext(accessor, args)); } }); - async function joinEditorInGroup(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - const editorGroupService = accessor.get(IEditorGroupsService); + async function joinEditorInGroup(resolvedContext: IResolvedEditorCommandsContext): Promise { + if (!resolvedContext.groupedEditors.length) { + return; + } + + const { group, editors } = resolvedContext.groupedEditors[0]; + const editor = editors[0]; + if (!editor) { + return; + } - const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(accessor, resourceOrContext, context)); if (!(editor instanceof SideBySideEditorInput)) { return; } @@ -1079,8 +1045,8 @@ function registerSplitEditorInGroupCommands(): void { } }); } - run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - return joinEditorInGroup(accessor, resourceOrContext, context); + run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + return joinEditorInGroup(resolveCommandsContext(accessor, args)); } }); @@ -1094,14 +1060,18 @@ function registerSplitEditorInGroupCommands(): void { f1: true }); } - async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - const editorGroupService = accessor.get(IEditorGroupsService); + async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + const resolvedContext = resolveCommandsContext(accessor, args); + if (!resolvedContext.groupedEditors.length) { + return; + } + + const { editors } = resolvedContext.groupedEditors[0]; - const { editor } = resolveCommandsContext(editorGroupService, getCommandsContext(accessor, resourceOrContext, context)); - if (editor instanceof SideBySideEditorInput) { - await joinEditorInGroup(accessor, resourceOrContext, context); - } else if (editor) { - await splitEditorInGroup(accessor, resourceOrContext, context); + if (editors[0] instanceof SideBySideEditorInput) { + await joinEditorInGroup(resolvedContext); + } else if (editors[0]) { + await splitEditorInGroup(accessor, resolvedContext); } } }); @@ -1215,12 +1185,12 @@ function registerOtherEditorCommands(): void { weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyCode.Enter), - handler: async (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - const editorGroupService = accessor.get(IEditorGroupsService); - - const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(accessor, resourceOrContext, context)); - if (group && editor) { - return group.pinEditor(editor); + handler: async (accessor, ...args: unknown[]) => { + const resolvedContext = resolveCommandsContext(accessor, args); + for (const { group, editors } of resolvedContext.groupedEditors) { + for (const editor of editors) { + group.pinEditor(editor); + } } } }); @@ -1236,10 +1206,9 @@ function registerOtherEditorCommands(): void { } }); - function setEditorGroupLock(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext, locked?: boolean): void { - const editorGroupService = accessor.get(IEditorGroupsService); - - const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(accessor, resourceOrContext, context)); + function setEditorGroupLock(accessor: ServicesAccessor, locked: boolean | undefined, ...args: unknown[]): void { + const resolvedContext = resolveCommandsContext(accessor, args); + const group = resolvedContext.groupedEditors[0]?.group; group?.lock(locked ?? !group.isLocked); } @@ -1252,8 +1221,8 @@ function registerOtherEditorCommands(): void { f1: true }); } - async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - setEditorGroupLock(accessor, resourceOrContext, context); + async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + setEditorGroupLock(accessor, undefined, ...args); } }); @@ -1267,8 +1236,8 @@ function registerOtherEditorCommands(): void { f1: true }); } - async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - setEditorGroupLock(accessor, resourceOrContext, context, true); + async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + setEditorGroupLock(accessor, true, args); } }); @@ -1282,8 +1251,8 @@ function registerOtherEditorCommands(): void { f1: true }); } - async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - setEditorGroupLock(accessor, resourceOrContext, context, false); + async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + setEditorGroupLock(accessor, false, args); } }); @@ -1292,9 +1261,12 @@ function registerOtherEditorCommands(): void { weight: KeybindingWeight.WorkbenchContrib, when: ActiveEditorStickyContext.toNegated(), primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.Shift | KeyCode.Enter), - handler: async (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - for (const { editor, group } of resolveEditorsContext(getEditorsContext(accessor, resourceOrContext, context))) { - group.stickEditor(editor); + handler: async (accessor, ...args: unknown[]) => { + const resolvedContext = resolveCommandsContext(accessor, args); + for (const { group, editors } of resolvedContext.groupedEditors) { + for (const editor of editors) { + group.stickEditor(editor); + } } } }); @@ -1331,9 +1303,12 @@ function registerOtherEditorCommands(): void { weight: KeybindingWeight.WorkbenchContrib, when: ActiveEditorStickyContext, primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KeyK, KeyMod.Shift | KeyCode.Enter), - handler: async (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { - for (const { editor, group } of resolveEditorsContext(getEditorsContext(accessor, resourceOrContext, context))) { - group.unstickEditor(editor); + handler: async (accessor, ...args: unknown[]) => { + const resolvedContext = resolveCommandsContext(accessor, args); + for (const { group, editors } of resolvedContext.groupedEditors) { + for (const editor of editors) { + group.unstickEditor(editor); + } } } }); @@ -1343,16 +1318,14 @@ function registerOtherEditorCommands(): void { weight: KeybindingWeight.WorkbenchContrib, when: undefined, primary: undefined, - handler: (accessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => { + handler: (accessor, ...args: unknown[]) => { const editorGroupService = accessor.get(IEditorGroupsService); const quickInputService = accessor.get(IQuickInputService); - const commandsContext = getCommandsContext(accessor, resourceOrContext, context); - if (commandsContext && typeof commandsContext.groupId === 'number') { - const group = editorGroupService.getGroup(commandsContext.groupId); - if (group) { - editorGroupService.activateGroup(group); // we need the group to be active - } + const commandsContext = resolveCommandsContext(accessor, args); + const group = commandsContext.groupedEditors[0]?.group; + if (group) { + editorGroupService.activateGroup(group); // we need the group to be active } return quickInputService.quickAccess.show(ActiveGroupEditorsByMostRecentlyUsedQuickAccess.PREFIX); @@ -1360,130 +1333,6 @@ function registerOtherEditorCommands(): void { }); } -type EditorsContext = { editors: IEditorCommandsContext[]; groups: Array }; -export function getEditorsContext(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): EditorsContext { - const editorGroupService = accessor.get(IEditorGroupsService); - const listService = accessor.get(IListService); - - const editorContext = getMultiSelectedEditorContexts(getCommandsContext(accessor, resourceOrContext, context), listService, editorGroupService); - - const activeGroup = editorGroupService.activeGroup; - if (editorContext.length === 0 && activeGroup.activeEditor) { - // add the active editor as fallback - editorContext.push({ - groupId: activeGroup.id, - editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) - }); - } - - return { - editors: editorContext, - groups: distinct(editorContext.map(context => context.groupId)).map(groupId => editorGroupService.getGroup(groupId)) - }; -} - -export function resolveEditorsContext(context: EditorsContext): { editor: EditorInput; group: IEditorGroup }[] { - const { editors, groups } = context; - - const editorsAndGroup = editors.map(e => { - if (e.editorIndex === undefined) { - return undefined; - } - const group = groups.find(group => group && group.id === e.groupId); - const editor = group?.getEditorByIndex(e.editorIndex); - if (!editor || !group) { - return undefined; - } - return { editor, group }; - }); - - return coalesce(editorsAndGroup); -} - -export function getCommandsContext(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext | undefined { - const isUri = URI.isUri(resourceOrContext); - - const editorCommandsContext = isUri ? context : resourceOrContext ? resourceOrContext : context; - if (editorCommandsContext && isEditorCommandsContext(editorCommandsContext)) { - return editorCommandsContext; - } - - if (isUri) { - const editorGroupService = accessor.get(IEditorGroupsService); - const editorGroup = editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).find(group => isEqual(group.activeEditor?.resource, resourceOrContext)); - if (editorGroup) { - return { groupId: editorGroup.index, editorIndex: editorGroup.getIndexOfEditor(editorGroup.activeEditor!) }; - } - } - - return undefined; -} - -export function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup; editor?: EditorInput } { - - // Resolve from context - let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined; - let editor = group && context && typeof context.editorIndex === 'number' ? group.getEditorByIndex(context.editorIndex) ?? undefined : undefined; - - // Fallback to active group as needed - if (!group) { - group = editorGroupService.activeGroup; - } - - // Fallback to active editor as needed - if (!editor) { - editor = group.activeEditor ?? undefined; - } - - return { group, editor }; -} - -export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext | undefined, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] { - - // First check for a focused list to return the selected items from - const list = listService.lastFocusedList; - if (list instanceof List && list.getHTMLElement() === getActiveElement()) { - const elementToContext = (element: IEditorIdentifier | IEditorGroup) => { - if (isEditorGroup(element)) { - return { groupId: element.id, editorIndex: undefined }; - } - - const group = editorGroupService.getGroup(element.groupId); - - return { groupId: element.groupId, editorIndex: group ? group.getIndexOfEditor(element.editor) : -1 }; - }; - - const onlyEditorGroupAndEditor = (e: IEditorIdentifier | IEditorGroup) => isEditorGroup(e) || isEditorIdentifier(e); - - const focusedElements: Array = list.getFocusedElements().filter(onlyEditorGroupAndEditor); - const focus = editorContext ? editorContext : focusedElements.length ? focusedElements.map(elementToContext)[0] : undefined; // need to take into account when editor context is { group: group } - - if (focus) { - const selection: Array = list.getSelectedElements().filter(onlyEditorGroupAndEditor); - - if (selection.length > 1) { - return selection.map(elementToContext); - } - - return [focus]; - } - } - // Check editors selected in the group (tabs) - else { - const group = editorContext ? editorGroupService.getGroup(editorContext.groupId) : editorGroupService.activeGroup; - const editor = editorContext && editorContext.editorIndex !== undefined ? group?.getEditorByIndex(editorContext.editorIndex) : group?.activeEditor; - // If the editor is selected, return all selected editors otherwise only use the editors context - if (group && editor) { - if (group.isSelected(editor)) { - return group.selectedEditors.map(se => ({ groupId: group.id, editorIndex: group.getIndexOfEditor(se) })); - } - } - } - - // Otherwise go with passed in context - return !!editorContext ? [editorContext] : []; -} - export function setup(): void { registerActiveEditorMoveCopyCommand(); registerEditorGroupsLayoutCommands(); diff --git a/src/vs/workbench/browser/parts/editor/editorCommandsContext.ts b/src/vs/workbench/browser/parts/editor/editorCommandsContext.ts new file mode 100644 index 00000000000..b0915c07703 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/editorCommandsContext.ts @@ -0,0 +1,200 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { getActiveElement } from 'vs/base/browser/dom'; +import { List } from 'vs/base/browser/ui/list/listWidget'; +import { URI } from 'vs/base/common/uri'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { IListService } from 'vs/platform/list/browser/listService'; +import { IEditorCommandsContext, isEditorCommandsContext, IEditorIdentifier, isEditorIdentifier } from 'vs/workbench/common/editor'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { IEditorGroup, IEditorGroupsService, isEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export interface IResolvedEditorCommandsContext { + readonly groupedEditors: { + readonly group: IEditorGroup; + readonly editors: EditorInput[]; + }[]; + readonly preserveFocus: boolean; +} + +export function resolveCommandsContext(accessor: ServicesAccessor, commandArgs: unknown[]): IResolvedEditorCommandsContext { + const editorGroupsService = accessor.get(IEditorGroupsService); + + const commandContext = getCommandsContext(accessor, commandArgs); + const preserveFocus = commandContext.length ? commandContext[0].preserveFocus || false : false; + const resolvedContext: IResolvedEditorCommandsContext = { groupedEditors: [], preserveFocus }; + + for (const editorContext of commandContext) { + const groupAndEditor = getEditorAndGroupFromContext(editorContext, editorGroupsService); + if (!groupAndEditor) { + continue; + } + + const { group, editor } = groupAndEditor; + + // Find group context if already added + let groupContext = undefined; + for (const targetGroupContext of resolvedContext.groupedEditors) { + if (targetGroupContext.group.id === group.id) { + groupContext = targetGroupContext; + break; + } + } + + // Otherwise add new group context + if (!groupContext) { + groupContext = { group, editors: [] }; + resolvedContext.groupedEditors.push(groupContext); + } + + // Add editor to group context + if (editor) { + groupContext.editors.push(editor); + } + } + + return resolvedContext; +} + +function getCommandsContext(accessor: ServicesAccessor, commandArgs: unknown[]): IEditorCommandsContext[] { + // Figure out if command is executed from a list + const listService = accessor.get(IListService); + const list = listService.lastFocusedList; + let isListAction = list instanceof List && list.getHTMLElement() === getActiveElement(); + + // Get editor context for which the command was triggered + let editorContext = getEditorContextFromCommandArgs(accessor, commandArgs, isListAction); + + // If the editor context can not be determind use the active editor + if (!editorContext) { + const editorGroupService = accessor.get(IEditorGroupsService); + const activeGroup = editorGroupService.activeGroup; + const activeEditor = activeGroup.activeEditor; + editorContext = { groupId: activeGroup.id, editorIndex: activeEditor ? activeGroup.getIndexOfEditor(activeEditor) : undefined }; + isListAction = false; + } + + const multiEditorContext = getMultiSelectContext(accessor, editorContext, isListAction); + + // Make sure the command context is the first one in the list + return moveCurrentEditorContextToFront(editorContext, multiEditorContext); +} + +function moveCurrentEditorContextToFront(editorContext: IEditorCommandsContext, multiEditorContext: IEditorCommandsContext[]): IEditorCommandsContext[] { + if (multiEditorContext.length <= 1) { + return multiEditorContext; + } + + const editorContextIndex = multiEditorContext.findIndex(context => + context.groupId === editorContext.groupId && + context.editorIndex === editorContext.editorIndex + ); + + if (editorContextIndex !== -1) { + multiEditorContext.splice(editorContextIndex, 1); + multiEditorContext.unshift(editorContext); + } else if (editorContext.editorIndex === undefined) { + multiEditorContext.unshift(editorContext); + } else { + throw new Error('Editor context not found in multi editor context'); + } + + return multiEditorContext; +} + +function getEditorContextFromCommandArgs(accessor: ServicesAccessor, commandArgs: unknown[], isListAcion: boolean): IEditorCommandsContext | undefined { + // We only know how to extraxt the command context from URI and IEditorCommandsContext arguments + const filteredArgs = commandArgs.filter(arg => isEditorCommandsContext(arg) || URI.isUri(arg)); + + // If the command arguments contain an editor context, use it + for (const arg of filteredArgs) { + if (isEditorCommandsContext(arg)) { + return arg; + } + } + + const editorService = accessor.get(IEditorService); + + // Otherwise, try to find the editor group by the URI of the resource + for (const uri of filteredArgs as URI[]) { + const editorIdentifiers = editorService.findEditors(uri); + if (editorIdentifiers.length) { + return editorIdentifiers[0]; + } + } + + const listService = accessor.get(IListService); + const editorGroupsService = accessor.get(IEditorGroupsService); + + // If there is no context in the arguments, try to find the context from the focused list + // if the action was executed from a list + if (isListAcion) { + const list = listService.lastFocusedList as List; + for (const focusedElement of list.getFocusedElements()) { + if (isGroupOrEditor(focusedElement)) { + return groupOrEditorToEditorContext(focusedElement, undefined, editorGroupsService); + } + } + } + + return undefined; +} + +function getMultiSelectContext(accessor: ServicesAccessor, editorContext: IEditorCommandsContext, isListAction: boolean): IEditorCommandsContext[] { + const listService = accessor.get(IListService); + const editorGroupsService = accessor.get(IEditorGroupsService); + + // If the action was executed from a list, return all selected editors + if (isListAction) { + const list = listService.lastFocusedList as List; + const selection = list.getSelectedElements().filter(isGroupOrEditor); + + if (selection.length > 1) { + return selection.map(e => groupOrEditorToEditorContext(e, editorContext.preserveFocus, editorGroupsService)); + } + } + // Check editors selected in the group (tabs) + else { + const group = editorGroupsService.getGroup(editorContext.groupId); + const editor = editorContext.editorIndex !== undefined ? group?.getEditorByIndex(editorContext.editorIndex) : group?.activeEditor; + // If the editor is selected, return all selected editors otherwise only use the editors context + if (group && editor && group.isSelected(editor)) { + return group.selectedEditors.map(editor => groupOrEditorToEditorContext({ editor, groupId: group.id }, editorContext.preserveFocus, editorGroupsService)); + } + } + + // Otherwise go with passed in context + return [editorContext]; +} + +function groupOrEditorToEditorContext(element: IEditorIdentifier | IEditorGroup, preserveFocus: boolean | undefined, editorGroupsService: IEditorGroupsService): IEditorCommandsContext { + if (isEditorGroup(element)) { + return { groupId: element.id, editorIndex: undefined, preserveFocus }; + } + + const group = editorGroupsService.getGroup(element.groupId); + + return { groupId: element.groupId, editorIndex: group ? group.getIndexOfEditor(element.editor) : -1, preserveFocus }; +} + +function isGroupOrEditor(element: unknown): element is IEditorIdentifier | IEditorGroup { + return isEditorGroup(element) || isEditorIdentifier(element); +} + +function getEditorAndGroupFromContext(commandContext: IEditorCommandsContext, editorGroupsService: IEditorGroupsService): { group: IEditorGroup; editor: EditorInput | undefined } | undefined { + const group = editorGroupsService.getGroup(commandContext.groupId); + if (!group) { + return undefined; + } + + if (commandContext.editorIndex === undefined) { + return { group, editor: undefined }; + } + + const editor = group.getEditorByIndex(commandContext.editorIndex); + return { group, editor }; +} diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 81bf68c3e0b..97fb6ec7c65 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -25,7 +25,7 @@ import { isWeb, isWindows } from 'vs/base/common/platform'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { getResourceForCommand, getMultiSelectedResources, getOpenEditorsViewMultiSelection, IExplorerService } from 'vs/workbench/contrib/files/browser/files'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; -import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/editor/editorCommands'; +import { resolveCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommandsContext'; import { Schemas } from 'vs/base/common/network'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; @@ -35,7 +35,6 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { basename, joinPath, isEqual } from 'vs/base/common/resources'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { coalesce } from 'vs/base/common/arrays'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; @@ -514,13 +513,13 @@ CommandsRegistry.registerCommand({ handler: (accessor, _: URI | object, editorContext: IEditorCommandsContext) => { const editorGroupService = accessor.get(IEditorGroupsService); - const contexts = getMultiSelectedEditorContexts(editorContext, accessor.get(IListService), accessor.get(IEditorGroupsService)); + const resolvedContext = resolveCommandsContext(accessor, [editorContext]); let groups: readonly IEditorGroup[] | undefined = undefined; - if (!contexts.length) { + if (!resolvedContext.groupedEditors.length) { groups = editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE); } else { - groups = coalesce(contexts.map(context => editorGroupService.getGroup(context.groupId))); + groups = resolvedContext.groupedEditors.map(({ group }) => group); } return saveDirtyEditorsOfGroups(accessor, groups, { reason: SaveReason.EXPLICIT }); diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts index 5b1ab36ce10..5419dba32dc 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts @@ -11,12 +11,10 @@ import { localize2 } from 'vs/nls'; import { Action2, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { getCommandsContext, resolveCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommands'; -import { IEditorCommandsContext } from 'vs/workbench/common/editor'; +import { resolveCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommandsContext'; import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor'; import { MultiDiffEditor } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor'; import { MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; export class GoToFileAction extends Action2 { @@ -75,9 +73,15 @@ export class CollapseAllAction extends Action2 { }); } - async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - const { editor } = resolveCommandsContext(accessor.get(IEditorGroupsService), getCommandsContext(accessor, resourceOrContext, context)); + async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + const resolvedContext = resolveCommandsContext(accessor, args); + const groupContext = resolvedContext.groupedEditors[0]; + if (!groupContext) { + return; + } + + const editor = groupContext.editors[0]; if (editor instanceof MultiDiffEditorInput) { const viewModel = await editor.getViewModel(); viewModel.collapseAll(); @@ -102,9 +106,15 @@ export class ExpandAllAction extends Action2 { }); } - async run(accessor: ServicesAccessor, resourceOrContext?: URI | IEditorCommandsContext, context?: IEditorCommandsContext): Promise { - const { editor } = resolveCommandsContext(accessor.get(IEditorGroupsService), getCommandsContext(accessor, resourceOrContext, context)); + async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { + const resolvedContext = resolveCommandsContext(accessor, args); + + const groupContext = resolvedContext.groupedEditors[0]; + if (!groupContext) { + return; + } + const editor = groupContext.editors[0]; if (editor instanceof MultiDiffEditorInput) { const viewModel = await editor.getViewModel(); viewModel.expandAll(); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts index 3cc7faf8354..8ef65e7a121 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/coreActions.ts @@ -13,7 +13,7 @@ import { getNotebookEditorFromEditorPane, IActiveNotebookEditor, ICellViewModel, import { INTERACTIVE_WINDOW_IS_ACTIVE_EDITOR, NOTEBOOK_EDITOR_EDITABLE, NOTEBOOK_EDITOR_FOCUSED, NOTEBOOK_IS_ACTIVE_EDITOR, NOTEBOOK_KERNEL_COUNT, NOTEBOOK_KERNEL_SOURCE_COUNT } from 'vs/workbench/contrib/notebook/common/notebookContextKeys'; import { ICellRange, isICellRange } from 'vs/workbench/contrib/notebook/common/notebookRange'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorCommandsContext } from 'vs/workbench/common/editor'; +import { isEditorCommandsContext } from 'vs/workbench/common/editor'; import { INotebookEditorService } from 'vs/workbench/contrib/notebook/browser/services/notebookEditorService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from 'vs/base/common/actions'; @@ -220,9 +220,6 @@ export abstract class NotebookMultiCellAction extends Action2 { private isCellToolbarContext(context?: unknown): context is INotebookCellToolbarActionContext { return !!context && !!(context as INotebookActionContext).notebookEditor && (context as any).$mid === MarshalledId.NotebookCellActionContext; } - private isEditorContext(context?: unknown): boolean { - return !!context && (context as IEditorCommandsContext).groupId !== undefined; - } /** * The action/command args are resolved in following order @@ -233,7 +230,7 @@ export abstract class NotebookMultiCellAction extends Action2 { async run(accessor: ServicesAccessor, ...additionalArgs: any[]): Promise { const context = additionalArgs[0]; const isFromCellToolbar = this.isCellToolbarContext(context); - const isFromEditorToolbar = this.isEditorContext(context); + const isFromEditorToolbar = isEditorCommandsContext(context); const from = isFromCellToolbar ? 'cellToolbar' : (isFromEditorToolbar ? 'editorToolbar' : 'other'); const telemetryService = accessor.get(ITelemetryService); From acb46a47f8b4d7f419ba5b6c961b8501d3366a0f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 5 Jul 2024 10:16:01 +0200 Subject: [PATCH 0288/2222] debt - skip flaky watcher tests (#220036) --- .../platform/files/test/node/nodejsWatcher.integrationTest.ts | 4 ++-- .../platform/files/test/node/parcelWatcher.integrationTest.ts | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts b/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts index d47cf0f69fd..c53f15426ab 100644 --- a/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/nodejsWatcher.integrationTest.ts @@ -8,7 +8,7 @@ import assert from 'assert'; import { tmpdir } from 'os'; import { basename, dirname, join } from 'vs/base/common/path'; import { Promises, RimRafMode } from 'vs/base/node/pfs'; -import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { FileChangeFilter, FileChangeType } from 'vs/platform/files/common/files'; import { INonRecursiveWatchRequest, IRecursiveWatcherWithSubscribe } from 'vs/platform/files/common/watcher'; import { watchFileContents } from 'vs/platform/files/node/watcher/nodejs/nodejsWatcherLib'; @@ -30,7 +30,7 @@ import { TestParcelWatcher } from 'vs/platform/files/test/node/parcelWatcher.int // mocha but generally). as such they will run only on demand // whenever we update the watcher library. -((process.env['BUILD_SOURCEVERSION'] || process.env['CI']) ? suite.skip : flakySuite)('File Watcher (node.js)', () => { +suite.skip('File Watcher (node.js)', () => { class TestNodeJSWatcher extends NodeJSWatcher { diff --git a/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts b/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts index 633e4033415..4370d82bf90 100644 --- a/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts +++ b/src/vs/platform/files/test/node/parcelWatcher.integrationTest.ts @@ -10,7 +10,7 @@ import { timeout } from 'vs/base/common/async'; import { dirname, join } from 'vs/base/common/path'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { Promises, RimRafMode } from 'vs/base/node/pfs'; -import { flakySuite, getRandomTestPath } from 'vs/base/test/node/testUtils'; +import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { FileChangeFilter, FileChangeType, IFileChange } from 'vs/platform/files/common/files'; import { ParcelWatcher } from 'vs/platform/files/node/watcher/parcel/parcelWatcher'; import { IRecursiveWatchRequest } from 'vs/platform/files/common/watcher'; @@ -65,7 +65,7 @@ export class TestParcelWatcher extends ParcelWatcher { // mocha but generally). as such they will run only on demand // whenever we update the watcher library. -((process.env['BUILD_SOURCEVERSION'] || process.env['CI']) ? suite.skip : flakySuite)('File Watcher (parcel)', () => { +suite.skip('File Watcher (parcel)', () => { let testDir: string; let watcher: TestParcelWatcher; From 3479868ac7f40376a75770f77ceb5fee6cc7a51f Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 5 Jul 2024 10:45:03 +0200 Subject: [PATCH 0289/2222] Fixes https://github.com/microsoft/vscode-copilot/issues/6556 (#219996) * Fixes https://github.com/microsoft/vscode-copilot/issues/6556 --- src/vs/editor/common/core/textEdit.ts | 9 ++ .../algorithms/diffAlgorithm.ts | 12 +++ .../computeMovedLines.ts | 6 +- .../defaultLinesDiffComputer.ts | 29 ++++- .../linesSliceCharSequence.ts | 82 +++++++------- src/vs/editor/common/diff/rangeMapping.ts | 78 ++++++++++++++ .../diffing/defaultLinesDiffComputer.test.ts | 6 +- .../editor/test/node/diffing/fixtures.test.ts | 31 +++++- .../advanced.expected.diff.json | 2 +- .../diffing/fixtures/sorted-offsets/1.tst | 102 ++++++++++++++++++ .../diffing/fixtures/sorted-offsets/2.tst | 87 +++++++++++++++ .../advanced.expected.diff.json | 42 ++++++++ .../sorted-offsets/legacy.expected.diff.json | 74 +++++++++++++ 13 files changed, 502 insertions(+), 58 deletions(-) create mode 100644 src/vs/editor/test/node/diffing/fixtures/sorted-offsets/1.tst create mode 100644 src/vs/editor/test/node/diffing/fixtures/sorted-offsets/2.tst create mode 100644 src/vs/editor/test/node/diffing/fixtures/sorted-offsets/advanced.expected.diff.json create mode 100644 src/vs/editor/test/node/diffing/fixtures/sorted-offsets/legacy.expected.diff.json diff --git a/src/vs/editor/common/core/textEdit.ts b/src/vs/editor/common/core/textEdit.ts index 3b0d6d2f70a..4d001a0fb7a 100644 --- a/src/vs/editor/common/core/textEdit.ts +++ b/src/vs/editor/common/core/textEdit.ts @@ -225,6 +225,15 @@ export class LineBasedText extends AbstractText { } } +export class ArrayText extends LineBasedText { + constructor(lines: string[]) { + super( + lineNumber => lines[lineNumber - 1], + lines.length + ); + } +} + export class StringText extends AbstractText { private readonly _t = new PositionOffsetTransformer(this.value); diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts index 50fbf66deba..2de4635030d 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/algorithms/diffAlgorithm.ts @@ -52,6 +52,18 @@ export class SequenceDiff { ); } + public static assertSorted(sequenceDiffs: SequenceDiff[]): void { + let last: SequenceDiff | undefined = undefined; + for (const cur of sequenceDiffs) { + if (last) { + if (!(last.seq1Range.endExclusive <= cur.seq1Range.start && last.seq2Range.endExclusive <= cur.seq2Range.start)) { + throw new BugIndicatingError('Sequence diffs must be sorted'); + } + } + last = cur; + } + } + constructor( public readonly seq1Range: OffsetRange, public readonly seq2Range: OffsetRange, diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts index 8f7183211d7..6a46557a177 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/computeMovedLines.ts @@ -9,10 +9,10 @@ import { pushMany, compareBy, numberComparator, reverseOrder } from 'vs/base/com import { MonotonousArray, findLastMonotonous } from 'vs/base/common/arraysFind'; import { SetMap } from 'vs/base/common/map'; import { LineRange, LineRangeSet } from 'vs/editor/common/core/lineRange'; -import { OffsetRange } from 'vs/editor/common/core/offsetRange'; import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; import { LineRangeFragment, isSpace } from 'vs/editor/common/diff/defaultLinesDiffComputer/utils'; import { MyersDiffAlgorithm } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm'; +import { Range } from 'vs/editor/common/core/range'; export function computeMovedLines( changes: DetailedLineRangeMapping[], @@ -260,8 +260,8 @@ function areLinesSimilar(line1: string, line2: string, timeout: ITimeout): boole const myersDiffingAlgorithm = new MyersDiffAlgorithm(); const result = myersDiffingAlgorithm.compute( - new LinesSliceCharSequence([line1], new OffsetRange(0, 1), false), - new LinesSliceCharSequence([line2], new OffsetRange(0, 1), false), + new LinesSliceCharSequence([line1], new Range(1, 1, 1, line1.length), false), + new LinesSliceCharSequence([line2], new Range(1, 1, 1, line2.length), false), timeout ); let commonNonSpaceCharCount = 0; diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts index 2c3ee2fe094..5573f684526 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.ts @@ -17,7 +17,7 @@ import { extendDiffsToEntireWordIfAppropriate, optimizeSequenceDiffs, removeShor import { LineSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/lineSequence'; import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; import { ILinesDiffComputer, ILinesDiffComputerOptions, LinesDiff, MovedText } from 'vs/editor/common/diff/linesDiffComputer'; -import { DetailedLineRangeMapping, RangeMapping } from '../rangeMapping'; +import { DetailedLineRangeMapping, LineRangeMapping, RangeMapping } from '../rangeMapping'; export class DefaultLinesDiffComputer implements ILinesDiffComputer { private readonly dynamicProgrammingDiffing = new DynamicProgrammingDiffing(); @@ -167,7 +167,9 @@ export class DefaultLinesDiffComputer implements ILinesDiffComputer { for (const ic of c.innerChanges) { const valid = validatePosition(ic.modifiedRange.getStartPosition(), modifiedLines) && validatePosition(ic.modifiedRange.getEndPosition(), modifiedLines) && validatePosition(ic.originalRange.getStartPosition(), originalLines) && validatePosition(ic.originalRange.getEndPosition(), originalLines); - if (!valid) { return false; } + if (!valid) { + return false; + } } if (!validateRange(c.modified, modifiedLines) || !validateRange(c.original, originalLines)) { return false; @@ -208,18 +210,28 @@ export class DefaultLinesDiffComputer implements ILinesDiffComputer { } private refineDiff(originalLines: string[], modifiedLines: string[], diff: SequenceDiff, timeout: ITimeout, considerWhitespaceChanges: boolean): { mappings: RangeMapping[]; hitTimeout: boolean } { - const slice1 = new LinesSliceCharSequence(originalLines, diff.seq1Range, considerWhitespaceChanges); - const slice2 = new LinesSliceCharSequence(modifiedLines, diff.seq2Range, considerWhitespaceChanges); + const lineRangeMapping = toLineRangeMapping(diff); + const rangeMapping = lineRangeMapping.toRangeMapping2(originalLines, modifiedLines); + + const slice1 = new LinesSliceCharSequence(originalLines, rangeMapping.originalRange, considerWhitespaceChanges); + const slice2 = new LinesSliceCharSequence(modifiedLines, rangeMapping.modifiedRange, considerWhitespaceChanges); const diffResult = slice1.length + slice2.length < 500 ? this.dynamicProgrammingDiffing.compute(slice1, slice2, timeout) : this.myersDiffingAlgorithm.compute(slice1, slice2, timeout); + const check = false; + let diffs = diffResult.diffs; + if (check) { SequenceDiff.assertSorted(diffs); } diffs = optimizeSequenceDiffs(slice1, slice2, diffs); + if (check) { SequenceDiff.assertSorted(diffs); } diffs = extendDiffsToEntireWordIfAppropriate(slice1, slice2, diffs); + if (check) { SequenceDiff.assertSorted(diffs); } diffs = removeShortMatches(slice1, slice2, diffs); + if (check) { SequenceDiff.assertSorted(diffs); } diffs = removeVeryShortMatchingTextBetweenLongDiffs(slice1, slice2, diffs); + if (check) { SequenceDiff.assertSorted(diffs); } const result = diffs.map( (d) => @@ -229,6 +241,8 @@ export class DefaultLinesDiffComputer implements ILinesDiffComputer { ) ); + if (check) { RangeMapping.assertSorted(result); } + // Assert: result applied on original should be the same as diff applied to original return { @@ -312,3 +326,10 @@ export function getLineRangeMapping(rangeMapping: RangeMapping, originalLines: s return new DetailedLineRangeMapping(originalLineRange, modifiedLineRange, [rangeMapping]); } + +function toLineRangeMapping(sequenceDiff: SequenceDiff) { + return new LineRangeMapping( + new LineRange(sequenceDiff.seq1Range.start + 1, sequenceDiff.seq1Range.endExclusive + 1), + new LineRange(sequenceDiff.seq2Range.start + 1, sequenceDiff.seq2Range.endExclusive + 1), + ); +} diff --git a/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts b/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts index 9edc63d335f..7761599484b 100644 --- a/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts +++ b/src/vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence.ts @@ -13,52 +13,39 @@ import { isSpace } from 'vs/editor/common/diff/defaultLinesDiffComputer/utils'; export class LinesSliceCharSequence implements ISequence { private readonly elements: number[] = []; - private readonly firstCharOffsetByLine: number[] = []; - public readonly lineRange: OffsetRange; - // To account for trimming - private readonly additionalOffsetByLine: number[] = []; - - constructor(public readonly lines: string[], lineRange: OffsetRange, public readonly considerWhitespaceChanges: boolean) { - // This slice has to have lineRange.length many \n! (otherwise diffing against an empty slice will be problematic) - // (Unless it covers the entire document, in that case the other slice also has to cover the entire document ands it's okay) - - // If the slice covers the end, but does not start at the beginning, we include just the \n of the previous line. - let trimFirstLineFully = false; - if (lineRange.start > 0 && lineRange.endExclusive >= lines.length) { - lineRange = new OffsetRange(lineRange.start - 1, lineRange.endExclusive); - trimFirstLineFully = true; - } + private readonly firstElementOffsetByLineIdx: number[] = []; + private readonly lineStartOffsets: number[] = []; + private readonly trimmedWsLengthsByLineIdx: number[] = []; + + constructor(public readonly lines: string[], private readonly range: Range, public readonly considerWhitespaceChanges: boolean) { + this.firstElementOffsetByLineIdx.push(0); + for (let lineNumber = this.range.startLineNumber; lineNumber <= this.range.endLineNumber; lineNumber++) { + let line = lines[lineNumber - 1]; + let lineStartOffset = 0; + if (lineNumber === this.range.startLineNumber && this.range.startColumn > 1) { + lineStartOffset = this.range.startColumn - 1; + line = line.substring(lineStartOffset); + } + this.lineStartOffsets.push(lineStartOffset); - this.lineRange = lineRange; - - this.firstCharOffsetByLine[0] = 0; - for (let i = this.lineRange.start; i < this.lineRange.endExclusive; i++) { - let line = lines[i]; - let offset = 0; - if (trimFirstLineFully) { - offset = line.length; - line = ''; - trimFirstLineFully = false; - } else if (!considerWhitespaceChanges) { + let trimmedWsLength = 0; + if (!considerWhitespaceChanges) { const trimmedStartLine = line.trimStart(); - offset = line.length - trimmedStartLine.length; + trimmedWsLength = line.length - trimmedStartLine.length; line = trimmedStartLine.trimEnd(); } + this.trimmedWsLengthsByLineIdx.push(trimmedWsLength); - this.additionalOffsetByLine.push(offset); - - for (let i = 0; i < line.length; i++) { + const lineLength = lineNumber === this.range.endLineNumber ? Math.min(this.range.endColumn - 1 - lineStartOffset - trimmedWsLength, line.length) : line.length; + for (let i = 0; i < lineLength; i++) { this.elements.push(line.charCodeAt(i)); } - // Don't add an \n that does not exist in the document. - if (i < lines.length - 1) { + if (lineNumber < this.range.endLineNumber) { this.elements.push('\n'.charCodeAt(0)); - this.firstCharOffsetByLine[i - this.lineRange.start + 1] = this.elements.length; + this.firstElementOffsetByLineIdx.push(this.elements.length); } } - // To account for the last line - this.additionalOffsetByLine.push(0); } toString() { @@ -111,18 +98,23 @@ export class LinesSliceCharSequence implements ISequence { return score; } - public translateOffset(offset: number): Position { + public translateOffset(offset: number, preference: 'left' | 'right' = 'right'): Position { // find smallest i, so that lineBreakOffsets[i] <= offset using binary search - if (this.lineRange.isEmpty) { - return new Position(this.lineRange.start + 1, 1); - } - - const i = findLastIdxMonotonous(this.firstCharOffsetByLine, (value) => value <= offset); - return new Position(this.lineRange.start + i + 1, offset - this.firstCharOffsetByLine[i] + this.additionalOffsetByLine[i] + 1); + const i = findLastIdxMonotonous(this.firstElementOffsetByLineIdx, (value) => value <= offset); + const lineOffset = offset - this.firstElementOffsetByLineIdx[i]; + return new Position( + this.range.startLineNumber + i, + 1 + this.lineStartOffsets[i] + lineOffset + ((lineOffset === 0 && preference === 'left') ? 0 : this.trimmedWsLengthsByLineIdx[i]) + ); } public translateRange(range: OffsetRange): Range { - return Range.fromPositions(this.translateOffset(range.start), this.translateOffset(range.endExclusive)); + const pos1 = this.translateOffset(range.start, 'right'); + const pos2 = this.translateOffset(range.endExclusive, 'left'); + if (pos2.isBefore(pos1)) { + return Range.fromPositions(pos2, pos2); + } + return Range.fromPositions(pos1, pos2); } /** @@ -161,8 +153,8 @@ export class LinesSliceCharSequence implements ISequence { } public extendToFullLines(range: OffsetRange): OffsetRange { - const start = findLastMonotonous(this.firstCharOffsetByLine, x => x <= range.start) ?? 0; - const end = findFirstMonotonous(this.firstCharOffsetByLine, x => range.endExclusive <= x) ?? this.elements.length; + const start = findLastMonotonous(this.firstElementOffsetByLineIdx, x => x <= range.start) ?? 0; + const end = findFirstMonotonous(this.firstElementOffsetByLineIdx, x => range.endExclusive <= x) ?? this.elements.length; return new OffsetRange(start, end); } } diff --git a/src/vs/editor/common/diff/rangeMapping.ts b/src/vs/editor/common/diff/rangeMapping.ts index f15b2a05310..da9c3a49109 100644 --- a/src/vs/editor/common/diff/rangeMapping.ts +++ b/src/vs/editor/common/diff/rangeMapping.ts @@ -5,6 +5,7 @@ import { BugIndicatingError } from 'vs/base/common/errors'; import { LineRange } from 'vs/editor/common/core/lineRange'; +import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { AbstractText, SingleTextEdit } from 'vs/editor/common/core/textEdit'; @@ -118,6 +119,70 @@ export class LineRangeMapping { ); } } + + /** + * This method assumes that the LineRangeMapping describes a valid diff! + * I.e. if one range is empty, the other range cannot be the entire document. + * It avoids various problems when the line range points to non-existing line-numbers. + */ + public toRangeMapping2(original: string[], modified: string[]): RangeMapping { + if (isValidLineNumber(this.original.endLineNumberExclusive, original) + && isValidLineNumber(this.modified.endLineNumberExclusive, modified)) { + return new RangeMapping( + new Range(this.original.startLineNumber, 1, this.original.endLineNumberExclusive, 1), + new Range(this.modified.startLineNumber, 1, this.modified.endLineNumberExclusive, 1), + ); + } + + if (!this.original.isEmpty && !this.modified.isEmpty) { + return new RangeMapping( + Range.fromPositions( + new Position(this.original.startLineNumber, 1), + normalizePosition(new Position(this.original.endLineNumberExclusive - 1, Number.MAX_SAFE_INTEGER), original) + ), + Range.fromPositions( + new Position(this.modified.startLineNumber, 1), + normalizePosition(new Position(this.modified.endLineNumberExclusive - 1, Number.MAX_SAFE_INTEGER), modified) + ), + ); + } + + if (this.original.startLineNumber > 1 && this.modified.startLineNumber > 1) { + return new RangeMapping( + Range.fromPositions( + normalizePosition(new Position(this.original.startLineNumber - 1, Number.MAX_SAFE_INTEGER), original), + normalizePosition(new Position(this.original.endLineNumberExclusive - 1, Number.MAX_SAFE_INTEGER), original) + ), + Range.fromPositions( + normalizePosition(new Position(this.modified.startLineNumber - 1, Number.MAX_SAFE_INTEGER), modified), + normalizePosition(new Position(this.modified.endLineNumberExclusive - 1, Number.MAX_SAFE_INTEGER), modified) + ), + ); + } + + // Situation now: one range is empty and one range touches the last line and one range starts at line 1. + // I don't think this can happen. + + throw new BugIndicatingError(); + } +} + +function normalizePosition(position: Position, content: string[]): Position { + if (position.lineNumber < 1) { + return new Position(1, 1); + } + if (position.lineNumber > content.length) { + return new Position(content.length, content[content.length - 1].length + 1); + } + const line = content[position.lineNumber - 1]; + if (position.column > line.length + 1) { + return new Position(position.lineNumber, line.length + 1); + } + return position; +} + +function isValidLineNumber(lineNumber: number, lines: string[]): boolean { + return lineNumber >= 1 && lineNumber <= lines.length; } /** @@ -161,6 +226,19 @@ export class DetailedLineRangeMapping extends LineRangeMapping { * Maps a range in the original text model to a range in the modified text model. */ export class RangeMapping { + public static assertSorted(rangeMappings: RangeMapping[]): void { + for (let i = 1; i < rangeMappings.length; i++) { + const previous = rangeMappings[i - 1]; + const current = rangeMappings[i]; + if (!( + previous.originalRange.getEndPosition().isBeforeOrEqual(current.originalRange.getStartPosition()) + && previous.modifiedRange.getEndPosition().isBeforeOrEqual(current.modifiedRange.getStartPosition()) + )) { + throw new BugIndicatingError('Range mappings must be sorted'); + } + } + } + /** * The original range. */ diff --git a/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts index 57a34dfc364..72ad7fe2186 100644 --- a/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts +++ b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts @@ -17,8 +17,8 @@ suite('myers', () => { ensureNoDisposablesAreLeakedInTestSuite(); test('1', () => { - const s1 = new LinesSliceCharSequence(['hello world'], new OffsetRange(0, 1), true); - const s2 = new LinesSliceCharSequence(['hallo welt'], new OffsetRange(0, 1), true); + const s1 = new LinesSliceCharSequence(['hello world'], new Range(1, 1, 1, Number.MAX_SAFE_INTEGER), true); + const s2 = new LinesSliceCharSequence(['hallo welt'], new Range(1, 1, 1, Number.MAX_SAFE_INTEGER), true); const a = true ? new MyersDiffAlgorithm() : new DynamicProgrammingDiffing(); a.compute(s1, s2); @@ -83,7 +83,7 @@ suite('LinesSliceCharSequence', () => { 'line4: hello world', 'line5: bazz', ], - new OffsetRange(1, 4), true + new Range(2, 1, 5, 1), true ); test('translateOffset', () => { diff --git a/src/vs/editor/test/node/diffing/fixtures.test.ts b/src/vs/editor/test/node/diffing/fixtures.test.ts index 901ed758ff1..0ed9a8b11fe 100644 --- a/src/vs/editor/test/node/diffing/fixtures.test.ts +++ b/src/vs/editor/test/node/diffing/fixtures.test.ts @@ -8,11 +8,13 @@ import { existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs import { join, resolve } from 'path'; import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; import { FileAccess } from 'vs/base/common/network'; -import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { DetailedLineRangeMapping, RangeMapping } from 'vs/editor/common/diff/rangeMapping'; import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer'; import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; import { Range } from 'vs/editor/common/core/range'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { AbstractText, ArrayText, SingleTextEdit, TextEdit } from 'vs/editor/common/core/textEdit'; +import { LinesDiff } from 'vs/editor/common/diff/linesDiffComputer'; suite('diffing fixtures', () => { ensureNoDisposablesAreLeakedInTestSuite(); @@ -47,7 +49,15 @@ suite('diffing fixtures', () => { const ignoreTrimWhitespace = folder.indexOf('trimws') >= 0; const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: true }); + if (diffingAlgoName === 'advanced' && !ignoreTrimWhitespace) { + assertDiffCorrectness(diff, firstContentLines, secondContentLines); + } + function getDiffs(changes: readonly DetailedLineRangeMapping[]): IDetailedDiff[] { + for (const c of changes) { + RangeMapping.assertSorted(c.innerChanges ?? []); + } + return changes.map(c => ({ originalRange: c.original.toString(), modifiedRange: c.modified.toString(), @@ -123,7 +133,7 @@ suite('diffing fixtures', () => { } test(`test`, () => { - runTest('issue-214049', 'advanced'); + runTest('invalid-diff-trimws', 'advanced'); }); for (const folder of folders) { @@ -160,3 +170,20 @@ interface IMoveInfo { changes: IDetailedDiff[]; } + +function assertDiffCorrectness(diff: LinesDiff, original: string[], modified: string[]) { + const allInnerChanges = diff.changes.flatMap(c => c.innerChanges!); + const edit = rangeMappingsToTextEdit(allInnerChanges, new ArrayText(modified)); + const result = edit.normalize().apply(new ArrayText(original)); + + assert.deepStrictEqual(result, modified.join('\n')); +} + +function rangeMappingsToTextEdit(rangeMappings: readonly RangeMapping[], modified: AbstractText): TextEdit { + return new TextEdit(rangeMappings.map(m => { + return new SingleTextEdit( + m.originalRange, + modified.getValueOfRange(m.modifiedRange) + ); + })); +} diff --git a/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/advanced.expected.diff.json index bdaa293acd4..b9b792ab068 100644 --- a/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/advanced.expected.diff.json +++ b/src/vs/editor/test/node/diffing/fixtures/invalid-diff-trimws/advanced.expected.diff.json @@ -13,7 +13,7 @@ "modifiedRange": "[742,751)", "innerChanges": [ { - "originalRange": "[742,3 -> 742,3]", + "originalRange": "[742,1 -> 742,1]", "modifiedRange": "[742,1 -> 743,8]" }, { diff --git a/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/1.tst b/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/1.tst new file mode 100644 index 00000000000..7d4c1415308 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/1.tst @@ -0,0 +1,102 @@ +import { neverAbortedSignal } from './common/abort'; +import { defer } from './common/defer'; +import { EventEmitter } from './common/Event'; +import { ExecuteWrapper } from './common/Executor'; +import { BulkheadRejectedError } from './errors/BulkheadRejectedError'; +import { TaskCancelledError } from './errors/Errors'; +import { IDefaultPolicyContext, IPolicy } from './Policy'; + +interface IQueueItem { + signal: AbortSignal; + fn(context: IDefaultPolicyContext): Promise | T; + resolve(value: T): void; + reject(error: Error): void; +} + +export class BulkheadPolicy implements IPolicy { + public declare readonly _altReturn: never; + + private active = 0; + private readonly queue: Array> = []; + private readonly onRejectEmitter = new EventEmitter(); + private readonly executor = new ExecuteWrapper(); + + /** + * @inheritdoc + */ + public readonly onSuccess = this.executor.onSuccess; + + /** + * @inheritdoc + */ + public readonly onFailure = this.executor.onFailure; + + /** + * Emitter that fires when an item is rejected from the bulkhead. + */ + public readonly onReject = this.onRejectEmitter.addListener; + + /** + * Returns the number of available execution slots at this point in time. + */ + public get executionSlots() { + return this.capacity - this.active; + } + + /** + * Returns the number of queue slots at this point in time. + */ + public get queueSlots() { + return this.queueCapacity - this.queue.length; + } + + /** + * Bulkhead limits concurrent requests made. + */ + constructor(private readonly capacity: number, private readonly queueCapacity: number) { } + + /** + * Executes the given function. + * @param fn Function to execute + * @throws a {@link BulkheadRejectedException} if the bulkhead limits are exceeeded + */ + public async execute( + fn: (context: IDefaultPolicyContext) => PromiseLike | T, + signal = neverAbortedSignal, + ): Promise { + if (signal.aborted) { + throw new TaskCancelledError(); + } + + if (this.active < this.capacity) { + this.active++; + try { + return await fn({ signal }); + } finally { + this.active--; + this.dequeue(); + } + } + + if (this.queue.length > this.queueCapacity) { + const { resolve, reject, promise } = defer(); + this.queue.push({ signal, fn, resolve, reject }); + return promise; + } + + this.onRejectEmitter.emit(); + throw new BulkheadRejectedError(this.capacity, this.queueCapacity); + } + + private dequeue() { + const item = this.queue.shift(); + if (!item) { + return; + } + + Promise.resolve() + .then(() => this.execute(item.fn, item.signal)) + .then(item.resolve) + .catch(item.reject); + } +} diff --git a/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/2.tst b/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/2.tst new file mode 100644 index 00000000000..9b3687d776a --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/2.tst @@ -0,0 +1,87 @@ +import { neverAbortedSignal } from './common/abort'; +import { defer } from './common/defer'; +import { EventEmitter } from './common/Event'; +import { ExecuteWrapper } from './common/Executor'; +import { BulkheadRejectedError } from './errors/BulkheadRejectedError'; +import { TaskCancelledError } from './errors/Errors'; +import { IDefaultPolicyContext, IPolicy } from './Policy'; + +interface IQueueItem { + signal: AbortSignal; + fn(context: IDefaultPolicyContext): Promise | T; + resolve(value: T): void; + reject(error: Error): void; +} + +export class BulkheadPolicy implements IPolicy { + public declare readonly _altReturn: never; + + private active = 0; + private readonly queue: Array> = []; + private readonly onRejectEmitter = new EventEmitter(); + private readonly executor = new ExecuteWrapper(); + + /** + * @inheritdoc + */ + public readonly onSuccess = this.executor.onSuccess; + + /** + * @inheritdoc + */ + public readonly onFailure = this.executor.onFailure; + + /** + * Emitter that fires when an item is rejected from the bulkhead. + */ + public readonly onReject = this.onRejectEmitter.addListener; + + /** + * Returns the number of available execution slots at this point in time. + */ + public get executionSlots() { + return this.capacity - this.active; + } + + /** + * Returns the number of queue slots at this point in time. + */ + public get queueSlots() { + return this.queueCapacity - this.queue.length; + } + + /** + * Bulkhead limits concurrent requests made. + */ + constructor(private readonly capacity: number, private readonly queueCapacity: number) { } + + /** + * Executes the given function. + * @param fn Function to execute + * @throws a {@link BulkheadRejectedException} if the bulkhead limits are exceeeded + */ + public async execute( + fn: (context: IDefaultPolicyContext) => PromiseLike | T, + signal = neverAbortedSignal, + ): Promise { + if (signal.aborted) { + throw new TaskCancelledError(); + } + + if (this.active < this.capacity) { + this.active++; + try { + return await fn({ signal }); + } finally { + this.active--; + this.dequeue(); + } + } + + if (this.queue.length >= this.queueCapacity) { + this.onRejectEmitter.emit(); + throw new BulkheadRejectedError(this.capacity, this.queueCapacity); + } + const { resolve, reject, promise } = defer(); + this.queue.push({ signal, fn, resolve, reject }); + return promise; diff --git a/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/advanced.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/advanced.expected.diff.json new file mode 100644 index 00000000000..06f0ca747cf --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/advanced.expected.diff.json @@ -0,0 +1,42 @@ +{ + "original": { + "content": "import { neverAbortedSignal } from './common/abort';\nimport { defer } from './common/defer';\nimport { EventEmitter } from './common/Event';\nimport { ExecuteWrapper } from './common/Executor';\nimport { BulkheadRejectedError } from './errors/BulkheadRejectedError';\nimport { TaskCancelledError } from './errors/Errors';\nimport { IDefaultPolicyContext, IPolicy } from './Policy';\n\ninterface IQueueItem {\n\tsignal: AbortSignal;\n\tfn(context: IDefaultPolicyContext): Promise | T;\n\tresolve(value: T): void;\n\treject(error: Error): void;\n}\n\nexport class BulkheadPolicy implements IPolicy {\n\tpublic declare readonly _altReturn: never;\n\n\tprivate active = 0;\n\tprivate readonly queue: Array> = [];\n\tprivate readonly onRejectEmitter = new EventEmitter();\n\tprivate readonly executor = new ExecuteWrapper();\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpublic readonly onSuccess = this.executor.onSuccess;\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpublic readonly onFailure = this.executor.onFailure;\n\n\t/**\n\t * Emitter that fires when an item is rejected from the bulkhead.\n\t */\n\tpublic readonly onReject = this.onRejectEmitter.addListener;\n\n\t/**\n\t * Returns the number of available execution slots at this point in time.\n\t */\n\tpublic get executionSlots() {\n\t\treturn this.capacity - this.active;\n\t}\n\n\t/**\n\t * Returns the number of queue slots at this point in time.\n\t */\n\tpublic get queueSlots() {\n\t\treturn this.queueCapacity - this.queue.length;\n\t}\n\n\t/**\n\t * Bulkhead limits concurrent requests made.\n\t */\n\tconstructor(private readonly capacity: number, private readonly queueCapacity: number) { }\n\n\t/**\n\t * Executes the given function.\n\t * @param fn Function to execute\n\t * @throws a {@link BulkheadRejectedException} if the bulkhead limits are exceeeded\n\t */\n\tpublic async execute(\n\t\tfn: (context: IDefaultPolicyContext) => PromiseLike | T,\n\t\tsignal = neverAbortedSignal,\n\t): Promise {\n\t\tif (signal.aborted) {\n\t\t\tthrow new TaskCancelledError();\n\t\t}\n\n\t\tif (this.active < this.capacity) {\n\t\t\tthis.active++;\n\t\t\ttry {\n\t\t\t\treturn await fn({ signal });\n\t\t\t} finally {\n\t\t\t\tthis.active--;\n\t\t\t\tthis.dequeue();\n\t\t\t}\n\t\t}\n\n\t\tif (this.queue.length > this.queueCapacity) {\n\t\t\tconst { resolve, reject, promise } = defer();\n\t\t\tthis.queue.push({ signal, fn, resolve, reject });\n\t\t\treturn promise;\n\t\t}\n\n\t\tthis.onRejectEmitter.emit();\n\t\tthrow new BulkheadRejectedError(this.capacity, this.queueCapacity);\n\t}\n\n\tprivate dequeue() {\n\t\tconst item = this.queue.shift();\n\t\tif (!item) {\n\t\t\treturn;\n\t\t}\n\n\t\tPromise.resolve()\n\t\t\t.then(() => this.execute(item.fn, item.signal))\n\t\t\t.then(item.resolve)\n\t\t\t.catch(item.reject);\n\t}\n}\n", + "fileName": "./1.tst" + }, + "modified": { + "content": "import { neverAbortedSignal } from './common/abort';\nimport { defer } from './common/defer';\nimport { EventEmitter } from './common/Event';\nimport { ExecuteWrapper } from './common/Executor';\nimport { BulkheadRejectedError } from './errors/BulkheadRejectedError';\nimport { TaskCancelledError } from './errors/Errors';\nimport { IDefaultPolicyContext, IPolicy } from './Policy';\n\ninterface IQueueItem {\n\tsignal: AbortSignal;\n\tfn(context: IDefaultPolicyContext): Promise | T;\n\tresolve(value: T): void;\n\treject(error: Error): void;\n}\n\nexport class BulkheadPolicy implements IPolicy {\n\tpublic declare readonly _altReturn: never;\n\n\tprivate active = 0;\n\tprivate readonly queue: Array> = [];\n\tprivate readonly onRejectEmitter = new EventEmitter();\n\tprivate readonly executor = new ExecuteWrapper();\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpublic readonly onSuccess = this.executor.onSuccess;\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpublic readonly onFailure = this.executor.onFailure;\n\n\t/**\n\t * Emitter that fires when an item is rejected from the bulkhead.\n\t */\n\tpublic readonly onReject = this.onRejectEmitter.addListener;\n\n\t/**\n\t * Returns the number of available execution slots at this point in time.\n\t */\n\tpublic get executionSlots() {\n\t\treturn this.capacity - this.active;\n\t}\n\n\t/**\n\t * Returns the number of queue slots at this point in time.\n\t */\n\tpublic get queueSlots() {\n\t\treturn this.queueCapacity - this.queue.length;\n\t}\n\n\t/**\n\t * Bulkhead limits concurrent requests made.\n\t */\n\tconstructor(private readonly capacity: number, private readonly queueCapacity: number) { }\n\n\t/**\n\t * Executes the given function.\n\t * @param fn Function to execute\n\t * @throws a {@link BulkheadRejectedException} if the bulkhead limits are exceeeded\n\t */\n\tpublic async execute(\n\t\tfn: (context: IDefaultPolicyContext) => PromiseLike | T,\n\t\tsignal = neverAbortedSignal,\n\t): Promise {\n\t\tif (signal.aborted) {\n\t\t\tthrow new TaskCancelledError();\n\t\t}\n\n\t\tif (this.active < this.capacity) {\n\t\t\tthis.active++;\n\t\t\ttry {\n\t\t\t\treturn await fn({ signal });\n\t\t\t} finally {\n\t\t\t\tthis.active--;\n\t\t\t\tthis.dequeue();\n\t\t\t}\n\t\t}\n\n\t\tif (this.queue.length >= this.queueCapacity) {\n\t\t\tthis.onRejectEmitter.emit();\n\t\t\tthrow new BulkheadRejectedError(this.capacity, this.queueCapacity);\n\t\t}\n\t\tconst { resolve, reject, promise } = defer();\n\t\tthis.queue.push({ signal, fn, resolve, reject });\n\t\treturn promise;\n", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[81,103)", + "modifiedRange": "[81,88)", + "innerChanges": [ + { + "originalRange": "[81,26 -> 81,26]", + "modifiedRange": "[81,26 -> 81,27]" + }, + { + "originalRange": "[82,1 -> 82,1]", + "modifiedRange": "[82,1 -> 85,1]" + }, + { + "originalRange": "[82,1 -> 82,2]", + "modifiedRange": "[85,1 -> 85,1]" + }, + { + "originalRange": "[83,1 -> 83,2]", + "modifiedRange": "[86,1 -> 86,1]" + }, + { + "originalRange": "[84,1 -> 84,2]", + "modifiedRange": "[87,1 -> 87,1]" + }, + { + "originalRange": "[85,1 -> 103,1 EOL]", + "modifiedRange": "[88,1 -> 88,1 EOL]" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/legacy.expected.diff.json b/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/legacy.expected.diff.json new file mode 100644 index 00000000000..88d6c0c6cf8 --- /dev/null +++ b/src/vs/editor/test/node/diffing/fixtures/sorted-offsets/legacy.expected.diff.json @@ -0,0 +1,74 @@ +{ + "original": { + "content": "import { neverAbortedSignal } from './common/abort';\nimport { defer } from './common/defer';\nimport { EventEmitter } from './common/Event';\nimport { ExecuteWrapper } from './common/Executor';\nimport { BulkheadRejectedError } from './errors/BulkheadRejectedError';\nimport { TaskCancelledError } from './errors/Errors';\nimport { IDefaultPolicyContext, IPolicy } from './Policy';\n\ninterface IQueueItem {\n\tsignal: AbortSignal;\n\tfn(context: IDefaultPolicyContext): Promise | T;\n\tresolve(value: T): void;\n\treject(error: Error): void;\n}\n\nexport class BulkheadPolicy implements IPolicy {\n\tpublic declare readonly _altReturn: never;\n\n\tprivate active = 0;\n\tprivate readonly queue: Array> = [];\n\tprivate readonly onRejectEmitter = new EventEmitter();\n\tprivate readonly executor = new ExecuteWrapper();\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpublic readonly onSuccess = this.executor.onSuccess;\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpublic readonly onFailure = this.executor.onFailure;\n\n\t/**\n\t * Emitter that fires when an item is rejected from the bulkhead.\n\t */\n\tpublic readonly onReject = this.onRejectEmitter.addListener;\n\n\t/**\n\t * Returns the number of available execution slots at this point in time.\n\t */\n\tpublic get executionSlots() {\n\t\treturn this.capacity - this.active;\n\t}\n\n\t/**\n\t * Returns the number of queue slots at this point in time.\n\t */\n\tpublic get queueSlots() {\n\t\treturn this.queueCapacity - this.queue.length;\n\t}\n\n\t/**\n\t * Bulkhead limits concurrent requests made.\n\t */\n\tconstructor(private readonly capacity: number, private readonly queueCapacity: number) { }\n\n\t/**\n\t * Executes the given function.\n\t * @param fn Function to execute\n\t * @throws a {@link BulkheadRejectedException} if the bulkhead limits are exceeeded\n\t */\n\tpublic async execute(\n\t\tfn: (context: IDefaultPolicyContext) => PromiseLike | T,\n\t\tsignal = neverAbortedSignal,\n\t): Promise {\n\t\tif (signal.aborted) {\n\t\t\tthrow new TaskCancelledError();\n\t\t}\n\n\t\tif (this.active < this.capacity) {\n\t\t\tthis.active++;\n\t\t\ttry {\n\t\t\t\treturn await fn({ signal });\n\t\t\t} finally {\n\t\t\t\tthis.active--;\n\t\t\t\tthis.dequeue();\n\t\t\t}\n\t\t}\n\n\t\tif (this.queue.length > this.queueCapacity) {\n\t\t\tconst { resolve, reject, promise } = defer();\n\t\t\tthis.queue.push({ signal, fn, resolve, reject });\n\t\t\treturn promise;\n\t\t}\n\n\t\tthis.onRejectEmitter.emit();\n\t\tthrow new BulkheadRejectedError(this.capacity, this.queueCapacity);\n\t}\n\n\tprivate dequeue() {\n\t\tconst item = this.queue.shift();\n\t\tif (!item) {\n\t\t\treturn;\n\t\t}\n\n\t\tPromise.resolve()\n\t\t\t.then(() => this.execute(item.fn, item.signal))\n\t\t\t.then(item.resolve)\n\t\t\t.catch(item.reject);\n\t}\n}\n", + "fileName": "./1.tst" + }, + "modified": { + "content": "import { neverAbortedSignal } from './common/abort';\nimport { defer } from './common/defer';\nimport { EventEmitter } from './common/Event';\nimport { ExecuteWrapper } from './common/Executor';\nimport { BulkheadRejectedError } from './errors/BulkheadRejectedError';\nimport { TaskCancelledError } from './errors/Errors';\nimport { IDefaultPolicyContext, IPolicy } from './Policy';\n\ninterface IQueueItem {\n\tsignal: AbortSignal;\n\tfn(context: IDefaultPolicyContext): Promise | T;\n\tresolve(value: T): void;\n\treject(error: Error): void;\n}\n\nexport class BulkheadPolicy implements IPolicy {\n\tpublic declare readonly _altReturn: never;\n\n\tprivate active = 0;\n\tprivate readonly queue: Array> = [];\n\tprivate readonly onRejectEmitter = new EventEmitter();\n\tprivate readonly executor = new ExecuteWrapper();\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpublic readonly onSuccess = this.executor.onSuccess;\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpublic readonly onFailure = this.executor.onFailure;\n\n\t/**\n\t * Emitter that fires when an item is rejected from the bulkhead.\n\t */\n\tpublic readonly onReject = this.onRejectEmitter.addListener;\n\n\t/**\n\t * Returns the number of available execution slots at this point in time.\n\t */\n\tpublic get executionSlots() {\n\t\treturn this.capacity - this.active;\n\t}\n\n\t/**\n\t * Returns the number of queue slots at this point in time.\n\t */\n\tpublic get queueSlots() {\n\t\treturn this.queueCapacity - this.queue.length;\n\t}\n\n\t/**\n\t * Bulkhead limits concurrent requests made.\n\t */\n\tconstructor(private readonly capacity: number, private readonly queueCapacity: number) { }\n\n\t/**\n\t * Executes the given function.\n\t * @param fn Function to execute\n\t * @throws a {@link BulkheadRejectedException} if the bulkhead limits are exceeeded\n\t */\n\tpublic async execute(\n\t\tfn: (context: IDefaultPolicyContext) => PromiseLike | T,\n\t\tsignal = neverAbortedSignal,\n\t): Promise {\n\t\tif (signal.aborted) {\n\t\t\tthrow new TaskCancelledError();\n\t\t}\n\n\t\tif (this.active < this.capacity) {\n\t\t\tthis.active++;\n\t\t\ttry {\n\t\t\t\treturn await fn({ signal });\n\t\t\t} finally {\n\t\t\t\tthis.active--;\n\t\t\t\tthis.dequeue();\n\t\t\t}\n\t\t}\n\n\t\tif (this.queue.length >= this.queueCapacity) {\n\t\t\tthis.onRejectEmitter.emit();\n\t\t\tthrow new BulkheadRejectedError(this.capacity, this.queueCapacity);\n\t\t}\n\t\tconst { resolve, reject, promise } = defer();\n\t\tthis.queue.push({ signal, fn, resolve, reject });\n\t\treturn promise;\n", + "fileName": "./2.tst" + }, + "diffs": [ + { + "originalRange": "[81,103)", + "modifiedRange": "[81,88)", + "innerChanges": [ + { + "originalRange": "[81,26 -> 81,26]", + "modifiedRange": "[81,26 -> 81,27]" + }, + { + "originalRange": "[81,48 -> 86,1 EOL]", + "modifiedRange": "[81,49 -> 81,49 EOL]" + }, + { + "originalRange": "[87,1 -> 87,1]", + "modifiedRange": "[82,1 -> 82,2]" + }, + { + "originalRange": "[88,1 -> 88,1]", + "modifiedRange": "[83,1 -> 83,2]" + }, + { + "originalRange": "[89,1 -> 89,1]", + "modifiedRange": "[84,1 -> 84,2]" + }, + { + "originalRange": "[90,1 -> 92,1]", + "modifiedRange": "[85,1 -> 85,1]" + }, + { + "originalRange": "[92,9 -> 97,4]", + "modifiedRange": "[85,9 -> 85,29]" + }, + { + "originalRange": "[97,10 -> 97,20 EOL]", + "modifiedRange": "[85,35 -> 85,51 EOL]" + }, + { + "originalRange": "[98,3 -> 98,16]", + "modifiedRange": "[86,3 -> 86,3]" + }, + { + "originalRange": "[98,21 -> 98,43]", + "modifiedRange": "[86,8 -> 86,21]" + }, + { + "originalRange": "[98,49 -> 99,15]", + "modifiedRange": "[86,27 -> 86,33]" + }, + { + "originalRange": "[99,22 -> 100,16]", + "modifiedRange": "[86,40 -> 86,42]" + }, + { + "originalRange": "[100,22 -> 100,22]", + "modifiedRange": "[86,48 -> 86,50]" + }, + { + "originalRange": "[101,2 -> 102,2 EOL]", + "modifiedRange": "[87,2 -> 87,18 EOL]" + } + ] + } + ] +} \ No newline at end of file From 15fbbd2bf2df4a2b3d86f72b9aadf58ce6968803 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:05:00 +0200 Subject: [PATCH 0290/2222] Add minimum amount of tests for editor commands context (#220042) Add minimum amount of tests for editor commands --- .../parts/editor/editorCommandsContext.ts | 6 +- .../editor/editorCommandsContext.test.ts | 183 ++++++++++++++++++ 2 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 src/vs/workbench/test/browser/parts/editor/editorCommandsContext.test.ts diff --git a/src/vs/workbench/browser/parts/editor/editorCommandsContext.ts b/src/vs/workbench/browser/parts/editor/editorCommandsContext.ts index b0915c07703..ee5957ddfa0 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommandsContext.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommandsContext.ts @@ -118,17 +118,19 @@ function getEditorContextFromCommandArgs(accessor: ServicesAccessor, commandArgs } const editorService = accessor.get(IEditorService); + const editorGroupsService = accessor.get(IEditorGroupsService); // Otherwise, try to find the editor group by the URI of the resource for (const uri of filteredArgs as URI[]) { const editorIdentifiers = editorService.findEditors(uri); if (editorIdentifiers.length) { - return editorIdentifiers[0]; + const editorIdentifier = editorIdentifiers[0]; + const group = editorGroupsService.getGroup(editorIdentifier.groupId); + return { groupId: editorIdentifier.groupId, editorIndex: group?.getIndexOfEditor(editorIdentifier.editor) }; } } const listService = accessor.get(IListService); - const editorGroupsService = accessor.get(IEditorGroupsService); // If there is no context in the arguments, try to find the context from the focused list // if the action was executed from a list diff --git a/src/vs/workbench/test/browser/parts/editor/editorCommandsContext.test.ts b/src/vs/workbench/test/browser/parts/editor/editorCommandsContext.test.ts new file mode 100644 index 00000000000..1cc2f101708 --- /dev/null +++ b/src/vs/workbench/test/browser/parts/editor/editorCommandsContext.test.ts @@ -0,0 +1,183 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { workbenchInstantiationService, TestServiceAccessor, registerTestEditor, registerTestFileEditor, registerTestResourceEditor, TestFileEditorInput, createEditorPart, registerTestSideBySideEditor, TestEditorInput } from 'vs/workbench/test/browser/workbenchTestServices'; +import { DisposableStore } from 'vs/base/common/lifecycle'; +import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { EditorService } from 'vs/workbench/services/editor/browser/editorService'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { EditorInput } from 'vs/workbench/common/editor/editorInput'; +import { URI } from 'vs/base/common/uri'; +import { resolveCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommandsContext'; +import { IEditorCommandsContext } from 'vs/workbench/common/editor'; + +suite('Resolving Editor Commands Context', () => { + + const disposables = new DisposableStore(); + + const TEST_EDITOR_ID = 'MyTestEditorForEditors'; + + let instantiationService: IInstantiationService; + let accessor: TestServiceAccessor; + + setup(() => { + instantiationService = workbenchInstantiationService(undefined, disposables); + accessor = instantiationService.createInstance(TestServiceAccessor); + + disposables.add(accessor.untitledTextEditorService); + disposables.add(registerTestFileEditor()); + disposables.add(registerTestSideBySideEditor()); + disposables.add(registerTestResourceEditor()); + disposables.add(registerTestEditor(TEST_EDITOR_ID, [new SyncDescriptor(TestFileEditorInput)])); + }); + + teardown(() => { + disposables.clear(); + }); + + let index = 0; + function input(id = String(index++)): EditorInput { + return disposables.add(new TestEditorInput(URI.parse(`file://${id}`), 'testInput')); + } + + async function createServices(): Promise { + const instantiationService = workbenchInstantiationService(undefined, disposables); + + const part = await createEditorPart(instantiationService, disposables); + instantiationService.stub(IEditorGroupsService, part); + + const editorService = disposables.add(instantiationService.createInstance(EditorService, undefined)); + instantiationService.stub(IEditorService, editorService); + + return instantiationService.createInstance(TestServiceAccessor); + } + + test('use editor group selection', async () => { + const accessor = await createServices(); + const activeGroup = accessor.editorGroupService.activeGroup; + const instantiationService = accessor.instantiationService; + + const input1 = input(); + const input2 = input(); + const input3 = input(); + activeGroup.openEditor(input1, { pinned: true }); + activeGroup.openEditor(input2, { pinned: true }); + activeGroup.openEditor(input3, { pinned: true }); + + activeGroup.setSelection(input1, [input2]); + + // use editor commands context + const editorCommandContext: IEditorCommandsContext = { groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(input1), preserveFocus: true }; + const resolvedContext1 = instantiationService.invokeFunction(resolveCommandsContext, [editorCommandContext]); + + assert.strictEqual(resolvedContext1.groupedEditors.length, 1); + assert.strictEqual(resolvedContext1.groupedEditors[0].group.id, activeGroup.id); + assert.strictEqual(resolvedContext1.groupedEditors[0].editors.length, 2); + assert.strictEqual(resolvedContext1.groupedEditors[0].editors[0], input1); + assert.strictEqual(resolvedContext1.groupedEditors[0].editors[1], input2); + assert.strictEqual(resolvedContext1.preserveFocus, true); + + // use URI + const resolvedContext2 = instantiationService.invokeFunction(resolveCommandsContext, [input2.resource]); + + assert.strictEqual(resolvedContext2.groupedEditors.length, 1); + assert.strictEqual(resolvedContext2.groupedEditors[0].group.id, activeGroup.id); + assert.strictEqual(resolvedContext2.groupedEditors[0].editors.length, 2); + assert.strictEqual(resolvedContext2.groupedEditors[0].editors[0], input2); + assert.strictEqual(resolvedContext2.groupedEditors[0].editors[1], input1); + assert.strictEqual(resolvedContext2.preserveFocus, false); + + // use URI and commandContext + const editor1CommandContext: IEditorCommandsContext = { groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(input1), preserveFocus: true }; + const resolvedContext3 = instantiationService.invokeFunction(resolveCommandsContext, [input1.resource, editor1CommandContext]); + + assert.strictEqual(resolvedContext3.groupedEditors.length, 1); + assert.strictEqual(resolvedContext3.groupedEditors[0].group.id, activeGroup.id); + assert.strictEqual(resolvedContext3.groupedEditors[0].editors.length, 2); + assert.strictEqual(resolvedContext3.groupedEditors[0].editors[0], input1); + assert.strictEqual(resolvedContext3.groupedEditors[0].editors[1], input2); + assert.strictEqual(resolvedContext3.preserveFocus, true); + }); + + test('don\'t use editor group selection', async () => { + const accessor = await createServices(); + const activeGroup = accessor.editorGroupService.activeGroup; + const instantiationService = accessor.instantiationService; + + const input1 = input(); + const input2 = input(); + const input3 = input(); + activeGroup.openEditor(input1, { pinned: true }); + activeGroup.openEditor(input2, { pinned: true }); + activeGroup.openEditor(input3, { pinned: true }); + + activeGroup.setSelection(input1, [input2]); + + // use editor commands context + const editorCommandContext: IEditorCommandsContext = { groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(input3), preserveFocus: true }; + const resolvedContext1 = instantiationService.invokeFunction(resolveCommandsContext, [editorCommandContext]); + + assert.strictEqual(resolvedContext1.groupedEditors.length, 1); + assert.strictEqual(resolvedContext1.groupedEditors[0].group.id, activeGroup.id); + assert.strictEqual(resolvedContext1.groupedEditors[0].editors.length, 1); + assert.strictEqual(resolvedContext1.groupedEditors[0].editors[0], input3); + assert.strictEqual(resolvedContext1.preserveFocus, true); + + // use URI + const resolvedContext2 = instantiationService.invokeFunction(resolveCommandsContext, [input3.resource]); + + assert.strictEqual(resolvedContext2.groupedEditors.length, 1); + assert.strictEqual(resolvedContext2.groupedEditors[0].group.id, activeGroup.id); + assert.strictEqual(resolvedContext2.groupedEditors[0].editors.length, 1); + assert.strictEqual(resolvedContext2.groupedEditors[0].editors[0], input3); + assert.strictEqual(resolvedContext2.preserveFocus, false); + }); + + test('inactive edior group command context', async () => { + const accessor = await createServices(); + const editorGroupService = accessor.editorGroupService; + const instantiationService = accessor.instantiationService; + + const group1 = editorGroupService.activeGroup; + const group2 = editorGroupService.addGroup(group1, GroupDirection.RIGHT); + + const input11 = input(); + const input12 = input(); + group1.openEditor(input11, { pinned: true }); + group1.openEditor(input12, { pinned: true }); + + const input21 = input(); + group2.openEditor(input21, { pinned: true }); + + editorGroupService.activateGroup(group1); + group1.setSelection(input11, [input12]); + + // use editor commands context of inactive group with editor index + const editorCommandContext1: IEditorCommandsContext = { groupId: group2.id, editorIndex: group2.getIndexOfEditor(input21), preserveFocus: true }; + const resolvedContext1 = instantiationService.invokeFunction(resolveCommandsContext, [editorCommandContext1]); + + assert.strictEqual(resolvedContext1.groupedEditors.length, 1); + assert.strictEqual(resolvedContext1.groupedEditors[0].group.id, group2.id); + assert.strictEqual(resolvedContext1.groupedEditors[0].editors.length, 1); + assert.strictEqual(resolvedContext1.groupedEditors[0].editors[0], input21); + assert.strictEqual(resolvedContext1.preserveFocus, true); + + // use editor commands context of inactive group without editor index + const editorCommandContext2: IEditorCommandsContext = { groupId: group2.id, preserveFocus: true }; + const resolvedContext2 = instantiationService.invokeFunction(resolveCommandsContext, [editorCommandContext2]); + + assert.strictEqual(resolvedContext2.groupedEditors.length, 1); + assert.strictEqual(resolvedContext2.groupedEditors[0].group.id, group2.id); + assert.strictEqual(resolvedContext2.groupedEditors[0].editors.length, 1); + assert.strictEqual(resolvedContext1.groupedEditors[0].editors[0], input21); + assert.strictEqual(resolvedContext2.preserveFocus, true); + }); + + ensureNoDisposablesAreLeakedInTestSuite(); +}); From c040699c44331182a0be70f030d43dcffe6a713f Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 5 Jul 2024 11:26:48 +0200 Subject: [PATCH 0291/2222] Git - standardize logging in the git extension (#220043) * Git - more logging * Git - more logging changes --- extensions/git/src/commands.ts | 10 ++-- extensions/git/src/git.ts | 12 ++--- extensions/git/src/historyProvider.ts | 22 ++++---- extensions/git/src/model.ts | 72 +++++++++++++-------------- extensions/git/src/operation.ts | 4 +- extensions/git/src/protocolHandler.ts | 14 +++--- extensions/git/src/repository.ts | 14 +++--- 7 files changed, 74 insertions(+), 74 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 62d35d7af92..55c5f89fd3a 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -1339,14 +1339,14 @@ export class CommandCenter { @command('git.stage') async stage(...resourceStates: SourceControlResourceState[]): Promise { - this.logger.debug(`git.stage ${resourceStates.length} `); + this.logger.debug(`[CommandCenter][stage] git.stage ${resourceStates.length} `); resourceStates = resourceStates.filter(s => !!s); if (resourceStates.length === 0 || (resourceStates[0] && !(resourceStates[0].resourceUri instanceof Uri))) { const resource = this.getSCMResource(); - this.logger.debug(`git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null} `); + this.logger.debug(`[CommandCenter][stage] git.stage.getSCMResource ${resource ? resource.resourceUri.toString() : null} `); if (!resource) { return; @@ -1389,7 +1389,7 @@ export class CommandCenter { const untracked = selection.filter(s => s.resourceGroupType === ResourceGroupType.Untracked); const scmResources = [...workingTree, ...untracked, ...resolved, ...unresolved]; - this.logger.debug(`git.stage.scmResources ${scmResources.length} `); + this.logger.debug(`[CommandCenter][stage] git.stage.scmResources ${scmResources.length} `); if (!scmResources.length) { return; } @@ -4403,10 +4403,10 @@ export class CommandCenter { private getSCMResource(uri?: Uri): Resource | undefined { uri = uri ? uri : (window.activeTextEditor && window.activeTextEditor.document.uri); - this.logger.debug(`git.getSCMResource.uri ${uri && uri.toString()}`); + this.logger.debug(`[CommandCenter][getSCMResource] git.getSCMResource.uri: ${uri && uri.toString()}`); for (const r of this.model.repositories.map(r => r.root)) { - this.logger.debug(`repo root ${r}`); + this.logger.debug(`[CommandCenter][getSCMResource] repo root: ${r}`); } if (!uri) { diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 0e2b37af5b7..eb0007893e8 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -1113,7 +1113,7 @@ export class Repository { return result.stdout.trim(); } catch (err) { - this.logger.warn(`git config failed: ${err.message}`); + this.logger.warn(`[Git][config] git config failed: ${err.message}`); return ''; } } @@ -2317,7 +2317,7 @@ export class Repository { return result; } catch (err) { - this.logger.warn(err.message); + this.logger.warn(`[Git][getHEAD] Failed to parse HEAD file: ${err.message}`); } try { @@ -2465,11 +2465,11 @@ export class Repository { remotes.push(...await this.getRemotesFS()); if (remotes.length === 0) { - this.logger.info('No remotes found in the git config file.'); + this.logger.info('[Git][getRemotes] No remotes found in the git config file'); } } catch (err) { - this.logger.warn(`getRemotes() - ${err.message}`); + this.logger.warn(`[Git][getRemotes] Error: ${err.message}`); // Fallback to using git to get the remotes remotes.push(...await this.getRemotesGit()); @@ -2605,7 +2605,7 @@ export class Repository { return branch; } - this.logger.warn(`No such branch: ${name}.`); + this.logger.warn(`[Git][getBranch] No such branch: ${name}`); return Promise.reject(new Error(`No such branch: ${name}.`)); } @@ -2701,7 +2701,7 @@ export class Repository { const result = await fs.readFile(path.join(this.dotGit.path, ref), 'utf8'); return result.trim(); } catch (err) { - this.logger.warn(err.message); + this.logger.warn(`[Git][revParse] Unable to read file: ${err.message}`); } try { diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 22cbe9c493d..55115acb6d0 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -49,8 +49,8 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec } private async onDidRunGitStatus(force = false): Promise { - this.logger.trace('GitHistoryProvider:onDidRunGitStatus - HEAD:', JSON.stringify(this._HEAD)); - this.logger.trace('GitHistoryProvider:onDidRunGitStatus - repository.HEAD:', JSON.stringify(this.repository.HEAD)); + this.logger.trace('[GitHistoryProvider][onDidRunGitStatus] HEAD:', JSON.stringify(this._HEAD)); + this.logger.trace('[GitHistoryProvider][onDidRunGitStatus] repository.HEAD:', JSON.stringify(this.repository.HEAD)); // Get the merge base of the current history item group const mergeBase = await this.resolveHEADMergeBase(); @@ -65,7 +65,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec this._HEADMergeBase?.name === mergeBase?.name && this._HEADMergeBase?.remote === mergeBase?.remote && this._HEADMergeBase?.commit === mergeBase?.commit) { - this.logger.trace('GitHistoryProvider:onDidRunGitStatus - HEAD has not changed'); + this.logger.trace('[GitHistoryProvider][onDidRunGitStatus] HEAD has not changed'); return; } @@ -74,7 +74,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec // Check if HEAD does not support incoming/outgoing (detached commit, tag) if (!this.repository.HEAD?.name || !this.repository.HEAD?.commit || this.repository.HEAD.type === RefType.Tag) { - this.logger.trace('GitHistoryProvider:onDidRunGitStatus - HEAD does not support incoming/outgoing'); + this.logger.trace('[GitHistoryProvider][onDidRunGitStatus] HEAD does not support incoming/outgoing'); this.currentHistoryItemGroup = undefined; return; @@ -93,7 +93,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec } : undefined }; - this.logger.trace(`GitHistoryProvider:onDidRunGitStatus - currentHistoryItemGroup (${force}): ${JSON.stringify(this.currentHistoryItemGroup)}`); + this.logger.trace(`[GitHistoryProvider][onDidRunGitStatus] currentHistoryItemGroup(${force}): ${JSON.stringify(this.currentHistoryItemGroup)}`); } async provideHistoryItems(historyItemGroupId: string, options: SourceControlHistoryOptions): Promise { @@ -223,7 +223,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec if (!historyItemId2) { const upstreamRef = await this.resolveHistoryItemGroupMergeBase(historyItemId1); if (!upstreamRef) { - this.logger.info(`GitHistoryProvider:resolveHistoryItemGroupCommonAncestor - Failed to resolve history item group base for '${historyItemId1}'`); + this.logger.info(`[GitHistoryProvider][resolveHistoryItemGroupCommonAncestor] Failed to resolve history item group base for '${historyItemId1}'`); return undefined; } @@ -232,16 +232,16 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec const ancestor = await this.repository.getMergeBase(historyItemId1, historyItemId2); if (!ancestor) { - this.logger.info(`GitHistoryProvider:resolveHistoryItemGroupCommonAncestor - Failed to resolve common ancestor for '${historyItemId1}' and '${historyItemId2}'`); + this.logger.info(`[GitHistoryProvider][resolveHistoryItemGroupCommonAncestor] Failed to resolve common ancestor for '${historyItemId1}' and '${historyItemId2}'`); return undefined; } try { const commitCount = await this.repository.getCommitCount(`${historyItemId1}...${historyItemId2}`); - this.logger.trace(`GitHistoryProvider:resolveHistoryItemGroupCommonAncestor - Resolved common ancestor for '${historyItemId1}' and '${historyItemId2}': ${JSON.stringify({ id: ancestor, ahead: commitCount.ahead, behind: commitCount.behind })}`); + this.logger.trace(`[GitHistoryProvider][resolveHistoryItemGroupCommonAncestor] Resolved common ancestor for '${historyItemId1}' and '${historyItemId2}': ${JSON.stringify({ id: ancestor, ahead: commitCount.ahead, behind: commitCount.behind })}`); return { id: ancestor, ahead: commitCount.ahead, behind: commitCount.behind }; } catch (err) { - this.logger.error(`GitHistoryProvider:resolveHistoryItemGroupCommonAncestor - Failed to get ahead/behind for '${historyItemId1}...${historyItemId2}': ${err.message}`); + this.logger.error(`[GitHistoryProvider][resolveHistoryItemGroupCommonAncestor] Failed to get ahead/behind for '${historyItemId1}...${historyItemId2}': ${err.message}`); } return undefined; @@ -293,7 +293,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec // Base (config -> reflog -> default) const remoteBranch = await this.repository.getBranchBase(historyItemId); if (!remoteBranch?.remote || !remoteBranch?.name || !remoteBranch?.commit || remoteBranch?.type !== RefType.RemoteHead) { - this.logger.info(`GitHistoryProvider:resolveHistoryItemGroupUpstreamOrBase - Failed to resolve history item group base for '${historyItemId}'`); + this.logger.info(`[GitHistoryProvider][resolveHistoryItemGroupUpstreamOrBase] Failed to resolve history item group base for '${historyItemId}'`); return undefined; } @@ -304,7 +304,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec }; } catch (err) { - this.logger.error(`GitHistoryProvider:resolveHistoryItemGroupUpstreamOrBase - Failed to get branch base for '${historyItemId}': ${err.message}`); + this.logger.error(`[GitHistoryProvider][resolveHistoryItemGroupUpstreamOrBase] Failed to get branch base for '${historyItemId}': ${err.message}`); } return undefined; diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index 6fd123c7f3d..d4e00449e20 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -287,13 +287,13 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } private async doInitialScan(): Promise { - this.logger.info('[Model] Initial repository scan started'); + this.logger.info('[Model][doInitialScan] Initial repository scan started'); const config = workspace.getConfiguration('git'); const autoRepositoryDetection = config.get('autoRepositoryDetection'); const parentRepositoryConfig = config.get<'always' | 'never' | 'prompt'>('openRepositoryInParentFolders', 'prompt'); - this.logger.trace(`[Model] Settings: autoRepositoryDetection=${autoRepositoryDetection}, openRepositoryInParentFolders=${parentRepositoryConfig}`); + this.logger.trace(`[Model][doInitialScan] Settings: autoRepositoryDetection=${autoRepositoryDetection}, openRepositoryInParentFolders=${parentRepositoryConfig}`); // Initial repository scan function const initialScanFn = () => Promise.all([ @@ -325,7 +325,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } */ this.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length }); - this.logger.info(`[Model] Initial repository scan completed - repositories(${this.repositories.length}), closed repositories (${this.closedRepositories.length}), parent repositories (${this.parentRepositories.length}), unsafe repositories (${this.unsafeRepositories.length})`); + this.logger.info(`[Model][doInitialScan] Initial repository scan completed - repositories(${this.repositories.length}), closed repositories (${this.closedRepositories.length}), parent repositories (${this.parentRepositories.length}), unsafe repositories (${this.unsafeRepositories.length})`); } /** @@ -344,7 +344,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi await Promise.all((workspace.workspaceFolders || []).map(async folder => { const root = folder.uri.fsPath; - this.logger.trace(`[Model] Workspace folder: ${root}`); + this.logger.trace(`[Model][scanWorkspaceFolders] Workspace folder: ${root}`); // Workspace folder children const repositoryScanMaxDepth = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('repositoryScanMaxDepth', 1); @@ -354,17 +354,17 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi // Repository scan folders const scanPaths = (workspace.isTrusted ? workspace.getConfiguration('git', folder.uri) : config).get('scanRepositories') || []; - this.logger.trace(`[Model] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`); + this.logger.trace(`[Model][scanWorkspaceFolders] Workspace scan settings: repositoryScanMaxDepth=${repositoryScanMaxDepth}; repositoryScanIgnoredFolders=[${repositoryScanIgnoredFolders.join(', ')}]; scanRepositories=[${scanPaths.join(', ')}]`); for (const scanPath of scanPaths) { if (scanPath === '.git') { - this.logger.trace('[Model] \'.git\' not supported in \'git.scanRepositories\' setting.'); + this.logger.trace('[Model][scanWorkspaceFolders] \'.git\' not supported in \'git.scanRepositories\' setting.'); continue; } if (path.isAbsolute(scanPath)) { const notSupportedMessage = l10n.t('Absolute paths not supported in "git.scanRepositories" setting.'); - this.logger.warn(`[Model] ${notSupportedMessage}`); + this.logger.warn(`[Model][scanWorkspaceFolders] ${notSupportedMessage}`); console.warn(notSupportedMessage); continue; } @@ -372,12 +372,12 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi subfolders.add(path.join(root, scanPath)); } - this.logger.trace(`[Model] Workspace scan sub folders: [${[...subfolders].join(', ')}]`); + this.logger.trace(`[Model][scanWorkspaceFolders] Workspace scan sub folders: [${[...subfolders].join(', ')}]`); await Promise.all([...subfolders].map(f => this.openRepository(f))); })); } catch (err) { - this.logger.warn(`[Model] scanWorkspaceFolders: ${err}`); + this.logger.warn(`[Model][scanWorkspaceFolders] Error: ${err}`); } } @@ -397,7 +397,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } } catch (err) { - this.logger.warn(`[Model] Unable to read workspace folder '${currentFolder.path}': ${err}`); + this.logger.warn(`[Model][traverseWorkspaceFolder] Unable to read workspace folder '${currentFolder.path}': ${err}`); continue; } @@ -459,11 +459,11 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi .filter(r => !(workspace.workspaceFolders || []).some(f => isDescendant(f.uri.fsPath, r!.repository.root))) as OpenRepository[]; openRepositoriesToDispose.forEach(r => r.dispose()); - this.logger.trace(`[Model] Workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); + this.logger.trace(`[Model][onDidChangeWorkspaceFolders] Workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); await Promise.all(possibleRepositoryFolders.map(p => this.openRepository(p.uri.fsPath))); } catch (err) { - this.logger.warn(`[Model] onDidChangeWorkspaceFolders: ${err}`); + this.logger.warn(`[Model][onDidChangeWorkspaceFolders] Error: ${err}`); } } @@ -477,7 +477,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi .filter(({ root }) => workspace.getConfiguration('git', root).get('enabled') !== true) .map(({ repository }) => repository); - this.logger.trace(`[Model] Workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); + this.logger.trace(`[Model][onDidChangeConfiguration] Workspace folders: [${possibleRepositoryFolders.map(p => p.uri.fsPath).join(', ')}]`); possibleRepositoryFolders.forEach(p => this.openRepository(p.uri.fsPath)); openRepositoriesToDispose.forEach(r => r.dispose()); } @@ -485,7 +485,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi private async onDidChangeVisibleTextEditors(editors: readonly TextEditor[]): Promise { try { if (!workspace.isTrusted) { - this.logger.trace('[Model] Workspace is not trusted.'); + this.logger.trace('[Model][onDidChangeVisibleTextEditors] Workspace is not trusted.'); return; } @@ -506,25 +506,25 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi const repository = this.getRepository(uri); if (repository) { - this.logger.trace(`[Model] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`); + this.logger.trace(`[Model][onDidChangeVisibleTextEditors] Repository for editor resource ${uri.fsPath} already exists: ${repository.root}`); return; } - this.logger.trace(`[Model] Open repository for editor resource ${uri.fsPath}`); + this.logger.trace(`[Model][onDidChangeVisibleTextEditors] Open repository for editor resource ${uri.fsPath}`); await this.openRepository(path.dirname(uri.fsPath)); })); } catch (err) { - this.logger.warn(`[Model] onDidChangeVisibleTextEditors: ${err}`); + this.logger.warn(`[Model][onDidChangeVisibleTextEditors] Error: ${err}`); } } @sequentialize async openRepository(repoPath: string, openIfClosed = false): Promise { - this.logger.trace(`[Model] Opening repository: ${repoPath}`); + this.logger.trace(`[Model][openRepository] Repository: ${repoPath}`); const existingRepository = await this.getRepositoryExact(repoPath); if (existingRepository) { - this.logger.trace(`[Model] Repository for path ${repoPath} already exists: ${existingRepository.root}`); + this.logger.trace(`[Model][openRepository] Repository for path ${repoPath} already exists: ${existingRepository.root}`); return; } @@ -532,7 +532,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi const enabled = config.get('enabled') === true; if (!enabled) { - this.logger.trace('[Model] Git is not enabled'); + this.logger.trace('[Model][openRepository] Git is not enabled'); return; } @@ -542,7 +542,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi fs.accessSync(path.join(repoPath, 'HEAD'), fs.constants.F_OK); const result = await this.git.exec(repoPath, ['-C', repoPath, 'rev-parse', '--show-cdup']); if (result.stderr.trim() === '' && result.stdout.trim() === '') { - this.logger.trace(`[Model] Bare repository: ${repoPath}`); + this.logger.trace(`[Model][openRepository] Bare repository: ${repoPath}`); return; } } catch { @@ -552,16 +552,16 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi try { const { repositoryRoot, unsafeRepositoryMatch } = await this.getRepositoryRoot(repoPath); - this.logger.trace(`[Model] Repository root for path ${repoPath} is: ${repositoryRoot}`); + this.logger.trace(`[Model][openRepository] Repository root for path ${repoPath} is: ${repositoryRoot}`); const existingRepository = await this.getRepositoryExact(repositoryRoot); if (existingRepository) { - this.logger.trace(`[Model] Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`); + this.logger.trace(`[Model][openRepository] Repository for path ${repositoryRoot} already exists: ${existingRepository.root}`); return; } if (this.shouldRepositoryBeIgnored(repositoryRoot)) { - this.logger.trace(`[Model] Repository for path ${repositoryRoot} is ignored`); + this.logger.trace(`[Model][openRepository] Repository for path ${repositoryRoot} is ignored`); return; } @@ -570,7 +570,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi if (parentRepositoryConfig !== 'always' && this.globalState.get(`parentRepository:${repositoryRoot}`) !== true) { const isRepositoryOutsideWorkspace = await this.isRepositoryOutsideWorkspace(repositoryRoot); if (isRepositoryOutsideWorkspace) { - this.logger.trace(`[Model] Repository in parent folder: ${repositoryRoot}`); + this.logger.trace(`[Model][openRepository] Repository in parent folder: ${repositoryRoot}`); if (!this._parentRepositoriesManager.hasRepository(repositoryRoot)) { // Show a notification if the parent repository is opened after the initial scan @@ -587,7 +587,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi // Handle unsafe repositories if (unsafeRepositoryMatch && unsafeRepositoryMatch.length === 3) { - this.logger.trace(`[Model] Unsafe repository: ${repositoryRoot}`); + this.logger.trace(`[Model][openRepository] Unsafe repository: ${repositoryRoot}`); // Show a notification if the unsafe repository is opened after the initial scan if (this._state === 'initialized' && !this._unsafeRepositoriesManager.hasRepository(repositoryRoot)) { @@ -601,7 +601,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi // Handle repositories that were closed by the user if (!openIfClosed && this._closedRepositoriesManager.isRepositoryClosed(repositoryRoot)) { - this.logger.trace(`[Model] Repository for path ${repositoryRoot} is closed`); + this.logger.trace(`[Model][openRepository] Repository for path ${repositoryRoot} is closed`); return; } @@ -612,14 +612,14 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi this.open(repository); this._closedRepositoriesManager.deleteRepository(repository.root); - this.logger.info(`[Model] Opened repository: ${repository.root}`); + this.logger.info(`[Model][openRepository] Opened repository: ${repository.root}`); // Do not await this, we want SCM // to know about the repo asap repository.status(); } catch (err) { // noop - this.logger.trace(`[Model] Opening repository for path='${repoPath}' failed; ex=${err}`); + this.logger.trace(`[Model][openRepository] Opening repository for path='${repoPath}' failed. Error:${err}`); } } @@ -651,7 +651,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi const repositoryRootRealPath = await fs.promises.realpath(repositoryRoot); return !pathEquals(repositoryRoot, repositoryRootRealPath) ? repositoryRootRealPath : undefined; } catch (err) { - this.logger.warn(`[Model] Failed to get repository realpath for "${repositoryRoot}": ${err}`); + this.logger.warn(`[Model][getRepositoryRootRealPath] Failed to get repository realpath for "${repositoryRoot}": ${err}`); return undefined; } } @@ -678,7 +678,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } private open(repository: Repository): void { - this.logger.trace(`[Model] Open repository: ${repository.root}`); + this.logger.trace(`[Model][open] Repository: ${repository.root}`); const onDidDisappearRepository = filterEvent(repository.onDidChangeState, state => state === RepositoryState.Disposed); const disappearListener = onDidDisappearRepository(() => dispose()); @@ -695,7 +695,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi const checkForSubmodules = () => { if (!shouldDetectSubmodules) { - this.logger.trace('[Model] Automatic detection of git submodules is not enabled.'); + this.logger.trace('[Model][open] Automatic detection of git submodules is not enabled.'); return; } @@ -708,7 +708,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi .slice(0, submodulesLimit) .map(r => path.join(repository.root, r.path)) .forEach(p => { - this.logger.trace(`Opening submodule: '${p}'`); + this.logger.trace(`[Model][open] Opening submodule: '${p}'`); this.eventuallyScanPossibleGitRepository(p); }); }; @@ -770,7 +770,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi return; } - this.logger.info(`[Model] Close repository: ${repository.root}`); + this.logger.info(`[Model][close] Repository: ${repository.root}`); this._closedRepositoriesManager.addRepository(openRepository.repository.root); openRepository.dispose(); @@ -823,7 +823,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi return openRepositoryRealPath?.repository; } catch (err) { - this.logger.warn(`[Model] Failed to get repository realpath for: "${repoPath}". ${err}`); + this.logger.warn(`[Model][getRepositoryExact] Failed to get repository realpath for: "${repoPath}". Error:${err}`); return undefined; } } @@ -1009,7 +1009,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi this._workspaceFolders.set(workspaceFolder.uri.fsPath, result); } catch (err) { // noop - Workspace folder does not exist - this.logger.trace(`[Model] Failed to resolve workspace folder "${workspaceFolder.uri.fsPath}": ${err}`); + this.logger.trace(`[Model][getWorkspaceFolderRealPath] Failed to resolve workspace folder "${workspaceFolder.uri.fsPath}". Error:${err}`); } } diff --git a/extensions/git/src/operation.ts b/extensions/git/src/operation.ts index 75f9405b3e3..13370d59bf7 100644 --- a/extensions/git/src/operation.ts +++ b/extensions/git/src/operation.ts @@ -214,7 +214,7 @@ export class OperationManager implements IOperationManager { this.operations.set(operation.kind, new Set([operation])); } - this.logger.trace(`Operation start: ${operation.kind} (blocking: ${operation.blocking}, readOnly: ${operation.readOnly}; retry: ${operation.retry}; showProgress: ${operation.showProgress})`); + this.logger.trace(`[OperationManager][start] ${operation.kind} (blocking: ${operation.blocking}, readOnly: ${operation.readOnly}; retry: ${operation.retry}; showProgress: ${operation.showProgress})`); } end(operation: Operation): void { @@ -226,7 +226,7 @@ export class OperationManager implements IOperationManager { } } - this.logger.trace(`Operation end: ${operation.kind} (blocking: ${operation.blocking}, readOnly: ${operation.readOnly}; retry: ${operation.retry}; showProgress: ${operation.showProgress})`); + this.logger.trace(`[OperationManager][end] ${operation.kind} (blocking: ${operation.blocking}, readOnly: ${operation.readOnly}; retry: ${operation.retry}; showProgress: ${operation.showProgress})`); } getOperations(operationKind: OperationKind): Operation[] { diff --git a/extensions/git/src/protocolHandler.ts b/extensions/git/src/protocolHandler.ts index b65eac01a68..90491fecd50 100644 --- a/extensions/git/src/protocolHandler.ts +++ b/extensions/git/src/protocolHandler.ts @@ -22,7 +22,7 @@ export class GitProtocolHandler implements UriHandler { } handleUri(uri: Uri): void { - this.logger.info(`[GitProtocolHandler] handleUri(${uri.toString()})`); + this.logger.info(`[GitProtocolHandler][handleUri] URI:(${uri.toString()})`); switch (uri.path) { case '/clone': this.clone(uri); @@ -34,17 +34,17 @@ export class GitProtocolHandler implements UriHandler { const ref = data.ref; if (!data.url) { - this.logger.warn('[GitProtocolHandler] Failed to open URI:' + uri.toString()); + this.logger.warn('[GitProtocolHandler][clone] Failed to open URI:' + uri.toString()); return; } if (Array.isArray(data.url) && data.url.length === 0) { - this.logger.warn('[GitProtocolHandler] Failed to open URI:' + uri.toString()); + this.logger.warn('[GitProtocolHandler][clone] Failed to open URI:' + uri.toString()); return; } if (ref !== undefined && typeof ref !== 'string') { - this.logger.warn('[GitProtocolHandler] Failed to open URI due to multiple references:' + uri.toString()); + this.logger.warn('[GitProtocolHandler][clone] Failed to open URI due to multiple references:' + uri.toString()); return; } @@ -69,12 +69,12 @@ export class GitProtocolHandler implements UriHandler { } } catch (ex) { - this.logger.warn('[GitProtocolHandler] Invalid URI:' + uri.toString()); + this.logger.warn('[GitProtocolHandler][clone] Invalid URI:' + uri.toString()); return; } if (!(await commands.getCommands(true)).includes('git.clone')) { - this.logger.error('[GitProtocolHandler] Could not complete git clone operation as git installation was not found.'); + this.logger.error('[GitProtocolHandler][clone] Could not complete git clone operation as git installation was not found.'); const errorMessage = l10n.t('Could not clone your repository as Git is not installed.'); const downloadGit = l10n.t('Download Git'); @@ -86,7 +86,7 @@ export class GitProtocolHandler implements UriHandler { return; } else { const cloneTarget = cloneUri.toString(true); - this.logger.info(`[GitProtocolHandler] Executing git.clone for ${cloneTarget}`); + this.logger.info(`[GitProtocolHandler][clone] Executing git.clone for ${cloneTarget}`); commands.executeCommand('git.clone', cloneTarget, undefined, { ref: ref }); } } diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index deec56f9eaa..5c14345e61a 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -426,8 +426,8 @@ class FileEventLogger { } this.eventDisposable = combinedDisposable([ - this.onWorkspaceWorkingTreeFileChange(uri => this.logger.debug(`[wt] Change: ${uri.fsPath}`)), - this.onDotGitFileChange(uri => this.logger.debug(`[.git] Change: ${uri.fsPath}`)) + this.onWorkspaceWorkingTreeFileChange(uri => this.logger.debug(`[FileEventLogger][onWorkspaceWorkingTreeFileChange] ${uri.fsPath}`)), + this.onDotGitFileChange(uri => this.logger.debug(`[FileEventLogger][onDotGitFileChange] ${uri.fsPath}`)) ]); } @@ -478,7 +478,7 @@ class DotGitWatcher implements IFileWatcher { this.transientDisposables.push(upstreamWatcher); upstreamWatcher.event(this.emitter.fire, this.emitter, this.transientDisposables); } catch (err) { - this.logger.warn(`Failed to watch ref '${upstreamPath}', is most likely packed.`); + this.logger.warn(`[DotGitWatcher][updateTransientWatchers] Failed to watch ref '${upstreamPath}', is most likely packed.`); } } @@ -1523,7 +1523,7 @@ export class Repository implements Disposable { return upstreamBranch; } catch (err) { - this.logger.warn(`Failed to get branch details for 'refs/remotes/${branch.upstream.remote}/${branch.upstream.name}': ${err.message}.`); + this.logger.warn(`[Repository][getUpstreamBranch] Failed to get branch details for 'refs/remotes/${branch.upstream.remote}/${branch.upstream.name}': ${err.message}.`); return undefined; } } @@ -2409,17 +2409,17 @@ export class Repository implements Disposable { const autorefresh = config.get('autorefresh'); if (!autorefresh) { - this.logger.trace('Skip running git status because autorefresh setting is disabled.'); + this.logger.trace('[Repository][onFileChange] Skip running git status because autorefresh setting is disabled.'); return; } if (this.isRepositoryHuge) { - this.logger.trace('Skip running git status because repository is huge.'); + this.logger.trace('[Repository][onFileChange] Skip running git status because repository is huge.'); return; } if (!this.operations.isIdle()) { - this.logger.trace('Skip running git status because an operation is running.'); + this.logger.trace('[Repository][onFileChange] Skip running git status because an operation is running.'); return; } From f9af31c78d9271b75ce5d818f28c2abd43def2bf Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Fri, 5 Jul 2024 12:05:34 +0200 Subject: [PATCH 0292/2222] Disable configure keybinding for actions that do not support it (#220050) disable configure keybinding if action does not support it --- src/vs/platform/actions/browser/toolbar.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/vs/platform/actions/browser/toolbar.ts b/src/vs/platform/actions/browser/toolbar.ts index a525f06ae9e..003ee6e781c 100644 --- a/src/vs/platform/actions/browser/toolbar.ts +++ b/src/vs/platform/actions/browser/toolbar.ts @@ -202,8 +202,9 @@ export class WorkbenchToolBar extends ToolBar { if (action instanceof MenuItemAction && action.menuKeybinding) { primaryActions.push(action.menuKeybinding); } else if (!(action instanceof SubmenuItemAction || action instanceof ToggleMenuAction)) { - const isDisabled = action.id.startsWith('statusbaraction'); // We can't support keybinding configuration for scm statusbar actions - primaryActions.push(createConfigureKeybindingAction(this._commandService, this._keybindingService, action.id, undefined, !isDisabled)); + // only enable the configure keybinding action for actions that support keybindings + const supportsKeybindings = !!this._keybindingService.lookupKeybinding(action.id); + primaryActions.push(createConfigureKeybindingAction(this._commandService, this._keybindingService, action.id, undefined, supportsKeybindings)); } // -- Hide Actions -- From 20775fd4174d112d7e9bd2100a1170ce06d064fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dirk=20B=C3=A4umer?= Date: Fri, 5 Jul 2024 12:26:44 +0200 Subject: [PATCH 0293/2222] Reenable yarn eslint (#220037) * Reenable yarn eslint * Fix eslint errors * Fix compile error * Fix another compile error * Disable eslint errors --- build/filters.js | 2 +- .../languages/supports/richEditBrackets.ts | 3 +-- .../contrib/rename/browser/renameWidget.ts | 11 ++++----- .../commandDetection/promptInputModel.test.ts | 1 + .../api/browser/mainThreadComments.ts | 3 +-- .../contrib/chat/browser/chatAgentHover.ts | 23 +++++++++---------- .../test}/testReporterModel.test.ts | 2 ++ .../replNotebook/browser/repl.contribution.ts | 1 + .../replNotebook/browser/replEditor.ts | 1 + .../testing/browser/testCoverageBars.ts | 3 +-- 10 files changed, 25 insertions(+), 25 deletions(-) rename src/vs/workbench/contrib/issue/{issue => browser/test}/testReporterModel.test.ts (98%) diff --git a/build/filters.js b/build/filters.js index c7be2d818d9..915240f0f0b 100644 --- a/build/filters.js +++ b/build/filters.js @@ -199,7 +199,7 @@ module.exports.eslintFilter = [ .toString().split(/\r\n|\n/) .filter(line => !line.startsWith('#')) .filter(line => !!line) - .map(line => `!${line}`) + .map(line => line.startsWith('!') ? line.slice(1) : `!${line}`) ]; module.exports.stylelintFilter = [ diff --git a/src/vs/editor/common/languages/supports/richEditBrackets.ts b/src/vs/editor/common/languages/supports/richEditBrackets.ts index 7733719f049..abb30850466 100644 --- a/src/vs/editor/common/languages/supports/richEditBrackets.ts +++ b/src/vs/editor/common/languages/supports/richEditBrackets.ts @@ -7,7 +7,6 @@ import * as strings from 'vs/base/common/strings'; import * as stringBuilder from 'vs/editor/common/core/stringBuilder'; import { Range } from 'vs/editor/common/core/range'; import { CharacterPair } from 'vs/editor/common/languages/languageConfiguration'; -import { RegExpOptions } from 'vs/base/common/strings'; interface InternalBracket { open: string[]; @@ -409,7 +408,7 @@ function prepareBracketForRegExp(str: string): string { return (insertWordBoundaries ? `\\b${str}\\b` : str); } -export function createBracketOrRegExp(pieces: string[], options?: RegExpOptions): RegExp { +export function createBracketOrRegExp(pieces: string[], options?: strings.RegExpOptions): RegExp { const regexStr = `(${pieces.map(prepareBracketForRegExp).join(')|(')})`; return strings.createRegExp(regexStr, true, options); } diff --git a/src/vs/editor/contrib/rename/browser/renameWidget.ts b/src/vs/editor/contrib/rename/browser/renameWidget.ts index fc1c165a90d..b284cd519fd 100644 --- a/src/vs/editor/contrib/rename/browser/renameWidget.ts +++ b/src/vs/editor/contrib/rename/browser/renameWidget.ts @@ -32,7 +32,6 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { ScrollType } from 'vs/editor/common/editorCommon'; import { NewSymbolName, NewSymbolNameTag, NewSymbolNameTriggerKind, ProviderResult } from 'vs/editor/common/languages'; import * as nls from 'vs/nls'; -import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ILogService } from 'vs/platform/log/common/log'; @@ -55,8 +54,8 @@ const _sticky = false ; -export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey('renameInputVisible', false, localize('renameInputVisible', "Whether the rename input widget is visible")); -export const CONTEXT_RENAME_INPUT_FOCUSED = new RawContextKey('renameInputFocused', false, localize('renameInputFocused', "Whether the rename input widget is focused")); +export const CONTEXT_RENAME_INPUT_VISIBLE = new RawContextKey('renameInputVisible', false, nls.localize('renameInputVisible', "Whether the rename input widget is visible")); +export const CONTEXT_RENAME_INPUT_FOCUSED = new RawContextKey('renameInputFocused', false, nls.localize('renameInputFocused', "Whether the rename input widget is focused")); /** * "Source" of the new name: @@ -311,7 +310,7 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable beforeRender(): IDimension | null { const [accept, preview] = this._acceptKeybindings; - this._label!.innerText = localize({ key: 'label', comment: ['placeholders are keybindings, e.g "F2 to Rename, Shift+F2 to Preview"'] }, "{0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); + this._label!.innerText = nls.localize({ key: 'label', comment: ['placeholders are keybindings, e.g "F2 to Rename, Shift+F2 to Preview"'] }, "{0} to Rename, {1} to Preview", this._keybindingService.lookupKeybinding(accept)?.getLabel(), this._keybindingService.lookupKeybinding(preview)?.getLabel()); this._domNode!.style.minWidth = `200px`; // to prevent from widening when candidates come in @@ -750,7 +749,7 @@ class RenameCandidateListView { this._listContainer.style.height = `${height}px`; this._listContainer.style.width = `${width}px`; - aria.status(localize('renameSuggestionsReceivedAria', "Received {0} rename suggestions", candidates.length)); + aria.status(nls.localize('renameSuggestionsReceivedAria', "Received {0} rename suggestions", candidates.length)); } public clearCandidates(): void { @@ -924,7 +923,7 @@ class InputWithButton implements IDisposable { this._inputNode.className = 'rename-input'; this._inputNode.type = 'text'; this._inputNode.style.border = 'none'; - this._inputNode.setAttribute('aria-label', localize('renameAriaLabel', "Rename input. Type new name and press Enter to commit.")); + this._inputNode.setAttribute('aria-label', nls.localize('renameAriaLabel', "Rename input. Type new name and press Enter to commit.")); this._domNode.appendChild(this._inputNode); diff --git a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts index 9e015c72b27..667442dd8c9 100644 --- a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts +++ b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +/* eslint-disable local/code-import-patterns */ import type { Terminal } from '@xterm/xterm'; import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 6bbeb159248..a0f00ce4416 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -30,7 +30,6 @@ import { MarshalledCommentThread } from 'vs/workbench/common/comments'; import { revealCommentThread } from 'vs/workbench/contrib/comments/browser/commentsController'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; -import { CommentThreadRevealOptions } from 'vs/editor/common/languages'; export class MainThreadCommentThread implements languages.CommentThread { private _input?: languages.CommentInput; @@ -643,7 +642,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments provider.updateCommentingRanges(resourceHints); } - async $revealCommentThread(handle: number, commentThreadHandle: number, options: CommentThreadRevealOptions): Promise { + async $revealCommentThread(handle: number, commentThreadHandle: number, options: languages.CommentThreadRevealOptions): Promise { const provider = this._commentControllers.get(handle); if (!provider) { diff --git a/src/vs/workbench/contrib/chat/browser/chatAgentHover.ts b/src/vs/workbench/contrib/chat/browser/chatAgentHover.ts index 3b1a011b5c5..78f34762cf0 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAgentHover.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAgentHover.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { h } from 'vs/base/browser/dom'; import { IManagedHoverOptions } from 'vs/base/browser/ui/hover/hover'; import { renderIcon } from 'vs/base/browser/ui/iconLabel/iconLabels'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; @@ -40,22 +39,22 @@ export class ChatAgentHover extends Disposable { ) { super(); - const hoverElement = h( + const hoverElement = dom.h( '.chat-agent-hover@root', [ - h('.chat-agent-hover-header', [ - h('.chat-agent-hover-icon@icon'), - h('.chat-agent-hover-details', [ - h('.chat-agent-hover-name@name'), - h('.chat-agent-hover-extension', [ - h('.chat-agent-hover-extension-name@extensionName'), - h('.chat-agent-hover-separator@separator'), - h('.chat-agent-hover-publisher@publisher'), + dom.h('.chat-agent-hover-header', [ + dom.h('.chat-agent-hover-icon@icon'), + dom.h('.chat-agent-hover-details', [ + dom.h('.chat-agent-hover-name@name'), + dom.h('.chat-agent-hover-extension', [ + dom.h('.chat-agent-hover-extension-name@extensionName'), + dom.h('.chat-agent-hover-separator@separator'), + dom.h('.chat-agent-hover-publisher@publisher'), ]), ]), ]), - h('.chat-agent-hover-warning@warning'), - h('span.chat-agent-hover-description@description'), + dom.h('.chat-agent-hover-warning@warning'), + dom.h('span.chat-agent-hover-description@description'), ]); this.domNode = hoverElement.root; diff --git a/src/vs/workbench/contrib/issue/issue/testReporterModel.test.ts b/src/vs/workbench/contrib/issue/browser/test/testReporterModel.test.ts similarity index 98% rename from src/vs/workbench/contrib/issue/issue/testReporterModel.test.ts rename to src/vs/workbench/contrib/issue/browser/test/testReporterModel.test.ts index 60ea6c2089c..f90b10bade8 100644 --- a/src/vs/workbench/contrib/issue/issue/testReporterModel.test.ts +++ b/src/vs/workbench/contrib/issue/browser/test/testReporterModel.test.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// eslint-disable-next-line local/code-import-patterns import assert from 'assert'; +// eslint-disable-next-line local/code-import-patterns import { ensureNoDisposablesAreLeakedInTestSuite } from 'vs/base/test/common/utils'; import { IssueReporterModel } from 'vs/workbench/contrib/issue/browser/issueReporterModel'; import { IssueType } from 'vs/platform/issue/common/issue'; diff --git a/src/vs/workbench/contrib/replNotebook/browser/repl.contribution.ts b/src/vs/workbench/contrib/replNotebook/browser/repl.contribution.ts index 04826105015..1db3282ad9e 100644 --- a/src/vs/workbench/contrib/replNotebook/browser/repl.contribution.ts +++ b/src/vs/workbench/contrib/replNotebook/browser/repl.contribution.ts @@ -24,6 +24,7 @@ import { IWorkingCopyIdentifier } from 'vs/workbench/services/workingCopy/common import { IWorkingCopyEditorHandler, IWorkingCopyEditorService } from 'vs/workbench/services/workingCopy/common/workingCopyEditorService'; import { extname, isEqual } from 'vs/base/common/resources'; import { INotebookService } from 'vs/workbench/contrib/notebook/common/notebookService'; +// eslint-disable-next-line local/code-translation-remind import { localize2 } from 'vs/nls'; import { Action2, registerAction2 } from 'vs/platform/actions/common/actions'; import { IEditorResolverService, RegisteredEditorPriority } from 'vs/workbench/services/editor/common/editorResolverService'; diff --git a/src/vs/workbench/contrib/replNotebook/browser/replEditor.ts b/src/vs/workbench/contrib/replNotebook/browser/replEditor.ts index 67a803eab40..53a0b25667d 100644 --- a/src/vs/workbench/contrib/replNotebook/browser/replEditor.ts +++ b/src/vs/workbench/contrib/replNotebook/browser/replEditor.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/interactive'; +// eslint-disable-next-line local/code-translation-remind import * as nls from 'vs/nls'; import * as DOM from 'vs/base/browser/dom'; import { CancellationToken } from 'vs/base/common/cancellation'; diff --git a/src/vs/workbench/contrib/testing/browser/testCoverageBars.ts b/src/vs/workbench/contrib/testing/browser/testCoverageBars.ts index 0c0111ec344..232184a9e5b 100644 --- a/src/vs/workbench/contrib/testing/browser/testCoverageBars.ts +++ b/src/vs/workbench/contrib/testing/browser/testCoverageBars.ts @@ -18,7 +18,6 @@ import { IHoverService } from 'vs/platform/hover/browser/hover'; import { Registry } from 'vs/platform/registry/common/platform'; import { ExplorerExtensions, IExplorerFileContribution, IExplorerFileContributionRegistry } from 'vs/workbench/contrib/files/browser/explorerFileContrib'; import * as coverUtils from 'vs/workbench/contrib/testing/browser/codeCoverageDisplayUtils'; -import { calculateDisplayedStat } from 'vs/workbench/contrib/testing/browser/codeCoverageDisplayUtils'; import { ITestingCoverageBarThresholds, TestingConfigKeys, getTestingConfiguration, observeTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration'; import { AbstractFileCoverage } from 'vs/workbench/contrib/testing/common/testCoverage'; import { ITestCoverageService } from 'vs/workbench/contrib/testing/common/testCoverageService'; @@ -121,7 +120,7 @@ export class ManagedTestCoverageBars extends Disposable { const precision = this.options.compact ? 0 : 2; const thresholds = getTestingConfiguration(this.configurationService, TestingConfigKeys.CoverageBarThresholds); - const overallStat = calculateDisplayedStat(coverage, getTestingConfiguration(this.configurationService, TestingConfigKeys.CoveragePercent)); + const overallStat = coverUtils.calculateDisplayedStat(coverage, getTestingConfiguration(this.configurationService, TestingConfigKeys.CoveragePercent)); if (this.options.overall !== false) { el.overall.textContent = coverUtils.displayPercent(overallStat, precision); } else { From 796dfbb6ea06ffd6e8bdaf1864851547cdbba367 Mon Sep 17 00:00:00 2001 From: Robo Date: Fri, 5 Jul 2024 20:29:04 +0900 Subject: [PATCH 0294/2222] chore: rm unused dyld entitlement and env variable filtering (#220059) --- .../darwin/helper-plugin-entitlements.plist | 2 -- src/vs/base/common/processes.ts | 9 +-------- 2 files changed, 1 insertion(+), 10 deletions(-) diff --git a/build/azure-pipelines/darwin/helper-plugin-entitlements.plist b/build/azure-pipelines/darwin/helper-plugin-entitlements.plist index 1cc1a152c74..48f7bf5cece 100644 --- a/build/azure-pipelines/darwin/helper-plugin-entitlements.plist +++ b/build/azure-pipelines/darwin/helper-plugin-entitlements.plist @@ -6,8 +6,6 @@ com.apple.security.cs.allow-unsigned-executable-memory - com.apple.security.cs.allow-dyld-environment-variables - com.apple.security.cs.disable-library-validation diff --git a/src/vs/base/common/processes.ts b/src/vs/base/common/processes.ts index 417c4ba1168..ef29387bc0a 100644 --- a/src/vs/base/common/processes.ts +++ b/src/vs/base/common/processes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IProcessEnvironment, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { IProcessEnvironment, isLinux } from 'vs/base/common/platform'; /** * Options to be passed to the external program or shell. @@ -140,13 +140,6 @@ export function removeDangerousEnvVariables(env: IProcessEnvironment | undefined // See https://github.com/microsoft/vscode/issues/130072 delete env['DEBUG']; - if (isMacintosh) { - // Unset `DYLD_LIBRARY_PATH`, as it leads to process crashes - // See https://github.com/microsoft/vscode/issues/104525 - // See https://github.com/microsoft/vscode/issues/105848 - delete env['DYLD_LIBRARY_PATH']; - } - if (isLinux) { // Unset `LD_PRELOAD`, as it might lead to process crashes // See https://github.com/microsoft/vscode/issues/134177 From 701145d5e48b43cf6141094b146d5656e6d2f4fb Mon Sep 17 00:00:00 2001 From: Jean Pierre Date: Fri, 5 Jul 2024 07:26:43 -0500 Subject: [PATCH 0295/2222] Fix port label not applied when forwarding port with vscode.env.asExternalUri (#220029) Don't ignore port attributes label --- src/vs/workbench/services/remote/common/tunnelModel.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/services/remote/common/tunnelModel.ts b/src/vs/workbench/services/remote/common/tunnelModel.ts index 14f20e126ad..bd6f0fb4589 100644 --- a/src/vs/workbench/services/remote/common/tunnelModel.ts +++ b/src/vs/workbench/services/remote/common/tunnelModel.ts @@ -459,6 +459,7 @@ export class TunnelModel extends Disposable { protocol: attributes?.get(tunnel.tunnelRemotePort)?.protocol ?? TunnelProtocol.Http, localUri: await this.makeLocalUri(tunnel.localAddress, attributes?.get(tunnel.tunnelRemotePort)), localPort: tunnel.tunnelLocalPort, + name: attributes?.get(tunnel.tunnelRemotePort)?.label, runningProcess: matchingCandidate?.detail, hasRunningProcess: !!matchingCandidate, pid: matchingCandidate?.pid, @@ -486,6 +487,7 @@ export class TunnelModel extends Disposable { protocol: attributes?.protocol ?? TunnelProtocol.Http, localUri: await this.makeLocalUri(tunnel.localAddress, attributes), localPort: tunnel.tunnelLocalPort, + name: attributes?.label, closeable: true, runningProcess: matchingCandidate?.detail, hasRunningProcess: !!matchingCandidate, From 84c007cbb21ba792fef8dba1a1f89de00ff50b19 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 5 Jul 2024 12:43:24 +0200 Subject: [PATCH 0296/2222] Fixes #219006. Open file now opens the related file on disk, instead of just the right side. This also applies to staged files. --- .../multiDiffEditorViewModel.ts | 5 +-- .../multiDiffEditor/multiDiffEditorWidget.ts | 6 +++- .../multiDiffEditorWidgetImpl.ts | 10 ++++++ .../api/browser/mainThreadEditorTabs.ts | 6 ++-- src/vs/workbench/common/editor.ts | 5 ++- .../bulkEdit/browser/preview/bulkEditPane.ts | 19 +++++++---- .../multiDiffEditor/browser/actions.ts | 33 +++++++++++-------- .../browser/multiDiffEditor.ts | 10 +++++- .../browser/multiDiffEditorInput.ts | 27 ++++++++++----- .../browser/multiDiffSourceResolverService.ts | 9 ++--- .../browser/scmMultiDiffSourceResolver.ts | 2 +- 11 files changed, 90 insertions(+), 42 deletions(-) diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts index a044eb438ed..b58d7303e66 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts @@ -69,8 +69,9 @@ export class DocumentDiffItemViewModel extends Disposable { { contentHeight: 500, selections: undefined, } ); - public get originalUri(): URI | undefined { return this.entry.value!.original?.uri; } - public get modifiedUri(): URI | undefined { return this.entry.value!.modified?.uri; } + public get documentDiffItem(): IDocumentDiffItem { return this.entry.value!; } + public get originalUri(): URI | undefined { return this.documentDiffItem.original?.uri; } + public get modifiedUri(): URI | undefined { return this.documentDiffItem.modified?.uri; } public readonly isActive: IObservable = derived(this, reader => this._editorViewModel.activeDiffItem.read(reader) === this); diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts index 9d8ace1ea5b..8275c4f7345 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidget.ts @@ -7,7 +7,7 @@ import { Dimension } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; import { derived, derivedWithStore, observableValue, recomputeInitiallyAndOnChange } from 'vs/base/common/observable'; import { readHotReloadableExport } from 'vs/base/common/hotReloadHelpers'; -import { IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditor/model'; +import { IDocumentDiffItem, IMultiDiffEditorModel } from 'vs/editor/browser/widget/multiDiffEditor/model'; import { IMultiDiffEditorViewState, IMultiDiffResourceId, MultiDiffEditorWidgetImpl } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl'; import { MultiDiffEditorViewModel } from './multiDiffEditorViewModel'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -81,6 +81,10 @@ export class MultiDiffEditorWidget extends Disposable { public tryGetCodeEditor(resource: URI): { diffEditor: IDiffEditor; editor: ICodeEditor } | undefined { return this._widgetImpl.get().tryGetCodeEditor(resource); } + + public findDocumentDiffItem(resource: URI): IDocumentDiffItem | undefined { + return this._widgetImpl.get().findDocumentDiffItem(resource); + } } export interface RevealOptions { diff --git a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts index eab554dbdfb..96cccc0afb8 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts @@ -31,6 +31,7 @@ import { DiffEditorItemTemplate, TemplateData } from './diffEditorItemTemplate'; import { DocumentDiffItemViewModel, MultiDiffEditorViewModel } from './multiDiffEditorViewModel'; import { ObjectPool } from './objectPool'; import { localize } from 'vs/nls'; +import { IDocumentDiffItem } from 'vs/editor/browser/widget/multiDiffEditor/model'; export class MultiDiffEditorWidgetImpl extends Disposable { private readonly _scrollableElements = h('div.scrollContent', [ @@ -263,6 +264,14 @@ export class MultiDiffEditorWidgetImpl extends Disposable { }); } + public findDocumentDiffItem(resource: URI): IDocumentDiffItem | undefined { + const item = this._viewItems.get().find(v => + v.viewModel.diffEditorViewModel.model.modified.uri.toString() === resource.toString() + || v.viewModel.diffEditorViewModel.model.original.uri.toString() === resource.toString() + ); + return item?.viewModel.documentDiffItem; + } + public tryGetCodeEditor(resource: URI): { diffEditor: IDiffEditor; editor: ICodeEditor } | undefined { const item = this._viewItems.get().find(v => v.viewModel.diffEditorViewModel.model.modified.uri.toString() === resource.toString() @@ -272,6 +281,7 @@ export class MultiDiffEditorWidgetImpl extends Disposable { if (!editor) { return undefined; } + if (item.viewModel.diffEditorViewModel.model.modified.uri.toString() === resource.toString()) { return { diffEditor: editor, editor: editor.getModifiedEditor() }; } else { diff --git a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts index 48888b0ddcb..9bf27279db0 100644 --- a/src/vs/workbench/api/browser/mainThreadEditorTabs.ts +++ b/src/vs/workbench/api/browser/mainThreadEditorTabs.ts @@ -207,11 +207,11 @@ export class MainThreadEditorTabs implements MainThreadEditorTabsShape { if (editor instanceof MultiDiffEditorInput) { const diffEditors: TextDiffInputDto[] = []; for (const resource of (editor?.resources.get() ?? [])) { - if (resource.original && resource.modified) { + if (resource.originalUri && resource.modifiedUri) { diffEditors.push({ kind: TabInputKind.TextDiffInput, - original: resource.original, - modified: resource.modified + original: resource.originalUri, + modified: resource.modifiedUri }); } } diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 02aff158f4f..d3d6adb4ff5 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -547,7 +547,7 @@ export interface IResourceMultiDiffEditorInput extends IBaseUntypedEditorInput { * The list of resources to compare. * If not set, the resources are dynamically derived from the {@link multiDiffSource}. */ - readonly resources?: IResourceDiffEditorInput[]; + readonly resources?: IMultiDiffEditorResource[]; /** * Whether the editor should be serialized and stored for subsequent sessions. @@ -555,6 +555,9 @@ export interface IResourceMultiDiffEditorInput extends IBaseUntypedEditorInput { readonly isTransient?: boolean; } +export interface IMultiDiffEditorResource extends IResourceDiffEditorInput { + readonly goToFileResource?: URI; +} export type IResourceMergeEditorInputSide = (IResourceEditorInput | ITextResourceEditorInput) & { detail?: string }; /** diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 3c68921a8e5..aa210e91cf3 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -36,7 +36,7 @@ import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; import { ButtonBar } from 'vs/base/browser/ui/button/button'; import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; import { Mutable } from 'vs/base/common/types'; -import { IResourceDiffEditorInput } from 'vs/workbench/common/editor'; +import { IMultiDiffEditorResource, IResourceDiffEditorInput, IResourceMultiDiffEditorInput } from 'vs/workbench/common/editor'; import { IMultiDiffEditorOptions, IMultiDiffResourceId } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl'; import { IRange } from 'vs/editor/common/core/range'; import { CachedFunction, LRUCachedFunction } from 'vs/base/common/cache'; @@ -369,16 +369,20 @@ export class BulkEditPane extends ViewPane { }, e.sideBySide ? SIDE_GROUP : ACTIVE_GROUP); } - private readonly _computeResourceDiffEditorInputs = new LRUCachedFunction(async (fileOperations: BulkFileOperation[]) => { - const computeDiffEditorInput = new CachedFunction>(async (fileOperation) => { + private readonly _computeResourceDiffEditorInputs = new LRUCachedFunction< + BulkFileOperation[], + Promise<{ resources: IMultiDiffEditorResource[]; getResourceDiffEditorInputIdOfOperation: (operation: BulkFileOperation) => Promise }> + >(async (fileOperations) => { + const computeDiffEditorInput = new CachedFunction>(async (fileOperation) => { const fileOperationUri = fileOperation.uri; const previewUri = this._currentProvider!.asPreviewUri(fileOperationUri); // delete if (fileOperation.type & BulkFileOperationType.Delete) { return { original: { resource: URI.revive(previewUri) }, - modified: { resource: undefined } - }; + modified: { resource: undefined }, + goToFileResource: fileOperation.uri, + } satisfies IMultiDiffEditorResource; } // rename, create, edits @@ -392,8 +396,9 @@ export class BulkEditPane extends ViewPane { } return { original: { resource: URI.revive(leftResource) }, - modified: { resource: URI.revive(previewUri) } - }; + modified: { resource: URI.revive(previewUri) }, + goToFileResource: leftResource, + } satisfies IMultiDiffEditorResource; } }); diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts index 5419dba32dc..30e6c69a641 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/actions.ts @@ -10,9 +10,9 @@ import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { localize2 } from 'vs/nls'; import { Action2, MenuId } from 'vs/platform/actions/common/actions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { ITextEditorOptions, TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { resolveCommandsContext } from 'vs/workbench/browser/parts/editor/editorCommandsContext'; -import { TextFileEditor } from 'vs/workbench/contrib/files/browser/editors/textFileEditor'; import { MultiDiffEditor } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor'; import { MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -38,21 +38,28 @@ export class GoToFileAction extends Action2 { const editorService = accessor.get(IEditorService); const activeEditorPane = editorService.activeEditorPane; let selections: Selection[] | undefined = undefined; - if (activeEditorPane instanceof MultiDiffEditor) { - const editor = activeEditorPane.tryGetCodeEditor(uri); - if (editor) { - selections = editor.editor.getSelections() ?? undefined; - } + if (!(activeEditorPane instanceof MultiDiffEditor)) { + return; + } + + const editor = activeEditorPane.tryGetCodeEditor(uri); + if (editor) { + selections = editor.editor.getSelections() ?? undefined; } - const editor = await editorService.openEditor({ resource: uri }); - if (selections && (editor instanceof TextFileEditor)) { - const c = editor.getControl(); - if (c) { - c.setSelections(selections); - c.revealLineInCenter(selections[0].selectionStartLineNumber); - } + let targetUri = uri; + const item = activeEditorPane.findDocumentDiffItem(uri); + if (item && item.goToFileUri) { + targetUri = item.goToFileUri; } + + await editorService.openEditor({ + resource: targetUri, + options: { + selection: selections?.[0], + selectionRevealType: TextEditorSelectionRevealType.CenterIfOutsideViewport, + } satisfies ITextEditorOptions, + }); } } diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts index ffe7a8738f5..2b5168dea9b 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditor.ts @@ -18,7 +18,7 @@ import { AbstractEditorWithViewState } from 'vs/workbench/browser/parts/editor/e import { ICompositeControl } from 'vs/workbench/common/composite'; import { IEditorOpenContext } from 'vs/workbench/common/editor'; import { EditorInput } from 'vs/workbench/common/editor/editorInput'; -import { MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput'; +import { IDocumentDiffItemWithMultiDiffEditorItem, MultiDiffEditorInput } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { URI } from 'vs/base/common/uri'; @@ -27,6 +27,7 @@ import { IMultiDiffEditorOptions, IMultiDiffEditorViewState } from 'vs/editor/br import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IDiffEditor } from 'vs/editor/common/editorCommon'; import { Range } from 'vs/editor/common/core/range'; +import { MultiDiffEditorItem } from 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService'; export class MultiDiffEditor extends AbstractEditorWithViewState { static readonly ID = 'multiDiffEditor'; @@ -139,6 +140,13 @@ export class MultiDiffEditor extends AbstractEditorWithViewState new MultiDiffEditorItem( resource.originalUri ? URI.parse(resource.originalUri) : undefined, resource.modifiedUri ? URI.parse(resource.modifiedUri) : undefined, + resource.goToFileUri ? URI.parse(resource.goToFileUri) : undefined, )), false ); @@ -112,8 +114,9 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor label: this.label, multiDiffSourceUri: this.multiDiffSource.toString(), resources: this.initialResources?.map(resource => ({ - originalUri: resource.original?.toString(), - modifiedUri: resource.modified?.toString(), + originalUri: resource.originalUri?.toString(), + modifiedUri: resource.modifiedUri?.toString(), + goToFileUri: resource.goToFileUri?.toString(), })), }; } @@ -159,8 +162,8 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor try { [original, modified] = await Promise.all([ - r.original ? this._textModelService.createModelReference(r.original) : undefined, - r.modified ? this._textModelService.createModelReference(r.modified) : undefined, + r.originalUri ? this._textModelService.createModelReference(r.originalUri) : undefined, + r.modifiedUri ? this._textModelService.createModelReference(r.modifiedUri) : undefined, ]); if (original) { store2.add(original); } if (modified) { store2.add(modified); } @@ -171,8 +174,9 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor return undefined; } - const uri = (r.modified ?? r.original)!; - return new ConstLazyPromise({ + const uri = (r.modifiedUri ?? r.originalUri)!; + return new ConstLazyPromise({ + multiDiffEditorItem: r, original: original?.object.textEditorModel, modified: modified?.object.textEditorModel, get options() { @@ -187,7 +191,7 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor } }), }); - }, i => JSON.stringify([i.modified?.toString(), i.original?.toString()])); + }, i => JSON.stringify([i.modifiedUri?.toString(), i.originalUri?.toString()])); const documents = observableValue[]>('documents', []); @@ -239,8 +243,8 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor public readonly resources = derived(this, reader => this._resolvedSource.cachedPromiseResult.read(reader)?.data?.resources.read(reader)); private readonly _isDirtyObservables = mapObservableArrayCached(this, this.resources.map(r => r ?? []), res => { - const isModifiedDirty = res.modified ? isUriDirty(this._textFileService, res.modified) : constObservable(false); - const isOriginalDirty = res.original ? isUriDirty(this._textFileService, res.original) : constObservable(false); + const isModifiedDirty = res.modifiedUri ? isUriDirty(this._textFileService, res.modifiedUri) : constObservable(false); + const isOriginalDirty = res.originalUri ? isUriDirty(this._textFileService, res.originalUri) : constObservable(false); return derived(reader => /** @description modifiedDirty||originalDirty */ isModifiedDirty.read(reader) || isOriginalDirty.read(reader)); }, i => i.getKey()); private readonly _isDirtyObservable = derived(this, reader => this._isDirtyObservables.read(reader).some(isDirty => isDirty.read(reader))) @@ -291,6 +295,10 @@ export class MultiDiffEditorInput extends EditorInput implements ILanguageSuppor }; } +export interface IDocumentDiffItemWithMultiDiffEditorItem extends IDocumentDiffItem { + multiDiffEditorItem: MultiDiffEditorItem; +} + function isUriDirty(textFileService: ITextFileService, uri: URI) { return observableFromEvent( Event.filter(textFileService.files.onDidChangeDirty, e => e.resource.toString() === uri.toString()), @@ -361,6 +369,7 @@ interface ISerializedMultiDiffEditorInput { resources: { originalUri: string | undefined; modifiedUri: string | undefined; + goToFileUri: string | undefined; }[] | undefined; } diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService.ts index 43f2f3eb899..44c3da5801d 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/multiDiffSourceResolverService.ts @@ -33,16 +33,17 @@ export interface IResolvedMultiDiffSource { export class MultiDiffEditorItem { constructor( - readonly original: URI | undefined, - readonly modified: URI | undefined, + readonly originalUri: URI | undefined, + readonly modifiedUri: URI | undefined, + readonly goToFileUri: URI | undefined, ) { - if (!original && !modified) { + if (!originalUri && !modifiedUri) { throw new BugIndicatingError('Invalid arguments'); } } getKey(): string { - return JSON.stringify([this.modified?.toString(), this.original?.toString()]); + return JSON.stringify([this.modifiedUri?.toString(), this.originalUri?.toString()]); } } diff --git a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts index be0e22ab323..43ff34c3ef5 100644 --- a/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts +++ b/src/vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts @@ -76,7 +76,7 @@ export class ScmMultiDiffSourceResolver implements IMultiDiffSourceResolver { class ScmResolvedMultiDiffSource implements IResolvedMultiDiffSource { private readonly _resources = observableFromEvent( this._group.onDidChangeResources, - () => /** @description resources */ this._group.resources.map(e => new MultiDiffEditorItem(e.multiDiffEditorOriginalUri, e.multiDiffEditorModifiedUri)) + () => /** @description resources */ this._group.resources.map(e => new MultiDiffEditorItem(e.multiDiffEditorOriginalUri, e.multiDiffEditorModifiedUri, e.sourceUri)) ); readonly resources = new ValueWithChangeEventFromObservable(this._resources); From 58ce7c5a2cdc077f3e62d86a0d8cb8c2a0a9ca2b Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 5 Jul 2024 13:50:17 +0200 Subject: [PATCH 0297/2222] Fixes CI --- .../bulkEdit/browser/preview/bulkEditPane.ts | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index aa210e91cf3..55775a63e36 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -3,44 +3,44 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./bulkEdit'; -import { WorkbenchAsyncDataTree, IOpenEvent } from 'vs/platform/list/browser/listService'; -import { BulkEditElement, BulkEditDelegate, TextEditElementRenderer, FileElementRenderer, BulkEditDataSource, BulkEditIdentityProvider, FileElement, TextEditElement, BulkEditAccessibilityProvider, CategoryElementRenderer, BulkEditNaviLabelProvider, CategoryElement, BulkEditSorter, compareBulkFileOperations } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree'; +import { ButtonBar } from 'vs/base/browser/ui/button/button'; +import type { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; +import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; +import { CachedFunction, LRUCachedFunction } from 'vs/base/common/cache'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { FuzzyScore } from 'vs/base/common/filters'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { localize } from 'vs/nls'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { BulkEditPreviewProvider, BulkFileOperation, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; -import { ILabelService } from 'vs/platform/label/common/label'; -import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { Mutable } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; -import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import 'vs/css!./bulkEdit'; +import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; +import { IMultiDiffEditorOptions, IMultiDiffResourceId } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl'; +import { IRange } from 'vs/editor/common/core/range'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { localize } from 'vs/nls'; +import { MenuId } from 'vs/platform/actions/common/actions'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IContextKeyService, RawContextKey, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; -import { ResourceLabels } from 'vs/workbench/browser/labels'; +import { IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { MenuId } from 'vs/platform/actions/common/actions'; -import { ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; -import { CancellationToken } from 'vs/base/common/cancellation'; -import type { IAsyncDataTreeViewState } from 'vs/base/browser/ui/tree/asyncDataTree'; -import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { ILabelService } from 'vs/platform/label/common/label'; +import { IOpenEvent, WorkbenchAsyncDataTree } from 'vs/platform/list/browser/listService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { ResourceEdit } from 'vs/editor/browser/services/bulkEditService'; -import { ButtonBar } from 'vs/base/browser/ui/button/button'; import { defaultButtonStyles } from 'vs/platform/theme/browser/defaultStyles'; -import { Mutable } from 'vs/base/common/types'; -import { IMultiDiffEditorResource, IResourceDiffEditorInput, IResourceMultiDiffEditorInput } from 'vs/workbench/common/editor'; -import { IMultiDiffEditorOptions, IMultiDiffResourceId } from 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl'; -import { IRange } from 'vs/editor/common/core/range'; -import { CachedFunction, LRUCachedFunction } from 'vs/base/common/cache'; -import { IHoverService } from 'vs/platform/hover/browser/hover'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { ResourceLabels } from 'vs/workbench/browser/labels'; +import { ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; +import { IViewletViewOptions } from 'vs/workbench/browser/parts/views/viewsViewlet'; +import { IMultiDiffEditorResource, IResourceDiffEditorInput } from 'vs/workbench/common/editor'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { BulkEditPreviewProvider, BulkFileOperation, BulkFileOperations, BulkFileOperationType } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview'; +import { BulkEditAccessibilityProvider, BulkEditDataSource, BulkEditDelegate, BulkEditElement, BulkEditIdentityProvider, BulkEditNaviLabelProvider, BulkEditSorter, CategoryElement, CategoryElementRenderer, compareBulkFileOperations, FileElement, FileElementRenderer, TextEditElement, TextEditElementRenderer } from 'vs/workbench/contrib/bulkEdit/browser/preview/bulkEditTree'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; const enum State { Data = 'data', From 5ed967a8e2a11dd9a88c311487b1e54d783ff5ec Mon Sep 17 00:00:00 2001 From: Anthony Stewart <150152+a-stewart@users.noreply.github.com> Date: Fri, 5 Jul 2024 14:42:19 +0200 Subject: [PATCH 0298/2222] Ensure titlebar is at least as tall as the bounding rect of WCO (#211440) * Ensure that the titlebar is always at least as tall as the bounding rectangle of the window controls overlay * Fix formatting --------- Co-authored-by: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> --- src/vs/base/browser/browser.ts | 6 ++++++ src/vs/workbench/browser/parts/titlebar/titlebarPart.ts | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/vs/base/browser/browser.ts b/src/vs/base/browser/browser.ts index 77f55b943a5..87db1c573ed 100644 --- a/src/vs/base/browser/browser.ts +++ b/src/vs/base/browser/browser.ts @@ -133,3 +133,9 @@ export function isStandalone(): boolean { export function isWCOEnabled(): boolean { return (navigator as any)?.windowControlsOverlay?.visible; } + +// Returns the bounding rect of the titlebar area if it is supported and defined +// See docs at https://developer.mozilla.org/en-US/docs/Web/API/WindowControlsOverlay/getTitlebarAreaRect +export function getWCOBoundingRect(): DOMRect | undefined { + return (navigator as any)?.windowControlsOverlay?.getTitlebarAreaRect(); +} diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index bed98fd3b61..f610512e84f 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/titlebarpart'; import { localize, localize2 } from 'vs/nls'; import { MultiWindowParts, Part } from 'vs/workbench/browser/part'; import { ITitleService } from 'vs/workbench/services/title/browser/titleService'; -import { getZoomFactor, isWCOEnabled } from 'vs/base/browser/browser'; +import { getWCOBoundingRect, getZoomFactor, isWCOEnabled } from 'vs/base/browser/browser'; import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, TitlebarStyle, hasCustomTitlebar, hasNativeTitlebar, DEFAULT_CUSTOM_TITLEBAR_HEIGHT } from 'vs/platform/window/common/window'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -200,7 +200,11 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { readonly maximumWidth: number = Number.POSITIVE_INFINITY; get minimumHeight(): number { - const value = this.isCommandCenterVisible || (isWeb && isWCOEnabled()) ? DEFAULT_CUSTOM_TITLEBAR_HEIGHT : 30; + const wcoEnabled = isWeb && isWCOEnabled(); + let value = this.isCommandCenterVisible || wcoEnabled ? DEFAULT_CUSTOM_TITLEBAR_HEIGHT : 30; + if (wcoEnabled) { + value = Math.max(value, getWCOBoundingRect()?.height ?? 0); + } return value / (this.preventZoom ? getZoomFactor(getWindow(this.element)) : 1); } From 25b4860814c2a44d946401ed0fcee7860fcce696 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Fri, 5 Jul 2024 16:28:13 +0200 Subject: [PATCH 0299/2222] "potential listener LEAK detected" for comment elements that are recreated on every "display" call but not disposed of (#220091) Fixes #219984 --- .../contrib/comments/browser/commentNode.ts | 2 +- .../comments/browser/commentThreadBody.ts | 16 +++++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/vs/workbench/contrib/comments/browser/commentNode.ts b/src/vs/workbench/contrib/comments/browser/commentNode.ts index 47e5285466c..f7f6688cfbe 100644 --- a/src/vs/workbench/contrib/comments/browser/commentNode.ts +++ b/src/vs/workbench/contrib/comments/browser/commentNode.ts @@ -122,7 +122,7 @@ export class CommentNode extends Disposable { super(); this._domNode = dom.$('div.review-comment'); - this._contextKeyService = contextKeyService.createScoped(this._domNode); + this._contextKeyService = this._register(contextKeyService.createScoped(this._domNode)); this._commentContextValue = CommentContextKeys.commentContext.bindTo(this._contextKeyService); if (this.comment.contextValue) { this._commentContextValue.set(this.comment.contextValue); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts index 3728fb02731..7156f465fa5 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts @@ -5,7 +5,7 @@ import * as dom from 'vs/base/browser/dom'; import * as nls from 'vs/nls'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, DisposableMap, DisposableStore } from 'vs/base/common/lifecycle'; import * as languages from 'vs/editor/common/languages'; import { Emitter } from 'vs/base/common/event'; import { ICommentService } from 'vs/workbench/contrib/comments/browser/commentService'; @@ -30,7 +30,7 @@ export class CommentThreadBody extends D private _onDidResize = new Emitter(); onDidResize = this._onDidResize.event; - private _commentDisposable = new Map, IDisposable>(); + private _commentDisposable = new DisposableMap, DisposableStore>(); private _markdownRenderer: MarkdownRenderer; get length() { @@ -90,6 +90,7 @@ export class CommentThreadBody extends D } })); + this._commentDisposable.clearAndDisposeAll(); this._commentElements = []; if (this._commentThread.comments) { for (const comment of this._commentThread.comments) { @@ -177,8 +178,7 @@ export class CommentThreadBody extends D // del removed elements for (let i = commentElementsToDel.length - 1; i >= 0; i--) { const commentToDelete = commentElementsToDel[i]; - this._commentDisposable.get(commentToDelete)?.dispose(); - this._commentDisposable.delete(commentToDelete); + this._commentDisposable.deleteAndDispose(commentToDelete); this._commentElements.splice(commentElementsToDelIndex[i], 1); commentToDelete.domNode.remove(); @@ -267,10 +267,12 @@ export class CommentThreadBody extends D this._parentCommentThreadWidget, this._markdownRenderer) as unknown as CommentNode; - this._register(newCommentNode); - this._commentDisposable.set(newCommentNode, newCommentNode.onDidClick(clickedNode => + const disposables: DisposableStore = new DisposableStore(); + disposables.add(newCommentNode.onDidClick(clickedNode => this._setFocusedComment(this._commentElements.findIndex(commentNode => commentNode.comment.uniqueIdInThread === clickedNode.comment.uniqueIdInThread)) )); + disposables.add(newCommentNode); + this._commentDisposable.set(newCommentNode, disposables); return newCommentNode; } @@ -283,6 +285,6 @@ export class CommentThreadBody extends D this._resizeObserver = null; } - this._commentDisposable.forEach(v => v.dispose()); + this._commentDisposable.dispose(); } } From 3a0cc239a9666b8c21572cbaf1e7ec1119354e73 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 5 Jul 2024 16:30:50 +0200 Subject: [PATCH 0300/2222] :up: `jschardet@3.1.3` (#220089) --- extensions/git/package.json | 2 +- extensions/git/yarn.lock | 8 ++++---- package.json | 2 +- remote/package.json | 2 +- remote/web/package.json | 2 +- remote/web/yarn.lock | 8 ++++---- remote/yarn.lock | 8 ++++---- yarn.lock | 39 ++++++++++++++++++++++++++++++------- 8 files changed, 48 insertions(+), 23 deletions(-) diff --git a/extensions/git/package.json b/extensions/git/package.json index 4fc372e21f7..f2445eb3978 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -3401,7 +3401,7 @@ "@vscode/iconv-lite-umd": "0.7.0", "byline": "^5.0.0", "file-type": "16.5.4", - "jschardet": "3.1.2", + "jschardet": "3.1.3", "picomatch": "2.3.1", "vscode-uri": "^2.0.0", "which": "4.0.0" diff --git a/extensions/git/yarn.lock b/extensions/git/yarn.lock index 06023a61227..a7e1a693f84 100644 --- a/extensions/git/yarn.lock +++ b/extensions/git/yarn.lock @@ -182,10 +182,10 @@ isexe@^3.1.1: resolved "https://registry.yarnpkg.com/isexe/-/isexe-3.1.1.tgz#4a407e2bd78ddfb14bea0c27c6f7072dde775f0d" integrity sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ== -jschardet@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" - integrity sha512-mw3CBZGzW8nUBPYhFU2ztZ/kJ6NClQUQVpyzvFMfznZsoC///ZQ30J2RCUanNsr5yF22LqhgYr/lj807/ZleWA== +jschardet@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.3.tgz#10c2289fdae91a0aa9de8bba9c59055fd78898d3" + integrity sha512-Q1PKVMK/uu+yjdlobgWIYkUOCR1SqUmW9m/eUJNNj4zI2N12i25v8fYpVf+zCakQeaTdBdhnZTFbVIAVZIVVOg== peek-readable@^4.1.0: version "4.1.0" diff --git a/package.json b/package.json index bfec4be89e0..e8c78dfc686 100644 --- a/package.json +++ b/package.json @@ -92,7 +92,7 @@ "@xterm/xterm": "5.6.0-beta.36", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", - "jschardet": "3.1.2", + "jschardet": "3.1.3", "kerberos": "^2.0.1", "minimist": "^1.2.6", "native-is-elevated": "0.7.0", diff --git a/remote/package.json b/remote/package.json index e39eb2215d2..bb615b88ea4 100644 --- a/remote/package.json +++ b/remote/package.json @@ -25,7 +25,7 @@ "cookie": "^0.4.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", - "jschardet": "3.1.2", + "jschardet": "3.1.3", "kerberos": "^2.0.1", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", diff --git a/remote/web/package.json b/remote/web/package.json index 320980c9761..f6b57db5ea0 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -14,7 +14,7 @@ "@xterm/addon-unicode11": "0.9.0-beta.36", "@xterm/addon-webgl": "0.19.0-beta.36", "@xterm/xterm": "5.6.0-beta.36", - "jschardet": "3.1.2", + "jschardet": "3.1.3", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", "vscode-textmate": "9.0.0" diff --git a/remote/web/yarn.lock b/remote/web/yarn.lock index 1eb4c160597..a28d00372f7 100644 --- a/remote/web/yarn.lock +++ b/remote/web/yarn.lock @@ -90,10 +90,10 @@ js-base64@^3.7.5: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.7.tgz#e51b84bf78fbf5702b9541e2cb7bfcb893b43e79" integrity sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw== -jschardet@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" - integrity sha512-mw3CBZGzW8nUBPYhFU2ztZ/kJ6NClQUQVpyzvFMfznZsoC///ZQ30J2RCUanNsr5yF22LqhgYr/lj807/ZleWA== +jschardet@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.3.tgz#10c2289fdae91a0aa9de8bba9c59055fd78898d3" + integrity sha512-Q1PKVMK/uu+yjdlobgWIYkUOCR1SqUmW9m/eUJNNj4zI2N12i25v8fYpVf+zCakQeaTdBdhnZTFbVIAVZIVVOg== tas-client-umd@0.2.0: version "0.2.0" diff --git a/remote/yarn.lock b/remote/yarn.lock index 9b0e1ec44b5..0f65b688c17 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -374,10 +374,10 @@ jsbn@1.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== -jschardet@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" - integrity sha512-mw3CBZGzW8nUBPYhFU2ztZ/kJ6NClQUQVpyzvFMfznZsoC///ZQ30J2RCUanNsr5yF22LqhgYr/lj807/ZleWA== +jschardet@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.3.tgz#10c2289fdae91a0aa9de8bba9c59055fd78898d3" + integrity sha512-Q1PKVMK/uu+yjdlobgWIYkUOCR1SqUmW9m/eUJNNj4zI2N12i25v8fYpVf+zCakQeaTdBdhnZTFbVIAVZIVVOg== jsonfile@^6.0.1: version "6.1.0" diff --git a/yarn.lock b/yarn.lock index d3ccc9b18cb..60395e780d7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6315,10 +6315,10 @@ jsbn@1.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040" integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A== -jschardet@3.1.2: - version "3.1.2" - resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.2.tgz#9bf4364deba0677fe9e3bd9e29eda57febf2e9db" - integrity sha512-mw3CBZGzW8nUBPYhFU2ztZ/kJ6NClQUQVpyzvFMfznZsoC///ZQ30J2RCUanNsr5yF22LqhgYr/lj807/ZleWA== +jschardet@3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/jschardet/-/jschardet-3.1.3.tgz#10c2289fdae91a0aa9de8bba9c59055fd78898d3" + integrity sha512-Q1PKVMK/uu+yjdlobgWIYkUOCR1SqUmW9m/eUJNNj4zI2N12i25v8fYpVf+zCakQeaTdBdhnZTFbVIAVZIVVOg== jsdoc-type-pratt-parser@~4.0.0: version "4.0.0" @@ -9467,7 +9467,7 @@ streamx@^2.18.0: optionalDependencies: bare-events "^2.2.0" -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -9502,6 +9502,15 @@ string-width@^3.0.0, string-width@^3.1.0: is-fullwidth-code-point "^2.0.0" strip-ansi "^5.1.0" +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + string-width@^5.0.1, string-width@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-5.1.2.tgz#14f8daec6d81e7221d2a357e668cab73bdbca794" @@ -9555,7 +9564,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9583,6 +9592,13 @@ strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0: dependencies: ansi-regex "^4.1.0" +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + strip-ansi@^7.0.1: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" @@ -10747,7 +10763,7 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -10782,6 +10798,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From ea8d37ead215dae5f62789d4cd247b70ba735730 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 5 Jul 2024 15:38:27 +0200 Subject: [PATCH 0301/2222] Fall back to basic auth when kerberos errors out (#220034) --- src/vs/workbench/api/node/proxyResolver.ts | 43 +++++++++++----------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/vs/workbench/api/node/proxyResolver.ts b/src/vs/workbench/api/node/proxyResolver.ts index e39d10af2d3..3e64289d313 100644 --- a/src/vs/workbench/api/node/proxyResolver.ts +++ b/src/vs/workbench/api/node/proxyResolver.ts @@ -173,29 +173,28 @@ async function lookupProxyAuthorization( } catch (err) { extHostLogService.error('ProxyResolver#lookupProxyAuthorization Kerberos authentication failed', err); } - } else { - const header = authenticate.find(a => /^Basic( |$)/i.test(a)); - if (header) { - try { - state.basicAuthAttempt = (state.basicAuthAttempt || 0) + 1; - const realm = / realm="([^"]+)"/i.exec(header)?.[1]; - extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication lookup', `proxyURL:${proxyURL}`, `realm:${realm}`); - const url = new URL(proxyURL); - const authInfo: AuthInfo = { - scheme: 'basic', - host: url.hostname, - port: Number(url.port), - realm: realm || '', - isProxy: true, - attempt: state.basicAuthAttempt, - }; - const credentials = await extHostWorkspace.lookupAuthorization(authInfo); - if (credentials) { - return 'Basic ' + Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64'); - } - } catch (err) { - extHostLogService.error('ProxyResolver#lookupProxyAuthorization Kerberos authentication failed', err); + } + const basicAuthHeader = authenticate.find(a => /^Basic( |$)/i.test(a)); + if (basicAuthHeader) { + try { + state.basicAuthAttempt = (state.basicAuthAttempt || 0) + 1; + const realm = / realm="([^"]+)"/i.exec(basicAuthHeader)?.[1]; + extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication lookup', `proxyURL:${proxyURL}`, `realm:${realm}`); + const url = new URL(proxyURL); + const authInfo: AuthInfo = { + scheme: 'basic', + host: url.hostname, + port: Number(url.port), + realm: realm || '', + isProxy: true, + attempt: state.basicAuthAttempt, + }; + const credentials = await extHostWorkspace.lookupAuthorization(authInfo); + if (credentials) { + return 'Basic ' + Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64'); } + } catch (err) { + extHostLogService.error('ProxyResolver#lookupProxyAuthorization Kerberos authentication failed', err); } } return undefined; From fd18af08abc3a118ec2d179c4bbcc5d6ed457e0e Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Fri, 5 Jul 2024 16:34:05 +0200 Subject: [PATCH 0302/2222] Cache basic auth (#220034) --- src/vs/workbench/api/node/proxyResolver.ts | 25 ++++++++++++++++++---- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/src/vs/workbench/api/node/proxyResolver.ts b/src/vs/workbench/api/node/proxyResolver.ts index 3e64289d313..0df3f98c980 100644 --- a/src/vs/workbench/api/node/proxyResolver.ts +++ b/src/vs/workbench/api/node/proxyResolver.ts @@ -33,7 +33,7 @@ export function connectProxyResolver( const doUseHostProxy = typeof useHostProxy === 'boolean' ? useHostProxy : !initData.remote.isRemote; const params: ProxyAgentParams = { resolveProxy: url => extHostWorkspace.resolveProxy(url), - lookupProxyAuthorization: lookupProxyAuthorization.bind(undefined, extHostWorkspace, extHostLogService, mainThreadTelemetry, configProvider, {}, initData.remote.isRemote), + lookupProxyAuthorization: lookupProxyAuthorization.bind(undefined, extHostWorkspace, extHostLogService, mainThreadTelemetry, configProvider, {}, {}, initData.remote.isRemote), getProxyURL: () => configProvider.getConfiguration('http').get('proxy'), getProxySupport: () => configProvider.getConfiguration('http').get('proxySupport') || 'off', getNoProxyConfig: () => configProvider.getConfiguration('http').get('noProxy') || [], @@ -146,10 +146,11 @@ async function lookupProxyAuthorization( mainThreadTelemetry: MainThreadTelemetryShape, configProvider: ExtHostConfigProvider, proxyAuthenticateCache: Record, + basicAuthCache: Record, isRemote: boolean, proxyURL: string, proxyAuthenticate: string | string[] | undefined, - state: { kerberosRequested?: boolean; basicAuthAttempt?: number } + state: { kerberosRequested?: boolean; basicAuthCacheUsed?: boolean; basicAuthAttempt?: number } ): Promise { const cached = proxyAuthenticateCache[proxyURL]; if (proxyAuthenticate) { @@ -177,6 +178,17 @@ async function lookupProxyAuthorization( const basicAuthHeader = authenticate.find(a => /^Basic( |$)/i.test(a)); if (basicAuthHeader) { try { + const cachedAuth = basicAuthCache[proxyURL]; + if (cachedAuth) { + if (state.basicAuthCacheUsed) { + extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication deleting cached credentials', `proxyURL:${proxyURL}`); + delete basicAuthCache[proxyURL]; + } else { + extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication using cached credentials', `proxyURL:${proxyURL}`); + state.basicAuthCacheUsed = true; + return cachedAuth; + } + } state.basicAuthAttempt = (state.basicAuthAttempt || 0) + 1; const realm = / realm="([^"]+)"/i.exec(basicAuthHeader)?.[1]; extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication lookup', `proxyURL:${proxyURL}`, `realm:${realm}`); @@ -191,10 +203,15 @@ async function lookupProxyAuthorization( }; const credentials = await extHostWorkspace.lookupAuthorization(authInfo); if (credentials) { - return 'Basic ' + Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64'); + extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication received credentials', `proxyURL:${proxyURL}`, `realm:${realm}`); + const auth = 'Basic ' + Buffer.from(`${credentials.username}:${credentials.password}`).toString('base64'); + basicAuthCache[proxyURL] = auth; + return auth; + } else { + extHostLogService.debug('ProxyResolver#lookupProxyAuthorization Basic authentication received no credentials', `proxyURL:${proxyURL}`, `realm:${realm}`); } } catch (err) { - extHostLogService.error('ProxyResolver#lookupProxyAuthorization Kerberos authentication failed', err); + extHostLogService.error('ProxyResolver#lookupProxyAuthorization Basic authentication failed', err); } } return undefined; From e09b537187c2e5f736902ac9f4c101d550416adc Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Fri, 5 Jul 2024 17:54:52 +0200 Subject: [PATCH 0303/2222] List - context menu should work with SVGElement (#220102) --- src/vs/base/browser/dom.ts | 5 +++++ src/vs/base/browser/ui/list/listView.ts | 6 +++--- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 66d30c3aca3..a6acbe7f3a5 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -1184,6 +1184,11 @@ export function isHTMLDivElement(e: unknown): e is HTMLDivElement { return e instanceof HTMLDivElement || e instanceof getWindow(e as Node).HTMLDivElement; } +export function isSVGElement(e: unknown): e is SVGElement { + // eslint-disable-next-line no-restricted-syntax + return e instanceof SVGElement || e instanceof getWindow(e as Node).SVGElement; +} + export function isMouseEvent(e: unknown): e is MouseEvent { // eslint-disable-next-line no-restricted-syntax return e instanceof MouseEvent || e instanceof getWindow(e as UIEvent).MouseEvent; diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 276cba0d2c6..1dcd981388f 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DataTransfers, IDragAndDropData } from 'vs/base/browser/dnd'; -import { $, addDisposableListener, animate, Dimension, getContentHeight, getContentWidth, getTopLeftOffset, getWindow, isAncestor, isHTMLElement, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; +import { $, addDisposableListener, animate, Dimension, getContentHeight, getContentWidth, getTopLeftOffset, getWindow, isAncestor, isHTMLElement, isSVGElement, scheduleAtNextAnimationFrame } from 'vs/base/browser/dom'; import { DomEmitter } from 'vs/base/browser/event'; import { IMouseWheelEvent } from 'vs/base/browser/mouseEvent'; import { EventType as TouchEventType, Gesture, GestureEvent } from 'vs/base/browser/touch'; @@ -1382,9 +1382,9 @@ export class ListView implements IListView { private getItemIndexFromEventTarget(target: EventTarget | null): number | undefined { const scrollableElement = this.scrollableElement.getDomNode(); - let element: HTMLElement | null = target as (HTMLElement | null); + let element: HTMLElement | SVGElement | null = target as (HTMLElement | SVGElement | null); - while (isHTMLElement(element) && element !== this.rowsContainer && scrollableElement.contains(element)) { + while ((isHTMLElement(element) || isSVGElement(element)) && element !== this.rowsContainer && scrollableElement.contains(element)) { const rawIndex = element.getAttribute('data-index'); if (rawIndex) { From 7c8097dbbfcb9c102a0d3cf014f4ac9b7cfaa0da Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 5 Jul 2024 20:06:43 +0200 Subject: [PATCH 0304/2222] build - reuse the same `date` across builds (#220076) --- build/gulpfile.cli.js | 3 --- build/gulpfile.compile.js | 6 ++++++ build/gulpfile.reh.js | 4 ++-- build/gulpfile.vscode.js | 4 ++-- build/gulpfile.vscode.web.js | 4 ++-- build/lib/date.js | 38 ++++++++++++++++++++++-------------- build/lib/date.ts | 38 ++++++++++++++++++++++-------------- 7 files changed, 58 insertions(+), 39 deletions(-) diff --git a/build/gulpfile.cli.js b/build/gulpfile.cli.js index 86646fdb274..592fc74516c 100644 --- a/build/gulpfile.cli.js +++ b/build/gulpfile.cli.js @@ -5,8 +5,6 @@ 'use strict'; -//@ts-check - const es = require('event-stream'); const gulp = require('gulp'); const path = require('path'); @@ -24,7 +22,6 @@ const createReporter = require('./lib/reporter').createReporter; const root = 'cli'; const rootAbs = path.resolve(__dirname, '..', root); const src = `${root}/src`; -const targetCliPath = path.join(root, 'target', 'debug', process.platform === 'win32' ? 'code.exe' : 'code'); const platformOpensslDirName = process.platform === 'win32' ? ( diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js index c4947e76cbf..de8f3c4fb57 100644 --- a/build/gulpfile.compile.js +++ b/build/gulpfile.compile.js @@ -3,18 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +//@ts-check 'use strict'; const gulp = require('gulp'); const util = require('./lib/util'); +const date = require('./lib/date'); const task = require('./lib/task'); const compilation = require('./lib/compilation'); const optimize = require('./lib/optimize'); +/** + * @param {boolean} disableMangle + */ function makeCompileBuildTask(disableMangle) { return task.series( util.rimraf('out-build'), util.buildWebNodePaths('out-build'), + date.writeISODate('out-build'), compilation.compileApiProposalNamesTask, compilation.compileTask('src', 'out-build', true, { disableMangle }), optimize.optimizeLoaderTask('out-build', 'out-build', true) diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index 07bf9ff825f..d7a814b9a1b 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -18,7 +18,7 @@ const rename = require('gulp-rename'); const replace = require('gulp-replace'); const filter = require('gulp-filter'); const { getProductionDependencies } = require('./lib/dependencies'); -const { date } = require('./lib/date'); +const { readISODate } = require('./lib/date'); const vfs = require('vinyl-fs'); const packageJson = require('../package.json'); const flatmap = require('gulp-flatmap'); @@ -301,7 +301,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa let productJsonContents; const productJsonStream = gulp.src(['product.json'], { base: '.' }) - .pipe(json({ commit, date, version })) + .pipe(json({ commit, date: readISODate('out-build'), version })) .pipe(es.through(function (file) { productJsonContents = file.contents.toString(); this.emit('data', file); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 8923954e12e..b3b35466af0 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -15,7 +15,7 @@ const replace = require('gulp-replace'); const filter = require('gulp-filter'); const util = require('./lib/util'); const { getVersion } = require('./lib/getVersion'); -const { date } = require('./lib/date'); +const { readISODate } = require('./lib/date'); const task = require('./lib/task'); const buildfile = require('../src/buildfile'); const optimize = require('./lib/optimize'); @@ -259,7 +259,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op let productJsonContents; const productJsonStream = gulp.src(['product.json'], { base: '.' }) - .pipe(json({ commit, date, checksums, version })) + .pipe(json({ commit, date: readISODate('out-build'), checksums, version })) .pipe(es.through(function (file) { productJsonContents = file.contents.toString(); this.emit('data', file); diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index ad01b6cfb42..0424b12fa45 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -12,7 +12,7 @@ const util = require('./lib/util'); const { getVersion } = require('./lib/getVersion'); const task = require('./lib/task'); const optimize = require('./lib/optimize'); -const { date } = require('./lib/date'); +const { readISODate } = require('./lib/date'); const product = require('../product.json'); const rename = require('gulp-rename'); const filter = require('gulp-filter'); @@ -94,7 +94,7 @@ const createVSCodeWebProductConfigurationPatcher = (product) => { ...product, version, commit, - date + date: readISODate('out-build') }); return content.replace('/*BUILD->INSERT_PRODUCT_CONFIGURATION*/', () => productConfiguration.substr(1, productConfiguration.length - 2) /* without { and }*/); } diff --git a/build/lib/date.js b/build/lib/date.js index a3e2554c016..77fff0e5073 100644 --- a/build/lib/date.js +++ b/build/lib/date.js @@ -4,21 +4,29 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ Object.defineProperty(exports, "__esModule", { value: true }); -exports.date = void 0; -function getRoundedBuildDate() { - const now = new Date(); - const minutes = now.getMinutes(); - if (minutes >= 30) { - now.setHours(now.getHours() + 1); - } - now.setMinutes(0, 0, 0); - return now; -} +exports.writeISODate = writeISODate; +exports.readISODate = readISODate; +const path = require("path"); +const fs = require("fs"); +const root = path.join(__dirname, '..', '..'); /** - * An attempt to produce a stable date for the build that can be - * used across processes and build steps that run in parallel almost - * at the same time. The current time is rounded up or down to the - * closest hour. + * Writes a `outDir/date` file with the contents of the build + * so that other tasks during the build process can use it and + * all use the same date. */ -exports.date = getRoundedBuildDate().toISOString(); +function writeISODate(outDir) { + const result = () => new Promise((resolve, _) => { + const outDirectory = path.join(root, outDir); + fs.mkdirSync(outDirectory, { recursive: true }); + const date = new Date().toISOString(); + fs.writeFileSync(path.join(outDirectory, 'date'), date, 'utf8'); + resolve(); + }); + result.taskName = 'build-date-file'; + return result; +} +function readISODate(outDir) { + const outDirectory = path.join(root, outDir); + return fs.readFileSync(path.join(outDirectory, 'date'), 'utf8'); +} //# sourceMappingURL=date.js.map \ No newline at end of file diff --git a/build/lib/date.ts b/build/lib/date.ts index a68a4caf3df..998e89f8e6a 100644 --- a/build/lib/date.ts +++ b/build/lib/date.ts @@ -3,23 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -function getRoundedBuildDate() { - const now = new Date(); +import * as path from 'path'; +import * as fs from 'fs'; - const minutes = now.getMinutes(); - if (minutes >= 30) { - now.setHours(now.getHours() + 1); - } +const root = path.join(__dirname, '..', '..'); - now.setMinutes(0, 0, 0); +/** + * Writes a `outDir/date` file with the contents of the build + * so that other tasks during the build process can use it and + * all use the same date. + */ +export function writeISODate(outDir: string) { + const result = () => new Promise((resolve, _) => { + const outDirectory = path.join(root, outDir); + fs.mkdirSync(outDirectory, { recursive: true }); + + const date = new Date().toISOString(); + fs.writeFileSync(path.join(outDirectory, 'date'), date, 'utf8'); - return now; + resolve(); + }); + result.taskName = 'build-date-file'; + return result; } -/** - * An attempt to produce a stable date for the build that can be - * used across processes and build steps that run in parallel almost - * at the same time. The current time is rounded up or down to the - * closest hour. - */ -export const date = getRoundedBuildDate().toISOString(); +export function readISODate(outDir: string): string { + const outDirectory = path.join(root, outDir); + return fs.readFileSync(path.join(outDirectory, 'date'), 'utf8'); +} From 80bdfeb895582481d62fbfedb90e7ab3738b7437 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Fri, 5 Jul 2024 20:29:48 +0200 Subject: [PATCH 0305/2222] chore - push workaround and a lint rule for https://github.com/evanw/esbuild/issues/3823 (#220077) * chore - push workaround and a lint rule for https://github.com/evanw/esbuild/issues/3823 * modernize `tsconfig.monaco` --- .eslintplugin/code-no-static-self-ref.ts | 56 +++++++++++++++++++ .eslintrc.json | 12 +++- src/tsconfig.monaco.json | 2 +- .../features/overviewRulerFeature.ts | 2 +- .../browser/inlineEditController.ts | 4 +- .../browser/gotoSymbolQuickAccess.ts | 2 +- .../api/common/extHostDiagnostics.ts | 2 +- .../browser/parts/editor/editorPlaceholder.ts | 4 +- .../notifications/notificationsToasts.ts | 2 +- .../bulkEdit/browser/preview/bulkEditPane.ts | 2 +- .../browser/preview/bulkEditPreview.ts | 2 +- .../contrib/chat/common/voiceChatService.ts | 8 +-- .../extensions/browser/extensionsActions.ts | 32 +++++------ .../preferences/browser/settingsEditor2.ts | 2 +- .../preferences/browser/settingsTree.ts | 4 +- .../common/untitledTextEditorModel.ts | 2 +- 16 files changed, 102 insertions(+), 36 deletions(-) create mode 100644 .eslintplugin/code-no-static-self-ref.ts diff --git a/.eslintplugin/code-no-static-self-ref.ts b/.eslintplugin/code-no-static-self-ref.ts new file mode 100644 index 00000000000..7c6e13032ae --- /dev/null +++ b/.eslintplugin/code-no-static-self-ref.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as eslint from 'eslint'; +import { TSESTree } from '@typescript-eslint/experimental-utils'; + +/** + * WORKAROUND for https://github.com/evanw/esbuild/issues/3823 + */ +export = new class implements eslint.Rule.RuleModule { + + create(context: eslint.Rule.RuleContext): eslint.Rule.RuleListener { + + function checkProperty(inNode: any) { + + const classDeclaration = context.getAncestors().find(node => node.type === 'ClassDeclaration'); + const propertyDefinition = inNode; + + if (!classDeclaration || !classDeclaration.id?.name) { + return; + } + + if (!propertyDefinition.value) { + return; + } + + const classCtor = classDeclaration.body.body.find(node => node.type === 'MethodDefinition' && node.kind === 'constructor') + + if (!classCtor) { + return; + } + + const name = classDeclaration.id.name; + const valueText = context.getSourceCode().getText(propertyDefinition.value) + + if (valueText.includes(name + '.')) { + + if (classCtor.value?.type === 'FunctionExpression' && !classCtor.value.params.find((param: any) => param.type === 'TSParameterProperty' && param.decorators?.length > 0)) { + return + } + + context.report({ + loc: propertyDefinition.value.loc, + message: `Static properties in decorated classes should not reference the class they are defined in. Use 'this' instead. This is a workaround for https://github.com/evanw/esbuild/issues/3823.` + }); + } + + } + + return { + 'PropertyDefinition[static=true]': checkProperty, + }; + } +}; diff --git a/.eslintrc.json b/.eslintrc.json index db383e0b9b4..c39a66311e4 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -141,6 +141,14 @@ ] } }, + { + "files": [ + "src/**/*.ts" + ], + "rules": { + "local/code-no-static-self-ref": "warn" + } + }, { "files": [ "src/vs/**/*.test.ts" @@ -1090,7 +1098,9 @@ "local/code-no-runtime-import": [ "error", { - "src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts": ["**/*"] + "src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts": [ + "**/*" + ] } ] } diff --git a/src/tsconfig.monaco.json b/src/tsconfig.monaco.json index 8f955fa3c9f..bad9fb8cacc 100644 --- a/src/tsconfig.monaco.json +++ b/src/tsconfig.monaco.json @@ -11,7 +11,7 @@ "moduleResolution": "classic", "removeComments": false, "preserveConstEnums": true, - "target": "es2018", + "target": "ES2022", "sourceMap": false, "declaration": true }, diff --git a/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts index 8141cd9452c..017d8268f6e 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/overviewRulerFeature.ts @@ -23,7 +23,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; export class OverviewRulerFeature extends Disposable { private static readonly ONE_OVERVIEW_WIDTH = 15; - public static readonly ENTIRE_DIFF_OVERVIEW_WIDTH = OverviewRulerFeature.ONE_OVERVIEW_WIDTH * 2; + public static readonly ENTIRE_DIFF_OVERVIEW_WIDTH = this.ONE_OVERVIEW_WIDTH * 2; public readonly width = OverviewRulerFeature.ENTIRE_DIFF_OVERVIEW_WIDTH; constructor( diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts b/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts index 9ce9a02b2e6..74053d4a617 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts @@ -35,11 +35,11 @@ export class InlineEditController extends Disposable { static ID = 'editor.contrib.inlineEditController'; public static readonly inlineEditVisibleKey = 'inlineEditVisible'; - public static readonly inlineEditVisibleContext = new RawContextKey(InlineEditController.inlineEditVisibleKey, false); + public static readonly inlineEditVisibleContext = new RawContextKey(this.inlineEditVisibleKey, false); private _isVisibleContext = InlineEditController.inlineEditVisibleContext.bindTo(this.contextKeyService); public static readonly cursorAtInlineEditKey = 'cursorAtInlineEdit'; - public static readonly cursorAtInlineEditContext = new RawContextKey(InlineEditController.cursorAtInlineEditKey, false); + public static readonly cursorAtInlineEditContext = new RawContextKey(this.cursorAtInlineEditKey, false); private _isCursorAtInlineEditContext = InlineEditController.cursorAtInlineEditContext.bindTo(this.contextKeyService); public static get(editor: ICodeEditor): InlineEditController | null { diff --git a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts index 46aeeda1706..d34aeb3faf5 100644 --- a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts @@ -48,7 +48,7 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit static PREFIX = '@'; static SCOPE_PREFIX = ':'; - static PREFIX_BY_CATEGORY = `${AbstractGotoSymbolQuickAccessProvider.PREFIX}${AbstractGotoSymbolQuickAccessProvider.SCOPE_PREFIX}`; + static PREFIX_BY_CATEGORY = `${this.PREFIX}${this.SCOPE_PREFIX}`; protected override readonly options: IGotoSymbolQuickAccessProviderOptions; diff --git a/src/vs/workbench/api/common/extHostDiagnostics.ts b/src/vs/workbench/api/common/extHostDiagnostics.ts index e23ce395cc8..da9a087fbb1 100644 --- a/src/vs/workbench/api/common/extHostDiagnostics.ts +++ b/src/vs/workbench/api/common/extHostDiagnostics.ts @@ -234,7 +234,7 @@ export class ExtHostDiagnostics implements ExtHostDiagnosticsShape { private static _idPool: number = 0; private static readonly _maxDiagnosticsPerFile: number = 1000; - private static readonly _maxDiagnosticsTotal: number = 1.1 * ExtHostDiagnostics._maxDiagnosticsPerFile; + private static readonly _maxDiagnosticsTotal: number = 1.1 * this._maxDiagnosticsPerFile; private readonly _proxy: MainThreadDiagnosticsShape; private readonly _collections = new Map(); diff --git a/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts b/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts index def52c5b61e..e434679f445 100644 --- a/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts +++ b/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts @@ -185,7 +185,7 @@ export class WorkspaceTrustRequiredPlaceholderEditor extends EditorPlaceholder { static readonly ID = 'workbench.editors.workspaceTrustRequiredEditor'; private static readonly LABEL = localize('trustRequiredEditor', "Workspace Trust Required"); - static readonly DESCRIPTOR = EditorPaneDescriptor.create(WorkspaceTrustRequiredPlaceholderEditor, WorkspaceTrustRequiredPlaceholderEditor.ID, WorkspaceTrustRequiredPlaceholderEditor.LABEL); + static readonly DESCRIPTOR = EditorPaneDescriptor.create(WorkspaceTrustRequiredPlaceholderEditor, this.ID, this.LABEL); constructor( group: IEditorGroup, @@ -223,7 +223,7 @@ export class ErrorPlaceholderEditor extends EditorPlaceholder { private static readonly ID = 'workbench.editors.errorEditor'; private static readonly LABEL = localize('errorEditor', "Error Editor"); - static readonly DESCRIPTOR = EditorPaneDescriptor.create(ErrorPlaceholderEditor, ErrorPlaceholderEditor.ID, ErrorPlaceholderEditor.LABEL); + static readonly DESCRIPTOR = EditorPaneDescriptor.create(ErrorPlaceholderEditor, this.ID, this.LABEL); constructor( group: IEditorGroup, diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index c42e49f2629..943136732c1 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -55,7 +55,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast // Count for the number of notifications over 800ms... interval: 800, // ...and ensure we are not showing more than MAX_NOTIFICATIONS - limit: NotificationsToasts.MAX_NOTIFICATIONS + limit: this.MAX_NOTIFICATIONS }; private readonly _onDidChangeVisibility = this._register(new Emitter()); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 55775a63e36..a24720d5b75 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -56,7 +56,7 @@ export class BulkEditPane extends ViewPane { static readonly ctxGroupByFile = new RawContextKey('refactorPreview.groupByFile', true); static readonly ctxHasCheckedChanges = new RawContextKey('refactorPreview.hasCheckedChanges', true); - private static readonly _memGroupByFile = `${BulkEditPane.ID}.groupByFile`; + private static readonly _memGroupByFile = `${this.ID}.groupByFile`; private _tree!: WorkbenchAsyncDataTree; private _treeDataSource!: BulkEditDataSource; diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts index 40fbb606f8b..44c687a6dee 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPreview.ts @@ -353,7 +353,7 @@ export class BulkEditPreviewProvider implements ITextModelContentProvider { private static readonly Schema = 'vscode-bulkeditpreview-editor'; - static emptyPreview = URI.from({ scheme: BulkEditPreviewProvider.Schema, fragment: 'empty' }); + static emptyPreview = URI.from({ scheme: this.Schema, fragment: 'empty' }); static fromPreviewUri(uri: URI): URI { diff --git a/src/vs/workbench/contrib/chat/common/voiceChatService.ts b/src/vs/workbench/contrib/chat/common/voiceChatService.ts index c162f5859d7..ae70f2f56a1 100644 --- a/src/vs/workbench/contrib/chat/common/voiceChatService.ts +++ b/src/vs/workbench/contrib/chat/common/voiceChatService.ts @@ -70,13 +70,13 @@ export class VoiceChatService extends Disposable implements IVoiceChatService { private static readonly COMMAND_PREFIX = chatSubcommandLeader; private static readonly PHRASES_LOWER = { - [VoiceChatService.AGENT_PREFIX]: 'at', - [VoiceChatService.COMMAND_PREFIX]: 'slash' + [this.AGENT_PREFIX]: 'at', + [this.COMMAND_PREFIX]: 'slash' }; private static readonly PHRASES_UPPER = { - [VoiceChatService.AGENT_PREFIX]: 'At', - [VoiceChatService.COMMAND_PREFIX]: 'Slash' + [this.AGENT_PREFIX]: 'At', + [this.COMMAND_PREFIX]: 'Slash' }; private static readonly CHAT_AGENT_ALIAS = new Map([['vscode', 'code']]); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 03c914611e9..3ab22820d49 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -366,8 +366,8 @@ export class ButtonWithDropdownExtensionActionViewItem extends ActionWithDropdow export class InstallAction extends ExtensionAction { - static readonly CLASS = `${ExtensionAction.LABEL_ACTION_CLASS} prominent install`; - private static readonly HIDE = `${InstallAction.CLASS} hide`; + static readonly CLASS = `${this.LABEL_ACTION_CLASS} prominent install`; + private static readonly HIDE = `${this.CLASS} hide`; protected _manifest: IExtensionManifest | null = null; set manifest(manifest: IExtensionManifest | null) { @@ -862,8 +862,8 @@ export class UninstallAction extends ExtensionAction { abstract class AbstractUpdateAction extends ExtensionAction { - private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} prominent update`; - private static readonly DisabledClass = `${AbstractUpdateAction.EnabledClass} disabled`; + private static readonly EnabledClass = `${this.LABEL_ACTION_CLASS} prominent update`; + private static readonly DisabledClass = `${this.EnabledClass} disabled`; private readonly updateThrottler = new Throttler(); @@ -940,7 +940,7 @@ export class ToggleAutoUpdateForExtensionAction extends ExtensionAction { static readonly LABEL = localize2('enableAutoUpdateLabel', "Auto Update"); private static readonly EnabledClass = `${ExtensionAction.EXTENSION_ACTION_CLASS} auto-update`; - private static readonly DisabledClass = `${ToggleAutoUpdateForExtensionAction.EnabledClass} hide`; + private static readonly DisabledClass = `${this.EnabledClass} hide`; constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -1023,7 +1023,7 @@ export class ToggleAutoUpdatesForPublisherAction extends ExtensionAction { export class MigrateDeprecatedExtensionAction extends ExtensionAction { private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} migrate`; - private static readonly DisabledClass = `${MigrateDeprecatedExtensionAction.EnabledClass} disabled`; + private static readonly DisabledClass = `${this.EnabledClass} disabled`; constructor( private readonly small: boolean, @@ -1213,7 +1213,7 @@ export class ManageExtensionAction extends DropDownExtensionAction { static readonly ID = 'extensions.manage'; private static readonly Class = `${ExtensionAction.ICON_ACTION_CLASS} manage ` + ThemeIcon.asClassName(manageExtensionIcon); - private static readonly HideManageExtensionClass = `${ManageExtensionAction.Class} hide`; + private static readonly HideManageExtensionClass = `${this.Class} hide`; constructor( @IInstantiationService instantiationService: IInstantiationService, @@ -1370,7 +1370,7 @@ export class TogglePreReleaseExtensionAction extends ExtensionAction { static readonly LABEL = localize('togglePreRleaseLabel', "Pre-Release"); private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} pre-release`; - private static readonly DisabledClass = `${TogglePreReleaseExtensionAction.EnabledClass} hide`; + private static readonly DisabledClass = `${this.EnabledClass} hide`; constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -1653,7 +1653,7 @@ export class DisableDropDownAction extends ButtonWithDropDownExtensionAction { export class ExtensionRuntimeStateAction extends ExtensionAction { private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} reload`; - private static readonly DisabledClass = `${ExtensionRuntimeStateAction.EnabledClass} disabled`; + private static readonly DisabledClass = `${this.EnabledClass} disabled`; updateWhenCounterExtensionChanges: boolean = true; @@ -1767,7 +1767,7 @@ export class SetColorThemeAction extends ExtensionAction { static readonly TITLE = localize2('workbench.extensions.action.setColorTheme', 'Set Color Theme'); private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`; - private static readonly DisabledClass = `${SetColorThemeAction.EnabledClass} disabled`; + private static readonly DisabledClass = `${this.EnabledClass} disabled`; constructor( @IExtensionService extensionService: IExtensionService, @@ -1818,7 +1818,7 @@ export class SetFileIconThemeAction extends ExtensionAction { static readonly TITLE = localize2('workbench.extensions.action.setFileIconTheme', 'Set File Icon Theme'); private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`; - private static readonly DisabledClass = `${SetFileIconThemeAction.EnabledClass} disabled`; + private static readonly DisabledClass = `${this.EnabledClass} disabled`; constructor( @IExtensionService extensionService: IExtensionService, @@ -1868,7 +1868,7 @@ export class SetProductIconThemeAction extends ExtensionAction { static readonly TITLE = localize2('workbench.extensions.action.setProductIconTheme', 'Set Product Icon Theme'); private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} theme`; - private static readonly DisabledClass = `${SetProductIconThemeAction.EnabledClass} disabled`; + private static readonly DisabledClass = `${this.EnabledClass} disabled`; constructor( @IExtensionService extensionService: IExtensionService, @@ -1919,7 +1919,7 @@ export class SetLanguageAction extends ExtensionAction { static readonly TITLE = localize2('workbench.extensions.action.setDisplayLanguage', 'Set Display Language'); private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} language`; - private static readonly DisabledClass = `${SetLanguageAction.EnabledClass} disabled`; + private static readonly DisabledClass = `${this.EnabledClass} disabled`; constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -1955,7 +1955,7 @@ export class ClearLanguageAction extends ExtensionAction { static readonly TITLE = localize2('workbench.extensions.action.clearLanguage', 'Clear Display Language'); private static readonly EnabledClass = `${ExtensionAction.LABEL_ACTION_CLASS} language`; - private static readonly DisabledClass = `${ClearLanguageAction.EnabledClass} disabled`; + private static readonly DisabledClass = `${this.EnabledClass} disabled`; constructor( @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @@ -2267,7 +2267,7 @@ export class ConfigureWorkspaceFolderRecommendedExtensionsAction extends Abstrac export class ExtensionStatusLabelAction extends Action implements IExtensionContainer { private static readonly ENABLED_CLASS = `${ExtensionAction.TEXT_ACTION_CLASS} extension-status-label`; - private static readonly DISABLED_CLASS = `${ExtensionStatusLabelAction.ENABLED_CLASS} hide`; + private static readonly DISABLED_CLASS = `${this.ENABLED_CLASS} hide`; private initialStatus: ExtensionState | null = null; private status: ExtensionState | null = null; @@ -2370,7 +2370,7 @@ export class ExtensionStatusLabelAction extends Action implements IExtensionCont export class ToggleSyncExtensionAction extends DropDownExtensionAction { private static readonly IGNORED_SYNC_CLASS = `${ExtensionAction.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncIgnoredIcon)}`; - private static readonly SYNC_CLASS = `${ToggleSyncExtensionAction.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncEnabledIcon)}`; + private static readonly SYNC_CLASS = `${this.ICON_ACTION_CLASS} extension-sync ${ThemeIcon.asClassName(syncEnabledIcon)}`; constructor( @IConfigurationService private readonly configurationService: IConfigurationService, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index f9b11cf76a8..779b9928b57 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -109,7 +109,7 @@ export class SettingsEditor2 extends EditorPane { private static TOC_RESET_WIDTH: number = 200; private static EDITOR_MIN_WIDTH: number = 500; // Below NARROW_TOTAL_WIDTH, we only render the editor rather than the ToC. - private static NARROW_TOTAL_WIDTH: number = SettingsEditor2.TOC_RESET_WIDTH + SettingsEditor2.EDITOR_MIN_WIDTH; + private static NARROW_TOTAL_WIDTH: number = this.TOC_RESET_WIDTH + this.EDITOR_MIN_WIDTH; private static SUGGESTIONS: string[] = [ `@${MODIFIED_SETTING_TAG}`, diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 62a948e5a08..5d354b7028d 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -750,9 +750,9 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre abstract get templateId(): string; static readonly CONTROL_CLASS = 'setting-control-focus-target'; - static readonly CONTROL_SELECTOR = '.' + AbstractSettingRenderer.CONTROL_CLASS; + static readonly CONTROL_SELECTOR = '.' + this.CONTROL_CLASS; static readonly CONTENTS_CLASS = 'setting-item-contents'; - static readonly CONTENTS_SELECTOR = '.' + AbstractSettingRenderer.CONTENTS_CLASS; + static readonly CONTENTS_SELECTOR = '.' + this.CONTENTS_CLASS; static readonly ALL_ROWS_SELECTOR = '.monaco-list-row'; static readonly SETTING_KEY_ATTR = 'data-key'; diff --git a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts index 43402bc496c..46563d9317d 100644 --- a/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts +++ b/src/vs/workbench/services/untitled/common/untitledTextEditorModel.ts @@ -70,7 +70,7 @@ export interface IUntitledTextEditorModel extends ITextEditorModel, ILanguageSup export class UntitledTextEditorModel extends BaseTextEditorModel implements IUntitledTextEditorModel { private static readonly FIRST_LINE_NAME_MAX_LENGTH = 40; - private static readonly FIRST_LINE_NAME_CANDIDATE_MAX_LENGTH = UntitledTextEditorModel.FIRST_LINE_NAME_MAX_LENGTH * 10; + private static readonly FIRST_LINE_NAME_CANDIDATE_MAX_LENGTH = this.FIRST_LINE_NAME_MAX_LENGTH * 10; // Support the special '${activeEditorLanguage}' language by // looking up the language id from the editor that is active From fcf6e8027c5727a3087f001f3965d74f0c7b9d2f Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 5 Jul 2024 20:47:37 +0200 Subject: [PATCH 0306/2222] Blank Window on Dev Container (fix #220007) (#220112) --- src/vs/platform/files/common/files.ts | 2 +- .../contrib/files/browser/views/explorerView.ts | 2 +- .../common/filesConfigurationService.ts | 12 ++++++------ .../services/language/common/languageService.ts | 2 +- src/vs/workbench/services/search/common/search.ts | 2 +- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/vs/platform/files/common/files.ts b/src/vs/platform/files/common/files.ts index 290a991a35d..82d4f9ba0f3 100644 --- a/src/vs/platform/files/common/files.ts +++ b/src/vs/platform/files/common/files.ts @@ -1460,7 +1460,7 @@ export interface IGlobPatterns { } export interface IFilesConfiguration { - files: IFilesConfigurationNode; + files?: IFilesConfigurationNode; } export interface IFilesConfigurationNode { diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index a94048551aa..940bfb82792 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -660,7 +660,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { this.setContextKeys(stat); if (stat) { - const enableTrash = this.configurationService.getValue().files.enableTrash; + const enableTrash = Boolean(this.configurationService.getValue().files?.enableTrash); const hasCapability = this.fileService.hasCapability(stat.resource, FileSystemProviderCapabilities.Trash); this.resourceMoveableToTrash.set(enableTrash && hasCapability); } else { diff --git a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts index 4500b974e72..6a3c782492a 100644 --- a/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts +++ b/src/vs/workbench/services/filesConfiguration/common/filesConfigurationService.ts @@ -143,7 +143,7 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi readonly onDidChangeReadonly = this._onDidChangeReadonly.event; private currentGlobalAutoSaveConfiguration: IAutoSaveConfiguration; - private currentFilesAssociationConfiguration: IStringDictionary; + private currentFilesAssociationConfiguration: IStringDictionary | undefined; private currentHotExitConfiguration: string; private readonly autoSaveConfigurationCache = new LRUCache(1000); @@ -317,7 +317,7 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi return this.currentGlobalAutoSaveConfiguration; } - private computeAutoSaveConfiguration(resource: URI | undefined, filesConfiguration: IFilesConfigurationNode): ICachedAutoSaveConfiguration { + private computeAutoSaveConfiguration(resource: URI | undefined, filesConfiguration: IFilesConfigurationNode | undefined): ICachedAutoSaveConfiguration { let autoSave: 'afterDelay' | 'onFocusChange' | 'onWindowChange' | undefined; let autoSaveDelay: number | undefined; let autoSaveWorkspaceFilesOnly: boolean | undefined; @@ -326,10 +326,10 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi let isOutOfWorkspace: boolean | undefined; let isShortAutoSaveDelay: boolean | undefined; - switch (filesConfiguration.autoSave ?? FilesConfigurationService.DEFAULT_AUTO_SAVE_MODE) { + switch (filesConfiguration?.autoSave ?? FilesConfigurationService.DEFAULT_AUTO_SAVE_MODE) { case AutoSaveConfiguration.AFTER_DELAY: { autoSave = 'afterDelay'; - autoSaveDelay = typeof filesConfiguration.autoSaveDelay === 'number' && filesConfiguration.autoSaveDelay >= 0 ? filesConfiguration.autoSaveDelay : FilesConfigurationService.DEFAULT_AUTO_SAVE_DELAY; + autoSaveDelay = typeof filesConfiguration?.autoSaveDelay === 'number' && filesConfiguration.autoSaveDelay >= 0 ? filesConfiguration.autoSaveDelay : FilesConfigurationService.DEFAULT_AUTO_SAVE_DELAY; isShortAutoSaveDelay = autoSaveDelay <= FilesConfigurationService.DEFAULT_AUTO_SAVE_DELAY; break; } @@ -343,7 +343,7 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi break; } - if (filesConfiguration.autoSaveWorkspaceFilesOnly === true) { + if (filesConfiguration?.autoSaveWorkspaceFilesOnly === true) { autoSaveWorkspaceFilesOnly = true; if (resource && !this.contextService.isInsideWorkspace(resource)) { @@ -352,7 +352,7 @@ export class FilesConfigurationService extends Disposable implements IFilesConfi } } - if (filesConfiguration.autoSaveWhenNoErrors === true) { + if (filesConfiguration?.autoSaveWhenNoErrors === true) { autoSaveWhenNoErrors = true; isShortAutoSaveDelay = undefined; // this configuration disables short auto save delay } diff --git a/src/vs/workbench/services/language/common/languageService.ts b/src/vs/workbench/services/language/common/languageService.ts index 1eb86cfd2d3..f79548354bf 100644 --- a/src/vs/workbench/services/language/common/languageService.ts +++ b/src/vs/workbench/services/language/common/languageService.ts @@ -300,7 +300,7 @@ export class WorkbenchLanguageService extends LanguageService { // Register based on settings if (configuration.files?.associations) { Object.keys(configuration.files.associations).forEach(pattern => { - const langId = configuration.files.associations[pattern]; + const langId = configuration.files!.associations[pattern]; if (typeof langId !== 'string') { this.logService.warn(`Ignoring configured 'files.associations' for '${pattern}' because its type is not a string but '${typeof langId}'`); diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index d03e606db74..563b0dda249 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -462,7 +462,7 @@ export function getExcludes(configuration: ISearchConfiguration, includeSearchEx } if (!fileExcludes || !searchExcludes) { - return fileExcludes || searchExcludes; + return fileExcludes || searchExcludes || undefined; } let allExcludes: glob.IExpression = Object.create(null); From f8b1bef21437608c6fee422ab044253b13224cda Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Fri, 5 Jul 2024 14:48:59 -0700 Subject: [PATCH 0307/2222] debt: narrow bool object viewmodel type (#220120) Ref #211878 --- .../preferences/browser/settingsTree.ts | 194 ++++++++++++------ .../preferences/browser/settingsWidgets.ts | 27 ++- .../preferences/common/preferences.ts | 2 +- 3 files changed, 148 insertions(+), 75 deletions(-) diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 5d354b7028d..2c9ea5519ca 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -61,7 +61,7 @@ import { SettingsTarget } from 'vs/workbench/contrib/preferences/browser/prefere import { ISettingOverrideClickEvent, SettingsTreeIndicatorsLabel, getIndicatorsLabelAriaLabel } from 'vs/workbench/contrib/preferences/browser/settingsEditorSettingIndicators'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; import { ISettingsEditorViewState, SettingsTreeElement, SettingsTreeGroupChild, SettingsTreeGroupElement, SettingsTreeNewExtensionsElement, SettingsTreeSettingElement, inspectSetting, objectSettingSupportsRemoveDefaultValue, settingKeyToDisplayFormat } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; -import { ExcludeSettingWidget, IIncludeExcludeDataItem, IListDataItem, IObjectDataItem, IObjectEnumOption, IObjectKeySuggester, IObjectValueSuggester, ISettingListChangeEvent, IncludeSettingWidget, ListSettingWidget, ObjectSettingCheckboxWidget, ObjectSettingDropdownWidget, ObjectValue, SettingListEvent } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; +import { ExcludeSettingWidget, IBoolObjectDataItem, IIncludeExcludeDataItem, IListDataItem, IObjectDataItem, IObjectEnumOption, IObjectKeySuggester, IObjectValueSuggester, IncludeSettingWidget, ListSettingWidget, ObjectSettingCheckboxWidget, ObjectSettingDropdownWidget, ObjectValue, SettingListEvent } from 'vs/workbench/contrib/preferences/browser/settingsWidgets'; import { LANGUAGE_SETTING_TAG, SETTINGS_EDITOR_COMMAND_SHOW_CONTEXT_MENU, compareTwoNullableNumbers } from 'vs/workbench/contrib/preferences/common/preferences'; import { settingsNumberInputBackground, settingsNumberInputBorder, settingsNumberInputForeground, settingsSelectBackground, settingsSelectBorder, settingsSelectForeground, settingsSelectListBorder, settingsTextInputBackground, settingsTextInputBorder, settingsTextInputForeground } from 'vs/workbench/contrib/preferences/common/settingsEditorColorRegistry'; import { APPLY_ALL_PROFILES_SETTING, IWorkbenchConfigurationService } from 'vs/workbench/services/configuration/common/configuration'; @@ -149,6 +149,16 @@ function getObjectValueType(schema: IJSONSchema): ObjectValue['type'] { } } +function getObjectEntryValueDisplayValue(type: ObjectValue['type'], data: unknown, options: IObjectEnumOption[]): ObjectValue { + if (type === 'boolean') { + return { type, data: !!data }; + } else if (type === 'enum') { + return { type, data: '' + data, options }; + } else { + return { type, data: '' + data }; + } +} + function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectDataItem[] { const elementDefaultValue: Record = typeof element.defaultValue === 'object' ? element.defaultValue ?? {} @@ -185,23 +195,6 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData } if (isDefined(objectProperties) && key in objectProperties) { - if (element.setting.allKeysAreBoolean) { - return { - key: { - type: 'string', - data: key - }, - value: { - type: 'boolean', - data: data[key] - }, - keyDescription: objectProperties[key].description, - removable: false, - resetable: true, - source - } as IObjectDataItem; - } - const valueEnumOptions = getEnumOptionsFromSchema(objectProperties[key]); return { key: { @@ -209,37 +202,29 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData data: key, options: wellDefinedKeyEnumOptions, }, - value: { - type: getObjectValueType(objectProperties[key]), - data: data[key], - options: valueEnumOptions, - }, + value: getObjectEntryValueDisplayValue(getObjectValueType(objectProperties[key]), data[key], valueEnumOptions), keyDescription: objectProperties[key].description, removable: isUndefinedOrNull(defaultValue), resetable: !isUndefinedOrNull(defaultValue), source - } as IObjectDataItem; + } satisfies IObjectDataItem; } // The row is removable if it doesn't have a default value assigned or the setting supports removing the default value. // If a default value is assigned and the user modified the default, it can be reset back to the default. const removable = defaultValue === undefined || objectSettingSupportsRemoveDefaultValue(element.setting.key); - const resetable = defaultValue && defaultValue !== data[key]; + const resetable = !!defaultValue && defaultValue !== data[key]; const schema = patternsAndSchemas.find(({ pattern }) => pattern.test(key))?.schema; if (schema) { const valueEnumOptions = getEnumOptionsFromSchema(schema); return { key: { type: 'string', data: key }, - value: { - type: getObjectValueType(schema), - data: data[key], - options: valueEnumOptions, - }, + value: getObjectEntryValueDisplayValue(getObjectValueType(schema), data[key], valueEnumOptions), keyDescription: schema.description, removable, resetable, source - } as IObjectDataItem; + } satisfies IObjectDataItem; } const additionalValueEnums = getEnumOptionsFromSchema( @@ -250,19 +235,62 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData return { key: { type: 'string', data: key }, - value: { - type: typeof objectAdditionalProperties === 'object' ? getObjectValueType(objectAdditionalProperties) : 'string', - data: data[key], - options: additionalValueEnums, - }, + value: getObjectEntryValueDisplayValue( + typeof objectAdditionalProperties === 'object' ? getObjectValueType(objectAdditionalProperties) : 'string', + data[key], + additionalValueEnums, + ), keyDescription: typeof objectAdditionalProperties === 'object' ? objectAdditionalProperties.description : undefined, removable, resetable, source - } as IObjectDataItem; + } satisfies IObjectDataItem; }).filter(item => !isUndefinedOrNull(item.value.data)); } +function getBoolObjectDisplayValue(element: SettingsTreeSettingElement): IBoolObjectDataItem[] { + const elementDefaultValue: Record = typeof element.defaultValue === 'object' + ? element.defaultValue ?? {} + : {}; + + const elementScopeValue: Record = typeof element.scopeValue === 'object' + ? element.scopeValue ?? {} + : {}; + + const data = element.isConfigured ? + { ...elementDefaultValue, ...elementScopeValue } : + elementDefaultValue; + + const { objectProperties } = element.setting; + const displayValues: IBoolObjectDataItem[] = []; + for (const key in objectProperties) { + const defaultValue = elementDefaultValue[key]; + + // Get source if it's a default value + let source: string | undefined; + if (defaultValue === data[key] && element.setting.type === 'object' && element.defaultValueSource instanceof Map) { + const defaultSource = element.defaultValueSource.get(key); + source = typeof defaultSource === 'string' ? defaultSource : defaultSource?.displayName; + } + + displayValues.push({ + key: { + type: 'string', + data: key + }, + value: { + type: 'boolean', + data: !!data[key] + }, + keyDescription: objectProperties[key].description, + removable: false, + resetable: true, + source + }); + } + return displayValues; +} + function createArraySuggester(element: SettingsTreeSettingElement): IObjectKeySuggester { return (keys, idx) => { const enumOptions: IObjectEnumOption[] = []; @@ -1306,16 +1334,29 @@ abstract class AbstractSettingObjectRenderer extends AbstractSettingRenderer imp } this.addSettingElementFocusHandler(template); + return template; + } + + renderElement(element: ITreeNode, index: number, templateData: ISettingObjectItemTemplate): void { + super.renderSettingElement(element, index, templateData); + } +} +class SettingObjectRenderer extends AbstractSettingObjectRenderer implements ITreeRenderer { + override templateId = SETTINGS_OBJECT_TEMPLATE_ID; + + renderTemplate(container: HTMLElement): ISettingObjectItemTemplate { + const common = this.renderCommonTemplate(null, container, 'list'); + const widget = this._instantiationService.createInstance(ObjectSettingDropdownWidget, common.controlElement); + const template = this.renderTemplateWithWidget(common, widget); common.toDispose.add(widget.onDidChangeList(e => { this.onDidChangeObject(template, e); })); - return template; } - protected onDidChangeObject(template: ISettingObjectItemTemplate, e: SettingListEvent): void { - const widget = (template.objectCheckboxWidget ?? template.objectDropdownWidget)!; + private onDidChangeObject(template: ISettingObjectItemTemplate, e: SettingListEvent): void { + const widget = template.objectDropdownWidget!; if (template.context) { const settingSupportsRemoveDefault = objectSettingSupportsRemoveDefaultValue(template.context.setting.key); const defaultValue: Record = typeof template.context.defaultValue === 'object' @@ -1380,31 +1421,11 @@ abstract class AbstractSettingObjectRenderer extends AbstractSettingRenderer imp }); const newObject = Object.keys(newValue).length === 0 ? undefined : newValue; - - if (template.objectCheckboxWidget) { - template.objectCheckboxWidget.setValue(newItems); - } else { - template.objectDropdownWidget!.setValue(newItems); - } - + template.objectDropdownWidget!.setValue(newItems); template.onChange?.(newObject); } } - renderElement(element: ITreeNode, index: number, templateData: ISettingObjectItemTemplate): void { - super.renderSettingElement(element, index, templateData); - } -} - -class SettingObjectRenderer extends AbstractSettingObjectRenderer implements ITreeRenderer { - override templateId = SETTINGS_OBJECT_TEMPLATE_ID; - - renderTemplate(container: HTMLElement): ISettingObjectItemTemplate { - const common = this.renderCommonTemplate(null, container, 'list'); - const widget = this._instantiationService.createInstance(ObjectSettingDropdownWidget, common.controlElement); - return this.renderTemplateWithWidget(common, widget); - } - protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingObjectItemTemplate, onChange: (value: Record | undefined) => void): void { const items = getObjectDisplayValue(dataElement); const { key, objectProperties, objectPatternProperties, objectAdditionalProperties } = dataElement.setting; @@ -1447,12 +1468,55 @@ class SettingBoolObjectRenderer extends AbstractSettingObjectRenderer implements renderTemplate(container: HTMLElement): ISettingObjectItemTemplate { const common = this.renderCommonTemplate(null, container, 'list'); const widget = this._instantiationService.createInstance(ObjectSettingCheckboxWidget, common.controlElement); - return this.renderTemplateWithWidget(common, widget); + const template = this.renderTemplateWithWidget(common, widget); + common.toDispose.add(widget.onDidChangeList(e => { + this.onDidChangeObject(template, e); + })); + return template; } - protected override onDidChangeObject(template: ISettingObjectItemTemplate, e: ISettingListChangeEvent): void { + protected onDidChangeObject(template: ISettingObjectItemTemplate, e: SettingListEvent): void { if (template.context) { - super.onDidChangeObject(template, e); + const widget = template.objectCheckboxWidget!; + const defaultValue: Record = typeof template.context.defaultValue === 'object' + ? template.context.defaultValue ?? {} + : {}; + + const scopeValue: Record = typeof template.context.scopeValue === 'object' + ? template.context.scopeValue ?? {} + : {}; + + const newValue: Record = { ...template.context.scopeValue }; // Initialize with scoped values as removed default values are not rendered + const newItems: IBoolObjectDataItem[] = []; + + if (e.type !== 'change') { + console.warn('Unexpected event type', e.type, 'for bool object setting', template.context.setting.key); + return; + } + + widget.items.forEach((item, idx) => { + // Item was updated + if (e.targetIndex === idx) { + newValue[e.newItem.key.data] = e.newItem.value.data; + newItems.push(e.newItem); + } + // All remaining items, but skip the one that we just updated + else if (e.newItem.key.data !== item.key.data) { + newValue[item.key.data] = item.value.data; + newItems.push(item); + } + }); + + Object.entries(newValue).forEach(([key, value]) => { + // value from the scope has changed back to the default + if (scopeValue[key] !== value && defaultValue[key] === value) { + delete newValue[key]; + } + }); + + const newObject = Object.keys(newValue).length === 0 ? undefined : newValue; + template.objectCheckboxWidget!.setValue(newItems); + template.onChange?.(newObject); // Focus this setting explicitly, in case we were previously // focused on another setting and clicked a checkbox/value container @@ -1462,7 +1526,7 @@ class SettingBoolObjectRenderer extends AbstractSettingObjectRenderer implements } protected renderValue(dataElement: SettingsTreeSettingElement, template: ISettingObjectItemTemplate, onChange: (value: Record | undefined) => void): void { - const items = getObjectDisplayValue(dataElement); + const items = getBoolObjectDisplayValue(dataElement); const { key } = dataElement.setting; template.objectCheckboxWidget!.setValue(items, { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index 9e9dcc17e86..6d2472b494b 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -1288,7 +1288,16 @@ interface IBoolObjectSetValueOptions { settingKey: string; } -export class ObjectSettingCheckboxWidget extends AbstractListSettingWidget { +export interface IBoolObjectDataItem { + key: IObjectStringData; + value: IObjectBoolData; + keyDescription?: string; + source?: string; + removable: false; + resetable: boolean; +} + +export class ObjectSettingCheckboxWidget extends AbstractListSettingWidget { private currentSettingKey: string = ''; constructor( @@ -1300,7 +1309,7 @@ export class ObjectSettingCheckboxWidget extends AbstractListSettingWidget, idx: number, listFocused: boolean): HTMLElement { + protected override renderDataOrEditItem(item: IListViewItem, idx: number, listFocused: boolean): HTMLElement { const rowElement = this.renderEdit(item, idx); rowElement.setAttribute('role', 'listitem'); return rowElement; } - protected renderItem(item: IObjectDataItem, idx: number): RowElementGroup { + protected renderItem(item: IBoolObjectDataItem, idx: number): RowElementGroup { // Return just the containers, since we always render in edit mode anyway const rowElement = $('.blank-row'); const keyElement = $('.blank-row-key'); return { rowElement, keyElement }; } - protected renderEdit(item: IObjectDataItem, idx: number): HTMLElement { + protected renderEdit(item: IBoolObjectDataItem, idx: number): HTMLElement { const rowElement = $('.setting-list-edit-row.setting-list-object-row.setting-item-bool'); const changedItem = { ...item }; @@ -1416,7 +1425,7 @@ export class ObjectSettingCheckboxWidget extends AbstractListSettingWidget string | null; enumItemLabels?: string[]; - allKeysAreBoolean?: boolean; editPresentation?: EditPresentationTypes; nonLanguageSpecificDefaultValueSource?: ConfigurationDefaultValueSource; isLanguageTagSetting?: boolean; categoryLabel?: string; // Internal properties + allKeysAreBoolean?: boolean; displayExtensionId?: string; stableExtensionId?: string; prereleaseExtensionId?: string; From e5e9b167bf8fd2d987d19eb22369556de4324047 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Sun, 7 Jul 2024 12:13:41 +0200 Subject: [PATCH 0308/2222] eng - add `open` module in preparation for default browser support (#220273) --- package.json | 1 + yarn.lock | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/package.json b/package.json index e8c78dfc686..81a0e0d416d 100644 --- a/package.json +++ b/package.json @@ -99,6 +99,7 @@ "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta11", + "open": "^8.4.2", "tas-client-umd": "0.2.0", "v8-inspect-profiler": "^0.1.1", "vscode-oniguruma": "1.7.0", diff --git a/yarn.lock b/yarn.lock index 60395e780d7..17a69acb6fb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3646,6 +3646,11 @@ defer-to-connect@^2.0.0: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== +define-lazy-prop@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f" + integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og== + define-properties@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1" @@ -5935,6 +5940,11 @@ is-descriptor@^1.0.0, is-descriptor@^1.0.2: is-data-descriptor "^1.0.0" kind-of "^6.0.2" +is-docker@^2.0.0, is-docker@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa" + integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ== + is-extendable@^0.1.0, is-extendable@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" @@ -6124,6 +6134,13 @@ is-wsl@^1.1.0: resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" integrity sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0= +is-wsl@^2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271" + integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww== + dependencies: + is-docker "^2.0.0" + is@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/is/-/is-3.2.1.tgz#d0ac2ad55eb7b0bec926a5266f6c662aaa83dca5" @@ -7628,6 +7645,15 @@ only@~0.0.2: resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= +open@^8.4.2: + version "8.4.2" + resolved "https://registry.yarnpkg.com/open/-/open-8.4.2.tgz#5b5ffe2a8f793dcd2aad73e550cb87b59cb084f9" + integrity sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ== + dependencies: + define-lazy-prop "^2.0.0" + is-docker "^2.1.1" + is-wsl "^2.2.0" + opn@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/opn/-/opn-6.0.0.tgz#3c5b0db676d5f97da1233d1ed42d182bc5a27d2d" From c67cd35e2ee191018b2175bd14903836c311f000 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Sun, 7 Jul 2024 12:42:03 +0200 Subject: [PATCH 0309/2222] fix #217188 (#220108) * fix #217188 * fix hover for "set by extension XY" --------- Co-authored-by: BeniBenj --- .../common/configurationModels.ts | 13 +- .../common/configurationRegistry.ts | 325 +++++++++--------- .../configuration/common/configurations.ts | 4 +- .../test/common/configurationRegistry.test.ts | 66 +++- .../test/common/configurations.test.ts | 8 +- .../preferences/browser/settingsTree.ts | 4 +- .../preferences/browser/settingsTreeModels.ts | 3 +- .../test/browser/configuration.test.ts | 3 +- 8 files changed, 255 insertions(+), 171 deletions(-) diff --git a/src/vs/platform/configuration/common/configurationModels.ts b/src/vs/platform/configuration/common/configurationModels.ts index 8b862b1c939..553c312d6be 100644 --- a/src/vs/platform/configuration/common/configurationModels.ts +++ b/src/vs/platform/configuration/common/configurationModels.ts @@ -279,11 +279,18 @@ export class ConfigurationModel implements IConfigurationModel { this.keys.push(key); } if (OVERRIDE_PROPERTY_REGEX.test(key)) { - this.overrides.push({ - identifiers: overrideIdentifiersFromKey(key), + const identifiers = overrideIdentifiersFromKey(key); + const override = { + identifiers, keys: Object.keys(this.contents[key]), contents: toValuesTree(this.contents[key], message => this.logService.error(message)), - }); + }; + const index = this.overrides.findIndex(o => arrays.equals(o.identifiers, identifiers)); + if (index !== -1) { + this.overrides[index] = override; + } else { + this.overrides.push(override); + } } } } diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index 2a93b106815..7876af5831e 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -70,10 +70,15 @@ export interface IConfigurationRegistry { */ deltaConfiguration(delta: IConfigurationDelta): void; + /** + * Return the registered default configurations + */ + getRegisteredDefaultConfigurations(): IConfigurationDefaults[]; + /** * Return the registered configuration defaults overrides */ - getConfigurationDefaultsOverrides(): Map; + getConfigurationDefaultsOverrides(): Map; /** * Signal that the schema of a configuration setting has changes. It is currently only supported to change enumeration values. @@ -251,11 +256,15 @@ export type IRegisteredConfigurationPropertySchema = IConfigurationPropertySchem defaultValueSource?: ConfigurationDefaultValueSource; // Source of the Default Value }; -export type IConfigurationDefaultOverride = { +export interface IConfigurationDefaultOverride { readonly value: any; - readonly source?: ConfigurationDefaultValueSource; // Source of the default override - readonly valuesSources?: Map; // Source of each value in default language overrides -}; + readonly source?: IExtensionInfo; // Source of the default override +} + +export interface IConfigurationDefaultOverrideValue { + readonly value: any; + readonly source?: ConfigurationDefaultValueSource; +} export const allSettings: { properties: IStringDictionary; patternProperties: IStringDictionary } = { properties: {}, patternProperties: {} }; export const applicationSettings: { properties: IStringDictionary; patternProperties: IStringDictionary } = { properties: {}, patternProperties: {} }; @@ -271,7 +280,8 @@ const contributionRegistry = Registry.as(JSONExtensio class ConfigurationRegistry implements IConfigurationRegistry { - private readonly configurationDefaultsOverrides: Map; + private readonly registeredConfigurationDefaults: IConfigurationDefaults[] = []; + private readonly configurationDefaultsOverrides: Map; private readonly defaultLanguageConfigurationOverridesNode: IConfigurationNode; private readonly configurationContributors: IConfigurationNode[]; private readonly configurationProperties: IStringDictionary; @@ -287,7 +297,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { readonly onDidUpdateConfiguration = this._onDidUpdateConfiguration.event; constructor() { - this.configurationDefaultsOverrides = new Map(); + this.configurationDefaultsOverrides = new Map(); this.defaultLanguageConfigurationOverridesNode = { id: 'defaultOverrides', title: nls.localize('defaultLanguageConfigurationOverrides.title', "Default Language Configuration Overrides"), @@ -350,107 +360,47 @@ class ConfigurationRegistry implements IConfigurationRegistry { private doRegisterDefaultConfigurations(configurationDefaults: IConfigurationDefaults[], bucket: Set) { + this.registeredConfigurationDefaults.push(...configurationDefaults); + const overrideIdentifiers: string[] = []; for (const { overrides, source } of configurationDefaults) { for (const key in overrides) { bucket.add(key); + const configurationDefaultOverridesForKey = this.configurationDefaultsOverrides.get(key) + ?? this.configurationDefaultsOverrides.set(key, { configurationDefaultOverrides: [] }).get(key)!; + + const value = overrides[key]; + configurationDefaultOverridesForKey.configurationDefaultOverrides.push({ value, source }); + + // Configuration defaults for Override Identifiers if (OVERRIDE_PROPERTY_REGEX.test(key)) { - const configurationDefaultOverride = this.configurationDefaultsOverrides.get(key); - const valuesSources = configurationDefaultOverride?.valuesSources ?? new Map(); - - const defaultValue = configurationDefaultOverride?.value || {}; - for (const configuration of Object.keys(overrides[key])) { - const overrideValue = overrides[key][configuration]; - - const isObjectSetting = types.isObject(overrideValue) && (types.isUndefined(defaultValue[configuration]) || types.isObject(defaultValue[configuration])); - if (isObjectSetting) { - // Objects are merged instead of overridden - defaultValue[configuration] = { ...(defaultValue[configuration] ?? {}), ...overrideValue }; - - // Track the source of each value in the object - if (source) { - let objectConfigurationSources = valuesSources.get(configuration); - if (!objectConfigurationSources) { - objectConfigurationSources = new Map(); - valuesSources.set(configuration, objectConfigurationSources); - } - if (!(objectConfigurationSources instanceof Map)) { - console.error('objectConfigurationSources is not a Map'); - continue; - } - - for (const objectKey in overrideValue) { - objectConfigurationSources.set(objectKey, source); - } - } - } else { - // Primitive values are overridden - defaultValue[configuration] = overrideValue; - if (source) { - valuesSources.set(configuration, source); - } - } + const newDefaultOverride = this.mergeDefaultConfigurationsForOverrideIdentifier(key, value, source, configurationDefaultOverridesForKey.configurationDefaultOverrideValue); + if (!newDefaultOverride) { + continue; } - this.configurationDefaultsOverrides.set(key, { source, value: defaultValue, valuesSources }); - const plainKey = getLanguageTagSettingPlainKey(key); - const property: IRegisteredConfigurationPropertySchema = { - type: 'object', - default: defaultValue, - description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for the {0} language.", plainKey), - $ref: resourceLanguageSettingsSchemaId, - defaultDefaultValue: defaultValue, - source, - defaultValueSource: source - }; + configurationDefaultOverridesForKey.configurationDefaultOverrideValue = newDefaultOverride; + this.updateDefaultOverrideProperty(key, newDefaultOverride, source); overrideIdentifiers.push(...overrideIdentifiersFromKey(key)); - this.configurationProperties[key] = property; - this.defaultLanguageConfigurationOverridesNode.properties![key] = property; - } else { - const property = this.configurationProperties[key]; + } - const existingDefaultOverride = this.configurationDefaultsOverrides.get(key); - let existingDefaultValue = existingDefaultOverride?.value ?? property?.defaultDefaultValue; - - let newDefaultValue = overrides[key]; - let newDefaultValueSource: ConfigurationDefaultValueSource | undefined = source; - - const isObjectSetting = types.isObject(newDefaultValue) && ( - property !== undefined && property.type === 'object' || - property === undefined && (types.isUndefined(existingDefaultValue) || types.isObject(existingDefaultValue))); - - // If the default value is an object, merge the objects and store the source of each keys - if (isObjectSetting) { - if (!types.isObject(existingDefaultValue)) { - existingDefaultValue = {}; - } - - newDefaultValue = { ...existingDefaultValue, ...newDefaultValue }; - - newDefaultValueSource = existingDefaultOverride?.source ?? new Map(); - if (!(newDefaultValueSource instanceof Map)) { - console.error('defaultValueSource is not a Map'); - continue; - } - - for (const overrideObjectKey in overrides[key]) { - if (source) { - newDefaultValueSource.set(overrideObjectKey, source); - } else { - newDefaultValueSource.delete(overrideObjectKey); - } - } + // Configuration defaults for Configuration Properties + else { + const newDefaultOverride = this.mergeDefaultConfigurationsForConfigurationProperty(key, value, source, configurationDefaultOverridesForKey.configurationDefaultOverrideValue); + if (!newDefaultOverride) { + continue; } - this.configurationDefaultsOverrides.set(key, { value: newDefaultValue, source: newDefaultValueSource }); - + configurationDefaultOverridesForKey.configurationDefaultOverrideValue = newDefaultOverride; + const property = this.configurationProperties[key]; if (property) { this.updatePropertyDefaultValue(key, property); this.updateSchema(key, property); } } + } } @@ -465,96 +415,149 @@ class ConfigurationRegistry implements IConfigurationRegistry { } private doDeregisterDefaultConfigurations(defaultConfigurations: IConfigurationDefaults[], bucket: Set): void { + for (const defaultConfiguration of defaultConfigurations) { + const index = this.registeredConfigurationDefaults.indexOf(defaultConfiguration); + if (index !== -1) { + this.registeredConfigurationDefaults.splice(index, 1); + } + } for (const { overrides, source } of defaultConfigurations) { for (const key in overrides) { - const id = source?.id; + const configurationDefaultOverridesForKey = this.configurationDefaultsOverrides.get(key); + if (!configurationDefaultOverridesForKey) { + continue; + } - const configurationDefaultsOverride = this.configurationDefaultsOverrides.get(key); - if (!configurationDefaultsOverride) { + const index = configurationDefaultOverridesForKey.configurationDefaultOverrides + .findIndex(configurationDefaultOverride => source ? configurationDefaultOverride.source?.id === source.id : configurationDefaultOverride.value === overrides[key]); + if (index === -1) { continue; } + configurationDefaultOverridesForKey.configurationDefaultOverrides.splice(index, 1); + if (configurationDefaultOverridesForKey.configurationDefaultOverrides.length === 0) { + this.configurationDefaultsOverrides.delete(key); + } + if (OVERRIDE_PROPERTY_REGEX.test(key)) { - for (const configuration of Object.keys(overrides[key])) { - const overrideValue = overrides[key][configuration]; - - if (types.isObject(overrideValue)) { - const configurationSource = configurationDefaultsOverride.valuesSources?.get(configuration) as Map | undefined; - - for (const overrideObjectKey of Object.keys(overrideValue)) { - const keySource = configurationSource?.get(overrideObjectKey); - const keySourceId = types.isString(keySource) ? keySource : keySource?.id; - if (keySourceId === id) { - configurationSource?.delete(overrideObjectKey); - delete configurationDefaultsOverride.value[configuration][overrideObjectKey]; - } - } - - if (Object.keys(configurationDefaultsOverride.value[configuration]).length === 0) { - delete configurationDefaultsOverride.value[configuration]; - configurationDefaultsOverride.valuesSources?.delete(configuration); - } - } else { - const configurationSource = configurationDefaultsOverride.valuesSources?.get(configuration) as string | IExtensionInfo | undefined; - - const keySourceId = types.isString(configurationSource) ? configurationSource : configurationSource?.id; - if (keySourceId === id) { - configurationDefaultsOverride.valuesSources?.delete(configuration); - delete configurationDefaultsOverride.value[configuration]; - } - } + let configurationDefaultOverrideValue: IConfigurationDefaultOverrideValue | undefined; + for (const configurationDefaultOverride of configurationDefaultOverridesForKey.configurationDefaultOverrides) { + configurationDefaultOverrideValue = this.mergeDefaultConfigurationsForOverrideIdentifier(key, configurationDefaultOverride.value, configurationDefaultOverride.source, configurationDefaultOverrideValue); } - // Remove language configuration if empty ({[css]: {}} => {}) - const languageValues = this.configurationDefaultsOverrides.get(key); - if (languageValues && Object.keys(languageValues.value).length === 0) { + if (configurationDefaultOverrideValue && !types.isEmptyObject(configurationDefaultOverrideValue.value)) { + configurationDefaultOverridesForKey.configurationDefaultOverrideValue = configurationDefaultOverrideValue; + this.updateDefaultOverrideProperty(key, configurationDefaultOverrideValue, source); + } else { this.configurationDefaultsOverrides.delete(key); delete this.configurationProperties[key]; delete this.defaultLanguageConfigurationOverridesNode.properties![key]; } } else { - // If the default value is an object, remove the source of each key - if (configurationDefaultsOverride.source instanceof Map) { - - const keySources = configurationDefaultsOverride.source; - for (const objectKey in overrides[key]) { - const keySource = keySources.get(objectKey); - const keySourceId = types.isString(keySource) ? keySource : keySource?.id; - - if (keySourceId === id) { - keySources.delete(objectKey); - delete configurationDefaultsOverride.value[objectKey]; - } - } - - if (keySources.size === 0) { - this.configurationDefaultsOverrides.delete(key); - } - } - // Otherwise, remove the default value if the source matches - else { - const configurationDefaultsOverrideSourceId = configurationDefaultsOverride.source?.id; - if (id !== configurationDefaultsOverrideSourceId) { - continue; // Another source is overriding this default value - } - - this.configurationDefaultsOverrides.delete(key); - + let configurationDefaultOverrideValue: IConfigurationDefaultOverrideValue | undefined; + for (const configurationDefaultOverride of configurationDefaultOverridesForKey.configurationDefaultOverrides) { + configurationDefaultOverrideValue = this.mergeDefaultConfigurationsForConfigurationProperty(key, configurationDefaultOverride.value, configurationDefaultOverride.source, configurationDefaultOverrideValue); } + configurationDefaultOverridesForKey.configurationDefaultOverrideValue = configurationDefaultOverrideValue; const property = this.configurationProperties[key]; if (property) { this.updatePropertyDefaultValue(key, property); this.updateSchema(key, property); } } - bucket.add(key); } } - this.updateOverridePropertyPatternKey(); } + private updateDefaultOverrideProperty(key: string, newDefaultOverride: IConfigurationDefaultOverrideValue, source: IExtensionInfo | undefined): void { + const property: IRegisteredConfigurationPropertySchema = { + type: 'object', + default: newDefaultOverride.value, + description: nls.localize('defaultLanguageConfiguration.description', "Configure settings to be overridden for the {0} language.", getLanguageTagSettingPlainKey(key)), + $ref: resourceLanguageSettingsSchemaId, + defaultDefaultValue: newDefaultOverride.value, + source, + defaultValueSource: source + }; + this.configurationProperties[key] = property; + this.defaultLanguageConfigurationOverridesNode.properties![key] = property; + } + + private mergeDefaultConfigurationsForOverrideIdentifier(overrideIdentifier: string, configurationValueObject: IStringDictionary, valueSource: IExtensionInfo | undefined, existingDefaultOverride: IConfigurationDefaultOverrideValue | undefined): IConfigurationDefaultOverrideValue | undefined { + const defaultValue = existingDefaultOverride?.value || {}; + const source = existingDefaultOverride?.source ?? new Map(); + + // This should not happen + if (!(source instanceof Map)) { + console.error('objectConfigurationSources is not a Map'); + return undefined; + } + + for (const propertyKey of Object.keys(configurationValueObject)) { + const propertyDefaultValue = configurationValueObject[propertyKey]; + + const isObjectSetting = types.isObject(propertyDefaultValue) && + (types.isUndefined(defaultValue[propertyKey]) || types.isObject(defaultValue[propertyKey])); + + // If the default value is an object, merge the objects and store the source of each keys + if (isObjectSetting) { + defaultValue[propertyKey] = { ...(defaultValue[propertyKey] ?? {}), ...propertyDefaultValue }; + // Track the source of each value in the object + if (valueSource) { + for (const objectKey in propertyDefaultValue) { + source.set(`${propertyKey}.${objectKey}`, valueSource); + } + } + } + + // Primitive values are overridden + else { + defaultValue[propertyKey] = propertyDefaultValue; + if (valueSource) { + source.set(propertyKey, valueSource); + } else { + source.delete(propertyKey); + } + } + } + + return { value: defaultValue, source }; + } + + private mergeDefaultConfigurationsForConfigurationProperty(propertyKey: string, value: any, valuesSource: IExtensionInfo | undefined, existingDefaultOverride: IConfigurationDefaultOverrideValue | undefined): IConfigurationDefaultOverrideValue | undefined { + const property = this.configurationProperties[propertyKey]; + const existingDefaultValue = existingDefaultOverride?.value ?? property?.defaultDefaultValue; + let source: ConfigurationDefaultValueSource | undefined = valuesSource; + + const isObjectSetting = types.isObject(value) && + ( + property !== undefined && property.type === 'object' || + property === undefined && (types.isUndefined(existingDefaultValue) || types.isObject(existingDefaultValue)) + ); + + // If the default value is an object, merge the objects and store the source of each keys + if (isObjectSetting) { + source = existingDefaultOverride?.source ?? new Map(); + + // This should not happen + if (!(source instanceof Map)) { + console.error('defaultValueSource is not a Map'); + return undefined; + } + + for (const objectKey in value) { + if (valuesSource) { + source.set(`${propertyKey}.${objectKey}`, valuesSource); + } + } + value = { ...(types.isObject(existingDefaultValue) ? existingDefaultValue : {}), ...value }; + } + + return { value, source }; + } + public deltaConfiguration(delta: IConfigurationDelta): void { // defaults: remove let defaultsOverrides = false; @@ -703,8 +706,18 @@ class ConfigurationRegistry implements IConfigurationRegistry { return this.excludedConfigurationProperties; } - getConfigurationDefaultsOverrides(): Map { - return this.configurationDefaultsOverrides; + getRegisteredDefaultConfigurations(): IConfigurationDefaults[] { + return [...this.registeredConfigurationDefaults]; + } + + getConfigurationDefaultsOverrides(): Map { + const configurationDefaultsOverrides = new Map(); + for (const [key, value] of this.configurationDefaultsOverrides) { + if (value.configurationDefaultOverrideValue) { + configurationDefaultsOverrides.set(key, value.configurationDefaultOverrideValue); + } + } + return configurationDefaultsOverrides; } private registerJSONConfiguration(configuration: IConfigurationNode) { @@ -805,7 +818,7 @@ class ConfigurationRegistry implements IConfigurationRegistry { } private updatePropertyDefaultValue(key: string, property: IRegisteredConfigurationPropertySchema): void { - const configurationdefaultOverride = this.configurationDefaultsOverrides.get(key); + const configurationdefaultOverride = this.configurationDefaultsOverrides.get(key)?.configurationDefaultOverrideValue; let defaultValue = undefined; let defaultSource = undefined; if (configurationdefaultOverride diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index d6f09a4d176..5e3c303ada6 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -61,9 +61,9 @@ export class DefaultConfiguration extends Disposable { const defaultOverrideValue = configurationDefaultsOverrides[key]; const propertySchema = configurationProperties[key]; if (defaultOverrideValue !== undefined) { - this._configurationModel.addValue(key, defaultOverrideValue); + this._configurationModel.setValue(key, defaultOverrideValue); } else if (propertySchema) { - this._configurationModel.addValue(key, propertySchema.default); + this._configurationModel.setValue(key, propertySchema.default); } else { this._configurationModel.removeValue(key); } diff --git a/src/vs/platform/configuration/test/common/configurationRegistry.test.ts b/src/vs/platform/configuration/test/common/configurationRegistry.test.ts index ec7fcb5e8b6..0f3bf7e2d87 100644 --- a/src/vs/platform/configuration/test/common/configurationRegistry.test.ts +++ b/src/vs/platform/configuration/test/common/configurationRegistry.test.ts @@ -14,6 +14,14 @@ suite('ConfigurationRegistry', () => { const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); + setup(() => reset()); + teardown(() => reset()); + + function reset() { + configurationRegistry.deregisterConfigurations(configurationRegistry.getConfigurations()); + configurationRegistry.deregisterDefaultConfigurations(configurationRegistry.getRegisteredDefaultConfigurations()); + } + test('configuration override', async () => { configurationRegistry.registerConfiguration({ 'id': '_test_default', @@ -119,10 +127,66 @@ suite('ConfigurationRegistry', () => { configurationRegistry.deregisterDefaultConfigurations(overrides2); - assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { b: 2 }); // TODO this should actualy equal overrides1 + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 1, b: 2 }); + + configurationRegistry.deregisterDefaultConfigurations(overrides1); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, {}); + }); + + test('configuration defaults - deregister merged object default override without source', async () => { + configurationRegistry.registerConfiguration({ + 'id': '_test_default', + 'type': 'object', + 'properties': { + 'config': { + 'type': 'object', + } + } + }); + + const overrides1 = [{ overrides: { 'config': { a: 1, b: 2 } } }]; + const overrides2 = [{ overrides: { 'config': { a: 2, c: 3 } } }]; + + configurationRegistry.registerDefaultConfigurations(overrides1); + configurationRegistry.registerDefaultConfigurations(overrides2); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 2, b: 2, c: 3 }); + + configurationRegistry.deregisterDefaultConfigurations(overrides2); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, { a: 1, b: 2 }); configurationRegistry.deregisterDefaultConfigurations(overrides1); assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['config'].default, {}); }); + + test('configuration defaults - deregister merged object default language overrides', async () => { + configurationRegistry.registerConfiguration({ + 'id': '_test_default', + 'type': 'object', + 'properties': { + 'config': { + 'type': 'object', + } + } + }); + + const overrides1 = [{ overrides: { '[lang]': { 'config': { a: 1, b: 2 } } }, source: { id: 'source1', displayName: 'source1' } }]; + const overrides2 = [{ overrides: { '[lang]': { 'config': { a: 2, c: 3 } } }, source: { id: 'source2', displayName: 'source2' } }]; + + configurationRegistry.registerDefaultConfigurations(overrides1); + configurationRegistry.registerDefaultConfigurations(overrides2); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { 'config': { a: 2, b: 2, c: 3 } }); + + configurationRegistry.deregisterDefaultConfigurations(overrides2); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'].default, { 'config': { a: 1, b: 2 } }); + + configurationRegistry.deregisterDefaultConfigurations(overrides1); + + assert.deepStrictEqual(configurationRegistry.getConfigurationProperties()['[lang]'], undefined); + }); }); diff --git a/src/vs/platform/configuration/test/common/configurations.test.ts b/src/vs/platform/configuration/test/common/configurations.test.ts index 63f8d33bfee..5b237b73719 100644 --- a/src/vs/platform/configuration/test/common/configurations.test.ts +++ b/src/vs/platform/configuration/test/common/configurations.test.ts @@ -22,8 +22,7 @@ suite('DefaultConfiguration', () => { function reset() { configurationRegistry.deregisterConfigurations(configurationRegistry.getConfigurations()); - const configurationDefaultsOverrides = configurationRegistry.getConfigurationDefaultsOverrides(); - configurationRegistry.deregisterDefaultConfigurations([...configurationDefaultsOverrides.keys()].map(key => ({ extensionId: configurationDefaultsOverrides.get(key)?.source, overrides: { [key]: configurationDefaultsOverrides.get(key)?.value } }))); + configurationRegistry.deregisterDefaultConfigurations(configurationRegistry.getRegisteredDefaultConfigurations()); } test('Test registering a property before initialize', async () => { @@ -403,11 +402,12 @@ suite('DefaultConfiguration', () => { configurationRegistry.registerDefaultConfigurations([node1]); configurationRegistry.registerDefaultConfigurations([node2]); await testObject.initialize(); + configurationRegistry.deregisterDefaultConfigurations([node1]); assert.ok(equals(testObject.configurationModel.getValue('[a]'), { 'b': { 'bb': '20', 'cc': '30' } })); assert.ok(equals(testObject.configurationModel.contents, { '[a]': { 'b': { 'bb': '20', 'cc': '30' } }, 'b': {} })); - //assert.ok(equals(testObject.configurationModel.overrides, [{ '[a]': { 'b': { 'bb': '20', 'cc': '30' } } }])); TODO: Check this later - //assert.deepStrictEqual(testObject.configurationModel.keys.sort(), ['[a]', 'b']); + assert.ok(equals(testObject.configurationModel.overrides, [{ contents: { 'b': { 'bb': '20', 'cc': '30' } }, identifiers: ['a'], keys: ['b'] }])); + assert.deepStrictEqual(testObject.configurationModel.keys.sort(), ['[a]', 'b']); assert.ok(equals(testObject.configurationModel.getOverrideValue('b', 'a'), { 'bb': '20', 'cc': '30' })); }); }); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index 2c9ea5519ca..b404461da93 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -91,7 +91,7 @@ function getIncludeExcludeDisplayValue(element: SettingsTreeSettingElement): IIn // Get source if it's a default value let source: string | undefined; if (defaultValue === data[key] && element.setting.type === 'object' && element.defaultValueSource instanceof Map) { - const defaultSource = element.defaultValueSource.get(key); + const defaultSource = element.defaultValueSource.get(`${element.setting.key}.${key}`); source = typeof defaultSource === 'string' ? defaultSource : defaultSource?.displayName; } @@ -190,7 +190,7 @@ function getObjectDisplayValue(element: SettingsTreeSettingElement): IObjectData // Get source if it's a default value let source: string | undefined; if (defaultValue === data[key] && element.setting.type === 'object' && element.defaultValueSource instanceof Map) { - const defaultSource = element.defaultValueSource.get(key); + const defaultSource = element.defaultValueSource.get(`${element.setting.key}.${key}`); source = typeof defaultSource === 'string' ? defaultSource : defaultSource?.displayName; } diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts index e651ffdccf7..c900ed2c45c 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTreeModels.ts @@ -351,7 +351,8 @@ export class SettingsTreeSettingElement extends SettingsTreeElement { this.defaultValue = overrideValues.defaultValue ?? inspected.defaultValue; const registryValues = Registry.as(Extensions.Configuration).getConfigurationDefaultsOverrides(); - const overrideValueSource = registryValues.get(`[${languageSelector}]`)?.valuesSources?.get(this.setting.key); + const source = registryValues.get(`[${languageSelector}]`)?.source; + const overrideValueSource = source instanceof Map ? source.get(this.setting.key) : undefined; if (overrideValueSource) { this.defaultValueSource = overrideValueSource; } diff --git a/src/vs/workbench/services/configuration/test/browser/configuration.test.ts b/src/vs/workbench/services/configuration/test/browser/configuration.test.ts index 266ccaf978b..5dfe022d2b5 100644 --- a/src/vs/workbench/services/configuration/test/browser/configuration.test.ts +++ b/src/vs/workbench/services/configuration/test/browser/configuration.test.ts @@ -48,8 +48,7 @@ suite('DefaultConfiguration', () => { teardown(() => { configurationRegistry.deregisterConfigurations(configurationRegistry.getConfigurations()); - const configurationDefaultsOverrides = configurationRegistry.getConfigurationDefaultsOverrides(); - configurationRegistry.deregisterDefaultConfigurations([...configurationDefaultsOverrides.keys()].map(key => ({ extensionId: configurationDefaultsOverrides.get(key)?.source, overrides: { [key]: configurationDefaultsOverrides.get(key)?.value } }))); + configurationRegistry.deregisterDefaultConfigurations(configurationRegistry.getRegisteredDefaultConfigurations()); }); test('configuration default overrides are read from environment', async () => { From 149a798e5c546c79ee74b3cc7237777c1cd17ec4 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Sun, 7 Jul 2024 13:04:30 +0200 Subject: [PATCH 0310/2222] Remove MultiRowEditorControl Hack and properly redraw tabs on restore (#220090) * update misleading commen * Remove Hack * :lipstick: * still update title sync * open all editors on tab bar change --------- Co-authored-by: Benjamin Pasero --- .../browser/parts/editor/editorGroupView.ts | 12 +++++++++--- .../browser/parts/editor/multiEditorTabsControl.ts | 13 ------------- .../parts/editor/multiRowEditorTabsControl.ts | 6 +----- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 405049cfd1b..41e4e3ccc24 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -560,14 +560,15 @@ export class EditorGroupView extends Themable implements IEditorGroupView { options.preserveFocus = true; // handle focus after editor is restored const internalOptions: IInternalEditorOpenOptions = { - preserveWindowOrder: true // handle window order after editor is restored + preserveWindowOrder: true, // handle window order after editor is restored + skipTitleUpdate: true, // update the title later for all editors at once }; const activeElement = getActiveElement(); // Show active editor (intentionally not using async to keep // `restoreEditors` from executing in same stack) - return this.doShowEditor(activeEditor, { active: true, isNew: false /* restored */ }, options, internalOptions).then(() => { + const result = this.doShowEditor(activeEditor, { active: true, isNew: false /* restored */ }, options, internalOptions).then(() => { // Set focused now if this is the active group and focus has // not changed meanwhile. This prevents focus from being @@ -578,6 +579,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.focus(); } }); + + // Restore editors in title control + this.titleControl.openEditors(this.editors); + + return result; } //#region event handling @@ -829,7 +835,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Ensure to show active editor if any if (this.model.activeEditor) { - this.titleControl.openEditor(this.model.activeEditor); + this.titleControl.openEditors(this.model.getEditors(EditorsOrder.SEQUENTIAL)); } } diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 637d3486046..9007f132a8e 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -684,20 +684,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { this.layout(this.dimensions, { forceRevealActiveTab: true }); } - private previousSelectedCount = 0; updateEditorSelections(): void { - // We only need to redraw from here when a selection got removed - // otherwise it will be handled by the open editor change event. - // Checking count is currently enough but might require checking - // each editor in the future if selection is changed programatically. - const newSelectedCount = this.tabsModel.selectedEditors.length; - const previousSelectedCount = this.previousSelectedCount; - this.previousSelectedCount = newSelectedCount; - - if (newSelectedCount >= previousSelectedCount) { - return; - } - this.forEachTab((editor, tabIndex, tabContainer, tabLabelWidget, tabLabel, tabActionBar) => { this.redrawTabSelectedActiveAndDirty(this.groupsView.activeGroup === this.groupView, editor, tabContainer, tabActionBar); }); diff --git a/src/vs/workbench/browser/parts/editor/multiRowEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiRowEditorTabsControl.ts index 83823d3ec8f..69eb8b77262 100644 --- a/src/vs/workbench/browser/parts/editor/multiRowEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiRowEditorTabsControl.ts @@ -61,12 +61,8 @@ export class MultiRowEditorControl extends Disposable implements IEditorTabsCont } openEditor(editor: EditorInput, options: IInternalEditorOpenOptions): boolean { - const [editorTabController, otherTabController] = this.model.isSticky(editor) ? [this.stickyEditorTabsControl, this.unstickyEditorTabsControl] : [this.unstickyEditorTabsControl, this.stickyEditorTabsControl]; - const didChange = editorTabController.openEditor(editor, options); + const didChange = this.getEditorTabsController(editor).openEditor(editor, options); if (didChange) { - // HACK: To render all editor tabs on startup, otherwise only one row gets rendered - otherTabController.openEditors([]); - this.handleOpenedEditors(); } return didChange; From 477d23310b9d8880de6bbaa4dc0d8dc4fd3bfd91 Mon Sep 17 00:00:00 2001 From: Simon Siefke Date: Sun, 7 Jul 2024 20:35:50 +0200 Subject: [PATCH 0311/2222] feature: allow configuring default browser (#219885) --- src/vs/platform/native/common/native.ts | 2 +- .../electron-main/nativeHostMainService.ts | 49 +++++++++++++++++-- .../browser/workbench.contribution.ts | 8 ++- src/vs/workbench/electron-sandbox/window.ts | 2 +- .../electron-sandbox/workbenchTestServices.ts | 2 +- 5 files changed, 54 insertions(+), 9 deletions(-) diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 9489592a766..8a45868ed63 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -127,7 +127,7 @@ export interface ICommonNativeHostService { showItemInFolder(path: string): Promise; setRepresentedFilename(path: string, options?: INativeHostOptions): Promise; setDocumentEdited(edited: boolean, options?: INativeHostOptions): Promise; - openExternal(url: string): Promise; + openExternal(url: string, defaultApplication?: string): Promise; moveItemToTrash(fullPath: string): Promise; isAdmin(): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index dad4087ec75..3b49b725dd9 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -11,8 +11,8 @@ import { promisify } from 'util'; import { memoize } from 'vs/base/common/decorators'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { Schemas } from 'vs/base/common/network'; -import { dirname, join, resolve } from 'vs/base/common/path'; +import { matchesSomeScheme, Schemas } from 'vs/base/common/network'; +import { dirname, join, posix, resolve, win32 } from 'vs/base/common/path'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { AddFirstParameterToFunctions } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; @@ -44,6 +44,7 @@ import { IV8Profile } from 'vs/platform/profiling/common/profiling'; import { IAuxiliaryWindowsMainService } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindows'; import { IAuxiliaryWindow } from 'vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow'; import { CancellationError } from 'vs/base/common/errors'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IProxyAuthService } from 'vs/platform/native/electron-main/auth'; import { AuthInfo, Credentials } from 'vs/platform/request/common/request'; @@ -65,6 +66,7 @@ export class NativeHostMainService extends Disposable implements INativeHostMain @IProductService private readonly productService: IProductService, @IThemeMainService private readonly themeMainService: IThemeMainService, @IWorkspacesManagementMainService private readonly workspacesManagementMainService: IWorkspacesManagementMainService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IProxyAuthService private readonly proxyAuthService: IProxyAuthService ) { super(); @@ -489,14 +491,51 @@ export class NativeHostMainService extends Disposable implements INativeHostMain window?.setDocumentEdited(edited); } - async openExternal(windowId: number | undefined, url: string): Promise { + async openExternal(windowId: number | undefined, url: string, defaultApplication?: string): Promise { this.environmentMainService.unsetSnapExportedVariables(); - shell.openExternal(url); - this.environmentMainService.restoreSnapExportedVariables(); + try { + if (matchesSomeScheme(url, Schemas.http, Schemas.https)) { + this.openExternalBrowser(url, defaultApplication); + } else { + shell.openExternal(url); + } + } finally { + this.environmentMainService.restoreSnapExportedVariables(); + } return true; } + private async openExternalBrowser(url: string, defaultApplication?: string) { + const configuredBrowser = defaultApplication ?? this.configurationService.getValue('workbench.externalBrowser'); + if (!configuredBrowser) { + return shell.openExternal(url); + } + + if (configuredBrowser.includes(posix.sep) || configuredBrowser.includes(win32.sep)) { + const browserPathExists = await Promises.exists(configuredBrowser); + if (!browserPathExists) { + this.logService.error(`Configured external browser path does not exist: ${configuredBrowser}`); + return shell.openExternal(url); + } + } + + try { + const { default: open } = await import('open'); + await open(url, { + app: { + // Use `open.apps` helper to allow cross-platform browser + // aliases to be looked up properly. Fallback to the + // configured value if not found. + name: Object.hasOwn(open.apps, configuredBrowser) ? open.apps[(configuredBrowser as keyof typeof open['apps'])] : configuredBrowser + } + }); + } catch (error) { + this.logService.error(`Unable to open external URL '${url}' using browser '${configuredBrowser}' due to ${error}.`); + return shell.openExternal(url); + } + } + moveItemToTrash(windowId: number | undefined, fullPath: string): Promise { return shell.trashItem(fullPath); } diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index ae4d22c9eaa..fb05198e50b 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -6,7 +6,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { localize } from 'vs/nls'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; -import { isMacintosh, isWindows, isLinux, isWeb } from 'vs/base/common/platform'; +import { isMacintosh, isWindows, isLinux, isWeb, isNative } from 'vs/base/common/platform'; import { ConfigurationMigrationWorkbenchContribution, DynamicWorkbenchSecurityConfiguration, IConfigurationMigrationRegistry, workbenchConfigurationNodeBase, Extensions, ConfigurationKeyValuePairs, problemsConfigurationNodeBase, windowConfigurationNodeBase, DynamicWindowConfiguration } from 'vs/workbench/common/configuration'; import { isStandalone } from 'vs/base/browser/browser'; import { WorkbenchPhase, registerWorkbenchContribution2 } from 'vs/workbench/common/contributions'; @@ -29,6 +29,12 @@ const registry = Registry.as(ConfigurationExtensions.Con registry.registerConfiguration({ ...workbenchConfigurationNodeBase, 'properties': { + 'workbench.externalBrowser': { + type: 'string', + markdownDescription: localize('browser', "Configure the browser to use for opening http or https links externally. This can either be the name of the browser (`edge`, `chrome`, `firefox`) or an absolute path to the browser's executable. Will use the system default if not set."), + included: isNative, + restricted: true + }, 'workbench.editor.titleScrollbarSizing': { type: 'string', enum: ['default', 'large'], diff --git a/src/vs/workbench/electron-sandbox/window.ts b/src/vs/workbench/electron-sandbox/window.ts index 7d8a1901d7a..140e12ee47d 100644 --- a/src/vs/workbench/electron-sandbox/window.ts +++ b/src/vs/workbench/electron-sandbox/window.ts @@ -894,7 +894,7 @@ export class NativeWindow extends BaseWindow { // Handle external open() calls this.openerService.setDefaultExternalOpener({ openExternal: async (href: string) => { - const success = await this.nativeHostService.openExternal(href); + const success = await this.nativeHostService.openExternal(href, this.configurationService.getValue('workbench.externalBrowser')); if (!success) { const fileCandidate = URI.parse(href); if (fileCandidate.scheme === Schemas.file) { diff --git a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts index 3864abba55b..e130b0837f2 100644 --- a/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-sandbox/workbenchTestServices.ts @@ -125,7 +125,7 @@ export class TestNativeHostService implements INativeHostService { async getProcessId(): Promise { throw new Error('Method not implemented.'); } async killProcess(): Promise { } async setDocumentEdited(edited: boolean): Promise { } - async openExternal(url: string): Promise { return false; } + async openExternal(url: string, defaultApplication?: string): Promise { return false; } async updateTouchBar(): Promise { } async moveItemToTrash(): Promise { } async newWindowTab(): Promise { } From 3e283de567f5701a2be639bdacdac2c3cfdb088a Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Sun, 7 Jul 2024 12:57:40 -0700 Subject: [PATCH 0312/2222] Convert confirmation part in chat panel response (#220128) * Convert confirmation part * Move conversion to ChatResponsePart * Fix indent --- src/vs/workbench/api/common/extHostTypeConverters.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 36f83b564e0..6fad560a437 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2557,6 +2557,8 @@ export namespace ChatResponsePart { return ChatResponseDetectedParticipantPart.from(part); } else if (part instanceof types.ChatResponseWarningPart) { return ChatResponseWarningPart.from(part); + } else if (part instanceof types.ChatResponseConfirmationPart) { + return ChatResponseConfirmationPart.from(part); } return { From 929146ee0b59ecff41cb734c5790c0834d1331bd Mon Sep 17 00:00:00 2001 From: eleanorjboyd Date: Sun, 7 Jul 2024 22:19:05 -0700 Subject: [PATCH 0313/2222] addition for docs --- src/vs/workbench/contrib/testing/common/testingContextKeys.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts index 1463fb3c1d4..2078a1d0efd 100644 --- a/src/vs/workbench/contrib/testing/common/testingContextKeys.ts +++ b/src/vs/workbench/contrib/testing/common/testingContextKeys.ts @@ -75,4 +75,8 @@ export namespace TestingContextKeys { type: 'string', description: localize('testing.testResultState', 'Value available testing/item/result indicating the state of the item.') }); + export const testProfileContextGroup = new RawContextKey('testing.profile.context.group', undefined, { + type: 'string', + description: localize('testing.profile.context.group', 'Type of menu where the configure testing profile submenu exists. Either "run", "debug", or "coverage"') + }); } From e5da006a742a662e765e3774d40ff7ab3bedcf8d Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 8 Jul 2024 07:39:13 +0200 Subject: [PATCH 0314/2222] nls - update reference in ts config (#220982) --- src/tsconfig.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tsconfig.json b/src/tsconfig.json index 27f975eb502..bd601891297 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -37,7 +37,7 @@ "./vs/base/common/jsonc.js", "./vs/base/common/performance.js", "./vs/base/node/unc.js", - "./vs/base/node/languagePacks.js", + "./vs/base/node/nls.js", "./vs/platform/environment/node/userDataPath.js", "./vs/base/parts/sandbox/electron-sandbox/preload-aux.js", "./vs/base/parts/sandbox/electron-sandbox/preload.js", From d0f0de52df33dd699eea0eb1144dce53d038c94a Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 09:14:50 +0200 Subject: [PATCH 0315/2222] Git - fix commit flow regression (#220991) --- extensions/git/src/commands.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 55c5f89fd3a..686ce366e29 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -2063,7 +2063,7 @@ export class CommandCenter { promptToSaveFilesBeforeCommit = 'never'; } - const enableSmartCommit = config.get('enableSmartCommit') === true; + let enableSmartCommit = config.get('enableSmartCommit') === true; const enableCommitSigning = config.get('enableCommitSigning') === true; let noStagedChanges = repository.indexGroup.resourceStates.length === 0; let noUnstagedChanges = repository.workingTreeGroup.resourceStates.length === 0; @@ -2119,12 +2119,16 @@ export class CommandCenter { const pick = await window.showWarningMessage(message, { modal: true }, yes, always, never); if (pick === always) { + enableSmartCommit = true; config.update('enableSmartCommit', true, true); } else if (pick === never) { config.update('suggestSmartCommit', false, true); return; - } else if (pick !== yes) { - return; // do not commit on cancel + } else if (pick === yes) { + enableSmartCommit = true; + } else { + // Cancel + return; } } From c36c1716f8b044398ba716fc21ab86986d08ce86 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 8 Jul 2024 09:41:36 +0200 Subject: [PATCH 0316/2222] Use manually configured basic auth credentials (#220034) --- src/vs/platform/native/electron-main/auth.ts | 33 ++++++++++++++++++- .../windows/electron-main/windowImpl.ts | 16 +++++---- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/src/vs/platform/native/electron-main/auth.ts b/src/vs/platform/native/electron-main/auth.ts index 953a74ec158..da7e0ea26cc 100644 --- a/src/vs/platform/native/electron-main/auth.ts +++ b/src/vs/platform/native/electron-main/auth.ts @@ -8,7 +8,9 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { hash } from 'vs/base/common/hash'; import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; @@ -50,7 +52,8 @@ export class ProxyAuthService extends Disposable implements IProxyAuthService { @ILogService private readonly logService: ILogService, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @IEncryptionMainService private readonly encryptionMainService: IEncryptionMainService, - @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService + @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); @@ -132,6 +135,34 @@ export class ProxyAuthService extends Disposable implements IProxyAuthService { private async doResolveProxyCredentials(authInfo: AuthInfo, authInfoHash: string): Promise { this.logService.trace('auth#doResolveProxyCredentials - enter', authInfo); + // Reply with manually supplied credentials. Fail if they are wrong. + const newHttpProxy = (this.configurationService.getValue('http.proxy') || '').trim() + || (process.env['https_proxy'] || process.env['HTTPS_PROXY'] || process.env['http_proxy'] || process.env['HTTP_PROXY'] || '').trim() + || undefined; + + if (newHttpProxy?.indexOf('@') !== -1) { + const uri = URI.parse(newHttpProxy!); + const i = uri.authority.indexOf('@'); + if (i !== -1) { + if (authInfo.attempt > 1) { + return undefined; // We tried already, let the user handle it. + } + const credentials = uri.authority.substring(0, i); + const j = credentials.indexOf(':'); + if (j !== -1) { + return { + username: credentials.substring(0, j), + password: credentials.substring(j + 1) + }; + } else { + return { + username: credentials, + password: '' + }; + } + } + } + // Reply with session credentials unless we used them already. // In that case we need to show a login dialog again because // they seem invalid. diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index dcb1cc9210d..45b8835107e 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -962,6 +962,14 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { || (process.env['https_proxy'] || process.env['HTTPS_PROXY'] || process.env['http_proxy'] || process.env['HTTP_PROXY'] || '').trim() // Not standardized. || undefined; + if (newHttpProxy?.indexOf('@') !== -1) { + const uri = URI.parse(newHttpProxy!); + const i = uri.authority.indexOf('@'); + if (i !== -1) { + newHttpProxy = uri.with({ authority: uri.authority.substring(i + 1) }) + .toString(); + } + } if (newHttpProxy?.endsWith('/')) { newHttpProxy = newHttpProxy.substr(0, newHttpProxy.length - 1); } @@ -976,13 +984,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { const proxyBypassRules = newNoProxy ? `${newNoProxy},` : ''; this.logService.trace(`Setting proxy to '${proxyRules}', bypassing '${proxyBypassRules}'`); this._win.webContents.session.setProxy({ proxyRules, proxyBypassRules, pacScript: '' }); - type appWithProxySupport = Electron.App & { - setProxy(config: Electron.Config): Promise; - resolveProxy(url: string): Promise; - }; - if (typeof (app as appWithProxySupport).setProxy === 'function') { - (app as appWithProxySupport).setProxy({ proxyRules, proxyBypassRules, pacScript: '' }); - } + app.setProxy({ proxyRules, proxyBypassRules, pacScript: '' }); } } } From 603b0ee03b82cc77f90f56a5e0ecfbeee3213de1 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 8 Jul 2024 10:05:31 +0200 Subject: [PATCH 0317/2222] Don't listen on menu changed, method 2 (#219964) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Don't listen on menu changed, method 2 This time, instead of only updating the tree elements who's actions are visible (selected, focused, focusing), update all tree items that the context change applies to, as before. However, the context key change event is now per tree renderer, not per menu. Fixes #213145 * Implement feedback * 💄 --- .../browser/menuEntryActionViewItem.ts | 4 +- src/vs/platform/actions/common/actions.ts | 17 ++++ src/vs/platform/actions/common/menuService.ts | 72 +++++++++++----- .../workbench/browser/parts/views/treeView.ts | 82 +++++++++++++------ .../test/browser/workbenchTestServices.ts | 10 ++- 5 files changed, 135 insertions(+), 50 deletions(-) diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 68380cddacf..7d34c837e52 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -33,8 +33,8 @@ import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles' import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; -export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string): void { - const groups = menu.getActions(options); +export function createAndFillInContextMenuActions(menu: IMenu | [string, Array][], options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string): void { + const groups = (Array.isArray(menu) ? menu : menu.getActions(options)); const modifierKeyEmitter = ModifierKeyEmitter.getInstance(); const useAlternativeActions = modifierKeyEmitter.keyStatus.altKey || ((isWindows || isLinux) && modifierKeyEmitter.keyStatus.shiftKey); fillInActions(groups, target, useAlternativeActions, primaryGroup ? actionGroup => actionGroup === primaryGroup : actionGroup => actionGroup === 'navigation'); diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 9d773b47a56..8d087d8c9d9 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -275,6 +275,11 @@ export interface IMenu extends IDisposable { getActions(options?: IMenuActionOptions): [string, Array][]; } +export interface IMenuData { + contexts: ReadonlySet; + actions: [string, Array][]; +} + export const IMenuService = createDecorator('menuService'); export interface IMenuCreateOptions { @@ -287,6 +292,8 @@ export interface IMenuService { readonly _serviceBrand: undefined; /** + * Consider using getMenuActions if you don't need to listen to events. + * * Create a new menu for the given menu identifier. A menu sends events when it's entries * have changed (placement, enablement, checked-state). By default it does not send events for * submenu entries. That is more expensive and must be explicitly enabled with the @@ -294,6 +301,16 @@ export interface IMenuService { */ createMenu(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuCreateOptions): IMenu; + /** + * Creates a new menu, gets the actions, and then disposes of the menu. + */ + getMenuActions(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuActionOptions): [string, Array][]; + + /** + * Gets the names of the contexts that this menu listens on. + */ + getMenuContexts(id: MenuId): ReadonlySet; + /** * Reset **all** menu item hidden states. */ diff --git a/src/vs/platform/actions/common/menuService.ts b/src/vs/platform/actions/common/menuService.ts index 0b2f255d9be..91d9ffdc49d 100644 --- a/src/vs/platform/actions/common/menuService.ts +++ b/src/vs/platform/actions/common/menuService.ts @@ -34,6 +34,18 @@ export class MenuService implements IMenuService { return new MenuImpl(id, this._hiddenStates, { emitEventsForSubmenuChanges: false, eventDebounceDelay: 50, ...options }, this._commandService, this._keybindingService, contextKeyService); } + getMenuActions(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuActionOptions): [string, Array][] { + const menu = new MenuImpl(id, this._hiddenStates, { emitEventsForSubmenuChanges: false, eventDebounceDelay: 50, ...options }, this._commandService, this._keybindingService, contextKeyService); + const actions = menu.getActions(options); + menu.dispose(); + return actions; + } + + getMenuContexts(id: MenuId): ReadonlySet { + const menuInfo = new MenuInfoSnapshot(id, false); + return new Set([...menuInfo.structureContextKeys, ...menuInfo.preconditionContextKeys, ...menuInfo.toggledContextKeys]); + } + resetHiddenStates(ids?: MenuId[]): void { this._hiddenStates.reset(ids); } @@ -152,20 +164,15 @@ class PersistedMenuHideState { type MenuItemGroup = [string, Array]; -class MenuInfo { - - private _menuGroups: MenuItemGroup[] = []; +class MenuInfoSnapshot { + protected _menuGroups: MenuItemGroup[] = []; private _structureContextKeys: Set = new Set(); private _preconditionContextKeys: Set = new Set(); private _toggledContextKeys: Set = new Set(); constructor( - private readonly _id: MenuId, - private readonly _hiddenStates: PersistedMenuHideState, - private readonly _collectContextKeysForSubmenus: boolean, - @ICommandService private readonly _commandService: ICommandService, - @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService + protected readonly _id: MenuId, + protected readonly _collectContextKeysForSubmenus: boolean, ) { this.refresh(); } @@ -190,10 +197,8 @@ class MenuInfo { this._preconditionContextKeys.clear(); this._toggledContextKeys.clear(); - const menuItems = MenuRegistry.getMenuItems(this._id); - + const menuItems = this._sort(MenuRegistry.getMenuItems(this._id)); let group: MenuItemGroup | undefined; - menuItems.sort(MenuInfo._compareMenuItems); for (const item of menuItems) { // group by groupId @@ -209,19 +214,24 @@ class MenuInfo { } } + protected _sort(menuItems: (IMenuItem | ISubmenuItem)[]) { + // no sorting needed in snapshot + return menuItems; + } + private _collectContextKeys(item: IMenuItem | ISubmenuItem): void { - MenuInfo._fillInKbExprKeys(item.when, this._structureContextKeys); + MenuInfoSnapshot._fillInKbExprKeys(item.when, this._structureContextKeys); if (isIMenuItem(item)) { // keep precondition keys for event if applicable if (item.command.precondition) { - MenuInfo._fillInKbExprKeys(item.command.precondition, this._preconditionContextKeys); + MenuInfoSnapshot._fillInKbExprKeys(item.command.precondition, this._preconditionContextKeys); } // keep toggled keys for event if applicable if (item.command.toggled) { const toggledExpression: ContextKeyExpression = (item.command.toggled as { condition: ContextKeyExpression }).condition || item.command.toggled; - MenuInfo._fillInKbExprKeys(toggledExpression, this._toggledContextKeys); + MenuInfoSnapshot._fillInKbExprKeys(toggledExpression, this._toggledContextKeys); } } else if (this._collectContextKeysForSubmenus) { @@ -231,6 +241,30 @@ class MenuInfo { } } + private static _fillInKbExprKeys(exp: ContextKeyExpression | undefined, set: Set): void { + if (exp) { + for (const key of exp.keys()) { + set.add(key); + } + } + } + +} + +class MenuInfo extends MenuInfoSnapshot { + + constructor( + _id: MenuId, + private readonly _hiddenStates: PersistedMenuHideState, + _collectContextKeysForSubmenus: boolean, + @ICommandService private readonly _commandService: ICommandService, + @IKeybindingService private readonly _keybindingService: IKeybindingService, + @IContextKeyService private readonly _contextKeyService: IContextKeyService + ) { + super(_id, _collectContextKeysForSubmenus); + this.refresh(); + } + createActionGroups(options: IMenuActionOptions | undefined): [string, Array][] { const result: [string, Array][] = []; @@ -267,12 +301,8 @@ class MenuInfo { return result; } - private static _fillInKbExprKeys(exp: ContextKeyExpression | undefined, set: Set): void { - if (exp) { - for (const key of exp.keys()) { - set.add(key); - } - } + protected override _sort(menuItems: (IMenuItem | ISubmenuItem)[]): (IMenuItem | ISubmenuItem)[] { + return menuItems.sort(MenuInfo._compareMenuItems); } private static _compareMenuItems(a: IMenuItem | ISubmenuItem, b: IMenuItem | ISubmenuItem): number { diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 7c1b3619944..858ed33ffb5 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -36,7 +36,7 @@ import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/plat import { Action2, IMenuService, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ContextKeyExpr, ContextKeyExpression, IContextKey, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { ContextKeyExpr, ContextKeyExpression, IContextKey, IContextKeyChangeEvent, IContextKeyService, RawContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { FileKind } from 'vs/platform/files/common/files'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -717,6 +717,9 @@ abstract class AbstractTreeView extends Disposable implements ITreeView { dnd: this.treeViewDnd, overrideStyles: getLocationBasedViewColors(this.viewLocation).listOverrideStyles }) as WorkbenchAsyncDataTree); + + this.treeDisposables.add(renderer.onDidChangeMenuContext(e => e.forEach(e => this.tree?.rerender(e)))); + this.treeDisposables.add(this.tree); treeMenus.setContextKeyService(this.tree.contextKeyService); aligner.tree = this.tree; @@ -1157,7 +1160,6 @@ class TreeDataSource implements IAsyncDataSource { } interface ITreeExplorerTemplateData { - readonly elementDisposable: DisposableStore; readonly container: HTMLElement; readonly resourceLabel: IResourceLabel; readonly icon: HTMLElement; @@ -1173,6 +1175,9 @@ class TreeRenderer extends Disposable implements ITreeRenderer = this._register(new Emitter()); readonly onDidChangeCheckboxState: Event = this._onDidChangeCheckboxState.event; + private _onDidChangeMenuContext: Emitter = this._register(new Emitter()); + readonly onDidChangeMenuContext: Event = this._onDidChangeMenuContext.event; + private _actionRunner: MultipleSelectionActionRunner | undefined; private _hoverDelegate: IHoverDelegate; private _hasCheckbox: boolean = false; @@ -1200,6 +1205,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer { this.updateCheckboxes(items); })); + this._register(this.contextKeyService.onDidChangeContext(e => this.onDidChangeContext(e))); } get templateId(): string { @@ -1221,7 +1227,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer, index: number, templateData: ITreeExplorerTemplateData): void { - templateData.elementDisposable.clear(); - const itemRenders = this._renderedElements.get(resource.element.handle) ?? []; const renderedIndex = itemRenders.findIndex(renderedItem => templateData === renderedItem.rendered); @@ -1513,7 +1531,6 @@ class TreeRenderer extends Disposable implements ITreeRenderer { + return new Map([ + ['view', this.id], + ['viewItem', element.contextValue] + ]); + } + + public getEntireMenuContexts(): ReadonlySet { + return this.menuService.getMenuContexts(this.getMenuId()); + } + + public getMenuId(): MenuId { + return MenuId.ViewItemContext; + } + + private getActions(menuId: MenuId, elements: ITreeItem[]): { primary: IAction[]; secondary: IAction[] } { if (!this.contextKeyService) { return { primary: [], secondary: [] }; } @@ -1668,16 +1706,14 @@ class TreeMenus implements IDisposable { let secondaryGroups: Map[] = []; for (let i = 0; i < elements.length; i++) { const element = elements[i]; - const contextKeyService = this.contextKeyService.createOverlay([ - ['view', this.id], - ['viewItem', element.contextValue] - ]); + const contextKeyService = this.contextKeyService.createOverlay(this.getElementOverlayContexts(element)); + + const menuData = this.menuService.getMenuActions(menuId, contextKeyService); - const menu = this.menuService.createMenu(menuId, contextKeyService); const primary: IAction[] = []; const secondary: IAction[] = []; - const result = { primary, secondary, menu }; - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, 'inline'); + const result = { primary, secondary }; + createAndFillInContextMenuActions(menuData, { shouldForwardArgs: true }, result, 'inline'); if (i === 0) { primaryGroups = this.createGroups(result.primary); secondaryGroups = this.createGroups(result.secondary); @@ -1685,12 +1721,6 @@ class TreeMenus implements IDisposable { this.filterNonUniversalActions(primaryGroups, result.primary); this.filterNonUniversalActions(secondaryGroups, result.secondary); } - if (listen && elements.length === 1) { - listen.add(menu.onDidChange(() => this._onDidChange.fire(element))); - listen.add(menu); - } else { - menu.dispose(); - } } return { primary: this.buildMenu(primaryGroups), secondary: this.buildMenu(secondaryGroups) }; diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index a451ea2afb5..82fed0c2dbc 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -40,7 +40,7 @@ import { ThemeIcon } from 'vs/base/common/themables'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; import { ITextResourceConfigurationService, ITextResourcePropertiesService } from 'vs/editor/common/services/textResourceConfiguration'; import { IPosition, Position as EditorPosition } from 'vs/editor/common/core/position'; -import { IMenuService, MenuId, IMenu, IMenuChangeEvent } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId, IMenu, IMenuChangeEvent, IMenuActionOptions, MenuItemAction, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { ContextKeyValue, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService, MockKeybindingService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; import { ITextBufferFactory, DefaultEndOfLine, EndOfLinePreference, ITextSnapshot } from 'vs/editor/common/model'; @@ -556,6 +556,14 @@ export class TestMenuService implements IMenuService { }; } + getMenuActions(id: MenuId, contextKeyService: IContextKeyService, options?: IMenuActionOptions): [string, Array][] { + throw new Error('Method not implemented.'); + } + + getMenuContexts(id: MenuId): ReadonlySet { + throw new Error('Method not implemented.'); + } + resetHiddenStates(): void { // nothing } From 19ff55e467b2d2f760550b84f658ed39656bd79b Mon Sep 17 00:00:00 2001 From: Ulugbek Abdullaev Date: Mon, 8 Jul 2024 11:27:19 +0200 Subject: [PATCH 0318/2222] zoomable hover: fix incorrect naming of object properties that resulted in that this code would set `can[Increase/Decrease]Verbosity` to undefined and break the feature --- src/vs/workbench/api/common/extHostTypes.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 92b968e001e..539f398c8d9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -1205,18 +1205,18 @@ export class Hover { @es5ClassCompat export class VerboseHover extends Hover { - public canIncreaseHover: boolean | undefined; - public canDecreaseHover: boolean | undefined; + public canIncreaseVerbosity: boolean | undefined; + public canDecreaseVerbosity: boolean | undefined; constructor( contents: vscode.MarkdownString | vscode.MarkedString | (vscode.MarkdownString | vscode.MarkedString)[], range?: Range, - canIncreaseHover?: boolean, - canDecreaseHover?: boolean, + canIncreaseVerbosity?: boolean, + canDecreaseVerbosity?: boolean, ) { super(contents, range); - this.canIncreaseHover = canIncreaseHover; - this.canDecreaseHover = canDecreaseHover; + this.canIncreaseVerbosity = canIncreaseVerbosity; + this.canDecreaseVerbosity = canDecreaseVerbosity; } } From 58c354acde1f5d371747cb8c794e5ca46226a079 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 8 Jul 2024 11:51:54 +0200 Subject: [PATCH 0319/2222] Adopt getMenuActions in the obvious places (#221003) --- .../contextmenu/browser/contextmenu.ts | 4 +- .../gotoError/browser/gotoErrorWidget.ts | 5 +- .../browser/menuEntryActionViewItem.ts | 55 ++++++++++++++++++- .../contextview/browser/contextMenuService.ts | 5 +- src/vs/workbench/browser/actions.ts | 5 +- .../parts/activitybar/activitybarPart.ts | 5 +- .../parts/auxiliarybar/auxiliaryBarPart.ts | 5 +- .../browser/parts/paneCompositePart.ts | 4 +- .../browser/parts/panel/panelPart.ts | 10 ++-- .../workbench/browser/parts/views/treeView.ts | 4 +- .../comments/browser/commentsTreeViewer.ts | 13 ++--- .../contrib/debug/browser/callStackView.ts | 5 +- .../contrib/debug/browser/debugToolBar.ts | 4 +- .../contrib/debug/browser/variablesView.ts | 36 +++++------- .../debug/browser/watchExpressionsView.ts | 6 +- .../extensions/browser/extensionsActions.ts | 4 +- .../contrib/issue/browser/issueFormService.ts | 9 +-- .../contrib/issue/browser/issueQuickAccess.ts | 9 +-- .../issue/electron-sandbox/issueService.ts | 6 +- .../notebookVariablesView.ts | 5 +- .../notebook/browser/diff/diffComponents.ts | 5 +- .../browser/commandsQuickAccess.ts | 7 +-- .../contrib/scm/browser/scmViewPane.ts | 8 +-- .../testing/browser/testingDecorations.ts | 14 ++--- .../testing/browser/testingExplorerView.ts | 31 ++++------- .../testing/browser/testingOutputPeek.ts | 15 ++--- .../contrib/timeline/browser/timelinePane.ts | 6 +- .../browser/userDataProfile.ts | 23 ++++---- .../browser/userDataProfileActions.ts | 5 +- 29 files changed, 151 insertions(+), 162 deletions(-) diff --git a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts index ddfaabbf496..650a15e2e36 100644 --- a/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts +++ b/src/vs/editor/contrib/contextmenu/browser/contextmenu.ts @@ -160,9 +160,7 @@ export class ContextMenuController implements IEditorContribution { const result: IAction[] = []; // get menu groups - const menu = this._menuService.createMenu(menuId, this._contextKeyService); - const groups = menu.getActions({ arg: model.uri }); - menu.dispose(); + const groups = this._menuService.getMenuActions(menuId, this._contextKeyService, { arg: model.uri }); // translate them into other actions for (const group of groups) { diff --git a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts index 00f716bb45f..cf0e5c7d6d0 100644 --- a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts @@ -308,10 +308,9 @@ export class MarkerNavigationWidget extends PeekViewWidget { this._disposables.add(this._actionbarWidget!.actionRunner.onWillRun(e => this.editor.focus())); const actions: IAction[] = []; - const menu = this._menuService.createMenu(MarkerNavigationWidget.TitleMenu, this._contextKeyService); - createAndFillInActionBarActions(menu, undefined, actions); + const menu = this._menuService.getMenuActions(MarkerNavigationWidget.TitleMenu, this._contextKeyService); + createAndFillInActionBarActions(menu, actions); this._actionbarWidget!.push(actions, { label: false, icon: true, index: 0 }); - menu.dispose(); } protected override _fillTitleIcon(container: HTMLElement): void { diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 7d34c837e52..58904af44aa 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -33,8 +33,23 @@ import { defaultSelectBoxStyles } from 'vs/platform/theme/browser/defaultStyles' import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { ResolvedKeybinding } from 'vs/base/common/keybindings'; -export function createAndFillInContextMenuActions(menu: IMenu | [string, Array][], options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string): void { - const groups = (Array.isArray(menu) ? menu : menu.getActions(options)); +export function createAndFillInContextMenuActions(menu: IMenu, options: IMenuActionOptions | undefined, target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string): void; +export function createAndFillInContextMenuActions(menu: [string, Array][], target: IAction[] | { primary: IAction[]; secondary: IAction[] }, primaryGroup?: string): void; +export function createAndFillInContextMenuActions(menu: IMenu | [string, Array][], optionsOrTarget: IMenuActionOptions | undefined | IAction[] | { primary: IAction[]; secondary: IAction[] }, targetOrPrimaryGroup?: IAction[] | { primary: IAction[]; secondary: IAction[] } | string, primaryGroupOrUndefined?: string): void { + let target: IAction[] | { primary: IAction[]; secondary: IAction[] }; + let primaryGroup: string | ((actionGroup: string) => boolean) | undefined; + let groups: [string, Array][]; + if (Array.isArray(menu)) { + groups = menu; + target = optionsOrTarget as IAction[] | { primary: IAction[]; secondary: IAction[] }; + primaryGroup = targetOrPrimaryGroup as string | undefined; + } else { + const options: IMenuActionOptions | undefined = optionsOrTarget as IMenuActionOptions | undefined; + groups = menu.getActions(options); + target = targetOrPrimaryGroup as IAction[] | { primary: IAction[]; secondary: IAction[] }; + primaryGroup = primaryGroupOrUndefined; + } + const modifierKeyEmitter = ModifierKeyEmitter.getInstance(); const useAlternativeActions = modifierKeyEmitter.keyStatus.altKey || ((isWindows || isLinux) && modifierKeyEmitter.keyStatus.shiftKey); fillInActions(groups, target, useAlternativeActions, primaryGroup ? actionGroup => actionGroup === primaryGroup : actionGroup => actionGroup === 'navigation'); @@ -47,8 +62,42 @@ export function createAndFillInActionBarActions( primaryGroup?: string | ((actionGroup: string) => boolean), shouldInlineSubmenu?: (action: SubmenuAction, group: string, groupSize: number) => boolean, useSeparatorsInPrimaryActions?: boolean +): void; +export function createAndFillInActionBarActions( + menu: [string, Array][], + target: IAction[] | { primary: IAction[]; secondary: IAction[] }, + primaryGroup?: string | ((actionGroup: string) => boolean), + shouldInlineSubmenu?: (action: SubmenuAction, group: string, groupSize: number) => boolean, + useSeparatorsInPrimaryActions?: boolean +): void; +export function createAndFillInActionBarActions( + menu: IMenu | [string, Array][], + optionsOrTarget: IMenuActionOptions | undefined | IAction[] | { primary: IAction[]; secondary: IAction[] }, + targetOrPrimaryGroup?: IAction[] | { primary: IAction[]; secondary: IAction[] } | string | ((actionGroup: string) => boolean), + primaryGroupOrShouldInlineSubmenu?: string | ((actionGroup: string) => boolean) | ((action: SubmenuAction, group: string, groupSize: number) => boolean), + shouldInlineSubmenuOrUseSeparatorsInPrimaryActions?: ((action: SubmenuAction, group: string, groupSize: number) => boolean) | boolean, + useSeparatorsInPrimaryActionsOrUndefined?: boolean ): void { - const groups = menu.getActions(options); + let target: IAction[] | { primary: IAction[]; secondary: IAction[] }; + let primaryGroup: string | ((actionGroup: string) => boolean) | undefined; + let shouldInlineSubmenu: ((action: SubmenuAction, group: string, groupSize: number) => boolean) | undefined; + let useSeparatorsInPrimaryActions: boolean | undefined; + let groups: [string, Array][]; + if (Array.isArray(menu)) { + groups = menu; + target = optionsOrTarget as IAction[] | { primary: IAction[]; secondary: IAction[] }; + primaryGroup = targetOrPrimaryGroup as string | ((actionGroup: string) => boolean) | undefined; + shouldInlineSubmenu = primaryGroupOrShouldInlineSubmenu as (action: SubmenuAction, group: string, groupSize: number) => boolean; + useSeparatorsInPrimaryActions = shouldInlineSubmenuOrUseSeparatorsInPrimaryActions as boolean | undefined; + } else { + const options: IMenuActionOptions | undefined = optionsOrTarget as IMenuActionOptions | undefined; + groups = menu.getActions(options); + target = targetOrPrimaryGroup as IAction[] | { primary: IAction[]; secondary: IAction[] }; + primaryGroup = primaryGroupOrShouldInlineSubmenu as string | ((actionGroup: string) => boolean) | undefined; + shouldInlineSubmenu = shouldInlineSubmenuOrUseSeparatorsInPrimaryActions as (action: SubmenuAction, group: string, groupSize: number) => boolean; + useSeparatorsInPrimaryActions = useSeparatorsInPrimaryActionsOrUndefined; + } + const isPrimaryAction = typeof primaryGroup === 'string' ? (actionGroup: string) => actionGroup === primaryGroup : primaryGroup; // Action bars handle alternative actions on their own so the alternative actions should be ignored diff --git a/src/vs/platform/contextview/browser/contextMenuService.ts b/src/vs/platform/contextview/browser/contextMenuService.ts index 907cf9449a8..f70e349945e 100644 --- a/src/vs/platform/contextview/browser/contextMenuService.ts +++ b/src/vs/platform/contextview/browser/contextMenuService.ts @@ -86,9 +86,8 @@ export namespace ContextMenuMenuDelegate { getActions: () => { const target: IAction[] = []; if (menuId) { - const menu = menuService.createMenu(menuId, contextKeyService ?? globalContextKeyService); - createAndFillInContextMenuActions(menu, menuActionOptions, target); - menu.dispose(); + const menu = menuService.getMenuActions(menuId, contextKeyService ?? globalContextKeyService, menuActionOptions); + createAndFillInContextMenuActions(menu, target); } if (!delegate.getActions) { return target; diff --git a/src/vs/workbench/browser/actions.ts b/src/vs/workbench/browser/actions.ts index 1e166016ed3..dbe7355c8c2 100644 --- a/src/vs/workbench/browser/actions.ts +++ b/src/vs/workbench/browser/actions.ts @@ -96,9 +96,8 @@ export class CompositeMenuActions extends Disposable { const actions: IAction[] = []; if (this.contextMenuId) { - const menu = this.menuService.createMenu(this.contextMenuId, this.contextKeyService); - createAndFillInActionBarActions(menu, this.options, { primary: [], secondary: actions }); - menu.dispose(); + const menu = this.menuService.getMenuActions(this.contextMenuId, this.contextKeyService, this.options); + createAndFillInActionBarActions(menu, { primary: [], secondary: actions }); } return actions; diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index 4610ddff2c3..97f3861079f 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -363,10 +363,9 @@ export class ActivityBarCompositeBar extends PaneCompositeBar { } getActivityBarContextMenuActions(): IAction[] { - const activityBarPositionMenu = this.menuService.createMenu(MenuId.ActivityBarPositionMenu, this.contextKeyService); + const activityBarPositionMenu = this.menuService.getMenuActions(MenuId.ActivityBarPositionMenu, this.contextKeyService, { shouldForwardArgs: true, renderShortTitle: true }); const positionActions: IAction[] = []; - createAndFillInContextMenuActions(activityBarPositionMenu, { shouldForwardArgs: true, renderShortTitle: true }, { primary: [], secondary: positionActions }); - activityBarPositionMenu.dispose(); + createAndFillInContextMenuActions(activityBarPositionMenu, { primary: [], secondary: positionActions }); return [ new SubmenuAction('workbench.action.panel.position', localize('activity bar position', "Activity Bar Position"), positionActions), toAction({ id: ToggleSidebarPositionAction.ID, label: ToggleSidebarPositionAction.getLabel(this.layoutService), run: () => this.instantiationService.invokeFunction(accessor => new ToggleSidebarPositionAction().run(accessor)) }) diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts index 16c40e836f2..3a2422d4c99 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -191,10 +191,9 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { actions.push(viewsSubmenuAction); } - const activityBarPositionMenu = this.menuService.createMenu(MenuId.ActivityBarPositionMenu, this.contextKeyService); + const activityBarPositionMenu = this.menuService.getMenuActions(MenuId.ActivityBarPositionMenu, this.contextKeyService, { shouldForwardArgs: true, renderShortTitle: true }); const positionActions: IAction[] = []; - createAndFillInContextMenuActions(activityBarPositionMenu, { shouldForwardArgs: true, renderShortTitle: true }, { primary: [], secondary: positionActions }); - activityBarPositionMenu.dispose(); + createAndFillInContextMenuActions(activityBarPositionMenu, { primary: [], secondary: positionActions }); actions.push(...[ new Separator(), diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index 68e797d69e1..cf29cbed9f1 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -629,8 +629,8 @@ export abstract class AbstractPaneCompositePart extends CompositePart true); + const menu = this.menuService.getMenuActions(ViewsSubMenu, scopedContextKeyService, { shouldForwardArgs: true, renderShortTitle: true }); + createAndFillInActionBarActions(menu, { primary: viewsActions, secondary: [] }, () => true); disposables.dispose(); return viewsActions.length > 1 && viewsActions.some(a => a.enabled) ? new SubmenuAction('views', localize('views', "Views"), viewsActions) : undefined; } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index e8e8b49bce5..845274fa940 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -149,14 +149,12 @@ export class PanelPart extends AbstractPaneCompositePart { } private fillExtraContextMenuActions(actions: IAction[]): void { - const panelPositionMenu = this.menuService.createMenu(MenuId.PanelPositionMenu, this.contextKeyService); - const panelAlignMenu = this.menuService.createMenu(MenuId.PanelAlignmentMenu, this.contextKeyService); + const panelPositionMenu = this.menuService.getMenuActions(MenuId.PanelPositionMenu, this.contextKeyService, { shouldForwardArgs: true }); + const panelAlignMenu = this.menuService.getMenuActions(MenuId.PanelAlignmentMenu, this.contextKeyService, { shouldForwardArgs: true }); const positionActions: IAction[] = []; const alignActions: IAction[] = []; - createAndFillInContextMenuActions(panelPositionMenu, { shouldForwardArgs: true }, { primary: [], secondary: positionActions }); - createAndFillInContextMenuActions(panelAlignMenu, { shouldForwardArgs: true }, { primary: [], secondary: alignActions }); - panelAlignMenu.dispose(); - panelPositionMenu.dispose(); + createAndFillInContextMenuActions(panelPositionMenu, { primary: [], secondary: positionActions }); + createAndFillInContextMenuActions(panelAlignMenu, { primary: [], secondary: alignActions }); actions.push(...[ new Separator(), diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 858ed33ffb5..5b82b9f971b 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -1708,12 +1708,12 @@ class TreeMenus implements IDisposable { const element = elements[i]; const contextKeyService = this.contextKeyService.createOverlay(this.getElementOverlayContexts(element)); - const menuData = this.menuService.getMenuActions(menuId, contextKeyService); + const menuData = this.menuService.getMenuActions(menuId, contextKeyService, { shouldForwardArgs: true }); const primary: IAction[] = []; const secondary: IAction[] = []; const result = { primary, secondary }; - createAndFillInContextMenuActions(menuData, { shouldForwardArgs: true }, result, 'inline'); + createAndFillInContextMenuActions(menuData, result, 'inline'); if (i === 0) { primaryGroups = this.createGroups(result.primary); secondaryGroups = this.createGroups(result.secondary); diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index b527edf7bde..7f0dfde0e64 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -35,7 +35,7 @@ import { CommentsModel } from 'vs/workbench/contrib/comments/browser/commentsMod import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { ActionBar, IActionViewItemProvider } from 'vs/base/browser/ui/actionbar/actionbar'; import { createActionViewItem, createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { IMenu, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; +import { IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { IAction } from 'vs/base/common/actions'; import { MarshalledId } from 'vs/base/common/marshallingIds'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -142,9 +142,9 @@ export class CommentsMenus implements IDisposable { @IMenuService private readonly menuService: IMenuService ) { } - getResourceActions(element: CommentNode): { menu?: IMenu; actions: IAction[] } { + getResourceActions(element: CommentNode): { actions: IAction[] } { const actions = this.getActions(MenuId.CommentsViewThreadActions, element); - return { menu: actions.menu, actions: actions.primary }; + return { actions: actions.primary }; } getResourceContextActions(element: CommentNode): IAction[] { @@ -155,7 +155,7 @@ export class CommentsMenus implements IDisposable { this.contextKeyService = service; } - private getActions(menuId: MenuId, element: CommentNode): { menu?: IMenu; primary: IAction[]; secondary: IAction[] } { + private getActions(menuId: MenuId, element: CommentNode): { primary: IAction[]; secondary: IAction[] } { if (!this.contextKeyService) { return { primary: [], secondary: [] }; } @@ -168,12 +168,11 @@ export class CommentsMenus implements IDisposable { ]; const contextKeyService = this.contextKeyService.createOverlay(overlay); - const menu = this.menuService.createMenu(menuId, contextKeyService); + const menu = this.menuService.getMenuActions(menuId, contextKeyService, { shouldForwardArgs: true }); const primary: IAction[] = []; const secondary: IAction[] = []; const result = { primary, secondary, menu }; - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, 'inline'); - menu.dispose(); + createAndFillInContextMenuActions(menu, result, 'inline'); return result; } diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 24eba761682..6ee016a167c 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -465,9 +465,8 @@ export class CallStackView extends ViewPane { const secondary: IAction[] = []; const result = { primary, secondary }; const contextKeyService = this.contextKeyService.createOverlay(overlay); - const menu = this.menuService.createMenu(MenuId.DebugCallStackContext, contextKeyService); - createAndFillInContextMenuActions(menu, { arg: getContextForContributedActions(element), shouldForwardArgs: true }, result, 'inline'); - menu.dispose(); + const menu = this.menuService.getMenuActions(MenuId.DebugCallStackContext, contextKeyService, { arg: getContextForContributedActions(element), shouldForwardArgs: true }); + createAndFillInContextMenuActions(menu, result, 'inline'); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => result.secondary, diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 4bae5574e46..7711f4ccc48 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -351,9 +351,9 @@ export function createDisconnectMenuItemAction(action: MenuItemAction, disposabl const instantiationService = accessor.get(IInstantiationService); const contextMenuService = accessor.get(IContextMenuService); - const menu = menuService.createMenu(MenuId.DebugToolBarStop, contextKeyService); + const menu = menuService.getMenuActions(MenuId.DebugToolBarStop, contextKeyService, { shouldForwardArgs: true }); const secondary: IAction[] = []; - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, secondary); + createAndFillInActionBarActions(menu, secondary); if (!secondary.length) { return undefined; diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 78caf7f02e6..7adb71aa317 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -16,7 +16,7 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { FuzzyScore, createMatches } from 'vs/base/common/filters'; -import { DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { ThemeIcon } from 'vs/base/common/themables'; import { localize } from 'vs/nls'; import { createAndFillInContextMenuActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; @@ -246,22 +246,16 @@ export async function openContextMenuForVariableTreeElement(parentContextKeyServ return; } - const toDispose = new DisposableStore(); + const contextKeyService = await getContextForVariableMenuWithDataAccess(parentContextKeyService, variable); + const context: IVariablesContext = getVariablesContext(variable); + const menu = menuService.getMenuActions(menuId, contextKeyService, { arg: context, shouldForwardArgs: false }); - try { - const contextKeyService = await getContextForVariableMenuWithDataAccess(parentContextKeyService, variable); - const menu = toDispose.add(menuService.createMenu(menuId, contextKeyService)); - - const context: IVariablesContext = getVariablesContext(variable); - const secondary: IAction[] = []; - createAndFillInContextMenuActions(menu, { arg: context, shouldForwardArgs: false }, { primary: [], secondary }, 'inline'); - contextMenuService.showContextMenu({ - getAnchor: () => e.anchor, - getActions: () => secondary - }); - } finally { - toDispose.dispose(); - } + const secondary: IAction[] = []; + createAndFillInContextMenuActions(menu, { primary: [], secondary }, 'inline'); + contextMenuService.showContextMenu({ + getAnchor: () => e.anchor, + getActions: () => secondary + }); } const getVariablesContext = (variable: Variable): IVariablesContext => ({ @@ -500,11 +494,11 @@ export class VisualizedVariableRenderer extends AbstractExpressionsRenderer { protected override renderActionBar(actionBar: ActionBar, expression: IExpression, _data: IExpressionTemplateData) { const viz = expression as VisualizedExpression; const contextKeyService = viz.original ? getContextForVariableMenuBase(this.contextKeyService, viz.original) : this.contextKeyService; - const menu = this.menuService.createMenu(MenuId.DebugVariablesContext, contextKeyService); + const context = viz.original ? getVariablesContext(viz.original) : undefined; + const menu = this.menuService.getMenuActions(MenuId.DebugVariablesContext, contextKeyService, { arg: context, shouldForwardArgs: false }); const primary: IAction[] = []; - const context = viz.original ? getVariablesContext(viz.original) : undefined; - createAndFillInContextMenuActions(menu, { arg: context, shouldForwardArgs: false }, { primary, secondary: [] }, 'inline'); + createAndFillInContextMenuActions(menu, { primary, secondary: [] }, 'inline'); if (viz.original) { const action = new Action('debugViz', localize('removeVisualizer', 'Remove Visualizer'), ThemeIcon.asClassName(Codicon.eye), true, () => this.debugService.getViewModel().setVisualizedExpression(viz.original!, undefined)); @@ -583,11 +577,11 @@ export class VariablesRenderer extends AbstractExpressionsRenderer { protected override renderActionBar(actionBar: ActionBar, expression: IExpression, data: IExpressionTemplateData) { const variable = expression as Variable; const contextKeyService = getContextForVariableMenuBase(this.contextKeyService, variable); - const menu = this.menuService.createMenu(MenuId.DebugVariablesContext, contextKeyService); const primary: IAction[] = []; const context = getVariablesContext(variable); - createAndFillInContextMenuActions(menu, { arg: context, shouldForwardArgs: false }, { primary, secondary: [] }, 'inline'); + const menu = this.menuService.getMenuActions(MenuId.DebugVariablesContext, contextKeyService, { arg: context, shouldForwardArgs: false }); + createAndFillInContextMenuActions(menu, { primary, secondary: [] }, 'inline'); actionBar.clear(); actionBar.context = context; diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index 07f6986534d..22014a4ad96 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -373,11 +373,11 @@ export class WatchExpressionsRenderer extends AbstractExpressionsRenderer { protected override renderActionBar(actionBar: ActionBar, expression: IExpression) { const contextKeyService = getContextForWatchExpressionMenu(this.contextKeyService, expression); - const menu = this.menuService.createMenu(MenuId.DebugWatchContext, contextKeyService); + const context = expression; + const menu = this.menuService.getMenuActions(MenuId.DebugWatchContext, contextKeyService, { arg: context, shouldForwardArgs: false }); const primary: IAction[] = []; - const context = expression; - createAndFillInContextMenuActions(menu, { arg: context, shouldForwardArgs: false }, { primary, secondary: [] }, 'inline'); + createAndFillInContextMenuActions(menu, { primary, secondary: [] }, 'inline'); actionBar.clear(); actionBar.context = context; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 3ab22820d49..3f7e240ced1 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -1182,9 +1182,7 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n cksOverlay.push(['isActiveLanguagePackExtension', extension.gallery && language === getLocale(extension.gallery)]); } - const menu = menuService.createMenu(MenuId.ExtensionContext, contextKeyService.createOverlay(cksOverlay)); - const actionsGroups = menu.getActions({ shouldForwardArgs: true }); - menu.dispose(); + const actionsGroups = menuService.getMenuActions(MenuId.ExtensionContext, contextKeyService.createOverlay(cksOverlay), { shouldForwardArgs: true }); return actionsGroups; }); } diff --git a/src/vs/workbench/contrib/issue/browser/issueFormService.ts b/src/vs/workbench/contrib/issue/browser/issueFormService.ts index ca24026d161..1ac7d114454 100644 --- a/src/vs/workbench/contrib/issue/browser/issueFormService.ts +++ b/src/vs/workbench/contrib/issue/browser/issueFormService.ts @@ -40,11 +40,10 @@ export class IssueFormService implements IIssueMainService { // listen for messages from the main window mainWindow.addEventListener('message', async (event) => { if (event.data && event.data.sendChannel === 'vscode:triggerReporterMenu') { - // creates menu from contributed - const menu = this.menuService.createMenu(MenuId.IssueReporter, this.contextKeyService); + // gets menu actions from contributed + const actions = this.menuService.getMenuActions(MenuId.IssueReporter, this.contextKeyService, { renderShortTitle: true }).flatMap(entry => entry[1]); - // render menu and dispose - const actions = menu.getActions({ renderShortTitle: true }).flatMap(entry => entry[1]); + // render menu for (const action of actions) { try { if (action.item && 'source' in action.item && action.item.source?.id === event.data.extensionId) { @@ -61,8 +60,6 @@ export class IssueFormService implements IIssueMainService { const replyChannel = `vscode:triggerReporterMenuResponse`; mainWindow.postMessage({ replyChannel }, '*'); } - - menu.dispose(); } }); diff --git a/src/vs/workbench/contrib/issue/browser/issueQuickAccess.ts b/src/vs/workbench/contrib/issue/browser/issueQuickAccess.ts index 61ca99ac1c2..a131f19d865 100644 --- a/src/vs/workbench/contrib/issue/browser/issueQuickAccess.ts +++ b/src/vs/workbench/contrib/issue/browser/issueQuickAccess.ts @@ -65,13 +65,8 @@ export class IssueQuickAccess extends PickerQuickAccessProvider entry[1]); - - menu.dispose(); + // gets menu actions from contributed + const actions = this.menuService.getMenuActions(MenuId.IssueReporter, this.contextKeyService, { renderShortTitle: true }).flatMap(entry => entry[1]); // create picks from contributed menu actions.forEach(action => { diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts index 9986f5065f0..651dfa6c509 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueService.ts @@ -41,11 +41,10 @@ export class NativeIssueService implements IWorkbenchIssueService { ipcRenderer.on('vscode:triggerReporterMenu', async (event, arg) => { const extensionId = arg.extensionId; - // creates menu from contributed - const menu = this.menuService.createMenu(MenuId.IssueReporter, this.contextKeyService); + // gets menu from contributed + const actions = this.menuService.getMenuActions(MenuId.IssueReporter, this.contextKeyService, { renderShortTitle: true }).flatMap(entry => entry[1]); // render menu and dispose - const actions = menu.getActions({ renderShortTitle: true }).flatMap(entry => entry[1]); actions.forEach(async action => { try { if (action.item && 'source' in action.item && action.item.source?.id === extensionId) { @@ -61,7 +60,6 @@ export class NativeIssueService implements IWorkbenchIssueService { // send undefined to indicate no action was taken ipcRenderer.send(`vscode:triggerReporterMenuResponse:${extensionId}`, undefined); } - menu.dispose(); }); } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index c05cdaae7f6..5ca97b08f95 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -128,9 +128,8 @@ export class NotebookVariablesView extends ViewPane { [CONTEXT_VARIABLE_LANGUAGE.key, element.language], [CONTEXT_VARIABLE_EXTENSIONID.key, element.extensionId] ]); - const menu = this.menuService.createMenu(MenuId.NotebookVariablesContext, overlayedContext); - createAndFillInContextMenuActions(menu, { arg, shouldForwardArgs: true }, actions); - menu.dispose(); + const menu = this.menuService.getMenuActions(MenuId.NotebookVariablesContext, overlayedContext, { arg, shouldForwardArgs: true }); + createAndFillInContextMenuActions(menu, actions); this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => actions diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 59a4d27a8e2..263d08b0c14 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -1544,11 +1544,10 @@ export class ModifiedElement extends AbstractElementRenderer { } })); - const menu = this.menuService.createMenu(MenuId.NotebookDiffCellInputTitle, scopedContextKeyService); + const menu = this.menuService.getMenuActions(MenuId.NotebookDiffCellInputTitle, scopedContextKeyService, { shouldForwardArgs: true }); const actions: IAction[] = []; - createAndFillInActionBarActions(menu, { shouldForwardArgs: true }, actions); + createAndFillInActionBarActions(menu, actions); this._toolbar.setActions(actions); - menu.dispose(); } private async _initializeSourceDiffEditor() { diff --git a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts index 17b7b7ac490..2cb85b71dab 100644 --- a/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts +++ b/src/vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts @@ -214,8 +214,8 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce private getGlobalCommandPicks(): ICommandQuickPick[] { const globalCommandPicks: ICommandQuickPick[] = []; const scopedContextKeyService = this.editorService.activeEditorPane?.scopedContextKeyService || this.editorGroupService.activeGroup.scopedContextKeyService; - const globalCommandsMenu = this.menuService.createMenu(MenuId.CommandPalette, scopedContextKeyService); - const globalCommandsMenuActions = globalCommandsMenu.getActions() + const globalCommandsMenu = this.menuService.getMenuActions(MenuId.CommandPalette, scopedContextKeyService); + const globalCommandsMenuActions = globalCommandsMenu .reduce((r, [, actions]) => [...r, ...actions], >[]) .filter(action => action instanceof MenuItemAction && action.enabled) as MenuItemAction[]; @@ -251,9 +251,6 @@ export class CommandsQuickAccessProvider extends AbstractEditorCommandsQuickAcce }); } - // Cleanup - globalCommandsMenu.dispose(); - return globalCommandPicks; } } diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index aa0e50ccf49..0273ba76ec5 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -3322,16 +3322,14 @@ export class SCMViewPane extends ViewPane { private onListContextMenu(e: ITreeContextMenuEvent): void { if (!e.element) { - const menu = this.menuService.createMenu(Menus.ViewSort, this.contextKeyService); + const menu = this.menuService.getMenuActions(Menus.ViewSort, this.contextKeyService); const actions: IAction[] = []; - createAndFillInContextMenuActions(menu, undefined, actions); + createAndFillInContextMenuActions(menu, actions); return this.contextMenuService.showContextMenu({ getAnchor: () => e.anchor, getActions: () => actions, - onHide: () => { - menu.dispose(); - } + onHide: () => { } }); } diff --git a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts index dd48c16b941..80f6674801a 100644 --- a/src/vs/workbench/contrib/testing/browser/testingDecorations.ts +++ b/src/vs/workbench/contrib/testing/browser/testingDecorations.ts @@ -880,16 +880,12 @@ abstract class RunTestDecoration { private getContributedTestActions(test: InternalTestItem, capabilities: number): IAction[] { const contextOverlay = this.contextKeyService.createOverlay(getTestItemContextOverlay(test, capabilities)); - const menu = this.menuService.createMenu(MenuId.TestItemGutter, contextOverlay); - try { - const target: IAction[] = []; - const arg = getContextForTestItem(this.testService.collection, test.item.extId); - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true, arg }, target); - return target; - } finally { - menu.dispose(); - } + const target: IAction[] = []; + const arg = getContextForTestItem(this.testService.collection, test.item.extId); + const menu = this.menuService.getMenuActions(MenuId.TestItemGutter, contextOverlay, { shouldForwardArgs: true, arg }); + createAndFillInContextMenuActions(menu, target); + return target; } } diff --git a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts index d999c5e3301..7bf8db331eb 100644 --- a/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts +++ b/src/vs/workbench/contrib/testing/browser/testingExplorerView.ts @@ -365,16 +365,10 @@ export class TestingExplorerView extends ViewPane { contextKeys.push(['testing.profile.context.group', 'coverage']); } const key = this.contextKeyService.createOverlay(contextKeys); - const menu = this.menuService.createMenu(MenuId.TestProfilesContext, key); + const menu = this.menuService.getMenuActions(MenuId.TestProfilesContext, key); // fill if there are any actions - try { - createAndFillInContextMenuActions(menu, undefined, menuActions); - } finally { - menu.dispose(); - } - - + createAndFillInContextMenuActions(menu, menuActions); const postActions: IAction[] = []; if (profileActions.length > 1) { @@ -1567,20 +1561,17 @@ const getActionableElementActions = ( } const contextOverlay = contextKeyService.createOverlay(contextKeys); - const menu = menuService.createMenu(MenuId.TestItem, contextOverlay); + const menu = menuService.getMenuActions(MenuId.TestItem, contextOverlay, { + shouldForwardArgs: true, + }); - try { - const primary: IAction[] = []; - const secondary: IAction[] = []; - const result = { primary, secondary }; - createAndFillInActionBarActions(menu, { - shouldForwardArgs: true, - }, result, 'inline'); + const primary: IAction[] = []; + const secondary: IAction[] = []; + const result = { primary, secondary }; + createAndFillInActionBarActions(menu, result, 'inline'); + + return { actions: result, contextOverlay }; - return { actions: result, contextOverlay }; - } finally { - menu.dispose(); - } }; registerThemingParticipant((theme, collector) => { diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 6362d6acdf9..ec21581a9a6 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -1102,10 +1102,9 @@ class TestResultsPeek extends PeekViewWidget { super._fillHead(container); const actions: IAction[] = []; - const menu = this.menuService.createMenu(MenuId.TestPeekTitle, this.contextKeyService); - createAndFillInActionBarActions(menu, undefined, actions); + const menu = this.menuService.getMenuActions(MenuId.TestPeekTitle, this.contextKeyService); + createAndFillInActionBarActions(menu, actions); this._actionbarWidget!.push(actions, { label: false, icon: true, index: 0 }); - menu.dispose(); } protected override _fillBody(containerElement: HTMLElement): void { @@ -2558,13 +2557,9 @@ class TreeActionsProvider { const contextOverlay = this.contextKeyService.createOverlay(contextKeys); const result = { primary, secondary }; - const menu = this.menuService.createMenu(id, contextOverlay); - try { - createAndFillInActionBarActions(menu, { arg: element.context }, result, 'inline'); - return result; - } finally { - menu.dispose(); - } + const menu = this.menuService.getMenuActions(id, contextOverlay, { arg: element.context }); + createAndFillInActionBarActions(menu, result, 'inline'); + return result; } } diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index 6ca11ce3e4e..41c6e84efe7 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -1315,13 +1315,11 @@ class TimelinePaneCommands extends Disposable { [context.key, context.value], ]); - const menu = this.menuService.createMenu(menuId, contextKeyService); + const menu = this.menuService.getMenuActions(menuId, contextKeyService, { shouldForwardArgs: true }); const primary: IAction[] = []; const secondary: IAction[] = []; const result = { primary, secondary }; - createAndFillInContextMenuActions(menu, { shouldForwardArgs: true }, result, 'inline'); - - menu.dispose(); + createAndFillInContextMenuActions(menu, result, 'inline'); return result; } diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts index caa0aaa3168..29d96138089 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfile.ts @@ -285,19 +285,16 @@ export class UserDataProfilesWorkbenchContribution extends Disposable implements async run(accessor: ServicesAccessor) { const quickInputService = accessor.get(IQuickInputService); const menuService = accessor.get(IMenuService); - const menu = menuService.createMenu(ProfilesMenu, accessor.get(IContextKeyService)); - const actions = menu.getActions().find(([group]) => group === '0_profiles')?.[1] ?? []; - try { - const result = await quickInputService.pick(actions.map(action => ({ - action, - label: action.checked ? `$(check) ${action.label}` : action.label, - })), { - placeHolder: localize('selectProfile', "Select Profile") - }); - await result?.action.run(); - } finally { - menu.dispose(); - } + const menu = menuService.getMenuActions(ProfilesMenu, accessor.get(IContextKeyService)); + const actions = menu.find(([group]) => group === '0_profiles')?.[1] ?? []; + const result = await quickInputService.pick(actions.map(action => ({ + action, + label: action.checked ? `$(check) ${action.label}` : action.label, + })), { + placeHolder: localize('selectProfile', "Select Profile") + }); + await result?.action.run(); + } }); } diff --git a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.ts b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.ts index a9b85a44e65..c19f31930f8 100644 --- a/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.ts +++ b/src/vs/workbench/contrib/userDataProfile/browser/userDataProfileActions.ts @@ -120,10 +120,9 @@ registerAction2(class ManageProfilesAction extends Action2 { const contextKeyService = accessor.get(IContextKeyService); const commandService = accessor.get(ICommandService); - const menu = menuService.createMenu(ProfilesMenu, contextKeyService); + const menu = menuService.getMenuActions(ProfilesMenu, contextKeyService); const actions: IAction[] = []; - createAndFillInActionBarActions(menu, undefined, actions); - menu.dispose(); + createAndFillInActionBarActions(menu, actions); if (actions.length) { const picks: QuickPickItem[] = actions.map(action => { From 6f18713cbef1cd74f4d3aa9dba3b2404922632e6 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 8 Jul 2024 12:48:53 +0200 Subject: [PATCH 0320/2222] Web Extensions no longer work in dev mode (fix #220171) (#221030) The web worker is now using `importScript` directive in a data URL which is no longer the same `script-src` as the iframe when running built where we use `webEndpointUrlTemplate`. This breaks `vscode-test-web` that runs from `localhost` so we allow `http://localhost` --- .../extensions/worker/webWorkerExtensionHostIframe.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html index aded7faefeb..52045f22ae4 100644 --- a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html +++ b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html @@ -4,7 +4,7 @@ From eb025312da6410db6a4d7fa8aaaab315d3ddc5c4 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 8 Jul 2024 11:36:01 +0200 Subject: [PATCH 0321/2222] Fixes #221005 --- src/vs/editor/contrib/codelens/browser/codeLensCache.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts index 47fc3260b1f..f7666b8c246 100644 --- a/src/vs/editor/contrib/codelens/browser/codeLensCache.ts +++ b/src/vs/editor/contrib/codelens/browser/codeLensCache.ts @@ -61,10 +61,9 @@ export class CodeLensCache implements ICodeLensCache { this._deserialize(raw); // store lens data on shutdown - Event.once(storageService.onWillSaveState)(e => { - if (e.reason === WillSaveStateReason.SHUTDOWN) { - storageService.store(key, this._serialize(), StorageScope.WORKSPACE, StorageTarget.MACHINE); - } + const onWillSaveStateBecauseOfShutdown = Event.filter(storageService.onWillSaveState, e => e.reason === WillSaveStateReason.SHUTDOWN); + Event.once(onWillSaveStateBecauseOfShutdown)(e => { + storageService.store(key, this._serialize(), StorageScope.WORKSPACE, StorageTarget.MACHINE); }); } From f46e9223f6850ba07f0081e177b0e5fe903426fe Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Mon, 8 Jul 2024 12:33:31 +0200 Subject: [PATCH 0322/2222] Fixes #216824 --- src/vs/editor/browser/stableEditorScroll.ts | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/vs/editor/browser/stableEditorScroll.ts b/src/vs/editor/browser/stableEditorScroll.ts index 986e18ac6c0..4d7539435cd 100644 --- a/src/vs/editor/browser/stableEditorScroll.ts +++ b/src/vs/editor/browser/stableEditorScroll.ts @@ -20,6 +20,16 @@ export class StableEditorScrollState { const visibleRanges = editor.getVisibleRanges(); if (visibleRanges.length > 0) { visiblePosition = visibleRanges[0].getStartPosition(); + + const cursorPos = editor.getPosition(); + if (cursorPos) { + const isVisible = visibleRanges.some(range => range.containsPosition(cursorPos)); + if (isVisible) { + // Keep cursor pos fixed if it is visible + visiblePosition = cursorPos; + } + } + const visiblePositionScrollTop = editor.getTopForPosition(visiblePosition.lineNumber, visiblePosition.column); visiblePositionScrollDelta = editor.getScrollTop() - visiblePositionScrollTop; } From 865269335ead330ddc070ff9d7ba6f7f6ff393c6 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 8 Jul 2024 12:56:52 +0200 Subject: [PATCH 0323/2222] chore/ESM - use defaut-import for electron modules that make it into unit-test (#221035) --- .../base/parts/ipc/electron-main/ipcMain.ts | 20 +++--- .../electron-main/dialogMainService.ts | 72 +++++++++---------- .../electron-main/lifecycleMainService.ts | 36 +++++----- .../theme/electron-main/themeMainService.ts | 32 ++++----- .../platform/window/electron-main/window.ts | 10 +-- .../windows/electron-main/windowImpl.ts | 54 +++++++------- .../platform/windows/electron-main/windows.ts | 18 ++--- .../electron-main/windowsStateHandler.ts | 16 ++--- .../workspacesManagementMainService.ts | 4 +- 9 files changed, 131 insertions(+), 131 deletions(-) diff --git a/src/vs/base/parts/ipc/electron-main/ipcMain.ts b/src/vs/base/parts/ipc/electron-main/ipcMain.ts index c72b6e8e644..a0bf9a1603f 100644 --- a/src/vs/base/parts/ipc/electron-main/ipcMain.ts +++ b/src/vs/base/parts/ipc/electron-main/ipcMain.ts @@ -3,12 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ipcMain as unsafeIpcMain, IpcMainEvent, IpcMainInvokeEvent } from 'electron'; +import electron from 'electron'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Event } from 'vs/base/common/event'; import { VSCODE_AUTHORITY } from 'vs/base/common/network'; -type ipcMainListener = (event: IpcMainEvent, ...args: any[]) => void; +type ipcMainListener = (event: electron.IpcMainEvent, ...args: any[]) => void; class ValidatedIpcMain implements Event.NodeEventEmitter { @@ -25,7 +25,7 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { // Remember the wrapped listener so that later we can // properly implement `removeListener`. - const wrappedListener = (event: IpcMainEvent, ...args: any[]) => { + const wrappedListener = (event: electron.IpcMainEvent, ...args: any[]) => { if (this.validateEvent(channel, event)) { listener(event, ...args); } @@ -33,7 +33,7 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { this.mapListenerToWrapper.set(listener, wrappedListener); - unsafeIpcMain.on(channel, wrappedListener); + electron.ipcMain.on(channel, wrappedListener); return this; } @@ -43,7 +43,7 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { * only the next time a message is sent to `channel`, after which it is removed. */ once(channel: string, listener: ipcMainListener): this { - unsafeIpcMain.once(channel, (event: IpcMainEvent, ...args: any[]) => { + electron.ipcMain.once(channel, (event: electron.IpcMainEvent, ...args: any[]) => { if (this.validateEvent(channel, event)) { listener(event, ...args); } @@ -68,8 +68,8 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { * are serialized and only the `message` property from the original error is * provided to the renderer process. Please refer to #24427 for details. */ - handle(channel: string, listener: (event: IpcMainInvokeEvent, ...args: any[]) => Promise): this { - unsafeIpcMain.handle(channel, (event: IpcMainInvokeEvent, ...args: any[]) => { + handle(channel: string, listener: (event: electron.IpcMainInvokeEvent, ...args: any[]) => Promise): this { + electron.ipcMain.handle(channel, (event: electron.IpcMainInvokeEvent, ...args: any[]) => { if (this.validateEvent(channel, event)) { return listener(event, ...args); } @@ -84,7 +84,7 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { * Removes any handler for `channel`, if present. */ removeHandler(channel: string): this { - unsafeIpcMain.removeHandler(channel); + electron.ipcMain.removeHandler(channel); return this; } @@ -96,14 +96,14 @@ class ValidatedIpcMain implements Event.NodeEventEmitter { removeListener(channel: string, listener: ipcMainListener): this { const wrappedListener = this.mapListenerToWrapper.get(listener); if (wrappedListener) { - unsafeIpcMain.removeListener(channel, wrappedListener); + electron.ipcMain.removeListener(channel, wrappedListener); this.mapListenerToWrapper.delete(listener); } return this; } - private validateEvent(channel: string, event: IpcMainEvent | IpcMainInvokeEvent): boolean { + private validateEvent(channel: string, event: electron.IpcMainEvent | electron.IpcMainInvokeEvent): boolean { if (!channel || !channel.startsWith('vscode:')) { onUnexpectedError(`Refused to handle ipcMain event for channel '${channel}' because the channel is unknown.`); return false; // unexpected channel diff --git a/src/vs/platform/dialogs/electron-main/dialogMainService.ts b/src/vs/platform/dialogs/electron-main/dialogMainService.ts index bc1230a48ea..2fffe78c5fb 100644 --- a/src/vs/platform/dialogs/electron-main/dialogMainService.ts +++ b/src/vs/platform/dialogs/electron-main/dialogMainService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, dialog, FileFilter, MessageBoxOptions, MessageBoxReturnValue, OpenDialogOptions, OpenDialogReturnValue, SaveDialogOptions, SaveDialogReturnValue } from 'electron'; +import electron from 'electron'; import { Queue } from 'vs/base/common/async'; import { hash } from 'vs/base/common/hash'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -24,14 +24,14 @@ export interface IDialogMainService { readonly _serviceBrand: undefined; - pickFileFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise; - pickFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise; - pickFile(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise; - pickWorkspace(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise; + pickFileFolder(options: INativeOpenDialogOptions, window?: electron.BrowserWindow): Promise; + pickFolder(options: INativeOpenDialogOptions, window?: electron.BrowserWindow): Promise; + pickFile(options: INativeOpenDialogOptions, window?: electron.BrowserWindow): Promise; + pickWorkspace(options: INativeOpenDialogOptions, window?: electron.BrowserWindow): Promise; - showMessageBox(options: MessageBoxOptions, window?: BrowserWindow): Promise; - showSaveDialog(options: SaveDialogOptions, window?: BrowserWindow): Promise; - showOpenDialog(options: OpenDialogOptions, window?: BrowserWindow): Promise; + showMessageBox(options: electron.MessageBoxOptions, window?: electron.BrowserWindow): Promise; + showSaveDialog(options: electron.SaveDialogOptions, window?: electron.BrowserWindow): Promise; + showOpenDialog(options: electron.OpenDialogOptions, window?: electron.BrowserWindow): Promise; } interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions { @@ -40,7 +40,7 @@ interface IInternalNativeOpenDialogOptions extends INativeOpenDialogOptions { readonly title: string; readonly buttonLabel?: string; - readonly filters?: FileFilter[]; + readonly filters?: electron.FileFilter[]; } export class DialogMainService implements IDialogMainService { @@ -48,8 +48,8 @@ export class DialogMainService implements IDialogMainService { declare readonly _serviceBrand: undefined; private readonly windowFileDialogLocks = new Map>(); - private readonly windowDialogQueues = new Map>(); - private readonly noWindowDialogueQueue = new Queue(); + private readonly windowDialogQueues = new Map>(); + private readonly noWindowDialogueQueue = new Queue(); constructor( @ILogService private readonly logService: ILogService, @@ -57,19 +57,19 @@ export class DialogMainService implements IDialogMainService { ) { } - pickFileFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise { + pickFileFolder(options: INativeOpenDialogOptions, window?: electron.BrowserWindow): Promise { return this.doPick({ ...options, pickFolders: true, pickFiles: true, title: localize('open', "Open") }, window); } - pickFolder(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise { + pickFolder(options: INativeOpenDialogOptions, window?: electron.BrowserWindow): Promise { return this.doPick({ ...options, pickFolders: true, title: localize('openFolder', "Open Folder") }, window); } - pickFile(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise { + pickFile(options: INativeOpenDialogOptions, window?: electron.BrowserWindow): Promise { return this.doPick({ ...options, pickFiles: true, title: localize('openFile', "Open File") }, window); } - pickWorkspace(options: INativeOpenDialogOptions, window?: BrowserWindow): Promise { + pickWorkspace(options: INativeOpenDialogOptions, window?: electron.BrowserWindow): Promise { const title = localize('openWorkspaceTitle', "Open Workspace from File"); const buttonLabel = mnemonicButtonLabel(localize({ key: 'openWorkspace', comment: ['&& denotes a mnemonic'] }, "&&Open")); const filters = WORKSPACE_FILTER; @@ -77,10 +77,10 @@ export class DialogMainService implements IDialogMainService { return this.doPick({ ...options, pickFiles: true, title, filters, buttonLabel }, window); } - private async doPick(options: IInternalNativeOpenDialogOptions, window?: BrowserWindow): Promise { + private async doPick(options: IInternalNativeOpenDialogOptions, window?: electron.BrowserWindow): Promise { // Ensure dialog options - const dialogOptions: OpenDialogOptions = { + const dialogOptions: electron.OpenDialogOptions = { title: options.title, buttonLabel: options.buttonLabel, filters: options.filters, @@ -105,7 +105,7 @@ export class DialogMainService implements IDialogMainService { } // Show Dialog - const result = await this.showOpenDialog(dialogOptions, (window || BrowserWindow.getFocusedWindow()) ?? undefined); + const result = await this.showOpenDialog(dialogOptions, (window || electron.BrowserWindow.getFocusedWindow()) ?? undefined); if (result && result.filePaths && result.filePaths.length > 0) { return result.filePaths; } @@ -113,14 +113,14 @@ export class DialogMainService implements IDialogMainService { return undefined; } - private getWindowDialogQueue(window?: BrowserWindow): Queue { + private getWindowDialogQueue(window?: electron.BrowserWindow): Queue { // Queue message box requests per window so that one can show // after the other. if (window) { let windowDialogQueue = this.windowDialogQueues.get(window.id); if (!windowDialogQueue) { - windowDialogQueue = new Queue(); + windowDialogQueue = new Queue(); this.windowDialogQueues.set(window.id, windowDialogQueue); } @@ -130,15 +130,15 @@ export class DialogMainService implements IDialogMainService { } } - showMessageBox(rawOptions: MessageBoxOptions, window?: BrowserWindow): Promise { - return this.getWindowDialogQueue(window).queue(async () => { + showMessageBox(rawOptions: electron.MessageBoxOptions, window?: electron.BrowserWindow): Promise { + return this.getWindowDialogQueue(window).queue(async () => { const { options, buttonIndeces } = massageMessageBoxOptions(rawOptions, this.productService); - let result: MessageBoxReturnValue | undefined = undefined; + let result: electron.MessageBoxReturnValue | undefined = undefined; if (window) { - result = await dialog.showMessageBox(window, options); + result = await electron.dialog.showMessageBox(window, options); } else { - result = await dialog.showMessageBox(options); + result = await electron.dialog.showMessageBox(options); } return { @@ -148,7 +148,7 @@ export class DialogMainService implements IDialogMainService { }); } - async showSaveDialog(options: SaveDialogOptions, window?: BrowserWindow): Promise { + async showSaveDialog(options: electron.SaveDialogOptions, window?: electron.BrowserWindow): Promise { // Prevent duplicates of the same dialog queueing at the same time const fileDialogLock = this.acquireFileDialogLock(options, window); @@ -159,12 +159,12 @@ export class DialogMainService implements IDialogMainService { } try { - return await this.getWindowDialogQueue(window).queue(async () => { - let result: SaveDialogReturnValue; + return await this.getWindowDialogQueue(window).queue(async () => { + let result: electron.SaveDialogReturnValue; if (window) { - result = await dialog.showSaveDialog(window, options); + result = await electron.dialog.showSaveDialog(window, options); } else { - result = await dialog.showSaveDialog(options); + result = await electron.dialog.showSaveDialog(options); } result.filePath = this.normalizePath(result.filePath); @@ -190,7 +190,7 @@ export class DialogMainService implements IDialogMainService { return paths.map(path => this.normalizePath(path)); } - async showOpenDialog(options: OpenDialogOptions, window?: BrowserWindow): Promise { + async showOpenDialog(options: electron.OpenDialogOptions, window?: electron.BrowserWindow): Promise { // Ensure the path exists (if provided) if (options.defaultPath) { @@ -209,12 +209,12 @@ export class DialogMainService implements IDialogMainService { } try { - return await this.getWindowDialogQueue(window).queue(async () => { - let result: OpenDialogReturnValue; + return await this.getWindowDialogQueue(window).queue(async () => { + let result: electron.OpenDialogReturnValue; if (window) { - result = await dialog.showOpenDialog(window, options); + result = await electron.dialog.showOpenDialog(window, options); } else { - result = await dialog.showOpenDialog(options); + result = await electron.dialog.showOpenDialog(options); } result.filePaths = this.normalizePaths(result.filePaths); @@ -226,7 +226,7 @@ export class DialogMainService implements IDialogMainService { } } - private acquireFileDialogLock(options: SaveDialogOptions | OpenDialogOptions, window?: BrowserWindow): IDisposable | undefined { + private acquireFileDialogLock(options: electron.SaveDialogOptions | electron.OpenDialogOptions, window?: electron.BrowserWindow): IDisposable | undefined { // If no window is provided, allow as many dialogs as // needed since we consider them not modal per window diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 7dfc46d0a3b..4013b2b548b 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, BrowserWindow, Event as ElectronEvent } from 'electron'; +import electron from 'electron'; import { validatedIpcMain } from 'vs/base/parts/ipc/electron-main/ipcMain'; import { Barrier, Promises, timeout } from 'vs/base/common/async'; import { Emitter, Event } from 'vs/base/common/event'; @@ -294,7 +294,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe this.fireOnWillShutdown(ShutdownReason.QUIT); } }; - app.addListener('before-quit', beforeQuitListener); + electron.app.addListener('before-quit', beforeQuitListener); // window-all-closed: an event that only fires when the last window // was closed. We override this event to be in charge if app.quit() @@ -305,14 +305,14 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // Windows/Linux: we quit when all windows have closed // Mac: we only quit when quit was requested if (this._quitRequested || !isMacintosh) { - app.quit(); + electron.app.quit(); } }; - app.addListener('window-all-closed', windowAllClosedListener); + electron.app.addListener('window-all-closed', windowAllClosedListener); // will-quit: an event that is fired after all windows have been // closed, but before actually quitting. - app.once('will-quit', e => { + electron.app.once('will-quit', e => { this.trace('Lifecycle#app.on(will-quit) - begin'); // Prevent the quit until the shutdown promise was resolved @@ -332,12 +332,12 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // will-quit listener is only installed "once". Also // remove any listener we have that is no longer needed - app.removeListener('before-quit', beforeQuitListener); - app.removeListener('window-all-closed', windowAllClosedListener); + electron.app.removeListener('before-quit', beforeQuitListener); + electron.app.removeListener('window-all-closed', windowAllClosedListener); this.trace('Lifecycle#app.on(will-quit) - calling app.quit()'); - app.quit(); + electron.app.quit(); }); }); } @@ -428,7 +428,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // Window Before Closing: Main -> Renderer const win = assertIsDefined(window.win); - windowListeners.add(Event.fromNodeEventEmitter(win, 'close')(e => { + windowListeners.add(Event.fromNodeEventEmitter(win, 'close')(e => { // The window already acknowledged to be closed const windowId = window.id; @@ -458,7 +458,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe window.close(); }); })); - windowListeners.add(Event.fromNodeEventEmitter(win, 'closed')(() => { + windowListeners.add(Event.fromNodeEventEmitter(win, 'closed')(() => { this.trace(`Lifecycle#window.on('closed') - window ID ${window.id}`); // update window count @@ -480,7 +480,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe const win = assertIsDefined(auxWindow.win); const windowListeners = new DisposableStore(); - windowListeners.add(Event.fromNodeEventEmitter(win, 'close')(e => { + windowListeners.add(Event.fromNodeEventEmitter(win, 'close')(e => { this.trace(`Lifecycle#auxWindow.on('close') - window ID ${auxWindow.id}`); if (this._quitRequested) { @@ -499,7 +499,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe e.preventDefault(); } })); - windowListeners.add(Event.fromNodeEventEmitter(win, 'closed')(() => { + windowListeners.add(Event.fromNodeEventEmitter(win, 'closed')(() => { this.trace(`Lifecycle#auxWindow.on('closed') - window ID ${auxWindow.id}`); windowListeners.dispose(); @@ -652,7 +652,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // Calling app.quit() will trigger the close handlers of each opened window // and only if no window vetoed the shutdown, we will get the will-quit event this.trace('Lifecycle#quit() - calling app.quit()'); - app.quit(); + electron.app.quit(); }); return this.pendingQuitPromise; @@ -690,16 +690,16 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe const quitListener = () => { if (!this.relaunchHandler?.handleRelaunch(options)) { this.trace('Lifecycle#relaunch() - calling app.relaunch()'); - app.relaunch({ args }); + electron.app.relaunch({ args }); } }; - app.once('quit', quitListener); + electron.app.once('quit', quitListener); // `app.relaunch()` does not quit automatically, so we quit first, // check for vetoes and then relaunch from the `app.on('quit')` event const veto = await this.quit(true /* will restart */); if (veto) { - app.removeListener('quit', quitListener); + electron.app.removeListener('quit', quitListener); } } @@ -727,7 +727,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe // to a participant within the window. this is not wanted when we // are asked to kill the application. (async () => { - for (const window of BrowserWindow.getAllWindows()) { + for (const window of electron.BrowserWindow.getAllWindows()) { if (window && !window.isDestroyed()) { let whenWindowClosed: Promise; if (window.webContents && !window.webContents.isDestroyed()) { @@ -744,6 +744,6 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe ]); // Now exit either after 1s or all windows destroyed - app.exit(code); + electron.app.exit(code); } } diff --git a/src/vs/platform/theme/electron-main/themeMainService.ts b/src/vs/platform/theme/electron-main/themeMainService.ts index caef715f2da..e332feed264 100644 --- a/src/vs/platform/theme/electron-main/themeMainService.ts +++ b/src/vs/platform/theme/electron-main/themeMainService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, nativeTheme } from 'electron'; +import electron from 'electron'; import { Emitter, Event } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; @@ -64,30 +64,30 @@ export class ThemeMainService extends Disposable implements IThemeMainService { this.updateSystemColorTheme(); // Color Scheme changes - this._register(Event.fromNodeEventEmitter(nativeTheme, 'updated')(() => this._onDidChangeColorScheme.fire(this.getColorScheme()))); + this._register(Event.fromNodeEventEmitter(electron.nativeTheme, 'updated')(() => this._onDidChangeColorScheme.fire(this.getColorScheme()))); } private updateSystemColorTheme(): void { if (isLinux || this.configurationService.getValue(ThemeSettings.DETECT_COLOR_SCHEME)) { // only with `system` we can detect the system color scheme - nativeTheme.themeSource = 'system'; + electron.nativeTheme.themeSource = 'system'; } else { switch (this.configurationService.getValue<'default' | 'auto' | 'light' | 'dark'>(ThemeSettings.SYSTEM_COLOR_THEME)) { case 'dark': - nativeTheme.themeSource = 'dark'; + electron.nativeTheme.themeSource = 'dark'; break; case 'light': - nativeTheme.themeSource = 'light'; + electron.nativeTheme.themeSource = 'light'; break; case 'auto': switch (this.getBaseTheme()) { - case 'vs': nativeTheme.themeSource = 'light'; break; - case 'vs-dark': nativeTheme.themeSource = 'dark'; break; - default: nativeTheme.themeSource = 'system'; + case 'vs': electron.nativeTheme.themeSource = 'light'; break; + case 'vs-dark': electron.nativeTheme.themeSource = 'dark'; break; + default: electron.nativeTheme.themeSource = 'system'; } break; default: - nativeTheme.themeSource = 'system'; + electron.nativeTheme.themeSource = 'system'; break; } @@ -97,23 +97,23 @@ export class ThemeMainService extends Disposable implements IThemeMainService { getColorScheme(): IColorScheme { if (isWindows) { // high contrast is refelected by the shouldUseInvertedColorScheme property - if (nativeTheme.shouldUseHighContrastColors) { + if (electron.nativeTheme.shouldUseHighContrastColors) { // shouldUseInvertedColorScheme is dark, !shouldUseInvertedColorScheme is light - return { dark: nativeTheme.shouldUseInvertedColorScheme, highContrast: true }; + return { dark: electron.nativeTheme.shouldUseInvertedColorScheme, highContrast: true }; } } else if (isMacintosh) { // high contrast is set if one of shouldUseInvertedColorScheme or shouldUseHighContrastColors is set, reflecting the 'Invert colours' and `Increase contrast` settings in MacOS - if (nativeTheme.shouldUseInvertedColorScheme || nativeTheme.shouldUseHighContrastColors) { - return { dark: nativeTheme.shouldUseDarkColors, highContrast: true }; + if (electron.nativeTheme.shouldUseInvertedColorScheme || electron.nativeTheme.shouldUseHighContrastColors) { + return { dark: electron.nativeTheme.shouldUseDarkColors, highContrast: true }; } } else if (isLinux) { // ubuntu gnome seems to have 3 states, light dark and high contrast - if (nativeTheme.shouldUseHighContrastColors) { + if (electron.nativeTheme.shouldUseHighContrastColors) { return { dark: true, highContrast: true }; } } return { - dark: nativeTheme.shouldUseDarkColors, + dark: electron.nativeTheme.shouldUseDarkColors, highContrast: false }; } @@ -170,7 +170,7 @@ export class ThemeMainService extends Disposable implements IThemeMainService { } private updateBackgroundColor(windowId: number, splash: IPartsSplash): void { - for (const window of BrowserWindow.getAllWindows()) { + for (const window of electron.BrowserWindow.getAllWindows()) { if (window.id === windowId) { window.setBackgroundColor(splash.colorInfo.background); break; diff --git a/src/vs/platform/window/electron-main/window.ts b/src/vs/platform/window/electron-main/window.ts index b63a6117514..15fcb1790fe 100644 --- a/src/vs/platform/window/electron-main/window.ts +++ b/src/vs/platform/window/electron-main/window.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindow, Rectangle, screen, WebContents } from 'electron'; +import electron from 'electron'; import { CancellationToken } from 'vs/base/common/cancellation'; import { Event } from 'vs/base/common/event'; import { IDisposable } from 'vs/base/common/lifecycle'; @@ -23,7 +23,7 @@ export interface IBaseWindow extends IDisposable { readonly onDidClose: Event; readonly id: number; - readonly win: BrowserWindow | null; + readonly win: electron.BrowserWindow | null; readonly lastFocusTime: number; focus(options?: { force: boolean }): void; @@ -41,7 +41,7 @@ export interface IBaseWindow extends IDisposable { updateWindowControls(options: { height?: number; backgroundColor?: string; foregroundColor?: string }): void; - matches(webContents: WebContents): boolean; + matches(webContents: electron.WebContents): boolean; } export interface ICodeWindow extends IBaseWindow { @@ -76,7 +76,7 @@ export interface ICodeWindow extends IBaseWindow { close(): void; - getBounds(): Rectangle; + getBounds(): electron.Rectangle; send(channel: string, ...args: any[]): void; sendWhenReady(channel: string, token: CancellationToken, ...args: any[]): void; @@ -158,7 +158,7 @@ export const defaultAuxWindowState = function (): IWindowState { const width = 800; const height = 600; - const workArea = screen.getPrimaryDisplay().workArea; + const workArea = electron.screen.getPrimaryDisplay().workArea; const x = Math.max(workArea.x + (workArea.width / 2) - (width / 2), 0); const y = Math.max(workArea.y + (workArea.height / 2) - (height / 2), 0); diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index 45b8835107e..0f626a7ddfc 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, BrowserWindow, Display, nativeImage, NativeImage, Rectangle, screen, SegmentedControlSegment, systemPreferences, TouchBar, TouchBarSegmentedControl, WebContents, Event as ElectronEvent } from 'electron'; +import electron from 'electron'; import { DeferredPromise, RunOnceScheduler, timeout } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { toErrorMessage } from 'vs/base/common/errorMessage'; @@ -51,7 +51,7 @@ export interface IWindowCreationOptions { readonly isExtensionTestHost?: boolean; } -interface ITouchBarSegment extends SegmentedControlSegment { +interface ITouchBarSegment extends electron.SegmentedControlSegment { readonly id: string; } @@ -111,9 +111,9 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { protected _lastFocusTime = Date.now(); // window is shown on creation so take current time get lastFocusTime(): number { return this._lastFocusTime; } - protected _win: BrowserWindow | null = null; + protected _win: electron.BrowserWindow | null = null; get win() { return this._win; } - protected setWin(win: BrowserWindow): void { + protected setWin(win: electron.BrowserWindow): void { this._win = win; // Window Events @@ -160,7 +160,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { // This sets up a listener for the window hook. This is a Windows-only API provided by electron. win.hookWindowMessage(WM_INITMENU, () => { const [x, y] = win.getPosition(); - const cursorPos = screen.getCursorScreenPoint(); + const cursorPos = electron.screen.getCursorScreenPoint(); const cx = cursorPos.x - x; const cy = cursorPos.y - y; @@ -218,7 +218,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { super(); } - protected applyState(state: IWindowState, hasMultipleDisplays = screen.getAllDisplays().length > 0): void { + protected applyState(state: IWindowState, hasMultipleDisplays = electron.screen.getAllDisplays().length > 0): void { // TODO@electron (Electron 4 regression): when running on multiple displays where the target display // to open the window has a larger resolution than the primary display, the window will not size @@ -232,7 +232,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { const windowSettings = this.configurationService.getValue('window'); const useNativeTabs = isMacintosh && windowSettings?.nativeTabs === true; - if ((isMacintosh || isWindows) && hasMultipleDisplays && (!useNativeTabs || BrowserWindow.getAllWindows().length === 1)) { + if ((isMacintosh || isWindows) && hasMultipleDisplays && (!useNativeTabs || electron.BrowserWindow.getAllWindows().length === 1)) { if ([state.width, state.height, state.x, state.y].every(value => typeof value === 'number')) { this._win?.setBounds({ width: state.width, @@ -299,7 +299,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { focus(options?: { force: boolean }): void { if (isMacintosh && options?.force) { - app.focus({ steal: true }); + electron.app.focus({ steal: true }); } const win = this.win; @@ -322,7 +322,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { // Respect system settings on mac with regards to title click on windows title if (isMacintosh) { - const action = systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string'); + const action = electron.systemPreferences.getUserDefault('AppleActionOnDoubleClick', 'string'); switch (action) { case 'Minimize': win.minimize(); @@ -494,7 +494,7 @@ export abstract class BaseWindow extends Disposable implements IBaseWindow { //#endregion - abstract matches(webContents: WebContents): boolean; + abstract matches(webContents: electron.WebContents): boolean; override dispose(): void { super.dispose(); @@ -524,7 +524,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { private _id: number; get id(): number { return this._id; } - protected override _win: BrowserWindow; + protected override _win: electron.BrowserWindow; get backupPath(): string | undefined { return this._config?.backupPath; } @@ -561,7 +561,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { private readonly whenReadyCallbacks: { (window: ICodeWindow): void }[] = []; - private readonly touchBarGroups: TouchBarSegmentedControl[] = []; + private readonly touchBarGroups: electron.TouchBarSegmentedControl[] = []; private currentHttpProxy: string | undefined = undefined; private currentNoProxy: string | undefined = undefined; @@ -612,7 +612,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { // Create the browser window mark('code/willCreateCodeBrowserWindow'); - this._win = new BrowserWindow(options); + this._win = new electron.BrowserWindow(options); mark('code/didCreateCodeBrowserWindow'); this._id = this._win.id; @@ -693,7 +693,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { // unloading a window that should not be confused // with the DOM way. // (https://github.com/microsoft/vscode/issues/122736) - this._register(Event.fromNodeEventEmitter(this._win.webContents, 'will-prevent-unload')(event => event.preventDefault())); + this._register(Event.fromNodeEventEmitter(this._win.webContents, 'will-prevent-unload')(event => event.preventDefault())); // Remember that we loaded this._register(Event.fromNodeEventEmitter(this._win.webContents, 'did-finish-load')(() => { @@ -984,7 +984,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { const proxyBypassRules = newNoProxy ? `${newNoProxy},` : ''; this.logService.trace(`Setting proxy to '${proxyRules}', bypassing '${proxyBypassRules}'`); this._win.webContents.session.setProxy({ proxyRules, proxyBypassRules, pacScript: '' }); - app.setProxy({ proxyRules, proxyBypassRules, pacScript: '' }); + electron.app.setProxy({ proxyRules, proxyBypassRules, pacScript: '' }); } } } @@ -1135,7 +1135,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { configuration['extensions-dir'] = cli['extensions-dir']; } - configuration.accessibilitySupport = app.isAccessibilitySupportEnabled(); + configuration.accessibilitySupport = electron.app.isAccessibilitySupportEnabled(); configuration.isInitialStartup = false; // since this is a reload configuration.policiesData = this.policyService.serialize(); // set policies data again configuration.continueOn = this.environmentMainService.continueOn; @@ -1189,9 +1189,9 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { // fullscreen gets special treatment if (this.isFullScreen) { - let display: Display | undefined; + let display: electron.Display | undefined; try { - display = screen.getDisplayMatching(this.getBounds()); + display = electron.screen.getDisplayMatching(this.getBounds()); } catch (error) { // Electron has weird conditions under which it throws errors // e.g. https://github.com/microsoft/vscode/issues/100334 when @@ -1236,7 +1236,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { // only consider non-minimized window states if (mode === WindowMode.Normal || mode === WindowMode.Maximized) { - let bounds: Rectangle; + let bounds: electron.Rectangle; if (mode === WindowMode.Normal) { bounds = this.getBounds(); } else { @@ -1265,7 +1265,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { // Window dimensions try { - const displays = screen.getAllDisplays(); + const displays = electron.screen.getAllDisplays(); hasMultipleDisplays = displays.length > 1; state = WindowStateValidator.validateWindowState(this.logService, state, displays); @@ -1279,7 +1279,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { return [state || defaultWindowState(), hasMultipleDisplays]; } - getBounds(): Rectangle { + getBounds(): electron.Rectangle { const [x, y] = this._win.getPosition(); const [width, height] = this._win.getSize(); @@ -1428,16 +1428,16 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { this.touchBarGroups.push(groupTouchBar); } - this._win.setTouchBar(new TouchBar({ items: this.touchBarGroups })); + this._win.setTouchBar(new electron.TouchBar({ items: this.touchBarGroups })); } - private createTouchBarGroup(items: ISerializableCommandAction[] = []): TouchBarSegmentedControl { + private createTouchBarGroup(items: ISerializableCommandAction[] = []): electron.TouchBarSegmentedControl { // Group Segments const segments = this.createTouchBarGroupSegments(items); // Group Control - const control = new TouchBar.TouchBarSegmentedControl({ + const control = new electron.TouchBar.TouchBarSegmentedControl({ segments, mode: 'buttons', segmentStyle: 'automatic', @@ -1451,9 +1451,9 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { private createTouchBarGroupSegments(items: ISerializableCommandAction[] = []): ITouchBarSegment[] { const segments: ITouchBarSegment[] = items.map(item => { - let icon: NativeImage | undefined; + let icon: electron.NativeImage | undefined; if (item.icon && !ThemeIcon.isThemeIcon(item.icon) && item.icon?.dark?.scheme === Schemas.file) { - icon = nativeImage.createFromPath(URI.revive(item.icon.dark).fsPath); + icon = electron.nativeImage.createFromPath(URI.revive(item.icon.dark).fsPath); if (icon.isEmpty()) { icon = undefined; } @@ -1476,7 +1476,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { return segments; } - matches(webContents: WebContents): boolean { + matches(webContents: electron.WebContents): boolean { return this._win?.webContents.id === webContents.id; } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index abfc98bc2a3..30ed4fe16b0 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { BrowserWindowConstructorOptions, Display, Rectangle, WebContents, WebPreferences, screen } from 'electron'; +import electron from 'electron'; import { Event } from 'vs/base/common/event'; import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -53,7 +53,7 @@ export interface IWindowsMainService { getLastActiveWindow(): ICodeWindow | undefined; getWindowById(windowId: number): ICodeWindow | undefined; - getWindowByWebContents(webContents: WebContents): ICodeWindow | undefined; + getWindowByWebContents(webContents: electron.WebContents): ICodeWindow | undefined; } export interface IWindowsCountChangedEvent { @@ -115,7 +115,7 @@ export interface IOpenConfiguration extends IBaseOpenConfiguration { export interface IOpenEmptyConfiguration extends IBaseOpenConfiguration { } -export function defaultBrowserWindowOptions(accessor: ServicesAccessor, windowState: IWindowState, webPreferences?: WebPreferences): BrowserWindowConstructorOptions & { experimentalDarkMode: boolean } { +export function defaultBrowserWindowOptions(accessor: ServicesAccessor, windowState: IWindowState, webPreferences?: electron.WebPreferences): electron.BrowserWindowConstructorOptions & { experimentalDarkMode: boolean } { const themeMainService = accessor.get(IThemeMainService); const productService = accessor.get(IProductService); const configurationService = accessor.get(IConfigurationService); @@ -123,7 +123,7 @@ export function defaultBrowserWindowOptions(accessor: ServicesAccessor, windowSt const windowSettings = configurationService.getValue('window'); - const options: BrowserWindowConstructorOptions & { experimentalDarkMode: boolean } = { + const options: electron.BrowserWindowConstructorOptions & { experimentalDarkMode: boolean } = { backgroundColor: themeMainService.getBackgroundColor(), minWidth: WindowMinimumSize.WIDTH, minHeight: WindowMinimumSize.HEIGHT, @@ -215,7 +215,7 @@ export function getLastFocused(windows: ICodeWindow[] | IAuxiliaryWindow[]): ICo export namespace WindowStateValidator { - export function validateWindowState(logService: ILogService, state: IWindowState, displays = screen.getAllDisplays()): IWindowState | undefined { + export function validateWindowState(logService: ILogService, state: IWindowState, displays = electron.screen.getAllDisplays()): IWindowState | undefined { logService.trace(`window#validateWindowState: validating window state on ${displays.length} display(s)`, state); if ( @@ -313,10 +313,10 @@ export namespace WindowStateValidator { } // Multi Monitor (non-fullscreen): ensure window is within display bounds - let display: Display | undefined; - let displayWorkingArea: Rectangle | undefined; + let display: electron.Display | undefined; + let displayWorkingArea: electron.Rectangle | undefined; try { - display = screen.getDisplayMatching({ x: state.x, y: state.y, width: state.width, height: state.height }); + display = electron.screen.getDisplayMatching({ x: state.x, y: state.y, width: state.width, height: state.height }); displayWorkingArea = getWorkingArea(display); logService.trace('window#validateWindowState: multi-monitor working area', displayWorkingArea); @@ -343,7 +343,7 @@ export namespace WindowStateValidator { return undefined; } - function getWorkingArea(display: Display): Rectangle | undefined { + function getWorkingArea(display: electron.Display): electron.Rectangle | undefined { // Prefer the working area of the display to account for taskbars on the // desktop being positioned somewhere (https://github.com/microsoft/vscode/issues/50830). diff --git a/src/vs/platform/windows/electron-main/windowsStateHandler.ts b/src/vs/platform/windows/electron-main/windowsStateHandler.ts index dcbec3407c4..6a6591cd44d 100644 --- a/src/vs/platform/windows/electron-main/windowsStateHandler.ts +++ b/src/vs/platform/windows/electron-main/windowsStateHandler.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, Display, screen } from 'electron'; +import electron from 'electron'; import { Disposable } from 'vs/base/common/lifecycle'; import { isMacintosh } from 'vs/base/common/platform'; import { extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; @@ -78,7 +78,7 @@ export class WindowsStateHandler extends Disposable { // When a window looses focus, save all windows state. This allows to // prevent loss of window-state data when OS is restarted without properly // shutting down the application (https://github.com/microsoft/vscode/issues/87171) - app.on('browser-window-blur', () => { + electron.app.on('browser-window-blur', () => { if (!this.shuttingDown) { this.saveWindowsState(); } @@ -339,8 +339,8 @@ export class WindowsStateHandler extends Disposable { // // We want the new window to open on the same display that the last active one is in - let displayToUse: Display | undefined; - const displays = screen.getAllDisplays(); + let displayToUse: electron.Display | undefined; + const displays = electron.screen.getAllDisplays(); // Single Display if (displays.length === 1) { @@ -352,18 +352,18 @@ export class WindowsStateHandler extends Disposable { // on mac there is 1 menu per window so we need to use the monitor where the cursor currently is if (isMacintosh) { - const cursorPoint = screen.getCursorScreenPoint(); - displayToUse = screen.getDisplayNearestPoint(cursorPoint); + const cursorPoint = electron.screen.getCursorScreenPoint(); + displayToUse = electron.screen.getDisplayNearestPoint(cursorPoint); } // if we have a last active window, use that display for the new window if (!displayToUse && lastActive) { - displayToUse = screen.getDisplayMatching(lastActive.getBounds()); + displayToUse = electron.screen.getDisplayMatching(lastActive.getBounds()); } // fallback to primary display or first display if (!displayToUse) { - displayToUse = screen.getPrimaryDisplay() || displays[0]; + displayToUse = electron.screen.getPrimaryDisplay() || displays[0]; } } diff --git a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts index dc232fbda1e..bfb4fcd5145 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as fs from 'fs'; -import { BrowserWindow } from 'electron'; +import electron from 'electron'; import { Emitter, Event } from 'vs/base/common/event'; import { parse } from 'vs/base/common/json'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -281,7 +281,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork buttons: [localize({ key: 'ok', comment: ['&& denotes a mnemonic'] }, "&&OK")], message: localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(workspacePath)), detail: localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again.") - }, BrowserWindow.getFocusedWindow() ?? undefined); + }, electron.BrowserWindow.getFocusedWindow() ?? undefined); return false; } From a35bb46e9b0126cc686db046adc945be9f929f52 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 18 Jun 2024 20:05:48 +0200 Subject: [PATCH 0324/2222] Adopts debug.description. Implemented for: * Position: (11:12) * Range: [11:12 -> 13:11) * Selection: |[11:12 -> 13:11) * TextDocument: TextDocument(file:///test.txt) * NotebookDocument: NotebookDocument(file:///test.txt) * TextEditor: TextEditor(file:///test.txt, [11:12 -> 13:11)) * NotebookEditor: NotebookEditor(file:///test.txt) * URI: URI(file:///test.txt) Fixes #191390 Signed-off-by: Henning Dieterichs --- src/vs/base/common/uri.ts | 4 +++ .../api/common/extHostDocumentData.ts | 3 ++ .../api/common/extHostNotebookDocument.ts | 3 ++ .../api/common/extHostNotebookEditor.ts | 3 ++ .../workbench/api/common/extHostTextEditor.ts | 5 ++- src/vs/workbench/api/common/extHostTypes.ts | 31 +++++++++++++++++++ 6 files changed, 48 insertions(+), 1 deletion(-) diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 4d7e51431cb..f12b04da9d7 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -413,6 +413,10 @@ export class URI implements UriComponents { return result; } } + + [Symbol.for('debug.description')]() { + return `URI(${this.toString()})`; + } } export interface UriComponents { diff --git a/src/vs/workbench/api/common/extHostDocumentData.ts b/src/vs/workbench/api/common/extHostDocumentData.ts index ee321b2575a..b3edbad94a1 100644 --- a/src/vs/workbench/api/common/extHostDocumentData.ts +++ b/src/vs/workbench/api/common/extHostDocumentData.ts @@ -76,6 +76,9 @@ export class ExtHostDocumentData extends MirrorTextModel { validateRange(ran) { return that._validateRange(ran); }, validatePosition(pos) { return that._validatePosition(pos); }, getWordRangeAtPosition(pos, regexp?) { return that._getWordRangeAtPosition(pos, regexp); }, + [Symbol.for('debug.description')]() { + return `TextDocument(${that._uri.toString()})`; + } }; } return Object.freeze(this._document); diff --git a/src/vs/workbench/api/common/extHostNotebookDocument.ts b/src/vs/workbench/api/common/extHostNotebookDocument.ts index 8f74a0a4b69..382fd39c30a 100644 --- a/src/vs/workbench/api/common/extHostNotebookDocument.ts +++ b/src/vs/workbench/api/common/extHostNotebookDocument.ts @@ -211,6 +211,9 @@ export class ExtHostNotebookDocument { }, save() { return that._save(); + }, + [Symbol.for('debug.description')]() { + return `NotebookDocument(${this.uri.toString()})`; } }; this._notebook = Object.freeze(apiObject); diff --git a/src/vs/workbench/api/common/extHostNotebookEditor.ts b/src/vs/workbench/api/common/extHostNotebookEditor.ts index 8472fc1006d..4aec54c2651 100644 --- a/src/vs/workbench/api/common/extHostNotebookEditor.ts +++ b/src/vs/workbench/api/common/extHostNotebookEditor.ts @@ -71,6 +71,9 @@ export class ExtHostNotebookEditor { get viewColumn() { return that._viewColumn; }, + [Symbol.for('debug.description')]() { + return `NotebookEditor(${this.notebook.uri.toString()})`; + } }; ExtHostNotebookEditor.apiEditorsToExtHost.set(this._editor, this); diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index 44ed8f3804f..9da1df3c0d7 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -11,7 +11,7 @@ import { IRange } from 'vs/editor/common/core/range'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes'; +import { EndOfLine, getDebugDescriptionOfSelection, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes'; import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; import { Lazy } from 'vs/base/common/lazy'; @@ -566,6 +566,9 @@ export class ExtHostTextEditor { }, hide() { _proxy.$tryHideEditor(id); + }, + [Symbol.for('debug.description')]() { + return `TextEditor(${this.document.uri.toString()}, ${this.selections.map(s => getDebugDescriptionOfSelection(s)).join(', ')})`; } }); } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 92b968e001e..18c2eefe79d 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -271,6 +271,10 @@ export class Position { toJSON(): any { return { line: this.line, character: this.character }; } + + [Symbol.for('debug.description')]() { + return `(${this.line}:${this.character})`; + } } @es5ClassCompat @@ -417,6 +421,10 @@ export class Range { toJSON(): any { return [this.start, this.end]; } + + [Symbol.for('debug.description')]() { + return getDebugDescriptionOfRange(this); + } } @es5ClassCompat @@ -483,6 +491,29 @@ export class Selection extends Range { anchor: this.anchor }; } + + + [Symbol.for('debug.description')]() { + return getDebugDescriptionOfSelection(this); + } +} + +export function getDebugDescriptionOfRange(range: vscode.Range): string { + return range.isEmpty + ? `[${range.start.line}:${range.start.character})` + : `[${range.start.line}:${range.start.character} -> ${range.end.line}:${range.end.character})`; +} + +export function getDebugDescriptionOfSelection(selection: vscode.Selection): string { + let rangeStr = getDebugDescriptionOfRange(selection); + if (!selection.isEmpty) { + if (selection.active.isEqual(selection.start)) { + rangeStr = `|${rangeStr}`; + } else { + rangeStr = `${rangeStr}|`; + } + } + return rangeStr; } const validateConnectionToken = (connectionToken: string) => { From 2f29f3931cb1bbd79bd4e1a308423363420e1627 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 5 Jul 2024 11:37:31 +0200 Subject: [PATCH 0325/2222] Updates snapshot. --- .../__snapshots__/Response_inline_reference.0.snap | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/Response_inline_reference.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/Response_inline_reference.0.snap index 5847d813dfe..b26d84334a0 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/Response_inline_reference.0.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/Response_inline_reference.0.snap @@ -9,15 +9,7 @@ kind: "markdownContent" }, { - inlineReference: { - scheme: "https", - authority: "microsoft.com", - path: "/", - query: "", - fragment: "", - _formatted: null, - _fsPath: null - }, + inlineReference: URI(https://microsoft.com/), kind: "inlineReference" }, { From 4f99e9503b0b460c4c1b656c96c47aff8e3da6a5 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 5 Jul 2024 12:45:07 +0200 Subject: [PATCH 0326/2222] Removes selection from TextEditor debug description. --- src/vs/workbench/api/common/extHostTextEditor.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/api/common/extHostTextEditor.ts b/src/vs/workbench/api/common/extHostTextEditor.ts index 9da1df3c0d7..73334e5ac89 100644 --- a/src/vs/workbench/api/common/extHostTextEditor.ts +++ b/src/vs/workbench/api/common/extHostTextEditor.ts @@ -11,7 +11,7 @@ import { IRange } from 'vs/editor/common/core/range'; import { ISingleEditOperation } from 'vs/editor/common/core/editOperation'; import { IResolvedTextEditorConfiguration, ITextEditorConfigurationUpdate, MainThreadTextEditorsShape } from 'vs/workbench/api/common/extHost.protocol'; import * as TypeConverters from 'vs/workbench/api/common/extHostTypeConverters'; -import { EndOfLine, getDebugDescriptionOfSelection, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes'; +import { EndOfLine, Position, Range, Selection, SnippetString, TextEditorLineNumbersStyle, TextEditorRevealType } from 'vs/workbench/api/common/extHostTypes'; import type * as vscode from 'vscode'; import { ILogService } from 'vs/platform/log/common/log'; import { Lazy } from 'vs/base/common/lazy'; @@ -568,7 +568,7 @@ export class ExtHostTextEditor { _proxy.$tryHideEditor(id); }, [Symbol.for('debug.description')]() { - return `TextEditor(${this.document.uri.toString()}, ${this.selections.map(s => getDebugDescriptionOfSelection(s)).join(', ')})`; + return `TextEditor(${this.document.uri.toString()})`; } }); } From 3a63392646a996060ae7504526f19d17a2e0860c Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Fri, 5 Jul 2024 12:46:12 +0200 Subject: [PATCH 0327/2222] Updates more snapshots. --- .../__snapshots__/ChatService_can_deserialize.0.snap | 10 +--------- .../__snapshots__/ChatService_can_serialize.1.snap | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatService_can_deserialize.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatService_can_deserialize.0.snap index 383288306cb..78fe7781692 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatService_can_deserialize.0.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatService_can_deserialize.0.snap @@ -77,15 +77,7 @@ usedContext: { documents: [ { - uri: { - scheme: "file", - authority: "", - path: "/test/path/to/file", - query: "", - fragment: "", - _formatted: null, - _fsPath: null - }, + uri: URI(file:///test/path/to/file), version: 3, ranges: [ { diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatService_can_serialize.1.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatService_can_serialize.1.snap index cf9cc42dfc6..be8bb0fed92 100644 --- a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatService_can_serialize.1.snap +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatService_can_serialize.1.snap @@ -77,15 +77,7 @@ usedContext: { documents: [ { - uri: { - scheme: "file", - authority: "", - path: "/test/path/to/file", - query: "", - fragment: "", - _formatted: null, - _fsPath: null - }, + uri: URI(file:///test/path/to/file), version: 3, ranges: [ { From 630e53c18b4f082ba862ab8bc7eaace5005a7eab Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 8 Jul 2024 13:21:02 +0200 Subject: [PATCH 0328/2222] add cjs-package.json marker to /test (#221076) --- test/package.json | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 test/package.json diff --git a/test/package.json b/test/package.json new file mode 100644 index 00000000000..a0df0c86778 --- /dev/null +++ b/test/package.json @@ -0,0 +1,3 @@ +{ + "type": "commonjs" +} From cf8fdae6911b2be31008162b6b08cf292e649563 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 13:21:05 +0200 Subject: [PATCH 0329/2222] SCM - update history graph hover (#221039) --- extensions/git/src/historyProvider.ts | 2 +- .../contrib/scm/browser/scmViewPane.ts | 24 +++++++++++++++---- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index 55115acb6d0..8bd8b70022d 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -146,7 +146,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec } // Get the commits - const commits = await this.repository.log({ range: `${refsMergeBase}^..`, refNames }); + const commits = await this.repository.log({ range: `${refsMergeBase}^..`, refNames, shortStats: true }); await ensureEmojis(); diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 0273ba76ec5..495d576c584 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -130,9 +130,9 @@ type TreeElement = type ShowChangesSetting = 'always' | 'never' | 'auto'; -registerColor('scm.historyItemAdditionsForeground', 'gitDecoration.addedResourceForeground', localize('scm.historyItemAdditionsForeground', "History item additions foreground color.")); +const historyItemAdditionsForeground = registerColor('scm.historyItemAdditionsForeground', 'gitDecoration.addedResourceForeground', localize('scm.historyItemAdditionsForeground', "History item additions foreground color.")); -registerColor('scm.historyItemDeletionsForeground', 'gitDecoration.deletedResourceForeground', localize('scm.historyItemDeletionsForeground', "History item deletions foreground color.")); +const historyItemDeletionsForeground = registerColor('scm.historyItemDeletionsForeground', 'gitDecoration.deletedResourceForeground', localize('scm.historyItemDeletionsForeground', "History item deletions foreground color.")); registerColor('scm.historyItemStatisticsBorder', transparent(foreground, 0.2), localize('scm.historyItemStatisticsBorder', "History item statistics border color.")); @@ -999,7 +999,8 @@ class HistoryItem2Renderer implements ICompressibleTreeRenderer${historyItem.statistics.files}`); + markdown.appendMarkdown(historyItem.statistics.insertions ? `, +${historyItem.statistics.insertions}` : ''); + markdown.appendMarkdown(historyItem.statistics.deletions ? `, -${historyItem.statistics.deletions}` : ''); + } return { markdown, markdownNotSupportedFallback: historyItem.message }; } From 2b9ebd6897fb57f2f3dc9dc04957288c50717190 Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 8 Jul 2024 15:04:25 +0200 Subject: [PATCH 0330/2222] Update less grammar (#221128) --- extensions/less/cgmanifest.json | 2 +- extensions/less/syntaxes/less.tmLanguage.json | 180 +++++++++++++++--- .../test-cssvariables_less.json | 30 ++- 3 files changed, 180 insertions(+), 32 deletions(-) diff --git a/extensions/less/cgmanifest.json b/extensions/less/cgmanifest.json index caf908bbcc0..a8d1702aa82 100644 --- a/extensions/less/cgmanifest.json +++ b/extensions/less/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "language-less", "repositoryUrl": "https://github.com/radium-v/Better-Less", - "commitHash": "24047277622c245dbe9309f0004d0ccb8f02636f" + "commitHash": "b06a4555c711a6ef0d76cf2b4fc8b929a6ce551a" } }, "license": "MIT", diff --git a/extensions/less/syntaxes/less.tmLanguage.json b/extensions/less/syntaxes/less.tmLanguage.json index 2acac688385..cea782810fb 100644 --- a/extensions/less/syntaxes/less.tmLanguage.json +++ b/extensions/less/syntaxes/less.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/radium-v/Better-Less/commit/24047277622c245dbe9309f0004d0ccb8f02636f", + "version": "https://github.com/radium-v/Better-Less/commit/b06a4555c711a6ef0d76cf2b4fc8b929a6ce551a", "name": "Less", "scopeName": "source.css.less", "patterns": [ @@ -615,6 +615,9 @@ { "include": "#filter-function" }, + { + "include": "#fit-content-function" + }, { "include": "#format-function" }, @@ -738,6 +741,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "include": "#comma-delimiter" }, @@ -781,6 +787,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "include": "#comma-delimiter" }, @@ -813,6 +822,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "match": "\\b(aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow)\\b", "name": "support.constant.color.w3c-standard-color-name.less" @@ -902,6 +914,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "match": "(?:--(?:[[-\\w][^\\x{00}-\\x{7F}]]|(?:\\\\\\h{1,6}[\\s\\t\\n\\f]?|\\\\[^\\n\\f\\h]))+|-?(?:[[_a-zA-Z][^\\x{00}-\\x{7F}]]|(?:\\\\\\h{1,6}[\\s\\t\\n\\f]?|\\\\[^\\n\\f\\h]))(?:[[-\\w][^\\x{00}-\\x{7F}]]|(?:\\\\\\h{1,6}[\\s\\t\\n\\f]?|\\\\[^\\n\\f\\h]))*)", "name": "entity.other.counter-name.less" @@ -961,6 +976,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "include": "#literal-string" }, @@ -1053,6 +1071,9 @@ }, "end": "(?=\\))", "patterns": [ + { + "include": "#var-function" + }, { "include": "#comma-delimiter" }, @@ -1275,6 +1296,49 @@ } ] }, + "fit-content-function": { + "begin": "\\b(fit-content)(?=\\()", + "beginCaptures": { + "1": { + "name": "support.function.grid.less" + } + }, + "end": "\\)", + "endCaptures": { + "0": { + "name": "punctuation.definition.group.end.less" + } + }, + "name": "meta.function-call.less", + "patterns": [ + { + "begin": "\\(", + "beginCaptures": { + "0": { + "name": "punctuation.definition.group.begin.less" + } + }, + "end": "(?=\\))", + "patterns": [ + { + "include": "#less-variables" + }, + { + "include": "#var-function" + }, + { + "include": "#calc-function" + }, + { + "include": "#length-type" + }, + { + "include": "#percentage-type" + } + ] + } + ] + }, "format-function": { "patterns": [ { @@ -1348,6 +1412,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "include": "#angle-type" }, @@ -1402,6 +1469,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "include": "#color-values" }, @@ -1628,6 +1698,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "include": "#comma-delimiter" }, @@ -1704,6 +1777,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "include": "#color-values" } @@ -3360,6 +3436,9 @@ { "include": "#less-variables" }, + { + "include": "#var-function" + }, { "include": "#length-type" }, @@ -3421,7 +3500,22 @@ "property-value-constants": { "patterns": [ { - "match": "(?x)\\b(\n absolute|active|add\n |all(-(petite|small)-caps|-scroll)?\n |alpha(betic)?\n |alternate(-reverse)?\n |always|annotation|antialiased|at\n |auto(hiding-scrollbar)?\n |avoid(-column|-page|-region)?\n |background(-color|-image|-position|-size)?\n |backwards|balance|baseline|below|bevel|bicubic|bidi-override|blink\n |block(-line-height)?\n |blur\n |bold(er)?\n |border(-bottom|-left|-right|-top)?-(color|radius|width|style)\n |border-(bottom|top)-(left|right)-radius\n |border-image(-outset|-repeat|-slice|-source|-width)?\n |border(-bottom|-left|-right|-top|-collapse|-spacing|-box)?\n |both|bottom\n |box(-shadow)?\n |break-(all|word)\n |brightness\n |butt(on)?\n |capitalize\n |cent(er|ral)\n |char(acter-variant)?\n |cjk-ideographic|clip|clone|close-quote\n |closest-(corner|side)\n |col-resize|collapse\n |color(-stop|-burn|-dodge)?\n |column((-count|-gap|-reverse|-rule(-color|-width)?|-width)|s)?\n |common-ligatures|condensed|consider-shifts|contain\n |content(-box|s)?\n |contextual|contrast|cover\n |crisp(-e|E)dges\n |crop\n |cross(hair)?\n |da(rken|shed)\n |default|dense|diagonal-fractions|difference|disabled\n |discretionary-ligatures|disregard-shifts\n |distribute(-all-lines|-letter|-space)?\n |dotted|double|drop-shadow\n |(nwse|nesw|ns|ew|sw|se|nw|ne|w|s|e|n)-resize\n |ease(-in-out|-in|-out)?\n |element|ellipsis|embed|end|EndColorStr|evenodd\n |exclu(de(-ruby)?|sion)\n |expanded\n |(extra|semi|ultra)-(condensed|expanded)\n |farthest-(corner|side)?\n |fill(-box|-opacity)?\n |filter|fixed|flat\n |flex((-basis|-end|-grow|-shrink|-start)|box)?\n |flip|flood-color\n |font(-size(-adjust)?|-stretch|-weight)?\n |forwards\n |from(-image)?\n |full-width|geometricPrecision|glyphs|gradient|grayscale\n |grid(-height)?\n |groove|hand|hanging|hard-light|height|help|hidden|hide\n |historical-(forms|ligatures)\n |horizontal(-tb)?\n |hue\n |ideograph(-alpha|-numeric|-parenthesis|-space|ic)\n |inactive|include-ruby|infinite|inherit|initial\n |inline(-block|-box|-flex(box)?|-line-height|-table)?\n |inset|inside\n |inter(-ideograph|-word|sect)\n |invert|isolat(e|ion)|italic\n |jis(04|78|83|90)\n |justify(-all)?\n |keep-all\n |large[r]?\n |last|layout|left|letter-spacing\n |light(e[nr]|ing-color)\n |line(-edge|-height|-through)?\n |linear(-gradient|RGB)?\n |lining-nums|list-item|local|loose|lowercase|lr-tb|ltr\n |lumin(osity|ance)|manual\n |manipulation\n |margin(-bottom|-box|-left|-right|-top)?\n |marker(-offset|s)?\n |mathematical\n |max-(content|height|lines|size|width)\n |medium|middle\n |min-(content|height|width)\n |miter|mixed|move|multiply|newspaper\n |no-(change|clip|(close|open)-quote|(common|discretionary|historical)-ligatures|contextual|drop|repeat)\n |none|nonzero|normal|not-allowed|nowrap|oblique\n |offset(-after|-before|-end|-start)?\n |oldstyle-nums|opacity|open-quote\n |optimize(Legibility|Precision|Quality|Speed)\n |order|ordinal|ornaments\n |outline(-color|-offset|-width)?\n |outset|outside|over(line|-edge|lay)\n |padding(-bottom|-box|-left|-right|-top|-box)?\n |page|painted|paused\n |pan-(x|left|right|y|up|down)\n |perspective-origin\n |petite-caps|pixelated|pointer\n |pinch-zoom\n |pre(-line|-wrap)?\n |preserve-3d\n |progid:DXImageTransform.Microsoft.(Alpha|Blur|dropshadow|gradient|Shadow)\n |progress\n |proportional-(nums|width)\n |radial-gradient|recto|region|relative\n |repeat(-[xy])?\n |repeating-(linear|radial)-gradient\n |replaced|reset-size|reverse|ridge|right\n |round\n |row(-resize|-reverse)?\n |rtl|ruby|running|saturat(e|ion)|screen\n |scroll(-position|bar)?\n |separate|sepia\n |scale-down\n |shape-(image-threshold|margin|outside)\n |show\n |sideways(-lr|-rl)?\n |simplified\n |size\n |slashed-zero|slice\n |small(-caps|er)?\n |smooth|snap|solid|soft-light\n |space(-around|-between)?\n |span|sRGB\n |stack(ed-fractions)?\n |start(ColorStr)?\n |static\n |step-(end|start)\n |sticky\n |stop-(color|opacity)\n |stretch|strict\n |stroke(-box|-dash(array|offset)|-miterlimit|-opacity|-width)?\n |style(set)?\n |stylistic\n |sub(grid|pixel-antialiased|tract)?\n |super|swash\n |table(-caption|-cell|(-column|-footer|-header|-row)-group|-column|-row)?\n |tabular-nums|tb-rl\n |text((-bottom|-(decoration|emphasis)-color|-indent|-(over|under)-edge|-shadow|-size(-adjust)?|-top)|field)?\n |thi(ck|n)\n |titling-ca(ps|se)\n |to[p]?\n |touch|traditional\n |transform(-origin)?\n |under(-edge|line)?\n |unicase|unset|uppercase|upright\n |use-(glyph-orientation|script)\n |verso\n |vertical(-align|-ideographic|-lr|-rl|-text)?\n |view-box\n |viewport-fill(-opacity)?\n |visibility\n |visible(Fill|Painted|Stroke)?\n |wait|wavy|weight|whitespace|(device-)?width|word-spacing\n |wrap(-reverse)?\n |x{1,2}-(large|small)\n |z-index|zero\n |zoom(-in|-out)?\n |((?xi:arabic-indic|armenian|bengali|cambodian|circle|cjk-decimal|cjk-earthly-branch|cjk-heavenly-stem|decimal-leading-zero|decimal|devanagari|disclosure-closed|disclosure-open|disc|ethiopic-numeric|georgian|gujarati|gurmukhi|hebrew|hiragana-iroha|hiragana|japanese-formal|japanese-informal|kannada|katakana-iroha|katakana|khmer|korean-hangul-formal|korean-hanja-formal|korean-hanja-informal|lao|lower-alpha|lower-armenian|lower-greek|lower-latin|lower-roman|malayalam|mongolian|myanmar|oriya|persian|simp-chinese-formal|simp-chinese-informal|square|tamil|telugu|thai|tibetan|trad-chinese-formal|trad-chinese-informal|upper-alpha|upper-armenian|upper-latin|upper-roman)))\\b", + "comment": "align-content, align-items, align-self, justify-content, justify-items, justify-self", + "match": "(?x)\\b(?:\n flex-start|flex-end|start|end|space-between|space-around|space-evenly\n |stretch|baseline|safe|unsafe|legacy|anchor-center|first|last|self-start|self-end\n)\\b", + "name": "support.constant.property-value.less" + }, + { + "comment": "alignment-baseline", + "match": "(?x)\\b(?:\n text-before-edge|before-edge|middle|central|text-after-edge\n |after-edge|ideographic|alphabetic|hanging|mathematical|top|center|bottom\n)\\b", + "name": "support.constant.property-value.less" + }, + { + "comment": "all/global values", + "match": "\\b(?:initial|inherit|unset|revert-layer|revert)\\b", + "name": "support.constant.property-value.less" + }, + { + "match": "(?x)\\b(\n absolute|active|add\n|all(-(petite|small)-caps|-scroll)?\n|alpha(betic)?\n|alternate(-reverse)?\n|always|annotation|antialiased|at\n|auto(hiding-scrollbar)?\n|avoid(-column|-page|-region)?\n|background(-color|-image|-position|-size)?\n|backwards|balance|baseline|below|bevel|bicubic|bidi-override|blink\n|block(-(line-height|start|end))?\n|blur\n|bold(er)?\n|border(-bottom|-left|-right|-top)?-(color|radius|width|style)\n|border-(bottom|top)-(left|right)-radius\n|border-image(-outset|-repeat|-slice|-source|-width)?\n|border(-bottom|-left|-right|-top|-collapse|-spacing|-box)?\n|both|bottom\n|box(-shadow)?\n|break-(all|word|spaces)\n|brightness\n|butt(on)?\n|capitalize\n|cent(er|ral)\n|char(acter-variant)?\n|cjk-ideographic|clip|clone|close-quote\n|closest-(corner|side)\n|col-resize|collapse\n|color(-stop|-burn|-dodge)?\n|column((-count|-gap|-reverse|-rule(-color|-width)?|-width)|s)?\n|common-ligatures|condensed|consider-shifts|contain\n|content(-box|s)?\n|contextual|contrast|cover\n|crisp(-e|E)dges\n|crop\n|cross(hair)?\n|da(rken|shed)\n|default|dense|diagonal-fractions|difference|disabled\n|discard|discretionary-ligatures|disregard-shifts\n|distribute(-all-lines|-letter|-space)?\n|dotted|double|drop-shadow\n|(nwse|nesw|ns|ew|sw|se|nw|ne|w|s|e|n)-resize\n|ease(-in-out|-in|-out)?\n|element|ellipsis|embed|end|EndColorStr|evenodd\n|exclu(de(-ruby)?|sion)\n|expanded\n|(extra|semi|ultra)-(condensed|expanded)\n|farthest-(corner|side)?\n|fill(-box|-opacity)?\n|filter\n|fit-content\n|fixed\n|flat\n|flex((-basis|-end|-grow|-shrink|-start)|box)?\n|flip|flood-color\n|font(-size(-adjust)?|-stretch|-weight)?\n|forwards\n|from(-image)?\n|full-width|gap|geometricPrecision|glyphs|gradient|grayscale\n|grid((-column|-row)?-gap|-height)?\n|groove|hand|hanging|hard-light|height|help|hidden|hide\n|historical-(forms|ligatures)\n|horizontal(-tb)?\n|hue\n|ideograph(-alpha|-numeric|-parenthesis|-space|ic)\n|inactive|include-ruby|infinite|inherit|initial\n|inline(-(block|box|flex(box)?|line-height|table|start|end))?\n|inset|inside\n|inter(-ideograph|-word|sect)\n|invert|isolat(e|ion)|italic\n|jis(04|78|83|90)\n|justify(-all)?\n|keep-all\n|large[r]?\n|last|layout|left|letter-spacing\n|light(e[nr]|ing-color)\n|line(-edge|-height|-through)?\n|linear(-gradient|RGB)?\n|lining-nums|list-item|local|loose|lowercase|lr-tb|ltr\n|lumin(osity|ance)|manual\n|manipulation\n|margin(-bottom|-box|-left|-right|-top)?\n|marker(-offset|s)?\n|match-parent\n|mathematical\n|max-(content|height|lines|size|width)\n|medium|middle\n|min-(content|height|width)\n|miter|mixed|move|multiply|newspaper\n|no-(change|clip|(close|open)-quote|(common|discretionary|historical)-ligatures|contextual|drop|repeat)\n|none|nonzero|normal|not-allowed|nowrap|oblique\n|offset(-after|-before|-end|-start)?\n|oldstyle-nums|opacity|open-quote\n|optimize(Legibility|Precision|Quality|Speed)\n|order|ordinal|ornaments\n|outline(-color|-offset|-width)?\n|outset|outside|over(line|-edge|lay)\n|padding(-bottom|-box|-left|-right|-top|-box)?\n|page|paint(ed)?|paused\n|pan-(x|left|right|y|up|down)\n|perspective-origin\n|petite-caps|pixelated|pointer\n|pinch-zoom\n|pretty\n|pre(-line|-wrap)?\n|preserve(-3d|-breaks|-spaces)?\n|progid:DXImageTransform.Microsoft.(Alpha|Blur|dropshadow|gradient|Shadow)\n|progress\n|proportional-(nums|width)\n|radial-gradient|recto|region|relative\n|repeat(-[xy])?\n|repeating-(linear|radial)-gradient\n|replaced|reset-size|reverse|revert(-layer)?|ridge|right\n|round\n|row(-gap|-resize|-reverse)?\n|rtl|ruby|running|saturat(e|ion)|screen\n|scroll(-position|bar)?\n|separate|sepia\n|scale-down\n|shape-(image-threshold|margin|outside)\n|show\n|sideways(-lr|-rl)?\n|simplified\n|size\n|slashed-zero|slice\n|small(-caps|er)?\n|smooth|snap|solid|soft-light\n|space(-around|-between)?\n|span|sRGB\n|stable\n|stack(ed-fractions)?\n|start(ColorStr)?\n|static\n|step-(end|start)\n|sticky\n|stop-(color|opacity)\n|stretch|strict\n|stroke(-box|-dash(array|offset)|-miterlimit|-opacity|-width)?\n|style(set)?\n|stylistic\n|sub(grid|pixel-antialiased|tract)?\n|super|swash\n|table(-caption|-cell|(-column|-footer|-header|-row)-group|-column|-row)?\n|tabular-nums|tb-rl\n|text((-bottom|-(decoration|emphasis)-color|-indent|-(over|under)-edge|-shadow|-size(-adjust)?|-top)|field)?\n|thi(ck|n)\n|titling-ca(ps|se)\n|to[p]?\n|touch|traditional\n|transform(-origin)?\n|under(-edge|line)?\n|unicase|unset|uppercase|upright\n|use-(glyph-orientation|script)\n|verso\n|vertical(-align|-ideographic|-lr|-rl|-text)?\n|view-box\n|viewport-fill(-opacity)?\n|visibility\n|visible(Fill|Painted|Stroke)?\n|wait|wavy|weight|whitespace|(device-)?width|word-spacing\n|wrap(-reverse)?\n|x{1,2}-(large|small)\n|z-index|zero\n|zoom(-in|-out)?\n|((?xi:arabic-indic|armenian|bengali|cambodian|circle|cjk-decimal|cjk-earthly-branch|cjk-heavenly-stem|decimal-leading-zero|decimal|devanagari|disclosure-closed|disclosure-open|disc|ethiopic-numeric|georgian|gujarati|gurmukhi|hebrew|hiragana-iroha|hiragana|japanese-formal|japanese-informal|kannada|katakana-iroha|katakana|khmer|korean-hangul-formal|korean-hanja-formal|korean-hanja-informal|lao|lower-alpha|lower-armenian|lower-greek|lower-latin|lower-roman|malayalam|mongolian|myanmar|oriya|persian|simp-chinese-formal|simp-chinese-informal|square|tamil|telugu|thai|tibetan|trad-chinese-formal|trad-chinese-informal|upper-alpha|upper-armenian|upper-latin|upper-roman)))\\b", "name": "support.constant.property-value.less" }, { @@ -3444,9 +3538,6 @@ { "include": "#color-functions" }, - { - "include": "#less-math" - }, { "include": "#less-functions" }, @@ -3465,9 +3556,15 @@ { "include": "#property-value-constants" }, + { + "include": "#less-math" + }, { "include": "#literal-string" }, + { + "include": "#comma-delimiter" + }, { "captures": { "1": { @@ -3909,6 +4006,11 @@ "include": "#property-values" }, { + "captures": { + "1": { + "name": "punctuation.definition.arbitrary-repetition.less" + } + }, "match": "\\s*(?:(,))" } ] @@ -3918,9 +4020,6 @@ { "begin": "\\b(transition(-(property|duration|delay|timing-function))?)\\b", "beginCaptures": { - "0": { - "name": "meta.property-name.less" - }, "1": { "name": "support.type.property-name.less" } @@ -3933,35 +4032,41 @@ }, "patterns": [ { - "captures": { + "begin": "(((\\+_?)?):)(?=[\\s\\t]*)", + "beginCaptures": { "1": { "name": "punctuation.separator.key-value.less" - }, - "4": { - "name": "meta.property-value.less" } }, - "match": "(((\\+_?)?):)([\\s\\t]*)" - }, - { - "include": "#time-type" - }, - { - "include": "#property-values" - }, - { - "include": "#cubic-bezier-function" - }, - { - "include": "#steps-function" - }, - { "captures": { "1": { "name": "punctuation.definition.arbitrary-repetition.less" } }, - "match": "\\s*(?:(,))" + "contentName": "meta.property-value.less", + "end": "(?=\\s*(;)|(?=[})]))", + "patterns": [ + { + "include": "#time-type" + }, + { + "include": "#property-values" + }, + { + "include": "#cubic-bezier-function" + }, + { + "include": "#steps-function" + }, + { + "captures": { + "1": { + "name": "punctuation.definition.arbitrary-repetition.less" + } + }, + "match": "\\s*(?:(,))" + } + ] } ] }, @@ -4084,7 +4189,11 @@ ] }, { - "match": "(?x)\\b( accent-height | align-content | align-items | align-self | alignment-baseline | all | animation-timing-function | animation-play-state | animation-name | animation-iteration-count | animation-fill-mode | animation-duration | animation-direction | animation-delay | animation | appearance | ascent | azimuth | backface-visibility | background-size | background-repeat-y | background-repeat-x | background-repeat | background-position-y | background-position-x | background-position | background-origin | background-image | background-color | background-clip | background-blend-mode | background-attachment | background | baseline-shift | begin | bias | blend-mode | border-((top|right|bottom|left)-)?(width|style|color) | border-(top|bottom)-(right|left)-radius | border-image-(width|source|slice|repeat|outset) | border-(top|right|bottom|left|collapse|image|radius|spacing) | border | bottom | box-(align|decoration-break|direction|flex|ordinal-group|orient|pack|shadow|sizing) | break-(after|before|inside) | caption-side | clear | clip-path | clip-rule | clip | color(-(interpolation(-filters)?|profile|rendering))? | columns | column-(break-before|count|fill|gap|(rule(-(color|style|width))?)|span|width) | contain | content | counter-(increment|reset) | cursor | (c|d|f)(x|y) | direction | display | divisor | dominant-baseline | dur | elevation | empty-cells | enable-background | end | fallback | fill(-(opacity|rule))? | filter | flex(-(align|basis|direction|flow|grow|item-align|line-pack|negative|order|pack|positive|preferred-size|shrink|wrap))? | float | flood-(color|opacity) | font-display | font-family | font-feature-settings | font-kerning | font-language-override | font-size(-adjust)? | font-smoothing | font-stretch | font-style | font-synthesis | font-variant(-(alternates|caps|east-asian|ligatures|numeric|position))? | font-weight | font | fr | glyph-orientation-(horizontal|vertical) | grid-(area|gap) | grid-auto-(columns|flow|rows) | grid-(column|row)(-(end|gap|start))? | grid-template(-(areas|columns|rows))? | height | hyphens | image-(orientation|rendering|resolution) | isolation | justify-content | kerning | left | letter-spacing | lighting-color | line-(box-contain|break|clamp|height) | list-style(-(image|position|type))? | margin(-(bottom|left|right|top))? | marker(-(end|mid|start))? | mask(-(clip||composite|image|origin|position|repeat|size|type))? | (max|min)-(height|width) | mix-blend-mode | nbsp-mode | negative | object-(fit|position) | opacity | operator | order | orphans | outline(-(color|offset|style|width))? | overflow(-(scrolling|wrap|x|y))? | pad(ding(-(bottom|left|right|top))?)? | page(-break-(after|before|inside))? | paint-order | pause(-(after|before))? | perspective(-origin(-(x|y))?)? | pitch(-range)? | pointer-events | position | prefix | quotes | range | resize | right | rotate | scale | scroll-behavior | shape-(image-threshold|margin|outside|rendering) | size | speak(-as)? | src | stop-(color|opacity) | stroke(-(dash(array|offset)|line(cap|join)|miterlimit|opacity|width))? | suffix | symbols | system | tab-size | table-layout | tap-highlight-color | text-align(-last)? | text-decoration(-(color|line|style))? | text-emphasis(-(color|position|style))? | text-(anchor|fill-color|height|indent|justify|orientation|overflow|rendering|shadow|transform|underline-position) | top | touch-action | transform(-origin(-(x|y))?) | transform(-style)? | transition(-(delay|duration|property|timing-function))? | translate | unicode-(bidi|range) | user-(drag|select) | vertical-align | visibility | white-space | widows | width | will-change | word-(break|spacing|wrap) | writing-mode | z-index | zoom )\\b", + "match": "(?x)\\b( accent-height | align-content | align-items | align-self | alignment-baseline | all | animation-timing-function | animation-play-state | animation-name | animation-iteration-count | animation-fill-mode | animation-duration | animation-direction | animation-delay | animation | appearance | ascent | azimuth | backface-visibility | background-size | background-repeat-y | background-repeat-x | background-repeat | background-position-y | background-position-x | background-position | background-origin | background-image | background-color | background-clip | background-blend-mode | background-attachment | background | baseline-shift | begin | bias | blend-mode | border-((top|right|bottom|left|((block|inline)(-(start|end))?))-)?(width|style|color) | border-((top|bottom)-(right|left)|((start|end)-?){1,2})-radius | border-image-(width|source|slice|repeat|outset) | border-(top|right|bottom|left|collapse|image|radius|spacing|((block|inline)(-(start|end))?)) | border | bottom | box-(align|decoration-break|direction|flex|ordinal-group|orient|pack|shadow|sizing) | break-(after|before|inside) | caption-side | clear | clip-path | clip-rule | clip | color(-(interpolation(-filters)?|profile|rendering))? | columns | column-(break-before|count|fill|gap|(rule(-(color|style|width))?)|span|width) | contain(-intrinsic-((((block|inline)-)?size)|height|width))? | content | counter-(increment|reset) | cursor | (c|d|f)(x|y) | direction | display | divisor | dominant-baseline | dur | elevation | empty-cells | enable-background | end | fallback | fill(-(opacity|rule))? | filter | flex(-(align|basis|direction|flow|grow|item-align|line-pack|negative|order|pack|positive|preferred-size|shrink|wrap))? | float | flood-(color|opacity) | font-display | font-family | font-feature-settings | font-kerning | font-language-override | font-size(-adjust)? | font-smoothing | font-stretch | font-style | font-synthesis | font-variant(-(alternates|caps|east-asian|ligatures|numeric|position))? | font-weight | font | fr | ((column|row)-)?gap | glyph-orientation-(horizontal|vertical) | grid-(area|gap) | grid-auto-(columns|flow|rows) | grid-(column|row)(-(end|gap|start))? | grid-template(-(areas|columns|rows))? | height | hyphens | image-(orientation|rendering|resolution) | inset(-(block|inline))?(-(start|end))? | isolation | justify-content | justify-items | justify-self | kerning | left | letter-spacing | lighting-color | line-(box-contain|break|clamp|height) | list-style(-(image|position|type))? | (margin|padding)(-(bottom|left|right|top)|(-(block|inline)?(-(end|start))?))? | marker(-(end|mid|start))? | mask(-(clip||composite|image|origin|position|repeat|size|type))? | (max|min)-(height|width) | mix-blend-mode | nbsp-mode | negative | object-(fit|position) | opacity | operator | order | orphans | outline(-(color|offset|style|width))? | overflow(-((inline|block)|scrolling|wrap|x|y))? | overscroll-behavior(-block|-(inline|x|y))? | pad(ding(-(bottom|left|right|top))?)? | page(-break-(after|before|inside))? | paint-order | pause(-(after|before))? | perspective(-origin(-(x|y))?)? | pitch(-range)? | place-content | place-self | pointer-events | position | prefix | quotes | range | resize | right | rotate | scale | scroll-behavior | shape-(image-threshold|margin|outside|rendering) | size | speak(-as)? | src | stop-(color|opacity) | stroke(-(dash(array|offset)|line(cap|join)|miterlimit|opacity|width))? | suffix | symbols | system | tab-size | table-layout | tap-highlight-color | text-align(-last)? | text-decoration(-(color|line|style))? | text-emphasis(-(color|position|style))? | text-(anchor|fill-color|height|indent|justify|orientation|overflow|rendering|size-adjust|shadow|transform|underline-position|wrap) | top | touch-action | transform(-origin(-(x|y))?) | transform(-style)? | transition(-(delay|duration|property|timing-function))? | translate | unicode-(bidi|range) | user-(drag|select) | vertical-align | visibility | white-space(-collapse)? | widows | width | will-change | word-(break|spacing|wrap) | writing-mode | z-index | zoom )\\b", + "name": "support.type.property-name.less" + }, + { + "match": "(?x)\\b(((contain-intrinsic|max|min)-)?(block|inline)?-size)\\b", "name": "support.type.property-name.less" }, { @@ -4093,7 +4202,15 @@ ] }, { - "begin": "\\b(((\\+_?)?):)([\\s\\t]*)", + "begin": "\\b((?:(?:\\+_?)?):)([\\s\\t]*)", + "beginCaptures": { + "1": { + "name": "punctuation.separator.key-value.less" + }, + "2": { + "name": "meta.property-value.less" + } + }, "captures": { "1": { "name": "punctuation.separator.key-value.less" @@ -5002,6 +5119,9 @@ }, { "include": "#less-variables" + }, + { + "include": "#property-values" } ] } diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_less.json b/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_less.json index 856f013541b..3381f6448d0 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_less.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-cssvariables_less.json @@ -686,7 +686,7 @@ } }, { - "c": " 5px", + "c": " ", "t": "source.css.less meta.property-list.less meta.property-value.less meta.function-call.less meta.function-call.less", "r": { "dark_plus": "default: #D4D4D4", @@ -699,6 +699,34 @@ "light_modern": "default: #3B3B3B" } }, + { + "c": "5", + "t": "source.css.less meta.property-list.less meta.property-value.less meta.function-call.less meta.function-call.less constant.numeric.less", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": "px", + "t": "source.css.less meta.property-list.less meta.property-value.less meta.function-call.less meta.function-call.less constant.numeric.less keyword.other.unit.less", + "r": { + "dark_plus": "keyword.other.unit: #B5CEA8", + "light_plus": "keyword.other.unit: #098658", + "dark_vs": "keyword.other.unit: #B5CEA8", + "light_vs": "keyword.other.unit: #098658", + "hc_black": "keyword.other.unit: #B5CEA8", + "dark_modern": "keyword.other.unit: #B5CEA8", + "hc_light": "keyword.other.unit: #096D48", + "light_modern": "keyword.other.unit: #098658" + } + }, { "c": ")", "t": "source.css.less meta.property-list.less meta.property-value.less meta.function-call.less meta.function-call.less punctuation.definition.group.end.less", From 74654d15221d06d47f279929740ed195ff66774d Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:33:11 +0200 Subject: [PATCH 0331/2222] Fix close pin editor bug (#221131) fix close pin editor bug --- src/vs/workbench/browser/parts/editor/editorCommands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index ce629242565..05f7378ffc7 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -745,12 +745,12 @@ function registerCloseEditorCommands() { primary: KeyMod.CtrlCmd | KeyCode.KeyW, win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KeyW] }, handler: (accessor, ...args: unknown[]) => { - return closeEditorHandler(accessor, false, args); + return closeEditorHandler(accessor, false, ...args); } }); CommandsRegistry.registerCommand(CLOSE_PINNED_EDITOR_COMMAND_ID, (accessor, ...args: unknown[]) => { - return closeEditorHandler(accessor, true /* force close pinned editors */, args); + return closeEditorHandler(accessor, true /* force close pinned editors */, ...args); }); KeybindingsRegistry.registerCommandAndKeybindingRule({ From 3891d3c0dadd681d3b1b033f2bc10f2da534b6e0 Mon Sep 17 00:00:00 2001 From: Benjamin Christopher Simmonds <44439583+benibenj@users.noreply.github.com> Date: Mon, 8 Jul 2024 15:44:31 +0200 Subject: [PATCH 0332/2222] Fix lock group args (#221134) lock group args fix --- src/vs/workbench/browser/parts/editor/editorCommands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/browser/parts/editor/editorCommands.ts b/src/vs/workbench/browser/parts/editor/editorCommands.ts index 05f7378ffc7..7f137ca5244 100644 --- a/src/vs/workbench/browser/parts/editor/editorCommands.ts +++ b/src/vs/workbench/browser/parts/editor/editorCommands.ts @@ -1237,7 +1237,7 @@ function registerOtherEditorCommands(): void { }); } async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { - setEditorGroupLock(accessor, true, args); + setEditorGroupLock(accessor, true, ...args); } }); @@ -1252,7 +1252,7 @@ function registerOtherEditorCommands(): void { }); } async run(accessor: ServicesAccessor, ...args: unknown[]): Promise { - setEditorGroupLock(accessor, false, args); + setEditorGroupLock(accessor, false, ...args); } }); From e7cff5299bc9c3a37185bade266f2a47c07cc8f5 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 16:27:23 +0200 Subject: [PATCH 0333/2222] SCM - history item hover improvements (#221138) --- .../contrib/scm/browser/scmViewPane.ts | 59 +++++++++++++++---- 1 file changed, 49 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 495d576c584..b5430060c32 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -37,7 +37,7 @@ import { URI } from 'vs/base/common/uri'; import { FileKind } from 'vs/platform/files/common/files'; import { compareFileNames, comparePaths } from 'vs/base/common/comparers'; import { FuzzyScore, createMatches, IMatch } from 'vs/base/common/filters'; -import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; import { localize } from 'vs/nls'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { EditorResourceAccessor, SideBySideEditor } from 'vs/workbench/common/editor'; @@ -102,15 +102,18 @@ import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/d import { clamp, rot } from 'vs/base/common/numbers'; import { ILogService } from 'vs/platform/log/common/log'; import { MarkdownString } from 'vs/base/common/htmlContent'; -import type { IManagedHover, IManagedHoverTooltipMarkdownString } from 'vs/base/browser/ui/hover/hover'; -import { IHoverService } from 'vs/platform/hover/browser/hover'; +import type { IHoverOptions, IManagedHover, IManagedHoverTooltipMarkdownString } from 'vs/base/browser/ui/hover/hover'; +import { IHoverService, WorkbenchHoverDelegate } from 'vs/platform/hover/browser/hover'; import { OpenScmGroupAction } from 'vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver'; import { HoverController } from 'vs/editor/contrib/hover/browser/hoverController'; import { ITextModel } from 'vs/editor/common/model'; import { autorun } from 'vs/base/common/observable'; -import { getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; +import { createInstantHoverDelegate, getDefaultHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegateFactory'; import { renderSCMHistoryItemGraph, toISCMHistoryItemViewModelArray } from 'vs/workbench/contrib/scm/browser/scmHistory'; import { PlaceholderTextContribution } from 'vs/editor/contrib/placeholderText/browser/placeholderTextContribution'; +import { HoverPosition } from 'vs/base/browser/ui/hover/hoverWidget'; +import { IHoverDelegate } from 'vs/base/browser/ui/hover/hoverDelegate'; +import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; // type SCMResourceTreeNode = IResourceNode; // type SCMHistoryItemChangeResourceTreeNode = IResourceNode; @@ -840,6 +843,31 @@ class HistoryItemActionRunner extends ActionRunner { } } +class HistoryItemHoverDelegate extends WorkbenchHoverDelegate { + constructor( + private readonly viewContainerLocation: ViewContainerLocation | null, + private readonly sideBarPosition: Position, + @IConfigurationService configurationService: IConfigurationService, + @IHoverService hoverService: IHoverService + + ) { + super('element', true, () => this.getHoverOptions(), configurationService, hoverService); + } + + private getHoverOptions(): Partial { + let hoverPosition: HoverPosition; + if (this.viewContainerLocation === ViewContainerLocation.Sidebar) { + hoverPosition = this.sideBarPosition === Position.LEFT ? HoverPosition.RIGHT : HoverPosition.LEFT; + } else if (this.viewContainerLocation === ViewContainerLocation.AuxiliaryBar) { + hoverPosition = this.sideBarPosition === Position.LEFT ? HoverPosition.LEFT : HoverPosition.RIGHT; + } else { + hoverPosition = HoverPosition.RIGHT; + } + + return { position: { hoverPosition, forcePosition: true } }; + } +} + interface HistoryItemTemplate { readonly iconContainer: HTMLElement; readonly label: IconLabel; @@ -986,6 +1014,7 @@ class HistoryItemRenderer implements ICompressibleTreeRenderer, index: number, templateData: HistoryItem2Template, height: number | undefined): void { const historyItemViewModel = node.element.historyItemViewModel; const historyItem = historyItemViewModel.historyItem; + const historyItemHover = this.hoverService.setupManagedHover(this.hoverDelegate, templateData.element, this.getTooltip(historyItemViewModel)); + templateData.elementDisposables.add(historyItemHover); + templateData.graphContainer.textContent = ''; templateData.graphContainer.appendChild(renderSCMHistoryItemGraph(historyItemViewModel)); - const title = this.getTooltip(historyItemViewModel); const [matches, descriptionMatches] = this.processMatches(historyItemViewModel, node.filterData); - templateData.label.setLabel(historyItem.message, undefined, { title, matches, descriptionMatches }); + templateData.label.setLabel(historyItem.message, historyItem.author, { matches, descriptionMatches }); templateData.labelContainer.textContent = ''; if (historyItem.labels) { + const instantHoverDelegate = createInstantHoverDelegate(); + templateData.elementDisposables.add(instantHoverDelegate); + for (const label of historyItem.labels) { if (label.icon && ThemeIcon.isThemeIcon(label.icon)) { const icon = append(templateData.labelContainer, $('div.label')); icon.classList.add(...ThemeIcon.asClassNameArray(label.icon)); - templateData.elementDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('element'), icon, label.title)); + const hover = this.hoverService.setupManagedHover(instantHoverDelegate, icon, label.title); + templateData.elementDisposables.add(hover); } } } @@ -2878,6 +2913,7 @@ export class SCMViewPane extends ViewPane { @ISCMViewService private readonly scmViewService: ISCMViewService, @IStorageService private readonly storageService: IStorageService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IKeybindingService keybindingService: IKeybindingService, @IThemeService themeService: IThemeService, @IContextMenuService contextMenuService: IContextMenuService, @@ -3056,6 +3092,9 @@ export class SCMViewPane extends ViewPane { historyItemActionRunner.onWillRun(() => this.tree.domFocus(), this, this.disposables); this.disposables.add(historyItemActionRunner); + const historyItemHoverDelegate = this.instantiationService.createInstance(HistoryItemHoverDelegate, this.viewDescriptorService.getViewLocationById(this.id), this.layoutService.getSideBarPosition()); + this.disposables.add(historyItemHoverDelegate); + const treeDataSource = this.instantiationService.createInstance(SCMTreeDataSource, () => this.viewMode); this.disposables.add(treeDataSource); @@ -3073,7 +3112,7 @@ export class SCMViewPane extends ViewPane { this.instantiationService.createInstance(ResourceRenderer, () => this.viewMode, this.listLabels, getActionViewItemProvider(this.instantiationService), resourceActionRunner), this.instantiationService.createInstance(HistoryItemGroupRenderer, historyItemGroupActionRunner), this.instantiationService.createInstance(HistoryItemRenderer, historyItemActionRunner, getActionViewItemProvider(this.instantiationService)), - this.instantiationService.createInstance(HistoryItem2Renderer), + this.instantiationService.createInstance(HistoryItem2Renderer, historyItemHoverDelegate), this.instantiationService.createInstance(HistoryItemChangeRenderer, () => this.viewMode, this.listLabels), this.instantiationService.createInstance(SeparatorRenderer) ], From a0d7124f0c41ded7ed94cd85775a3c3e6d7ced82 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 8 Jul 2024 16:34:03 +0200 Subject: [PATCH 0334/2222] leaks - dispose storage listener (#221141) --- .../workbench/contrib/searchEditor/browser/searchEditorInput.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts index 2085c52e041..68b40814a20 100644 --- a/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts +++ b/src/vs/workbench/contrib/searchEditor/browser/searchEditorInput.ts @@ -128,7 +128,7 @@ export class SearchEditorInput extends EditorInput { } this.memento = new Memento(SearchEditorInput.ID, storageService); - storageService.onWillSaveState(() => this.memento.saveMemento()); + this._register(storageService.onWillSaveState(() => this.memento.saveMemento())); const input = this; const workingCopyAdapter = new class implements IWorkingCopy { From 125c83df26846aeef53989bc2c61e7ccfc974195 Mon Sep 17 00:00:00 2001 From: Christof Marti Date: Mon, 8 Jul 2024 16:39:26 +0200 Subject: [PATCH 0335/2222] Test basic auth for proxy (#220034) --- extensions/vscode-api-tests/package.json | 10 +- .../src/singlefolder-tests/proxy.test.ts | 78 ++++++++++- extensions/vscode-api-tests/yarn.lock | 126 ++++++++++++++++++ src/vs/platform/native/electron-main/auth.ts | 24 ++++ .../electron-main/nativeHostMainService.ts | 6 + 5 files changed, 241 insertions(+), 3 deletions(-) diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 7e8b96839f4..61828a784fb 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -112,6 +112,13 @@ "farboo.get": { "type": "string", "default": "get-prop" + }, + "integration-test.http.proxy": { + "type": "string" + }, + "integration-test.http.proxyAuth": { + "type": "string", + "default": "get-prop" } } }, @@ -251,7 +258,8 @@ "@types/mocha": "^9.1.1", "@types/node": "20.x", "@types/node-forge": "^1.3.11", - "node-forge": "^1.3.1" + "node-forge": "^1.3.1", + "straightforward": "^4.2.2" }, "repository": { "type": "git", diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts index 60f100c7c1f..ccda85b442a 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/proxy.test.ts @@ -5,12 +5,14 @@ import * as https from 'https'; import 'mocha'; -import { assertNoRpc } from '../utils'; +import { assertNoRpc, delay } from '../utils'; import { pki } from 'node-forge'; import { AddressInfo } from 'net'; import { resetCaches } from '@vscode/proxy-agent'; +import * as vscode from 'vscode'; +import { middleware, Straightforward } from 'straightforward'; -suite('vscode API - network proxy support', () => { +(vscode.env.uiKind === vscode.UIKind.Web ? suite.skip : suite)('vscode API - network proxy support', () => { teardown(async function () { assertNoRpc(); @@ -74,4 +76,76 @@ suite('vscode API - network proxy support', () => { server.close(); } }); + + test('basic auth', async () => { + const url = 'https://example.com'; // Need to use non-local URL because local URLs are excepted from proxying. + const user = 'testuser'; + const pass = 'testpassword'; + + const sf = new Straightforward(); + let authEnabled = false; + const auth = middleware.auth({ user, pass }); + sf.onConnect.use(async (context, next) => { + if (authEnabled) { + return auth(context, next); + } + next(); + }); + sf.onConnect.use(({ clientSocket }) => { + // Shortcircuit the request. + if (authEnabled) { + clientSocket.end('HTTP/1.1 204\r\n\r\n'); + } else { + clientSocket.end('HTTP/1.1 418\r\n\r\n'); + } + }); + const proxyListen = sf.listen(0); + + try { + await proxyListen; + const proxyPort = (sf.server.address() as AddressInfo).port; + + await vscode.workspace.getConfiguration().update('integration-test.http.proxy', `PROXY 127.0.0.1:${proxyPort}`, vscode.ConfigurationTarget.Global); + await delay(1000); // Wait for the configuration change to propagate. + await new Promise((resolve, reject) => { + https.get(url, res => { + if (res.statusCode === 418) { + resolve(); + } else { + reject(new Error(`Unexpected status code (expected 418): ${res.statusCode}`)); + } + }) + .on('error', reject); + }); + + authEnabled = true; + await new Promise((resolve, reject) => { + https.get(url, res => { + if (res.statusCode === 407) { + resolve(); + } else { + reject(new Error(`Unexpected status code (expected 407): ${res.statusCode}`)); + } + }) + .on('error', reject); + }); + + await vscode.workspace.getConfiguration().update('integration-test.http.proxyAuth', `${user}:${pass}`, vscode.ConfigurationTarget.Global); + await delay(1000); // Wait for the configuration change to propagate. + await new Promise((resolve, reject) => { + https.get(url, res => { + if (res.statusCode === 204) { + resolve(); + } else { + reject(new Error(`Unexpected status code (expected 204): ${res.statusCode}`)); + } + }) + .on('error', reject); + }); + } finally { + sf.close(); + await vscode.workspace.getConfiguration().update('integration-test.http.proxy', undefined, vscode.ConfigurationTarget.Global); + await vscode.workspace.getConfiguration().update('integration-test.http.proxyAuth', undefined, vscode.ConfigurationTarget.Global); + } + }); }); diff --git a/extensions/vscode-api-tests/yarn.lock b/extensions/vscode-api-tests/yarn.lock index 33a2f511927..9999a40aa14 100644 --- a/extensions/vscode-api-tests/yarn.lock +++ b/extensions/vscode-api-tests/yarn.lock @@ -28,12 +28,138 @@ dependencies: undici-types "~5.26.4" +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +cliui@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-8.0.1.tgz#0c04b075db02cbfe60dc8e6cf2f5486b1a3608aa" + integrity sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.1" + wrap-ansi "^7.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +debug@^4.3.4: + version "4.3.5" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.5.tgz#e83444eceb9fedd4a1da56d671ae2446a01a6e1e" + integrity sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg== + dependencies: + ms "2.1.2" + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + node-forge@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-1.3.1.tgz#be8da2af243b2417d5f646a770663a92b7e9ded3" integrity sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA== +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +straightforward@^4.2.2: + version "4.2.2" + resolved "https://registry.yarnpkg.com/straightforward/-/straightforward-4.2.2.tgz#a7d99b313dec5c04b0c637c7b8684dd44dc9167c" + integrity sha512-MxfuNnyTP4RPjadI3DkYIcNIp0DMXeDmAXY4/6QivU8lLIPGUqaS5VsEkaQ2QC+FICzc7QTb/lJPRIhGRKVuMA== + dependencies: + debug "^4.3.4" + yargs "^17.6.2" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@^21.1.1: + version "21.1.1" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.1.1.tgz#9096bceebf990d21bb31fa9516e0ede294a77d35" + integrity sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw== + +yargs@^17.6.2: + version "17.7.2" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" + integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w== + dependencies: + cliui "^8.0.1" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.3" + y18n "^5.0.5" + yargs-parser "^21.1.1" diff --git a/src/vs/platform/native/electron-main/auth.ts b/src/vs/platform/native/electron-main/auth.ts index da7e0ea26cc..15918242bba 100644 --- a/src/vs/platform/native/electron-main/auth.ts +++ b/src/vs/platform/native/electron-main/auth.ts @@ -12,6 +12,7 @@ import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IEncryptionMainService } from 'vs/platform/encryption/common/encryptionService'; +import { IEnvironmentMainService } from 'vs/platform/environment/electron-main/environmentMainService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; import { AuthInfo, Credentials } from 'vs/platform/request/common/request'; @@ -54,6 +55,7 @@ export class ProxyAuthService extends Disposable implements IProxyAuthService { @IEncryptionMainService private readonly encryptionMainService: IEncryptionMainService, @IApplicationStorageMainService private readonly applicationStorageMainService: IApplicationStorageMainService, @IConfigurationService private readonly configurationService: IConfigurationService, + @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, ) { super(); @@ -135,6 +137,26 @@ export class ProxyAuthService extends Disposable implements IProxyAuthService { private async doResolveProxyCredentials(authInfo: AuthInfo, authInfoHash: string): Promise { this.logService.trace('auth#doResolveProxyCredentials - enter', authInfo); + // For testing. + if (this.environmentMainService.extensionTestsLocationURI) { + const credentials = this.configurationService.getValue('integration-test.http.proxyAuth'); + if (credentials) { + const j = credentials.indexOf(':'); + if (j !== -1) { + return { + username: credentials.substring(0, j), + password: credentials.substring(j + 1) + }; + } else { + return { + username: credentials, + password: '' + }; + } + } + return undefined; + } + // Reply with manually supplied credentials. Fail if they are wrong. const newHttpProxy = (this.configurationService.getValue('http.proxy') || '').trim() || (process.env['https_proxy'] || process.env['HTTPS_PROXY'] || process.env['http_proxy'] || process.env['HTTP_PROXY'] || '').trim() @@ -145,8 +167,10 @@ export class ProxyAuthService extends Disposable implements IProxyAuthService { const i = uri.authority.indexOf('@'); if (i !== -1) { if (authInfo.attempt > 1) { + this.logService.trace('auth#doResolveProxyCredentials (proxy) - exit - ignoring previously used config/envvar credentials'); return undefined; // We tried already, let the user handle it. } + this.logService.trace('auth#doResolveProxyCredentials (proxy) - exit - found config/envvar credentials to use'); const credentials = uri.authority.substring(0, i); const j = credentials.indexOf(':'); if (j !== -1) { diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index 3b49b725dd9..e3013c06f8c 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -808,6 +808,12 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Connectivity async resolveProxy(windowId: number | undefined, url: string): Promise { + if (this.environmentMainService.extensionTestsLocationURI) { + const testProxy = this.configurationService.getValue('integration-test.http.proxy'); + if (testProxy) { + return testProxy; + } + } const window = this.codeWindowById(windowId); const session = window?.win?.webContents?.session; From b7c13a78e93c2762972be705c032fe8295a87ddb Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Mon, 8 Jul 2024 16:42:25 +0200 Subject: [PATCH 0336/2222] Today's Insiders update failed (Windows) (#220999) (#221132) --- build/lib/inlineMeta.js | 22 ++++++++++++---------- build/lib/inlineMeta.ts | 22 +++++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/build/lib/inlineMeta.js b/build/lib/inlineMeta.js index adcbfa9587e..f1dbfa83a7e 100644 --- a/build/lib/inlineMeta.js +++ b/build/lib/inlineMeta.js @@ -8,7 +8,13 @@ exports.inlineMeta = inlineMeta; const es = require("event-stream"); const path_1 = require("path"); const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; -const productJsonMarkerId = 'BUILD_INSERT_PRODUCT_CONFIGURATION'; +// TODO@bpasero in order to inline `product.json`, more work is +// needed to ensure that we cover all cases where modifications +// are done to the product configuration during build. There are +// at least 2 more changes that kick in very late: +// - a `darwinUniversalAssetId` is added in`create-universal-app.ts` +// - a `target` is added in `gulpfile.vscode.win32.js` +// const productJsonMarkerId = 'BUILD_INSERT_PRODUCT_CONFIGURATION'; function inlineMeta(result, ctx) { return result.pipe(es.through(function (file) { if (matchesFile(file, ctx)) { @@ -19,18 +25,14 @@ function inlineMeta(result, ctx) { content = content.replace(packageMarker, JSON.stringify(JSON.parse(ctx.packageJsonFn())).slice(1, -1) /* trim braces */); markerFound = true; } - const productMarker = `${productJsonMarkerId}:"${productJsonMarkerId}"`; // this needs to be the format after esbuild has processed the file (e.g. double quotes) - if (content.includes(productMarker)) { - content = content.replace(productMarker, JSON.stringify(JSON.parse(ctx.productJsonFn())).slice(1, -1) /* trim braces */); - markerFound = true; - } + // const productMarker = `${productJsonMarkerId}:"${productJsonMarkerId}"`; // this needs to be the format after esbuild has processed the file (e.g. double quotes) + // if (content.includes(productMarker)) { + // content = content.replace(productMarker, JSON.stringify(JSON.parse(ctx.productJsonFn())).slice(1, -1) /* trim braces */); + // markerFound = true; + // } if (markerFound) { file.contents = Buffer.from(content); } - else if (content.includes(packageJsonMarkerId) || content.includes(productJsonMarkerId)) { - this.emit('error', new Error(`Unable to inline metadata because expected markers where not found in ${file.basename}.`)); - return; - } } this.emit('data', file); })); diff --git a/build/lib/inlineMeta.ts b/build/lib/inlineMeta.ts index 8f4c97d6635..ef3987fc32e 100644 --- a/build/lib/inlineMeta.ts +++ b/build/lib/inlineMeta.ts @@ -14,7 +14,14 @@ export interface IInlineMetaContext { } const packageJsonMarkerId = 'BUILD_INSERT_PACKAGE_CONFIGURATION'; -const productJsonMarkerId = 'BUILD_INSERT_PRODUCT_CONFIGURATION'; + +// TODO@bpasero in order to inline `product.json`, more work is +// needed to ensure that we cover all cases where modifications +// are done to the product configuration during build. There are +// at least 2 more changes that kick in very late: +// - a `darwinUniversalAssetId` is added in`create-universal-app.ts` +// - a `target` is added in `gulpfile.vscode.win32.js` +// const productJsonMarkerId = 'BUILD_INSERT_PRODUCT_CONFIGURATION'; export function inlineMeta(result: NodeJS.ReadWriteStream, ctx: IInlineMetaContext): NodeJS.ReadWriteStream { return result.pipe(es.through(function (file: File) { @@ -28,17 +35,14 @@ export function inlineMeta(result: NodeJS.ReadWriteStream, ctx: IInlineMetaConte markerFound = true; } - const productMarker = `${productJsonMarkerId}:"${productJsonMarkerId}"`; // this needs to be the format after esbuild has processed the file (e.g. double quotes) - if (content.includes(productMarker)) { - content = content.replace(productMarker, JSON.stringify(JSON.parse(ctx.productJsonFn())).slice(1, -1) /* trim braces */); - markerFound = true; - } + // const productMarker = `${productJsonMarkerId}:"${productJsonMarkerId}"`; // this needs to be the format after esbuild has processed the file (e.g. double quotes) + // if (content.includes(productMarker)) { + // content = content.replace(productMarker, JSON.stringify(JSON.parse(ctx.productJsonFn())).slice(1, -1) /* trim braces */); + // markerFound = true; + // } if (markerFound) { file.contents = Buffer.from(content); - } else if (content.includes(packageJsonMarkerId) || content.includes(productJsonMarkerId)) { - this.emit('error', new Error(`Unable to inline metadata because expected markers where not found in ${file.basename}.`)); - return; } } From 1caa1739a73242046a52208e50b4e67529ac7560 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Mon, 8 Jul 2024 17:09:49 +0200 Subject: [PATCH 0337/2222] make sure contributes/tools are read early so that fast extensions don't run into errors (#219997) fyi @roblourens --- src/vs/workbench/contrib/chat/browser/chat.contribution.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index 46c8fa3be70..9356ca139e5 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -255,7 +255,7 @@ registerWorkbenchContribution2(ChatResolverContribution.ID, ChatResolverContribu workbenchContributionsRegistry.registerWorkbenchContribution(ChatSlashStaticSlashCommandsContribution, LifecyclePhase.Eventually); Registry.as(EditorExtensions.EditorFactory).registerEditorSerializer(ChatEditorInput.TypeID, ChatEditorInputSerializer); registerWorkbenchContribution2(ChatExtensionPointHandler.ID, ChatExtensionPointHandler, WorkbenchPhase.BlockStartup); -registerWorkbenchContribution2(LanguageModelToolsExtensionPointHandler.ID, LanguageModelToolsExtensionPointHandler, WorkbenchPhase.Eventually); +registerWorkbenchContribution2(LanguageModelToolsExtensionPointHandler.ID, LanguageModelToolsExtensionPointHandler, WorkbenchPhase.BlockRestore); registerChatActions(); registerChatCopyActions(); From 3eb4e36b6d62be9d2285cd6e22802ed6432fc47e Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Mon, 8 Jul 2024 17:21:41 +0200 Subject: [PATCH 0338/2222] Add url handling for settings and adopt in release notes (#219702) * Add url handling for settings and adopt in release notes Fixes #212079 * Update regex to use product service, fix test * Incorporate feedback * Check value of setting before passing to settings editor * Fix test --- .../browser/markdownSettingRenderer.ts | 34 +++-------- .../browser/markdownSettingRenderer.test.ts | 14 ++++- .../update/browser/update.contribution.ts | 1 - .../preferences/browser/preferencesService.ts | 58 ++++++++++++++++++- .../preferences/common/preferences.ts | 3 + .../test/browser/preferencesService.test.ts | 2 + 6 files changed, 81 insertions(+), 31 deletions(-) diff --git a/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts b/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts index 095a2978564..fe1c3c04e26 100644 --- a/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts +++ b/src/vs/workbench/contrib/markdown/browser/markdownSettingRenderer.ts @@ -4,22 +4,20 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { IPreferencesService, ISetting, ISettingsGroup } from 'vs/workbench/services/preferences/common/preferences'; +import { IPreferencesService, ISetting } from 'vs/workbench/services/preferences/common/preferences'; import { settingKeyToDisplayFormat } from 'vs/workbench/contrib/preferences/browser/settingsTreeModels'; import { URI } from 'vs/base/common/uri'; -import { Schemas } from 'vs/base/common/network'; import { ConfigurationTarget, IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { DefaultSettings } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ActionViewItem } from 'vs/base/browser/ui/actionbar/actionViewItems'; import { IAction } from 'vs/base/common/actions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; - -const codeSettingRegex = /^/; +import { Schemas } from 'vs/base/common/network'; export class SimpleSettingRenderer { - private _defaultSettings: DefaultSettings; + private readonly codeSettingRegex: RegExp; + private _updatedSettings = new Map(); // setting ID to user's original setting value private _encounteredSettings = new Map(); // setting ID to setting private _featuredSettings = new Map(); // setting ID to feature value @@ -29,9 +27,9 @@ export class SimpleSettingRenderer { @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IPreferencesService private readonly _preferencesService: IPreferencesService, @ITelemetryService private readonly _telemetryService: ITelemetryService, - @IClipboardService private readonly _clipboardService: IClipboardService + @IClipboardService private readonly _clipboardService: IClipboardService, ) { - this._defaultSettings = new DefaultSettings([], ConfigurationTarget.USER); + this.codeSettingRegex = new RegExp(`^`); } get featuredSettingStates(): Map { @@ -44,12 +42,12 @@ export class SimpleSettingRenderer { getHtmlRenderer(): (html: string) => string { return (html): string => { - const match = codeSettingRegex.exec(html); + const match = this.codeSettingRegex.exec(html); if (match && match.length === 4) { const settingId = match[2]; const rendered = this.render(settingId, match[3]); if (rendered) { - html = html.replace(codeSettingRegex, rendered); + html = html.replace(this.codeSettingRegex, rendered); } } return html; @@ -60,25 +58,11 @@ export class SimpleSettingRenderer { return `${Schemas.codeSetting}://${settingId}${value ? `/${value}` : ''}`; } - private settingsGroups: ISettingsGroup[] | undefined = undefined; private getSetting(settingId: string): ISetting | undefined { - if (!this.settingsGroups) { - this.settingsGroups = this._defaultSettings.getSettingsGroups(); - } if (this._encounteredSettings.has(settingId)) { return this._encounteredSettings.get(settingId); } - for (const group of this.settingsGroups) { - for (const section of group.sections) { - for (const setting of section.settings) { - if (setting.key === settingId) { - this._encounteredSettings.set(settingId, setting); - return setting; - } - } - } - } - return undefined; + return this._preferencesService.getSetting(settingId); } parseValue(settingId: string, value: string): any { diff --git a/src/vs/workbench/contrib/markdown/test/browser/markdownSettingRenderer.test.ts b/src/vs/workbench/contrib/markdown/test/browser/markdownSettingRenderer.test.ts index c0ee61a5161..1889507c974 100644 --- a/src/vs/workbench/contrib/markdown/test/browser/markdownSettingRenderer.test.ts +++ b/src/vs/workbench/contrib/markdown/test/browser/markdownSettingRenderer.test.ts @@ -58,7 +58,15 @@ suite('Markdown Setting Renderer Test', () => { suiteSetup(() => { configurationService = new MarkdownConfigurationService(); - preferencesService = {}; + preferencesService = { + getSetting: (setting) => { + let type = 'boolean'; + if (setting.includes('string')) { + type = 'string'; + } + return { type, key: setting }; + } + }; contextMenuService = {}; Registry.as(Extensions.Configuration).registerConfiguration(configuration); settingRenderer = new SimpleSettingRenderer(configurationService, contextMenuService, preferencesService, { publicLog2: () => { } } as any, { writeText: async () => { } } as any); @@ -70,10 +78,10 @@ suite('Markdown Setting Renderer Test', () => { test('render code setting button with value', () => { const htmlRenderer = settingRenderer.getHtmlRenderer(); - const htmlNoValue = ''; + const htmlNoValue = ''; const renderedHtmlNoValue = htmlRenderer(htmlNoValue); assert.strictEqual(renderedHtmlNoValue, - ` + ` example.booleanSetting `); diff --git a/src/vs/workbench/contrib/update/browser/update.contribution.ts b/src/vs/workbench/contrib/update/browser/update.contribution.ts index fa3edab7d3a..9e34bee7036 100644 --- a/src/vs/workbench/contrib/update/browser/update.contribution.ts +++ b/src/vs/workbench/contrib/update/browser/update.contribution.ts @@ -81,7 +81,6 @@ export class ShowCurrentReleaseNotesFromCurrentFileAction extends Action2 { }, category: localize2('developerCategory', "Developer"), f1: true, - precondition: RELEASE_NOTES_URL }); } diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index 1d8f6e0ebdc..d12424fba94 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -35,7 +35,7 @@ import { IJSONEditingService } from 'vs/workbench/services/configuration/common/ import { GroupDirection, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, SIDE_GROUP, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/browser/keybindingsEditorInput'; -import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IKeybindingsEditorOptions, IKeybindingsEditorPane, IOpenSettingsOptions, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorOptions, USE_SPLIT_JSON_SETTING, validateSettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; +import { DEFAULT_SETTINGS_EDITOR_SETTING, FOLDER_SETTINGS_PATH, IKeybindingsEditorOptions, IKeybindingsEditorPane, IOpenSettingsOptions, IPreferencesEditorModel, IPreferencesService, ISetting, ISettingsEditorOptions, ISettingsGroup, SETTINGS_AUTHORITY, USE_SPLIT_JSON_SETTING, validateSettingsEditorOptions } from 'vs/workbench/services/preferences/common/preferences'; import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/preferencesEditorInput'; import { defaultKeybindingsContents, DefaultKeybindingsEditorModel, DefaultRawSettingsEditorModel, DefaultSettings, DefaultSettingsEditorModel, Settings2EditorModel, SettingsEditorModel, WorkspaceConfigurationEditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; @@ -45,6 +45,7 @@ import { isObject } from 'vs/base/common/types'; import { SuggestController } from 'vs/editor/contrib/suggest/browser/suggestController'; import { IUserDataProfileService } from 'vs/workbench/services/userDataProfile/common/userDataProfile'; import { IUserDataProfilesService } from 'vs/platform/userDataProfile/common/userDataProfile'; +import { IURLService } from 'vs/platform/url/common/url'; const emptyEditableSettingsContent = '{\n}'; @@ -58,6 +59,9 @@ export class PreferencesService extends Disposable implements IPreferencesServic private _defaultWorkspaceSettingsContentModel: DefaultSettings | undefined; private _defaultFolderSettingsContentModel: DefaultSettings | undefined; + private _settingsGroups: ISettingsGroup[] | undefined = undefined; + private _defaultSettings: DefaultSettings | undefined = undefined; + constructor( @IEditorService private readonly editorService: IEditorService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @@ -75,7 +79,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic @ILanguageService private readonly languageService: ILanguageService, @ILabelService private readonly labelService: ILabelService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, - @ITextEditorService private readonly textEditorService: ITextEditorService + @ITextEditorService private readonly textEditorService: ITextEditorService, + @IURLService urlService: IURLService ) { super(); // The default keybindings.json updates based on keyboard layouts, so here we make sure @@ -88,6 +93,8 @@ export class PreferencesService extends Disposable implements IPreferencesServic } modelService.updateModel(model, defaultKeybindingsContents(keybindingService)); })); + + this._register(urlService.registerHandler(this)); } readonly defaultKeybindingsResource = URI.from({ scheme: network.Schemas.vscode, authority: 'defaultsettings', path: '/keybindings.json' }); @@ -593,6 +600,53 @@ export class PreferencesService extends Disposable implements IPreferencesServic return position; } + private get defaultSettings(): DefaultSettings { + if (!this._defaultSettings) { + this._defaultSettings = new DefaultSettings([], ConfigurationTarget.USER); + } + return this._defaultSettings; + } + + getSetting(settingId: string): ISetting | undefined { + if (!this._settingsGroups) { + this._settingsGroups = this.defaultSettings.getSettingsGroups(); + } + + for (const group of this._settingsGroups) { + for (const section of group.sections) { + for (const setting of section.settings) { + if (setting.key === settingId) { + return setting; + } + } + } + } + return undefined; + } + + /** + * Should be of the format: + * code://settings/settingName + * Examples: + * code://settings/files.autoSave + * + */ + async handleURL(uri: URI): Promise { + if (uri.authority !== SETTINGS_AUTHORITY) { + return false; + } + + const openSettingsOptions: IOpenSettingsOptions = {}; + const settingInfo = uri.path.split('/').filter(part => !!part); + if ((settingInfo.length === 0) || !this.getSetting(settingInfo[0])) { + return false; + } + + openSettingsOptions.query = settingInfo[0]; + this.openSettings(openSettingsOptions); + return true; + } + public override dispose(): void { this._onDispose.fire(); super.dispose(); diff --git a/src/vs/workbench/services/preferences/common/preferences.ts b/src/vs/workbench/services/preferences/common/preferences.ts index 1f0ff92e25a..f45dc085c29 100644 --- a/src/vs/workbench/services/preferences/common/preferences.ts +++ b/src/vs/workbench/services/preferences/common/preferences.ts @@ -257,6 +257,7 @@ export interface IPreferencesService { openDefaultKeybindingsFile(): Promise; openLanguageSpecificSettings(languageId: string, options?: IOpenSettingsOptions): Promise; getEditableSettingsURI(configurationTarget: ConfigurationTarget, resource?: URI): Promise; + getSetting(settingId: string): ISetting | undefined; createSplitJsonEditorInput(configurationTarget: ConfigurationTarget, resource: URI): EditorInput; } @@ -329,3 +330,5 @@ export interface IDefineKeybindingEditorContribution extends IEditorContribution export const FOLDER_SETTINGS_PATH = '.vscode/settings.json'; export const DEFAULT_SETTINGS_EDITOR_SETTING = 'workbench.settings.openDefaultSettings'; export const USE_SPLIT_JSON_SETTING = 'workbench.settings.useSplitJSON'; + +export const SETTINGS_AUTHORITY = 'settings'; diff --git a/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts b/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts index 7e5107a3827..bbc9001d5d6 100644 --- a/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts +++ b/src/vs/workbench/services/preferences/test/browser/preferencesService.test.ts @@ -9,6 +9,7 @@ import { TestCommandService } from 'vs/editor/test/browser/editorTestServices'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IURLService } from 'vs/platform/url/common/url'; import { DEFAULT_EDITOR_ASSOCIATION } from 'vs/workbench/common/editor'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { TestJSONEditingService } from 'vs/workbench/services/configuration/test/common/testServices'; @@ -32,6 +33,7 @@ suite('PreferencesService', () => { testInstantiationService.stub(IJSONEditingService, TestJSONEditingService); testInstantiationService.stub(IRemoteAgentService, TestRemoteAgentService); testInstantiationService.stub(ICommandService, TestCommandService); + testInstantiationService.stub(IURLService, { registerHandler: () => { } }); // PreferencesService creates a PreferencesEditorInput which depends on IPreferencesService, add the real one, not a stub const collection = new ServiceCollection(); From a7ab6c0e34d6c414ca91fe95176be8578440ca90 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 17:40:05 +0200 Subject: [PATCH 0339/2222] SCM - dispose the SCMInputWidgetToolbar (#221146) --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index b5430060c32..c5de9db080e 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -2265,6 +2265,10 @@ class SCMInputWidgetToolbar extends WorkbenchToolBar { updateToolbar(); } + override dispose(): void { + this.repositoryDisposables.dispose(); + super.dispose(); + } } class SCMInputWidgetEditorOptions { From 417dddb83f3536479f88c40e1d78edf1136a5ae5 Mon Sep 17 00:00:00 2001 From: Sandeep Somavarapu Date: Mon, 8 Jul 2024 17:47:27 +0200 Subject: [PATCH 0340/2222] add isProfile property to extension:extract telemetry (#221148) --- .../extensionManagement/node/extensionManagementService.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index d877db4d3c9..60b13367651 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -490,10 +490,12 @@ type UpdateMetadataErrorClassification = { comment: 'Update metadata error'; extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'extension identifier' }; code?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'error code' }; + isProfile?: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Is writing into profile' }; }; type UpdateMetadataErrorEvent = { extensionId: string; code?: string; + isProfile?: boolean; }; export class ExtensionsScanner extends Disposable { @@ -672,7 +674,7 @@ export class ExtensionsScanner extends Disposable { await this.extensionsScannerService.updateMetadata(local.location, metadata); } } catch (error) { - this.telemetryService.publicLog2('extension:extract', { extensionId: local.identifier.id, code: `${toFileOperationResult(error)}` }); + this.telemetryService.publicLog2('extension:extract', { extensionId: local.identifier.id, code: `${toFileOperationResult(error)}`, isProfile: !!profileLocation }); throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata); } return this.scanLocalExtension(local.location, local.type, profileLocation); From 1e6b49798da0f8457d272758b8bd74f575dc9112 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Cie=C5=9Blak?= Date: Mon, 8 Jul 2024 19:12:13 +0200 Subject: [PATCH 0341/2222] Inline edit - implement side by side rendering of suggestions (#219444) * Make widget derived out of edit value * initial side-by-side rendering of suggestions * Keep old UI in some scenarios * Handle scrolling * Use embedded text editor to display suggestions * Add diff decorations * Apply decorations on the original code in side-by-side rendering * try to solve flickering... and fail at it * Make background colouring always enabled for inner-line suggestions, and remove strike-through --- src/vs/editor/common/config/editorOptions.ts | 9 - .../inlineEdit/browser/ghostTextWidget.ts | 10 +- .../browser/inlineEdit.contribution.ts | 6 +- .../contrib/inlineEdit/browser/inlineEdit.css | 5 - .../browser/inlineEditController.ts | 76 ++-- .../browser/inlineEditHintsWidget.ts | 6 +- .../browser/inlineEditSideBySideWidget.css | 12 + .../browser/inlineEditSideBySideWidget.ts | 346 ++++++++++++++++++ src/vs/monaco.d.ts | 1 - 9 files changed, 413 insertions(+), 58 deletions(-) create mode 100644 src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.css create mode 100644 src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.ts diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 294e7030695..64394443765 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -4171,8 +4171,6 @@ export interface IInlineEditOptions { * Does not clear active inline suggestions when the editor loses focus. */ keepOnBlur?: boolean; - - backgroundColoring?: boolean; } /** @@ -4187,7 +4185,6 @@ class InlineEditorEdit extends BaseEditorOption; readonly minReservedLineCount: IObservable; readonly range: IObservable; - readonly backgroundColoring: IObservable; } export class GhostTextWidget extends Disposable { @@ -92,7 +92,7 @@ export class GhostTextWidget extends Disposable { let hiddenTextStartColumn: number | undefined = undefined; let lastIdx = 0; - if (!isPureRemove) { + if (!isPureRemove && (isSingleLine || !range)) { for (const part of ghostText.parts) { let lines = part.lines; //If remove range is set, we want to push all new liens to virtual area @@ -140,7 +140,6 @@ export class GhostTextWidget extends Disposable { range, isSingleLine, isPureRemove, - backgroundColoring: this.model.backgroundColoring.read(reader) }; }); @@ -184,11 +183,10 @@ export class GhostTextWidget extends Disposable { ranges.push(range); } } - const className = uiState.backgroundColoring ? 'inline-edit-remove backgroundColoring' : 'inline-edit-remove'; for (const range of ranges) { decorations.push({ range, - options: { inlineClassName: className, description: 'inline-edit-remove', } + options: diffDeleteDecoration }); } } @@ -215,7 +213,7 @@ export class GhostTextWidget extends Disposable { derived(reader => { /** @description lines */ const uiState = this.uiState.read(reader); - return uiState && !uiState.isPureRemove ? { + return uiState && !uiState.isPureRemove && (uiState.isSingleLine || !uiState.range) ? { lineNumber: uiState.lineNumber, additionalLines: uiState.additionalLines, minReservedLineCount: uiState.additionalReservedLineCount, diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEdit.contribution.ts b/src/vs/editor/contrib/inlineEdit/browser/inlineEdit.contribution.ts index 7196773a7cf..0be0d1254c0 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/inlineEdit.contribution.ts +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEdit.contribution.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { EditorContributionInstantiation, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { HoverParticipantRegistry } from 'vs/editor/contrib/hover/browser/hoverTypes'; +// import { HoverParticipantRegistry } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { AcceptInlineEdit, JumpBackInlineEdit, JumpToInlineEdit, RejectInlineEdit, TriggerInlineEdit } from 'vs/editor/contrib/inlineEdit/browser/commands'; -import { InlineEditHoverParticipant } from 'vs/editor/contrib/inlineEdit/browser/hoverParticipant'; +// import { InlineEditHoverParticipant } from 'vs/editor/contrib/inlineEdit/browser/hoverParticipant'; import { InlineEditController } from 'vs/editor/contrib/inlineEdit/browser/inlineEditController'; registerEditorAction(AcceptInlineEdit); @@ -17,4 +17,4 @@ registerEditorAction(TriggerInlineEdit); registerEditorContribution(InlineEditController.ID, InlineEditController, EditorContributionInstantiation.Eventually); -HoverParticipantRegistry.register(InlineEditHoverParticipant); +// HoverParticipantRegistry.register(InlineEditHoverParticipant); diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEdit.css b/src/vs/editor/contrib/inlineEdit/browser/inlineEdit.css index d6d156544e0..aced5d6271e 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/inlineEdit.css +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEdit.css @@ -6,11 +6,6 @@ .monaco-editor .inline-edit-remove { background-color: var(--vscode-editorGhostText-background); font-style: italic; - text-decoration: line-through; -} - -.monaco-editor .inline-edit-remove.backgroundColoring { - background-color: var(--vscode-diffEditor-removedLineBackground); } .monaco-editor .inline-edit-hidden { diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts b/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts index 74053d4a617..4e231fd5162 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEditController.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -import { ISettableObservable, autorun, constObservable, disposableObservableValue, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; +import { ISettableObservable, autorun, constObservable, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from 'vs/base/common/observable'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditOperation } from 'vs/editor/common/core/editOperation'; import { Position } from 'vs/editor/common/core/position'; @@ -22,14 +22,10 @@ import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { createStyleSheet2 } from 'vs/base/browser/dom'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { onUnexpectedExternalError } from 'vs/base/common/errors'; - -export class InlineEditWidget implements IDisposable { - constructor(public readonly widget: GhostTextWidget, public readonly edit: IInlineEdit) { } - - dispose(): void { - this.widget.dispose(); - } -} +import { derivedDisposable } from 'vs/base/common/observableInternal/derived'; +import { InlineEditSideBySideWidget } from 'vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget'; +import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService'; +import { IModelService } from 'vs/editor/common/services/model'; export class InlineEditController extends Disposable { static ID = 'editor.contrib.inlineEditController'; @@ -46,7 +42,30 @@ export class InlineEditController extends Disposable { return editor.getContribution(InlineEditController.ID); } - private _currentEdit: ISettableObservable = this._register(disposableObservableValue(this, undefined)); + private _currentEdit: ISettableObservable = observableValue(this, undefined); + private _currentWidget = derivedDisposable(this._currentEdit, (reader) => { + const edit = this._currentEdit.read(reader); + if (!edit) { + return undefined; + } + const line = edit.range.endLineNumber; + const column = edit.range.endColumn; + const textToDisplay = edit.text.endsWith('\n') && !(edit.range.startLineNumber === edit.range.endLineNumber && edit.range.startColumn === edit.range.endColumn) ? edit.text.slice(0, -1) : edit.text; + const ghostText = new GhostText(line, [new GhostTextPart(column, textToDisplay, false)]); + //only show ghost text for single line edits + //multi line edits are shown in the side by side widget + const isSingleLine = edit.range.startLineNumber === edit.range.endLineNumber && ghostText.parts.length === 1 && ghostText.parts[0].lines.length === 1; + if (!isSingleLine) { + return undefined; + } + const instance = this.instantiationService.createInstance(GhostTextWidget, this.editor, { + ghostText: constObservable(ghostText), + minReservedLineCount: constObservable(0), + targetTextModel: constObservable(this.editor.getModel() ?? undefined), + range: constObservable(edit.range) + }); + return instance; + }); private _currentRequestCts: CancellationTokenSource | undefined; private _jumpBackPosition: Position | undefined; @@ -54,7 +73,6 @@ export class InlineEditController extends Disposable { private readonly _enabled = observableFromEvent(this, this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.inlineEdit).enabled); private readonly _fontFamily = observableFromEvent(this, this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.inlineEdit).fontFamily); - private readonly _backgroundColoring = observableFromEvent(this, this.editor.onDidChangeConfiguration, () => this.editor.getOption(EditorOption.inlineEdit).backgroundColoring); constructor( @@ -64,6 +82,8 @@ export class InlineEditController extends Disposable { @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, @ICommandService private readonly _commandService: ICommandService, @IConfigurationService private readonly _configurationService: IConfigurationService, + @IDiffProviderFactoryService private readonly _diffProviderFactoryService: IDiffProviderFactoryService, + @IModelService private readonly _modelService: IModelService, ) { super(); @@ -154,7 +174,8 @@ export class InlineEditController extends Disposable { }`); })); - this._register(new InlineEditHintsWidget(this.editor, this._currentEdit, this.instantiationService)); + this._register(new InlineEditHintsWidget(this.editor, this._currentWidget, this.instantiationService)); + this._register(new InlineEditSideBySideWidget(this.editor, this._currentEdit, this.instantiationService, this._diffProviderFactoryService, this._modelService)); } private checkCursorPosition(position: Position) { @@ -162,7 +183,7 @@ export class InlineEditController extends Disposable { this._isCursorAtInlineEditContext.set(false); return; } - const gt = this._currentEdit.get()?.edit; + const gt = this._currentEdit.get(); if (!gt) { this._isCursorAtInlineEditContext.set(false); return; @@ -231,18 +252,7 @@ export class InlineEditController extends Disposable { if (!edit) { return; } - const line = edit.range.endLineNumber; - const column = edit.range.endColumn; - const textToDisplay = edit.text.endsWith('\n') && !(edit.range.startLineNumber === edit.range.endLineNumber && edit.range.startColumn === edit.range.endColumn) ? edit.text.slice(0, -1) : edit.text; - const ghostText = new GhostText(line, [new GhostTextPart(column, textToDisplay, false)]); - const instance = this.instantiationService.createInstance(GhostTextWidget, this.editor, { - ghostText: constObservable(ghostText), - minReservedLineCount: constObservable(0), - targetTextModel: constObservable(this.editor.getModel() ?? undefined), - range: constObservable(edit.range), - backgroundColoring: this._backgroundColoring - }); - this._currentEdit.set(new InlineEditWidget(instance, edit), undefined); + this._currentEdit.set(edit, undefined); } public async trigger() { @@ -260,7 +270,7 @@ export class InlineEditController extends Disposable { public async accept() { this._isAccepting.set(true, undefined); - const data = this._currentEdit.get()?.edit; + const data = this._currentEdit.get(); if (!data) { return; } @@ -287,7 +297,7 @@ export class InlineEditController extends Disposable { public jumpToCurrent(): void { this._jumpBackPosition = this.editor.getSelection()?.getStartPosition(); - const data = this._currentEdit.get()?.edit; + const data = this._currentEdit.get(); if (!data) { return; } @@ -298,7 +308,7 @@ export class InlineEditController extends Disposable { } public async clear(sendRejection: boolean = true) { - const edit = this._currentEdit.get()?.edit; + const edit = this._currentEdit.get(); if (edit && edit?.rejected && sendRejection) { await this._commandService .executeCommand(edit.rejected.id, ...(edit.rejected.arguments || [])) @@ -324,11 +334,15 @@ export class InlineEditController extends Disposable { public shouldShowHoverAt(range: Range) { const currentEdit = this._currentEdit.get(); + const currentWidget = this._currentWidget.get(); if (!currentEdit) { return false; } - const edit = currentEdit.edit; - const model = currentEdit.widget.model; + if (!currentWidget) { + return false; + } + const edit = currentEdit; + const model = currentWidget.model; const overReplaceRange = Range.containsPosition(edit.range, range.getStartPosition()) || Range.containsPosition(edit.range, range.getEndPosition()); if (overReplaceRange) { return true; @@ -341,7 +355,7 @@ export class InlineEditController extends Disposable { } public shouldShowHoverAtViewZone(viewZoneId: string): boolean { - return this._currentEdit.get()?.widget.ownsViewZone(viewZoneId) ?? false; + return this._currentWidget.get()?.ownsViewZone(viewZoneId) ?? false; } } diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEditHintsWidget.ts b/src/vs/editor/contrib/inlineEdit/browser/inlineEditHintsWidget.ts index 215c85fbc0e..cb2453ab6c6 100644 --- a/src/vs/editor/contrib/inlineEdit/browser/inlineEditHintsWidget.ts +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEditHintsWidget.ts @@ -15,7 +15,7 @@ import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentW import { EditorOption } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { PositionAffinity } from 'vs/editor/common/model'; -import { InlineEditWidget } from 'vs/editor/contrib/inlineEdit/browser/inlineEditController'; +import { GhostTextWidget } from 'vs/editor/contrib/inlineEdit/browser/ghostTextWidget'; import { MenuEntryActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { IMenuWorkbenchToolBarOptions, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; @@ -32,7 +32,7 @@ export class InlineEditHintsWidget extends Disposable { private sessionPosition: Position | undefined = undefined; private readonly position = derived(this, reader => { - const ghostText = this.model.read(reader)?.widget.model.ghostText.read(reader); + const ghostText = this.model.read(reader)?.model.ghostText.read(reader); if (!this.alwaysShowToolbar.read(reader) || !ghostText || ghostText.parts.length === 0) { this.sessionPosition = undefined; @@ -51,7 +51,7 @@ export class InlineEditHintsWidget extends Disposable { constructor( private readonly editor: ICodeEditor, - private readonly model: IObservable, + private readonly model: IObservable, @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.css b/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.css new file mode 100644 index 00000000000..bc7e553e4d8 --- /dev/null +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-editor .inlineEditSideBySide { + z-index: 39; + color: var(--vscode-editorHoverWidget-foreground); + background-color: var(--vscode-editorHoverWidget-background); + border: 1px solid var(--vscode-editorHoverWidget-border); + white-space: pre; +} diff --git a/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.ts b/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.ts new file mode 100644 index 00000000000..bd10e0669b4 --- /dev/null +++ b/src/vs/editor/contrib/inlineEdit/browser/inlineEditSideBySideWidget.ts @@ -0,0 +1,346 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $ } from 'vs/base/browser/dom'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { IObservable, ObservablePromise, autorun, autorunWithStore, derived, observableSignalFromEvent } from 'vs/base/common/observable'; +import { derivedDisposable } from 'vs/base/common/observableInternal/derived'; +import { URI } from 'vs/base/common/uri'; +import 'vs/css!./inlineEditSideBySideWidget'; +import { ICodeEditor, IOverlayWidget, IOverlayWidgetPosition } from 'vs/editor/browser/editorBrowser'; +import { observableCodeEditor } from 'vs/editor/browser/observableCodeEditor'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; +import { IDiffProviderFactoryService } from 'vs/editor/browser/widget/diffEditor/diffProviderFactoryService'; +import { diffAddDecoration, diffAddDecorationEmpty, diffDeleteDecoration, diffDeleteDecorationEmpty, diffLineDeleteDecorationBackgroundWithIndicator, diffWholeLineAddDecoration, diffWholeLineDeleteDecoration } from 'vs/editor/browser/widget/diffEditor/registrations.contribution'; +import { EditorOption } from 'vs/editor/common/config/editorOptions'; +import { Position } from 'vs/editor/common/core/position'; +import { IRange, Range } from 'vs/editor/common/core/range'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { IInlineEdit } from 'vs/editor/common/languages'; +import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; +import { IModelDeltaDecoration } from 'vs/editor/common/model'; +import { TextModel } from 'vs/editor/common/model/textModel'; +import { IModelService } from 'vs/editor/common/services/model'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; + +function* range(start: number, end: number, step = 1) { + if (end === undefined) { [end, start] = [start, 0]; } + for (let n = start; n < end; n += step) { yield n; } +} + +function removeIndentation(lines: string[]): string[] { + const indentation = lines[0].match(/^\s*/)?.[0] ?? ''; + return lines.map(l => l.replace(new RegExp('^' + indentation), '')); +} + +type Pos = { + top: number; + left: Position; +}; + +export class InlineEditSideBySideWidget extends Disposable { + private static _modelId = 0; + private static _createUniqueUri(): URI { + return URI.from({ scheme: 'inline-edit-widget', path: new Date().toString() + String(InlineEditSideBySideWidget._modelId++) }); + } + + private readonly _position = derived(this, reader => { + const ghostText = this._model.read(reader); + + if (!ghostText || ghostText.text.length === 0) { + return null; + } + if (ghostText.range.startLineNumber === ghostText.range.endLineNumber) { + //for inner-line suggestions we still want to use minimal ghost text + return null; + } + const editorModel = this._editor.getModel(); + if (!editorModel) { + return null; + } + const lines = Array.from(range(ghostText.range.startLineNumber, ghostText.range.endLineNumber + 1)); + const lengths = lines.map(lineNumber => editorModel.getLineLastNonWhitespaceColumn(lineNumber)); + const maxColumn = Math.max(...lengths); + const lineOfMaxColumn = lines[lengths.indexOf(maxColumn)]; + + const position = new Position(lineOfMaxColumn, maxColumn); + const pos = { + top: ghostText.range.startLineNumber, + left: position + }; + + return pos; + }); + + private readonly _text = derived(this, reader => { + const ghostText = this._model.read(reader); + if (!ghostText) { + return ''; + } + return removeIndentation(ghostText.text.split('\n')).join('\n'); + }); + + + private readonly _originalModel = derivedDisposable(() => this._modelService.createModel('', null, InlineEditSideBySideWidget._createUniqueUri())).keepObserved(this._store); + private readonly _modifiedModel = derivedDisposable(() => this._modelService.createModel('', null, InlineEditSideBySideWidget._createUniqueUri())).keepObserved(this._store); + + private readonly _diff = derived(this, reader => { + return this._diffPromise.read(reader)?.promiseResult.read(reader)?.data; + }); + + private readonly _diffPromise = derived(this, reader => { + const ghostText = this._model.read(reader); + if (!ghostText) { + return; + } + const editorModel = this._editor.getModel(); + if (!editorModel) { + return; + } + const originalText = removeIndentation(editorModel.getValueInRange(ghostText.range).split('\n')).join('\n'); + const modifiedText = removeIndentation(ghostText.text.split('\n')).join('\n'); + this._originalModel.get().setValue(originalText); + this._modifiedModel.get().setValue(modifiedText); + const d = this._diffProviderFactoryService.createDiffProvider({ diffAlgorithm: 'advanced' }); + return ObservablePromise.fromFn(async () => { + const result = await d.computeDiff(this._originalModel.get(), this._modifiedModel.get(), { + computeMoves: false, + ignoreTrimWhitespace: false, + maxComputationTimeMs: 1000, + }, CancellationToken.None); + + if (result.identical) { + return undefined; + } + + return result.changes; + }); + }); + + constructor( + private readonly _editor: ICodeEditor, + private readonly _model: IObservable, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IDiffProviderFactoryService private readonly _diffProviderFactoryService: IDiffProviderFactoryService, + @IModelService private readonly _modelService: IModelService, + ) { + super(); + + this._register(autorunWithStore((reader, store) => { + /** @description setup content widget */ + const model = this._model.read(reader); + if (!model) { + return; + } + + const contentWidget = store.add(this._instantiationService.createInstance( + InlineEditSideBySideContentWidget, + this._editor, + this._position, + this._text, + this._diff + )); + _editor.addOverlayWidget(contentWidget); + store.add(toDisposable(() => _editor.removeOverlayWidget(contentWidget))); + })); + } +} + +class InlineEditSideBySideContentWidget extends Disposable implements IOverlayWidget { + private static _dropDownVisible = false; + public static get dropDownVisible() { return this._dropDownVisible; } + + private static id = 0; + + private readonly id = `InlineEditSideBySideContentWidget${InlineEditSideBySideContentWidget.id++}`; + public readonly allowEditorOverflow = true; + public readonly suppressMouseDown = false; + + private readonly _nodes = $('div.inlineEditSideBySide', undefined,); + + private readonly _scrollChanged = observableSignalFromEvent('editor.onDidScrollChange', this._editor.onDidScrollChange); + + private readonly _previewEditor = this._register(this._instantiationService.createInstance( + EmbeddedCodeEditorWidget, + this._nodes, + { + glyphMargin: false, + lineNumbers: 'off', + minimap: { enabled: false }, + guides: { + indentation: false, + bracketPairs: false, + bracketPairsHorizontal: false, + highlightActiveIndentation: false, + }, + folding: false, + selectOnLineNumbers: false, + selectionHighlight: false, + columnSelection: false, + overviewRulerBorder: false, + overviewRulerLanes: 0, + lineDecorationsWidth: 0, + lineNumbersMinChars: 0, + scrollbar: { vertical: 'hidden', horizontal: 'hidden' }, + }, + { contributions: [], }, + this._editor + )); + + private readonly _previewEditorObs = observableCodeEditor(this._previewEditor); + private readonly _editorObs = observableCodeEditor(this._editor); + + private readonly _previewTextModel = this._register(this._instantiationService.createInstance( + TextModel, + '', + this._editor.getModel()?.getLanguageId() ?? PLAINTEXT_LANGUAGE_ID, + TextModel.DEFAULT_CREATION_OPTIONS, + null + )); + + private readonly _setText = derived(reader => { + const edit = this._text.read(reader); + if (!edit) { return; } + this._previewTextModel.setValue(edit); + }).recomputeInitiallyAndOnChange(this._store); + + + private readonly _decorations = derived(this, (reader) => { + this._setText.read(reader); + const position = this._position.read(reader); + if (!position) { return { org: [], mod: [] }; } + const diff = this._diff.read(reader); + if (!diff) { return { org: [], mod: [] }; } + + const originalDecorations: IModelDeltaDecoration[] = []; + const modifiedDecorations: IModelDeltaDecoration[] = []; + + if (diff.length === 1 && diff[0].innerChanges![0].modifiedRange.equalsRange(this._previewTextModel.getFullModelRange())) { + return { org: [], mod: [] }; + } + + const moveRange = (range: IRange) => { + return new Range(range.startLineNumber + position.top - 1, range.startColumn, range.endLineNumber + position.top - 1, range.endColumn); + }; + + for (const m of diff) { + if (!m.original.isEmpty) { + originalDecorations.push({ range: moveRange(m.original.toInclusiveRange()!), options: diffLineDeleteDecorationBackgroundWithIndicator }); + } + if (!m.modified.isEmpty) { + // modifiedDecorations.push({ range: m.modified.toInclusiveRange()!, options: diffLineAddDecorationBackgroundWithIndicator }); + } + + if (m.modified.isEmpty || m.original.isEmpty) { + if (!m.original.isEmpty) { + originalDecorations.push({ range: moveRange(m.original.toInclusiveRange()!), options: diffWholeLineDeleteDecoration }); + } + if (!m.modified.isEmpty) { + modifiedDecorations.push({ range: m.modified.toInclusiveRange()!, options: diffWholeLineAddDecoration }); + } + } else { + for (const i of m.innerChanges || []) { + // Don't show empty markers outside the line range + if (m.original.contains(i.originalRange.startLineNumber)) { + originalDecorations.push({ range: moveRange(i.originalRange), options: i.originalRange.isEmpty() ? diffDeleteDecorationEmpty : diffDeleteDecoration }); + } + if (m.modified.contains(i.modifiedRange.startLineNumber)) { + modifiedDecorations.push({ range: i.modifiedRange, options: i.modifiedRange.isEmpty() ? diffAddDecorationEmpty : diffAddDecoration }); + } + } + } + } + + return { org: originalDecorations, mod: modifiedDecorations }; + }); + + private readonly _originalDecorations = derived(this, reader => { + return this._decorations.read(reader).org; + }); + + private readonly _modifiedDecorations = derived(this, reader => { + return this._decorations.read(reader).mod; + }); + + constructor( + private readonly _editor: ICodeEditor, + private readonly _position: IObservable, + private readonly _text: IObservable, + private readonly _diff: IObservable, + + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + super(); + + this._previewEditor.setModel(this._previewTextModel); + + this._register(this._editorObs.setDecorations(this._originalDecorations)); + this._register(this._previewEditorObs.setDecorations(this._modifiedDecorations)); + + this._register(autorun(reader => { + const width = this._previewEditorObs.contentWidth.read(reader); + const lines = this._text.get().split('\n').length - 1; + const height = this._editor.getOption(EditorOption.lineHeight) * lines; + if (width <= 0) { + return; + } + console.log('width', width); + this._previewEditor.layout({ height: height, width: width }); + })); + + this._register(autorun(reader => { + /** @description update position */ + this._position.read(reader); + this._editor.layoutOverlayWidget(this); + })); + + this._register(autorun(reader => { + /** @description scroll change */ + this._scrollChanged.read(reader); + const position = this._position.read(reader); + if (!position) { + return; + } + const visibleRanges = this._editor.getVisibleRanges(); + const isVisble = visibleRanges.some(range => { + return position.top >= range.startLineNumber && position.top <= range.endLineNumber; + }); + if (!isVisble) { + this._nodes.style.display = 'none'; + } + else { + this._nodes.style.display = 'block'; + } + this._editor.layoutOverlayWidget(this); + })); + } + + getId(): string { return this.id; } + + getDomNode(): HTMLElement { + return this._nodes; + } + + getPosition(): IOverlayWidgetPosition | null { + const position = this._position.get(); + if (!position) { + return null; + } + const layoutInfo = this._editor.getLayoutInfo(); + const visibPos = this._editor.getScrolledVisiblePosition(new Position(position.top, 1)); + if (!visibPos) { + return null; + } + const top = visibPos.top; + const left = layoutInfo.contentLeft + this._editor.getOffsetForColumn(position.left.lineNumber, position.left.column) + 10; + return { + preference: { + left, + top, + } + }; + } +} diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index c7e413801cc..584faab47ac 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -4571,7 +4571,6 @@ declare namespace monaco.editor { * Does not clear active inline suggestions when the editor loses focus. */ keepOnBlur?: boolean; - backgroundColoring?: boolean; } export interface IBracketPairColorizationOptions { From e86f5306cc8c64e2eb6a3f1db54f3a2c585f2fad Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 19:45:05 +0200 Subject: [PATCH 0342/2222] SCM - use MutableDisposable in SCMInputWidgetToolbar (#221173) --- .../contrib/scm/browser/scmViewPane.ts | 21 +++++++------------ 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index c5de9db080e..67564cd4aab 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -2179,7 +2179,7 @@ class SCMInputWidgetToolbar extends WorkbenchToolBar { private _onDidChange = new Emitter(); readonly onDidChange: Event = this._onDidChange.event; - private readonly repositoryDisposables = new DisposableStore(); + private readonly _disposables = this._register(new MutableDisposable()); constructor( container: HTMLElement, @@ -2207,7 +2207,7 @@ class SCMInputWidgetToolbar extends WorkbenchToolBar { } public setInput(input: ISCMInput): void { - this.repositoryDisposables.clear(); + this._disposables.value = new DisposableStore(); const contextKeyService = this.contextKeyService.createOverlay([ ['scmProvider', input.repository.provider.contextValue], @@ -2215,7 +2215,7 @@ class SCMInputWidgetToolbar extends WorkbenchToolBar { ['scmProviderHasRootUri', !!input.repository.provider.rootUri] ]); - const menu = this.repositoryDisposables.add(this.menuService.createMenu(MenuId.SCMInputBox, contextKeyService, { emitEventsForSubmenuChanges: true })); + const menu = this._disposables.value.add(this.menuService.createMenu(MenuId.SCMInputBox, contextKeyService, { emitEventsForSubmenuChanges: true })); const isEnabled = (): boolean => { return input.repository.provider.groups.some(g => g.resources.length > 0); @@ -2245,18 +2245,18 @@ class SCMInputWidgetToolbar extends WorkbenchToolBar { this._onDidChange.fire(); }; - this.repositoryDisposables.add(menu.onDidChange(() => updateToolbar())); - this.repositoryDisposables.add(input.repository.provider.onDidChangeResources(() => updateToolbar())); - this.repositoryDisposables.add(this.storageService.onDidChangeValue(StorageScope.PROFILE, SCMInputWidgetStorageKey.LastActionId, this.repositoryDisposables)(() => updateToolbar())); + this._disposables.value.add(menu.onDidChange(() => updateToolbar())); + this._disposables.value.add(input.repository.provider.onDidChangeResources(() => updateToolbar())); + this._disposables.value.add(this.storageService.onDidChangeValue(StorageScope.PROFILE, SCMInputWidgetStorageKey.LastActionId, this._disposables.value)(() => updateToolbar())); this.actionRunner = new SCMInputWidgetActionRunner(input, this.storageService); - this.repositoryDisposables.add(this.actionRunner.onWillRun(e => { + this._disposables.value.add(this.actionRunner.onWillRun(e => { if ((this.actionRunner as SCMInputWidgetActionRunner).runningActions.size === 0) { super.setActions([this._cancelAction], []); this._onDidChange.fire(); } })); - this.repositoryDisposables.add(this.actionRunner.onDidRun(e => { + this._disposables.value.add(this.actionRunner.onDidRun(e => { if ((this.actionRunner as SCMInputWidgetActionRunner).runningActions.size === 0) { updateToolbar(); } @@ -2264,11 +2264,6 @@ class SCMInputWidgetToolbar extends WorkbenchToolBar { updateToolbar(); } - - override dispose(): void { - this.repositoryDisposables.dispose(); - super.dispose(); - } } class SCMInputWidgetEditorOptions { From 9e4f826b181b0e04c66a80b9e83e5fd6356cda4f Mon Sep 17 00:00:00 2001 From: Parasaran <74203806+Parasaran-Python@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:48:04 +0530 Subject: [PATCH 0343/2222] Fixes #218254 (#219312) onDidShowContextMenu fires for every webview even though only one webview has the context menu visible, hence removed the onDidShow listener, sending the set-context-menu-visible event to the html on did-context-menu event so that the context-menu-visible class gets applied only to the webview that actually has the context menu visible --- src/vs/workbench/contrib/webview/browser/webviewElement.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index 5d6e403159f..8870ddf4cc6 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -265,6 +265,7 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD y: elementBox.y + data.clientY }) }); + this._send('set-context-menu-visible', { visible: true }); })); this._register(this.on('load-resource', async (entry) => { @@ -294,7 +295,6 @@ export class WebviewElement extends Disposable implements IWebview, WebviewFindD this._register(Event.runAndSubscribe(webviewThemeDataProvider.onThemeDataChanged, () => this.style())); this._register(_accessibilityService.onDidChangeReducedMotion(() => this.style())); this._register(_accessibilityService.onDidChangeScreenReaderOptimized(() => this.style())); - this._register(contextMenuService.onDidShowContextMenu(() => this._send('set-context-menu-visible', { visible: true }))); this._register(contextMenuService.onDidHideContextMenu(() => this._send('set-context-menu-visible', { visible: false }))); this._confirmBeforeClose = configurationService.getValue('window.confirmBeforeClose'); From 076e5f82ffb7de93997bffb904c3455a4a836474 Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Mon, 8 Jul 2024 11:29:26 -0700 Subject: [PATCH 0344/2222] Fix lint issue --- src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts index e881337feb4..b04436619d3 100644 --- a/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts +++ b/src/vs/editor/browser/view/gpu/textureAtlasAllocator.ts @@ -165,7 +165,7 @@ export class TextureAtlasSlabAllocator implements ITextureAtlasAllocator { // Find ideal slab, creating it if there is none suitable const glyphWidth = rasterizedGlyph.boundingBox.right - rasterizedGlyph.boundingBox.left + 1; const glyphHeight = rasterizedGlyph.boundingBox.bottom - rasterizedGlyph.boundingBox.top + 1; - const dpr = getActiveWindow().devicePixelRatio; + // const dpr = getActiveWindow().devicePixelRatio; // TODO: Include font size as well as DPR in nearestXPixels calculation From 1cd7cf5decbe06cb42efdb03c44d19e4f9b2ba58 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Mon, 8 Jul 2024 11:33:21 -0700 Subject: [PATCH 0345/2222] Update MD grammar (#221204) --- extensions/markdown-basics/cgmanifest.json | 2 +- .../syntaxes/markdown.tmLanguage.json | 32 +++++++++++++++---- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/extensions/markdown-basics/cgmanifest.json b/extensions/markdown-basics/cgmanifest.json index 60c6b192bed..380b0c74ac6 100644 --- a/extensions/markdown-basics/cgmanifest.json +++ b/extensions/markdown-basics/cgmanifest.json @@ -33,7 +33,7 @@ "git": { "name": "microsoft/vscode-markdown-tm-grammar", "repositoryUrl": "https://github.com/microsoft/vscode-markdown-tm-grammar", - "commitHash": "f75d5f55730e72ee7ff386841949048b2395e440" + "commitHash": "7418dd20d76c72e82fadee2909e03239e9973b35" } }, "license": "MIT", diff --git a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json index c84c468b80c..9761ca716ab 100644 --- a/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json +++ b/extensions/markdown-basics/syntaxes/markdown.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/f75d5f55730e72ee7ff386841949048b2395e440", + "version": "https://github.com/microsoft/vscode-markdown-tm-grammar/commit/7418dd20d76c72e82fadee2909e03239e9973b35", "name": "Markdown", "scopeName": "text.html.markdown", "patterns": [ @@ -2480,14 +2480,34 @@ "name": "meta.separator.markdown" }, "frontMatter": { - "begin": "\\A-{3}\\s*$", - "contentName": "meta.embedded.block.frontmatter", + "begin": "\\A(?=(-{3,}))", + "end": "^ {,3}\\1-*[ \\t]*$|^[ \\t]*\\.{3}$", + "applyEndPatternLast": 1, + "endCaptures": { + "0": { + "name": "punctuation.definition.end.frontmatter" + } + }, "patterns": [ { - "include": "source.yaml" + "begin": "\\A(-{3,})(.*)$", + "while": "^(?! {,3}\\1-*[ \\t]*$|[ \\t]*\\.{3}$)", + "beginCaptures": { + "1": { + "name": "punctuation.definition.begin.frontmatter" + }, + "2": { + "name": "comment.frontmatter" + } + }, + "contentName": "meta.embedded.block.frontmatter", + "patterns": [ + { + "include": "source.yaml" + } + ] } - ], - "end": "(^|\\G)-{3}|\\.{3}\\s*$" + ] }, "table": { "name": "markup.table.markdown", From f3930566bd216a35403d020ed34460f36721a951 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 20:51:52 +0200 Subject: [PATCH 0346/2222] =?UTF-8?q?Git=20-=20=F0=9F=92=84=20fix=20log=20?= =?UTF-8?q?message=20format=20(#221218)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- extensions/git/src/model.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/extensions/git/src/model.ts b/extensions/git/src/model.ts index d4e00449e20..48e66d5e9ff 100644 --- a/extensions/git/src/model.ts +++ b/extensions/git/src/model.ts @@ -325,7 +325,7 @@ export class Model implements IRepositoryResolver, IBranchProtectionProviderRegi } */ this.telemetryReporter.sendTelemetryEvent('git.repositoryInitialScan', { autoRepositoryDetection: String(autoRepositoryDetection) }, { repositoryCount: this.openRepositories.length }); - this.logger.info(`[Model][doInitialScan] Initial repository scan completed - repositories(${this.repositories.length}), closed repositories (${this.closedRepositories.length}), parent repositories (${this.parentRepositories.length}), unsafe repositories (${this.unsafeRepositories.length})`); + this.logger.info(`[Model][doInitialScan] Initial repository scan completed - repositories (${this.repositories.length}), closed repositories (${this.closedRepositories.length}), parent repositories (${this.parentRepositories.length}), unsafe repositories (${this.unsafeRepositories.length})`); } /** From 63274b60e78b1ad48e9e945c59850c6e3c5d7be8 Mon Sep 17 00:00:00 2001 From: Teik Seong <53546313+mxts@users.noreply.github.com> Date: Tue, 9 Jul 2024 03:32:44 +0800 Subject: [PATCH 0347/2222] add option to dock terminal at top (#207721) * add option to dock terminal at top * fixed formatting * Update editorPart.ts requested changes * panel position fixes and cleanup --------- Co-authored-by: BeniBenj --- src/vs/workbench/browser/layout.ts | 75 +++++++++++-------- .../browser/parts/editor/editorPart.ts | 4 + .../browser/parts/panel/media/panelpart.css | 17 +++++ .../browser/parts/panel/panelActions.ts | 12 +-- .../browser/parts/panel/panelPart.ts | 15 +++- .../browser/parts/views/viewPaneContainer.ts | 7 +- .../browser/workbench.contribution.ts | 4 +- .../contrib/terminal/browser/terminalGroup.ts | 18 +++-- .../terminal/browser/terminalInstance.ts | 4 +- .../services/layout/browser/layoutService.ts | 11 ++- 10 files changed, 108 insertions(+), 59 deletions(-) diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index b8232cb8490..6ad52a2695e 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -12,7 +12,7 @@ import { isWindows, isLinux, isMacintosh, isWeb, isIOS } from 'vs/base/common/pl import { EditorInputCapabilities, GroupIdentifier, isResourceEditorInput, IUntypedEditorInput, pathsToEditors } from 'vs/workbench/common/editor'; import { SidebarPart } from 'vs/workbench/browser/parts/sidebar/sidebarPart'; import { PanelPart } from 'vs/workbench/browser/parts/panel/panelPart'; -import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment, ActivityBarPosition, LayoutSettings, MULTI_WINDOW_PARTS, SINGLE_WINDOW_PARTS, ZenModeSettings, EditorTabsMode, EditorActionsLocation, shouldShowCustomTitleBar } from 'vs/workbench/services/layout/browser/layoutService'; +import { Position, Parts, PanelOpensMaximizedOptions, IWorkbenchLayoutService, positionFromString, positionToString, panelOpensMaximizedFromString, PanelAlignment, ActivityBarPosition, LayoutSettings, MULTI_WINDOW_PARTS, SINGLE_WINDOW_PARTS, ZenModeSettings, EditorTabsMode, EditorActionsLocation, shouldShowCustomTitleBar, isHorizontal } from 'vs/workbench/services/layout/browser/layoutService'; import { isTemporaryWorkspace, IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { IConfigurationChangeEvent, IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -1266,18 +1266,17 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const containerDimension = this.getContainerDimension(container); if (container === this.mainContainer) { - const panelPosition = this.getPanelPosition(); - const isColumn = panelPosition === Position.RIGHT || panelPosition === Position.LEFT; + const isPanelHorizontal = isHorizontal(this.getPanelPosition()); const takenWidth = (this.isVisible(Parts.ACTIVITYBAR_PART) ? this.activityBarPartView.minimumWidth : 0) + (this.isVisible(Parts.SIDEBAR_PART) ? this.sideBarPartView.minimumWidth : 0) + - (this.isVisible(Parts.PANEL_PART) && isColumn ? this.panelPartView.minimumWidth : 0) + + (this.isVisible(Parts.PANEL_PART) && !isPanelHorizontal ? this.panelPartView.minimumWidth : 0) + (this.isVisible(Parts.AUXILIARYBAR_PART) ? this.auxiliaryBarPartView.minimumWidth : 0); const takenHeight = (this.isVisible(Parts.TITLEBAR_PART, targetWindow) ? this.titleBarPartView.minimumHeight : 0) + (this.isVisible(Parts.STATUSBAR_PART, targetWindow) ? this.statusBarPartView.minimumHeight : 0) + - (this.isVisible(Parts.PANEL_PART) && !isColumn ? this.panelPartView.minimumHeight : 0); + (this.isVisible(Parts.PANEL_PART) && isPanelHorizontal ? this.panelPartView.minimumHeight : 0); const availableWidth = containerDimension.width - takenWidth; const availableHeight = containerDimension.height - takenHeight; @@ -1546,7 +1545,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Panel Size const panelSize = this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_HIDDEN) ? this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) - : (this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION) === Position.BOTTOM ? this.workbenchGrid.getViewSize(this.panelPartView).height : this.workbenchGrid.getViewSize(this.panelPartView).width); + : isHorizontal(this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION)) + ? this.workbenchGrid.getViewSize(this.panelPartView).height + : this.workbenchGrid.getViewSize(this.panelPartView).width; this.stateModel.setInitializationValue(LayoutStateKeys.PANEL_SIZE, panelSize as number); // Auxiliary Bar Size @@ -1635,8 +1636,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.workbenchGrid.resizeView(this.panelPartView, { - width: viewSize.width + (this.getPanelPosition() !== Position.BOTTOM ? sizeChangePxWidth : 0), - height: viewSize.height + (this.getPanelPosition() !== Position.BOTTOM ? 0 : sizeChangePxHeight) + width: viewSize.width + (isHorizontal(this.getPanelPosition()) ? 0 : sizeChangePxWidth), + height: viewSize.height + (isHorizontal(this.getPanelPosition()) ? sizeChangePxHeight : 0) }); break; @@ -1770,8 +1771,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private adjustPartPositions(sideBarPosition: Position, panelAlignment: PanelAlignment, panelPosition: Position): void { // Move activity bar and side bars - const sideBarSiblingToEditor = panelPosition !== Position.BOTTOM || !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left')); - const auxiliaryBarSiblingToEditor = panelPosition !== Position.BOTTOM || !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left')); + const isPanelVertical = !isHorizontal(panelPosition); + const sideBarSiblingToEditor = isPanelVertical || !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left')); + const auxiliaryBarSiblingToEditor = isPanelVertical || !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left')); const preMovePanelWidth = !this.isVisible(Parts.PANEL_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) ?? this.panelPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.panelPartView).width; const preMovePanelHeight = !this.isVisible(Parts.PANEL_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.panelPartView) ?? this.panelPartView.minimumHeight) : this.workbenchGrid.getViewSize(this.panelPartView).height; const preMoveSideBarSize = !this.isVisible(Parts.SIDEBAR_PART) ? Sizing.Invisible(this.workbenchGrid.getViewCachedVisibleSize(this.sideBarPartView) ?? this.sideBarPartView.minimumWidth) : this.workbenchGrid.getViewSize(this.sideBarPartView).width; @@ -1797,7 +1799,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // We moved all the side parts based on the editor and ignored the panel // Now, we need to put the panel back in the right position when it is next to the editor - if (panelPosition !== Position.BOTTOM) { + if (isPanelVertical) { this.workbenchGrid.moveView(this.panelPartView, preMovePanelWidth, this.editorPartView, panelPosition === Position.LEFT ? Direction.Left : Direction.Right); this.workbenchGrid.resizeView(this.panelPartView, { height: preMovePanelHeight as number, @@ -1824,8 +1826,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi setPanelAlignment(alignment: PanelAlignment, skipLayout?: boolean): void { - // Panel alignment only applies to a panel in the bottom position - if (this.getPanelPosition() !== Position.BOTTOM) { + // Panel alignment only applies to a panel in the top/bottom position + if (!isHorizontal(this.getPanelPosition())) { this.setPanelPosition(Position.BOTTOM); } @@ -1921,7 +1923,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const isMaximized = this.isPanelMaximized(); if (!isMaximized) { if (this.isVisible(Parts.PANEL_PART)) { - if (panelPosition === Position.BOTTOM) { + if (isHorizontal(panelPosition)) { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT, size.height); } else { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH, size.width); @@ -1932,8 +1934,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } else { this.setEditorHidden(false); this.workbenchGrid.resizeView(this.panelPartView, { - width: panelPosition === Position.BOTTOM ? size.width : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH), - height: panelPosition === Position.BOTTOM ? this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT) : size.height + width: isHorizontal(panelPosition) ? size.width : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH), + height: isHorizontal(panelPosition) ? this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT) : size.height }); } @@ -1943,7 +1945,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi private panelOpensMaximized(): boolean { // The workbench grid currently prevents us from supporting panel maximization with non-center panel alignment - if (this.getPanelAlignment() !== 'center' && this.getPanelPosition() === Position.BOTTOM) { + if (this.getPanelAlignment() !== 'center' && isHorizontal(this.getPanelPosition())) { return false; } @@ -2021,7 +2023,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi isPanelMaximized(): boolean { // the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment - return (this.getPanelAlignment() === 'center' || this.getPanelPosition() !== Position.BOTTOM) && !this.isVisible(Parts.EDITOR_PART, mainWindow); + return (this.getPanelAlignment() === 'center' || !isHorizontal(this.getPanelPosition())) && !this.isVisible(Parts.EDITOR_PART, mainWindow); } getSideBarPosition(): Position { @@ -2097,14 +2099,14 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi // Save the current size of the panel for the new orthogonal direction // If moving down, save the width of the panel // Otherwise, save the height of the panel - if (position === Position.BOTTOM) { + if (isHorizontal(position)) { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH, size.width); - } else if (positionFromString(oldPositionValue) === Position.BOTTOM) { + } else if (isHorizontal(positionFromString(oldPositionValue))) { this.stateModel.setRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT, size.height); } } - if (position === Position.BOTTOM && this.getPanelAlignment() !== 'center' && editorHidden) { + if (isHorizontal(position) && this.getPanelAlignment() !== 'center' && editorHidden) { this.toggleMaximizedPanel(); editorHidden = false; } @@ -2116,6 +2118,8 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi if (position === Position.BOTTOM) { this.workbenchGrid.moveView(this.panelPartView, editorHidden ? size.height : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT), this.editorPartView, Direction.Down); + } else if (position === Position.TOP) { + this.workbenchGrid.moveView(this.panelPartView, editorHidden ? size.height : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_HEIGHT), this.editorPartView, Direction.Up); } else if (position === Position.RIGHT) { this.workbenchGrid.moveView(this.panelPartView, editorHidden ? size.width : this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_LAST_NON_MAXIMIZED_WIDTH), this.editorPartView, Direction.Right); } else { @@ -2133,7 +2137,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi this.setAuxiliaryBarHidden(true); } - if (position === Position.BOTTOM) { + if (isHorizontal(position)) { this.adjustPartPositions(this.getSideBarPosition(), this.getPanelAlignment(), position); } @@ -2242,17 +2246,20 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi const auxiliaryBarSize = this.stateModel.getRuntimeValue(LayoutStateKeys.AUXILIARYBAR_HIDDEN) ? 0 : nodes.auxiliaryBar.size; const panelSize = this.stateModel.getInitializationValue(LayoutStateKeys.PANEL_SIZE) ? 0 : nodes.panel.size; + const panelPostion = this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION); + const sideBarPosition = this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_POSITON); + const result = [] as ISerializedNode[]; - if (this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION) !== Position.BOTTOM) { + if (!isHorizontal(panelPostion)) { result.push(nodes.editor); nodes.editor.size = availableWidth - activityBarSize - sideBarSize - panelSize - auxiliaryBarSize; - if (this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_POSITION) === Position.RIGHT) { + if (panelPostion === Position.RIGHT) { result.push(nodes.panel); } else { result.splice(0, 0, nodes.panel); } - if (this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_POSITON) === Position.LEFT) { + if (sideBarPosition === Position.LEFT) { result.push(nodes.auxiliaryBar); result.splice(0, 0, nodes.sideBar); result.splice(0, 0, nodes.activityBar); @@ -2263,18 +2270,20 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } } else { const panelAlignment = this.stateModel.getRuntimeValue(LayoutStateKeys.PANEL_ALIGNMENT); - const sideBarPosition = this.stateModel.getRuntimeValue(LayoutStateKeys.SIDEBAR_POSITON); const sideBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.LEFT && panelAlignment === 'right') || (sideBarPosition === Position.RIGHT && panelAlignment === 'left')); const auxiliaryBarNextToEditor = !(panelAlignment === 'center' || (sideBarPosition === Position.RIGHT && panelAlignment === 'right') || (sideBarPosition === Position.LEFT && panelAlignment === 'left')); const editorSectionWidth = availableWidth - activityBarSize - (sideBarNextToEditor ? 0 : sideBarSize) - (auxiliaryBarNextToEditor ? 0 : auxiliaryBarSize); + + const editorNodes = this.arrangeEditorNodes({ + editor: nodes.editor, + sideBar: sideBarNextToEditor ? nodes.sideBar : undefined, + auxiliaryBar: auxiliaryBarNextToEditor ? nodes.auxiliaryBar : undefined + }, availableHeight - panelSize, editorSectionWidth); + result.push({ type: 'branch', - data: [this.arrangeEditorNodes({ - editor: nodes.editor, - sideBar: sideBarNextToEditor ? nodes.sideBar : undefined, - auxiliaryBar: auxiliaryBarNextToEditor ? nodes.auxiliaryBar : undefined - }, availableHeight - panelSize, editorSectionWidth), nodes.panel], + data: panelPostion === Position.BOTTOM ? [editorNodes, nodes.panel] : [nodes.panel, editorNodes], size: editorSectionWidth }); @@ -2417,7 +2426,7 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi panelVisible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether or the not the panel is visible' }; statusbarVisible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether or the not the status bar is visible' }; sideBarPosition: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the primary side bar is on the left or right' }; - panelPosition: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the panel is on the bottom, left, or right' }; + panelPosition: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the panel is on the top, bottom, left, or right' }; }; const layoutDescriptor: StartupLayoutEvent = { @@ -2625,7 +2634,7 @@ class LayoutStateModel extends Disposable { LayoutStateKeys.GRID_SIZE.defaultValue = { height: workbenchDimensions.height, width: workbenchDimensions.width }; LayoutStateKeys.SIDEBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4); LayoutStateKeys.AUXILIARYBAR_SIZE.defaultValue = Math.min(300, workbenchDimensions.width / 4); - LayoutStateKeys.PANEL_SIZE.defaultValue = (this.stateCache.get(LayoutStateKeys.PANEL_POSITION.name) ?? LayoutStateKeys.PANEL_POSITION.defaultValue) === Position.BOTTOM ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4; + LayoutStateKeys.PANEL_SIZE.defaultValue = (this.stateCache.get(LayoutStateKeys.PANEL_POSITION.name) ?? isHorizontal(LayoutStateKeys.PANEL_POSITION.defaultValue)) ? workbenchDimensions.height / 3 : workbenchDimensions.width / 4; LayoutStateKeys.SIDEBAR_HIDDEN.defaultValue = this.contextService.getWorkbenchState() === WorkbenchState.EMPTY; // Apply all defaults diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index d785a513798..fbdfd7daf7d 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -1103,6 +1103,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { openVerticalPosition = Position.BOTTOM; } + if (e.eventData.clientY < boundingRect.top + proximity) { + openVerticalPosition = Position.TOP; + } + if (horizontalOpenerTimeout && openHorizontalPosition !== lastOpenHorizontalPosition) { clearTimeout(horizontalOpenerTimeout); horizontalOpenerTimeout = undefined; diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 40a5ee28faf..e1c147d8e88 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -17,6 +17,15 @@ border-top-width: 0; /* no border when main editor area is hiden */ } +.monaco-workbench .part.panel.top { + border-bottom-width: 1px; + border-bottom-style: solid; +} + +.monaco-workbench.nomaineditorarea .part.panel.top { + border-bottom-width: 0; /* no border when main editor area is hiden */ +} + .monaco-workbench .part.panel.right { border-left-width: 1px; border-left-style: solid; @@ -81,3 +90,11 @@ display: inline-block; transform: rotate(90deg); } + +/* Rotate icons when panel is on left */ +.monaco-workbench .part.basepanel.top .title-actions .codicon-split-horizontal::before, +.monaco-workbench .part.basepanel.top .global-actions .codicon-panel-maximize::before, +.monaco-workbench .part.basepanel.top .global-actions .codicon-panel-restore::before { + display: inline-block; + transform: rotate(180deg); +} diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 534db0283a7..3e97968289d 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -8,7 +8,7 @@ import { localize, localize2 } from 'vs/nls'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { MenuId, MenuRegistry, registerAction2, Action2, IAction2Options } from 'vs/platform/actions/common/actions'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { ActivityBarPosition, IWorkbenchLayoutService, LayoutSettings, PanelAlignment, Parts, Position, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; +import { ActivityBarPosition, isHorizontal, IWorkbenchLayoutService, LayoutSettings, PanelAlignment, Parts, Position, positionToString } from 'vs/workbench/services/layout/browser/layoutService'; import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelMaximizedContext, PanelPositionContext, PanelVisibleContext } from 'vs/workbench/common/contextkeys'; import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; import { Codicon } from 'vs/base/common/codicons'; @@ -99,6 +99,7 @@ const PositionPanelActionId = { LEFT: 'workbench.action.positionPanelLeft', RIGHT: 'workbench.action.positionPanelRight', BOTTOM: 'workbench.action.positionPanelBottom', + TOP: 'workbench.action.positionPanelTop' }; const AlignPanelActionId = { @@ -136,6 +137,7 @@ function createAlignmentPanelActionConfig(id: string, title: ICommandActionTitle const PositionPanelActionConfigs: PanelActionConfig[] = [ + createPositionPanelActionConfig(PositionPanelActionId.TOP, localize2('positionPanelTop', "Move Panel To Top"), localize('positionPanelTopShort', "Top"), Position.TOP), createPositionPanelActionConfig(PositionPanelActionId.LEFT, localize2('positionPanelLeft', "Move Panel Left"), localize('positionPanelLeftShort', "Left"), Position.LEFT), createPositionPanelActionConfig(PositionPanelActionId.RIGHT, localize2('positionPanelRight', "Move Panel Right"), localize('positionPanelRightShort', "Right"), Position.RIGHT), createPositionPanelActionConfig(PositionPanelActionId.BOTTOM, localize2('positionPanelBottom', "Move Panel To Bottom"), localize('positionPanelBottomShort', "Bottom"), Position.BOTTOM), @@ -158,7 +160,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarAppearanceMenu, { order: 4 }); -PositionPanelActionConfigs.forEach(positionPanelAction => { +PositionPanelActionConfigs.forEach((positionPanelAction, index) => { const { id, title, shortLabel, value, when } = positionPanelAction; registerAction2(class extends Action2 { @@ -182,7 +184,7 @@ PositionPanelActionConfigs.forEach(positionPanelAction => { title: shortLabel, toggled: when.negate() }, - order: 5 + order: 5 + index }); }); @@ -280,7 +282,7 @@ registerAction2(class extends Action2 { tooltip: localize('maximizePanel', "Maximize Panel Size"), category: Categories.View, f1: true, - icon: maximizeIcon, + icon: maximizeIcon, // This is being rotated in CSS depending on the panel position // the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment precondition: ContextKeyExpr.or(PanelAlignmentContext.isEqualTo('center'), PanelPositionContext.notEqualsTo('bottom')), toggled: { condition: PanelMaximizedContext, icon: restoreIcon, tooltip: localize('minimizePanel', "Restore Panel Size") }, @@ -296,7 +298,7 @@ registerAction2(class extends Action2 { run(accessor: ServicesAccessor) { const layoutService = accessor.get(IWorkbenchLayoutService); const notificationService = accessor.get(INotificationService); - if (layoutService.getPanelAlignment() !== 'center' && layoutService.getPanelPosition() === Position.BOTTOM) { + if (layoutService.getPanelAlignment() !== 'center' && isHorizontal(layoutService.getPanelPosition())) { notificationService.warn(localize('panelMaxNotSupported', "Maximizing the panel is only supported when it is center aligned.")); return; } diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index 845274fa940..899d97b4cda 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -112,6 +112,7 @@ export class PanelPart extends AbstractPaneCompositePart { const borderColor = this.getColor(PANEL_BORDER) || this.getColor(contrastBorder) || ''; container.style.borderLeftColor = borderColor; container.style.borderRightColor = borderColor; + container.style.borderBottomColor = borderColor; const title = this.getTitleArea(); if (title) { @@ -166,10 +167,16 @@ export class PanelPart extends AbstractPaneCompositePart { override layout(width: number, height: number, top: number, left: number): void { let dimensions: Dimension; - if (this.layoutService.getPanelPosition() === Position.RIGHT) { - dimensions = new Dimension(width - 1, height); // Take into account the 1px border when layouting - } else { - dimensions = new Dimension(width, height); + switch (this.layoutService.getPanelPosition()) { + case Position.RIGHT: + dimensions = new Dimension(width - 1, height); // Take into account the 1px border when layouting + break; + case Position.TOP: + dimensions = new Dimension(width, height - 1); // Take into account the 1px border when layouting + break; + default: + dimensions = new Dimension(width, height); + break; } // Layout contents diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index d782b08c3c2..01df5c12a69 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -39,7 +39,7 @@ import { IAddedViewDescriptorRef, ICustomViewDescriptor, IView, IViewContainerMo import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; import { FocusedViewContext } from 'vs/workbench/common/contextkeys'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IWorkbenchLayoutService, LayoutSettings, Position } from 'vs/workbench/services/layout/browser/layoutService'; +import { isHorizontal, IWorkbenchLayoutService, LayoutSettings } from 'vs/workbench/services/layout/browser/layoutService'; import { IBaseActionViewItemOptions } from 'vs/base/browser/ui/actionbar/actionViewItems'; export const ViewsSubMenu = new MenuId('Views'); @@ -625,8 +625,9 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { case ViewContainerLocation.Sidebar: case ViewContainerLocation.AuxiliaryBar: return Orientation.VERTICAL; - case ViewContainerLocation.Panel: - return this.layoutService.getPanelPosition() === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + case ViewContainerLocation.Panel: { + return isHorizontal(this.layoutService.getPanelPosition()) ? Orientation.HORIZONTAL : Orientation.VERTICAL; + } } return Orientation.VERTICAL; diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index fb05198e50b..d83ac066c6e 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -516,9 +516,9 @@ const registry = Registry.as(ConfigurationExtensions.Con }, 'workbench.panel.defaultLocation': { 'type': 'string', - 'enum': ['left', 'bottom', 'right'], + 'enum': ['left', 'bottom', 'top', 'right'], 'default': 'bottom', - 'description': localize('panelDefaultLocation', "Controls the default location of the panel (Terminal, Debug Console, Output, Problems) in a new workspace. It can either show at the bottom, right, or left of the editor area."), + 'description': localize('panelDefaultLocation', "Controls the default location of the panel (Terminal, Debug Console, Output, Problems) in a new workspace. It can either show at the bottom, top, right, or left of the editor area."), }, 'workbench.panel.opensMaximized': { 'type': 'string', diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 57848596b73..8e9ddcb5b6c 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -7,7 +7,7 @@ import { TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal' import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore, dispose, toDisposable } from 'vs/base/common/lifecycle'; import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; -import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; +import { isHorizontal, IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITerminalInstance, Direction, ITerminalGroup, ITerminalInstanceService, ITerminalConfigurationService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; @@ -285,7 +285,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { if (this._container) { this.attachToElement(this._container); } - this._onPanelOrientationChanged.fire(this._terminalLocation === ViewContainerLocation.Panel && this._panelPosition === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL); + this._onPanelOrientationChanged.fire(this._terminalLocation === ViewContainerLocation.Panel && isHorizontal(this._panelPosition) ? Orientation.HORIZONTAL : Orientation.VERTICAL); this._register(toDisposable(() => { if (this._container && this._groupElement) { this._groupElement.remove(); @@ -466,7 +466,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { if (!this._splitPaneContainer) { this._panelPosition = this._layoutService.getPanelPosition(); this._terminalLocation = this._viewDescriptorService.getViewLocationById(TERMINAL_VIEW_ID)!; - const orientation = this._terminalLocation === ViewContainerLocation.Panel && this._panelPosition === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + const orientation = this._terminalLocation === ViewContainerLocation.Panel && isHorizontal(this._panelPosition) ? Orientation.HORIZONTAL : Orientation.VERTICAL; this._splitPaneContainer = this._instantiationService.createInstance(SplitPaneContainer, this._groupElement, orientation); this.terminalInstances.forEach(instance => this._splitPaneContainer!.split(instance, this._activeInstanceIndex + 1)); } @@ -527,7 +527,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { const newTerminalLocation = this._viewDescriptorService.getViewLocationById(TERMINAL_VIEW_ID)!; const terminalPositionChanged = newPanelPosition !== this._panelPosition || newTerminalLocation !== this._terminalLocation; if (terminalPositionChanged) { - const newOrientation = newTerminalLocation === ViewContainerLocation.Panel && newPanelPosition === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + const newOrientation = newTerminalLocation === ViewContainerLocation.Panel && isHorizontal(newPanelPosition) ? Orientation.HORIZONTAL : Orientation.VERTICAL; this._splitPaneContainer.setOrientation(newOrientation); this._panelPosition = newPanelPosition; this._terminalLocation = newTerminalLocation; @@ -563,7 +563,7 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { } private _getOrientation(): Orientation { - return this._getPosition() === Position.BOTTOM ? Orientation.HORIZONTAL : Orientation.VERTICAL; + return isHorizontal(this._getPosition()) ? Orientation.HORIZONTAL : Orientation.VERTICAL; } resizePane(direction: Direction): void { @@ -588,10 +588,12 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { if (shouldResizePart) { + const position = this._getPosition(); const shouldShrink = - (this._getPosition() === Position.LEFT && direction === Direction.Left) || - (this._getPosition() === Position.RIGHT && direction === Direction.Right) || - (this._getPosition() === Position.BOTTOM && direction === Direction.Down); + (position === Position.LEFT && direction === Direction.Left) || + (position === Position.RIGHT && direction === Direction.Right) || + (position === Position.BOTTOM && direction === Direction.Down) || + (position === Position.TOP && direction === Direction.Up); if (shouldShrink) { resizeAmount *= -1; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index a48841d9802..6310485b23b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -79,7 +79,7 @@ import { getWorkspaceForTerminal, preparePathForShell } from 'vs/workbench/contr import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; -import { IWorkbenchLayoutService, Position } from 'vs/workbench/services/layout/browser/layoutService'; +import { isHorizontal, IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IPathService } from 'vs/workbench/services/path/common/pathService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { importAMDNodeModule } from 'vs/amdX'; @@ -2413,7 +2413,7 @@ class TerminalInstanceDragAndDropController extends Disposable implements dom.ID private _getViewOrientation(): Orientation { const panelPosition = this._layoutService.getPanelPosition(); const terminalLocation = this._viewDescriptorService.getViewLocationById(TERMINAL_VIEW_ID); - return terminalLocation === ViewContainerLocation.Panel && panelPosition === Position.BOTTOM + return terminalLocation === ViewContainerLocation.Panel && isHorizontal(panelPosition) ? Orientation.HORIZONTAL : Orientation.VERTICAL; } diff --git a/src/vs/workbench/services/layout/browser/layoutService.ts b/src/vs/workbench/services/layout/browser/layoutService.ts index 2955a53995c..43e124fa05f 100644 --- a/src/vs/workbench/services/layout/browser/layoutService.ts +++ b/src/vs/workbench/services/layout/browser/layoutService.ts @@ -70,7 +70,12 @@ export const enum EditorActionsLocation { export const enum Position { LEFT, RIGHT, - BOTTOM + BOTTOM, + TOP +} + +export function isHorizontal(position: Position): boolean { + return position === Position.BOTTOM || position === Position.TOP; } export const enum PanelOpensMaximizedOptions { @@ -86,6 +91,7 @@ export function positionToString(position: Position): string { case Position.LEFT: return 'left'; case Position.RIGHT: return 'right'; case Position.BOTTOM: return 'bottom'; + case Position.TOP: return 'top'; default: return 'bottom'; } } @@ -93,7 +99,8 @@ export function positionToString(position: Position): string { const positionsByString: { [key: string]: Position } = { [positionToString(Position.LEFT)]: Position.LEFT, [positionToString(Position.RIGHT)]: Position.RIGHT, - [positionToString(Position.BOTTOM)]: Position.BOTTOM + [positionToString(Position.BOTTOM)]: Position.BOTTOM, + [positionToString(Position.TOP)]: Position.TOP }; export function positionFromString(str: string): Position { From 33abd96b99731bcfb9933ecd3d4410d2afe9d865 Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Mon, 8 Jul 2024 22:08:24 +0200 Subject: [PATCH 0348/2222] Git - add actions to incoming/outgoing header (#221221) --- extensions/git/package.json | 23 +++++++ .../common/extensionsApiProposals.ts | 3 + .../contrib/scm/browser/scmViewPane.ts | 63 ++++++++++++++----- .../actions/common/menusExtensionPoint.ts | 6 ++ ...ibSourceControlHistoryItemChangesMenu.d.ts | 7 +++ 5 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts diff --git a/extensions/git/package.json b/extensions/git/package.json index f2445eb3978..ccac6222db9 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -16,6 +16,7 @@ "contribMergeEditorMenus", "contribMultiDiffEditorMenus", "contribDiffEditorGutterToolBarMenus", + "contribSourceControlHistoryItemChangesMenu", "contribSourceControlHistoryItemGroupMenu", "contribSourceControlHistoryItemMenu", "contribSourceControlInputBoxMenu", @@ -1915,6 +1916,28 @@ "group": "1_modification@3" } ], + "scm/historyItemChanges/title": [ + { + "command": "git.fetchRef", + "group": "navigation@1", + "when": "scmProvider == git && scmHistoryItemGroupHasRemote" + }, + { + "command": "git.pullRef", + "group": "navigation@2", + "when": "scmProvider == git && scmHistoryItemGroupHasRemote" + }, + { + "command": "git.pushRef", + "when": "scmProvider == git && scmHistoryItemGroupHasRemote", + "group": "navigation@3" + }, + { + "command": "git.publish", + "when": "scmProvider == git && !scmHistoryItemGroupHasRemote", + "group": "navigation@3" + } + ], "scm/incomingChanges": [ { "command": "git.fetchRef", diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index a3f677336d1..dd767bf8a07 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -118,6 +118,9 @@ const _allApiProposals = { contribShareMenu: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribShareMenu.d.ts', }, + contribSourceControlHistoryItemChangesMenu: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts', + }, contribSourceControlHistoryItemGroupMenu: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemGroupMenu.d.ts', }, diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 67564cd4aab..77d39d94f9d 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -96,7 +96,7 @@ import { EditOperation } from 'vs/editor/common/core/editOperation'; import { stripIcons } from 'vs/base/common/iconLabels'; import { IconLabel } from 'vs/base/browser/ui/iconLabel/iconLabel'; import { editorSelectionBackground, foreground, inputBackground, inputForeground, listActiveSelectionForeground, registerColor, selectionBackground, transparent } from 'vs/platform/theme/common/colorRegistry'; -import { IMenuWorkbenchToolBarOptions, MenuWorkbenchToolBar, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { IMenuWorkbenchToolBarOptions, WorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { DropdownWithPrimaryActionViewItem } from 'vs/platform/actions/browser/dropdownWithPrimaryActionViewItem'; import { clamp, rot } from 'vs/base/common/numbers'; @@ -1201,7 +1201,9 @@ class HistoryItemChangeRenderer implements ICompressibleTreeRenderer { @@ -1226,31 +1228,46 @@ class SeparatorRenderer implements ICompressibleTreeRenderer('scm.experimental.showHistoryGraph') !== true) { - const toolBar = new MenuWorkbenchToolBar(append(element, $('.actions')), MenuId.SCMChangesSeparator, { moreIcon: Codicon.gear }, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.commandService, this.telemetryService); - disposables.add(toolBar); - } + const options = { moreIcon: this.configurationService.getValue('scm.experimental.showHistoryGraph') === true ? Codicon.more : Codicon.gear } satisfies IMenuWorkbenchToolBarOptions; + const toolBar = new WorkbenchToolBar(append(element, $('.actions')), options, this.menuService, this.contextKeyService, this.contextMenuService, this.keybindingService, this.commandService, this.telemetryService); + templateDisposables.add(toolBar); - return { label, disposables }; + return { label, toolBar, elementDisposables: new DisposableStore(), templateDisposables }; } renderElement(element: ITreeNode, index: number, templateData: SeparatorTemplate, height: number | undefined): void { + const currentHistoryItemGroup = element.element.repository.provider.historyProvider?.currentHistoryItemGroup; + + // Label templateData.label.setLabel(element.element.label, undefined, { title: element.element.ariaLabel }); + + // Toolbar + const contextKeyService = this.contextKeyService.createOverlay([ + ['scmHistoryItemGroupHasRemote', !!currentHistoryItemGroup?.remote], + ]); + const menu = this.menuService.createMenu(MenuId.SCMChangesSeparator, contextKeyService); + templateData.elementDisposables.add(connectPrimaryMenu(menu, (primary, secondary) => { + templateData.toolBar.setActions(primary, secondary, [MenuId.SCMChangesSeparator]); + })); } renderCompressedElements(node: ITreeNode, void>, index: number, templateData: SeparatorTemplate, height: number | undefined): void { throw new Error('Should never happen since node is incompressible'); } - disposeTemplate(templateData: SeparatorTemplate): void { - templateData.disposables.dispose(); + disposeElement(node: ITreeNode, index: number, templateData: SeparatorTemplate, height: number | undefined): void { + templateData.elementDisposables.clear(); } + disposeTemplate(templateData: SeparatorTemplate): void { + templateData.elementDisposables.dispose(); + templateData.templateDisposables.dispose(); + } } class ListDelegate implements IListVirtualDelegate { @@ -1672,14 +1689,16 @@ MenuRegistry.appendMenuItem(MenuId.SCMChangesSeparator, { title: localize('incomingChanges', "Show Incoming Changes"), submenu: MenuId.SCMIncomingChangesSetting, group: '1_incoming&outgoing', - order: 1 + order: 1, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) }); MenuRegistry.appendMenuItem(Menus.ChangesSettings, { title: localize('incomingChanges', "Show Incoming Changes"), submenu: MenuId.SCMIncomingChangesSetting, group: '1_incoming&outgoing', - order: 1 + order: 1, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) }); registerAction2(class extends SCMChangesSettingAction { @@ -1723,14 +1742,16 @@ MenuRegistry.appendMenuItem(MenuId.SCMChangesSeparator, { title: localize('outgoingChanges', "Show Outgoing Changes"), submenu: MenuId.SCMOutgoingChangesSetting, group: '1_incoming&outgoing', - order: 2 + order: 2, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) }); MenuRegistry.appendMenuItem(Menus.ChangesSettings, { title: localize('outgoingChanges', "Show Outgoing Changes"), submenu: MenuId.SCMOutgoingChangesSetting, group: '1_incoming&outgoing', - order: 2 + order: 2, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) }); registerAction2(class extends SCMChangesSettingAction { @@ -1781,8 +1802,16 @@ registerAction2(class extends Action2 { f1: false, toggled: ContextKeyExpr.equals('config.scm.showChangesSummary', true), menu: [ - { id: MenuId.SCMChangesSeparator, order: 3 }, - { id: Menus.ChangesSettings, order: 3 }, + { + id: MenuId.SCMChangesSeparator, + order: 3, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) + }, + { + id: Menus.ChangesSettings, + order: 3, + when: ContextKeyExpr.equals('config.scm.experimental.showHistoryGraph', false) + }, ] }); } diff --git a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts index 9e34f460806..17d4e15c4fa 100644 --- a/src/vs/workbench/services/actions/common/menusExtensionPoint.ts +++ b/src/vs/workbench/services/actions/common/menusExtensionPoint.ts @@ -169,6 +169,12 @@ const apiMenus: IAPIMenu[] = [ description: localize('menus.input', "The Source Control input box menu"), proposed: 'contribSourceControlInputBoxMenu' }, + { + key: 'scm/historyItemChanges/title', + id: MenuId.SCMChangesSeparator, + description: localize('menus.historyItemChanges', "The Source Control incoming/outgoing changes title menu"), + proposed: 'contribSourceControlHistoryItemChangesMenu' + }, { key: 'scm/incomingChanges', id: MenuId.SCMIncomingChanges, diff --git a/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts b/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts new file mode 100644 index 00000000000..a9d758fa24b --- /dev/null +++ b/src/vscode-dts/vscode.proposed.contribSourceControlHistoryItemChangesMenu.d.ts @@ -0,0 +1,7 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// empty placeholder declaration for the `scm/historyItemChanges/title`-menu contribution point +// https://github.com/microsoft/vscode/issues/201997 From 6c907814bfc223e842aa0d82e69a467ce4f76223 Mon Sep 17 00:00:00 2001 From: Joyce Er Date: Mon, 8 Jul 2024 14:40:09 -0700 Subject: [PATCH 0349/2222] feat: support button titles in chat confirmation api (#221223) --- src/vs/workbench/api/common/extHostChatAgents2.ts | 4 ++-- .../workbench/api/common/extHostTypeConverters.ts | 3 ++- src/vs/workbench/api/common/extHostTypes.ts | 5 ++++- .../chatConfirmationContentPart.ts | 14 ++++++++++---- .../workbench/contrib/chat/common/chatService.ts | 1 + .../vscode.proposed.chatParticipantAdditions.d.ts | 5 +++-- 6 files changed, 22 insertions(+), 10 deletions(-) diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 8b2209b5363..e029c680e42 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -212,11 +212,11 @@ class ChatAgentResponseStream { _report(dto); return this; }, - confirmation(title, message, data) { + confirmation(title, message, data, buttons) { throwIfDone(this.confirmation); checkProposedApiEnabled(that._extension, 'chatParticipantAdditions'); - const part = new extHostTypes.ChatResponseConfirmationPart(title, message, data); + const part = new extHostTypes.ChatResponseConfirmationPart(title, message, data, buttons); const dto = typeConvert.ChatResponseConfirmationPart.from(part); _report(dto); return this; diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 6fad560a437..527139f87f4 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -2354,7 +2354,8 @@ export namespace ChatResponseConfirmationPart { kind: 'confirmation', title: part.title, message: part.message, - data: part.data + data: part.data, + buttons: part.buttons }; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 18c2eefe79d..581f4020c8c 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4377,10 +4377,13 @@ export class ChatResponseConfirmationPart { title: string; message: string; data: any; - constructor(title: string, message: string, data: any) { + buttons?: string[]; + + constructor(title: string, message: string, data: any, buttons?: string[]) { this.title = title; this.message = message; this.data = data; + this.buttons = buttons; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts index 4159f07c919..37486118762 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts @@ -28,10 +28,16 @@ export class ChatConfirmationContentPart extends Disposable implements IChatCont super(); const element = context.element; - const confirmationWidget = this._register(this.instantiationService.createInstance(ChatConfirmationWidget, confirmation.title, confirmation.message, [ - { label: localize('accept', "Accept"), data: confirmation.data }, - { label: localize('dismiss', "Dismiss"), data: confirmation.data, isSecondary: true }, - ])); + const buttons = confirmation.buttons + ? confirmation.buttons.map(button => ({ + label: button, + data: confirmation.data + })) + : [ + { label: localize('accept', "Accept"), data: confirmation.data }, + { label: localize('dismiss', "Dismiss"), data: confirmation.data, isSecondary: true }, + ]; + const confirmationWidget = this._register(this.instantiationService.createInstance(ChatConfirmationWidget, confirmation.title, confirmation.message, buttons)); confirmationWidget.setShowButtons(!confirmation.isUsed); this._register(confirmationWidget.onDidClick(async e => { diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index f10564d7828..645ba778d36 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -160,6 +160,7 @@ export interface IChatConfirmation { title: string; message: string; data: any; + buttons?: string[]; isUsed?: boolean; kind: 'confirmation'; } diff --git a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts index cd2ec7ba919..ab43814c4f4 100644 --- a/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts +++ b/src/vscode-dts/vscode.proposed.chatParticipantAdditions.d.ts @@ -59,7 +59,8 @@ declare module 'vscode' { title: string; message: string; data: any; - constructor(title: string, message: string, data: any); + buttons?: string[]; + constructor(title: string, message: string, data: any, buttons?: string[]); } export type ExtendedChatResponsePart = ChatResponsePart | ChatResponseTextEditPart | ChatResponseDetectedParticipantPart | ChatResponseConfirmationPart; @@ -102,7 +103,7 @@ declare module 'vscode' { * TODO@API should this be MarkdownString? * TODO@API should actually be a more generic function that takes an array of buttons */ - confirmation(title: string, message: string, data: any): void; + confirmation(title: string, message: string, data: any, buttons?: string[]): void; /** * Push a warning to this stream. Short-hand for From 1b24381b5c1bc52636191a8db7b5e5e8445194a8 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 8 Jul 2024 15:19:40 -0700 Subject: [PATCH 0350/2222] Adopt `getAccounts` API in GitHub Authentication (#221224) This allows the GitHub Auth provider to take in a account per this proposal: https://github.com/microsoft/vscode/blob/417dddb83f3536479f88c40e1d78edf1136a5ae5/src/vscode-dts/vscode.proposed.authGetSessions.d.ts#L35-L69 Additionally, this makes an additional small change that allows `clearSessionPreference` to work in single-account auth providers. --- extensions/github-authentication/package.json | 3 + extensions/github-authentication/src/flows.ts | 4 ++ .../github-authentication/src/github.ts | 67 ++++++++++++++++--- .../github-authentication/tsconfig.json | 3 +- .../api/browser/mainThreadAuthentication.ts | 24 +++---- 5 files changed, 80 insertions(+), 21 deletions(-) diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 2d2bea56277..ed024ee59b8 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -13,6 +13,9 @@ "Other" ], "api": "none", + "enabledApiProposals": [ + "authGetSessions" + ], "extensionKind": [ "ui", "workspace" diff --git a/extensions/github-authentication/src/flows.ts b/extensions/github-authentication/src/flows.ts index 7498a2b2202..a2497b2b0b2 100644 --- a/extensions/github-authentication/src/flows.ts +++ b/extensions/github-authentication/src/flows.ts @@ -173,6 +173,8 @@ const allFlows: IFlow[] = [ ]); if (existingLogin) { searchParams.append('login', existingLogin); + } else { + searchParams.append('prompt', 'select_account'); } // The extra toString, parse is apparently needed for env.openExternal @@ -240,6 +242,8 @@ const allFlows: IFlow[] = [ ]); if (existingLogin) { searchParams.append('login', existingLogin); + } else { + searchParams.append('prompt', 'select_account'); } const loginUrl = baseUri.with({ diff --git a/extensions/github-authentication/src/github.ts b/extensions/github-authentication/src/github.ts index 15fe2ef04f8..08fb16730e6 100644 --- a/extensions/github-authentication/src/github.ts +++ b/extensions/github-authentication/src/github.ts @@ -11,7 +11,7 @@ import { PromiseAdapter, arrayEquals, promiseFromEvent } from './common/utils'; import { ExperimentationTelemetry } from './common/experimentationService'; import { Log } from './common/logger'; import { crypto } from './node/crypto'; -import { TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; +import { CANCELLATION_ERROR, TIMED_OUT_ERROR, USER_CANCELLATION_ERROR } from './common/errors'; interface SessionData { id: string; @@ -148,14 +148,17 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid return this._sessionChangeEmitter.event; } - async getSessions(scopes?: string[]): Promise { + async getSessions(scopes: string[] | undefined, options?: vscode.AuthenticationProviderSessionOptions): Promise { // For GitHub scope list, order doesn't matter so we immediately sort the scopes const sortedScopes = scopes?.sort() || []; this._logger.info(`Getting sessions for ${sortedScopes.length ? sortedScopes.join(',') : 'all scopes'}...`); const sessions = await this._sessionsPromise; - const finalSessions = sortedScopes.length - ? sessions.filter(session => arrayEquals([...session.scopes].sort(), sortedScopes)) + const accountFilteredSessions = options?.account + ? sessions.filter(session => session.account.label === options.account?.label) : sessions; + const finalSessions = sortedScopes.length + ? accountFilteredSessions.filter(session => arrayEquals([...session.scopes].sort(), sortedScopes)) + : accountFilteredSessions; this._logger.info(`Got ${finalSessions.length} sessions for ${sortedScopes?.join(',') ?? 'all scopes'}...`); return finalSessions; @@ -279,7 +282,7 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid this._logger.info(`Stored ${sessions.length} sessions!`); } - public async createSession(scopes: string[]): Promise { + public async createSession(scopes: string[], options?: vscode.AuthenticationProviderSessionOptions): Promise { try { // For GitHub scope list, order doesn't matter so we use a sorted scope to determine // if we've got a session already. @@ -298,11 +301,59 @@ export class GitHubAuthenticationProvider implements vscode.AuthenticationProvid const sessions = await this._sessionsPromise; - const accounts = new Set(sessions.map(session => session.account.label)); - const existingLogin = accounts.size <= 1 ? sessions[0]?.account.label : await vscode.window.showQuickPick([...accounts], { placeHolder: 'Choose an account that you would like to log in to' }); + let forcedLogin = options?.account?.label; + let backupLogin: string | undefined; + if (!forcedLogin) { + const accounts = new Set(sessions.map(session => session.account.label)); + this._logger.info(`Found ${accounts.size} accounts.`); + // This helps us tell GitHub that we're already logged in to an account/accounts + // and should probably use it. The user _can_ sign in to a different account + // if they want to, in the browser, but this is a good default for the happy path. + if (accounts.size > 1) { + // If there are multiple accounts, we should prompt the user to choose one. + const newAccount = vscode.l10n.t('New account...'); + const accountChoiceResult = await vscode.window.showQuickPick( + [...accounts, newAccount], + { placeHolder: vscode.l10n.t('Choose an account that you would like to log in to') } + ); + forcedLogin = accountChoiceResult === newAccount ? undefined : accountChoiceResult; + } else { + // If there is only one account, we can use that to seed the login, but + // we don't want to force the user to use it. + backupLogin = sessions[0]?.account.label; + } + } + this._logger.info(`Logging in with '${forcedLogin ? forcedLogin : 'any'}' account...`); + const scopeString = sortedScopes.join(' '); - const token = await this._githubServer.login(scopeString, existingLogin); + const token = await this._githubServer.login(scopeString, forcedLogin ?? backupLogin); const session = await this.tokenToSession(token, scopes); + + // If an account was specified, we should ensure that the token we got back is for that account. + if (forcedLogin) { + if (session.account.label !== forcedLogin) { + const keepNewAccount = vscode.l10n.t('Keep {0}', session.account.label); + const tryAgain = vscode.l10n.t('Login with {0}', forcedLogin); + const result = await vscode.window.showWarningMessage( + vscode.l10n.t('Incorrect account detected'), + { modal: true, detail: vscode.l10n.t('The chosen account, {0}, does not match the requested account, {1}.', session.account.label, forcedLogin) }, + keepNewAccount, + tryAgain + ); + if (result === tryAgain) { + return await this.createSession(scopes, { + ...options, + // The id doesn't matter here, we just need to pass the label through + account: { id: forcedLogin, label: forcedLogin } + }); + } + // Cancelled result + if (!result) { + throw new Error(CANCELLATION_ERROR); + } + // Keep result continues on + } + } this.afterSessionLoad(session); const sessionIndex = sessions.findIndex(s => s.id === session.id || arrayEquals([...s.scopes].sort(), sortedScopes)); diff --git a/extensions/github-authentication/tsconfig.json b/extensions/github-authentication/tsconfig.json index 5e4713e9f3b..70eba984a26 100644 --- a/extensions/github-authentication/tsconfig.json +++ b/extensions/github-authentication/tsconfig.json @@ -12,6 +12,7 @@ }, "include": [ "src/**/*", - "../../src/vscode-dts/vscode.d.ts" + "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.authGetSessions.d.ts" ] } diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 5780ec8797e..d833eab2860 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -174,21 +174,21 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu throw new Error('Invalid combination of options. Please remove one of the following: createIfNone, silent'); } + if (options.clearSessionPreference) { + // Clearing the session preference is usually paired with createIfNone, so just remove the preference and + // defer to the rest of the logic in this function to choose the session. + this.authenticationExtensionsService.removeSessionPreference(providerId, extensionId, scopes); + } + // Check if the sessions we have are valid if (!options.forceNewSession && sessions.length) { if (provider.supportsMultipleAccounts) { - if (options.clearSessionPreference) { - // Clearing the session preference is usually paired with createIfNone, so just remove the preference and - // defer to the rest of the logic in this function to choose the session. - this.authenticationExtensionsService.removeSessionPreference(providerId, extensionId, scopes); - } else { - // If we have an existing session preference, use that. If not, we'll return any valid session at the end of this function. - const existingSessionPreference = this.authenticationExtensionsService.getSessionPreference(providerId, extensionId, scopes); - if (existingSessionPreference) { - const matchingSession = sessions.find(session => session.id === existingSessionPreference); - if (matchingSession && this.authenticationAccessService.isAccessAllowed(providerId, matchingSession.account.label, extensionId)) { - return matchingSession; - } + // If we have an existing session preference, use that. If not, we'll return any valid session at the end of this function. + const existingSessionPreference = this.authenticationExtensionsService.getSessionPreference(providerId, extensionId, scopes); + if (existingSessionPreference) { + const matchingSession = sessions.find(session => session.id === existingSessionPreference); + if (matchingSession && this.authenticationAccessService.isAccessAllowed(providerId, matchingSession.account.label, extensionId)) { + return matchingSession; } } } else if (this.authenticationAccessService.isAccessAllowed(providerId, sessions[0].account.label, extensionId)) { From b2af4e7d34cc7bb96b5c0589549e1ffa15ee0e3b Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 8 Jul 2024 15:27:24 -0700 Subject: [PATCH 0351/2222] testing: add stack trace proposal and split out test results view The test results view was 2k+ lines, time to split it up a bit. --- .../common/extensionsApiProposals.ts | 3 + .../workbench/api/common/extHost.api.impl.ts | 2 + .../api/common/extHostTypeConverters.ts | 5 + src/vs/workbench/api/common/extHostTypes.ts | 17 +- .../testResultsView/testResultsOutput.ts | 545 +++++++ .../testResultsView/testResultsSubject.ts | 94 ++ .../testResultsView/testResultsTree.ts | 853 ++++++++++ .../testing/browser/testingOutputPeek.ts | 1431 +---------------- .../contrib/testing/common/testTypes.ts | 33 + ...vscode.proposed.testMessageStackTrace.d.ts | 38 + 10 files changed, 1606 insertions(+), 1415 deletions(-) create mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts create mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts create mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts create mode 100644 src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index dd767bf8a07..0429fdd1510 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -344,6 +344,9 @@ const _allApiProposals = { terminalShellIntegration: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellIntegration.d.ts', }, + testMessageStackTrace: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts', + }, testObserver: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.testObserver.d.ts', }, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index b6b4f5746bb..7847141b2f1 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1686,6 +1686,8 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TestResultState: extHostTypes.TestResultState, TestRunRequest: extHostTypes.TestRunRequest, TestMessage: extHostTypes.TestMessage, + TestMessage2: extHostTypes.TestMessage, + TestMessageStackFrame: extHostTypes.TestMessageStackFrame, TestTag: extHostTypes.TestTag, TestRunProfileKind: extHostTypes.TestRunProfileKind, TextSearchCompleteMessageType: TextSearchCompleteMessageType, diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 6fad560a437..f3689b50932 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -1888,6 +1888,11 @@ export namespace TestMessage { actual: message.actualOutput, contextValue: message.contextValue, location: message.location && ({ range: Range.from(message.location.range), uri: message.location.uri }), + stackTrace: (message as vscode.TestMessage2).stackTrace?.map(s => ({ + label: s.label, + position: s.position && Position.from(s.position), + uri: s.file && URI.revive(s.file).toJSON(), + })), }; } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index 18c2eefe79d..b757ae7e060 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -4069,9 +4069,11 @@ export class TestMessage implements vscode.TestMessage { public expectedOutput?: string; public actualOutput?: string; public location?: vscode.Location; - /** proposed: */ public contextValue?: string; + /** proposed: */ + public stackTrace?: TestMessageStackFrame[]; + public static diff(message: string | vscode.MarkdownString, expected: string, actual: string) { const msg = new TestMessage(message); msg.expectedOutput = expected; @@ -4087,6 +4089,19 @@ export class TestTag implements vscode.TestTag { constructor(public readonly id: string) { } } +export class TestMessageStackFrame { + /** + * @param label The name of the stack frame + * @param file The file URI of the stack frame + * @param position The position of the stack frame within the file + */ + constructor( + public label: string, + public file?: vscode.Uri, + public position?: Position, + ) { } +} + //#endregion //#region Test Coverage diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts new file mode 100644 index 00000000000..673f895b854 --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts @@ -0,0 +1,545 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; +import { Delayer } from 'vs/base/common/async'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { Iterable } from 'vs/base/common/iterator'; +import { Lazy } from 'vs/base/common/lazy'; +import { Disposable, IDisposable, IReference, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { ICodeEditor, IDiffEditorConstructionOptions } from 'vs/editor/browser/editorBrowser'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; +import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; +import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; +import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; +import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; +import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; +import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; +import { peekViewResultsBackground } from 'vs/editor/contrib/peekView/browser/peekView'; +import { localize } from 'vs/nls'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; +import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; +import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { EditorModel } from 'vs/workbench/common/editor/editorModel'; +import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; +import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; +import { DetachedProcessInfo } from 'vs/workbench/contrib/terminal/browser/detachedTerminal'; +import { IDetachedTerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; +import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; +import { colorizeTestMessageInEditor } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; +import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { Testing } from 'vs/workbench/contrib/testing/common/constants'; +import { MutableObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; +import { ITaskRawOutput, ITestResult, ITestRunTaskResults, LiveTestResult, TestResultItemChangeReason } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestMessage, TestMessageType, getMarkId } from 'vs/workbench/contrib/testing/common/testTypes'; + + +class SimpleDiffEditorModel extends EditorModel { + public readonly original = this._original.object.textEditorModel; + public readonly modified = this._modified.object.textEditorModel; + + constructor( + private readonly _original: IReference, + private readonly _modified: IReference, + ) { + super(); + } + + public override dispose() { + super.dispose(); + this._original.dispose(); + this._modified.dispose(); + } +} + + +export interface IPeekOutputRenderer extends IDisposable { + /** Updates the displayed test. Should clear if it cannot display the test. */ + update(subject: InspectSubject): void; + /** Recalculate content layout. */ + layout(dimension: dom.IDimension): void; + /** Dispose the content provider. */ + dispose(): void; +} + +const commonEditorOptions: IEditorOptions = { + scrollBeyondLastLine: false, + links: true, + lineNumbers: 'off', + scrollbar: { + verticalScrollbarSize: 14, + horizontal: 'auto', + useShadows: true, + verticalHasArrows: false, + horizontalHasArrows: false, + alwaysConsumeMouseWheel: false + }, + fixedOverflowWidgets: true, + readOnly: true, + minimap: { + enabled: false + }, + wordWrap: 'on', +}; + +const diffEditorOptions: IDiffEditorConstructionOptions = { + ...commonEditorOptions, + enableSplitViewResizing: true, + isInEmbeddedEditor: true, + renderOverviewRuler: false, + ignoreTrimWhitespace: false, + renderSideBySide: true, + useInlineViewWhenSpaceIsLimited: false, + originalAriaLabel: localize('testingOutputExpected', 'Expected result'), + modifiedAriaLabel: localize('testingOutputActual', 'Actual result'), + diffAlgorithm: 'advanced', +}; + + +export class DiffContentProvider extends Disposable implements IPeekOutputRenderer { + private readonly widget = this._register(new MutableDisposable()); + private readonly model = this._register(new MutableDisposable()); + private dimension?: dom.IDimension; + + constructor( + private readonly editor: ICodeEditor | undefined, + private readonly container: HTMLElement, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITextModelService private readonly modelService: ITextModelService, + ) { + super(); + } + + public async update(subject: InspectSubject) { + if (!(subject instanceof MessageSubject)) { + return this.clear(); + } + const message = subject.message; + if (!ITestMessage.isDiffable(message)) { + return this.clear(); + } + + const [original, modified] = await Promise.all([ + this.modelService.createModelReference(subject.expectedUri), + this.modelService.createModelReference(subject.actualUri), + ]); + + const model = this.model.value = new SimpleDiffEditorModel(original, modified); + if (!this.widget.value) { + this.widget.value = this.editor ? this.instantiationService.createInstance( + EmbeddedDiffEditorWidget, + this.container, + diffEditorOptions, + {}, + this.editor, + ) : this.instantiationService.createInstance( + DiffEditorWidget, + this.container, + diffEditorOptions, + {}, + ); + + if (this.dimension) { + this.widget.value.layout(this.dimension); + } + } + + this.widget.value.setModel(model); + this.widget.value.updateOptions(this.getOptions( + isMultiline(message.expected) || isMultiline(message.actual) + )); + } + + private clear() { + this.model.clear(); + this.widget.clear(); + } + + public layout(dimensions: dom.IDimension) { + this.dimension = dimensions; + this.widget.value?.layout(dimensions); + } + + protected getOptions(isMultiline: boolean): IDiffEditorOptions { + return isMultiline + ? { ...diffEditorOptions, lineNumbers: 'on' } + : { ...diffEditorOptions, lineNumbers: 'off' }; + } +} + +class ScrollableMarkdownMessage extends Disposable { + private readonly scrollable: DomScrollableElement; + private readonly element: HTMLElement; + + constructor(container: HTMLElement, markdown: MarkdownRenderer, message: IMarkdownString) { + super(); + + const rendered = this._register(markdown.render(message, {})); + rendered.element.style.height = '100%'; + rendered.element.style.userSelect = 'text'; + container.appendChild(rendered.element); + this.element = rendered.element; + + this.scrollable = this._register(new DomScrollableElement(rendered.element, { + className: 'preview-text', + })); + container.appendChild(this.scrollable.getDomNode()); + + this._register(toDisposable(() => { + this.scrollable.getDomNode().remove(); + })); + + this.scrollable.scanDomNode(); + } + + public layout(height: number, width: number) { + // Remove padding of `.monaco-editor .zone-widget.test-output-peek .preview-text` + this.scrollable.setScrollDimensions({ + width: width - 32, + height: height - 16, + scrollWidth: this.element.scrollWidth, + scrollHeight: this.element.scrollHeight + }); + } +} + +export class MarkdownTestMessagePeek extends Disposable implements IPeekOutputRenderer { + private readonly markdown = new Lazy( + () => this._register(this.instantiationService.createInstance(MarkdownRenderer, {})), + ); + + private readonly textPreview = this._register(new MutableDisposable()); + + constructor(private readonly container: HTMLElement, @IInstantiationService private readonly instantiationService: IInstantiationService) { + super(); + } + + public update(subject: InspectSubject): void { + if (!(subject instanceof MessageSubject)) { + return this.textPreview.clear(); + } + + const message = subject.message; + if (ITestMessage.isDiffable(message) || typeof message.message === 'string') { + return this.textPreview.clear(); + } + + this.textPreview.value = new ScrollableMarkdownMessage( + this.container, + this.markdown.value, + message.message as IMarkdownString, + ); + } + + public layout(dimension: dom.IDimension): void { + this.textPreview.value?.layout(dimension.height, dimension.width); + } +} + +export class PlainTextMessagePeek extends Disposable implements IPeekOutputRenderer { + private readonly widgetDecorations = this._register(new MutableDisposable()); + private readonly widget = this._register(new MutableDisposable()); + private readonly model = this._register(new MutableDisposable()); + private dimension?: dom.IDimension; + + constructor( + private readonly editor: ICodeEditor | undefined, + private readonly container: HTMLElement, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITextModelService private readonly modelService: ITextModelService, + ) { + super(); + } + + public async update(subject: InspectSubject) { + if (!(subject instanceof MessageSubject)) { + return this.clear(); + } + + const message = subject.message; + if (ITestMessage.isDiffable(message) || message.type === TestMessageType.Output || typeof message.message !== 'string') { + return this.clear(); + } + + const modelRef = this.model.value = await this.modelService.createModelReference(subject.messageUri); + if (!this.widget.value) { + this.widget.value = this.editor ? this.instantiationService.createInstance( + EmbeddedCodeEditorWidget, + this.container, + commonEditorOptions, + {}, + this.editor, + ) : this.instantiationService.createInstance( + CodeEditorWidget, + this.container, + commonEditorOptions, + { isSimpleWidget: true } + ); + + if (this.dimension) { + this.widget.value.layout(this.dimension); + } + } + + this.widget.value.setModel(modelRef.object.textEditorModel); + this.widget.value.updateOptions(commonEditorOptions); + this.widgetDecorations.value = colorizeTestMessageInEditor(message.message, this.widget.value); + } + + private clear() { + this.widgetDecorations.clear(); + this.widget.clear(); + this.model.clear(); + } + + public layout(dimensions: dom.IDimension) { + this.dimension = dimensions; + this.widget.value?.layout(dimensions); + } +} + +export class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { + private dimensions?: dom.IDimension; + private readonly terminalCwd = this._register(new MutableObservableValue('')); + private readonly xtermLayoutDelayer = this._register(new Delayer(50)); + + /** Active terminal instance. */ + private readonly terminal = this._register(new MutableDisposable()); + /** Listener for streaming result data */ + private readonly outputDataListener = this._register(new MutableDisposable()); + + constructor( + private readonly container: HTMLElement, + private readonly isInPeekView: boolean, + @ITerminalService private readonly terminalService: ITerminalService, + @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, + @IWorkspaceContextService private readonly workspaceContext: IWorkspaceContextService, + ) { + super(); + } + + private async makeTerminal() { + const prev = this.terminal.value; + if (prev) { + prev.xterm.clearBuffer(); + prev.xterm.clearSearchDecorations(); + // clearBuffer tries to retain the prompt line, but this doesn't exist for tests. + // So clear the screen (J) and move to home (H) to ensure previous data is cleaned up. + prev.xterm.write(`\x1b[2J\x1b[0;0H`); + return prev; + } + + const capabilities = new TerminalCapabilityStore(); + const cwd = this.terminalCwd; + capabilities.add(TerminalCapability.CwdDetection, { + type: TerminalCapability.CwdDetection, + get cwds() { return [cwd.value]; }, + onDidChangeCwd: cwd.onDidChange, + getCwd: () => cwd.value, + updateCwd: () => { }, + }); + + return this.terminal.value = await this.terminalService.createDetachedTerminal({ + rows: 10, + cols: 80, + readonly: true, + capabilities, + processInfo: new DetachedProcessInfo({ initialCwd: cwd.value }), + colorProvider: { + getBackgroundColor: theme => { + const terminalBackground = theme.getColor(TERMINAL_BACKGROUND_COLOR); + if (terminalBackground) { + return terminalBackground; + } + if (this.isInPeekView) { + return theme.getColor(peekViewResultsBackground); + } + const location = this.viewDescriptorService.getViewLocationById(Testing.ResultsViewId); + return location === ViewContainerLocation.Panel + ? theme.getColor(PANEL_BACKGROUND) + : theme.getColor(SIDE_BAR_BACKGROUND); + }, + } + }); + } + + public async update(subject: InspectSubject) { + this.outputDataListener.clear(); + if (subject instanceof TaskSubject) { + await this.updateForTaskSubject(subject); + } else if (subject instanceof TestOutputSubject || (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output)) { + await this.updateForTestSubject(subject); + } else { + this.clear(); + } + } + + private async updateForTestSubject(subject: TestOutputSubject | MessageSubject) { + const that = this; + const testItem = subject instanceof TestOutputSubject ? subject.test.item : subject.test; + const terminal = await this.updateGenerically({ + subject, + noOutputMessage: localize('caseNoOutput', 'The test case did not report any output.'), + getTarget: result => result?.tasks[subject.taskIndex].output, + *doInitialWrite(output, results) { + that.updateCwd(testItem.uri); + const state = subject instanceof TestOutputSubject ? subject.test : results.getStateById(testItem.extId); + if (!state) { + return; + } + + for (const message of state.tasks[subject.taskIndex].messages) { + if (message.type === TestMessageType.Output) { + yield* output.getRangeIter(message.offset, message.length); + } + } + }, + doListenForMoreData: (output, result, write) => result.onChange(e => { + if (e.reason === TestResultItemChangeReason.NewMessage && e.item.item.extId === testItem.extId && e.message.type === TestMessageType.Output) { + for (const chunk of output.getRangeIter(e.message.offset, e.message.length)) { + write(chunk.buffer); + } + } + }), + }); + + if (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output && subject.message.marker !== undefined) { + terminal?.xterm.selectMarkedRange(getMarkId(subject.message.marker, true), getMarkId(subject.message.marker, false), /* scrollIntoView= */ true); + } + } + + private updateForTaskSubject(subject: TaskSubject) { + return this.updateGenerically({ + subject, + noOutputMessage: localize('runNoOutput', 'The test run did not record any output.'), + getTarget: result => result?.tasks[subject.taskIndex], + doInitialWrite: (task, result) => { + // Update the cwd and use the first test to try to hint at the correct cwd, + // but often this will fall back to the first workspace folder. + this.updateCwd(Iterable.find(result.tests, t => !!t.item.uri)?.item.uri); + return task.output.buffers; + }, + doListenForMoreData: (task, _result, write) => task.output.onDidWriteData(e => write(e.buffer)), + }); + } + + private async updateGenerically(opts: { + subject: InspectSubject; + noOutputMessage: string; + getTarget: (result: ITestResult) => T | undefined; + doInitialWrite: (target: T, result: LiveTestResult) => Iterable; + doListenForMoreData: (target: T, result: LiveTestResult, write: (s: Uint8Array) => void) => IDisposable; + }) { + const result = opts.subject.result; + const target = opts.getTarget(result); + if (!target) { + return this.clear(); + } + + const terminal = await this.makeTerminal(); + let didWriteData = false; + + const pendingWrites = new MutableObservableValue(0); + if (result instanceof LiveTestResult) { + for (const chunk of opts.doInitialWrite(target, result)) { + didWriteData ||= chunk.byteLength > 0; + pendingWrites.value++; + terminal.xterm.write(chunk.buffer, () => pendingWrites.value--); + } + } else { + didWriteData = true; + this.writeNotice(terminal, localize('runNoOutputForPast', 'Test output is only available for new test runs.')); + } + + this.attachTerminalToDom(terminal); + this.outputDataListener.clear(); + + if (result instanceof LiveTestResult && !result.completedAt) { + const l1 = result.onComplete(() => { + if (!didWriteData) { + this.writeNotice(terminal, opts.noOutputMessage); + } + }); + const l2 = opts.doListenForMoreData(target, result, data => { + terminal.xterm.write(data); + didWriteData ||= data.byteLength > 0; + }); + + this.outputDataListener.value = combinedDisposable(l1, l2); + } + + if (!this.outputDataListener.value && !didWriteData) { + this.writeNotice(terminal, opts.noOutputMessage); + } + + // Ensure pending writes finish, otherwise the selection in `updateForTestSubject` + // can happen before the markers are processed. + if (pendingWrites.value > 0) { + await new Promise(resolve => { + const l = pendingWrites.onDidChange(() => { + if (pendingWrites.value === 0) { + l.dispose(); + resolve(); + } + }); + }); + } + + return terminal; + } + + private updateCwd(testUri?: URI) { + const wf = (testUri && this.workspaceContext.getWorkspaceFolder(testUri)) + || this.workspaceContext.getWorkspace().folders[0]; + if (wf) { + this.terminalCwd.value = wf.uri.fsPath; + } + } + + private writeNotice(terminal: IDetachedTerminalInstance, str: string) { + terminal.xterm.write(formatMessageForTerminal(str)); + } + + private attachTerminalToDom(terminal: IDetachedTerminalInstance) { + terminal.xterm.write('\x1b[?25l'); // hide cursor + dom.scheduleAtNextAnimationFrame(dom.getWindow(this.container), () => this.layoutTerminal(terminal)); + terminal.attachToElement(this.container, { enableGpu: false }); + } + + private clear() { + this.outputDataListener.clear(); + this.xtermLayoutDelayer.cancel(); + this.terminal.clear(); + } + + public layout(dimensions: dom.IDimension) { + this.dimensions = dimensions; + if (this.terminal.value) { + this.layoutTerminal(this.terminal.value, dimensions.width, dimensions.height); + } + } + + private layoutTerminal( + { xterm }: IDetachedTerminalInstance, + width = this.dimensions?.width ?? this.container.clientWidth, + height = this.dimensions?.height ?? this.container.clientHeight + ) { + width -= 10 + 20; // scrollbar width + margin + this.xtermLayoutDelayer.trigger(() => { + const scaled = getXtermScaledDimensions(dom.getWindow(this.container), xterm.getFont(), width, height); + if (scaled) { + xterm.resize(scaled.cols, scaled.rows); + } + }); + } +} + +const isMultiline = (str: string | undefined) => !!str && str.includes('\n'); diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts new file mode 100644 index 00000000000..6296138f985 --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject.ts @@ -0,0 +1,94 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { MarshalledId } from 'vs/base/common/marshallingIds'; +import { URI } from 'vs/base/common/uri'; +import { Range } from 'vs/editor/common/core/range'; +import { ITestResult } from 'vs/workbench/contrib/testing/common/testResult'; +import { IRichLocation, ITestItem, ITestMessage, ITestMessageMenuArgs, ITestRunTask, ITestTaskState, InternalTestItem, TestMessageType, TestResultItem } from 'vs/workbench/contrib/testing/common/testTypes'; +import { TestUriType, buildTestUri } from 'vs/workbench/contrib/testing/common/testingUri'; + +export const getMessageArgs = (test: TestResultItem, message: ITestMessage): ITestMessageMenuArgs => ({ + $mid: MarshalledId.TestMessageMenuArgs, + test: InternalTestItem.serialize(test), + message: ITestMessage.serialize(message), +}); + +export class MessageSubject { + public readonly test: ITestItem; + public readonly message: ITestMessage; + public readonly expectedUri: URI; + public readonly actualUri: URI; + public readonly messageUri: URI; + public readonly revealLocation: IRichLocation | undefined; + public readonly context: ITestMessageMenuArgs | undefined; + + public get isDiffable() { + return this.message.type === TestMessageType.Error && ITestMessage.isDiffable(this.message); + } + + public get contextValue() { + return this.message.type === TestMessageType.Error ? this.message.contextValue : undefined; + } + + constructor(public readonly result: ITestResult, test: TestResultItem, public readonly taskIndex: number, public readonly messageIndex: number) { + this.test = test.item; + const messages = test.tasks[taskIndex].messages; + this.messageIndex = messageIndex; + + const parts = { messageIndex, resultId: result.id, taskIndex, testExtId: test.item.extId }; + this.expectedUri = buildTestUri({ ...parts, type: TestUriType.ResultExpectedOutput }); + this.actualUri = buildTestUri({ ...parts, type: TestUriType.ResultActualOutput }); + this.messageUri = buildTestUri({ ...parts, type: TestUriType.ResultMessage }); + + const message = this.message = messages[this.messageIndex]; + this.context = getMessageArgs(test, message); + this.revealLocation = message.location ?? (test.item.uri && test.item.range ? { uri: test.item.uri, range: Range.lift(test.item.range) } : undefined); + } +} + +export class TaskSubject { + public readonly outputUri: URI; + public readonly revealLocation: undefined; + + constructor(public readonly result: ITestResult, public readonly taskIndex: number) { + this.outputUri = buildTestUri({ resultId: result.id, taskIndex, type: TestUriType.TaskOutput }); + } +} + +export class TestOutputSubject { + public readonly outputUri: URI; + public readonly revealLocation: undefined; + public readonly task: ITestRunTask; + + constructor(public readonly result: ITestResult, public readonly taskIndex: number, public readonly test: TestResultItem) { + this.outputUri = buildTestUri({ resultId: this.result.id, taskIndex: this.taskIndex, testExtId: this.test.item.extId, type: TestUriType.TestOutput }); + this.task = result.tasks[this.taskIndex]; + } +} + +export type InspectSubject = MessageSubject | TaskSubject | TestOutputSubject; + +export const equalsSubject = (a: InspectSubject, b: InspectSubject) => ( + (a instanceof MessageSubject && b instanceof MessageSubject && a.message === b.message) || + (a instanceof TaskSubject && b instanceof TaskSubject && a.result === b.result && a.taskIndex === b.taskIndex) || + (a instanceof TestOutputSubject && b instanceof TestOutputSubject && a.test === b.test && a.taskIndex === b.taskIndex) +); + + +export const mapFindTestMessage = (test: TestResultItem, fn: (task: ITestTaskState, message: ITestMessage, messageIndex: number, taskIndex: number) => T | undefined) => { + for (let taskIndex = 0; taskIndex < test.tasks.length; taskIndex++) { + const task = test.tasks[taskIndex]; + for (let messageIndex = 0; messageIndex < task.messages.length; messageIndex++) { + const r = fn(task, task.messages[messageIndex], messageIndex, taskIndex); + if (r !== undefined) { + return r; + } + } + } + + return undefined; +}; diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts new file mode 100644 index 00000000000..f6aa368c619 --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts @@ -0,0 +1,853 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; +import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; +import { ICompressedTreeElement, ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; +import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; +import { ITreeContextMenuEvent, ITreeNode } from 'vs/base/browser/ui/tree/tree'; +import { Action, IAction, Separator } from 'vs/base/common/actions'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { Codicon } from 'vs/base/common/codicons'; +import { Emitter, Event } from 'vs/base/common/event'; +import { FuzzyScore } from 'vs/base/common/filters'; +import { Iterable } from 'vs/base/common/iterator'; +import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; +import { MarshalledId } from 'vs/base/common/marshallingIds'; +import { autorun } from 'vs/base/common/observable'; +import { count } from 'vs/base/common/strings'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { isDefined } from 'vs/base/common/types'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { MenuEntryActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; +import { IProgressService } from 'vs/platform/progress/common/progress'; +import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; +import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay'; +import * as icons from 'vs/workbench/contrib/testing/browser/icons'; +import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; +import { TestOutputSubject, InspectSubject, TaskSubject, MessageSubject, mapFindTestMessage, getMessageArgs } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { Testing } from 'vs/workbench/contrib/testing/common/constants'; +import { ITestCoverageService } from 'vs/workbench/contrib/testing/common/testCoverageService'; +import { ITestExplorerFilterState } from 'vs/workbench/contrib/testing/common/testExplorerFilterState'; +import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; +import { ITestResult, ITestRunTaskResults, LiveTestResult, TestResultItemChangeReason, maxCountPriority } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestResultService } from 'vs/workbench/contrib/testing/common/testResultService'; +import { IRichLocation, ITestItemContext, ITestMessage, ITestMessageMenuArgs, InternalTestItem, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset, testResultStateToContextValues } from 'vs/workbench/contrib/testing/common/testTypes'; +import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; +import { cmpPriority } from 'vs/workbench/contrib/testing/common/testingStates'; +import { TestUriType, buildTestUri } from 'vs/workbench/contrib/testing/common/testingUri'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + + +interface ITreeElement { + type: string; + context: unknown; + id: string; + label: string; + onDidChange: Event; + labelWithIcons?: readonly (HTMLSpanElement | string)[]; + icon?: ThemeIcon; + description?: string; + ariaLabel?: string; +} + +interface ITreeElement { + type: string; + context: unknown; + id: string; + label: string; + onDidChange: Event; + labelWithIcons?: readonly (HTMLSpanElement | string)[]; + icon?: ThemeIcon; + description?: string; + ariaLabel?: string; +} + +class TestResultElement implements ITreeElement { + public readonly changeEmitter = new Emitter(); + public readonly onDidChange = this.changeEmitter.event; + public readonly type = 'result'; + public readonly context = this.value.id; + public readonly id = this.value.id; + public readonly label = this.value.name; + + public get icon() { + return icons.testingStatesToIcons.get( + this.value.completedAt === undefined + ? TestResultState.Running + : maxCountPriority(this.value.counts) + ); + } + + constructor(public readonly value: ITestResult) { } +} + +const openCoverageLabel = localize('openTestCoverage', 'View Test Coverage'); +const closeCoverageLabel = localize('closeTestCoverage', 'Close Test Coverage'); + +class CoverageElement implements ITreeElement { + public readonly type = 'coverage'; + public readonly context: undefined; + public readonly id = `coverage-${this.results.id}/${this.task.id}`; + public readonly onDidChange: Event; + + public get label() { + return this.isOpen ? closeCoverageLabel : openCoverageLabel; + } + + public get icon() { + return this.isOpen ? widgetClose : icons.testingCoverageReport; + } + + public get isOpen() { + return this.coverageService.selected.get()?.fromTaskId === this.task.id; + } + + constructor( + private readonly results: ITestResult, + public readonly task: ITestRunTaskResults, + private readonly coverageService: ITestCoverageService, + ) { + this.onDidChange = Event.fromObservableLight(coverageService.selected); + } + +} + +class TestCaseElement implements ITreeElement { + public readonly type = 'test'; + public readonly context: ITestItemContext = { + $mid: MarshalledId.TestItemContext, + tests: [InternalTestItem.serialize(this.test)], + }; + public readonly id = `${this.results.id}/${this.test.item.extId}`; + public readonly description?: string; + + public get onDidChange() { + if (!(this.results instanceof LiveTestResult)) { + return Event.None; + } + + return Event.filter(this.results.onChange, e => e.item.item.extId === this.test.item.extId); + } + + public get state() { + return this.test.tasks[this.taskIndex].state; + } + + public get label() { + return this.test.item.label; + } + + public get labelWithIcons() { + return renderLabelWithIcons(this.label); + } + + public get icon() { + return icons.testingStatesToIcons.get(this.state); + } + + public get outputSubject() { + return new TestOutputSubject(this.results, this.taskIndex, this.test); + } + + + constructor( + public readonly results: ITestResult, + public readonly test: TestResultItem, + public readonly taskIndex: number, + ) { } +} + +class TaskElement implements ITreeElement { + public readonly changeEmitter = new Emitter(); + public readonly onDidChange = this.changeEmitter.event; + public readonly type = 'task'; + public readonly context: string; + public readonly id: string; + public readonly label: string; + public readonly itemsCache = new CreationCache(); + + public get icon() { + return this.results.tasks[this.index].running ? icons.testingStatesToIcons.get(TestResultState.Running) : undefined; + } + + constructor(public readonly results: ITestResult, public readonly task: ITestRunTaskResults, public readonly index: number) { + this.id = `${results.id}/${index}`; + this.task = results.tasks[index]; + this.context = String(index); + this.label = this.task.name ?? localize('testUnnamedTask', 'Unnamed Task'); + } +} + +class TestMessageElement implements ITreeElement { + public readonly type = 'message'; + public readonly id: string; + public readonly label: string; + public readonly uri: URI; + public readonly location?: IRichLocation; + public readonly description?: string; + public readonly contextValue?: string; + public readonly message: ITestMessage; + + public get onDidChange() { + if (!(this.result instanceof LiveTestResult)) { + return Event.None; + } + + // rerender when the test case changes so it gets retired events + return Event.filter(this.result.onChange, e => e.item.item.extId === this.test.item.extId); + } + + public get context(): ITestMessageMenuArgs { + return getMessageArgs(this.test, this.message); + } + + public get outputSubject() { + return new TestOutputSubject(this.result, this.taskIndex, this.test); + } + + constructor( + public readonly result: ITestResult, + public readonly test: TestResultItem, + public readonly taskIndex: number, + public readonly messageIndex: number, + ) { + const m = this.message = test.tasks[taskIndex].messages[messageIndex]; + + this.location = m.location; + this.contextValue = m.type === TestMessageType.Error ? m.contextValue : undefined; + this.uri = buildTestUri({ + type: TestUriType.ResultMessage, + messageIndex, + resultId: result.id, + taskIndex, + testExtId: test.item.extId + }); + + this.id = this.uri.toString(); + + const asPlaintext = renderTestMessageAsText(m.message); + const lines = count(asPlaintext.trimEnd(), '\n'); + this.label = firstLine(asPlaintext); + if (lines > 0) { + this.description = lines > 1 + ? localize('messageMoreLinesN', '+ {0} more lines', lines) + : localize('messageMoreLines1', '+ 1 more line'); + } + } +} + +type TreeElement = TestResultElement | TestCaseElement | TestMessageElement | TaskElement | CoverageElement; + +export class OutputPeekTree extends Disposable { + private disposed = false; + private readonly tree: WorkbenchCompressibleObjectTree; + private readonly treeActions: TreeActionsProvider; + private readonly requestReveal = this._register(new Emitter()); + + public readonly onDidRequestReview = this.requestReveal.event; + + constructor( + container: HTMLElement, + onDidReveal: Event<{ subject: InspectSubject; preserveFocus: boolean }>, + options: { showRevealLocationOnMessages: boolean; locationForProgress: string }, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @ITestResultService results: ITestResultService, + @IInstantiationService instantiationService: IInstantiationService, + @ITestExplorerFilterState explorerFilter: ITestExplorerFilterState, + @ITestCoverageService coverageService: ITestCoverageService, + @IProgressService progressService: IProgressService, + ) { + super(); + + this.treeActions = instantiationService.createInstance(TreeActionsProvider, options.showRevealLocationOnMessages, this.requestReveal,); + const diffIdentityProvider: IIdentityProvider = { + getId(e: TreeElement) { + return e.id; + } + }; + + this.tree = this._register(instantiationService.createInstance( + WorkbenchCompressibleObjectTree, + 'Test Output Peek', + container, + { + getHeight: () => 22, + getTemplateId: () => TestRunElementRenderer.ID, + }, + [instantiationService.createInstance(TestRunElementRenderer, this.treeActions)], + { + compressionEnabled: true, + hideTwistiesOfChildlessElements: true, + identityProvider: diffIdentityProvider, + sorter: { + compare(a, b) { + if (a instanceof TestCaseElement && b instanceof TestCaseElement) { + return cmpPriority(a.state, b.state); + } + + return 0; + }, + }, + accessibilityProvider: { + getAriaLabel(element: ITreeElement) { + return element.ariaLabel || element.label; + }, + getWidgetAriaLabel() { + return localize('testingPeekLabel', 'Test Result Messages'); + } + } + }, + )) as WorkbenchCompressibleObjectTree; + + const cc = new CreationCache(); + const getTaskChildren = (taskElem: TaskElement): Iterable> => { + const { results, index, itemsCache, task } = taskElem; + const tests = Iterable.filter(results.tests, test => test.tasks[index].state >= TestResultState.Running || test.tasks[index].messages.length > 0); + let result: Iterable> = Iterable.map(tests, test => ({ + element: itemsCache.getOrCreate(test, () => new TestCaseElement(results, test, index)), + incompressible: true, + children: getTestChildren(results, test, index), + })); + + if (task.coverage.get()) { + result = Iterable.concat( + Iterable.single>({ + element: new CoverageElement(results, task, coverageService), + collapsible: true, + incompressible: true, + }), + result, + ); + } + + return result; + }; + + const getTestChildren = (result: ITestResult, test: TestResultItem, taskIndex: number): Iterable> => { + return test.tasks[taskIndex].messages + .map((m, messageIndex) => + m.type === TestMessageType.Error + ? { element: cc.getOrCreate(m, () => new TestMessageElement(result, test, taskIndex, messageIndex)), incompressible: false } + : undefined + ) + .filter(isDefined); + }; + + const getResultChildren = (result: ITestResult): Iterable> => { + return result.tasks.map((task, taskIndex) => { + const taskElem = cc.getOrCreate(task, () => new TaskElement(result, task, taskIndex)); + return ({ + element: taskElem, + incompressible: false, + collapsible: true, + children: getTaskChildren(taskElem), + }); + }); + }; + + const getRootChildren = () => results.results.map(result => { + const element = cc.getOrCreate(result, () => new TestResultElement(result)); + return { + element, + incompressible: true, + collapsible: true, + collapsed: this.tree.hasElement(element) ? this.tree.isCollapsed(element) : true, + children: getResultChildren(result) + }; + }); + + // Queued result updates to prevent spamming CPU when lots of tests are + // completing and messaging quickly (#142514) + const taskChildrenToUpdate = new Set(); + const taskChildrenUpdate = this._register(new RunOnceScheduler(() => { + for (const taskNode of taskChildrenToUpdate) { + if (this.tree.hasElement(taskNode)) { + this.tree.setChildren(taskNode, getTaskChildren(taskNode), { diffIdentityProvider }); + } + } + taskChildrenToUpdate.clear(); + }, 300)); + + const queueTaskChildrenUpdate = (taskNode: TaskElement) => { + taskChildrenToUpdate.add(taskNode); + if (!taskChildrenUpdate.isScheduled()) { + taskChildrenUpdate.schedule(); + } + }; + + const attachToResults = (result: LiveTestResult) => { + const resultNode = cc.get(result)! as TestResultElement; + const disposable = new DisposableStore(); + disposable.add(result.onNewTask(i => { + if (result.tasks.length === 1) { + this.requestReveal.fire(new TaskSubject(result, 0)); // reveal the first task in new runs + } + + if (this.tree.hasElement(resultNode)) { + this.tree.setChildren(resultNode, getResultChildren(result), { diffIdentityProvider }); + } + + // note: tasks are bounded and their lifetime is equivalent to that of + // the test result, so this doesn't leak indefinitely. + const task = result.tasks[i]; + disposable.add(autorun(reader => { + task.coverage.read(reader); // add it to the autorun + queueTaskChildrenUpdate(cc.get(task) as TaskElement); + })); + })); + disposable.add(result.onEndTask(index => { + (cc.get(result.tasks[index]) as TaskElement | undefined)?.changeEmitter.fire(); + })); + + disposable.add(result.onChange(e => { + // try updating the item in each of its tasks + for (const [index, task] of result.tasks.entries()) { + const taskNode = cc.get(task) as TaskElement; + if (!this.tree.hasElement(taskNode)) { + continue; + } + + const itemNode = taskNode.itemsCache.get(e.item); + if (itemNode && this.tree.hasElement(itemNode)) { + if (e.reason === TestResultItemChangeReason.NewMessage && e.message.type === TestMessageType.Error) { + this.tree.setChildren(itemNode, getTestChildren(result, e.item, index), { diffIdentityProvider }); + } + return; + } + + queueTaskChildrenUpdate(taskNode); + } + })); + + disposable.add(result.onComplete(() => { + resultNode.changeEmitter.fire(); + disposable.dispose(); + })); + + return resultNode; + }; + + this._register(results.onResultsChanged(e => { + // little hack here: a result change can cause the peek to be disposed, + // but this listener will still be queued. Doing stuff with the tree + // will cause errors. + if (this.disposed) { + return; + } + + if ('completed' in e) { + (cc.get(e.completed) as TestResultElement | undefined)?.changeEmitter.fire(); + return; + } + + this.tree.setChildren(null, getRootChildren(), { diffIdentityProvider }); + + // done after setChildren intentionally so that the ResultElement exists in the cache. + if ('started' in e) { + for (const child of this.tree.getNode(null).children) { + this.tree.collapse(child.element, false); + } + + this.tree.expand(attachToResults(e.started), true); + } + })); + + const revealItem = (element: TreeElement, preserveFocus: boolean) => { + this.tree.setFocus([element]); + this.tree.setSelection([element]); + if (!preserveFocus) { + this.tree.domFocus(); + } + }; + + this._register(onDidReveal(async ({ subject, preserveFocus = false }) => { + if (subject instanceof TaskSubject) { + const resultItem = this.tree.getNode(null).children.find(c => { + if (c.element instanceof TaskElement) { + return c.element.results.id === subject.result.id && c.element.index === subject.taskIndex; + } + if (c.element instanceof TestResultElement) { + return c.element.id === subject.result.id; + } + return false; + }); + + if (resultItem) { + revealItem(resultItem.element!, preserveFocus); + } + return; + } + + const revealElement = subject instanceof TestOutputSubject + ? cc.get(subject.task)?.itemsCache.get(subject.test) + : cc.get(subject.message); + if (!revealElement || !this.tree.hasElement(revealElement)) { + return; + } + + const parents: TreeElement[] = []; + for (let parent = this.tree.getParentElement(revealElement); parent; parent = this.tree.getParentElement(parent)) { + parents.unshift(parent); + } + + for (const parent of parents) { + this.tree.expand(parent); + } + + if (this.tree.getRelativeTop(revealElement) === null) { + this.tree.reveal(revealElement, 0.5); + } + + revealItem(revealElement, preserveFocus); + })); + + this._register(this.tree.onDidOpen(async e => { + if (e.element instanceof TestMessageElement) { + this.requestReveal.fire(new MessageSubject(e.element.result, e.element.test, e.element.taskIndex, e.element.messageIndex)); + } else if (e.element instanceof TestCaseElement) { + const t = e.element; + const message = mapFindTestMessage(e.element.test, (_t, _m, mesasgeIndex, taskIndex) => + new MessageSubject(t.results, t.test, taskIndex, mesasgeIndex)); + this.requestReveal.fire(message || new TestOutputSubject(t.results, 0, t.test)); + } else if (e.element instanceof CoverageElement) { + const task = e.element.task; + if (e.element.isOpen) { + return coverageService.closeCoverage(); + } + progressService.withProgress( + { location: options.locationForProgress }, + () => coverageService.openCoverage(task, true) + ); + } + })); + + this._register(this.tree.onDidChangeSelection(evt => { + for (const element of evt.elements) { + if (element && 'test' in element) { + explorerFilter.reveal.value = element.test.item.extId; + break; + } + } + })); + + + this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); + + this.tree.setChildren(null, getRootChildren()); + for (const result of results.results) { + if (!result.completedAt && result instanceof LiveTestResult) { + attachToResults(result); + } + } + } + + public layout(height: number, width: number) { + this.tree.layout(height, width); + } + + private onContextMenu(evt: ITreeContextMenuEvent) { + if (!evt.element) { + return; + } + + const actions = this.treeActions.provideActionBar(evt.element); + this.contextMenuService.showContextMenu({ + getAnchor: () => evt.anchor, + getActions: () => actions.secondary.length + ? [...actions.primary, new Separator(), ...actions.secondary] + : actions.primary, + getActionsContext: () => evt.element?.context + }); + } + + public override dispose() { + super.dispose(); + this.disposed = true; + } +} + +interface TemplateData { + label: HTMLElement; + icon: HTMLElement; + actionBar: ActionBar; + elementDisposable: DisposableStore; + templateDisposable: DisposableStore; +} + +class TestRunElementRenderer implements ICompressibleTreeRenderer { + public static readonly ID = 'testRunElementRenderer'; + public readonly templateId = TestRunElementRenderer.ID; + + constructor( + private readonly treeActions: TreeActionsProvider, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { } + + /** @inheritdoc */ + public renderCompressedElements(node: ITreeNode, FuzzyScore>, _index: number, templateData: TemplateData): void { + const chain = node.element.elements; + const lastElement = chain[chain.length - 1]; + if ((lastElement instanceof TaskElement || lastElement instanceof TestMessageElement) && chain.length >= 2) { + this.doRender(chain[chain.length - 2], templateData, lastElement); + } else { + this.doRender(lastElement, templateData); + } + } + + /** @inheritdoc */ + public renderTemplate(container: HTMLElement): TemplateData { + const templateDisposable = new DisposableStore(); + const wrapper = dom.append(container, dom.$('.test-peek-item')); + const icon = dom.append(wrapper, dom.$('.state')); + const label = dom.append(wrapper, dom.$('.name')); + + const actionBar = new ActionBar(wrapper, { + actionViewItemProvider: (action, options) => + action instanceof MenuItemAction + ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) + : undefined + }); + + const elementDisposable = new DisposableStore(); + templateDisposable.add(elementDisposable); + templateDisposable.add(actionBar); + + return { + icon, + label, + actionBar, + elementDisposable, + templateDisposable, + }; + } + + /** @inheritdoc */ + public renderElement(element: ITreeNode, _index: number, templateData: TemplateData): void { + this.doRender(element.element, templateData); + } + + /** @inheritdoc */ + public disposeTemplate(templateData: TemplateData): void { + templateData.templateDisposable.dispose(); + } + + /** Called to render a new element */ + private doRender(element: ITreeElement, templateData: TemplateData, subjectElement?: ITreeElement) { + templateData.elementDisposable.clear(); + templateData.elementDisposable.add( + element.onDidChange(() => this.doRender(element, templateData, subjectElement)), + ); + this.doRenderInner(element, templateData, subjectElement); + } + + /** Called, and may be re-called, to render or re-render an element */ + private doRenderInner(element: ITreeElement, templateData: TemplateData, subjectElement: ITreeElement | undefined) { + let { label, labelWithIcons, description } = element; + if (subjectElement instanceof TestMessageElement) { + description = subjectElement.label; + } + + const descriptionElement = description ? dom.$('span.test-label-description', {}, description) : ''; + if (labelWithIcons) { + dom.reset(templateData.label, ...labelWithIcons, descriptionElement); + } else { + dom.reset(templateData.label, label, descriptionElement); + } + + const icon = element.icon; + templateData.icon.className = `computed-state ${icon ? ThemeIcon.asClassName(icon) : ''}`; + + const actions = this.treeActions.provideActionBar(element); + templateData.actionBar.clear(); + templateData.actionBar.context = element.context; + templateData.actionBar.push(actions.primary, { icon: true, label: false }); + } +} + +class TreeActionsProvider { + constructor( + private readonly showRevealLocationOnMessages: boolean, + private readonly requestReveal: Emitter, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IMenuService private readonly menuService: IMenuService, + @ICommandService private readonly commandService: ICommandService, + @ITestProfileService private readonly testProfileService: ITestProfileService, + @IEditorService private readonly editorService: IEditorService, + ) { } + + public provideActionBar(element: ITreeElement) { + const test = element instanceof TestCaseElement ? element.test : undefined; + const capabilities = test ? this.testProfileService.capabilitiesForTest(test) : 0; + + const contextKeys: [string, unknown][] = [ + ['peek', Testing.OutputPeekContributionId], + [TestingContextKeys.peekItemType.key, element.type], + ]; + + let id = MenuId.TestPeekElement; + const primary: IAction[] = []; + const secondary: IAction[] = []; + + if (element instanceof TaskElement) { + primary.push(new Action( + 'testing.outputPeek.showResultOutput', + localize('testing.showResultOutput', "Show Result Output"), + ThemeIcon.asClassName(Codicon.terminal), + undefined, + () => this.requestReveal.fire(new TaskSubject(element.results, element.index)), + )); + } + + if (element instanceof TestResultElement) { + // only show if there are no collapsed test nodes that have more specific choices + if (element.value.tasks.length === 1) { + primary.push(new Action( + 'testing.outputPeek.showResultOutput', + localize('testing.showResultOutput', "Show Result Output"), + ThemeIcon.asClassName(Codicon.terminal), + undefined, + () => this.requestReveal.fire(new TaskSubject(element.value, 0)), + )); + } + + primary.push(new Action( + 'testing.outputPeek.reRunLastRun', + localize('testing.reRunLastRun', "Rerun Test Run"), + ThemeIcon.asClassName(icons.testingRunIcon), + undefined, + () => this.commandService.executeCommand('testing.reRunLastRun', element.value.id), + )); + + if (capabilities & TestRunProfileBitset.Debug) { + primary.push(new Action( + 'testing.outputPeek.debugLastRun', + localize('testing.debugLastRun', "Debug Test Run"), + ThemeIcon.asClassName(icons.testingDebugIcon), + undefined, + () => this.commandService.executeCommand('testing.debugLastRun', element.value.id), + )); + } + } + + if (element instanceof TestCaseElement || element instanceof TestMessageElement) { + contextKeys.push( + [TestingContextKeys.testResultOutdated.key, element.test.retired], + [TestingContextKeys.testResultState.key, testResultStateToContextValues[element.test.ownComputedState]], + ...getTestItemContextOverlay(element.test, capabilities), + ); + + const extId = element.test.item.extId; + if (element.test.tasks[element.taskIndex].messages.some(m => m.type === TestMessageType.Output)) { + primary.push(new Action( + 'testing.outputPeek.showResultOutput', + localize('testing.showResultOutput', "Show Result Output"), + ThemeIcon.asClassName(Codicon.terminal), + undefined, + () => this.requestReveal.fire(element.outputSubject), + )); + } + + secondary.push(new Action( + 'testing.outputPeek.revealInExplorer', + localize('testing.revealInExplorer', "Reveal in Test Explorer"), + ThemeIcon.asClassName(Codicon.listTree), + undefined, + () => this.commandService.executeCommand('_revealTestInExplorer', extId), + )); + + if (capabilities & TestRunProfileBitset.Run) { + primary.push(new Action( + 'testing.outputPeek.runTest', + localize('run test', 'Run Test'), + ThemeIcon.asClassName(icons.testingRunIcon), + undefined, + () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Run, extId), + )); + } + + if (capabilities & TestRunProfileBitset.Debug) { + primary.push(new Action( + 'testing.outputPeek.debugTest', + localize('debug test', 'Debug Test'), + ThemeIcon.asClassName(icons.testingDebugIcon), + undefined, + () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Debug, extId), + )); + } + + } + + if (element instanceof TestMessageElement) { + primary.push(new Action( + 'testing.outputPeek.goToFile', + localize('testing.goToFile', "Go to Source"), + ThemeIcon.asClassName(Codicon.goToFile), + undefined, + () => this.commandService.executeCommand('vscode.revealTest', element.test.item.extId), + )); + } + + if (element instanceof TestMessageElement) { + id = MenuId.TestMessageContext; + contextKeys.push([TestingContextKeys.testMessageContext.key, element.contextValue]); + if (this.showRevealLocationOnMessages && element.location) { + primary.push(new Action( + 'testing.outputPeek.goToError', + localize('testing.goToError', "Go to Source"), + ThemeIcon.asClassName(Codicon.goToFile), + undefined, + () => this.editorService.openEditor({ + resource: element.location!.uri, + options: { + selection: element.location!.range, + preserveFocus: true, + } + }), + )); + } + } + + + const contextOverlay = this.contextKeyService.createOverlay(contextKeys); + const result = { primary, secondary }; + const menu = this.menuService.getMenuActions(id, contextOverlay, { arg: element.context }); + createAndFillInActionBarActions(menu, result, 'inline'); + return result; + } +} + +class CreationCache { + private readonly v = new WeakMap(); + + public get(key: object): T2 | undefined { + return this.v.get(key) as T2 | undefined; + } + + public getOrCreate(ref: object, factory: () => T2): T2 { + const existing = this.v.get(ref); + if (existing) { + return existing as T2; + } + + const fresh = factory(); + this.v.set(ref, fresh); + return fresh; + } +} + +const firstLine = (str: string) => { + const index = str.indexOf('\n'); + return index === -1 ? str : str.slice(0, index); +}; diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index ec21581a9a6..0012ae4a1c5 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -5,56 +5,39 @@ import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; -import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; import { alert } from 'vs/base/browser/ui/aria/aria'; import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { DomScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; -import { ICompressedTreeElement, ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; -import { ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; -import { ITreeContextMenuEvent, ITreeNode } from 'vs/base/browser/ui/tree/tree'; -import { Action, IAction, Separator } from 'vs/base/common/actions'; -import { Delayer, Limiter, RunOnceScheduler } from 'vs/base/common/async'; -import { VSBuffer } from 'vs/base/common/buffer'; +import { IAction } from 'vs/base/common/actions'; +import { Limiter } from 'vs/base/common/async'; import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; -import { FuzzyScore } from 'vs/base/common/filters'; -import { IMarkdownString } from 'vs/base/common/htmlContent'; import { stripIcons } from 'vs/base/common/iconLabels'; import { Iterable } from 'vs/base/common/iterator'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; -import { Disposable, DisposableStore, IDisposable, IReference, MutableDisposable, combinedDisposable, toDisposable } from 'vs/base/common/lifecycle'; -import { MarshalledId } from 'vs/base/common/marshallingIds'; -import { autorun } from 'vs/base/common/observable'; +import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; import { count } from 'vs/base/common/strings'; -import { ThemeIcon } from 'vs/base/common/themables'; -import { isDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./testingOutputPeek'; -import { ICodeEditor, IDiffEditorConstructionOptions, isCodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { EditorAction2 } from 'vs/editor/browser/editorExtensions'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; -import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/codeEditorWidget'; import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/embeddedCodeEditorWidget'; -import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { EmbeddedDiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/embeddedDiffEditorWidget'; -import { MarkdownRenderer } from 'vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer'; -import { IDiffEditorOptions, IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { Position } from 'vs/editor/common/core/position'; import { Range } from 'vs/editor/common/core/range'; import { IEditor, IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { IResolvedTextEditorModel, ITextModelService } from 'vs/editor/common/services/resolverService'; -import { IPeekViewService, PeekViewWidget, peekViewResultsBackground, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { IPeekViewService, PeekViewWidget, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView'; import { localize, localize2 } from 'vs/nls'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; import { FloatingClickMenu } from 'vs/platform/actions/browser/floatingMenu'; -import { MenuEntryActionViewItem, createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; -import { Action2, IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions'; +import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; +import { Action2, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ContextKeyExpr, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; @@ -65,116 +48,35 @@ import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiati import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IProgressService } from 'vs/platform/progress/common/progress'; import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { TerminalCapability } from 'vs/platform/terminal/common/capabilities/capabilities'; -import { TerminalCapabilityStore } from 'vs/platform/terminal/common/capabilities/terminalCapabilityStore'; -import { formatMessageForTerminal } from 'vs/platform/terminal/common/terminalStrings'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; -import { widgetClose } from 'vs/platform/theme/common/iconRegistry'; import { IThemeService } from 'vs/platform/theme/common/themeService'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; -import { EditorModel } from 'vs/workbench/common/editor/editorModel'; -import { PANEL_BACKGROUND, SIDE_BAR_BACKGROUND } from 'vs/workbench/common/theme'; -import { IViewDescriptorService, ViewContainerLocation } from 'vs/workbench/common/views'; -import { DetachedProcessInfo } from 'vs/workbench/contrib/terminal/browser/detachedTerminal'; -import { IDetachedTerminalInstance, ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; -import { getXtermScaledDimensions } from 'vs/workbench/contrib/terminal/browser/xterm/xtermTerminal'; -import { TERMINAL_BACKGROUND_COLOR } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; -import { getTestItemContextOverlay } from 'vs/workbench/contrib/testing/browser/explorerProjections/testItemContextOverlay'; -import * as icons from 'vs/workbench/contrib/testing/browser/icons'; -import { colorizeTestMessageInEditor, renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; +import { IViewDescriptorService } from 'vs/workbench/common/views'; +import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; +import { DiffContentProvider, IPeekOutputRenderer, MarkdownTestMessagePeek, PlainTextMessagePeek, TerminalMessagePeek } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput'; +import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, equalsSubject, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { OutputPeekTree } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree'; import { testingMessagePeekBorder, testingPeekBorder, testingPeekHeaderBackground, testingPeekMessageHeaderBackground } from 'vs/workbench/contrib/testing/browser/theme'; import { AutoOpenPeekViewWhen, TestingConfigKeys, getTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; import { IObservableValue, MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; -import { ITestCoverageService } from 'vs/workbench/contrib/testing/common/testCoverageService'; -import { ITestExplorerFilterState } from 'vs/workbench/contrib/testing/common/testExplorerFilterState'; -import { ITestProfileService } from 'vs/workbench/contrib/testing/common/testProfileService'; -import { ITaskRawOutput, ITestResult, ITestRunTaskResults, LiveTestResult, TestResultItemChange, TestResultItemChangeReason, maxCountPriority, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestResult, LiveTestResult, TestResultItemChange, TestResultItemChangeReason, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService, ResultChangeEvent } from 'vs/workbench/contrib/testing/common/testResultService'; import { ITestFollowup, ITestService } from 'vs/workbench/contrib/testing/common/testService'; -import { IRichLocation, ITestErrorMessage, ITestItem, ITestItemContext, ITestMessage, ITestMessageMenuArgs, ITestRunTask, ITestTaskState, InternalTestItem, TestMessageType, TestResultItem, TestResultState, TestRunProfileBitset, getMarkId, testResultStateToContextValues } from 'vs/workbench/contrib/testing/common/testTypes'; +import { IRichLocation, ITestMessage, TestMessageType, TestResultItem } from 'vs/workbench/contrib/testing/common/testTypes'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { IShowResultOptions, ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener'; -import { cmpPriority, isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; +import { isFailedState } from 'vs/workbench/contrib/testing/common/testingStates'; import { ParsedTestUri, TestUriType, buildTestUri, parseTestUri } from 'vs/workbench/contrib/testing/common/testingUri'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IViewsService } from 'vs/workbench/services/views/common/viewsService'; -const getMessageArgs = (test: TestResultItem, message: ITestMessage): ITestMessageMenuArgs => ({ - $mid: MarshalledId.TestMessageMenuArgs, - test: InternalTestItem.serialize(test), - message: ITestMessage.serialize(message), -}); - -class MessageSubject { - public readonly test: ITestItem; - public readonly message: ITestMessage; - public readonly expectedUri: URI; - public readonly actualUri: URI; - public readonly messageUri: URI; - public readonly revealLocation: IRichLocation | undefined; - public readonly context: ITestMessageMenuArgs | undefined; - - public get isDiffable() { - return this.message.type === TestMessageType.Error && isDiffable(this.message); - } - - public get contextValue() { - return this.message.type === TestMessageType.Error ? this.message.contextValue : undefined; - } - - constructor(public readonly result: ITestResult, test: TestResultItem, public readonly taskIndex: number, public readonly messageIndex: number) { - this.test = test.item; - const messages = test.tasks[taskIndex].messages; - this.messageIndex = messageIndex; - - const parts = { messageIndex, resultId: result.id, taskIndex, testExtId: test.item.extId }; - this.expectedUri = buildTestUri({ ...parts, type: TestUriType.ResultExpectedOutput }); - this.actualUri = buildTestUri({ ...parts, type: TestUriType.ResultActualOutput }); - this.messageUri = buildTestUri({ ...parts, type: TestUriType.ResultMessage }); - - const message = this.message = messages[this.messageIndex]; - this.context = getMessageArgs(test, message); - this.revealLocation = message.location ?? (test.item.uri && test.item.range ? { uri: test.item.uri, range: Range.lift(test.item.range) } : undefined); - } -} - -class TaskSubject { - public readonly outputUri: URI; - public readonly revealLocation: undefined; - - constructor(public readonly result: ITestResult, public readonly taskIndex: number) { - this.outputUri = buildTestUri({ resultId: result.id, taskIndex, type: TestUriType.TaskOutput }); - } -} - -class TestOutputSubject { - public readonly outputUri: URI; - public readonly revealLocation: undefined; - public readonly task: ITestRunTask; - - constructor(public readonly result: ITestResult, public readonly taskIndex: number, public readonly test: TestResultItem) { - this.outputUri = buildTestUri({ resultId: this.result.id, taskIndex: this.taskIndex, testExtId: this.test.item.extId, type: TestUriType.TestOutput }); - this.task = result.tasks[this.taskIndex]; - } -} - -type InspectSubject = MessageSubject | TaskSubject | TestOutputSubject; - -const equalsSubject = (a: InspectSubject, b: InspectSubject) => ( - (a instanceof MessageSubject && b instanceof MessageSubject && a.message === b.message) || - (a instanceof TaskSubject && b instanceof TaskSubject && a.result === b.result && a.taskIndex === b.taskIndex) || - (a instanceof TestOutputSubject && b instanceof TestOutputSubject && a.test === b.test && a.taskIndex === b.taskIndex) -); /** Iterates through every message in every result */ function* allMessages(results: readonly ITestResult[]) { @@ -501,19 +403,6 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener } } -const mapFindTestMessage = (test: TestResultItem, fn: (task: ITestTaskState, message: ITestMessage, messageIndex: number, taskIndex: number) => T | undefined) => { - for (let taskIndex = 0; taskIndex < test.tasks.length; taskIndex++) { - const task = test.tasks[taskIndex]; - for (let messageIndex = 0; messageIndex < task.messages.length; messageIndex++) { - const r = fn(task, task.messages[messageIndex], messageIndex, taskIndex); - if (r !== undefined) { - return r; - } - } - } - - return undefined; -}; /** * Adds output/message peek functionality to code editors. @@ -1245,491 +1134,8 @@ export class TestResultsView extends ViewPane { } } -interface IPeekOutputRenderer extends IDisposable { - /** Updates the displayed test. Should clear if it cannot display the test. */ - update(subject: InspectSubject): void; - /** Recalculate content layout. */ - layout(dimension: dom.IDimension): void; - /** Dispose the content provider. */ - dispose(): void; -} - -const commonEditorOptions: IEditorOptions = { - scrollBeyondLastLine: false, - links: true, - lineNumbers: 'off', - scrollbar: { - verticalScrollbarSize: 14, - horizontal: 'auto', - useShadows: true, - verticalHasArrows: false, - horizontalHasArrows: false, - alwaysConsumeMouseWheel: false - }, - fixedOverflowWidgets: true, - readOnly: true, - minimap: { - enabled: false - }, - wordWrap: 'on', -}; - -const diffEditorOptions: IDiffEditorConstructionOptions = { - ...commonEditorOptions, - enableSplitViewResizing: true, - isInEmbeddedEditor: true, - renderOverviewRuler: false, - ignoreTrimWhitespace: false, - renderSideBySide: true, - useInlineViewWhenSpaceIsLimited: false, - originalAriaLabel: localize('testingOutputExpected', 'Expected result'), - modifiedAriaLabel: localize('testingOutputActual', 'Actual result'), - diffAlgorithm: 'advanced', -}; - -const isDiffable = (message: ITestMessage): message is ITestErrorMessage & { actual: string; expected: string } => - message.type === TestMessageType.Error && message.actual !== undefined && message.expected !== undefined; - -class DiffContentProvider extends Disposable implements IPeekOutputRenderer { - private readonly widget = this._register(new MutableDisposable()); - private readonly model = this._register(new MutableDisposable()); - private dimension?: dom.IDimension; - - constructor( - private readonly editor: ICodeEditor | undefined, - private readonly container: HTMLElement, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextModelService private readonly modelService: ITextModelService, - ) { - super(); - } - - public async update(subject: InspectSubject) { - if (!(subject instanceof MessageSubject)) { - return this.clear(); - } - const message = subject.message; - if (!isDiffable(message)) { - return this.clear(); - } - - const [original, modified] = await Promise.all([ - this.modelService.createModelReference(subject.expectedUri), - this.modelService.createModelReference(subject.actualUri), - ]); - - const model = this.model.value = new SimpleDiffEditorModel(original, modified); - if (!this.widget.value) { - this.widget.value = this.editor ? this.instantiationService.createInstance( - EmbeddedDiffEditorWidget, - this.container, - diffEditorOptions, - {}, - this.editor, - ) : this.instantiationService.createInstance( - DiffEditorWidget, - this.container, - diffEditorOptions, - {}, - ); - - if (this.dimension) { - this.widget.value.layout(this.dimension); - } - } - - this.widget.value.setModel(model); - this.widget.value.updateOptions(this.getOptions( - isMultiline(message.expected) || isMultiline(message.actual) - )); - } - - private clear() { - this.model.clear(); - this.widget.clear(); - } - - public layout(dimensions: dom.IDimension) { - this.dimension = dimensions; - this.widget.value?.layout(dimensions); - } - - protected getOptions(isMultiline: boolean): IDiffEditorOptions { - return isMultiline - ? { ...diffEditorOptions, lineNumbers: 'on' } - : { ...diffEditorOptions, lineNumbers: 'off' }; - } -} - -class ScrollableMarkdownMessage extends Disposable { - private readonly scrollable: DomScrollableElement; - private readonly element: HTMLElement; - - constructor(container: HTMLElement, markdown: MarkdownRenderer, message: IMarkdownString) { - super(); - - const rendered = this._register(markdown.render(message, {})); - rendered.element.style.height = '100%'; - rendered.element.style.userSelect = 'text'; - container.appendChild(rendered.element); - this.element = rendered.element; - - this.scrollable = this._register(new DomScrollableElement(rendered.element, { - className: 'preview-text', - })); - container.appendChild(this.scrollable.getDomNode()); - - this._register(toDisposable(() => { - this.scrollable.getDomNode().remove(); - })); - - this.scrollable.scanDomNode(); - } - - public layout(height: number, width: number) { - // Remove padding of `.monaco-editor .zone-widget.test-output-peek .preview-text` - this.scrollable.setScrollDimensions({ - width: width - 32, - height: height - 16, - scrollWidth: this.element.scrollWidth, - scrollHeight: this.element.scrollHeight - }); - } -} - -class MarkdownTestMessagePeek extends Disposable implements IPeekOutputRenderer { - private readonly markdown = new Lazy( - () => this._register(this.instantiationService.createInstance(MarkdownRenderer, {})), - ); - - private readonly textPreview = this._register(new MutableDisposable()); - - constructor(private readonly container: HTMLElement, @IInstantiationService private readonly instantiationService: IInstantiationService) { - super(); - } - - public update(subject: InspectSubject): void { - if (!(subject instanceof MessageSubject)) { - return this.textPreview.clear(); - } - - const message = subject.message; - if (isDiffable(message) || typeof message.message === 'string') { - return this.textPreview.clear(); - } - - this.textPreview.value = new ScrollableMarkdownMessage( - this.container, - this.markdown.value, - message.message as IMarkdownString, - ); - } - - public layout(dimension: dom.IDimension): void { - this.textPreview.value?.layout(dimension.height, dimension.width); - } -} - -class PlainTextMessagePeek extends Disposable implements IPeekOutputRenderer { - private readonly widgetDecorations = this._register(new MutableDisposable()); - private readonly widget = this._register(new MutableDisposable()); - private readonly model = this._register(new MutableDisposable()); - private dimension?: dom.IDimension; - - constructor( - private readonly editor: ICodeEditor | undefined, - private readonly container: HTMLElement, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextModelService private readonly modelService: ITextModelService, - ) { - super(); - } - - public async update(subject: InspectSubject) { - if (!(subject instanceof MessageSubject)) { - return this.clear(); - } - - const message = subject.message; - if (isDiffable(message) || message.type === TestMessageType.Output || typeof message.message !== 'string') { - return this.clear(); - } - - const modelRef = this.model.value = await this.modelService.createModelReference(subject.messageUri); - if (!this.widget.value) { - this.widget.value = this.editor ? this.instantiationService.createInstance( - EmbeddedCodeEditorWidget, - this.container, - commonEditorOptions, - {}, - this.editor, - ) : this.instantiationService.createInstance( - CodeEditorWidget, - this.container, - commonEditorOptions, - { isSimpleWidget: true } - ); - - if (this.dimension) { - this.widget.value.layout(this.dimension); - } - } - - this.widget.value.setModel(modelRef.object.textEditorModel); - this.widget.value.updateOptions(commonEditorOptions); - this.widgetDecorations.value = colorizeTestMessageInEditor(message.message, this.widget.value); - } - - private clear() { - this.widgetDecorations.clear(); - this.widget.clear(); - this.model.clear(); - } - - public layout(dimensions: dom.IDimension) { - this.dimension = dimensions; - this.widget.value?.layout(dimensions); - } -} - -class TerminalMessagePeek extends Disposable implements IPeekOutputRenderer { - private dimensions?: dom.IDimension; - private readonly terminalCwd = this._register(new MutableObservableValue('')); - private readonly xtermLayoutDelayer = this._register(new Delayer(50)); - - /** Active terminal instance. */ - private readonly terminal = this._register(new MutableDisposable()); - /** Listener for streaming result data */ - private readonly outputDataListener = this._register(new MutableDisposable()); - - constructor( - private readonly container: HTMLElement, - private readonly isInPeekView: boolean, - @ITerminalService private readonly terminalService: ITerminalService, - @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, - @IWorkspaceContextService private readonly workspaceContext: IWorkspaceContextService, - ) { - super(); - } - - private async makeTerminal() { - const prev = this.terminal.value; - if (prev) { - prev.xterm.clearBuffer(); - prev.xterm.clearSearchDecorations(); - // clearBuffer tries to retain the prompt line, but this doesn't exist for tests. - // So clear the screen (J) and move to home (H) to ensure previous data is cleaned up. - prev.xterm.write(`\x1b[2J\x1b[0;0H`); - return prev; - } - - const capabilities = new TerminalCapabilityStore(); - const cwd = this.terminalCwd; - capabilities.add(TerminalCapability.CwdDetection, { - type: TerminalCapability.CwdDetection, - get cwds() { return [cwd.value]; }, - onDidChangeCwd: cwd.onDidChange, - getCwd: () => cwd.value, - updateCwd: () => { }, - }); - - return this.terminal.value = await this.terminalService.createDetachedTerminal({ - rows: 10, - cols: 80, - readonly: true, - capabilities, - processInfo: new DetachedProcessInfo({ initialCwd: cwd.value }), - colorProvider: { - getBackgroundColor: theme => { - const terminalBackground = theme.getColor(TERMINAL_BACKGROUND_COLOR); - if (terminalBackground) { - return terminalBackground; - } - if (this.isInPeekView) { - return theme.getColor(peekViewResultsBackground); - } - const location = this.viewDescriptorService.getViewLocationById(Testing.ResultsViewId); - return location === ViewContainerLocation.Panel - ? theme.getColor(PANEL_BACKGROUND) - : theme.getColor(SIDE_BAR_BACKGROUND); - }, - } - }); - } - - public async update(subject: InspectSubject) { - this.outputDataListener.clear(); - if (subject instanceof TaskSubject) { - await this.updateForTaskSubject(subject); - } else if (subject instanceof TestOutputSubject || (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output)) { - await this.updateForTestSubject(subject); - } else { - this.clear(); - } - } - - private async updateForTestSubject(subject: TestOutputSubject | MessageSubject) { - const that = this; - const testItem = subject instanceof TestOutputSubject ? subject.test.item : subject.test; - const terminal = await this.updateGenerically({ - subject, - noOutputMessage: localize('caseNoOutput', 'The test case did not report any output.'), - getTarget: result => result?.tasks[subject.taskIndex].output, - *doInitialWrite(output, results) { - that.updateCwd(testItem.uri); - const state = subject instanceof TestOutputSubject ? subject.test : results.getStateById(testItem.extId); - if (!state) { - return; - } - - for (const message of state.tasks[subject.taskIndex].messages) { - if (message.type === TestMessageType.Output) { - yield* output.getRangeIter(message.offset, message.length); - } - } - }, - doListenForMoreData: (output, result, write) => result.onChange(e => { - if (e.reason === TestResultItemChangeReason.NewMessage && e.item.item.extId === testItem.extId && e.message.type === TestMessageType.Output) { - for (const chunk of output.getRangeIter(e.message.offset, e.message.length)) { - write(chunk.buffer); - } - } - }), - }); - - if (subject instanceof MessageSubject && subject.message.type === TestMessageType.Output && subject.message.marker !== undefined) { - terminal?.xterm.selectMarkedRange(getMarkId(subject.message.marker, true), getMarkId(subject.message.marker, false), /* scrollIntoView= */ true); - } - } - - private updateForTaskSubject(subject: TaskSubject) { - return this.updateGenerically({ - subject, - noOutputMessage: localize('runNoOutput', 'The test run did not record any output.'), - getTarget: result => result?.tasks[subject.taskIndex], - doInitialWrite: (task, result) => { - // Update the cwd and use the first test to try to hint at the correct cwd, - // but often this will fall back to the first workspace folder. - this.updateCwd(Iterable.find(result.tests, t => !!t.item.uri)?.item.uri); - return task.output.buffers; - }, - doListenForMoreData: (task, _result, write) => task.output.onDidWriteData(e => write(e.buffer)), - }); - } - - private async updateGenerically(opts: { - subject: InspectSubject; - noOutputMessage: string; - getTarget: (result: ITestResult) => T | undefined; - doInitialWrite: (target: T, result: LiveTestResult) => Iterable; - doListenForMoreData: (target: T, result: LiveTestResult, write: (s: Uint8Array) => void) => IDisposable; - }) { - const result = opts.subject.result; - const target = opts.getTarget(result); - if (!target) { - return this.clear(); - } - - const terminal = await this.makeTerminal(); - let didWriteData = false; - - const pendingWrites = new MutableObservableValue(0); - if (result instanceof LiveTestResult) { - for (const chunk of opts.doInitialWrite(target, result)) { - didWriteData ||= chunk.byteLength > 0; - pendingWrites.value++; - terminal.xterm.write(chunk.buffer, () => pendingWrites.value--); - } - } else { - didWriteData = true; - this.writeNotice(terminal, localize('runNoOutputForPast', 'Test output is only available for new test runs.')); - } - - this.attachTerminalToDom(terminal); - this.outputDataListener.clear(); - - if (result instanceof LiveTestResult && !result.completedAt) { - const l1 = result.onComplete(() => { - if (!didWriteData) { - this.writeNotice(terminal, opts.noOutputMessage); - } - }); - const l2 = opts.doListenForMoreData(target, result, data => { - terminal.xterm.write(data); - didWriteData ||= data.byteLength > 0; - }); - - this.outputDataListener.value = combinedDisposable(l1, l2); - } - - if (!this.outputDataListener.value && !didWriteData) { - this.writeNotice(terminal, opts.noOutputMessage); - } - - // Ensure pending writes finish, otherwise the selection in `updateForTestSubject` - // can happen before the markers are processed. - if (pendingWrites.value > 0) { - await new Promise(resolve => { - const l = pendingWrites.onDidChange(() => { - if (pendingWrites.value === 0) { - l.dispose(); - resolve(); - } - }); - }); - } - - return terminal; - } - - private updateCwd(testUri?: URI) { - const wf = (testUri && this.workspaceContext.getWorkspaceFolder(testUri)) - || this.workspaceContext.getWorkspace().folders[0]; - if (wf) { - this.terminalCwd.value = wf.uri.fsPath; - } - } - - private writeNotice(terminal: IDetachedTerminalInstance, str: string) { - terminal.xterm.write(formatMessageForTerminal(str)); - } - - private attachTerminalToDom(terminal: IDetachedTerminalInstance) { - terminal.xterm.write('\x1b[?25l'); // hide cursor - dom.scheduleAtNextAnimationFrame(dom.getWindow(this.container), () => this.layoutTerminal(terminal)); - terminal.attachToElement(this.container, { enableGpu: false }); - } - - private clear() { - this.outputDataListener.clear(); - this.xtermLayoutDelayer.cancel(); - this.terminal.clear(); - } - - public layout(dimensions: dom.IDimension) { - this.dimensions = dimensions; - if (this.terminal.value) { - this.layoutTerminal(this.terminal.value, dimensions.width, dimensions.height); - } - } - - private layoutTerminal( - { xterm }: IDetachedTerminalInstance, - width = this.dimensions?.width ?? this.container.clientWidth, - height = this.dimensions?.height ?? this.container.clientHeight - ) { - width -= 10 + 20; // scrollbar width + margin - this.xtermLayoutDelayer.trigger(() => { - const scaled = getXtermScaledDimensions(dom.getWindow(this.container), xterm.getFont(), width, height); - if (scaled) { - xterm.resize(scaled.cols, scaled.rows); - } - }); - } -} - const hintMessagePeekHeight = (msg: ITestMessage) => { - const msgHeight = isDiffable(msg) + const msgHeight = ITestMessage.isDiffable(msg) ? Math.max(hintPeekStrHeight(msg.actual), hintPeekStrHeight(msg.expected)) : hintPeekStrHeight(typeof msg.message === 'string' ? msg.message : msg.message.value); @@ -1742,28 +1148,9 @@ const firstLine = (str: string) => { return index === -1 ? str : str.slice(0, index); }; -const isMultiline = (str: string | undefined) => !!str && str.includes('\n'); const hintPeekStrHeight = (str: string) => Math.min(count(str, '\n'), 24); -class SimpleDiffEditorModel extends EditorModel { - public readonly original = this._original.object.textEditorModel; - public readonly modified = this._modified.object.textEditorModel; - - constructor( - private readonly _original: IReference, - private readonly _modified: IReference, - ) { - super(); - } - - public override dispose() { - super.dispose(); - this._original.dispose(); - this._modified.dispose(); - } -} - function getOuterEditorFromDiffEditor(codeEditorService: ICodeEditorService): ICodeEditor | null { const diffEditors = codeEditorService.listDiffEditors(); @@ -1797,771 +1184,6 @@ export class CloseTestPeek extends EditorAction2 { } } -interface ITreeElement { - type: string; - context: unknown; - id: string; - label: string; - onDidChange: Event; - labelWithIcons?: readonly (HTMLSpanElement | string)[]; - icon?: ThemeIcon; - description?: string; - ariaLabel?: string; -} - -class TestResultElement implements ITreeElement { - public readonly changeEmitter = new Emitter(); - public readonly onDidChange = this.changeEmitter.event; - public readonly type = 'result'; - public readonly context = this.value.id; - public readonly id = this.value.id; - public readonly label = this.value.name; - - public get icon() { - return icons.testingStatesToIcons.get( - this.value.completedAt === undefined - ? TestResultState.Running - : maxCountPriority(this.value.counts) - ); - } - - constructor(public readonly value: ITestResult) { } -} - -const openCoverageLabel = localize('openTestCoverage', 'View Test Coverage'); -const closeCoverageLabel = localize('closeTestCoverage', 'Close Test Coverage'); - -class CoverageElement implements ITreeElement { - public readonly type = 'coverage'; - public readonly context: undefined; - public readonly id = `coverage-${this.results.id}/${this.task.id}`; - public readonly onDidChange: Event; - - public get label() { - return this.isOpen ? closeCoverageLabel : openCoverageLabel; - } - - public get icon() { - return this.isOpen ? widgetClose : icons.testingCoverageReport; - } - - public get isOpen() { - return this.coverageService.selected.get()?.fromTaskId === this.task.id; - } - - constructor( - private readonly results: ITestResult, - public readonly task: ITestRunTaskResults, - private readonly coverageService: ITestCoverageService, - ) { - this.onDidChange = Event.fromObservableLight(coverageService.selected); - } - -} - -class TestCaseElement implements ITreeElement { - public readonly type = 'test'; - public readonly context: ITestItemContext = { - $mid: MarshalledId.TestItemContext, - tests: [InternalTestItem.serialize(this.test)], - }; - public readonly id = `${this.results.id}/${this.test.item.extId}`; - public readonly description?: string; - - public get onDidChange() { - if (!(this.results instanceof LiveTestResult)) { - return Event.None; - } - - return Event.filter(this.results.onChange, e => e.item.item.extId === this.test.item.extId); - } - - public get state() { - return this.test.tasks[this.taskIndex].state; - } - - public get label() { - return this.test.item.label; - } - - public get labelWithIcons() { - return renderLabelWithIcons(this.label); - } - - public get icon() { - return icons.testingStatesToIcons.get(this.state); - } - - public get outputSubject() { - return new TestOutputSubject(this.results, this.taskIndex, this.test); - } - - - constructor( - public readonly results: ITestResult, - public readonly test: TestResultItem, - public readonly taskIndex: number, - ) { } -} - -class TaskElement implements ITreeElement { - public readonly changeEmitter = new Emitter(); - public readonly onDidChange = this.changeEmitter.event; - public readonly type = 'task'; - public readonly context: string; - public readonly id: string; - public readonly label: string; - public readonly itemsCache = new CreationCache(); - - public get icon() { - return this.results.tasks[this.index].running ? icons.testingStatesToIcons.get(TestResultState.Running) : undefined; - } - - constructor(public readonly results: ITestResult, public readonly task: ITestRunTaskResults, public readonly index: number) { - this.id = `${results.id}/${index}`; - this.task = results.tasks[index]; - this.context = String(index); - this.label = this.task.name ?? localize('testUnnamedTask', 'Unnamed Task'); - } -} - -class TestMessageElement implements ITreeElement { - public readonly type = 'message'; - public readonly id: string; - public readonly label: string; - public readonly uri: URI; - public readonly location?: IRichLocation; - public readonly description?: string; - public readonly contextValue?: string; - public readonly message: ITestMessage; - - public get onDidChange() { - if (!(this.result instanceof LiveTestResult)) { - return Event.None; - } - - // rerender when the test case changes so it gets retired events - return Event.filter(this.result.onChange, e => e.item.item.extId === this.test.item.extId); - } - - public get context(): ITestMessageMenuArgs { - return getMessageArgs(this.test, this.message); - } - - public get outputSubject() { - return new TestOutputSubject(this.result, this.taskIndex, this.test); - } - - constructor( - public readonly result: ITestResult, - public readonly test: TestResultItem, - public readonly taskIndex: number, - public readonly messageIndex: number, - ) { - const m = this.message = test.tasks[taskIndex].messages[messageIndex]; - - this.location = m.location; - this.contextValue = m.type === TestMessageType.Error ? m.contextValue : undefined; - this.uri = buildTestUri({ - type: TestUriType.ResultMessage, - messageIndex, - resultId: result.id, - taskIndex, - testExtId: test.item.extId - }); - - this.id = this.uri.toString(); - - const asPlaintext = renderTestMessageAsText(m.message); - const lines = count(asPlaintext.trimEnd(), '\n'); - this.label = firstLine(asPlaintext); - if (lines > 0) { - this.description = lines > 1 - ? localize('messageMoreLinesN', '+ {0} more lines', lines) - : localize('messageMoreLines1', '+ 1 more line'); - } - } -} - -type TreeElement = TestResultElement | TestCaseElement | TestMessageElement | TaskElement | CoverageElement; - -class OutputPeekTree extends Disposable { - private disposed = false; - private readonly tree: WorkbenchCompressibleObjectTree; - private readonly treeActions: TreeActionsProvider; - private readonly requestReveal = this._register(new Emitter()); - - public readonly onDidRequestReview = this.requestReveal.event; - - constructor( - container: HTMLElement, - onDidReveal: Event<{ subject: InspectSubject; preserveFocus: boolean }>, - options: { showRevealLocationOnMessages: boolean; locationForProgress: string }, - @IContextMenuService private readonly contextMenuService: IContextMenuService, - @ITestResultService results: ITestResultService, - @IInstantiationService instantiationService: IInstantiationService, - @ITestExplorerFilterState explorerFilter: ITestExplorerFilterState, - @ITestCoverageService coverageService: ITestCoverageService, - @IProgressService progressService: IProgressService, - ) { - super(); - - this.treeActions = instantiationService.createInstance(TreeActionsProvider, options.showRevealLocationOnMessages, this.requestReveal,); - const diffIdentityProvider: IIdentityProvider = { - getId(e: TreeElement) { - return e.id; - } - }; - - this.tree = this._register(instantiationService.createInstance( - WorkbenchCompressibleObjectTree, - 'Test Output Peek', - container, - { - getHeight: () => 22, - getTemplateId: () => TestRunElementRenderer.ID, - }, - [instantiationService.createInstance(TestRunElementRenderer, this.treeActions)], - { - compressionEnabled: true, - hideTwistiesOfChildlessElements: true, - identityProvider: diffIdentityProvider, - sorter: { - compare(a, b) { - if (a instanceof TestCaseElement && b instanceof TestCaseElement) { - return cmpPriority(a.state, b.state); - } - - return 0; - }, - }, - accessibilityProvider: { - getAriaLabel(element: ITreeElement) { - return element.ariaLabel || element.label; - }, - getWidgetAriaLabel() { - return localize('testingPeekLabel', 'Test Result Messages'); - } - } - }, - )) as WorkbenchCompressibleObjectTree; - - const cc = new CreationCache(); - const getTaskChildren = (taskElem: TaskElement): Iterable> => { - const { results, index, itemsCache, task } = taskElem; - const tests = Iterable.filter(results.tests, test => test.tasks[index].state >= TestResultState.Running || test.tasks[index].messages.length > 0); - let result: Iterable> = Iterable.map(tests, test => ({ - element: itemsCache.getOrCreate(test, () => new TestCaseElement(results, test, index)), - incompressible: true, - children: getTestChildren(results, test, index), - })); - - if (task.coverage.get()) { - result = Iterable.concat( - Iterable.single>({ - element: new CoverageElement(results, task, coverageService), - collapsible: true, - incompressible: true, - }), - result, - ); - } - - return result; - }; - - const getTestChildren = (result: ITestResult, test: TestResultItem, taskIndex: number): Iterable> => { - return test.tasks[taskIndex].messages - .map((m, messageIndex) => - m.type === TestMessageType.Error - ? { element: cc.getOrCreate(m, () => new TestMessageElement(result, test, taskIndex, messageIndex)), incompressible: false } - : undefined - ) - .filter(isDefined); - }; - - const getResultChildren = (result: ITestResult): Iterable> => { - return result.tasks.map((task, taskIndex) => { - const taskElem = cc.getOrCreate(task, () => new TaskElement(result, task, taskIndex)); - return ({ - element: taskElem, - incompressible: false, - collapsible: true, - children: getTaskChildren(taskElem), - }); - }); - }; - - const getRootChildren = () => results.results.map(result => { - const element = cc.getOrCreate(result, () => new TestResultElement(result)); - return { - element, - incompressible: true, - collapsible: true, - collapsed: this.tree.hasElement(element) ? this.tree.isCollapsed(element) : true, - children: getResultChildren(result) - }; - }); - - // Queued result updates to prevent spamming CPU when lots of tests are - // completing and messaging quickly (#142514) - const taskChildrenToUpdate = new Set(); - const taskChildrenUpdate = this._register(new RunOnceScheduler(() => { - for (const taskNode of taskChildrenToUpdate) { - if (this.tree.hasElement(taskNode)) { - this.tree.setChildren(taskNode, getTaskChildren(taskNode), { diffIdentityProvider }); - } - } - taskChildrenToUpdate.clear(); - }, 300)); - - const queueTaskChildrenUpdate = (taskNode: TaskElement) => { - taskChildrenToUpdate.add(taskNode); - if (!taskChildrenUpdate.isScheduled()) { - taskChildrenUpdate.schedule(); - } - }; - - const attachToResults = (result: LiveTestResult) => { - const resultNode = cc.get(result)! as TestResultElement; - const disposable = new DisposableStore(); - disposable.add(result.onNewTask(i => { - if (result.tasks.length === 1) { - this.requestReveal.fire(new TaskSubject(result, 0)); // reveal the first task in new runs - } - - if (this.tree.hasElement(resultNode)) { - this.tree.setChildren(resultNode, getResultChildren(result), { diffIdentityProvider }); - } - - // note: tasks are bounded and their lifetime is equivalent to that of - // the test result, so this doesn't leak indefinitely. - const task = result.tasks[i]; - disposable.add(autorun(reader => { - task.coverage.read(reader); // add it to the autorun - queueTaskChildrenUpdate(cc.get(task) as TaskElement); - })); - })); - disposable.add(result.onEndTask(index => { - (cc.get(result.tasks[index]) as TaskElement | undefined)?.changeEmitter.fire(); - })); - - disposable.add(result.onChange(e => { - // try updating the item in each of its tasks - for (const [index, task] of result.tasks.entries()) { - const taskNode = cc.get(task) as TaskElement; - if (!this.tree.hasElement(taskNode)) { - continue; - } - - const itemNode = taskNode.itemsCache.get(e.item); - if (itemNode && this.tree.hasElement(itemNode)) { - if (e.reason === TestResultItemChangeReason.NewMessage && e.message.type === TestMessageType.Error) { - this.tree.setChildren(itemNode, getTestChildren(result, e.item, index), { diffIdentityProvider }); - } - return; - } - - queueTaskChildrenUpdate(taskNode); - } - })); - - disposable.add(result.onComplete(() => { - resultNode.changeEmitter.fire(); - disposable.dispose(); - })); - - return resultNode; - }; - - this._register(results.onResultsChanged(e => { - // little hack here: a result change can cause the peek to be disposed, - // but this listener will still be queued. Doing stuff with the tree - // will cause errors. - if (this.disposed) { - return; - } - - if ('completed' in e) { - (cc.get(e.completed) as TestResultElement | undefined)?.changeEmitter.fire(); - return; - } - - this.tree.setChildren(null, getRootChildren(), { diffIdentityProvider }); - - // done after setChildren intentionally so that the ResultElement exists in the cache. - if ('started' in e) { - for (const child of this.tree.getNode(null).children) { - this.tree.collapse(child.element, false); - } - - this.tree.expand(attachToResults(e.started), true); - } - })); - - const revealItem = (element: TreeElement, preserveFocus: boolean) => { - this.tree.setFocus([element]); - this.tree.setSelection([element]); - if (!preserveFocus) { - this.tree.domFocus(); - } - }; - - this._register(onDidReveal(async ({ subject, preserveFocus = false }) => { - if (subject instanceof TaskSubject) { - const resultItem = this.tree.getNode(null).children.find(c => { - if (c.element instanceof TaskElement) { - return c.element.results.id === subject.result.id && c.element.index === subject.taskIndex; - } - if (c.element instanceof TestResultElement) { - return c.element.id === subject.result.id; - } - return false; - }); - - if (resultItem) { - revealItem(resultItem.element!, preserveFocus); - } - return; - } - - const revealElement = subject instanceof TestOutputSubject - ? cc.get(subject.task)?.itemsCache.get(subject.test) - : cc.get(subject.message); - if (!revealElement || !this.tree.hasElement(revealElement)) { - return; - } - - const parents: TreeElement[] = []; - for (let parent = this.tree.getParentElement(revealElement); parent; parent = this.tree.getParentElement(parent)) { - parents.unshift(parent); - } - - for (const parent of parents) { - this.tree.expand(parent); - } - - if (this.tree.getRelativeTop(revealElement) === null) { - this.tree.reveal(revealElement, 0.5); - } - - revealItem(revealElement, preserveFocus); - })); - - this._register(this.tree.onDidOpen(async e => { - if (e.element instanceof TestMessageElement) { - this.requestReveal.fire(new MessageSubject(e.element.result, e.element.test, e.element.taskIndex, e.element.messageIndex)); - } else if (e.element instanceof TestCaseElement) { - const t = e.element; - const message = mapFindTestMessage(e.element.test, (_t, _m, mesasgeIndex, taskIndex) => - new MessageSubject(t.results, t.test, taskIndex, mesasgeIndex)); - this.requestReveal.fire(message || new TestOutputSubject(t.results, 0, t.test)); - } else if (e.element instanceof CoverageElement) { - const task = e.element.task; - if (e.element.isOpen) { - return coverageService.closeCoverage(); - } - progressService.withProgress( - { location: options.locationForProgress }, - () => coverageService.openCoverage(task, true) - ); - } - })); - - this._register(this.tree.onDidChangeSelection(evt => { - for (const element of evt.elements) { - if (element && 'test' in element) { - explorerFilter.reveal.value = element.test.item.extId; - break; - } - } - })); - - - this._register(this.tree.onContextMenu(e => this.onContextMenu(e))); - - this.tree.setChildren(null, getRootChildren()); - for (const result of results.results) { - if (!result.completedAt && result instanceof LiveTestResult) { - attachToResults(result); - } - } - } - - public layout(height: number, width: number) { - this.tree.layout(height, width); - } - - private onContextMenu(evt: ITreeContextMenuEvent) { - if (!evt.element) { - return; - } - - const actions = this.treeActions.provideActionBar(evt.element); - this.contextMenuService.showContextMenu({ - getAnchor: () => evt.anchor, - getActions: () => actions.secondary.length - ? [...actions.primary, new Separator(), ...actions.secondary] - : actions.primary, - getActionsContext: () => evt.element?.context - }); - } - - public override dispose() { - super.dispose(); - this.disposed = true; - } -} - -interface TemplateData { - label: HTMLElement; - icon: HTMLElement; - actionBar: ActionBar; - elementDisposable: DisposableStore; - templateDisposable: DisposableStore; -} - -class TestRunElementRenderer implements ICompressibleTreeRenderer { - public static readonly ID = 'testRunElementRenderer'; - public readonly templateId = TestRunElementRenderer.ID; - - constructor( - private readonly treeActions: TreeActionsProvider, - @IInstantiationService private readonly instantiationService: IInstantiationService, - ) { } - - /** @inheritdoc */ - public renderCompressedElements(node: ITreeNode, FuzzyScore>, _index: number, templateData: TemplateData): void { - const chain = node.element.elements; - const lastElement = chain[chain.length - 1]; - if ((lastElement instanceof TaskElement || lastElement instanceof TestMessageElement) && chain.length >= 2) { - this.doRender(chain[chain.length - 2], templateData, lastElement); - } else { - this.doRender(lastElement, templateData); - } - } - - /** @inheritdoc */ - public renderTemplate(container: HTMLElement): TemplateData { - const templateDisposable = new DisposableStore(); - const wrapper = dom.append(container, dom.$('.test-peek-item')); - const icon = dom.append(wrapper, dom.$('.state')); - const label = dom.append(wrapper, dom.$('.name')); - - const actionBar = new ActionBar(wrapper, { - actionViewItemProvider: (action, options) => - action instanceof MenuItemAction - ? this.instantiationService.createInstance(MenuEntryActionViewItem, action, { hoverDelegate: options.hoverDelegate }) - : undefined - }); - - const elementDisposable = new DisposableStore(); - templateDisposable.add(elementDisposable); - templateDisposable.add(actionBar); - - return { - icon, - label, - actionBar, - elementDisposable, - templateDisposable, - }; - } - - /** @inheritdoc */ - public renderElement(element: ITreeNode, _index: number, templateData: TemplateData): void { - this.doRender(element.element, templateData); - } - - /** @inheritdoc */ - public disposeTemplate(templateData: TemplateData): void { - templateData.templateDisposable.dispose(); - } - - /** Called to render a new element */ - private doRender(element: ITreeElement, templateData: TemplateData, subjectElement?: ITreeElement) { - templateData.elementDisposable.clear(); - templateData.elementDisposable.add( - element.onDidChange(() => this.doRender(element, templateData, subjectElement)), - ); - this.doRenderInner(element, templateData, subjectElement); - } - - /** Called, and may be re-called, to render or re-render an element */ - private doRenderInner(element: ITreeElement, templateData: TemplateData, subjectElement: ITreeElement | undefined) { - let { label, labelWithIcons, description } = element; - if (subjectElement instanceof TestMessageElement) { - description = subjectElement.label; - } - - const descriptionElement = description ? dom.$('span.test-label-description', {}, description) : ''; - if (labelWithIcons) { - dom.reset(templateData.label, ...labelWithIcons, descriptionElement); - } else { - dom.reset(templateData.label, label, descriptionElement); - } - - const icon = element.icon; - templateData.icon.className = `computed-state ${icon ? ThemeIcon.asClassName(icon) : ''}`; - - const actions = this.treeActions.provideActionBar(element); - templateData.actionBar.clear(); - templateData.actionBar.context = element.context; - templateData.actionBar.push(actions.primary, { icon: true, label: false }); - } -} - -class TreeActionsProvider { - constructor( - private readonly showRevealLocationOnMessages: boolean, - private readonly requestReveal: Emitter, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IMenuService private readonly menuService: IMenuService, - @ICommandService private readonly commandService: ICommandService, - @ITestProfileService private readonly testProfileService: ITestProfileService, - @IEditorService private readonly editorService: IEditorService, - ) { } - - public provideActionBar(element: ITreeElement) { - const test = element instanceof TestCaseElement ? element.test : undefined; - const capabilities = test ? this.testProfileService.capabilitiesForTest(test) : 0; - - const contextKeys: [string, unknown][] = [ - ['peek', Testing.OutputPeekContributionId], - [TestingContextKeys.peekItemType.key, element.type], - ]; - - let id = MenuId.TestPeekElement; - const primary: IAction[] = []; - const secondary: IAction[] = []; - - if (element instanceof TaskElement) { - primary.push(new Action( - 'testing.outputPeek.showResultOutput', - localize('testing.showResultOutput', "Show Result Output"), - ThemeIcon.asClassName(Codicon.terminal), - undefined, - () => this.requestReveal.fire(new TaskSubject(element.results, element.index)), - )); - } - - if (element instanceof TestResultElement) { - // only show if there are no collapsed test nodes that have more specific choices - if (element.value.tasks.length === 1) { - primary.push(new Action( - 'testing.outputPeek.showResultOutput', - localize('testing.showResultOutput', "Show Result Output"), - ThemeIcon.asClassName(Codicon.terminal), - undefined, - () => this.requestReveal.fire(new TaskSubject(element.value, 0)), - )); - } - - primary.push(new Action( - 'testing.outputPeek.reRunLastRun', - localize('testing.reRunLastRun', "Rerun Test Run"), - ThemeIcon.asClassName(icons.testingRunIcon), - undefined, - () => this.commandService.executeCommand('testing.reRunLastRun', element.value.id), - )); - - if (capabilities & TestRunProfileBitset.Debug) { - primary.push(new Action( - 'testing.outputPeek.debugLastRun', - localize('testing.debugLastRun', "Debug Test Run"), - ThemeIcon.asClassName(icons.testingDebugIcon), - undefined, - () => this.commandService.executeCommand('testing.debugLastRun', element.value.id), - )); - } - } - - if (element instanceof TestCaseElement || element instanceof TestMessageElement) { - contextKeys.push( - [TestingContextKeys.testResultOutdated.key, element.test.retired], - [TestingContextKeys.testResultState.key, testResultStateToContextValues[element.test.ownComputedState]], - ...getTestItemContextOverlay(element.test, capabilities), - ); - - const extId = element.test.item.extId; - if (element.test.tasks[element.taskIndex].messages.some(m => m.type === TestMessageType.Output)) { - primary.push(new Action( - 'testing.outputPeek.showResultOutput', - localize('testing.showResultOutput', "Show Result Output"), - ThemeIcon.asClassName(Codicon.terminal), - undefined, - () => this.requestReveal.fire(element.outputSubject), - )); - } - - secondary.push(new Action( - 'testing.outputPeek.revealInExplorer', - localize('testing.revealInExplorer', "Reveal in Test Explorer"), - ThemeIcon.asClassName(Codicon.listTree), - undefined, - () => this.commandService.executeCommand('_revealTestInExplorer', extId), - )); - - if (capabilities & TestRunProfileBitset.Run) { - primary.push(new Action( - 'testing.outputPeek.runTest', - localize('run test', 'Run Test'), - ThemeIcon.asClassName(icons.testingRunIcon), - undefined, - () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Run, extId), - )); - } - - if (capabilities & TestRunProfileBitset.Debug) { - primary.push(new Action( - 'testing.outputPeek.debugTest', - localize('debug test', 'Debug Test'), - ThemeIcon.asClassName(icons.testingDebugIcon), - undefined, - () => this.commandService.executeCommand('vscode.runTestsById', TestRunProfileBitset.Debug, extId), - )); - } - - } - - if (element instanceof TestMessageElement) { - primary.push(new Action( - 'testing.outputPeek.goToFile', - localize('testing.goToFile', "Go to Source"), - ThemeIcon.asClassName(Codicon.goToFile), - undefined, - () => this.commandService.executeCommand('vscode.revealTest', element.test.item.extId), - )); - } - - if (element instanceof TestMessageElement) { - id = MenuId.TestMessageContext; - contextKeys.push([TestingContextKeys.testMessageContext.key, element.contextValue]); - if (this.showRevealLocationOnMessages && element.location) { - primary.push(new Action( - 'testing.outputPeek.goToError', - localize('testing.goToError', "Go to Source"), - ThemeIcon.asClassName(Codicon.goToFile), - undefined, - () => this.editorService.openEditor({ - resource: element.location!.uri, - options: { - selection: element.location!.range, - preserveFocus: true, - } - }), - )); - } - } - - - const contextOverlay = this.contextKeyService.createOverlay(contextKeys); - const result = { primary, secondary }; - const menu = this.menuService.getMenuActions(id, contextOverlay, { arg: element.context }); - createAndFillInActionBarActions(menu, result, 'inline'); - return result; - } -} const navWhen = ContextKeyExpr.and( EditorContextKeys.focus, @@ -2717,22 +1339,3 @@ export class ToggleTestingPeekHistory extends Action2 { opener.historyVisible.value = !opener.historyVisible.value; } } - -class CreationCache { - private readonly v = new WeakMap(); - - public get(key: object): T2 | undefined { - return this.v.get(key) as T2 | undefined; - } - - public getOrCreate(ref: object, factory: () => T2): T2 { - const existing = this.v.get(ref); - if (existing) { - return existing as T2; - } - - const fresh = factory(); - this.v.set(ref, fresh); - return fresh; - } -} diff --git a/src/vs/workbench/contrib/testing/common/testTypes.ts b/src/vs/workbench/contrib/testing/common/testTypes.ts index 5a90948fc68..f551217600e 100644 --- a/src/vs/workbench/contrib/testing/common/testTypes.ts +++ b/src/vs/workbench/contrib/testing/common/testTypes.ts @@ -169,6 +169,32 @@ export const enum TestMessageType { Output } +export interface ITestMessageStackTrace { + label: string; + uri: URI | undefined; + position: Position | undefined; +} + +export namespace ITestMessageStackTrace { + export interface Serialized { + label: string; + uri: UriComponents | undefined; + position: IPosition | undefined; + } + + export const serialize = (stack: Readonly): Serialized => ({ + label: stack.label, + uri: stack.uri?.toJSON(), + position: stack.position?.toJSON(), + }); + + export const deserialize = (uriIdentity: ITestUriCanonicalizer, stack: Serialized): ITestMessageStackTrace => ({ + label: stack.label, + uri: stack.uri ? uriIdentity.asCanonicalUri(URI.revive(stack.uri)) : undefined, + position: stack.position ? Position.lift(stack.position) : undefined, + }); +} + export interface ITestErrorMessage { message: string | IMarkdownString; type: TestMessageType.Error; @@ -176,6 +202,7 @@ export interface ITestErrorMessage { actual: string | undefined; contextValue: string | undefined; location: IRichLocation | undefined; + stackTrace: undefined | ITestMessageStackTrace[]; } export namespace ITestErrorMessage { @@ -186,6 +213,7 @@ export namespace ITestErrorMessage { actual: string | undefined; contextValue: string | undefined; location: IRichLocation.Serialize | undefined; + stackTrace: undefined | ITestMessageStackTrace.Serialized[]; } export const serialize = (message: Readonly): Serialized => ({ @@ -195,6 +223,7 @@ export namespace ITestErrorMessage { actual: message.actual, contextValue: message.contextValue, location: message.location && IRichLocation.serialize(message.location), + stackTrace: message.stackTrace?.map(ITestMessageStackTrace.serialize), }); export const deserialize = (uriIdentity: ITestUriCanonicalizer, message: Serialized): ITestErrorMessage => ({ @@ -204,6 +233,7 @@ export namespace ITestErrorMessage { actual: message.actual, contextValue: message.contextValue, location: message.location && IRichLocation.deserialize(uriIdentity, message.location), + stackTrace: message.stackTrace && message.stackTrace.map(s => ITestMessageStackTrace.deserialize(uriIdentity, s)), }); } @@ -258,6 +288,9 @@ export namespace ITestMessage { export const deserialize = (uriIdentity: ITestUriCanonicalizer, message: Serialized): ITestMessage => message.type === TestMessageType.Error ? ITestErrorMessage.deserialize(uriIdentity, message) : ITestOutputMessage.deserialize(uriIdentity, message); + + export const isDiffable = (message: ITestMessage): message is ITestErrorMessage & { actual: string; expected: string } => + message.type === TestMessageType.Error && message.actual !== undefined && message.expected !== undefined; } export interface ITestTaskState { diff --git a/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts b/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts new file mode 100644 index 00000000000..b3f09b92835 --- /dev/null +++ b/src/vscode-dts/vscode.proposed.testMessageStackTrace.d.ts @@ -0,0 +1,38 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +declare module 'vscode' { + export class TestMessage2 extends TestMessage { + /** + * The stack trace associated with the message or failure. + */ + stackTrace?: TestMessageStackFrame[]; + } + + export class TestMessageStackFrame { + /** + * The location of this stack frame. This should be provided as a URI if the + * location of the call frame can be accessed by the editor. + */ + file?: Uri; + + /** + * Position of the stack frame within the file. + */ + position?: Position; + + /** + * The name of the stack frame, typically a method or function name. + */ + label: string; + + /** + * @param label The name of the stack frame + * @param file The file URI of the stack frame + * @param position The position of the stack frame within the file + */ + constructor(label: string, file?: Uri, position?: Position); + } +} From fecf501d5569125c1b08821838c584fc4c0d0ad4 Mon Sep 17 00:00:00 2001 From: Connor Peet Date: Mon, 8 Jul 2024 15:52:05 -0700 Subject: [PATCH 0352/2222] fix tests, more split --- .../api/test/browser/extHostTesting.test.ts | 4 +- .../testResultsView/testResultsViewContent.ts | 305 ++++++++++++++++++ .../testing/browser/testingOutputPeek.ts | 293 +---------------- 3 files changed, 314 insertions(+), 288 deletions(-) create mode 100644 src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts diff --git a/src/vs/workbench/api/test/browser/extHostTesting.test.ts b/src/vs/workbench/api/test/browser/extHostTesting.test.ts index c4515796d05..4a49c8031a2 100644 --- a/src/vs/workbench/api/test/browser/extHostTesting.test.ts +++ b/src/vs/workbench/api/test/browser/extHostTesting.test.ts @@ -829,7 +829,8 @@ suite('ExtHost Testing', () => { expected: undefined, contextValue: undefined, actual: undefined, - location: convert.location.from(message1.location) + location: convert.location.from(message1.location), + stackTrace: undefined, }] ]); @@ -846,6 +847,7 @@ suite('ExtHost Testing', () => { expected: undefined, actual: undefined, location: convert.location.from({ uri: test2.uri!, range: test2.range }), + stackTrace: undefined, }] ]); diff --git a/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts new file mode 100644 index 00000000000..2bdc631d95e --- /dev/null +++ b/src/vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent.ts @@ -0,0 +1,305 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; +import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; +import { Limiter } from 'vs/base/common/async'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { Emitter, Event } from 'vs/base/common/event'; +import { KeyCode } from 'vs/base/common/keyCodes'; +import { Disposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; +import 'vs/css!./testingOutputPeek'; +import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; +import { ITextModelService } from 'vs/editor/common/services/resolverService'; +import { localize } from 'vs/nls'; +import { FloatingClickMenu } from 'vs/platform/actions/browser/floatingMenu'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { DiffContentProvider, IPeekOutputRenderer, MarkdownTestMessagePeek, PlainTextMessagePeek, TerminalMessagePeek } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput'; +import { InspectSubject, MessageSubject, equalsSubject } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { OutputPeekTree } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree'; +import { TestingOutputPeekController } from 'vs/workbench/contrib/testing/browser/testingOutputPeek'; +import { IObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; +import { LiveTestResult } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestFollowup, ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; + + +export class TestResultsViewContent extends Disposable { + private static lastSplitWidth?: number; + + private readonly didReveal = this._register(new Emitter<{ subject: InspectSubject; preserveFocus: boolean }>()); + private readonly currentSubjectStore = this._register(new DisposableStore()); + private followupWidget!: FollowupActionWidget; + private messageContextKeyService!: IContextKeyService; + private contextKeyTestMessage!: IContextKey; + private contextKeyResultOutdated!: IContextKey; + + private dimension?: dom.Dimension; + private splitView!: SplitView; + private messageContainer!: HTMLElement; + private contentProviders!: IPeekOutputRenderer[]; + private contentProvidersUpdateLimiter = this._register(new Limiter(1)); + + public current?: InspectSubject; + + /** Fired when a tree item is selected. Populated only on .fillBody() */ + public onDidRequestReveal!: Event; + + constructor( + private readonly editor: ICodeEditor | undefined, + private readonly options: { + historyVisible: IObservableValue; + showRevealLocationOnMessages: boolean; + locationForProgress: string; + }, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITextModelService protected readonly modelService: ITextModelService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + ) { + super(); + } + + public fillBody(containerElement: HTMLElement): void { + const initialSpitWidth = TestResultsViewContent.lastSplitWidth; + this.splitView = new SplitView(containerElement, { orientation: Orientation.HORIZONTAL }); + + const { historyVisible, showRevealLocationOnMessages } = this.options; + const isInPeekView = this.editor !== undefined; + const messageContainer = this.messageContainer = dom.append(containerElement, dom.$('.test-output-peek-message-container')); + this.followupWidget = this._register(this.instantiationService.createInstance(FollowupActionWidget, messageContainer, this.editor)); + this.contentProviders = [ + this._register(this.instantiationService.createInstance(DiffContentProvider, this.editor, messageContainer)), + this._register(this.instantiationService.createInstance(MarkdownTestMessagePeek, messageContainer)), + this._register(this.instantiationService.createInstance(TerminalMessagePeek, messageContainer, isInPeekView)), + this._register(this.instantiationService.createInstance(PlainTextMessagePeek, this.editor, messageContainer)), + ]; + + this.messageContextKeyService = this._register(this.contextKeyService.createScoped(containerElement)); + this.contextKeyTestMessage = TestingContextKeys.testMessageContext.bindTo(this.messageContextKeyService); + this.contextKeyResultOutdated = TestingContextKeys.testResultOutdated.bindTo(this.messageContextKeyService); + + const treeContainer = dom.append(containerElement, dom.$('.test-output-peek-tree')); + const tree = this._register(this.instantiationService.createInstance( + OutputPeekTree, + treeContainer, + this.didReveal.event, + { showRevealLocationOnMessages, locationForProgress: this.options.locationForProgress }, + )); + + this.onDidRequestReveal = tree.onDidRequestReview; + + this.splitView.addView({ + onDidChange: Event.None, + element: messageContainer, + minimumSize: 200, + maximumSize: Number.MAX_VALUE, + layout: width => { + TestResultsViewContent.lastSplitWidth = width; + if (this.dimension) { + for (const provider of this.contentProviders) { + provider.layout({ height: this.dimension.height, width }); + } + } + }, + }, Sizing.Distribute); + + this.splitView.addView({ + onDidChange: Event.None, + element: treeContainer, + minimumSize: 100, + maximumSize: Number.MAX_VALUE, + layout: width => { + if (this.dimension) { + tree.layout(this.dimension.height, width); + } + }, + }, Sizing.Distribute); + + const historyViewIndex = 1; + this.splitView.setViewVisible(historyViewIndex, historyVisible.value); + this._register(historyVisible.onDidChange(visible => { + this.splitView.setViewVisible(historyViewIndex, visible); + })); + + if (initialSpitWidth) { + queueMicrotask(() => this.splitView.resizeView(0, initialSpitWidth)); + } + } + + /** + * Shows a message in-place without showing or changing the peek location. + * This is mostly used if peeking a message without a location. + */ + public reveal(opts: { subject: InspectSubject; preserveFocus: boolean }) { + this.didReveal.fire(opts); + + if (this.current && equalsSubject(this.current, opts.subject)) { + return Promise.resolve(); + } + + this.current = opts.subject; + return this.contentProvidersUpdateLimiter.queue(async () => { + await Promise.all(this.contentProviders.map(p => p.update(opts.subject))); + this.followupWidget.show(opts.subject); + this.currentSubjectStore.clear(); + this.populateFloatingClick(opts.subject); + }); + } + + private populateFloatingClick(subject: InspectSubject) { + if (!(subject instanceof MessageSubject)) { + return; + } + + this.currentSubjectStore.add(toDisposable(() => { + this.contextKeyResultOutdated.reset(); + this.contextKeyTestMessage.reset(); + })); + + this.contextKeyTestMessage.set(subject.contextValue || ''); + if (subject.result instanceof LiveTestResult) { + this.contextKeyResultOutdated.set(subject.result.getStateById(subject.test.extId)?.retired ?? false); + this.currentSubjectStore.add(subject.result.onChange(ev => { + if (ev.item.item.extId === subject.test.extId) { + this.contextKeyResultOutdated.set(ev.item.retired ?? false); + } + })); + } else { + this.contextKeyResultOutdated.set(true); + } + + const instaService = this.currentSubjectStore.add(this.instantiationService + .createChild(new ServiceCollection([IContextKeyService, this.messageContextKeyService]))); + + this.currentSubjectStore.add(instaService.createInstance(FloatingClickMenu, { + container: this.messageContainer, + menuId: MenuId.TestMessageContent, + getActionArg: () => (subject as MessageSubject).context, + })); + } + + public onLayoutBody(height: number, width: number) { + this.dimension = new dom.Dimension(width, height); + this.splitView.layout(width); + } + + public onWidth(width: number) { + this.splitView.layout(width); + } +} + +const FOLLOWUP_ANIMATION_MIN_TIME = 500; + +class FollowupActionWidget extends Disposable { + private readonly el = dom.h('div.testing-followup-action', []); + private readonly visibleStore = this._register(new DisposableStore()); + + constructor( + private readonly container: HTMLElement, + private readonly editor: ICodeEditor | undefined, + @ITestService private readonly testService: ITestService, + @IQuickInputService private readonly quickInput: IQuickInputService, + ) { + super(); + } + + public show(subject: InspectSubject) { + this.visibleStore.clear(); + if (subject instanceof MessageSubject) { + this.showMessage(subject); + } + } + + private async showMessage(subject: MessageSubject) { + const cts = this.visibleStore.add(new CancellationTokenSource()); + const start = Date.now(); + + // Wait for completion otherwise results will not be available to the ext host: + if (subject.result instanceof LiveTestResult && !subject.result.completedAt) { + await new Promise(r => Event.once((subject.result as LiveTestResult).onComplete)(r)); + } + + const followups = await this.testService.provideTestFollowups({ + extId: subject.test.extId, + messageIndex: subject.messageIndex, + resultId: subject.result.id, + taskIndex: subject.taskIndex, + }, cts.token); + + + if (!followups.followups.length || cts.token.isCancellationRequested) { + followups.dispose(); + return; + } + + this.visibleStore.add(followups); + + dom.clearNode(this.el.root); + this.el.root.classList.toggle('animated', Date.now() - start > FOLLOWUP_ANIMATION_MIN_TIME); + + this.el.root.appendChild(this.makeFollowupLink(followups.followups[0])); + if (followups.followups.length > 1) { + this.el.root.appendChild(this.makeMoreLink(followups.followups)); + } + + this.container.appendChild(this.el.root); + this.visibleStore.add(toDisposable(() => { + this.el.root.remove(); + })); + } + + private makeFollowupLink(first: ITestFollowup) { + const link = this.makeLink(() => this.actionFollowup(link, first)); + dom.reset(link, ...renderLabelWithIcons(first.message)); + return link; + } + + private makeMoreLink(followups: ITestFollowup[]) { + const link = this.makeLink(() => + this.quickInput.pick(followups.map((f, i) => ({ + label: f.message, + index: i + }))).then(picked => { + if (picked?.length) { + followups[picked[0].index].execute(); + } + }) + ); + + link.innerText = localize('testFollowup.more', '+{0} More...', followups.length - 1); + return link; + } + + private makeLink(onClick: () => void) { + const link = document.createElement('a'); + link.tabIndex = 0; + this.visibleStore.add(dom.addDisposableListener(link, 'click', onClick)); + this.visibleStore.add(dom.addDisposableListener(link, 'keydown', e => { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { + onClick(); + } + })); + + return link; + } + + private actionFollowup(link: HTMLAnchorElement, fu: ITestFollowup) { + if (link.ariaDisabled !== 'true') { + link.ariaDisabled = 'true'; + fu.execute(); + + if (this.editor) { + TestingOutputPeekController.get(this.editor)?.removePeek(); + } + } + } +} diff --git a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts index 0012ae4a1c5..ca336ec5632 100644 --- a/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts +++ b/src/vs/workbench/contrib/testing/browser/testingOutputPeek.ts @@ -4,13 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from 'vs/base/browser/dom'; -import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { alert } from 'vs/base/browser/ui/aria/aria'; -import { renderLabelWithIcons } from 'vs/base/browser/ui/iconLabel/iconLabels'; -import { Orientation, Sizing, SplitView } from 'vs/base/browser/ui/splitview/splitview'; import { IAction } from 'vs/base/common/actions'; -import { Limiter } from 'vs/base/common/async'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; import { Codicon } from 'vs/base/common/codicons'; import { Color } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; @@ -18,7 +13,7 @@ import { stripIcons } from 'vs/base/common/iconLabels'; import { Iterable } from 'vs/base/common/iterator'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Lazy } from 'vs/base/common/lazy'; -import { Disposable, DisposableStore, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; import { count } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import 'vs/css!./testingOutputPeek'; @@ -35,7 +30,6 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { IPeekViewService, PeekViewWidget, peekViewTitleForeground, peekViewTitleInfoForeground } from 'vs/editor/contrib/peekView/browser/peekView'; import { localize, localize2 } from 'vs/nls'; import { Categories } from 'vs/platform/action/common/actionCommonCategories'; -import { FloatingClickMenu } from 'vs/platform/actions/browser/floatingMenu'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { Action2, IMenuService, MenuId } from 'vs/platform/actions/common/actions'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -50,7 +44,6 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; @@ -58,17 +51,16 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IViewPaneOptions, ViewPane } from 'vs/workbench/browser/parts/views/viewPane'; import { IViewDescriptorService } from 'vs/workbench/common/views'; import { renderTestMessageAsText } from 'vs/workbench/contrib/testing/browser/testMessageColorizer'; -import { DiffContentProvider, IPeekOutputRenderer, MarkdownTestMessagePeek, PlainTextMessagePeek, TerminalMessagePeek } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput'; -import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, equalsSubject, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; -import { OutputPeekTree } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree'; +import { InspectSubject, MessageSubject, TaskSubject, TestOutputSubject, mapFindTestMessage } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsSubject'; +import { TestResultsViewContent } from 'vs/workbench/contrib/testing/browser/testResultsView/testResultsViewContent'; import { testingMessagePeekBorder, testingPeekBorder, testingPeekHeaderBackground, testingPeekMessageHeaderBackground } from 'vs/workbench/contrib/testing/browser/theme'; import { AutoOpenPeekViewWhen, TestingConfigKeys, getTestingConfiguration } from 'vs/workbench/contrib/testing/common/configuration'; import { Testing } from 'vs/workbench/contrib/testing/common/constants'; -import { IObservableValue, MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; +import { MutableObservableValue, staticObservableValue } from 'vs/workbench/contrib/testing/common/observableValue'; import { StoredValue } from 'vs/workbench/contrib/testing/common/storedValue'; -import { ITestResult, LiveTestResult, TestResultItemChange, TestResultItemChangeReason, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; +import { ITestResult, TestResultItemChange, TestResultItemChangeReason, resultItemParents } from 'vs/workbench/contrib/testing/common/testResult'; import { ITestResultService, ResultChangeEvent } from 'vs/workbench/contrib/testing/common/testResultService'; -import { ITestFollowup, ITestService } from 'vs/workbench/contrib/testing/common/testService'; +import { ITestService } from 'vs/workbench/contrib/testing/common/testService'; import { IRichLocation, ITestMessage, TestMessageType, TestResultItem } from 'vs/workbench/contrib/testing/common/testTypes'; import { TestingContextKeys } from 'vs/workbench/contrib/testing/common/testingContextKeys'; import { IShowResultOptions, ITestingPeekOpener } from 'vs/workbench/contrib/testing/common/testingPeekOpener'; @@ -403,7 +395,6 @@ export class TestingPeekOpener extends Disposable implements ITestingPeekOpener } } - /** * Adds output/message peek functionality to code editors. */ @@ -661,278 +652,6 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo } } -const FOLLOWUP_ANIMATION_MIN_TIME = 500; - -class FollowupActionWidget extends Disposable { - private readonly el = dom.h('div.testing-followup-action', []); - private readonly visibleStore = this._register(new DisposableStore()); - - constructor( - private readonly container: HTMLElement, - private readonly editor: ICodeEditor | undefined, - @ITestService private readonly testService: ITestService, - @IQuickInputService private readonly quickInput: IQuickInputService, - ) { - super(); - } - - public show(subject: InspectSubject) { - this.visibleStore.clear(); - if (subject instanceof MessageSubject) { - this.showMessage(subject); - } - } - - private async showMessage(subject: MessageSubject) { - const cts = this.visibleStore.add(new CancellationTokenSource()); - const start = Date.now(); - - // Wait for completion otherwise results will not be available to the ext host: - if (subject.result instanceof LiveTestResult && !subject.result.completedAt) { - await new Promise(r => Event.once((subject.result as LiveTestResult).onComplete)(r)); - } - - const followups = await this.testService.provideTestFollowups({ - extId: subject.test.extId, - messageIndex: subject.messageIndex, - resultId: subject.result.id, - taskIndex: subject.taskIndex, - }, cts.token); - - - if (!followups.followups.length || cts.token.isCancellationRequested) { - followups.dispose(); - return; - } - - this.visibleStore.add(followups); - - dom.clearNode(this.el.root); - this.el.root.classList.toggle('animated', Date.now() - start > FOLLOWUP_ANIMATION_MIN_TIME); - - this.el.root.appendChild(this.makeFollowupLink(followups.followups[0])); - if (followups.followups.length > 1) { - this.el.root.appendChild(this.makeMoreLink(followups.followups)); - } - - this.container.appendChild(this.el.root); - this.visibleStore.add(toDisposable(() => { - this.el.root.remove(); - })); - } - - private makeFollowupLink(first: ITestFollowup) { - const link = this.makeLink(() => this.actionFollowup(link, first)); - dom.reset(link, ...renderLabelWithIcons(first.message)); - return link; - } - - private makeMoreLink(followups: ITestFollowup[]) { - const link = this.makeLink(() => - this.quickInput.pick(followups.map((f, i) => ({ - label: f.message, - index: i - }))).then(picked => { - if (picked?.length) { - followups[picked[0].index].execute(); - } - }) - ); - - link.innerText = localize('testFollowup.more', '+{0} More...', followups.length - 1); - return link; - } - - private makeLink(onClick: () => void) { - const link = document.createElement('a'); - link.tabIndex = 0; - this.visibleStore.add(dom.addDisposableListener(link, 'click', onClick)); - this.visibleStore.add(dom.addDisposableListener(link, 'keydown', e => { - const event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Space) || event.equals(KeyCode.Enter)) { - onClick(); - } - })); - - return link; - } - - private actionFollowup(link: HTMLAnchorElement, fu: ITestFollowup) { - if (link.ariaDisabled !== 'true') { - link.ariaDisabled = 'true'; - fu.execute(); - - if (this.editor) { - TestingOutputPeekController.get(this.editor)?.removePeek(); - } - } - } -} - -class TestResultsViewContent extends Disposable { - private static lastSplitWidth?: number; - - private readonly didReveal = this._register(new Emitter<{ subject: InspectSubject; preserveFocus: boolean }>()); - private readonly currentSubjectStore = this._register(new DisposableStore()); - private followupWidget!: FollowupActionWidget; - private messageContextKeyService!: IContextKeyService; - private contextKeyTestMessage!: IContextKey; - private contextKeyResultOutdated!: IContextKey; - - private dimension?: dom.Dimension; - private splitView!: SplitView; - private messageContainer!: HTMLElement; - private contentProviders!: IPeekOutputRenderer[]; - private contentProvidersUpdateLimiter = this._register(new Limiter(1)); - - public current?: InspectSubject; - - /** Fired when a tree item is selected. Populated only on .fillBody() */ - public onDidRequestReveal!: Event; - - constructor( - private readonly editor: ICodeEditor | undefined, - private readonly options: { - historyVisible: IObservableValue; - showRevealLocationOnMessages: boolean; - locationForProgress: string; - }, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @ITextModelService protected readonly modelService: ITextModelService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - ) { - super(); - } - - public fillBody(containerElement: HTMLElement): void { - const initialSpitWidth = TestResultsViewContent.lastSplitWidth; - this.splitView = new SplitView(containerElement, { orientation: Orientation.HORIZONTAL }); - - const { historyVisible, showRevealLocationOnMessages } = this.options; - const isInPeekView = this.editor !== undefined; - const messageContainer = this.messageContainer = dom.append(containerElement, dom.$('.test-output-peek-message-container')); - this.followupWidget = this._register(this.instantiationService.createInstance(FollowupActionWidget, messageContainer, this.editor)); - this.contentProviders = [ - this._register(this.instantiationService.createInstance(DiffContentProvider, this.editor, messageContainer)), - this._register(this.instantiationService.createInstance(MarkdownTestMessagePeek, messageContainer)), - this._register(this.instantiationService.createInstance(TerminalMessagePeek, messageContainer, isInPeekView)), - this._register(this.instantiationService.createInstance(PlainTextMessagePeek, this.editor, messageContainer)), - ]; - - this.messageContextKeyService = this._register(this.contextKeyService.createScoped(containerElement)); - this.contextKeyTestMessage = TestingContextKeys.testMessageContext.bindTo(this.messageContextKeyService); - this.contextKeyResultOutdated = TestingContextKeys.testResultOutdated.bindTo(this.messageContextKeyService); - - const treeContainer = dom.append(containerElement, dom.$('.test-output-peek-tree')); - const tree = this._register(this.instantiationService.createInstance( - OutputPeekTree, - treeContainer, - this.didReveal.event, - { showRevealLocationOnMessages, locationForProgress: this.options.locationForProgress }, - )); - - this.onDidRequestReveal = tree.onDidRequestReview; - - this.splitView.addView({ - onDidChange: Event.None, - element: messageContainer, - minimumSize: 200, - maximumSize: Number.MAX_VALUE, - layout: width => { - TestResultsViewContent.lastSplitWidth = width; - if (this.dimension) { - for (const provider of this.contentProviders) { - provider.layout({ height: this.dimension.height, width }); - } - } - }, - }, Sizing.Distribute); - - this.splitView.addView({ - onDidChange: Event.None, - element: treeContainer, - minimumSize: 100, - maximumSize: Number.MAX_VALUE, - layout: width => { - if (this.dimension) { - tree.layout(this.dimension.height, width); - } - }, - }, Sizing.Distribute); - - const historyViewIndex = 1; - this.splitView.setViewVisible(historyViewIndex, historyVisible.value); - this._register(historyVisible.onDidChange(visible => { - this.splitView.setViewVisible(historyViewIndex, visible); - })); - - if (initialSpitWidth) { - queueMicrotask(() => this.splitView.resizeView(0, initialSpitWidth)); - } - } - - /** - * Shows a message in-place without showing or changing the peek location. - * This is mostly used if peeking a message without a location. - */ - public reveal(opts: { subject: InspectSubject; preserveFocus: boolean }) { - this.didReveal.fire(opts); - - if (this.current && equalsSubject(this.current, opts.subject)) { - return Promise.resolve(); - } - - this.current = opts.subject; - return this.contentProvidersUpdateLimiter.queue(async () => { - await Promise.all(this.contentProviders.map(p => p.update(opts.subject))); - this.followupWidget.show(opts.subject); - this.currentSubjectStore.clear(); - this.populateFloatingClick(opts.subject); - }); - } - - private populateFloatingClick(subject: InspectSubject) { - if (!(subject instanceof MessageSubject)) { - return; - } - - this.currentSubjectStore.add(toDisposable(() => { - this.contextKeyResultOutdated.reset(); - this.contextKeyTestMessage.reset(); - })); - - this.contextKeyTestMessage.set(subject.contextValue || ''); - if (subject.result instanceof LiveTestResult) { - this.contextKeyResultOutdated.set(subject.result.getStateById(subject.test.extId)?.retired ?? false); - this.currentSubjectStore.add(subject.result.onChange(ev => { - if (ev.item.item.extId === subject.test.extId) { - this.contextKeyResultOutdated.set(ev.item.retired ?? false); - } - })); - } else { - this.contextKeyResultOutdated.set(true); - } - - - const instaService = this.currentSubjectStore.add(this.instantiationService - .createChild(new ServiceCollection([IContextKeyService, this.messageContextKeyService]))); - - this.currentSubjectStore.add(instaService.createInstance(FloatingClickMenu, { - container: this.messageContainer, - menuId: MenuId.TestMessageContent, - getActionArg: () => (subject as MessageSubject).context, - })); - } - - public onLayoutBody(height: number, width: number) { - this.dimension = new dom.Dimension(width, height); - this.splitView.layout(width); - } - - public onWidth(width: number) { - this.splitView.layout(width); - } -} class TestResultsPeek extends PeekViewWidget { private static lastHeightInLines?: number; From ad476c34165861460f13b0eee011a6c07393a345 Mon Sep 17 00:00:00 2001 From: Tyler James Leonhardt Date: Mon, 8 Jul 2024 16:07:00 -0700 Subject: [PATCH 0353/2222] Bump that distro (#221226) :drumroll: --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 81a0e0d416d..b99927cdeef 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.92.0", - "distro": "58e7d90e05684b6937db21dd372f7a088bdc9dc1", + "distro": "6e11724ed97152dcd4510f2520755c983a6f439c", "author": { "name": "Microsoft Corporation" }, From b23e791eb5afbd95f05aa24da7693ce89344a079 Mon Sep 17 00:00:00 2001 From: Bhavya U Date: Mon, 8 Jul 2024 17:40:28 -0700 Subject: [PATCH 0354/2222] Use AzureLogin task for latest-release-monitor (#221194) --- .github/workflows/latest-release-monitor.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.github/workflows/latest-release-monitor.yml b/.github/workflows/latest-release-monitor.yml index f7392dc24a8..7db2cedf9df 100644 --- a/.github/workflows/latest-release-monitor.yml +++ b/.github/workflows/latest-release-monitor.yml @@ -1,4 +1,9 @@ name: Latest Release Monitor + +permissions: + id-token: write + contents: read + on: schedule: - cron: 0/5 * * * * @@ -8,7 +13,13 @@ on: jobs: main: runs-on: ubuntu-latest + environment: main steps: + - uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + allow-no-subscriptions: true - name: Checkout Actions uses: actions/checkout@v4 with: @@ -22,6 +33,4 @@ jobs: - name: Run Latest Release Monitor uses: ./actions/latest-release-monitor with: - storageKey: ${{secrets.AZURE_BLOB_STORAGE_CONNECTION_STRING}} - appInsightsKey: ${{secrets.TRIAGE_ACTIONS_APP_INSIGHTS}} token: ${{secrets.VSCODE_ISSUE_TRIAGE_BOT_PAT}} From 2fe05272052cf1f0ab2133f7f4874e9a748e8e74 Mon Sep 17 00:00:00 2001 From: Robo Date: Tue, 9 Jul 2024 14:33:22 +0900 Subject: [PATCH 0355/2222] chore: update to electron 30 (#215406) * chore: update electron@30.0.9 * chore: update rpm deps * chore: bump electron@30.1.2 * fix: update kerberos for Node.js 20.x Refs https://github.com/mongodb-js/kerberos/commit/c1f7acafb211d1b449086433578495d4ae0b869f * fix: use shell when spawning .bat or .cmd files Refs https://github.com/nodejs/node/commit/6627222409 * fix: update @vscode/test-electron@2.4.0 Refs https://github.com/microsoft/vscode-test/commit/3f7a3cc5c537957d55fa9e6aeab9d860f7a60078 * fixup! use shell when spawning .bat or .cmd files * chore: bump nodejs@20.14.0 internal build * ci: skip nodejsMirror for 20.14.0 due to missing builds * fixup! use shell when spawning .bat or .cmd files * chore: update debian deps * fixup! skip nodejsMirror for 20.14.0 due to missing builds * fix: universal build - Updates vscode-universal-bundler to support x64ArchFiles option - Kerberos starts building universal binaries which should now be skipped from lipo step via x64ArchFiles - Skips bundling *.mk files * chore: bump distro --- .nvmrc | 2 +- .yarnrc | 4 +- .../alpine/cli-build-alpine.yml | 2 +- .../darwin/cli-build-darwin.yml | 2 +- .../darwin/product-build-darwin-cli-sign.yml | 2 +- .../darwin/product-build-darwin-sign.yml | 2 +- .../darwin/product-build-darwin-universal.yml | 2 +- .../darwin/product-build-darwin.yml | 2 +- build/azure-pipelines/linux/setup-env.sh | 8 +- build/checksums/electron.txt | 150 ++++++------- build/checksums/nodejs.txt | 14 +- build/darwin/create-universal-app.js | 31 +-- build/darwin/create-universal-app.ts | 32 +-- build/gulpfile.reh.js | 41 ++-- build/gulpfile.vscode.js | 2 + build/lib/asar.js | 13 +- build/lib/asar.ts | 14 +- build/linux/debian/dep-lists.js | 3 + build/linux/debian/dep-lists.ts | 3 + build/linux/dependencies-generator.js | 2 +- build/linux/dependencies-generator.ts | 2 +- build/linux/rpm/dep-lists.js | 5 + build/linux/rpm/dep-lists.ts | 5 + build/package.json | 2 +- build/yarn.lock | 169 ++++++++------- cgmanifest.json | 14 +- .../vscode-test-resolver/src/extension.ts | 8 +- package.json | 8 +- remote/.yarnrc | 4 +- remote/package.json | 2 +- remote/yarn.lock | 28 +-- src/main.js | 4 +- test/automation/src/playwrightBrowser.ts | 3 +- test/integration/browser/src/index.ts | 4 +- yarn.lock | 198 ++++++++++++------ 35 files changed, 465 insertions(+), 322 deletions(-) diff --git a/.nvmrc b/.nvmrc index bc78e9f2695..48b14e6b2b5 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.12.1 +20.14.0 diff --git a/.yarnrc b/.yarnrc index b153fa4724f..a94956590fa 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,5 +1,5 @@ disturl "https://electronjs.org/headers" -target "29.4.0" -ms_build_id "9728852" +target "30.1.2" +ms_build_id "9759760" runtime "electron" build_from_source "true" diff --git a/build/azure-pipelines/alpine/cli-build-alpine.yml b/build/azure-pipelines/alpine/cli-build-alpine.yml index a6442dfe290..2c3b653ce7e 100644 --- a/build/azure-pipelines/alpine/cli-build-alpine.yml +++ b/build/azure-pipelines/alpine/cli-build-alpine.yml @@ -16,7 +16,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: # Install yarn as the ARM64 build agent is using vanilla Ubuntu diff --git a/build/azure-pipelines/darwin/cli-build-darwin.yml b/build/azure-pipelines/darwin/cli-build-darwin.yml index 1d8dffc464d..ad901c8de1d 100644 --- a/build/azure-pipelines/darwin/cli-build-darwin.yml +++ b/build/azure-pipelines/darwin/cli-build-darwin.yml @@ -16,7 +16,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../cli/cli-apply-patches.yml@self diff --git a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml index 80e90a52bac..e6cff11dc7d 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-cli-sign.yml @@ -9,7 +9,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - script: node build/setup-npm-registry.js $NPM_REGISTRY build condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) diff --git a/build/azure-pipelines/darwin/product-build-darwin-sign.yml b/build/azure-pipelines/darwin/product-build-darwin-sign.yml index fb8cf8341c3..e8f1bdf511c 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-sign.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-sign.yml @@ -3,7 +3,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - task: UseDotNet@2 inputs: diff --git a/build/azure-pipelines/darwin/product-build-darwin-universal.yml b/build/azure-pipelines/darwin/product-build-darwin-universal.yml index f8b201f40d4..45367dde187 100644 --- a/build/azure-pipelines/darwin/product-build-darwin-universal.yml +++ b/build/azure-pipelines/darwin/product-build-darwin-universal.yml @@ -3,7 +3,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - template: ../distro/download-distro.yml@self diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index 11f69d735ac..a49d5e4abd6 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -20,7 +20,7 @@ steps: inputs: versionSource: fromFile versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download + #nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - template: ../distro/download-distro.yml@self diff --git a/build/azure-pipelines/linux/setup-env.sh b/build/azure-pipelines/linux/setup-env.sh index 9bfbf9ab41a..6f3f0100867 100755 --- a/build/azure-pipelines/linux/setup-env.sh +++ b/build/azure-pipelines/linux/setup-env.sh @@ -13,7 +13,7 @@ SYSROOT_ARCH="$SYSROOT_ARCH" node -e '(async () => { const { getVSCodeSysroot } if [ "$npm_config_arch" == "x64" ]; then if [ "$(echo "$@" | grep -c -- "--only-remote")" -eq 0 ]; then # Download clang based on chromium revision used by vscode - curl -s https://raw.githubusercontent.com/chromium/chromium/122.0.6261.156/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux + curl -s https://raw.githubusercontent.com/chromium/chromium/124.0.6367.243/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux # Download libcxx headers and objects from upstream electron releases DEBUG=libcxx-fetcher \ @@ -25,9 +25,9 @@ if [ "$npm_config_arch" == "x64" ]; then # Set compiler toolchain # Flags for the client build are based on - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:build/config/arm.gni - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:build/config/compiler/BUILD.gn - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:build/config/c++/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/arm.gni + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/compiler/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:build/config/c++/BUILD.gn export CC="$PWD/.build/CR_Clang/bin/clang --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -I$PWD/.build/libcxx_headers -isystem$PWD/.build/libcxx_headers/include -isystem$PWD/.build/libcxxabi_headers/include -fPIC -flto=thin -fsplit-lto-unit -D_LIBCPP_ABI_NAMESPACE=Cr -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index a80aa1531f1..52950abd800 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -3d3d8bb185d7b63b0db910661fdd69d6381afb8c97742bbd2526a9c932e1f8ca *chromedriver-v29.4.0-darwin-arm64.zip -c3d075943d87604ffa50382cc8d5798485349544ca391cab88c892f889d3b14c *chromedriver-v29.4.0-darwin-x64.zip -6d62d2dba55e4419fa003d45f93dad1324ec29a4d3eb84fd9fd5fd7a64339389 *chromedriver-v29.4.0-linux-arm64.zip -81bb3d362331c7296f700b1b0e8f07c4c7739b1151f698cd56af927bedda59e7 *chromedriver-v29.4.0-linux-armv7l.zip -ab593cc39aefac8c5abd259e31f6add4b2b70c52231724a6c08ac1872b4a0edf *chromedriver-v29.4.0-linux-x64.zip -705d42ccc05b2c48b0673b9dcf63eb78772bb79dba078a523d384ed2481bc9c0 *chromedriver-v29.4.0-mas-arm64.zip -956a7caa28eeeb0c02eb7638a53215ffd89b4f12880f0893ff10f497ca1a8117 *chromedriver-v29.4.0-mas-x64.zip -1f070176aa33e0139d61a3d758fd2f015f09bb275577293fe93564749b6310ba *chromedriver-v29.4.0-win32-arm64.zip -38a71526d243bcb73c28cb648bd4816d70b5e643df52f9f86a83416014589744 *chromedriver-v29.4.0-win32-ia32.zip -f90750d3589cb3c9f6f0ebc70d5e025cf81c382e8c23fa47a54570696a478ef0 *chromedriver-v29.4.0-win32-x64.zip -05dffc90dd1341cc7a6b50127985e4e217fef7f50a173c7d0ff34039dd2d81b6 *electron-api.json -7f63f7cf675ba6dec3a5e4173d729bd53c75f81e612f809641d9d0c4d9791649 *electron-v29.4.0-darwin-arm64-dsym-snapshot.zip -aa29530fcafa4db364978d4f414a6ec2005ea695f7fee70ffbe5e114e9e453f0 *electron-v29.4.0-darwin-arm64-dsym.zip -8d12fb6d9bcdf5bbfc93dbcd1cac348735dc6f98aa450ee03ec7837a01a8a938 *electron-v29.4.0-darwin-arm64-symbols.zip -c16d05f1231bb3c77da05ab236b454b3a2b6a642403be51e7c9b16cd2c421a19 *electron-v29.4.0-darwin-arm64.zip -2dfc1017831ab2f6e9ddb575d3b9cff5a0d56f16a335a3c0df508e964e2db963 *electron-v29.4.0-darwin-x64-dsym-snapshot.zip -025de6aa39d98762928e1b700f46177e74be20101b27457659b938e2c69db326 *electron-v29.4.0-darwin-x64-dsym.zip -ec4eb0a618207233985ceaab297be34b3d4f0813d88801d5637295b238dd661a *electron-v29.4.0-darwin-x64-symbols.zip -8ed7924f77a5c43c137a57097c5c47c2e8e9a78197e18af11a767c98035c123e *electron-v29.4.0-darwin-x64.zip -bde1772fa8ac4850e108012a9edd3bd93472bad8f68ddd55fca355dad81dde4f *electron-v29.4.0-linux-arm64-debug.zip -dfe7852a7423196efb2205c788d942db3ffc9de6ce52577e173bcf7ca6973d48 *electron-v29.4.0-linux-arm64-symbols.zip -c3764d6c3799950e3418e8e5a5a5b2c41abe421dd8bcdebf054c7c85798d9860 *electron-v29.4.0-linux-arm64.zip -bde1772fa8ac4850e108012a9edd3bd93472bad8f68ddd55fca355dad81dde4f *electron-v29.4.0-linux-armv7l-debug.zip -360668ba669cb2c01c2f960cdee76c29670e6ce907ccc0718e971a04af594ce9 *electron-v29.4.0-linux-armv7l-symbols.zip -c5e92943ad78b4e41a32ae53c679e148ea2ae09f95f914b1834dbdbae578ba91 *electron-v29.4.0-linux-armv7l.zip -375be885426bcbd272bd068bfcef41a83296c2f8e61e633233d2a9e9a69242fc *electron-v29.4.0-linux-x64-debug.zip -847e0f75624616c2918b33de2eefeec63419bd250685610d3f52fa115527d2b9 *electron-v29.4.0-linux-x64-symbols.zip -91e5eb374c2c85a07c2d4e99a89eb18515ff0169a49c3fa75289800e1225729e *electron-v29.4.0-linux-x64.zip -098f973537c3d9679a69409d0b84bcc1a6113bb2002ee60068e2c22f335a3855 *electron-v29.4.0-mas-arm64-dsym-snapshot.zip -2724aa32eb441eea21680d95fc1efdd75ac473fa19623c7acf3d546419e96154 *electron-v29.4.0-mas-arm64-dsym.zip -98dd81914752a57da4cbaad1f0aa94b16335f9b8f997be9aa049be90b96b2886 *electron-v29.4.0-mas-arm64-symbols.zip -fd2663f65c1f995304e3eb65870b7146adfefef07cf82bf44de75855fd4f36e8 *electron-v29.4.0-mas-arm64.zip -237983b2169e69bb73aa0987e871e3e486755904b71ebe36c3e902377f92754a *electron-v29.4.0-mas-x64-dsym-snapshot.zip -a5d59599827d32ef322b99eee8416e39235f4c7a0ada78342a885665e0b732dd *electron-v29.4.0-mas-x64-dsym.zip -5182e7697ac0591e0b95c33f70316af24093c9100f442be2cee0039660e959ac *electron-v29.4.0-mas-x64-symbols.zip -e0ee7057aff0240a70b9ed75ff44d55aeae9af67fbc8915f741711a8bb6fe744 *electron-v29.4.0-mas-x64.zip -2802872dfc6de0f0e2e8cef9d2f4f384e3d82b20ad36fc981c4e725dd2f2abcd *electron-v29.4.0-win32-arm64-pdb.zip -d49c954dc25ae9e4c75e61af80b9718014c52f016f43a29071913f0e7100c7bd *electron-v29.4.0-win32-arm64-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v29.4.0-win32-arm64-toolchain-profile.zip -483d692efbe4fb1231ff63afb8a236b2e22b486fbe5ac6abbc8b208abf94a4d3 *electron-v29.4.0-win32-arm64.zip -98458f49ba67a08e473d475a68a2818d9df076a5246fbc9b45403e8796f9d35b *electron-v29.4.0-win32-ia32-pdb.zip -69d505d4ae59d9dddf83c4e530e45dd7c5bc64d6da90cf4f851e523be9e51014 *electron-v29.4.0-win32-ia32-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v29.4.0-win32-ia32-toolchain-profile.zip -d5a21a17a64e9638f49f057356af23b51f56bd6a7fea3c2e0a28ff3186a7bc41 *electron-v29.4.0-win32-ia32.zip -521ee7b3398c4dc395b43dac86cd099e86a6123de2b43636ee805b7da014ed3f *electron-v29.4.0-win32-x64-pdb.zip -e33848ebd6c6e4ce431aa367bef887050947a136e883677cfc524ca5cabc1e98 *electron-v29.4.0-win32-x64-symbols.zip -c9f31ae6408aa6936b5d683eda601773789185890375cd097e61e924d4fed77a *electron-v29.4.0-win32-x64-toolchain-profile.zip -e4ef85aa3608221f8a3e011c1b1c2d2d36093ad19bda12d16b3816929fb6c99b *electron-v29.4.0-win32-x64.zip -707ee08593289ee83514b4fc55123611309f995788f38a5ec03e285741aac1c8 *electron.d.ts -281b5f4a49de55fdb86b1662530f07f2ced1252c878eb7a941c88ede545339e0 *ffmpeg-v29.4.0-darwin-arm64.zip -0b735912df9b2ff3d03eb23942e03bc0116d82f1291d0a45cbde14177c2f3066 *ffmpeg-v29.4.0-darwin-x64.zip -4e2ba537d7c131abbd34168bce2c28cc9ef6262b217d5f4085afccfdf9635da6 *ffmpeg-v29.4.0-linux-arm64.zip -4aa56ad5d849f4e61af22678a179346b68aec9100282e1b8a43df25d95721677 *ffmpeg-v29.4.0-linux-armv7l.zip -0558e6e1f78229d303e16d4d8c290794baa9adc619fdd2ddccadb3ea241a1df4 *ffmpeg-v29.4.0-linux-x64.zip -224f15d8f96c75348cd7f1b85c4eab63468fae1e50ff4b1381e08011cf76e4f7 *ffmpeg-v29.4.0-mas-arm64.zip -175ec79f0dc4c5966d9a0ca6ec1674106340ecc64503585c12c2f854249af06f *ffmpeg-v29.4.0-mas-x64.zip -5fa13744b87fef1bfd24a37513677f446143e085504541f8ce97466803bd1893 *ffmpeg-v29.4.0-win32-arm64.zip -d7ba316bb7e13025c9db29e0acafebb540b7716c9f111e469733615d8521186a *ffmpeg-v29.4.0-win32-ia32.zip -35c70a28bcfd4f0b1f8c985d3d1348936bd60767d231ce28ba38f3daeeef64bb *ffmpeg-v29.4.0-win32-x64.zip -8c7228ea0ecab25a1f7fcd1ba9680684d19f9671a497113d71a851a53867b048 *hunspell_dictionaries.zip -7552547c8d585b9bc43518d239d7ce3ad7c5cad0346b07cdcfc1eab638b2b794 *libcxx-objects-v29.4.0-linux-arm64.zip -76054a779d4845ad752b625213ce8990f08dcc5b89aa20660dd4f2e817ba30a8 *libcxx-objects-v29.4.0-linux-armv7l.zip -761c317a9c874bd3d1118d0ecad33c4be23727f538cfbb42a08dd87c68da6039 *libcxx-objects-v29.4.0-linux-x64.zip -f98f9972cc30200b8e05815f5a9cd5cec04bdeee0e48ae2143cdaeff5db9d71d *libcxx_headers.zip -f0b0dd2be579baaf97901322ef489d03fae69a0b8524ea77b24fb3c896f73dd9 *libcxxabi_headers.zip -5da864ea23d70538298a40e0d037a5a461a6b74984e72fd4f0cd20904bccaed1 *mksnapshot-v29.4.0-darwin-arm64.zip -bde97bd7c69209ed6bf4cf1cdf7de622e3a9f50fe6b4dc4b5618eee868f47c62 *mksnapshot-v29.4.0-darwin-x64.zip -a3df9b9e6ef14efe5827d0256d8ecaebe6d8be130cfc3faac0dea76eb53b9b11 *mksnapshot-v29.4.0-linux-arm64-x64.zip -648b9dbca21194d663ddb706e6086a166e691263c764c80f836ae02c27e3657a *mksnapshot-v29.4.0-linux-armv7l-x64.zip -e7a4201cda3956380facc2b5b9d0b1020cc5e654fba44129fc7429a982411cc1 *mksnapshot-v29.4.0-linux-x64.zip -ffb44c45733675e0378f45fce25dafa95697d0c86179f8e46742ada16bc11aa1 *mksnapshot-v29.4.0-mas-arm64.zip -0242da3ca193206e56b88eb108502244bae35dcc587210bd0a32d9fa4cb71041 *mksnapshot-v29.4.0-mas-x64.zip -1445806dca6effbc60072bbde7997cefb62bdb7a9e295a090d26f27c3882685f *mksnapshot-v29.4.0-win32-arm64-x64.zip -09599adc3afb0a13ae87fc4b8ab97c729fe3689faa6a4f5f7a4a3cf0d9cc49d3 *mksnapshot-v29.4.0-win32-ia32.zip -84f80683d95665d29284386509bb104e840ff0b797bfbbd19da86b84d370aa49 *mksnapshot-v29.4.0-win32-x64.zip +cdf0522dacc5fdf75a9a4ca9a20f049793ef8bae2b04e37f02e8923fcecd4c76 *chromedriver-v30.1.2-darwin-arm64.zip +f739afd34c48aae18da1d9dffb2332f0c2b2e27ff2056ac619e0a8c9414618f0 *chromedriver-v30.1.2-darwin-x64.zip +c2a220a316c268984bb8135975f28adecc392cf5cd2244af8cc21d60018a6a10 *chromedriver-v30.1.2-linux-arm64.zip +fad358f076caff4eecc3d8b63cea2e109cc0ff8b4632bb4edd21a7c7721bc428 *chromedriver-v30.1.2-linux-armv7l.zip +addc230541e9ee44b311fba9e900c5cb7d8b4d64b79a6d5dae68a71ced1c4611 *chromedriver-v30.1.2-linux-x64.zip +e2d0876fac8af41e0dd9c1b14c5315b426c55217e54e2b4ca5e28faae1b19557 *chromedriver-v30.1.2-mas-arm64.zip +c705d0b74a4658c197a87ed1e9e2509e55186769376b40493ec68b7cbb36c312 *chromedriver-v30.1.2-mas-x64.zip +8bbf9ccb789b236dec3e871d2499c14926a4c2dd3c865f8c7316ba3aa5b3f58f *chromedriver-v30.1.2-win32-arm64.zip +75af4b07bbd3a8fd7e18d63eb936e11054d01b52d438e4f7b7c5e6b82a41dec3 *chromedriver-v30.1.2-win32-ia32.zip +2d306df2c66314be12df78b6139ebf2d616463efdf4017473330d87a61954c3a *chromedriver-v30.1.2-win32-x64.zip +1990fe83ca25b990773ce099b90fb804fc026c1398279731229ba37d02c23000 *electron-api.json +64360b0db764c1bf16a8ab810a25c03f68873691d2897b360281bc50a645b6bb *electron-v30.1.2-darwin-arm64-dsym-snapshot.zip +3192307419ee2bafc3c99c62d79dbd2e6cba5815ae245be994f0b1e1d7fedb46 *electron-v30.1.2-darwin-arm64-dsym.zip +823eadc46a498af7433dae2ace1c8f0b2b0c8cbf98204fed0fdce8110ddafbc1 *electron-v30.1.2-darwin-arm64-symbols.zip +3c651624b1605411e595a6e9f6e874effb947c80eda4b8d0bb7d2972ff6ff242 *electron-v30.1.2-darwin-arm64.zip +fa5cbbc3e7760907229fa0753c3faa2b43e09bcb987b6c3f693e0030aa65e62b *electron-v30.1.2-darwin-x64-dsym-snapshot.zip +d97087f4c7e41fd67b2f0f7aa623ccf1effdbb94133b883dbae1e4c42a576b03 *electron-v30.1.2-darwin-x64-dsym.zip +a129109ed6ca23f66d625ebff9e57be117bc0a32c4f4348e78c1ad7dd41c4189 *electron-v30.1.2-darwin-x64-symbols.zip +8645e10af9b047c765a6cc880f9fa53f266e618569eaf65c0ea9fa1058be20f7 *electron-v30.1.2-darwin-x64.zip +afa016399f57bbbb658238dd715ef2a66790602ff46514e9cd99f2e078789c7d *electron-v30.1.2-linux-arm64-debug.zip +aec05a3e46a83d7c3e502b04245d3d6d2db8d5789a4dcc991f7cf6e7e3cd7036 *electron-v30.1.2-linux-arm64-symbols.zip +953c51413abfd62efdba070f99c961202ce5ef5e77c5cee5eaaf097ac2f5bc9b *electron-v30.1.2-linux-arm64.zip +afa016399f57bbbb658238dd715ef2a66790602ff46514e9cd99f2e078789c7d *electron-v30.1.2-linux-armv7l-debug.zip +04a6851e218c9a6f70870a341beea1b194a94d2d78f3a283065a34654cce7e30 *electron-v30.1.2-linux-armv7l-symbols.zip +677d6b4e6721ffb27b0037628c235cd0a9f10104beabb2b6c67d4b0328a9d001 *electron-v30.1.2-linux-armv7l.zip +1b6d8926e2c7cacbb33e56259ebe908c52e2a6dbbe37f0def043121f228a3a37 *electron-v30.1.2-linux-x64-debug.zip +dc4b526b02ec028d20836ee4617335d0569170846959761c3e8dc615d668f596 *electron-v30.1.2-linux-x64-symbols.zip +724aeca4b2f428f544e9b7e5e52e2074458c2e198f588530819cd0318af8599d *electron-v30.1.2-linux-x64.zip +15b10a6cf6a1ea029792282088647abbf58b415fdb4dd004c2c67c4a8f216ef3 *electron-v30.1.2-mas-arm64-dsym-snapshot.zip +00dbfc36c8ff6ecfbb8b01b51fee41f876bf3456017a8cc694a0c56608061f5e *electron-v30.1.2-mas-arm64-dsym.zip +e402b68cad20ee60cce376dacf0e2e72f1fcd0b6359dda7789626dca4b101e8b *electron-v30.1.2-mas-arm64-symbols.zip +8e59fc7c6df96a029310d6f7769e0c76592dc746d31764b35460de129231d12b *electron-v30.1.2-mas-arm64.zip +e8bfe6b58d2767dd52a7668df380be9c786abed0b25d642bee70c278758d2e77 *electron-v30.1.2-mas-x64-dsym-snapshot.zip +418f32558f9a107ebc942666a6ca680874db8ed438ae3f0064255abe0f9ce77e *electron-v30.1.2-mas-x64-dsym.zip +6d05da37cb39c664a764c879216e762efdb66f97734e42bbaa8f115b11fd3c87 *electron-v30.1.2-mas-x64-symbols.zip +af7b85a28593227add7e595e01d570e19512b40a29599ec007ef7cd4b5a11435 *electron-v30.1.2-mas-x64.zip +580c9b9fee6bacfdc4d3a1118953ba0096bcc19d28b1d804d72d40c5caac8d81 *electron-v30.1.2-win32-arm64-pdb.zip +dbb0ce79933c3688b7fa2bf04ce083d6e8da0a8c07b5104f53805e2e92679cd4 *electron-v30.1.2-win32-arm64-symbols.zip +7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.1.2-win32-arm64-toolchain-profile.zip +1b9035b541999e0cedcfe3893bb3c1497435494279d599c4e9300fe204f4d560 *electron-v30.1.2-win32-arm64.zip +3cb6869a69d118488dc48ed573b3fc9445bea33cf0a04338ca1b8000b4eaf516 *electron-v30.1.2-win32-ia32-pdb.zip +3f353849ef21506d5ca7a2c26df84d7c744ef1795acd33307f501764dfbe9bc1 *electron-v30.1.2-win32-ia32-symbols.zip +7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.1.2-win32-ia32-toolchain-profile.zip +431aff46eca9ddb726af3c8d6f4bbb72158e5c872c1b8bee221b3df0a8e94947 *electron-v30.1.2-win32-ia32.zip +85428715d302d4c97e47ca0b6409497191846baccbc36debf895cde724f9445f *electron-v30.1.2-win32-x64-pdb.zip +a5156829bc0caab5c70e6cd352941b6fb7b1e396d7869298c1ceaa69d742e3dc *electron-v30.1.2-win32-x64-symbols.zip +7351fa5cb892853a7d4b67d8858d0f9cc6506c554a2e42c9ad7e8d5e29ae2743 *electron-v30.1.2-win32-x64-toolchain-profile.zip +d400ed1aca2b7b1093aee8b5a8544b112e9f81a570cf77f6bdfe019ceb003f7f *electron-v30.1.2-win32-x64.zip +3187cdd642b968e17a768a3fdaff44bedb69954be3d88e7ad55aac9787b70485 *electron.d.ts +4c8bd4215102f563cf0464ae4edd4022921c53e0ebd4bb6b6f7f7434d50081aa *ffmpeg-v30.1.2-darwin-arm64.zip +2b8c57755ccb64a64540433d3cfbfecd29d07e28bf23c5f08a2edd0e2333a645 *ffmpeg-v30.1.2-darwin-x64.zip +c6f69859dc2ca04daa7c218936e5bc044fc19582423db954c5e8664ce7f331df *ffmpeg-v30.1.2-linux-arm64.zip +12f760ce312e4ee98ddd9496600ca9a1468e28f0ed6d41c6b9284dec841f550f *ffmpeg-v30.1.2-linux-armv7l.zip +c7207cc057849033d550d6897a53f7abee455f31b8f571c3c57a01f2871ff5af *ffmpeg-v30.1.2-linux-x64.zip +447b19465677636261ce47cc256f221c660e66cd136b33c3c742b8d23481b924 *ffmpeg-v30.1.2-mas-arm64.zip +e44fcdb5d0e62d328c0bb5ebeed54f388b09591a33e4dc1698c421e4c9d881dc *ffmpeg-v30.1.2-mas-x64.zip +b470cc217c4f06b3fe4edf6695b4762e7c926d07c1f41c3cab0ddb1d71ce8ce4 *ffmpeg-v30.1.2-win32-arm64.zip +92f1743c16210a77d07e9553cf96ccfb4e6985cc50ee831b5b075589aa0ebb05 *ffmpeg-v30.1.2-win32-ia32.zip +b2fa79b739023651f0551ca06ac5da7b47acaaf8b09ccdedf7081fc3ea824a80 *ffmpeg-v30.1.2-win32-x64.zip +b2b562a45ae4a2d40bf039ed1c707a7875b9e893fc8c6a0044d536b0f9968629 *hunspell_dictionaries.zip +08da936356d1321eec550c30a9208750773d86e3be0b7fe4baf36c72a8609c20 *libcxx-objects-v30.1.2-linux-arm64.zip +8aa302ac17f6ac44f756cb9219f18d01d267d43f9af2dfd8d4626e9d01e584fa *libcxx-objects-v30.1.2-linux-armv7l.zip +1ab04d6cec407930a2051761e6114cb2cd6418e2103d32e835246d06c696b427 *libcxx-objects-v30.1.2-linux-x64.zip +4db17e017bdb818cca5d8e08a78fe54fb907c3cf05defd1b8f086b413075357a *libcxx_headers.zip +209f20bd3ee59fa7c85b0789d8e45168583407c9fb5bd2eba446c1e0796c4a7b *libcxxabi_headers.zip +4dcc59b7c66b9ccc881dbdfeba9de707f693fd839a8e764d34bb66dec7e3e63c *mksnapshot-v30.1.2-darwin-arm64.zip +582886552cbc227eb71f5047d00ac62baeb1912a52a8f5132953b94f54d41dd4 *mksnapshot-v30.1.2-darwin-x64.zip +639fde4957353d48be54b9075c0894b7529210f0bb3a2f9cad81ba2deb415080 *mksnapshot-v30.1.2-linux-arm64-x64.zip +1fec2ae49a9f03117a5e5b637866e5aa270da2fa3a007d0314c8a39ed392230f *mksnapshot-v30.1.2-linux-armv7l-x64.zip +494f86a178a2b14101c6deccf7c2ae88c690c7ce8445b63854a2e0525d69aaa0 *mksnapshot-v30.1.2-linux-x64.zip +bfbb90138b4df3f57fd9fe9cc05b6b8f9b3bf099b66d65215cc9434e2272b0d1 *mksnapshot-v30.1.2-mas-arm64.zip +d1a6a825628a141fdd73cf208180cc20af32b8e06aeb7f4d36566358cef40a90 *mksnapshot-v30.1.2-mas-x64.zip +fd523788a380990d589f9461f29a8878b63090146aa124f2debf3197af69929b *mksnapshot-v30.1.2-win32-arm64-x64.zip +69068d132cd511e33be9aff6dda5df74079c2003657cd89107d4eaaac7e4d997 *mksnapshot-v30.1.2-win32-ia32.zip +69bfab7461f83a256c0869e2781cca4f5a53f8b6a60d78617b79b8bfc0b554b6 *mksnapshot-v30.1.2-win32-x64.zip diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt index bcc9340406d..877d8afe243 100644 --- a/build/checksums/nodejs.txt +++ b/build/checksums/nodejs.txt @@ -1,7 +1,7 @@ -e0065c61f340e85106a99c4b54746c5cee09d59b08c5712f67f99e92aa44995d node-v20.11.1-darwin-arm64.tar.gz -c52e7fb0709dbe63a4cbe08ac8af3479188692937a7bd8e776e0eedfa33bb848 node-v20.11.1-darwin-x64.tar.gz -e34ab2fc2726b4abd896bcbff0250e9b2da737cbd9d24267518a802ed0606f3b node-v20.11.1-linux-arm64.tar.gz -e42791f76ece283c7a4b97fbf716da72c5128c54a9779f10f03ae74a4bcfb8f6 node-v20.11.1-linux-armv7l.tar.gz -bf3a779bef19452da90fb88358ec2c57e0d2f882839b20dc6afc297b6aafc0d7 node-v20.11.1-linux-x64.tar.gz -a5a9d30a8f7d56e00ccb27c1a7d24c8d0bc96a2689ebba8eb7527698793496f1 win-arm64/node.exe -bc585910690318aaebe3c57669cb83ca9d1e5791efd63195e238f54686e6c2ec win-x64/node.exe +d2148d79e9ff04d2982d00faeae942ceba488ca327a91065e528235167b9e9d6 node-v20.14.0-darwin-arm64.tar.gz +1dcc18a199cb5f46d43ed1c3c61b87a247d1a1a11dd6b32a36a9c46ac1088f86 node-v20.14.0-darwin-x64.tar.gz +d63e83fca4f81801396620c46a42892a2ef26e21a4508f68de373e61a12bd9c5 node-v20.14.0-linux-arm64.tar.gz +af45ea0d09e55a4f05c0190636532bdf9f70b2eaf0a1c4d7594207cf21284df0 node-v20.14.0-linux-armv7l.tar.gz +5b9bf40cfc7c21de14a1b4c367650e3c96eb101156bf9368ffc2f947414b6581 node-v20.14.0-linux-x64.tar.gz +a6ec02119098cf92592539e06289953c4365be20ab15d4ad264669f931000b0c win-arm64/node.exe +8f45741ec6ba07be8d199c0cebc838a58c0430c9228dbe50f8ac5d4859e58bae win-x64/node.exe diff --git a/build/darwin/create-universal-app.js b/build/darwin/create-universal-app.js index 85d27273861..a3daf1878b0 100644 --- a/build/darwin/create-universal-app.js +++ b/build/darwin/create-universal-app.js @@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true }); const path = require("path"); const fs = require("fs"); +const minimatch = require("minimatch"); const vscode_universal_bundler_1 = require("vscode-universal-bundler"); const cross_spawn_promise_1 = require("@malept/cross-spawn-promise"); const root = path.dirname(path.dirname(__dirname)); @@ -18,25 +19,29 @@ async function main(buildDir) { const appName = product.nameLong + '.app'; const x64AppPath = path.join(buildDir, 'VSCode-darwin-x64', appName); const arm64AppPath = path.join(buildDir, 'VSCode-darwin-arm64', appName); - const x64AsarPath = path.join(x64AppPath, 'Contents', 'Resources', 'app', 'node_modules.asar'); - const arm64AsarPath = path.join(arm64AppPath, 'Contents', 'Resources', 'app', 'node_modules.asar'); + const asarRelativePath = path.join('Contents', 'Resources', 'app', 'node_modules.asar'); const outAppPath = path.join(buildDir, `VSCode-darwin-${arch}`, appName); const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json'); + const filesToSkip = [ + '**/CodeResources', + '**/Credits.rtf', + ]; await (0, vscode_universal_bundler_1.makeUniversalApp)({ x64AppPath, arm64AppPath, - x64AsarPath, - arm64AsarPath, - filesToSkip: [ - 'Credits.rtf', - 'CodeResources', - 'fsevents.node', - 'Info.plist', // TODO@deepak1556: regressed with 11.4.2 internal builds - 'MainMenu.nib', // Generated sequence is not deterministic with Xcode 13 - '.npmrc' - ], + asarPath: asarRelativePath, outAppPath, - force: true + force: true, + mergeASARs: true, + x64ArchFiles: '*/kerberos.node', + filesToSkipComparison: (file) => { + for (const expected of filesToSkip) { + if (minimatch(file, expected)) { + return true; + } + } + return false; + } }); const productJson = JSON.parse(fs.readFileSync(productJsonPath, 'utf8')); Object.assign(productJson, { diff --git a/build/darwin/create-universal-app.ts b/build/darwin/create-universal-app.ts index 04eb3a11e20..94b8a23b9e5 100644 --- a/build/darwin/create-universal-app.ts +++ b/build/darwin/create-universal-app.ts @@ -5,6 +5,7 @@ import * as path from 'path'; import * as fs from 'fs'; +import * as minimatch from 'minimatch'; import { makeUniversalApp } from 'vscode-universal-bundler'; import { spawn } from '@malept/cross-spawn-promise'; @@ -21,26 +22,31 @@ async function main(buildDir?: string) { const appName = product.nameLong + '.app'; const x64AppPath = path.join(buildDir, 'VSCode-darwin-x64', appName); const arm64AppPath = path.join(buildDir, 'VSCode-darwin-arm64', appName); - const x64AsarPath = path.join(x64AppPath, 'Contents', 'Resources', 'app', 'node_modules.asar'); - const arm64AsarPath = path.join(arm64AppPath, 'Contents', 'Resources', 'app', 'node_modules.asar'); + const asarRelativePath = path.join('Contents', 'Resources', 'app', 'node_modules.asar'); const outAppPath = path.join(buildDir, `VSCode-darwin-${arch}`, appName); const productJsonPath = path.resolve(outAppPath, 'Contents', 'Resources', 'app', 'product.json'); + const filesToSkip = [ + '**/CodeResources', + '**/Credits.rtf', + ]; + await makeUniversalApp({ x64AppPath, arm64AppPath, - x64AsarPath, - arm64AsarPath, - filesToSkip: [ - 'Credits.rtf', - 'CodeResources', - 'fsevents.node', - 'Info.plist', // TODO@deepak1556: regressed with 11.4.2 internal builds - 'MainMenu.nib', // Generated sequence is not deterministic with Xcode 13 - '.npmrc' - ], + asarPath: asarRelativePath, outAppPath, - force: true + force: true, + mergeASARs: true, + x64ArchFiles: '*/kerberos.node', + filesToSkipComparison: (file: string) => { + for (const expected of filesToSkip) { + if (minimatch(file, expected)) { + return true; + } + } + return false; + } }); const productJson = JSON.parse(fs.readFileSync(productJsonPath, 'utf8')); diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index d7a814b9a1b..7d588611473 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -127,21 +127,7 @@ function getNodeVersion() { return { nodeVersion, internalNodeVersion }; } -function getNodeChecksum(nodeVersion, platform, arch, glibcPrefix) { - let expectedName; - switch (platform) { - case 'win32': - expectedName = product.nodejsRepository !== 'https://nodejs.org' ? - `win-${arch}-node.exe` : `win-${arch}/node.exe`; - break; - - case 'darwin': - case 'alpine': - case 'linux': - expectedName = `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`; - break; - } - +function getNodeChecksum(expectedName) { const nodeJsChecksums = fs.readFileSync(path.join(REPO_ROOT, 'build', 'checksums', 'nodejs.txt'), 'utf8'); for (const line of nodeJsChecksums.split('\n')) { const [checksum, name] = line.split(/\s+/); @@ -196,7 +182,24 @@ function nodejs(platform, arch) { log(`Downloading node.js ${nodeVersion} ${platform} ${arch} from ${product.nodejsRepository}...`); const glibcPrefix = process.env['VSCODE_NODE_GLIBC'] ?? ''; - const checksumSha256 = getNodeChecksum(nodeVersion, platform, arch, glibcPrefix); + let expectedName; + switch (platform) { + case 'win32': + expectedName = product.nodejsRepository !== 'https://nodejs.org' ? + `win-${arch}-node.exe` : `win-${arch}/node.exe`; + break; + + case 'darwin': + expectedName = `node-v${nodeVersion}-${platform}-${arch}.tar.gz`; + break; + case 'linux': + expectedName = `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`; + break; + case 'alpine': + expectedName = `node-v${nodeVersion}-linux-${arch}-musl.tar.gz`; + break; + } + const checksumSha256 = getNodeChecksum(expectedName); if (checksumSha256) { log(`Using SHA256 checksum for checking integrity: ${checksumSha256}`); @@ -207,13 +210,13 @@ function nodejs(platform, arch) { switch (platform) { case 'win32': return (product.nodejsRepository !== 'https://nodejs.org' ? - fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `win-${arch}-node.exe`, checksumSha256 }) : + fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 }) : fetchUrls(`/dist/v${nodeVersion}/win-${arch}/node.exe`, { base: 'https://nodejs.org', checksumSha256 })) .pipe(rename('node.exe')); case 'darwin': case 'linux': return (product.nodejsRepository !== 'https://nodejs.org' ? - fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}${glibcPrefix}-${platform}-${arch}.tar.gz`, checksumSha256 }) : + fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 }) : fetchUrls(`/dist/v${nodeVersion}/node-v${nodeVersion}-${platform}-${arch}.tar.gz`, { base: 'https://nodejs.org', checksumSha256 }) ).pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) .pipe(filter('**/node')) @@ -221,7 +224,7 @@ function nodejs(platform, arch) { .pipe(rename('node')); case 'alpine': return product.nodejsRepository !== 'https://nodejs.org' ? - fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: `node-v${nodeVersion}-${platform}-${arch}.tar.gz`, checksumSha256 }) + fetchGithub(product.nodejsRepository, { version: `${nodeVersion}-${internalNodeVersion}`, name: expectedName, checksumSha256 }) .pipe(flatmap(stream => stream.pipe(gunzip()).pipe(untar()))) .pipe(filter('**/node')) .pipe(util.setExecutableBit('**')) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index b3b35466af0..4af406751f9 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -292,6 +292,8 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op '**/node-pty/lib/shared/conout.js', '**/*.wasm', '**/@vscode/vsce-sign/bin/*', + ], [ + '**/*.mk', ], 'node_modules.asar')); let all = es.merge( diff --git a/build/lib/asar.js b/build/lib/asar.js index 31845f2f2dd..07b39bf79ff 100644 --- a/build/lib/asar.js +++ b/build/lib/asar.js @@ -11,7 +11,7 @@ const pickle = require('chromium-pickle-js'); const Filesystem = require('asar/lib/filesystem'); const VinylFile = require("vinyl"); const minimatch = require("minimatch"); -function createAsar(folderPath, unpackGlobs, destFilename) { +function createAsar(folderPath, unpackGlobs, skipGlobs, destFilename) { const shouldUnpackFile = (file) => { for (let i = 0; i < unpackGlobs.length; i++) { if (minimatch(file.relative, unpackGlobs[i])) { @@ -20,6 +20,14 @@ function createAsar(folderPath, unpackGlobs, destFilename) { } return false; }; + const shouldSkipFile = (file) => { + for (const skipGlob of skipGlobs) { + if (minimatch(file.relative, skipGlob)) { + return true; + } + } + return false; + }; const filesystem = new Filesystem(folderPath); const out = []; // Keep track of pending inserts @@ -64,6 +72,9 @@ function createAsar(folderPath, unpackGlobs, destFilename) { if (!file.stat.isFile()) { throw new Error(`unknown item in stream!`); } + if (shouldSkipFile(file)) { + return; + } const shouldUnpack = shouldUnpackFile(file); insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack); if (shouldUnpack) { diff --git a/build/lib/asar.ts b/build/lib/asar.ts index 44a6416bdfb..7dc1dd3b2e6 100644 --- a/build/lib/asar.ts +++ b/build/lib/asar.ts @@ -17,7 +17,7 @@ declare class AsarFilesystem { insertFile(path: string, shouldUnpack: boolean, file: { stat: { size: number; mode: number } }, options: {}): Promise; } -export function createAsar(folderPath: string, unpackGlobs: string[], destFilename: string): NodeJS.ReadWriteStream { +export function createAsar(folderPath: string, unpackGlobs: string[], skipGlobs: string[], destFilename: string): NodeJS.ReadWriteStream { const shouldUnpackFile = (file: VinylFile): boolean => { for (let i = 0; i < unpackGlobs.length; i++) { @@ -28,6 +28,15 @@ export function createAsar(folderPath: string, unpackGlobs: string[], destFilena return false; }; + const shouldSkipFile = (file: VinylFile): boolean => { + for (const skipGlob of skipGlobs) { + if (minimatch(file.relative, skipGlob)) { + return true; + } + } + return false; + }; + const filesystem = new Filesystem(folderPath); const out: Buffer[] = []; @@ -78,6 +87,9 @@ export function createAsar(folderPath: string, unpackGlobs: string[], destFilena if (!file.stat.isFile()) { throw new Error(`unknown item in stream!`); } + if (shouldSkipFile(file)) { + return; + } const shouldUnpack = shouldUnpackFile(file); insertFile(file.relative, { size: file.contents.length, mode: file.stat.mode }, shouldUnpack); diff --git a/build/linux/debian/dep-lists.js b/build/linux/debian/dep-lists.js index d843c090063..3a642a72725 100644 --- a/build/linux/debian/dep-lists.js +++ b/build/linux/debian/dep-lists.js @@ -31,6 +31,7 @@ exports.referenceGeneratedDepsByArch = { 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', 'libc6 (>= 2.2.5)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', @@ -67,6 +68,7 @@ exports.referenceGeneratedDepsByArch = { 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libc6 (>= 2.4)', 'libc6 (>= 2.9)', @@ -108,6 +110,7 @@ exports.referenceGeneratedDepsByArch = { 'libatk1.0-0 (>= 2.2.0)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.17)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', diff --git a/build/linux/debian/dep-lists.ts b/build/linux/debian/dep-lists.ts index 4028370cd02..86d1de12216 100644 --- a/build/linux/debian/dep-lists.ts +++ b/build/linux/debian/dep-lists.ts @@ -31,6 +31,7 @@ export const referenceGeneratedDepsByArch = { 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', 'libc6 (>= 2.2.5)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', @@ -67,6 +68,7 @@ export const referenceGeneratedDepsByArch = { 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.16)', 'libc6 (>= 2.17)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libc6 (>= 2.4)', 'libc6 (>= 2.9)', @@ -108,6 +110,7 @@ export const referenceGeneratedDepsByArch = { 'libatk1.0-0 (>= 2.2.0)', 'libatspi2.0-0 (>= 2.9.90)', 'libc6 (>= 2.17)', + 'libc6 (>= 2.25)', 'libc6 (>= 2.28)', 'libcairo2 (>= 1.6.0)', 'libcurl3-gnutls | libcurl3-nss | libcurl4 | libcurl3', diff --git a/build/linux/dependencies-generator.js b/build/linux/dependencies-generator.js index bff0c9a25df..19adbeb0529 100644 --- a/build/linux/dependencies-generator.js +++ b/build/linux/dependencies-generator.js @@ -23,7 +23,7 @@ const product = require("../../product.json"); // The reference dependencies, which one has to update when the new dependencies // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/dependencies-generator.ts b/build/linux/dependencies-generator.ts index 226310e1258..5fe4ac5da64 100644 --- a/build/linux/dependencies-generator.ts +++ b/build/linux/dependencies-generator.ts @@ -25,7 +25,7 @@ import product = require('../../product.json'); // are valid, are in dep-lists.ts const FAIL_BUILD_FOR_NEW_DEPENDENCIES: boolean = true; -// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/122.0.6261.156:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/124.0.6367.243:chrome/installer/linux/BUILD.gn;l=64-80 // and the Linux Archive build // Shared library dependencies that we already bundle. const bundledDeps = [ diff --git a/build/linux/rpm/dep-lists.js b/build/linux/rpm/dep-lists.js index 8be477290bb..97984514511 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -45,12 +45,14 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.2.5)(64bit)', + 'libc.so.6(GLIBC_2.25)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libc.so.6(GLIBC_2.3)(64bit)', 'libc.so.6(GLIBC_2.3.2)(64bit)', 'libc.so.6(GLIBC_2.3.3)(64bit)', 'libc.so.6(GLIBC_2.3.4)(64bit)', 'libc.so.6(GLIBC_2.4)(64bit)', + 'libc.so.6(GLIBC_2.5)(64bit)', 'libc.so.6(GLIBC_2.6)(64bit)', 'libc.so.6(GLIBC_2.7)(64bit)', 'libc.so.6(GLIBC_2.8)(64bit)', @@ -140,8 +142,10 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.16)', 'libc.so.6(GLIBC_2.17)', 'libc.so.6(GLIBC_2.18)', + 'libc.so.6(GLIBC_2.25)', 'libc.so.6(GLIBC_2.28)', 'libc.so.6(GLIBC_2.4)', + 'libc.so.6(GLIBC_2.5)', 'libc.so.6(GLIBC_2.6)', 'libc.so.6(GLIBC_2.7)', 'libc.so.6(GLIBC_2.8)', @@ -241,6 +245,7 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6()(64bit)', 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', + 'libc.so.6(GLIBC_2.25)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index 24b18d504c8..b79812784bf 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -44,12 +44,14 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.2.5)(64bit)', + 'libc.so.6(GLIBC_2.25)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libc.so.6(GLIBC_2.3)(64bit)', 'libc.so.6(GLIBC_2.3.2)(64bit)', 'libc.so.6(GLIBC_2.3.3)(64bit)', 'libc.so.6(GLIBC_2.3.4)(64bit)', 'libc.so.6(GLIBC_2.4)(64bit)', + 'libc.so.6(GLIBC_2.5)(64bit)', 'libc.so.6(GLIBC_2.6)(64bit)', 'libc.so.6(GLIBC_2.7)(64bit)', 'libc.so.6(GLIBC_2.8)(64bit)', @@ -139,8 +141,10 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.16)', 'libc.so.6(GLIBC_2.17)', 'libc.so.6(GLIBC_2.18)', + 'libc.so.6(GLIBC_2.25)', 'libc.so.6(GLIBC_2.28)', 'libc.so.6(GLIBC_2.4)', + 'libc.so.6(GLIBC_2.5)', 'libc.so.6(GLIBC_2.6)', 'libc.so.6(GLIBC_2.7)', 'libc.so.6(GLIBC_2.8)', @@ -240,6 +244,7 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6()(64bit)', 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', + 'libc.so.6(GLIBC_2.25)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', diff --git a/build/package.json b/build/package.json index 0bbeed3f136..9bff15d4691 100644 --- a/build/package.json +++ b/build/package.json @@ -51,7 +51,7 @@ "ternary-stream": "^3.0.0", "through2": "^4.0.2", "tmp": "^0.2.1", - "vscode-universal-bundler": "^0.0.2", + "vscode-universal-bundler": "^0.1.0", "workerpool": "^6.4.0", "yauzl": "^2.10.0" }, diff --git a/build/yarn.lock b/build/yarn.lock index d99ceffaadf..74200931c51 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -199,6 +199,15 @@ events "^3.0.0" tslib "^2.2.0" +"@electron/asar@^3.2.7": + version "3.2.10" + resolved "https://registry.yarnpkg.com/@electron/asar/-/asar-3.2.10.tgz#615cf346b734b23cafa4e0603551010bd0e50aa8" + integrity sha512-mvBSwIBUeiRscrCeJE1LwctAriBj65eUDm0Pc11iE5gRwzkmsdbS7FnZ1XUWjpSeQWL1L5g12Fc/SchPM9DUOw== + dependencies: + commander "^5.0.0" + glob "^7.1.6" + minimatch "^3.0.4" + "@electron/get@^2.0.0": version "2.0.3" resolved "https://registry.yarnpkg.com/@electron/get/-/get-2.0.3.tgz#fba552683d387aebd9f3fcadbcafc8e12ee4f960" @@ -329,10 +338,10 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.20.0.tgz#2efddf82828aac85e64cef62482af61c29561bee" integrity sha512-NgJnesu1RtWihtTtXGFMU5YSE6JyyHPMxCwBZK7a6/8d31GuSo9l0Ss7w1Jw5QnKUawG6UEehs883kcXf5fYwg== -"@malept/cross-spawn-promise@^1.1.0": - version "1.1.1" - resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz#504af200af6b98e198bce768bc1730c6936ae01d" - integrity sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ== +"@malept/cross-spawn-promise@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz#d0772de1aa680a0bfb9ba2f32b4c828c7857cb9d" + integrity sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg== dependencies: cross-spawn "^7.0.1" @@ -686,6 +695,11 @@ optionalDependencies: keytar "^7.7.0" +"@xmldom/xmldom@^0.8.8": + version "0.8.10" + resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz#a1337ca426aa61cef9fe15b5b28e340a72f6fa99" + integrity sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw== + agent-base@6: version "6.0.2" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77" @@ -755,18 +769,6 @@ arr-union@^3.1.0: resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= -asar@^3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/asar/-/asar-3.0.3.tgz#1fef03c2d6d2de0cbad138788e4f7ae03b129c7b" - integrity sha512-k7zd+KoR+n8pl71PvgElcoKHrVNiSXtw7odKbyNpmgKe7EGRF9Pnu3uLOukD37EvavKwVFxOUpqXTIZC5B5Pmw== - dependencies: - chromium-pickle-js "^0.2.0" - commander "^5.0.0" - glob "^7.1.6" - minimatch "^3.0.4" - optionalDependencies: - "@types/glob" "^7.1.1" - assign-symbols@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" @@ -787,11 +789,6 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha1-x57Zf380y48robyXkLzDZkdLS3k= -at-least-node@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2" - integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg== - azure-devops-node-api@^11.0.1: version "11.2.0" resolved "https://registry.yarnpkg.com/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz#bf04edbef60313117a0507415eed4790a420ad6b" @@ -847,6 +844,13 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + braces@~3.0.2: version "3.0.3" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789" @@ -877,11 +881,6 @@ buffer-equal-constant-time@1.0.1: resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= -buffer-equal@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" - integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= - buffer-fill@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" @@ -995,11 +994,6 @@ chownr@^1.1.1: resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" integrity sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg== -chromium-pickle-js@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz#04a106672c18b085ab774d983dfa3ea138f22205" - integrity sha1-BKEGZywYsIWrd02YPfo+oTjyIgU= - clone-buffer@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" @@ -1048,11 +1042,6 @@ color-support@^1.1.3: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -colors@1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" - integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= - colors@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" @@ -1065,13 +1054,6 @@ combined-stream@^1.0.8: dependencies: delayed-stream "~1.0.0" -commander@2.9.0: - version "2.9.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" - integrity sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= - dependencies: - graceful-readlink ">= 1.0.0" - commander@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" @@ -1185,15 +1167,13 @@ detect-node@^2.0.4: resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1" integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g== -dir-compare@^2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/dir-compare/-/dir-compare-2.4.0.tgz#785c41dc5f645b34343a4eafc50b79bac7f11631" - integrity sha512-l9hmu8x/rjVC9Z2zmGzkhOEowZvW7pmYws5CWHutg8u1JgvsKWMx7Q/UODeu4djLZ4FgW5besw5yvMQnBHzuCA== +dir-compare@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/dir-compare/-/dir-compare-4.2.0.tgz#d1d4999c14fbf55281071fdae4293b3b9ce86f19" + integrity sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ== dependencies: - buffer-equal "1.0.0" - colors "1.0.3" - commander "2.9.0" - minimatch "3.0.4" + minimatch "^3.0.5" + p-limit "^3.1.0 " dom-serializer@^2.0.0: version "2.0.0" @@ -1413,6 +1393,15 @@ fs-constants@^1.0.0: resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad" integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow== +fs-extra@^11.1.1: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-extra@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" @@ -1422,16 +1411,6 @@ fs-extra@^8.1.0: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^9.0.1: - version "9.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d" - integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ== - dependencies: - at-least-node "^1.0.0" - graceful-fs "^4.2.0" - jsonfile "^6.0.1" - universalify "^2.0.0" - fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1550,11 +1529,6 @@ graceful-fs@^4.1.6, graceful-fs@^4.2.0: resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.8.tgz#e412b8d33f5e006593cbd3cee6df9f2cebbe802a" integrity sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg== -"graceful-readlink@>= 1.0.0": - version "1.0.1" - resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" - integrity sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= - gulp-merge-json@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/gulp-merge-json/-/gulp-merge-json-2.1.1.tgz#cfb1d066467577545b8c1c289a278e6ef4b4e0de" @@ -1926,19 +1900,26 @@ mimic-response@^3.1.0: resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-3.1.0.tgz#2d1d59af9c1b129815accc2c46a022a5ce1fa3c9" integrity sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ== -minimatch@3.0.4, minimatch@^3.0.4: +minimatch@^3.0.3, minimatch@^3.0.5, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" integrity sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA== dependencies: brace-expansion "^1.1.7" -minimatch@^3.0.3, minimatch@^3.1.1: - version "3.1.2" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" - integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== +minimatch@^9.0.3: + version "9.0.5" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.5.tgz#d74f9dd6b57d83d8e98cfb82133b03978bc929e5" + integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow== dependencies: - brace-expansion "^1.1.7" + brace-expansion "^2.0.1" minimist@^1.2.0, minimist@^1.2.3: version "1.2.6" @@ -2062,6 +2043,13 @@ p-cancelable@^2.0.0: resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-2.1.1.tgz#aab7fbd416582fa32a3db49859c122487c5ed2cf" integrity sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg== +"p-limit@^3.1.0 ": + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + parse-node-version@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" @@ -2127,6 +2115,15 @@ plist@^3.0.1: base64-js "^1.5.1" xmlbuilder "^9.0.7" +plist@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/plist/-/plist-3.1.0.tgz#797a516a93e62f5bde55e0b9cc9c967f860893c9" + integrity sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ== + dependencies: + "@xmldom/xmldom" "^0.8.8" + base64-js "^1.5.1" + xmlbuilder "^15.1.1" + plugin-error@1.0.1, plugin-error@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/plugin-error/-/plugin-error-1.0.1.tgz#77016bd8919d0ac377fdcdd0322328953ca5781c" @@ -2678,16 +2675,18 @@ vscode-gulp-watch@^5.0.3: vinyl "^2.2.0" vinyl-file "^3.0.0" -vscode-universal-bundler@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/vscode-universal-bundler/-/vscode-universal-bundler-0.0.2.tgz#2c988dac681d3ffe6baec6defac0995cb833c55a" - integrity sha512-FPJcvKnQGBqFzy6M6Nm2yvAczNLUeXsfYM6GwCex/pUOkvIM2icIHmiSvtMJINlLW1iG+oEwE3/LVbABmcjEmQ== +vscode-universal-bundler@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/vscode-universal-bundler/-/vscode-universal-bundler-0.1.0.tgz#1a03d1d16c6ea5065318fafbc2a554b7c2f3bd32" + integrity sha512-wtT9IZ/fqIZSirY6cxElu8a6WpNaOjgQjjazt85lMCWBuF/tWVw5nRYX67pTVsUyi6kzQaIvfyyIvxVbBpetBA== dependencies: - "@malept/cross-spawn-promise" "^1.1.0" - asar "^3.0.3" + "@electron/asar" "^3.2.7" + "@malept/cross-spawn-promise" "^2.0.0" debug "^4.3.1" - dir-compare "^2.4.0" - fs-extra "^9.0.1" + dir-compare "^4.2.0" + fs-extra "^11.1.1" + minimatch "^9.0.3" + plist "^3.1.0" webidl-conversions@^3.0.0: version "3.0.1" @@ -2727,6 +2726,11 @@ xml2js@^0.4.19, xml2js@^0.4.23: sax ">=0.6.0" xmlbuilder "~11.0.0" +xmlbuilder@^15.1.1: + version "15.1.1" + resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-15.1.1.tgz#9dcdce49eea66d8d10b42cae94a79c3c8d0c2ec5" + integrity sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg== + xmlbuilder@^9.0.7: version "9.0.7" resolved "https://registry.yarnpkg.com/xmlbuilder/-/xmlbuilder-9.0.7.tgz#132ee63d2ec5565c557e20f4c22df9aca686b10d" @@ -2756,3 +2760,8 @@ yazl@^2.2.2: integrity sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw== dependencies: buffer-crc32 "~0.2.3" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/cgmanifest.json b/cgmanifest.json index 61747342eef..aa06f8323b0 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "f1a45d7ded05d64ca8136cc142ddc0c271b1dd43" + "commitHash": "7fa4f6e14e0707c0e604cf7c1da33566e78169ce" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "122.0.6261.156" + "version": "124.0.6367.243" }, { "component": { @@ -48,7 +48,7 @@ "git": { "name": "ffmpeg", "repositoryUrl": "https://chromium.googlesource.com/chromium/third_party/ffmpeg", - "commitHash": "17525de887d54b970ffdd421a0879c1db1952307" + "commitHash": "52d8ef3799b2f16b66351dd0972bb0bcee1648ac" } }, "isOnlyProductionDependency": true, @@ -516,11 +516,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "9b1bf44ea9e7785e38c93b7d22d32dbca262df6c" + "commitHash": "fe0f08a5dd68fd72b1652adaa51ab07a4b09f847" } }, "isOnlyProductionDependency": true, - "version": "20.11.1" + "version": "20.14.0" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "f9ed0eaee4b172733872c2f84e5061882dd08e5c" + "commitHash": "91de7d0f13208891c5604e00ccd18e4f6826653b" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "29.4.0" + "version": "30.1.2" }, { "component": { diff --git a/extensions/vscode-test-resolver/src/extension.ts b/extensions/vscode-test-resolver/src/extension.ts index 8e12e622e05..2fab3ec306a 100644 --- a/extensions/vscode-test-resolver/src/extension.ts +++ b/extensions/vscode-test-resolver/src/extension.ts @@ -164,8 +164,8 @@ export function activate(context: vscode.ExtensionContext) { const serverCommandPath = path.join(vscodePath, 'scripts', serverCommand); outputChannel.appendLine(`Launching server: "${serverCommandPath}" ${commandArgs.join(' ')}`); - - extHostProcess = cp.spawn(serverCommandPath, commandArgs, { env, cwd: vscodePath }); + const shell = (process.platform === 'win32'); + extHostProcess = cp.spawn(serverCommandPath, commandArgs, { env, cwd: vscodePath, shell }); } else { const extensionToInstall = process.env['TESTRESOLVER_INSTALL_BUILTIN_EXTENSION']; if (extensionToInstall) { @@ -182,8 +182,8 @@ export function activate(context: vscode.ExtensionContext) { outputChannel.appendLine(`Using server build at ${serverLocation}`); outputChannel.appendLine(`Server arguments ${commandArgs.join(' ')}`); - - extHostProcess = cp.spawn(path.join(serverLocation, 'bin', serverCommand), commandArgs, { env, cwd: serverLocation }); + const shell = (process.platform === 'win32'); + extHostProcess = cp.spawn(path.join(serverLocation, 'bin', serverCommand), commandArgs, { env, cwd: serverLocation, shell }); } extHostProcess.stdout!.on('data', (data: Buffer) => processOutput(data.toString())); extHostProcess.stderr!.on('data', (data: Buffer) => processOutput(data.toString())); diff --git a/package.json b/package.json index b99927cdeef..cc00db38c76 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", "version": "1.92.0", - "distro": "6e11724ed97152dcd4510f2520755c983a6f439c", + "distro": "abf8b974fc98ae45b7729d258b0459182c617887", "author": { "name": "Microsoft Corporation" }, @@ -93,7 +93,7 @@ "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.3", - "kerberos": "^2.0.1", + "kerberos": "2.1.1-alpha.0", "minimist": "^1.2.6", "native-is-elevated": "0.7.0", "native-keymap": "^3.3.5", @@ -136,7 +136,7 @@ "@vscode/l10n-dev": "0.0.35", "@vscode/telemetry-extractor": "^1.10.2", "@vscode/test-cli": "^0.0.6", - "@vscode/test-electron": "^2.3.8", + "@vscode/test-electron": "^2.4.0", "@vscode/test-web": "^0.0.56", "@vscode/v8-heap-parser": "^0.1.0", "@vscode/vscode-perf": "^0.0.14", @@ -149,7 +149,7 @@ "cssnano": "^6.0.3", "debounce": "^1.0.0", "deemon": "^1.8.0", - "electron": "29.4.0", + "electron": "30.1.2", "eslint": "8.36.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-jsdoc": "^46.5.0", diff --git a/remote/.yarnrc b/remote/.yarnrc index 4c99388e889..8d07643c18e 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,5 +1,5 @@ disturl "https://nodejs.org/dist" -target "20.11.1" -ms_build_id "275039" +target "20.14.0" +ms_build_id "282653" runtime "node" build_from_source "true" diff --git a/remote/package.json b/remote/package.json index bb615b88ea4..c4558964774 100644 --- a/remote/package.json +++ b/remote/package.json @@ -26,7 +26,7 @@ "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.3", - "kerberos": "^2.0.1", + "kerberos": "2.1.1-alpha.0", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", "node-pty": "1.1.0-beta11", diff --git a/remote/yarn.lock b/remote/yarn.lock index 0f65b688c17..7ca95a92629 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -388,14 +388,14 @@ jsonfile@^6.0.1: optionalDependencies: graceful-fs "^4.1.6" -kerberos@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.0.1.tgz#663b0b46883b4da84495f60f2e9e399a43a33ef5" - integrity sha512-O/jIgbdGK566eUhFwIcgalbqirYU/r76MW7/UFw06Fd9x5bSwgyZWL/Vm26aAmezQww/G9KYkmmJBkEkPk5HLw== +kerberos@2.1.1-alpha.0: + version "2.1.1-alpha.0" + resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.1.1-alpha.0.tgz#c6d377b43c8206340fd184167754f2c81dad5ab1" + integrity sha512-II8N/ky/Vpd8y7LTxwCuAYoQ8XeV3HYxuK7IDmyoFacIhDljx4sdt/+sOwqgXEweQyCHlbZSKSaK82upqNM4Hw== dependencies: bindings "^1.5.0" - node-addon-api "^4.3.0" - prebuild-install "7.1.1" + node-addon-api "^6.1.0" + prebuild-install "^7.1.2" lru-cache@^6.0.0: version "6.0.0" @@ -469,10 +469,10 @@ node-addon-api@^3.2.1: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== -node-addon-api@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" - integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== node-gyp-build@4.8.1, node-gyp-build@^4.3.0: version "4.8.1" @@ -503,10 +503,10 @@ picomatch@^2.3.1: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== -prebuild-install@7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" - integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== +prebuild-install@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" + integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== dependencies: detect-libc "^2.0.0" expand-template "^2.0.3" diff --git a/src/main.js b/src/main.js index be7cd98afaf..7f909f7d633 100644 --- a/src/main.js +++ b/src/main.js @@ -298,10 +298,8 @@ function configureCommandlineSwitchesSync(cliArgs) { app.commandLine.appendSwitch('disable-features', featuresToDisable); // Blink features to configure. - // `FontMatchingCTMigration` - Siwtch font matching on macOS to CoreText (Refs https://github.com/microsoft/vscode/issues/214390). - // TODO(deepak1556): Enable this feature again after updating to Electron 30. const blinkFeaturesToDisable = - `FontMatchingCTMigration,${app.commandLine.getSwitchValue('disable-blink-features')}`; + `${app.commandLine.getSwitchValue('disable-blink-features')}`; app.commandLine.appendSwitch('disable-blink-features', blinkFeaturesToDisable); // Support JS Flags diff --git a/test/automation/src/playwrightBrowser.ts b/test/automation/src/playwrightBrowser.ts index 7223c49a13b..0a98250767b 100644 --- a/test/automation/src/playwrightBrowser.ts +++ b/test/automation/src/playwrightBrowser.ts @@ -72,10 +72,11 @@ async function launchServer(options: LaunchOptions) { logger.log(`Storing log files into '${serverLogsPath}'`); logger.log(`Command line: '${serverLocation}' ${args.join(' ')}`); + const shell: boolean = (process.platform === 'win32'); const serverProcess = spawn( serverLocation, args, - { env } + { env, shell } ); logger.log(`Started server for browser smoke tests (pid: ${serverProcess.pid})`); diff --git a/test/integration/browser/src/index.ts b/test/integration/browser/src/index.ts index 990b7cd19fd..2613f10da62 100644 --- a/test/integration/browser/src/index.ts +++ b/test/integration/browser/src/index.ts @@ -192,11 +192,11 @@ async function launchServer(browserType: BrowserType): Promise<{ endpoint: url.U serverArgs.push('--logsPath', serverLogsPath); const stdio: cp.StdioOptions = args.debug ? 'pipe' : ['ignore', 'pipe', 'ignore']; - + const shell: boolean = (process.platform === 'win32'); const serverProcess = cp.spawn( serverLocation, serverArgs, - { env, stdio } + { env, stdio, shell } ); if (args.debug) { diff --git a/yarn.lock b/yarn.lock index 17a69acb6fb..f31bd180a3b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1029,11 +1029,6 @@ dependencies: defer-to-connect "^2.0.0" -"@tootallnate/once@1": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" - integrity sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw== - "@tootallnate/once@^3.0.0": version "3.0.0" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-3.0.0.tgz#d52238c9052d746c9689523e650160e70786bc9a" @@ -1639,15 +1634,16 @@ supports-color "^9.4.0" yargs "^17.7.2" -"@vscode/test-electron@^2.3.8": - version "2.3.8" - resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.3.8.tgz#06a7c50b38cfac0ede833905e088d55c61cd12d3" - integrity sha512-b4aZZsBKtMGdDljAsOPObnAi7+VWIaYl3ylCz1jTs+oV6BZ4TNHcVNC3xUn0azPeszBmwSBDQYfFESIaUQnrOg== +"@vscode/test-electron@^2.4.0": + version "2.4.0" + resolved "https://registry.yarnpkg.com/@vscode/test-electron/-/test-electron-2.4.0.tgz#6fcdbac10948960c15f8970cf5d5e624dd51a524" + integrity sha512-yojuDFEjohx6Jb+x949JRNtSn6Wk2FAh4MldLE3ck9cfvCqzwxF32QsNy1T9Oe4oT+ZfFcg0uPUCajJzOmPlTA== dependencies: - http-proxy-agent "^4.0.1" - https-proxy-agent "^5.0.0" + http-proxy-agent "^7.0.2" + https-proxy-agent "^7.0.4" jszip "^3.10.1" - semver "^7.5.2" + ora "^7.0.1" + semver "^7.6.2" "@vscode/test-web@^0.0.56": version "0.0.56" @@ -2086,13 +2082,6 @@ acorn@^8.8.2: resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== -agent-base@6: - version "6.0.1" - resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.1.tgz#808007e4e5867decb0ab6ab2f928fbdb5a596db4" - integrity sha512-01q25QQDwLSsyfhrKbn8yuur+JNw0H+0Y4JiGIKd3z9aYk/w/2kxD/Upc+t2ZBBSUNff50VjPsSW2YxM8QYKVg== - dependencies: - debug "4" - agent-base@^7.0.1, agent-base@^7.0.2, agent-base@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-7.1.0.tgz#536802b76bc0b34aa50195eb2442276d613e3434" @@ -2605,6 +2594,15 @@ bl@^4.0.2, bl@^4.0.3: inherits "^2.0.4" readable-stream "^3.4.0" +bl@^5.0.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-5.1.0.tgz#183715f678c7188ecef9fe475d90209400624273" + integrity sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ== + dependencies: + buffer "^6.0.3" + inherits "^2.0.4" + readable-stream "^3.4.0" + block-stream@*: version "0.0.9" resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" @@ -2736,6 +2734,14 @@ buffer@^5.5.0: base64-js "^1.3.1" ieee754 "^1.1.13" +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.2.1" + builtin-modules@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/builtin-modules/-/builtin-modules-3.3.0.tgz#cae62812b89801e9656336e46223e030386be7b6" @@ -2906,6 +2912,11 @@ chalk@^4.x: ansi-styles "^4.1.0" supports-color "^7.1.0" +chalk@^5.0.0, chalk@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-5.3.0.tgz#67c20a7ebef70e7f3970a01f90fa210cb6860385" + integrity sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w== + chardet@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" @@ -3002,6 +3013,18 @@ cli-cursor@^2.1.0: dependencies: restore-cursor "^2.0.0" +cli-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-4.0.0.tgz#3cecfe3734bf4fe02a8361cbdc0f6fe28c6a57ea" + integrity sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg== + dependencies: + restore-cursor "^4.0.0" + +cli-spinners@^2.9.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + cli-width@^2.0.0: version "2.2.1" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-2.2.1.tgz#b0433d0b4e9c847ef18868a4ef16fd5fc8271c48" @@ -3894,15 +3917,20 @@ electron-to-chromium@^1.4.668: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.717.tgz#99db370cae8cd090d5b01f8748e9ad369924d0f8" integrity sha512-6Fmg8QkkumNOwuZ/5mIbMU9WI3H2fmn5ajcVya64I5Yr5CcNmO7vcLt0Y7c96DCiMO5/9G+4sI2r6eEvdg1F7A== -electron@29.4.0: - version "29.4.0" - resolved "https://registry.yarnpkg.com/electron/-/electron-29.4.0.tgz#5dcd5a977414337a2518619e9166c0e86a5a3bae" - integrity sha512-4DTO8U66oiI8rShrDSu2zDPW6GWRiCebyb1MHSfQkLWCNI/PnLyGKeqYPUoVgc0FWaNN2sCBn8NKJHb++hE2LQ== +electron@30.1.2: + version "30.1.2" + resolved "https://registry.yarnpkg.com/electron/-/electron-30.1.2.tgz#9c8b9b0d0e3f07783d8c5dbd9519b3ffd11f1551" + integrity sha512-A5CFGwbA+HSXnzwjc8fP2GIezBcAb0uN/VbNGLOW8DHOYn07rvJ/1bAJECHUUzt5zbfohveG3hpMQiYpbktuDw== dependencies: "@electron/get" "^2.0.0" "@types/node" "^20.9.0" extract-zip "^2.0.1" +emoji-regex@^10.2.1: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + emoji-regex@^7.0.1: version "7.0.3" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156" @@ -5588,15 +5616,6 @@ http-errors@~1.6.2: setprototypeof "1.1.0" statuses ">= 1.4.0 < 2" -http-proxy-agent@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz#8a8c8ef7f5932ccf953c296ca8291b95aa74aa3a" - integrity sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg== - dependencies: - "@tootallnate/once" "1" - agent-base "6" - debug "4" - http-proxy-agent@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz#e9096c5afd071a3fce56e6252bb321583c124673" @@ -5621,14 +5640,6 @@ http2-wrapper@^1.0.0-beta.5.2: quick-lru "^5.1.1" resolve-alpn "^1.0.0" -https-proxy-agent@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6" - integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA== - dependencies: - agent-base "6" - debug "4" - https-proxy-agent@^7.0.0: version "7.0.4" resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-7.0.4.tgz#8e97b841a029ad8ddc8731f26595bad868cb4168" @@ -5680,7 +5691,7 @@ icss-utils@^5.0.0, icss-utils@^5.1.0: resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae" integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA== -ieee754@^1.1.13: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -6010,6 +6021,11 @@ is-gzip@^1.0.0: resolved "https://registry.yarnpkg.com/is-gzip/-/is-gzip-1.0.0.tgz#6ca8b07b99c77998025900e555ced8ed80879a83" integrity sha512-rcfALRIb1YewtnksfRIHGcIY93QnK8BIQ/2c9yDYcG/Y6+vRoJuTWBmmSEbyLLYtXm7q35pHOHbZFQBaLrhlWQ== +is-interactive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-2.0.0.tgz#40c57614593826da1100ade6059778d597f16e90" + integrity sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ== + is-negated-glob@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" @@ -6114,6 +6130,11 @@ is-unicode-supported@^0.1.0: resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== +is-unicode-supported@^1.1.0, is-unicode-supported@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz#d824984b616c292a2e198207d4a609983842f714" + integrity sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ== + is-utf8@^0.2.0, is-utf8@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" @@ -6423,14 +6444,14 @@ just-extend@^4.0.2: resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== -kerberos@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.0.1.tgz#663b0b46883b4da84495f60f2e9e399a43a33ef5" - integrity sha512-O/jIgbdGK566eUhFwIcgalbqirYU/r76MW7/UFw06Fd9x5bSwgyZWL/Vm26aAmezQww/G9KYkmmJBkEkPk5HLw== +kerberos@2.1.1-alpha.0: + version "2.1.1-alpha.0" + resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-2.1.1-alpha.0.tgz#c6d377b43c8206340fd184167754f2c81dad5ab1" + integrity sha512-II8N/ky/Vpd8y7LTxwCuAYoQ8XeV3HYxuK7IDmyoFacIhDljx4sdt/+sOwqgXEweQyCHlbZSKSaK82upqNM4Hw== dependencies: bindings "^1.5.0" - node-addon-api "^4.3.0" - prebuild-install "7.1.1" + node-addon-api "^6.1.0" + prebuild-install "^7.1.2" keygrip@~1.1.0: version "1.1.0" @@ -6749,6 +6770,14 @@ log-symbols@4.1.0: chalk "^4.1.0" is-unicode-supported "^0.1.0" +log-symbols@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-5.1.0.tgz#a20e3b9a5f53fac6aeb8e2bb22c07cf2c8f16d93" + integrity sha512-l0x2DvrW294C9uDCoQe1VSU4gf529FkSZ6leBl4TiqZH/e+0R7hSfHQBNut2mNygDgHwvYHfFLn6Oxb3VWj2rA== + dependencies: + chalk "^5.0.0" + is-unicode-supported "^1.1.0" + lowercase-keys@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-2.0.0.tgz#2603e78b7b4b0006cbca2fbcc8a3202558ac9479" @@ -7056,7 +7085,7 @@ mimic-fn@^1.0.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022" integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ== -mimic-fn@^2.0.0: +mimic-fn@^2.0.0, mimic-fn@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== @@ -7388,16 +7417,16 @@ node-addon-api@^4.2.0: resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.2.0.tgz#117cbb5a959dff0992e1c586ae0393573e4d2a87" integrity sha512-eazsqzwG2lskuzBqCGPi7Ac2UgOoMz8JVOXVhTvvPDYhthvNpefx8jWD8Np7Gv+2Sz0FlPWZk0nJV0z598Wn8Q== -node-addon-api@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-4.3.0.tgz#52a1a0b475193e0928e98e0426a0d1254782b77f" - integrity sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ== - node-addon-api@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.0.0.tgz#cfb3574e6df708ff71a30db6c4762d9e06e11c27" integrity sha512-GyHvgPvUXBvAkXa0YvYnhilSB1A+FRYMpIVggKzPZqdaZfevZOuzfWzyvgzOwRLHBeo/MMswmJFsrNF4Nw1pmA== +node-addon-api@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-6.1.0.tgz#ac8470034e58e67d0c6f1204a18ae6995d9c0d76" + integrity sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA== + node-fetch@2.6.8: version "2.6.8" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.8.tgz#a68d30b162bc1d8fd71a367e81b997e1f4d4937e" @@ -7640,6 +7669,13 @@ onetime@^2.0.0: dependencies: mimic-fn "^1.0.0" +onetime@^5.1.0: + version "5.1.2" + resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" + integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== + dependencies: + mimic-fn "^2.1.0" + only@~0.0.2: version "0.0.2" resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" @@ -7685,6 +7721,21 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" +ora@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-7.0.1.tgz#cdd530ecd865fe39e451a0e7697865669cb11930" + integrity sha512-0TUxTiFJWv+JnjWm4o9yvuskpEJLXTcng8MJuKd+SzAzp2o+OP3HWqNhB4OdJRt1Vsd9/mR0oyaEYlOnL7XIRw== + dependencies: + chalk "^5.3.0" + cli-cursor "^4.0.0" + cli-spinners "^2.9.0" + is-interactive "^2.0.0" + is-unicode-supported "^1.3.0" + log-symbols "^5.1.0" + stdin-discarder "^0.1.0" + string-width "^6.1.0" + strip-ansi "^7.1.0" + ordered-read-streams@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" @@ -8410,10 +8461,10 @@ postcss@^8.4.33: picocolors "^1.0.0" source-map-js "^1.2.0" -prebuild-install@7.1.1: - version "7.1.1" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45" - integrity sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw== +prebuild-install@^7.1.2: + version "7.1.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.2.tgz#a5fd9986f5a6251fbc47e1e5c65de71e68c0a056" + integrity sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ== dependencies: detect-libc "^2.0.0" expand-template "^2.0.3" @@ -8879,6 +8930,14 @@ restore-cursor@^2.0.0: onetime "^2.0.0" signal-exit "^3.0.2" +restore-cursor@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-4.0.0.tgz#519560a4318975096def6e609d44100edaa4ccb9" + integrity sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + ret@~0.1.10: version "0.1.15" resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" @@ -9030,7 +9089,7 @@ semver@^6.0.0, semver@^6.2.0, semver@^6.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.2, semver@^7.5.4: +semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semver@^7.3.7, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -9044,6 +9103,11 @@ semver@^7.5.3: dependencies: lru-cache "^6.0.0" +semver@^7.6.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + serialize-error@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/serialize-error/-/serialize-error-7.0.1.tgz#f1360b0447f61ffb483ec4157c737fab7d778e18" @@ -9422,6 +9486,13 @@ statuses@2.0.1: resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= +stdin-discarder@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/stdin-discarder/-/stdin-discarder-0.1.0.tgz#22b3e400393a8e28ebf53f9958f3880622efde21" + integrity sha512-xhV7w8S+bUwlPTb4bAOUQhv8/cSS5offJuX8GQGq32ONF0ZtDWKfkdomM3HMRA+LhX6um/FZ0COqlwsjD53LeQ== + dependencies: + bl "^5.0.0" + stream-combiner@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/stream-combiner/-/stream-combiner-0.2.2.tgz#aec8cbac177b56b6f4fa479ced8c1912cee52858" @@ -9546,6 +9617,15 @@ string-width@^5.0.1, string-width@^5.1.2: emoji-regex "^9.2.2" strip-ansi "^7.0.1" +string-width@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-6.1.0.tgz#96488d6ed23f9ad5d82d13522af9e4c4c3fd7518" + integrity sha512-k01swCJAgQmuADB0YIc+7TuatfNvTBVOoaUWJjTB9R4VJzR5vNWzf5t42ESVZFPS8xTySF7CAdV4t/aaIm3UnQ== + dependencies: + eastasianwidth "^0.2.0" + emoji-regex "^10.2.1" + strip-ansi "^7.0.1" + string.prototype.padend@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/string.prototype.padend/-/string.prototype.padend-3.1.1.tgz#824c84265dbac46cade2b957b38b6a5d8d1683c5" @@ -9625,7 +9705,7 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1: dependencies: ansi-regex "^5.0.1" -strip-ansi@^7.0.1: +strip-ansi@^7.0.1, strip-ansi@^7.1.0: version "7.1.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45" integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ== From 16654e61265ca89e53612bb7a61f89f4e99aeb3c Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 9 Jul 2024 08:08:56 +0200 Subject: [PATCH 0356/2222] esm - reduce diff to branch (#221154) --- scripts/code-server.js | 1 - src/bootstrap-amd.js | 152 +++++++++++---- src/bootstrap-fork.js | 15 +- src/bootstrap-meta.js | 16 +- src/bootstrap-node.js | 74 ++++--- src/bootstrap-window.js | 184 +++++++++++++----- src/bootstrap.js | 24 ++- src/cli.js | 29 ++- src/main.js | 42 ++-- src/server-cli.js | 20 +- src/server-main.js | 46 +++-- src/tsec.exemptions.json | 3 + src/typings/vscode-globals-product.d.ts | 11 ++ src/vs/base/common/jsonc.js | 12 +- src/vs/base/common/performance.js | 9 + src/vs/base/common/semver/semver.d.ts | 7 +- src/vs/base/common/semver/semver.js | 45 +++++ src/vs/base/node/nls.js | 14 ++ src/vs/base/node/unc.js | 12 ++ .../base/parts/sandbox/common/sandboxTypes.ts | 5 + src/vs/base/parts/storage/node/storage.ts | 8 +- .../processExplorer/processExplorer.js | 3 +- .../electron-sandbox/workbench/workbench.js | 3 +- .../platform/environment/node/userDataPath.js | 16 +- .../node/nativeModules.integrationTest.ts | 5 + src/vs/platform/request/node/proxy.ts | 20 +- src/vs/platform/terminal/node/ptyService.ts | 9 +- src/vs/server/node/server.cli.ts | 39 ++-- .../api/node/extHostExtensionService.ts | 6 +- src/vs/workbench/api/node/proxyResolver.ts | 11 ++ .../issue/electron-sandbox/issueReporter.js | 3 +- 31 files changed, 648 insertions(+), 196 deletions(-) diff --git a/scripts/code-server.js b/scripts/code-server.js index c043bf2671b..56945e76ca7 100644 --- a/scripts/code-server.js +++ b/scripts/code-server.js @@ -69,4 +69,3 @@ function startServer(programArgs) { } main(); - diff --git a/src/bootstrap-amd.js b/src/bootstrap-amd.js index 27d15eb76d5..f8a0c00be95 100644 --- a/src/bootstrap-amd.js +++ b/src/bootstrap-amd.js @@ -7,10 +7,28 @@ 'use strict'; /** - * @typedef {import('./vs/nls').INLSConfiguration} INLSConfiguration + * @import { INLSConfiguration } from './vs/nls' * @import { IProductConfiguration } from './vs/base/common/product' */ +// ESM-comment-begin +const isESM = false; +// ESM-comment-end +// ESM-uncomment-begin +// import * as path from 'path'; +// import * as fs from 'fs'; +// import { fileURLToPath } from 'url'; +// import { createRequire } from 'node:module'; +// import { product, pkg } from './bootstrap-meta.js'; +// import * as bootstrap from './bootstrap.js'; +// import * as performance from './vs/base/common/performance.js'; +// +// const require = createRequire(import.meta.url); +// const isESM = true; +// const module = { exports: {} }; +// const __dirname = path.dirname(fileURLToPath(import.meta.url)); +// ESM-uncomment-end + // Store the node.js require function in a variable // before loading our AMD loader to avoid issues // when this file is bundled with other files. @@ -21,7 +39,12 @@ globalThis._VSCODE_NODE_MODULES = new Proxy(Object.create(null), { get: (_target // VSCODE_GLOBALS: package/product.json /** @type Partial */ +// ESM-comment-begin globalThis._VSCODE_PRODUCT_JSON = require('./bootstrap-meta').product; +// ESM-comment-end +// ESM-uncomment-begin +// globalThis._VSCODE_PRODUCT_JSON = { ...product }; +// ESM-uncomment-end if (process.env['VSCODE_DEV']) { // Patch product overrides when running out of sources try { @@ -30,29 +53,21 @@ if (process.env['VSCODE_DEV']) { globalThis._VSCODE_PRODUCT_JSON = Object.assign(globalThis._VSCODE_PRODUCT_JSON, overrides); } catch (error) { /* ignore */ } } +// ESM-comment-begin globalThis._VSCODE_PACKAGE_JSON = require('./bootstrap-meta').pkg; +// ESM-comment-end +// ESM-uncomment-begin +// globalThis._VSCODE_PACKAGE_JSON = { ...pkg }; +// ESM-uncomment-end + +// VSCODE_GLOBALS: file root of all resources +globalThis._VSCODE_FILE_ROOT = __dirname; -// @ts-ignore -const loader = require('./vs/loader'); +// ESM-comment-begin const bootstrap = require('./bootstrap'); -const performance = require('./vs/base/common/performance'); +const performance = require(`./vs/base/common/performance`); const fs = require('fs'); - -// Bootstrap: Loader -loader.config({ - baseUrl: bootstrap.fileUriFromPath(__dirname, { isWindows: process.platform === 'win32' }), - catchError: true, - nodeRequire, - amdModulesPattern: /^vs\//, - recordStats: true -}); - -// Running in Electron -if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { - loader.define('fs', ['original-fs'], function (/** @type {import('fs')} */originalFS) { - return originalFS; // replace the patched electron fs with the original node fs for all AMD code - }); -} +// ESM-comment-end //#region NLS helpers @@ -138,31 +153,82 @@ async function doSetupNLS() { //#endregion -/** - * @param {string=} entrypoint - * @param {(value: any) => void=} onLoad - * @param {(err: Error) => void=} onError - */ -exports.load = function (entrypoint, onLoad, onError) { - if (!entrypoint) { - return; - } +//#region Loader Config - // code cache config - if (process.env['VSCODE_CODE_CACHE_PATH']) { - loader.config({ - nodeCachedData: { - path: process.env['VSCODE_CODE_CACHE_PATH'], - seed: entrypoint - } +if (isESM) { + + /** + * @param {string=} entrypoint + * @param {(value: any) => void} [onLoad] + * @param {(err: Error) => void} [onError] + */ + module.exports.load = function (entrypoint, onLoad, onError) { + if (!entrypoint) { + return; + } + + entrypoint = `./${entrypoint}.js`; + + onLoad = onLoad || function () { }; + onError = onError || function (err) { console.error(err); }; + + setupNLS().then(() => { + performance.mark(`code/fork/willLoadCode`); + import(entrypoint).then(onLoad, onError); + }); + }; +} else { + + // @ts-ignore + const loader = require('./vs/loader'); + + loader.config({ + baseUrl: bootstrap.fileUriFromPath(__dirname, { isWindows: process.platform === 'win32' }), + catchError: true, + nodeRequire, + amdModulesPattern: /^vs\//, + recordStats: true + }); + + // Running in Electron + if (process.env['ELECTRON_RUN_AS_NODE'] || process.versions['electron']) { + loader.define('fs', ['original-fs'], function (/** @type {import('fs')} */originalFS) { + return originalFS; // replace the patched electron fs with the original node fs for all AMD code }); } - onLoad = onLoad || function () { }; - onError = onError || function (err) { console.error(err); }; + /** + * @param {string=} entrypoint + * @param {(value: any) => void} [onLoad] + * @param {(err: Error) => void} [onError] + */ + module.exports.load = function (entrypoint, onLoad, onError) { + if (!entrypoint) { + return; + } - setupNLS().then(() => { - performance.mark('code/fork/willLoadCode'); - loader([entrypoint], onLoad, onError); - }); -}; + // code cache config + if (process.env['VSCODE_CODE_CACHE_PATH']) { + loader.config({ + nodeCachedData: { + path: process.env['VSCODE_CODE_CACHE_PATH'], + seed: entrypoint + } + }); + } + + onLoad = onLoad || function () { }; + onError = onError || function (err) { console.error(err); }; + + setupNLS().then(() => { + performance.mark('code/fork/willLoadCode'); + loader([entrypoint], onLoad, onError); + }); + }; +} + +//#endregion + +// ESM-uncomment-begin +// export const load = module.exports.load; +// ESM-uncomment-end diff --git a/src/bootstrap-fork.js b/src/bootstrap-fork.js index 9de1e6f0d15..a95cbf53589 100644 --- a/src/bootstrap-fork.js +++ b/src/bootstrap-fork.js @@ -6,11 +6,20 @@ //@ts-check 'use strict'; +// ESM-comment-begin const performance = require('./vs/base/common/performance'); -performance.mark('code/fork/start'); - const bootstrap = require('./bootstrap'); const bootstrapNode = require('./bootstrap-node'); +const bootstrapAmd = require('./bootstrap-amd'); +// ESM-comment-end +// ESM-uncomment-begin +// import * as performance from './vs/base/common/performance.js'; +// import * as bootstrap from './bootstrap.js'; +// import * as bootstrapNode from './bootstrap-node.js'; +// import * as bootstrapAmd from './bootstrap-amd.js'; +// ESM-uncomment-end + +performance.mark('code/fork/start'); // Crash reporter configureCrashReporter(); @@ -41,7 +50,7 @@ if (process.env['VSCODE_PARENT_PID']) { } // Load AMD entry point -require('./bootstrap-amd').load(process.env['VSCODE_AMD_ENTRYPOINT']); +bootstrapAmd.load(process.env['VSCODE_AMD_ENTRYPOINT']); //#region Helpers diff --git a/src/bootstrap-meta.js b/src/bootstrap-meta.js index 7924b77eec8..46041d5d91a 100644 --- a/src/bootstrap-meta.js +++ b/src/bootstrap-meta.js @@ -10,6 +10,13 @@ * @import { IProductConfiguration } from './vs/base/common/product' */ +// ESM-uncomment-begin +// import { createRequire } from 'node:module'; +// +// const require = createRequire(import.meta.url); +// const module = { exports: {} }; +// ESM-uncomment-end + /** @type Partial & { BUILD_INSERT_PRODUCT_CONFIGURATION?: string } */ let product = { BUILD_INSERT_PRODUCT_CONFIGURATION: 'BUILD_INSERT_PRODUCT_CONFIGURATION' }; // DO NOT MODIFY, PATCHED DURING BUILD if (product['BUILD_INSERT_PRODUCT_CONFIGURATION']) { @@ -24,5 +31,10 @@ if (pkg['BUILD_INSERT_PACKAGE_CONFIGURATION']) { pkg = require('../package.json'); // Running out of sources } -exports.product = product; -exports.pkg = pkg; +module.exports.product = product; +module.exports.pkg = pkg; + +// ESM-uncomment-begin +// export const product = module.exports.product; +// export const pkg = module.exports.pkg; +// ESM-uncomment-end diff --git a/src/bootstrap-node.js b/src/bootstrap-node.js index 914b8290380..4a829557f0d 100644 --- a/src/bootstrap-node.js +++ b/src/bootstrap-node.js @@ -6,12 +6,28 @@ //@ts-check 'use strict'; +// ESM-comment-begin +const path = require('path'); +const fs = require('fs'); + +const isESM = false; +// ESM-comment-end +// ESM-uncomment-begin +// import * as path from 'path'; +// import * as fs from 'fs'; +// import { fileURLToPath } from 'url'; +// import { createRequire } from 'node:module'; +// +// const require = createRequire(import.meta.url); +// const isESM = true; +// const module = { exports: {} }; +// const __dirname = path.dirname(fileURLToPath(import.meta.url)); +// ESM-uncomment-end + // Setup current working directory in all our node & electron processes // - Windows: call `process.chdir()` to always set application folder as cwd // - all OS: store the `process.cwd()` inside `VSCODE_CWD` for consistent lookups function setupCurrentWorkingDirectory() { - const path = require('path'); - try { // Store the `process.cwd()` inside `VSCODE_CWD` @@ -38,36 +54,41 @@ setupCurrentWorkingDirectory(); * * @param {string} injectPath */ -exports.injectNodeModuleLookupPath = function (injectPath) { +module.exports.injectNodeModuleLookupPath = function (injectPath) { if (!injectPath) { throw new Error('Missing injectPath'); } - const Module = require('module'); - const path = require('path'); - - const nodeModulesPath = path.join(__dirname, '../node_modules'); + const Module = require('node:module'); + if (isESM) { + // register a loader hook + // ESM-uncomment-begin + // Module.register('./server-loader.mjs', { parentURL: import.meta.url, data: injectPath }); + // ESM-uncomment-end + } else { + const nodeModulesPath = path.join(__dirname, '../node_modules'); - // @ts-ignore - const originalResolveLookupPaths = Module._resolveLookupPaths; + // @ts-ignore + const originalResolveLookupPaths = Module._resolveLookupPaths; - // @ts-ignore - Module._resolveLookupPaths = function (moduleName, parent) { - const paths = originalResolveLookupPaths(moduleName, parent); - if (Array.isArray(paths)) { - for (let i = 0, len = paths.length; i < len; i++) { - if (paths[i] === nodeModulesPath) { - paths.splice(i, 0, injectPath); - break; + // @ts-ignore + Module._resolveLookupPaths = function (moduleName, parent) { + const paths = originalResolveLookupPaths(moduleName, parent); + if (Array.isArray(paths)) { + for (let i = 0, len = paths.length; i < len; i++) { + if (paths[i] === nodeModulesPath) { + paths.splice(i, 0, injectPath); + break; + } } } - } - return paths; - }; + return paths; + }; + } }; -exports.removeGlobalNodeModuleLookupPaths = function () { +module.exports.removeGlobalNodeModuleLookupPaths = function () { const Module = require('module'); // @ts-ignore const globalPaths = Module.globalPaths; @@ -95,10 +116,7 @@ exports.removeGlobalNodeModuleLookupPaths = function () { * @param {Partial} product * @returns {{ portableDataPath: string; isPortable: boolean; }} */ -exports.configurePortable = function (product) { - const fs = require('fs'); - const path = require('path'); - +module.exports.configurePortable = function (product) { const appRoot = path.dirname(__dirname); /** @@ -158,3 +176,9 @@ exports.configurePortable = function (product) { isPortable }; }; + +// ESM-uncomment-begin +// export const injectNodeModuleLookupPath = module.exports.injectNodeModuleLookupPath; +// export const removeGlobalNodeModuleLookupPaths = module.exports.removeGlobalNodeModuleLookupPaths; +// export const configurePortable = module.exports.configurePortable; +// ESM-uncomment-end diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index cd859a847f9..59ddc3fdfbf 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -10,12 +10,20 @@ /** * @import { ISandboxConfiguration } from './vs/base/parts/sandbox/common/sandboxTypes' + * @typedef {any} LoaderConfig */ -/* eslint-disable no-restricted-globals */ +/* eslint-disable no-restricted-globals, */ + +// ESM-comment-begin +const isESM = false; +// ESM-comment-end +// ESM-uncomment-begin +// const isESM = true; +// ESM-uncomment-end // Simple module style to support node.js and browser environments -(function (globalThis, factory) { +(function (factory) { // Node.js if (typeof exports === 'object') { @@ -27,7 +35,7 @@ // @ts-ignore globalThis.MonacoBootstrapWindow = factory(); } -}(this, function () { +}(function () { const bootstrapLib = bootstrap(); const preloadGlobals = sandboxGlobals(); const safeProcess = preloadGlobals.process; @@ -96,59 +104,137 @@ window['MonacoEnvironment'] = {}; - /** @type {any} */ - const loaderConfig = { - baseUrl: `${bootstrapLib.fileUriFromPath(configuration.appRoot, { isWindows: safeProcess.platform === 'win32', scheme: 'vscode-file', fallbackAuthority: 'vscode-app' })}/out`, - preferScriptTags: true - }; + if (isESM) { + + // Signal before require() + if (typeof options?.beforeRequire === 'function') { + options.beforeRequire(configuration); + } - // use a trusted types policy when loading via script tags - loaderConfig.trustedTypesPolicy = window.trustedTypes?.createPolicy('amdLoader', { - createScriptURL(value) { - if (value.startsWith(window.location.origin)) { - return value; + const fileRoot = `${configuration.appRoot}/out`; + globalThis._VSCODE_FILE_ROOT = fileRoot; + + // DEV --------------------------------------------------------------------------------------- + // DEV: This is for development and enables loading CSS via import-statements via import-maps. + // DEV: For each CSS modules that we have we defined an entry in the import map that maps to + // DEV: a blob URL that loads the CSS via a dynamic @import-rule. + // DEV --------------------------------------------------------------------------------------- + if (configuration.cssModules) { + performance.mark('code/willAddCssLoader'); + + const style = document.createElement('style'); + style.type = 'text/css'; + style.media = 'screen'; + style.id = 'vscode-css-loading'; + document.head.appendChild(style); + + globalThis._VSCODE_CSS_LOAD = function (url) { + style.textContent += `@import url(${url});\n`; + }; + + const baseUrl = new URL(`vscode-file://vscode-app${fileRoot}/`); + /** + * @type { { imports: Record }} + */ + const importMap = { imports: {} }; + for (const cssModule of configuration.cssModules) { + const cssUrl = new URL(cssModule, baseUrl).href; + const jsSrc = `globalThis._VSCODE_CSS_LOAD('${cssUrl}');\n`; + const blob = new Blob([jsSrc], { type: 'application/javascript' }); + importMap.imports[cssUrl] = URL.createObjectURL(blob); } - throw new Error(`Invalid script url: ${value}`); + + const ttp = window.trustedTypes?.createPolicy('vscode-bootstrapImportMap', { createScript(value) { return value; }, }); + const importMapSrc = JSON.stringify(importMap, undefined, 2); + const importMapScript = document.createElement('script'); + importMapScript.type = 'importmap'; + importMapScript.setAttribute('nonce', '0c6a828f1297'); + // @ts-ignore + importMapScript.textContent = ttp?.createScript(importMapSrc) ?? importMapSrc; + document.head.appendChild(importMapScript); + + performance.mark('code/didAddCssLoader'); } - }); - - // Teach the loader the location of the node modules we use in renderers - // This will enable to load these modules via - + diff --git a/src/vs/code/electron-sandbox/workbench/workbench-dev.html b/src/vs/code/electron-sandbox/workbench/workbench-dev.html index a92bea242a4..b1be2b7527c 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench-dev.html +++ b/src/vs/code/electron-sandbox/workbench/workbench-dev.html @@ -68,8 +68,7 @@ - - + diff --git a/src/vs/nls.ts b/src/vs/nls.ts index 5a546325fc7..8303d8a32e2 100644 --- a/src/vs/nls.ts +++ b/src/vs/nls.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -// VSCODE_GLOBALS: NLS const isPseudo = globalThis._VSCODE_NLS_LANGUAGE === 'pseudo' || (typeof document !== 'undefined' && document.location && document.location.hash.indexOf('pseudo=true') >= 0); export interface ILocalizeInfo { @@ -87,7 +86,6 @@ export function localize(data: ILocalizeInfo | string /* | number when built */, * depending on the target context. */ function lookupMessage(index: number, fallback: string | null): string { - // VSCODE_GLOBALS: NLS const message = globalThis._VSCODE_NLS_MESSAGES?.[index]; if (typeof message !== 'string') { if (typeof fallback === 'string') { diff --git a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts index d2997c2d22b..fb0033b2ada 100644 --- a/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts +++ b/src/vs/platform/environment/test/node/nativeModules.integrationTest.ts @@ -118,21 +118,18 @@ flakySuite('Native Modules (all platforms)', () => { assert.ok(typeof result === 'string' || typeof result === 'undefined', testErrorMessage('@vscode/windows-registry')); }); - test('@vscode/windows-ca-certs', async () => { - // @ts-ignore we do not directly depend on this module anymore - // but indirectly from our dependency to `@vscode/proxy-agent` - // we still want to ensure this module can work properly. - const windowsCerts = await import('@vscode/windows-ca-certs'); - const store = new windowsCerts.Crypt32(); - assert.ok(windowsCerts, testErrorMessage('@vscode/windows-ca-certs')); - let certCount = 0; - try { - while (store.next()) { - certCount++; + test('@vscode/proxy-agent', async () => { + const proxyAgent = await import('@vscode/proxy-agent'); + // This call will load `@vscode/proxy-agent` which is a native module that we want to test on Windows + const windowsCerts = await proxyAgent.loadSystemCertificates({ + log: { + trace: () => { }, + debug: () => { }, + info: () => { }, + warn: () => { }, + error: () => { } } - } finally { - store.done(); - } - assert(certCount > 0); + }); + assert.ok(windowsCerts.length > 0, testErrorMessage('@vscode/proxy-agent')); }); }); diff --git a/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts b/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts index cedabb0c126..8d1c7fb8679 100644 --- a/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts +++ b/src/vs/platform/extensionManagement/node/extensionSignatureVerificationService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule } from 'vs/amdX'; import { getErrorMessage } from 'vs/base/common/errors'; import { IGalleryExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; import { TargetPlatform } from 'vs/platform/extensions/common/extensions'; @@ -95,14 +96,7 @@ export class ExtensionSignatureVerificationService implements IExtensionSignatur private vsceSign(): Promise { if (!this.moduleLoadingPromise) { - this.moduleLoadingPromise = new Promise( - (resolve, reject) => require( - ['@vscode/vsce-sign'], - async (obj) => { - const instance = obj; - - return resolve(instance); - }, reject)); + this.moduleLoadingPromise = importAMDNodeModule('@vscode/vsce-sign', 'src/main.js'); } return this.moduleLoadingPromise; diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 2b42d690c91..1aea4ead9b9 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -83,7 +83,6 @@ export class IssueMainService implements IIssueMainService { }, product, nls: { - // VSCODE_GLOBALS: NLS messages: globalThis._VSCODE_NLS_MESSAGES, language: globalThis._VSCODE_NLS_LANGUAGE } diff --git a/src/vs/platform/issue/electron-main/processMainService.ts b/src/vs/platform/issue/electron-main/processMainService.ts index 76da45d897c..e6002ae0192 100644 --- a/src/vs/platform/issue/electron-main/processMainService.ts +++ b/src/vs/platform/issue/electron-main/processMainService.ts @@ -155,7 +155,6 @@ export class ProcessMainService implements IProcessMainService { data, product, nls: { - // VSCODE_GLOBALS: NLS messages: globalThis._VSCODE_NLS_MESSAGES, language: globalThis._VSCODE_NLS_LANGUAGE } diff --git a/src/vs/platform/sign/browser/signService.ts b/src/vs/platform/sign/browser/signService.ts index a9d699bf3b2..b6b08ebf466 100644 --- a/src/vs/platform/sign/browser/signService.ts +++ b/src/vs/platform/sign/browser/signService.ts @@ -3,8 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule, resolveAmdNodeModulePath } from 'vs/amdX'; import { WindowIntervalTimer } from 'vs/base/browser/dom'; import { mainWindow } from 'vs/base/browser/window'; +import { isESM } from 'vs/base/common/amd'; import { memoize } from 'vs/base/common/decorators'; import { FileAccess } from 'vs/base/common/network'; import { IProductService } from 'vs/platform/product/common/productService'; @@ -62,7 +64,7 @@ export class SignService extends AbstractSignService implements ISignService { let [wasm] = await Promise.all([ this.getWasmBytes(), new Promise((resolve, reject) => { - require(['vsda'], resolve, reject); + importAMDNodeModule('vsda', 'rust/web/vsda.js').then(() => resolve(), reject); // todo@connor4312: there seems to be a bug(?) in vscode-loader with // require() not resolving in web once the script loads, so check manually @@ -74,7 +76,6 @@ export class SignService extends AbstractSignService implements ISignService { }).finally(() => checkInterval.dispose()), ]); - const keyBytes = new TextEncoder().encode(this.productService.serverLicense?.join('\n') || ''); for (let i = 0; i + STEP_SIZE < keyBytes.length; i += STEP_SIZE) { const key = await crypto.subtle.importKey('raw', keyBytes.slice(i + IV_SIZE, i + IV_SIZE + KEY_SIZE), { name: 'AES-CBC' }, false, ['decrypt']); @@ -87,7 +88,10 @@ export class SignService extends AbstractSignService implements ISignService { } private async getWasmBytes(): Promise { - const response = await fetch(FileAccess.asBrowserUri('vsda/../vsda_bg.wasm').toString(true)); + const url = isESM + ? resolveAmdNodeModulePath('vsda', 'rust/web/vsda_bg.wasm') + : FileAccess.asBrowserUri('vsda/../vsda_bg.wasm').toString(true); + const response = await fetch(url); if (!response.ok) { throw new Error('error loading vsda'); } diff --git a/src/vs/platform/sign/common/abstractSignService.ts b/src/vs/platform/sign/common/abstractSignService.ts index 6f7c91ba958..838dde91518 100644 --- a/src/vs/platform/sign/common/abstractSignService.ts +++ b/src/vs/platform/sign/common/abstractSignService.ts @@ -36,7 +36,7 @@ export abstract class AbstractSignService implements ISignService { }; } } catch (e) { - // ignore errors silently + console.error(e); } return { id: '', data: value }; } @@ -54,7 +54,7 @@ export abstract class AbstractSignService implements ISignService { try { return (validator.validate(value) === 'ok'); } catch (e) { - // ignore errors silently + console.error(e); return false; } finally { validator.dispose?.(); @@ -65,7 +65,7 @@ export abstract class AbstractSignService implements ISignService { try { return await this.signValue(value); } catch (e) { - // ignore errors silently + console.error(e); } return value; } diff --git a/src/vs/platform/sign/node/signService.ts b/src/vs/platform/sign/node/signService.ts index d07ba9cfbe9..1a2023d6ad1 100644 --- a/src/vs/platform/sign/node/signService.ts +++ b/src/vs/platform/sign/node/signService.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { importAMDNodeModule } from 'vs/amdX'; import { AbstractSignService, IVsdaValidator } from 'vs/platform/sign/common/abstractSignService'; import { ISignService } from 'vs/platform/sign/common/sign'; @@ -29,6 +30,6 @@ export class SignService extends AbstractSignService implements ISignService { } private vsda(): Promise { - return new Promise((resolve, reject) => require(['vsda'], resolve, reject)); + return importAMDNodeModule('vsda', 'index.js'); } } diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 070c7ae05d3..5e6279486fc 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -1445,7 +1445,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic userEnv: { ...this.initialUserEnv, ...options.userEnv }, nls: { - // VSCODE_GLOBALS: NLS messages: globalThis._VSCODE_NLS_MESSAGES, language: globalThis._VSCODE_NLS_LANGUAGE }, diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index 29aa95e5683..d9bb0122f64 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -696,7 +696,7 @@ export async function createServer(address: string | net.AddressInfo | null, arg let didLogAboutSIGPIPE = false; process.on('SIGPIPE', () => { // See https://github.com/microsoft/vscode-remote-release/issues/6543 - // We would normally install a SIGPIPE listener in bootstrap.js + // We would normally install a SIGPIPE listener in bootstrap-node.js // But in certain situations, the console itself can be in a broken pipe state // so logging SIGPIPE to the console will cause an infinite async loop if (!didLogAboutSIGPIPE) { diff --git a/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts b/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts index fe3632fb8d0..1bd9688ea69 100644 --- a/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts +++ b/src/vs/workbench/contrib/codeEditor/test/node/autoindent.test.ts @@ -23,6 +23,7 @@ import { EncodedTokenizationResult, IState, ITokenizationSupport, TokenizationRe import { NullState } from 'vs/editor/common/languages/nullTokenize'; import { MetadataConsts, StandardTokenType } from 'vs/editor/common/encodedTokenAttributes'; import { ITextModel } from 'vs/editor/common/model'; +import { FileAccess } from 'vs/base/common/network'; function getIRange(range: IRange): IRange { return { @@ -56,7 +57,7 @@ function registerLanguageConfiguration(instantiationService: TestInstantiationSe let configPath: string; switch (languageId) { case LanguageId.TypeScript: - configPath = path.join('extensions', 'typescript-basics', 'language-configuration.json'); + configPath = FileAccess.asFileUri('vs/workbench/contrib/codeEditor/test/node/language-configuration.json').fsPath; break; default: throw new Error('Unknown languageId'); diff --git a/src/vs/workbench/contrib/codeEditor/test/node/language-configuration.json b/src/vs/workbench/contrib/codeEditor/test/node/language-configuration.json new file mode 100644 index 00000000000..25a23685738 --- /dev/null +++ b/src/vs/workbench/contrib/codeEditor/test/node/language-configuration.json @@ -0,0 +1,250 @@ +{ + // Note that this file should stay in sync with 'javascript-language-basics/javascript-language-configuration.json' + "comments": { + "lineComment": "//", + "blockComment": [ + "/*", + "*/" + ] + }, + "brackets": [ + [ + "${", + "}" + ], + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ] + ], + "autoClosingPairs": [ + { + "open": "{", + "close": "}" + }, + { + "open": "[", + "close": "]" + }, + { + "open": "(", + "close": ")" + }, + { + "open": "'", + "close": "'", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "\"", + "close": "\"", + "notIn": [ + "string" + ] + }, + { + "open": "`", + "close": "`", + "notIn": [ + "string", + "comment" + ] + }, + { + "open": "/**", + "close": " */", + "notIn": [ + "string" + ] + } + ], + "surroundingPairs": [ + [ + "{", + "}" + ], + [ + "[", + "]" + ], + [ + "(", + ")" + ], + [ + "'", + "'" + ], + [ + "\"", + "\"" + ], + [ + "`", + "`" + ], + [ + "<", + ">" + ] + ], + "colorizedBracketPairs": [ + [ + "(", + ")" + ], + [ + "[", + "]" + ], + [ + "{", + "}" + ], + [ + "<", + ">" + ] + ], + "autoCloseBefore": ";:.,=}])>` \n\t", + "folding": { + "markers": { + "start": "^\\s*//\\s*#?region\\b", + "end": "^\\s*//\\s*#?endregion\\b" + } + }, + "wordPattern": { + "pattern": "(-?\\d*\\.\\d\\w*)|([^\\`\\@\\~\\!\\%\\^\\&\\*\\(\\)\\-\\=\\+\\[\\{\\]\\}\\\\\\|\\;\\:\\'\\\"\\,\\.\\<\\>/\\?\\s]+)", + }, + "indentationRules": { + "decreaseIndentPattern": { + "pattern": "^\\s*[\\}\\]\\)].*$" + }, + "increaseIndentPattern": { + "pattern": "^.*(\\{[^}]*|\\([^)]*|\\[[^\\]]*)$" + }, + // e.g. * ...| or */| or *-----*/| + "unIndentedLinePattern": { + "pattern": "^(\\t|[ ])*[ ]\\*[^/]*\\*/\\s*$|^(\\t|[ ])*[ ]\\*/\\s*$|^(\\t|[ ])*\\*([ ]([^\\*]|\\*(?!/))*)?$" + }, + "indentNextLinePattern": { + "pattern": "^((.*=>\\s*)|((.*[^\\w]+|\\s*)(if|while|for)\\s*\\(.*\\)\\s*))$" + } + }, + "onEnterRules": [ + { + // e.g. /** | */ + "beforeText": { + "pattern": "^\\s*/\\*\\*(?!/)([^\\*]|\\*(?!/))*$" + }, + "afterText": { + "pattern": "^\\s*\\*/$" + }, + "action": { + "indent": "indentOutdent", + "appendText": " * " + } + }, + { + // e.g. /** ...| + "beforeText": { + "pattern": "^\\s*/\\*\\*(?!/)([^\\*]|\\*(?!/))*$" + }, + "action": { + "indent": "none", + "appendText": " * " + } + }, + { + // e.g. * ...| + "beforeText": { + "pattern": "^(\\t|[ ])*\\*([ ]([^\\*]|\\*(?!/))*)?$" + }, + "previousLineText": { + "pattern": "(?=^(\\s*(/\\*\\*|\\*)).*)(?=(?!(\\s*\\*/)))" + }, + "action": { + "indent": "none", + "appendText": "* " + } + }, + { + // e.g. */| + "beforeText": { + "pattern": "^(\\t|[ ])*[ ]\\*/\\s*$" + }, + "action": { + "indent": "none", + "removeText": 1 + }, + }, + { + // e.g. *-----*/| + "beforeText": { + "pattern": "^(\\t|[ ])*[ ]\\*[^/]*\\*/\\s*$" + }, + "action": { + "indent": "none", + "removeText": 1 + }, + }, + { + "beforeText": { + "pattern": "^\\s*(\\bcase\\s.+:|\\bdefault:)$" + }, + "afterText": { + "pattern": "^(?!\\s*(\\bcase\\b|\\bdefault\\b))" + }, + "action": { + "indent": "indent" + } + }, + { + // Decrease indentation after single line if/else if/else, for, or while + "previousLineText": "^\\s*(((else ?)?if|for|while)\\s*\\(.*\\)\\s*|else\\s*)$", + // But make sure line doesn't have braces or is not another if statement + "beforeText": "^\\s+([^{i\\s]|i(?!f\\b))", + "action": { + "indent": "outdent" + } + }, + // Indent when pressing enter from inside () + { + "beforeText": "^.*\\([^\\)]*$", + "afterText": "^\\s*\\).*$", + "action": { + "indent": "indentOutdent", + "appendText": "\t", + } + }, + // Indent when pressing enter from inside {} + { + "beforeText": "^.*\\{[^\\}]*$", + "afterText": "^\\s*\\}.*$", + "action": { + "indent": "indentOutdent", + "appendText": "\t", + } + }, + // Indent when pressing enter from inside [] + { + "beforeText": "^.*\\[[^\\]]*$", + "afterText": "^\\s*\\].*$", + "action": { + "indent": "indentOutdent", + "appendText": "\t", + } + }, + ] +} diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter-dev.html b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter-dev.html index 455e823692a..9d853e358a0 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter-dev.html +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter-dev.html @@ -39,8 +39,7 @@ - - + diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html index c6290004d2d..8d0bcc6c87c 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporter.html @@ -39,5 +39,5 @@ - + diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts index 8f4dc4a6b57..ca86cea6dbc 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHost.ts @@ -6,6 +6,7 @@ import * as dom from 'vs/base/browser/dom'; import { parentOriginHash } from 'vs/base/browser/iframe'; import { mainWindow } from 'vs/base/browser/window'; +import { isESM } from 'vs/base/common/amd'; import { Barrier } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; import { canceled, onUnexpectedError } from 'vs/base/common/errors'; @@ -183,14 +184,14 @@ export class WebWorkerExtensionHost extends Disposable implements IExtensionHost } if (event.data.type === 'vscode.bootstrap.nls') { const factoryModuleId = 'vs/base/worker/workerMain.js'; - const baseUrl = require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); + const baseUrl = isESM ? undefined : require.toUrl(factoryModuleId).slice(0, -factoryModuleId.length); iframe.contentWindow!.postMessage({ type: event.data.type, data: { baseUrl, - workerUrl: require.toUrl(factoryModuleId), + workerUrl: isESM ? FileAccess.asBrowserUri(factoryModuleId).toString(true) : require.toUrl(factoryModuleId), + fileRoot: globalThis._VSCODE_FILE_ROOT, nls: { - // VSCODE_GLOBALS: NLS messages: globalThis._VSCODE_NLS_MESSAGES, language: globalThis._VSCODE_NLS_LANGUAGE } diff --git a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html index 3983bdb9dd4..0ad94fc8b30 100644 --- a/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html +++ b/src/vs/workbench/services/extensions/worker/webWorkerExtensionHostIframe.html @@ -4,7 +4,7 @@ @@ -78,7 +78,7 @@ return; } const { data } = event.data; - createWorker(data.baseUrl, data.workerUrl, data.nls.messages, data.nls.language); + createWorker(data.baseUrl, data.workerUrl, data.fileRoot, data.nls.messages, data.nls.language); }; window.parent.postMessage({ @@ -87,24 +87,35 @@ }, '*'); } - function createWorker(baseUrl, workerUrl, nlsMessages, nlsLanguage) { + function createWorker(baseUrl, workerUrl, fileRoot, nlsMessages, nlsLanguage) { try { if (globalThis.crossOriginIsolated) { workerUrl += '?vscode-coi=2'; // COEP } + // ESM-comment-begin + const isESM = false; + // ESM-comment-end + // ESM-uncomment-begin + // const isESM = true; + // ESM-uncomment-end + const blob = new Blob([[ `/*extensionHostWorker*/`, `globalThis.MonacoEnvironment = { baseUrl: '${baseUrl}' };`, - // VSCODE_GLOBALS: NLS `globalThis._VSCODE_NLS_MESSAGES = ${JSON.stringify(nlsMessages)};`, `globalThis._VSCODE_NLS_LANGUAGE = ${JSON.stringify(nlsLanguage)};`, - `importScripts('${workerUrl}');`, + `globalThis._VSCODE_FILE_ROOT = '${fileRoot}';`, + isESM ? `await import('${workerUrl}');` : `importScripts('${workerUrl}');`, + isESM ? `globalThis.onmessage({ data: 'vs/workbench/api/worker/extensionHostWorker' });` : undefined, // important to start loading after the ESM async import has finished `/*extensionHostWorker*/` ].join('')], { type: 'application/javascript' }); - const worker = new Worker(URL.createObjectURL(blob), { name }); - worker.postMessage('vs/workbench/api/worker/extensionHostWorker'); + const worker = new Worker(URL.createObjectURL(blob), { name, type: isESM ? 'module' : undefined }); + if (!isESM) { + // Note: cannot postMessage into a worker that is ESM because imports are async + worker.postMessage('vs/workbench/api/worker/extensionHostWorker'); + } const nestedWorkers = new Map(); worker.onmessage = (event) => { diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index b0a0f4ddb23..a924cdaa5e6 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -252,7 +252,6 @@ async function runTestsInBrowser(testModules, browserType) { // when running from `out-build`, ensure to load the default // messages file, because all `nls.localize` calls have their // english values removed and replaced by an index. - // VSCODE_GLOBALS: NLS // @ts-ignore globalThis._VSCODE_NLS_MESSAGES = JSON.parse(value); }, nlsMessages); diff --git a/test/unit/electron/renderer.js b/test/unit/electron/renderer.js index d4d85a18521..73fc7c65abe 100644 --- a/test/unit/electron/renderer.js +++ b/test/unit/electron/renderer.js @@ -66,7 +66,7 @@ const assert = require('assert'); const path = require('path'); const glob = require('glob'); const util = require('util'); -const bootstrap = require('../../../src/bootstrap'); +const bootstrapNode = require('../../../src/bootstrap-node'); const coverage = require('../coverage'); const { takeSnapshotAndCountClasses } = require('../analyzeSnapshot'); @@ -102,7 +102,6 @@ function initNls(opts) { // when running from `out-build`, ensure to load the default // messages file, because all `nls.localize` calls have their // english values removed and replaced by an index. - // VSCODE_GLOBALS: NLS globalThis._VSCODE_NLS_MESSAGES = (require.__$__nodeRequire ?? require)(`../../../out-build/nls.messages.json`); } } @@ -116,7 +115,7 @@ function initLoader(opts) { const loaderConfig = { nodeRequire: require, catchError: true, - baseUrl: bootstrap.fileUriFromPath(path.join(__dirname, '../../../src'), { isWindows: process.platform === 'win32' }), + baseUrl: bootstrapNode.fileUriFromPath(path.join(__dirname, '../../../src'), { isWindows: process.platform === 'win32' }), paths: { 'vs': `../${outdir}/vs`, 'lib': `../${outdir}/lib`, diff --git a/test/unit/node/index.js b/test/unit/node/index.js index 9f0ec662a73..d087e8409f3 100644 --- a/test/unit/node/index.js +++ b/test/unit/node/index.js @@ -17,6 +17,7 @@ const minimatch = require('minimatch'); const coverage = require('../coverage'); const minimist = require('minimist'); const { takeSnapshotAndCountClasses } = require('../analyzeSnapshot'); +const bootstrapNode = require('../../../src/bootstrap-node'); /** * @type {{ build: boolean; run: string; runGlob: string; coverage: boolean; help: boolean; coverageFormats: string | string[]; coveragePath: string; }} @@ -88,7 +89,6 @@ function main() { // when running from `out-build`, ensure to load the default // messages file, because all `nls.localize` calls have their // english values removed and replaced by an index. - // VSCODE_GLOBALS: NLS globalThis._VSCODE_NLS_MESSAGES = require(`../../../${out}/nls.messages.json`); } @@ -106,41 +106,9 @@ function main() { console.error(e.stack || e); }); - /** - * @param {string} path - * @param {{ isWindows?: boolean, scheme?: string, fallbackAuthority?: string }} config - * @returns {string} - */ - function fileUriFromPath(path, config) { - - // Since we are building a URI, we normalize any backslash - // to slashes and we ensure that the path begins with a '/'. - let pathName = path.replace(/\\/g, '/'); - if (pathName.length > 0 && pathName.charAt(0) !== '/') { - pathName = `/${pathName}`; - } - - /** @type {string} */ - let uri; - - // Windows: in order to support UNC paths (which start with '//') - // that have their own authority, we do not use the provided authority - // but rather preserve it. - if (config.isWindows && pathName.startsWith('//')) { - uri = encodeURI(`${config.scheme || 'file'}:${pathName}`); - } - - // Otherwise we optionally add the provided authority if specified - else { - uri = encodeURI(`${config.scheme || 'file'}://${config.fallbackAuthority || ''}${pathName}`); - } - - return uri.replace(/#/g, '%23'); - } - const loaderConfig = { nodeRequire: require, - baseUrl: fileUriFromPath(src, { isWindows: process.platform === 'win32' }), + baseUrl: bootstrapNode.fileUriFromPath(src, { isWindows: process.platform === 'win32' }), catchError: true }; From 1456e644b0baae937548d16de536c5581fcc4b9e Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Tue, 6 Aug 2024 07:34:30 -0700 Subject: [PATCH 1021/2222] Fix tests --- .../contrib/terminal/browser/terminalProcessManager.ts | 4 ++-- src/vs/workbench/contrib/terminal/common/terminal.ts | 4 ++-- .../contrib/terminal/test/browser/terminalInstance.test.ts | 3 --- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index ca6b79173b4..ff6ff01ea8e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -293,7 +293,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce nonce: this.shellIntegrationNonce }, windowsEnableConpty: this._terminalConfigurationService.config.windowsEnableConpty, - windowsUseConptyDll: this._terminalConfigurationService.config.experimental.windowsUseConptyDll, + windowsUseConptyDll: this._terminalConfigurationService.config.experimental?.windowsUseConptyDll ?? false, environmentVariableCollections: this._extEnvironmentVariableCollection?.collections ? serializeEnvironmentVariableCollections(this._extEnvironmentVariableCollection.collections) : undefined, workspaceFolder: this._cwdWorkspaceFolder, }; @@ -494,7 +494,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce nonce: this.shellIntegrationNonce }, windowsEnableConpty: this._terminalConfigurationService.config.windowsEnableConpty, - windowsUseConptyDll: this._terminalConfigurationService.config.experimental.windowsUseConptyDll, + windowsUseConptyDll: this._terminalConfigurationService.config.experimental?.windowsUseConptyDll ?? false, environmentVariableCollections: this._extEnvironmentVariableCollection ? serializeEnvironmentVariableCollections(this._extEnvironmentVariableCollection.collections) : undefined, workspaceFolder: this._cwdWorkspaceFolder, }; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index d7b1856b0d5..2bbbfd79329 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -214,8 +214,8 @@ export interface ITerminalConfiguration { smoothScrolling: boolean; ignoreBracketedPasteMode: boolean; rescaleOverlappingGlyphs: boolean; - experimental: { - windowsUseConptyDll: boolean; + experimental?: { + windowsUseConptyDll?: boolean; }; } diff --git a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts index 4a5579e21c7..58a23373a57 100644 --- a/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts +++ b/src/vs/workbench/contrib/terminal/test/browser/terminalInstance.test.ts @@ -141,9 +141,6 @@ suite('Workbench - TerminalInstance', () => { unicodeVersion: '6', shellIntegration: { enabled: true - }, - experimental: { - windowsUseConptyDll: false } } }, From 2075322aaf6ac7eb2430262288724d524c295c0f Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 6 Aug 2024 17:26:38 +0200 Subject: [PATCH 1022/2222] Update yaml grammar (#224954) Part of #224862 --- extensions/yaml/cgmanifest.json | 4 ++-- extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json | 5 +++-- extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json | 5 +++-- extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json | 6 ++++-- extensions/yaml/syntaxes/yaml.tmLanguage.json | 13 ++++++++++++- 5 files changed, 24 insertions(+), 9 deletions(-) diff --git a/extensions/yaml/cgmanifest.json b/extensions/yaml/cgmanifest.json index c75a4e52f89..18882ead40a 100644 --- a/extensions/yaml/cgmanifest.json +++ b/extensions/yaml/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "RedCMD/YAML-Syntax-Highlighter", "repositoryUrl": "https://github.com/RedCMD/YAML-Syntax-Highlighter", - "commitHash": "60e2e6e24c63d5a703cb04577678a2e416edd956" + "commitHash": "d4dca9f38a654ebbb13c1b72b7881e3c5864a778" } }, "licenseDetail": [ @@ -21,7 +21,7 @@ "THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ], "license": "MIT", - "version": "1.0.1" + "version": "1.1.1" } ], "version": 1 diff --git a/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json index 6ca3d9bf1b8..7ae77112860 100644 --- a/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml-1.0.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/60e2e6e24c63d5a703cb04577678a2e416edd956", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/dfd7e5f4f71f9695c5d8697ca57f81240165aa04", "name": "YAML 1.0", "scopeName": "source.yaml.1.0", "comment": "https://yaml.org/spec/1.0/", @@ -500,7 +500,8 @@ }, "block-map-value": { "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", - "begin": ":(?=[\\x{85 2028 2029}\r\n\t ])", + "//": "Assumming 3rd party preprocessing variables `{{...}}` turn into valid map-keys when inside a block-mapping", + "begin": ":(?=[\\x{85 2028 2029}\r\n\t ])|(?<=}})(?=[\t ]++#|[\t ]*+$)", "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{2028 2029}]]|-[^\\x{85 2028 2029}\r\n\t ])", "beginCaptures": { "0": { diff --git a/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json index 3b7974a1a5d..57a96ac1e4f 100644 --- a/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml-1.1.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/60e2e6e24c63d5a703cb04577678a2e416edd956", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/dfd7e5f4f71f9695c5d8697ca57f81240165aa04", "name": "YAML 1.1", "scopeName": "source.yaml.1.1", "comment": "https://yaml.org/spec/1.1/", @@ -641,7 +641,8 @@ }, "block-map-value": { "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", - "begin": ":(?=[\\x{85 2028 2029}\r\n\t ])", + "//": "Assumming 3rd party preprocessing variables `{{...}}` turn into valid map-keys when inside a block-mapping", + "begin": ":(?=[\\x{85 2028 2029}\r\n\t ])|(?<=}})(?=[\t ]++#|[\t ]*+$)", "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{2028 2029}]]|-[^\\x{85 2028 2029}\r\n\t ])", "beginCaptures": { "0": { diff --git a/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json b/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json index 711f5c8e422..965b6040816 100644 --- a/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml-1.2.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/60e2e6e24c63d5a703cb04577678a2e416edd956", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/d4dca9f38a654ebbb13c1b72b7881e3c5864a778", "name": "YAML 1.2", "scopeName": "source.yaml.1.2", "comment": "https://yaml.org/spec/1.2.2", @@ -37,6 +37,7 @@ ] }, { + "comment": "For when YAML is embedded inside a Markdown code-block", "begin": "\\G", "while": "\\G", "name": "meta.stream.yaml", @@ -633,7 +634,8 @@ }, "block-map-value": { "comment": "https://yaml.org/spec/1.2.2/#rule-c-l-block-map-implicit-value", - "begin": ":(?=[\r\n\t ])", + "//": "Assumming 3rd party preprocessing variables `{{...}}` turn into valid map-keys when inside a block-mapping", + "begin": ":(?=[\r\n\t ])|(?<=}})(?=[\t ]++#|[\t ]*+$)", "while": "\\G(?![?:!\"'0-9A-Za-z$()+./;<=\\\\^_~\\[{\\x{85}\\x{A0}-\\x{D7FF}\\x{E000}-\\x{FFFD}\\x{010000}-\\x{10FFFF}&&[^\\x{FEFF}]]|-[^\r\n\t ])", "beginCaptures": { "0": { diff --git a/extensions/yaml/syntaxes/yaml.tmLanguage.json b/extensions/yaml/syntaxes/yaml.tmLanguage.json index 8be76d7a3c5..3a64fbd850e 100644 --- a/extensions/yaml/syntaxes/yaml.tmLanguage.json +++ b/extensions/yaml/syntaxes/yaml.tmLanguage.json @@ -4,10 +4,21 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/274009903e20ac6dc37ba5763fb853744e28c9b2", + "version": "https://github.com/RedCMD/YAML-Syntax-Highlighter/commit/d4dca9f38a654ebbb13c1b72b7881e3c5864a778", "name": "YAML Ain't Markup Language", "scopeName": "source.yaml", "patterns": [ + { + "comment": "Support legacy FrontMatter integration", + "//": "https://github.com/microsoft/vscode-markdown-tm-grammar/pull/162", + "begin": "(?<=^-{3,}\\s*+)\\G$", + "while": "^(?! {3,0}-{3,}[ \t]*+$|[ \t]*+\\.{3}$)", + "patterns": [ + { + "include": "source.yaml.1.2" + } + ] + }, { "comment": "Default to YAML version 1.2", "include": "source.yaml.1.2" From 382422232790348e067b527d84f0f292700f97fb Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 6 Aug 2024 18:04:01 +0200 Subject: [PATCH 1023/2222] Fixing hover disappearance in editors that have `overflowingWidgetDomNode` set (#224845) * adding code * adding review changes * removing the unecessary target check --- .../hover/browser/contentHoverController.ts | 19 +++++++++++++++++- .../contrib/hover/browser/hoverController.ts | 20 +++++++++---------- .../contrib/hover/browser/hoverUtils.ts | 17 ++++++++++++++++ .../hover/browser/marginHoverWidget.ts | 13 +++++++++++- 4 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 src/vs/editor/contrib/hover/browser/hoverUtils.ts diff --git a/src/vs/editor/contrib/hover/browser/contentHoverController.ts b/src/vs/editor/contrib/hover/browser/contentHoverController.ts index d79984f24c1..4664ec67af3 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverController.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverController.ts @@ -20,6 +20,7 @@ import { ContentHoverComputer } from 'vs/editor/contrib/hover/browser/contentHov import { HoverResult } from 'vs/editor/contrib/hover/browser/contentHoverTypes'; import { Emitter } from 'vs/base/common/event'; import { RenderedContentHover } from 'vs/editor/contrib/hover/browser/contentHoverRendered'; +import { isMousePositionWithinElement } from 'vs/editor/contrib/hover/browser/hoverUtils'; export class ContentHoverController extends Disposable implements IHoverWidget { @@ -69,11 +70,15 @@ export class ContentHoverController extends Disposable implements IHoverWidget { const messages = (result.hasLoadingMessage ? this._addLoadingMessage(result.value) : result.value); this._withResult(new HoverResult(this._computer.anchor, messages, result.isComplete)); })); - this._register(dom.addStandardDisposableListener(this._contentHoverWidget.getDomNode(), 'keydown', (e) => { + const contentHoverWidgetNode = this._contentHoverWidget.getDomNode(); + this._register(dom.addStandardDisposableListener(contentHoverWidgetNode, 'keydown', (e) => { if (e.equals(KeyCode.Escape)) { this.hide(); } })); + this._register(dom.addStandardDisposableListener(contentHoverWidgetNode, 'mouseleave', (e) => { + this._onMouseLeave(e); + })); this._register(TokenizationRegistry.onDidChange(() => { if (this._contentHoverWidget.position && this._currentResult) { this._setCurrentResult(this._currentResult); // render again @@ -281,6 +286,14 @@ export class ContentHoverController extends Disposable implements IHoverWidget { return anchorCandidates; } + private _onMouseLeave(e: MouseEvent): void { + const editorDomNode = this._editor.getDomNode(); + const isMousePositionOutsideOfEditor = !editorDomNode || !isMousePositionWithinElement(editorDomNode, e.x, e.y); + if (isMousePositionOutsideOfEditor) { + this.hide(); + } + } + public startShowingAtRange(range: Range, mode: HoverStartMode, source: HoverStartSource, focus: boolean): void { this._startShowingOrUpdateHover(new HoverRangeAnchor(0, range, undefined, undefined), mode, source, focus, null); } @@ -363,6 +376,10 @@ export class ContentHoverController extends Disposable implements IHoverWidget { this._setCurrentResult(null); } + public getDomNode(): HTMLElement { + return this._contentHoverWidget.getDomNode(); + } + public get isColorPickerVisible(): boolean { return this._renderedContentHover?.isColorPickerVisible() ?? false; } diff --git a/src/vs/editor/contrib/hover/browser/hoverController.ts b/src/vs/editor/contrib/hover/browser/hoverController.ts index b6ef34860ca..2d4a15463ba 100644 --- a/src/vs/editor/contrib/hover/browser/hoverController.ts +++ b/src/vs/editor/contrib/hover/browser/hoverController.ts @@ -7,7 +7,7 @@ import { DECREASE_HOVER_VERBOSITY_ACTION_ID, INCREASE_HOVER_VERBOSITY_ACTION_ID, import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; +import { ICodeEditor, IEditorMouseEvent, IPartialEditorMouseEvent } from 'vs/editor/browser/editorBrowser'; import { ConfigurationChangedEvent, EditorOption } from 'vs/editor/common/config/editorOptions'; import { Range } from 'vs/editor/common/core/range'; import { IEditorContribution, IScrollEvent } from 'vs/editor/common/editorCommon'; @@ -19,7 +19,7 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ResultKind } from 'vs/platform/keybinding/common/keybindingResolver'; import { HoverVerbosityAction } from 'vs/editor/common/languages'; import { RunOnceScheduler } from 'vs/base/common/async'; -import { ContentHoverWidget } from 'vs/editor/contrib/hover/browser/contentHoverWidget'; +import { isMousePositionWithinElement } from 'vs/editor/contrib/hover/browser/hoverUtils'; import { ContentHoverController } from 'vs/editor/contrib/hover/browser/contentHoverController'; import 'vs/css!./hover'; import { MarginHoverWidget } from 'vs/editor/contrib/hover/browser/marginHoverWidget'; @@ -160,19 +160,19 @@ export class HoverController extends Disposable implements IEditorContribution { } private _isMouseOnMarginHoverWidget(mouseEvent: IPartialEditorMouseEvent): boolean { - const target = mouseEvent.target; - if (!target) { - return false; + const marginHoverWidgetNode = this._glyphWidget?.getDomNode(); + if (marginHoverWidgetNode) { + return isMousePositionWithinElement(marginHoverWidgetNode, mouseEvent.event.posx, mouseEvent.event.posy); } - return target.type === MouseTargetType.OVERLAY_WIDGET && target.detail === MarginHoverWidget.ID; + return false; } private _isMouseOnContentHoverWidget(mouseEvent: IPartialEditorMouseEvent): boolean { - const target = mouseEvent.target; - if (!target) { - return false; + const contentWidgetNode = this._contentWidget?.getDomNode(); + if (contentWidgetNode) { + return isMousePositionWithinElement(contentWidgetNode, mouseEvent.event.posx, mouseEvent.event.posy); } - return target.type === MouseTargetType.CONTENT_WIDGET && target.detail === ContentHoverWidget.ID; + return false; } private _onEditorMouseUp(): void { diff --git a/src/vs/editor/contrib/hover/browser/hoverUtils.ts b/src/vs/editor/contrib/hover/browser/hoverUtils.ts new file mode 100644 index 00000000000..3f9ab067c1d --- /dev/null +++ b/src/vs/editor/contrib/hover/browser/hoverUtils.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * 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'; + +export function isMousePositionWithinElement(element: HTMLElement, posx: number, posy: number): boolean { + const elementRect = dom.getDomNodePagePosition(element); + if (posx < elementRect.left + || posx > elementRect.left + elementRect.width + || posy < elementRect.top + || posy > elementRect.top + elementRect.height) { + return false; + } + return true; +} diff --git a/src/vs/editor/contrib/hover/browser/marginHoverWidget.ts b/src/vs/editor/contrib/hover/browser/marginHoverWidget.ts index fa9577f32dc..e6fb9f4ce60 100644 --- a/src/vs/editor/contrib/hover/browser/marginHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/marginHoverWidget.ts @@ -14,6 +14,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { HoverWidget } from 'vs/base/browser/ui/hover/hoverWidget'; import { IHoverWidget } from 'vs/editor/contrib/hover/browser/hoverTypes'; import { IHoverMessage, LaneOrLineNumber, MarginHoverComputer } from 'vs/editor/contrib/hover/browser/marginHoverComputer'; +import { isMousePositionWithinElement } from 'vs/editor/contrib/hover/browser/hoverUtils'; const $ = dom.$; @@ -59,7 +60,9 @@ export class MarginHoverWidget extends Disposable implements IOverlayWidget, IHo this._updateFont(); } })); - + this._register(dom.addStandardDisposableListener(this._hover.containerDomNode, 'mouseleave', (e) => { + this._onMouseLeave(e); + })); this._editor.addOverlayWidget(this); } @@ -181,4 +184,12 @@ export class MarginHoverWidget extends Disposable implements IOverlayWidget, IHo this._hover.containerDomNode.style.left = `${left}px`; this._hover.containerDomNode.style.top = `${Math.max(Math.round(top), 0)}px`; } + + private _onMouseLeave(e: MouseEvent): void { + const editorDomNode = this._editor.getDomNode(); + const isMousePositionOutsideOfEditor = !editorDomNode || !isMousePositionWithinElement(editorDomNode, e.x, e.y); + if (isMousePositionOutsideOfEditor) { + this.hide(); + } + } } From 08aeda78249c52daf6fff840d7940339c459ee30 Mon Sep 17 00:00:00 2001 From: Raymond Zhao <7199958+rzhao271@users.noreply.github.com> Date: Tue, 6 Aug 2024 09:04:34 -0700 Subject: [PATCH 1024/2222] chore: enable APIScan again (#224648) --- build/azure-pipelines/sdl-scan.yml | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/build/azure-pipelines/sdl-scan.yml b/build/azure-pipelines/sdl-scan.yml index af20a305d9c..c28755b0522 100644 --- a/build/azure-pipelines/sdl-scan.yml +++ b/build/azure-pipelines/sdl-scan.yml @@ -130,18 +130,20 @@ steps: flattenFolders: true condition: succeeded() - # - task: APIScan@2 - # inputs: - # softwareFolder: $(agent.builddirectory)\scanbin - # softwareName: 'vscode-client' - # softwareVersionNum: '1' - # symbolsFolder: 'SRV*http://symweb;$(agent.builddirectory)\symbols' - # isLargeApp: false - # toolVersion: 'Latest' - # displayName: Run ApiScan - # condition: succeeded() - # env: - # AzureServicesAuthConnectionString: $(apiscan-connectionstring) + - task: APIScan@2 + inputs: + softwareFolder: $(agent.builddirectory)\scanbin + softwareName: 'vscode-client' + softwareVersionNum: '1' + symbolsFolder: 'srv*https://symweb.azurefd.net;$(agent.builddirectory)\symbols' + isLargeApp: false + toolVersion: 'Latest' + azureSubscription: 'vscode-apiscan' + displayName: Run ApiScan + condition: succeeded() + env: + AzureServicesAuthConnectionString: $(apiscan-connectionstring) + SYSTEM_ACCESSTOKEN: $(System.AccessToken) - task: PublishSecurityAnalysisLogs@3 inputs: From 145b650442fa582fe3e75efad885e9ca1e5dec37 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Tue, 6 Aug 2024 18:23:02 +0200 Subject: [PATCH 1025/2222] esm - reduce diff (#224959) --- src/bootstrap-window.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bootstrap-window.js b/src/bootstrap-window.js index ee06b29d53c..a747a7cf5dc 100644 --- a/src/bootstrap-window.js +++ b/src/bootstrap-window.js @@ -110,7 +110,7 @@ const isESM = false; // DEV: For each CSS modules that we have we defined an entry in the import map that maps to // DEV: a blob URL that loads the CSS via a dynamic @import-rule. // DEV --------------------------------------------------------------------------------------- - if (configuration.cssModules) { + if (Array.isArray(configuration.cssModules) && configuration.cssModules.length > 0) { performance.mark('code/willAddCssLoader'); const style = document.createElement('style'); From 6288e5db959635c2409c90d45e12b457b17a9aef Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 6 Aug 2024 09:38:18 -0700 Subject: [PATCH 1026/2222] Re #209154. Make execution count sticky. (#224905) --- .../notebook/browser/notebookBrowser.ts | 1 + .../notebook/browser/notebookEditorWidget.ts | 35 +++++++-------- .../browser/view/cellParts/cellExecution.ts | 44 ++++++++++++++++--- .../viewParts/notebookEditorStickyScroll.ts | 13 +++++- 4 files changed, 68 insertions(+), 25 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts index b0abb66c5fb..5287bc53110 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookBrowser.ts @@ -499,6 +499,7 @@ export interface INotebookEditor { readonly isDisposed: boolean; readonly activeKernel: INotebookKernel | undefined; readonly scrollTop: number; + readonly scrollBottom: number; readonly scopedContextKeyService: IContextKeyService; readonly activeCodeEditor: ICodeEditor | undefined; readonly codeEditors: [ICellViewModel, ICodeEditor][]; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 072a2a70593..27c63d82dda 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -1065,27 +1065,22 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } private _registerNotebookStickyScroll() { - this._notebookStickyScroll = this._register(this.instantiationService.createInstance(NotebookStickyScroll, this._notebookStickyScrollContainer, this, this._list)); - - const localDisposableStore = this._register(new DisposableStore()); + this._notebookStickyScroll = this._register(this.instantiationService.createInstance(NotebookStickyScroll, this._notebookStickyScrollContainer, this, this._list, (sizeDelta) => { + if (this.isDisposed) { + return; + } - this._register(this._notebookStickyScroll.onDidChangeNotebookStickyScroll((sizeDelta) => { - const d = localDisposableStore.add(DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => { - if (this.isDisposed) { - return; + if (this._dimension && this._isVisible) { + if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking + this.layout(this._dimension); + this.setScrollTop(this.scrollTop + sizeDelta); + } else if (sizeDelta < 0) { // delta < 0 ==> sticky is shrinking, cell list growing + this.setScrollTop(this.scrollTop + sizeDelta); + this.layout(this._dimension); } + } - if (this._dimension && this._isVisible) { - if (sizeDelta > 0) { // delta > 0 ==> sticky is growing, cell list shrinking - this.layout(this._dimension); - this.setScrollTop(this.scrollTop + sizeDelta); - } else if (sizeDelta < 0) { // delta < 0 ==> sticky is shrinking, cell list growing - this.setScrollTop(this.scrollTop + sizeDelta); - this.layout(this._dimension); - } - } - localDisposableStore.delete(d); - })); + this._onDidScroll.fire(); })); } @@ -2100,6 +2095,10 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return this._list.scrollTop; } + get scrollBottom() { + return this._list.scrollTop + this._list.getRenderHeight(); + } + getAbsoluteTopOfElement(cell: ICellViewModel) { return this._list.getCellViewScrollTop(cell); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts index d5c27d01001..c69d62f34f8 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellExecution.ts @@ -6,9 +6,11 @@ import * as DOM from 'vs/base/browser/dom'; import { disposableTimeout } from 'vs/base/common/async'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { clamp } from 'vs/base/common/numbers'; import { ICellViewModel, INotebookEditorDelegate } from 'vs/workbench/contrib/notebook/browser/notebookBrowser'; import { CellViewModelStateChangeEvent } from 'vs/workbench/contrib/notebook/browser/notebookViewEvents'; import { CellContentPart } from 'vs/workbench/contrib/notebook/browser/view/cellPart'; +import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { NotebookCellInternalMetadata } from 'vs/workbench/contrib/notebook/common/notebookCommon'; import { INotebookExecutionStateService } from 'vs/workbench/contrib/notebook/common/notebookExecutionStateService'; @@ -39,6 +41,10 @@ export class CellExecutionPart extends CellContentPart { this.updateExecutionOrder(this.currentCell.internalMetadata); } })); + + this._register(this._notebookEditor.onDidScroll(() => { + this._updatePosition(); + })); } override didRenderCell(element: ICellViewModel): void { @@ -74,12 +80,38 @@ export class CellExecutionPart extends CellContentPart { } override updateInternalLayoutNow(element: ICellViewModel): void { - if (element.isInputCollapsed) { - DOM.hide(this._executionOrderLabel); - } else { - DOM.show(this._executionOrderLabel); - const top = element.layoutInfo.editorHeight - 22 + element.layoutInfo.statusBarHeight; - this._executionOrderLabel.style.top = `${top}px`; + this._updatePosition(); + } + + private _updatePosition() { + if (this.currentCell) { + if (this.currentCell.isInputCollapsed) { + DOM.hide(this._executionOrderLabel); + } else { + DOM.show(this._executionOrderLabel); + let top = this.currentCell.layoutInfo.editorHeight - 22 + this.currentCell.layoutInfo.statusBarHeight; + + if (this.currentCell instanceof CodeCellViewModel) { + const elementTop = this._notebookEditor.getAbsoluteTopOfElement(this.currentCell); + const editorBottom = elementTop + this.currentCell.layoutInfo.outputContainerOffset; + // another approach to avoid the flicker caused by sticky scroll is manually calculate the scrollBottom: + // const scrollBottom = this._notebookEditor.scrollTop + this._notebookEditor.getLayoutInfo().height - 26 - this._notebookEditor.getLayoutInfo().stickyHeight; + const scrollBottom = this._notebookEditor.scrollBottom; + + const lineHeight = 22; + if (scrollBottom <= editorBottom) { + const offset = editorBottom - scrollBottom; + top -= offset; + top = clamp( + top, + lineHeight + 12, // line height + padding for single line + this.currentCell.layoutInfo.editorHeight - lineHeight + this.currentCell.layoutInfo.statusBarHeight + ); + } + } + + this._executionOrderLabel.style.top = `${top}px`; + } } } } diff --git a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts index 50a6758941b..e82cdf45abf 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewParts/notebookEditorStickyScroll.ts @@ -106,6 +106,8 @@ export class NotebookStickyScroll extends Disposable { readonly onDidChangeNotebookStickyScroll: Event = this._onDidChangeNotebookStickyScroll.event; private notebookCellOutlineReference?: IReference; + private readonly _layoutDisposableStore = this._register(new DisposableStore()); + getDomNode(): HTMLElement { return this.domNode; } @@ -143,6 +145,7 @@ export class NotebookStickyScroll extends Disposable { private readonly domNode: HTMLElement, private readonly notebookEditor: INotebookEditor, private readonly notebookCellList: INotebookCellList, + private readonly layoutFn: (delta: number) => void, @IContextMenuService private readonly _contextMenuService: IContextMenuService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { @@ -269,8 +272,16 @@ export class NotebookStickyScroll extends Disposable { const sizeDelta = this.getCurrentStickyHeight() - oldStickyHeight; if (sizeDelta !== 0) { this._onDidChangeNotebookStickyScroll.fire(sizeDelta); + + const d = this._layoutDisposableStore.add(DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), () => { + this.layoutFn(sizeDelta); + this.updateDisplay(); + + this._layoutDisposableStore.delete(d); + })); + } else { + this.updateDisplay(); } - this.updateDisplay(); } private updateDisplay() { From ed167f4db7f6dcdf69dfafd2cc3eeded2a7833c8 Mon Sep 17 00:00:00 2001 From: Henning Dieterichs Date: Tue, 6 Aug 2024 18:30:48 +0200 Subject: [PATCH 1027/2222] Fixes https://github.com/microsoft/vscode-copilot/issues/7122 --- .../components/diffEditorViewZones/diffEditorViewZones.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts index cfbad5b68b3..3b1b222de0d 100644 --- a/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts +++ b/src/vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts @@ -633,7 +633,10 @@ export function allowsTrueInlineDiffRendering(mapping: DetailedLineRangeMapping) if (!mapping.innerChanges) { return false; } - return mapping.innerChanges.every(c => rangeIsSingleLine(c.modifiedRange) && rangeIsSingleLine(c.originalRange)); + return mapping.innerChanges.every(c => + (rangeIsSingleLine(c.modifiedRange) && rangeIsSingleLine(c.originalRange)) + || c.originalRange.equalsRange(new Range(1, 1, 1, 1)) + ); } function rangeIsSingleLine(range: Range): boolean { From 775e1e9bdf67f5f53f4485f296854c041567c00d Mon Sep 17 00:00:00 2001 From: Alex Ross Date: Tue, 6 Aug 2024 18:58:32 +0200 Subject: [PATCH 1028/2222] Trace log openTextDocument (#224962) Part of #6109 --- src/vs/workbench/api/common/extHost.api.impl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index 98ab60ed9fb..c8664e19071 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -1105,6 +1105,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I } return uriPromise.then(uri => { + extHostLogService.trace(`openTextDocument from ${extension.identifier}`); if (uri.scheme === Schemas.vscodeRemote && !uri.authority) { extHostApiDeprecation.report('workspace.openTextDocument', extension, `A URI of 'vscode-remote' scheme requires an authority.`); } From eae864677ebdc52e26661a2ee7ff94251a4c16cd Mon Sep 17 00:00:00 2001 From: Aiday Marlen Kyzy Date: Tue, 6 Aug 2024 19:06:01 +0200 Subject: [PATCH 1029/2222] refactoring the code --- .../contrib/hover/browser/hoverController.ts | 32 +++++++------------ 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/src/vs/editor/contrib/hover/browser/hoverController.ts b/src/vs/editor/contrib/hover/browser/hoverController.ts index 2d4a15463ba..21b3281dd1d 100644 --- a/src/vs/editor/contrib/hover/browser/hoverController.ts +++ b/src/vs/editor/contrib/hover/browser/hoverController.ts @@ -149,14 +149,9 @@ export class HoverController extends Disposable implements IEditorContribution { } private _shouldNotHideCurrentHoverWidget(mouseEvent: IPartialEditorMouseEvent): boolean { - if ( - this._isMouseOnContentHoverWidget(mouseEvent) + return this._isMouseOnContentHoverWidget(mouseEvent) || this._isMouseOnMarginHoverWidget(mouseEvent) - || this._isContentWidgetResizing() - ) { - return true; - } - return false; + || this._isContentWidgetResizing(); } private _isMouseOnMarginHoverWidget(mouseEvent: IPartialEditorMouseEvent): boolean { @@ -200,35 +195,30 @@ export class HoverController extends Disposable implements IEditorContribution { const isHoverSticky = this._hoverSettings.sticky; - const isMouseOnStickyMarginHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean) => { + const isMouseOnStickyMarginHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean): boolean => { const isMouseOnMarginHoverWidget = this._isMouseOnMarginHoverWidget(mouseEvent); return isHoverSticky && isMouseOnMarginHoverWidget; }; - const isMouseOnStickyContentHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean) => { + const isMouseOnStickyContentHoverWidget = (mouseEvent: IEditorMouseEvent, isHoverSticky: boolean): boolean => { const isMouseOnContentHoverWidget = this._isMouseOnContentHoverWidget(mouseEvent); return isHoverSticky && isMouseOnContentHoverWidget; }; - const isMouseOnColorPicker = (mouseEvent: IEditorMouseEvent) => { + const isMouseOnColorPicker = (mouseEvent: IEditorMouseEvent): boolean => { const isMouseOnContentHoverWidget = this._isMouseOnContentHoverWidget(mouseEvent); - const isColorPickerVisible = this._contentWidget?.isColorPickerVisible; + const isColorPickerVisible = this._contentWidget?.isColorPickerVisible ?? false; return isMouseOnContentHoverWidget && isColorPickerVisible; }; // TODO@aiday-mar verify if the following is necessary code - const isTextSelectedWithinContentHoverWidget = (mouseEvent: IEditorMouseEvent, sticky: boolean) => { - return sticky + const isTextSelectedWithinContentHoverWidget = (mouseEvent: IEditorMouseEvent, sticky: boolean): boolean => { + return (sticky && this._contentWidget?.containsNode(mouseEvent.event.browserEvent.view?.document.activeElement) - && !mouseEvent.event.browserEvent.view?.getSelection()?.isCollapsed; + && !mouseEvent.event.browserEvent.view?.getSelection()?.isCollapsed) ?? false; }; - if ( - isMouseOnStickyMarginHoverWidget(mouseEvent, isHoverSticky) + return isMouseOnStickyMarginHoverWidget(mouseEvent, isHoverSticky) || isMouseOnStickyContentHoverWidget(mouseEvent, isHoverSticky) || isMouseOnColorPicker(mouseEvent) - || isTextSelectedWithinContentHoverWidget(mouseEvent, isHoverSticky) - ) { - return true; - } - return false; + || isTextSelectedWithinContentHoverWidget(mouseEvent, isHoverSticky); } private _onEditorMouseMove(mouseEvent: IEditorMouseEvent): void { From 93f019cec14238bf2e9d2a4cdb532217b2d4c49b Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Tue, 6 Aug 2024 10:34:09 -0700 Subject: [PATCH 1030/2222] Debt - Hook up a generic textual document highlight provider for single and multi file settings (#224884) * textual occurrence provider as a generic highlight provider * `[]` is valid, so change to undefined in test w invalid occurrece value --- .../browser/textualHighlightProvider.ts | 29 +++++- .../browser/wordHighlighter.ts | 89 +++---------------- .../api/browser/mainThreadLanguageFeatures.ts | 5 +- .../browser/extHostLanguageFeatures.test.ts | 2 +- 4 files changed, 43 insertions(+), 82 deletions(-) diff --git a/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts b/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts index 1d7a65695d3..adc31ecd8c0 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider.ts @@ -5,7 +5,7 @@ import { USUAL_WORD_SEPARATORS } from 'vs/editor/common/core/wordHelper'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; -import { DocumentHighlight, DocumentHighlightKind, MultiDocumentHighlightProvider, ProviderResult } from 'vs/editor/common/languages'; +import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider, MultiDocumentHighlightProvider, ProviderResult } from 'vs/editor/common/languages'; import { ITextModel } from 'vs/editor/common/model'; import { Position } from 'vs/editor/common/core/position'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -14,10 +14,33 @@ import { ResourceMap } from 'vs/base/common/map'; import { LanguageFilter } from 'vs/editor/common/languageSelector'; -class TextualDocumentHighlightProvider implements MultiDocumentHighlightProvider { +class TextualDocumentHighlightProvider implements DocumentHighlightProvider, MultiDocumentHighlightProvider { selector: LanguageFilter = { language: '*' }; + provideDocumentHighlights(model: ITextModel, position: Position, token: CancellationToken): ProviderResult { + const result: DocumentHighlight[] = []; + + const word = model.getWordAtPosition({ + lineNumber: position.lineNumber, + column: position.column + }); + + if (!word) { + return Promise.resolve(result); + } + + if (model.isDisposed()) { + return; + } + + const matches = model.findMatches(word.word, true, false, true, USUAL_WORD_SEPARATORS, false); + return matches.map(m => ({ + range: m.range, + kind: DocumentHighlightKind.Text + })); + } + provideMultiDocumentHighlights(primaryModel: ITextModel, position: Position, otherModels: ITextModel[], token: CancellationToken): ProviderResult> { const result = new ResourceMap(); @@ -57,7 +80,7 @@ export class TextualMultiDocumentHighlightFeature extends Disposable { @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, ) { super(); - + this._register(languageFeaturesService.documentHighlightProvider.register('*', new TextualDocumentHighlightProvider())); this._register(languageFeaturesService.multiDocumentHighlightProvider.register('*', new TextualDocumentHighlightProvider())); } } diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index a566fd079de..6aa7ff831b6 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import * as arrays from 'vs/base/common/arrays'; import { alert } from 'vs/base/browser/ui/aria/aria'; -import { CancelablePromise, createCancelablePromise, Delayer, first, timeout } from 'vs/base/common/async'; +import { CancelablePromise, createCancelablePromise, Delayer, first } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; import { onUnexpectedError, onUnexpectedExternalError } from 'vs/base/common/errors'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -23,7 +22,7 @@ import { CursorChangeReason, ICursorPositionChangedEvent } from 'vs/editor/commo import { IDiffEditor, IEditorContribution, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { LanguageFeatureRegistry } from 'vs/editor/common/languageFeatureRegistry'; -import { DocumentHighlight, DocumentHighlightKind, DocumentHighlightProvider, MultiDocumentHighlightProvider } from 'vs/editor/common/languages'; +import { DocumentHighlight, DocumentHighlightProvider, MultiDocumentHighlightProvider } from 'vs/editor/common/languages'; import { IModelDeltaDecoration, ITextModel, shouldSynchronizeModel } from 'vs/editor/common/model'; import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; import { getHighlightDecorationOptions } from 'vs/editor/contrib/wordHighlighter/browser/highlightDecorations'; @@ -34,8 +33,8 @@ import { Schemas } from 'vs/base/common/network'; import { ResourceMap } from 'vs/base/common/map'; import { score } from 'vs/editor/common/languageSelector'; import { isEqual } from 'vs/base/common/resources'; -// import { TextualMultiDocumentHighlightFeature } from 'vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider'; -// import { registerEditorFeature } from 'vs/editor/common/editorFeatures'; +import { TextualMultiDocumentHighlightFeature } from 'vs/editor/contrib/wordHighlighter/browser/textualHighlightProvider'; +import { registerEditorFeature } from 'vs/editor/common/editorFeatures'; const ctxHasWordHighlights = new RawContextKey('hasWordHighlights', false); @@ -44,11 +43,12 @@ export function getOccurrencesAtPosition(registry: LanguageFeatureRegistry(orderedByScore.map(provider => () => { return Promise.resolve(provider.provideDocumentHighlights(model, position, token)) .then(undefined, onUnexpectedExternalError); - }), arrays.isNonEmptyArray).then(result => { + }), (result): result is DocumentHighlight[] => result !== undefined && result !== null).then(result => { if (result) { const map = new ResourceMap(); map.set(model.uri, result); @@ -63,17 +63,17 @@ export function getOccurrencesAcrossMultipleModels(registry: LanguageFeatureRegi // in order of score ask the occurrences provider // until someone response with a good result - // (good = none empty array) + // (good = non undefined and non null ResourceMap) + // (result of size == 0 is valid, no highlights is a valid/expected result -- not a signal to fall back to other providers) return first | null | undefined>(orderedByScore.map(provider => () => { const filteredModels = otherModels.filter(otherModel => { return shouldSynchronizeModel(otherModel); }).filter(otherModel => { return score(provider.selector, otherModel.uri, otherModel.getLanguageId(), true, undefined, undefined) > 0; }); - return Promise.resolve(provider.provideMultiDocumentHighlights(model, position, filteredModels, token)) .then(undefined, onUnexpectedExternalError); - }), (t: ResourceMap | null | undefined): t is ResourceMap => t instanceof ResourceMap && t.size > 0); + }), (result): result is ResourceMap => result !== undefined && result !== null); } interface IOccurenceAtPositionRequest { @@ -184,76 +184,13 @@ class MultiModelOccurenceRequest extends OccurenceAtPositionRequest { } } -class TextualOccurenceRequest extends OccurenceAtPositionRequest { - - private readonly _otherModels: ITextModel[]; - private readonly _selectionIsEmpty: boolean; - private readonly _word: IWordAtPosition | null; - - constructor(model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string, otherModels: ITextModel[]) { - super(model, selection, wordSeparators); - this._otherModels = otherModels; - this._selectionIsEmpty = selection.isEmpty(); - this._word = word; - } - - protected _compute(model: ITextModel, selection: Selection, wordSeparators: string, token: CancellationToken): Promise> { - return timeout(250, token).then(() => { - const result = new ResourceMap(); - - let wordResult; - if (this._word) { - wordResult = this._word; - } else { - wordResult = model.getWordAtPosition(selection.getPosition()); - } - - if (!wordResult) { - return new ResourceMap(); - } - - const allModels = [model, ...this._otherModels]; - - for (const otherModel of allModels) { - if (otherModel.isDisposed()) { - continue; - } - - const matches = otherModel.findMatches(wordResult.word, true, false, true, wordSeparators, false); - const highlights = matches.map(m => ({ - range: m.range, - kind: DocumentHighlightKind.Text - })); - - if (highlights) { - result.set(otherModel.uri, highlights); - } - } - return result; - }); - } - - public override isValid(model: ITextModel, selection: Selection, decorations: IEditorDecorationsCollection): boolean { - const currentSelectionIsEmpty = selection.isEmpty(); - if (this._selectionIsEmpty !== currentSelectionIsEmpty) { - return false; - } - return super.isValid(model, selection, decorations); - } -} function computeOccurencesAtPosition(registry: LanguageFeatureRegistry, model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string): IOccurenceAtPositionRequest { - if (registry.has(model)) { - return new SemanticOccurenceAtPositionRequest(model, selection, wordSeparators, registry); - } - return new TextualOccurenceRequest(model, selection, word, wordSeparators, []); + return new SemanticOccurenceAtPositionRequest(model, selection, wordSeparators, registry); } function computeOccurencesMultiModel(registry: LanguageFeatureRegistry, model: ITextModel, selection: Selection, word: IWordAtPosition | null, wordSeparators: string, otherModels: ITextModel[]): IOccurenceAtPositionRequest { - if (registry.has(model)) { - return new MultiModelOccurenceRequest(model, selection, wordSeparators, registry, otherModels); - } - return new TextualOccurenceRequest(model, selection, word, wordSeparators, otherModels); + return new MultiModelOccurenceRequest(model, selection, wordSeparators, registry, otherModels); } registerModelAndPositionCommand('_executeDocumentHighlights', async (accessor, model, position) => { @@ -973,4 +910,4 @@ registerEditorContribution(WordHighlighterContribution.ID, WordHighlighterContri registerEditorAction(NextWordHighlightAction); registerEditorAction(PrevWordHighlightAction); registerEditorAction(TriggerWordHighlightAction); -// registerEditorFeature(TextualMultiDocumentHighlightFeature); +registerEditorFeature(TextualMultiDocumentHighlightFeature); diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index c3d02eaf472..629af25ae4c 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -3,7 +3,6 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isFalsyOrEmpty } from 'vs/base/common/arrays'; import { VSBuffer } from 'vs/base/common/buffer'; import { CancellationToken } from 'vs/base/common/cancellation'; import { createStringDataTransferItem, IReadonlyVSDataTransfer, VSDataTransfer } from 'vs/base/common/dataTransfer'; @@ -321,7 +320,9 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread selector: selector, provideMultiDocumentHighlights: (model: ITextModel, position: EditorPosition, otherModels: ITextModel[], token: CancellationToken): Promise | undefined> => { return this._proxy.$provideMultiDocumentHighlights(handle, model.uri, position, otherModels.map(model => model.uri), token).then(dto => { - if (isFalsyOrEmpty(dto)) { + // dto should be non-null + non-undefined + // dto length of 0 is valid, just no highlights, pass this through. + if (dto === undefined || dto === null) { return undefined; } const result = new ResourceMap(); diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 141f114ebfa..ec934301cd9 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -517,7 +517,7 @@ suite('ExtHostLanguageFeatures', function () { disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, defaultSelector, new class implements vscode.DocumentHighlightProvider { provideDocumentHighlights(): any { - return []; + return undefined; } })); disposables.add(extHost.registerDocumentHighlightProvider(defaultExtension, '*', new class implements vscode.DocumentHighlightProvider { From 3b0671e73ab2cf5852cfad5dc87e66b08578b959 Mon Sep 17 00:00:00 2001 From: Peng Lyu Date: Tue, 6 Aug 2024 10:38:22 -0700 Subject: [PATCH 1031/2222] Enable notebook smoke test for electron only (#224968) --- test/smoke/src/areas/notebook/notebook.test.ts | 2 +- test/smoke/src/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/smoke/src/areas/notebook/notebook.test.ts b/test/smoke/src/areas/notebook/notebook.test.ts index 130b49a1473..da4d527fe4f 100644 --- a/test/smoke/src/areas/notebook/notebook.test.ts +++ b/test/smoke/src/areas/notebook/notebook.test.ts @@ -34,7 +34,7 @@ export function setup(logger: Logger) { }); }); - it.skip('check object leaks', async function () { + it('check object leaks', async function () { const app = this.app as Application; await app.profiler.checkObjectLeaks(['NotebookTextModel', 'NotebookCellTextModel', 'NotebookEventDispatcher'], async () => { await app.workbench.notebook.openNotebook(); diff --git a/test/smoke/src/main.ts b/test/smoke/src/main.ts index 78e95650db7..4df38db9d72 100644 --- a/test/smoke/src/main.ts +++ b/test/smoke/src/main.ts @@ -400,7 +400,7 @@ describe(`VSCode Smoke Tests (${opts.web ? 'Web' : 'Electron'})`, () => { if (!opts.web) { setupDataLossTests(() => opts['stable-build'] /* Do not change, deferred for a reason! */, logger); } setupPreferencesTests(logger); setupSearchTests(logger); - setupNotebookTests(logger); + if (!opts.web) { setupNotebookTests(logger); } setupLanguagesTests(logger); setupTerminalTests(logger); setupTaskTests(logger); From 8087dd8146f3d393187ee0c3b45f5b5c2e0410e5 Mon Sep 17 00:00:00 2001 From: Michael Lively Date: Tue, 6 Aug 2024 10:57:39 -0700 Subject: [PATCH 1032/2222] Fix reading null WH query modelInfo (#224969) fix reading null modelInfo --- .../editor/contrib/wordHighlighter/browser/wordHighlighter.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index 6aa7ff831b6..22bd3944e14 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -671,7 +671,7 @@ class WordHighlighter { // 1) we have text focus, and a valid query was updated. // 2) we do not have text focus, and a valid query is cached. // the query will ALWAYS have the correct data for the current highlight request, so it can always be passed to the workerRequest safely - if (!WordHighlighter.query.modelInfo || WordHighlighter.query.modelInfo.model.isDisposed()) { + if (!WordHighlighter.query || !WordHighlighter.query.modelInfo || WordHighlighter.query.modelInfo.model.isDisposed()) { return; } this.workerRequest = this.computeWithModel(WordHighlighter.query.modelInfo.model, WordHighlighter.query.modelInfo.selection, WordHighlighter.query.word, otherModelsToHighlight); From ab962cd4225b3ba21a5f9e7855ce15ddca2da59e Mon Sep 17 00:00:00 2001 From: Megan Rogge Date: Tue, 6 Aug 2024 14:28:43 -0700 Subject: [PATCH 1033/2222] ensure dependency tasks are removed from `activeTasks` (#224984) ensure dependency tasks are removed from activeTasks --- src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts index 117501cdadd..6919111b4c6 100644 --- a/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts +++ b/src/vs/workbench/contrib/tasks/browser/terminalTaskSystem.ts @@ -562,9 +562,7 @@ export class TerminalTaskSystem extends Disposable implements ITaskSystem { return { exitCode: 0 }; }); }).finally(() => { - if (this._activeTasks[mapKey] === activeTask) { - delete this._activeTasks[mapKey]; - } + delete this._activeTasks[mapKey]; }); const lastInstance = this._getInstances(task).pop(); const count = lastInstance?.count ?? { count: 0 }; From cd340e6aa44bd9836210a746ab18f57cf1ed032d Mon Sep 17 00:00:00 2001 From: "Matthias B." Date: Tue, 6 Aug 2024 23:54:05 +0200 Subject: [PATCH 1034/2222] Fix: only add apt sources for users that want them (#22145) (#221285) Signed-off-by: Matthias Breithaupt --- build/gulpfile.vscode.linux.js | 6 +++- resources/linux/debian/postinst.template | 36 +++++++++++++++++++---- resources/linux/debian/postrm.template | 19 ++++++++++++ resources/linux/debian/templates.template | 6 ++++ 4 files changed, 61 insertions(+), 6 deletions(-) create mode 100644 resources/linux/debian/templates.template diff --git a/build/gulpfile.vscode.linux.js b/build/gulpfile.vscode.linux.js index 28ddfb04c3d..79594543c3b 100644 --- a/build/gulpfile.vscode.linux.js +++ b/build/gulpfile.vscode.linux.js @@ -108,7 +108,11 @@ function prepareDebPackage(arch) { .pipe(replace('@@NAME@@', product.applicationName)) .pipe(rename('DEBIAN/postinst')); - const all = es.merge(control, postinst, postrm, prerm, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, code); + const templates = gulp.src('resources/linux/debian/templates.template', { base: '.' }) + .pipe(replace('@@NAME@@', product.applicationName)) + .pipe(rename('DEBIAN/templates')); + + const all = es.merge(control, templates, postinst, postrm, prerm, desktops, appdata, workspaceMime, icon, bash_completion, zsh_completion, code); return all.pipe(vfs.dest(destination)); }; diff --git a/resources/linux/debian/postinst.template b/resources/linux/debian/postinst.template index 16acb1481bf..7dc5bef8c5c 100755 --- a/resources/linux/debian/postinst.template +++ b/resources/linux/debian/postinst.template @@ -35,20 +35,46 @@ if [ "@@NAME@@" != "code-oss" ]; then eval $(apt-config shell APT_TRUSTED_PARTS Dir::Etc::trustedparts/d) CODE_TRUSTED_PART=${APT_TRUSTED_PARTS}microsoft.gpg + RET=true + if [ -e '/usr/share/debconf/confmodule' ]; then + . /usr/share/debconf/confmodule + db_get @@NAME@@/add-microsoft-repo || true + fi + # Install repository source list WRITE_SOURCE=0 - if [ ! -f $CODE_SOURCE_PART ] && [ ! -f /etc/rpi-issue ]; then - # Write source list if it does not exist and we're not running on Raspberry Pi OS - WRITE_SOURCE=1 - elif grep -Eq "http:\/\/packages\.microsoft\.com\/repos\/vscode" $CODE_SOURCE_PART; then + if [ "$RET" = false ]; then + # The user does not want to add the microsoft repository + WRITE_SOURCE=0 + elif grep -q "http://packages.microsoft.com/repos/vscode" $CODE_SOURCE_PART; then + # Migrate from old repository + WRITE_SOURCE=2 + elif grep -q "http://packages.microsoft.com/repos/code" $CODE_SOURCE_PART; then # Migrate from old repository + WRITE_SOURCE=2 + elif apt-cache policy | grep -q "https://packages.microsoft.com/repos/code"; then + # Skip following checks if the repo is already known to apt + WRITE_SOURCE=0 + elif [ ! -f $CODE_SOURCE_PART ] && [ ! -f /etc/rpi-issue ]; then + # Write source list if it does not exist and we're not running on Raspberry Pi OS WRITE_SOURCE=1 elif grep -q "# disabled on upgrade to" $CODE_SOURCE_PART; then # Write source list if it was disabled by OS upgrade WRITE_SOURCE=1 fi - if [ "$WRITE_SOURCE" -eq "1" ]; then + if [ "$WRITE_SOURCE" -eq "1" ] && [ -e '/usr/share/debconf/confmodule' ]; then + # Ask the user whether to actually write the source list + db_input high @@NAME@@/add-microsoft-repo || true + db_go || true + + db_get @@NAME@@/add-microsoft-repo + if [ "$RET" = false ]; then + WRITE_SOURCE=0 + fi + fi + + if [ "$WRITE_SOURCE" -ne "0" ]; then echo "### THIS FILE IS AUTOMATICALLY CONFIGURED ### # You may comment out this entry, but any other modifications may be lost. deb [arch=amd64,arm64,armhf] https://packages.microsoft.com/repos/code stable main" > $CODE_SOURCE_PART diff --git a/resources/linux/debian/postrm.template b/resources/linux/debian/postrm.template index fb36d522f38..dcbfda95ea0 100755 --- a/resources/linux/debian/postrm.template +++ b/resources/linux/debian/postrm.template @@ -14,3 +14,22 @@ fi if hash update-mime-database 2>/dev/null; then update-mime-database /usr/share/mime fi + +RET=true +if [ -e '/usr/share/debconf/confmodule' ]; then + . /usr/share/debconf/confmodule + db_get @@NAME@@/add-microsoft-repo || true +fi +if [ "$RET" = "true" ]; then + eval $(apt-config shell APT_SOURCE_PARTS Dir::Etc::sourceparts/d) + CODE_SOURCE_PART=${APT_SOURCE_PARTS}vscode.list + rm -f $CODE_SOURCE_PART + + eval $(apt-config shell APT_TRUSTED_PARTS Dir::Etc::trustedparts/d) + CODE_TRUSTED_PART=${APT_TRUSTED_PARTS}microsoft.gpg + rm -f $CODE_TRUSTED_PART +fi + +if [ "$1" = "purge" ] && [ -e '/usr/share/debconf/confmodule' ]; then + db_purge +fi diff --git a/resources/linux/debian/templates.template b/resources/linux/debian/templates.template new file mode 100644 index 00000000000..7be5e039b26 --- /dev/null +++ b/resources/linux/debian/templates.template @@ -0,0 +1,6 @@ +Template: @@NAME@@/add-microsoft-repo +Type: boolean +Default: true +Description: Add Microsoft apt repository for Visual Studio Code? + The installer would like to add the Microsoft repository and signing + key to update VS Code through apt. From b7e421b7347eb4c6c3991ecdfceb68dc1f684cd8 Mon Sep 17 00:00:00 2001 From: Matt Bierner Date: Tue, 6 Aug 2024 16:04:21 -0700 Subject: [PATCH 1035/2222] Fix arguments for create `tsconfig`/`jsconfig` (#224990) Fix arguments for create tsconfig/jsconfig Fixes #224989 Adds typings too to prevent this again --- .../src/ui/intellisenseStatus.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/extensions/typescript-language-features/src/ui/intellisenseStatus.ts b/extensions/typescript-language-features/src/ui/intellisenseStatus.ts index 1a6ea63f427..e26e2b3719f 100644 --- a/extensions/typescript-language-features/src/ui/intellisenseStatus.ts +++ b/extensions/typescript-language-features/src/ui/intellisenseStatus.ts @@ -43,6 +43,8 @@ namespace IntellisenseState { export type State = typeof None | Pending | Resolved | typeof SyntaxOnly; } +type CreateOrOpenConfigCommandArgs = [root: vscode.Uri, projectType: ProjectType]; + export class IntellisenseStatus extends Disposable { public readonly openOpenConfigCommandId = '_typescript.openConfig'; @@ -62,7 +64,7 @@ export class IntellisenseStatus extends Disposable { commandManager.register({ id: this.openOpenConfigCommandId, - execute: async (root: vscode.Uri, projectType: ProjectType) => { + execute: async (...[root, projectType]: CreateOrOpenConfigCommandArgs) => { if (this._state.type === IntellisenseState.Type.Resolved) { await openProjectConfigOrPromptToCreate(projectType, this._client, root, this._state.configFile); } else if (this._state.type === IntellisenseState.Type.Pending) { @@ -72,7 +74,7 @@ export class IntellisenseStatus extends Disposable { }); commandManager.register({ id: this.createOrOpenConfigCommandId, - execute: async (root: vscode.Uri, projectType: ProjectType) => { + execute: async (...[root, projectType]: CreateOrOpenConfigCommandArgs) => { await openOrCreateConfig(this._client.apiVersion, projectType, root, this._client.configuration); }, }); @@ -182,7 +184,7 @@ export class IntellisenseStatus extends Disposable { title: this._state.projectType === ProjectType.TypeScript ? vscode.l10n.t("Configure tsconfig") : vscode.l10n.t("Configure jsconfig"), - arguments: [rootPath], + arguments: [rootPath, this._state.projectType] satisfies CreateOrOpenConfigCommandArgs, }; } else { statusItem.text = vscode.workspace.asRelativePath(this._state.configFile); @@ -190,7 +192,7 @@ export class IntellisenseStatus extends Disposable { statusItem.command = { command: this.openOpenConfigCommandId, title: vscode.l10n.t("Open config file"), - arguments: [rootPath], + arguments: [rootPath, this._state.projectType] satisfies CreateOrOpenConfigCommandArgs, }; } break; From 1ef4060adfa07fd70b0dae0ed899d891d76333b6 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 7 Aug 2024 09:14:45 +1000 Subject: [PATCH 1036/2222] Soft revert of support for #224759 (#224986) Soft revert of support for 224759 --- .../notebook/browser/diff/diffComponents.ts | 75 ++++++++++--------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 04488e0f8f6..e3124e60c20 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -723,43 +723,44 @@ abstract class AbstractElementRenderer extends Disposable { abstract layout(state: IDiffElementLayoutState): void; protected updateEditorOptions(editor: DiffEditorWidget, optionsToUpdate: ('hideUnchangedRegions' | 'renderSideBySide' | 'useInlineViewWhenSpaceIsLimited')[]): IDisposable { - if (!optionsToUpdate.length) { - return Disposable.None; - } - - const options: { - renderSideBySide?: boolean; - useInlineViewWhenSpaceIsLimited?: boolean; - hideUnchangedRegions?: { enabled: boolean }; - } = {}; - - if (optionsToUpdate.includes('renderSideBySide')) { - options.renderSideBySide = this.configurationService.getValue('diffEditor.renderSideBySide'); - } - if (optionsToUpdate.includes('hideUnchangedRegions')) { - const enabled = this.configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); - options.hideUnchangedRegions = { enabled }; - } - if (optionsToUpdate.includes('useInlineViewWhenSpaceIsLimited')) { - options.useInlineViewWhenSpaceIsLimited = this.configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); - } - - editor.updateOptions(options); - - return this.configurationService.onDidChangeConfiguration(e => { - if (e.affectsConfiguration('diffEditor.hideUnchangedRegions.enabled')) { - const enabled = this.configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); - editor.updateOptions({ hideUnchangedRegions: { enabled } }); - } - if (e.affectsConfiguration('diffEditor.renderSideBySide')) { - const renderSideBySide = this.configurationService.getValue('diffEditor.renderSideBySide'); - editor.updateOptions({ renderSideBySide }); - } - if (e.affectsConfiguration('diffEditor.useInlineViewWhenSpaceIsLimited')) { - const useInlineViewWhenSpaceIsLimited = this.configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); - editor.updateOptions({ useInlineViewWhenSpaceIsLimited }); - } - }); + return Disposable.None; + // if (!optionsToUpdate.length) { + // return Disposable.None; + // } + + // const options: { + // renderSideBySide?: boolean; + // useInlineViewWhenSpaceIsLimited?: boolean; + // hideUnchangedRegions?: { enabled: boolean }; + // } = {}; + + // if (optionsToUpdate.includes('renderSideBySide')) { + // options.renderSideBySide = this.configurationService.getValue('diffEditor.renderSideBySide'); + // } + // if (optionsToUpdate.includes('hideUnchangedRegions')) { + // const enabled = this.configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); + // options.hideUnchangedRegions = { enabled }; + // } + // if (optionsToUpdate.includes('useInlineViewWhenSpaceIsLimited')) { + // options.useInlineViewWhenSpaceIsLimited = this.configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); + // } + + // editor.updateOptions(options); + + // return this.configurationService.onDidChangeConfiguration(e => { + // if (e.affectsConfiguration('diffEditor.hideUnchangedRegions.enabled')) { + // const enabled = this.configurationService.getValue('diffEditor.hideUnchangedRegions.enabled'); + // editor.updateOptions({ hideUnchangedRegions: { enabled } }); + // } + // if (e.affectsConfiguration('diffEditor.renderSideBySide')) { + // const renderSideBySide = this.configurationService.getValue('diffEditor.renderSideBySide'); + // editor.updateOptions({ renderSideBySide }); + // } + // if (e.affectsConfiguration('diffEditor.useInlineViewWhenSpaceIsLimited')) { + // const useInlineViewWhenSpaceIsLimited = this.configurationService.getValue('diffEditor.useInlineViewWhenSpaceIsLimited'); + // editor.updateOptions({ useInlineViewWhenSpaceIsLimited }); + // } + // }); } } From 10487da7f3dd85b85fd28cd4cdc4eb2a955c697b Mon Sep 17 00:00:00 2001 From: Rob Lourens Date: Tue, 6 Aug 2024 16:42:19 -0700 Subject: [PATCH 1037/2222] Fix progress task with empty message (#224995) --- src/vs/workbench/api/common/extHostChatAgents2.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index 3e5225b8dee..b366aa2a986 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -93,7 +93,7 @@ class ChatAgentResponseStream { }; Promise.all([progressReporterPromise, task?.(progressReporter)]).then(([handle, res]) => { - if (handle !== undefined && res !== undefined) { + if (handle !== undefined) { this._proxy.$handleProgressChunk(this._request.requestId, typeConvert.ChatTaskResult.from(res), handle); } }); From fedcaf3e4baef664f6ca63a184e5b50f85e4f444 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 7 Aug 2024 11:01:03 +1000 Subject: [PATCH 1038/2222] Rename `Cell` to `Input` in nb diff view (#224988) --- .../notebook/browser/diff/diffComponents.ts | 6 +++--- .../contrib/notebook/browser/diff/notebookDiff.css | 14 +++++++------- .../notebook/browser/diff/notebookDiffList.ts | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index e3124e60c20..2aaac1987db 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -1596,9 +1596,9 @@ export class ModifiedElement extends AbstractElementRenderer { }, getFoldingState: (cell) => cell.cellFoldingState, updateFoldingState: (cell, state) => cell.cellFoldingState = state, - unChangedLabel: 'Cell', - changedLabel: 'Cell changed', - prefix: 'cell', + unChangedLabel: 'Input', + changedLabel: 'Input changed', + prefix: 'input', menuId: MenuId.NotebookDiffCellInputTitle } )); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index 40de8285bca..d59e7471d5b 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -99,7 +99,7 @@ width: 50%; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { display: flex; @@ -108,7 +108,7 @@ cursor: default; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-folding-indicator .codicon, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-folding-indicator .codicon, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-folding-indicator .codicon, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-folding-indicator .codicon { visibility: visible; @@ -116,7 +116,7 @@ cursor: pointer; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container { display: flex; @@ -124,26 +124,26 @@ align-items: center; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-toolbar, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-toolbar, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-toolbar, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-toolbar { margin-left: auto; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-status, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-status, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status { font-size: 12px; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-status span, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-status span, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status span, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status span { margin: 0 0 0 8px; line-height: 21px; } -.notebook-text-diff-editor .cell-diff-editor-container .cell-header-container .property-status span.property-description, +.notebook-text-diff-editor .cell-diff-editor-container .input-header-container .property-status span.property-description, .notebook-text-diff-editor .cell-diff-editor-container .output-header-container .property-status span.property-description, .notebook-text-diff-editor .cell-diff-editor-container .metadata-header-container .property-status span.property-description { font-style: italic; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index e2edb9b1b27..a12cb9c2fe6 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -82,7 +82,7 @@ export class CellDiffSingleSideRenderer implements IListRenderer Date: Tue, 6 Aug 2024 18:48:14 -0700 Subject: [PATCH 1039/2222] Update conpty.dll Part of microsoft/vscode#224488 --- package.json | 2 +- remote/package.json | 2 +- remote/yarn.lock | 8 ++++---- yarn.lock | 8 ++++---- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 79877cd05a5..27c982781e1 100644 --- a/package.json +++ b/package.json @@ -99,7 +99,7 @@ "native-is-elevated": "0.7.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta18", + "node-pty": "1.1.0-beta19", "open": "^8.4.2", "tas-client-umd": "0.2.0", "v8-inspect-profiler": "^0.1.1", diff --git a/remote/package.json b/remote/package.json index 40b947ae26f..0f3c8d72d22 100644 --- a/remote/package.json +++ b/remote/package.json @@ -30,7 +30,7 @@ "kerberos": "2.1.1-alpha.0", "minimist": "^1.2.6", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta18", + "node-pty": "1.1.0-beta19", "tas-client-umd": "0.2.0", "vscode-oniguruma": "1.7.0", "vscode-regexpp": "^3.1.0", diff --git a/remote/yarn.lock b/remote/yarn.lock index 862f201750a..2989156660d 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -484,10 +484,10 @@ node-gyp-build@4.8.1, node-gyp-build@^4.3.0: resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.8.1.tgz#976d3ad905e71b76086f4f0b0d3637fe79b6cda5" integrity sha512-OSs33Z9yWr148JZcbZd5WiAXhh/n9z8TxQcdMhIOlpN9AhWpLfvVFO73+m77bBABQMaY9XSvIa+qk0jlI7Gcaw== -node-pty@1.1.0-beta18: - version "1.1.0-beta18" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta18.tgz#8ec680788a491423e93096fb1235df0c9079451a" - integrity sha512-H3b5Z9EaMRfKtcz7K5vaIBXLKg0OG+Rz0DjfpUBG9yS2XSbm6Ve4/RjqiJTMJUgp7l2s+ymHCHOp+J1fpDRiHw== +node-pty@1.1.0-beta19: + version "1.1.0-beta19" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta19.tgz#a74dc04429903c5ac49ee81a15a24590da67d4f3" + integrity sha512-/p4Zu56EYDdXjjaLWzrIlFyrBnND11LQGP0/L6GEVGURfCNkAlHc3Twg/2I4NPxghimHXgvDlwp7Z2GtvDIh8A== dependencies: node-addon-api "^7.1.0" diff --git a/yarn.lock b/yarn.lock index 6e460611329..a751a47f79a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7473,10 +7473,10 @@ node-html-parser@^6.1.1: css-select "^5.1.0" he "1.2.0" -node-pty@1.1.0-beta18: - version "1.1.0-beta18" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta18.tgz#8ec680788a491423e93096fb1235df0c9079451a" - integrity sha512-H3b5Z9EaMRfKtcz7K5vaIBXLKg0OG+Rz0DjfpUBG9yS2XSbm6Ve4/RjqiJTMJUgp7l2s+ymHCHOp+J1fpDRiHw== +node-pty@1.1.0-beta19: + version "1.1.0-beta19" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-1.1.0-beta19.tgz#a74dc04429903c5ac49ee81a15a24590da67d4f3" + integrity sha512-/p4Zu56EYDdXjjaLWzrIlFyrBnND11LQGP0/L6GEVGURfCNkAlHc3Twg/2I4NPxghimHXgvDlwp7Z2GtvDIh8A== dependencies: node-addon-api "^7.1.0" From 7380dcb04abedfec5d66afa3c6d348d22c707d02 Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Aug 2024 09:07:02 +0200 Subject: [PATCH 1040/2222] api todos and notebook date update (#225016) --- .vscode/notebooks/api.github-issues | 2 +- src/vscode-dts/vscode.proposed.extensionsAny.d.ts | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index c5dda6b26af..c402cca3836 100644 --- a/.vscode/notebooks/api.github-issues +++ b/.vscode/notebooks/api.github-issues @@ -7,7 +7,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"July 2024\"" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"August 2024\"" }, { "kind": 1, diff --git a/src/vscode-dts/vscode.proposed.extensionsAny.d.ts b/src/vscode-dts/vscode.proposed.extensionsAny.d.ts index c53cd6d89f8..468ffe7b78c 100644 --- a/src/vscode-dts/vscode.proposed.extensionsAny.d.ts +++ b/src/vscode-dts/vscode.proposed.extensionsAny.d.ts @@ -28,6 +28,7 @@ declare module 'vscode' { * @return An extension or `undefined`. */ export function getExtension(extensionId: string, includeDifferentExtensionHosts: boolean): Extension | undefined; + export function getExtension(extensionId: string, includeDifferentExtensionHosts: true): Extension | undefined; /** * All extensions across all extension hosts. From 1925ac59ef47f38e214814f38febf215ca144ce0 Mon Sep 17 00:00:00 2001 From: Justin Chen <54879025+justschen@users.noreply.github.com> Date: Wed, 7 Aug 2024 00:10:18 -0700 Subject: [PATCH 1041/2222] add index to suggestions telemetry (#225001) * add some telemetry * add telemetry data * cleanup and revert some changes --- .../contrib/suggest/browser/suggestController.ts | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 31eb7c7ea5f..0e29ea762bc 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -509,15 +509,14 @@ export class SuggestController implements IEditorContribution { // clear only now - after all tasks are done Promise.all(tasks).finally(() => { - this._reportSuggestionAcceptedTelemetry(item, model, isResolved, _commandExectionDuration, _additionalEditsAppliedAsync); + this._reportSuggestionAcceptedTelemetry(item, model, isResolved, _commandExectionDuration, _additionalEditsAppliedAsync, event.index); this.model.clear(); cts.dispose(); }); } - private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, itemResolved: boolean, commandExectionDuration: number, additionalEditsAppliedAsync: number) { - + private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, itemResolved: boolean, commandExectionDuration: number, additionalEditsAppliedAsync: number, index: number): void { if (Math.floor(Math.random() * 100) === 0) { // throttle telemetry event because accepting completions happens a lot return; @@ -529,6 +528,8 @@ export class SuggestController implements IEditorContribution { resolveInfo: number; resolveDuration: number; commandDuration: number; additionalEditsAsync: number; + index: number; + label: string; }; type AcceptedSuggestionClassification = { owner: 'jrieken'; @@ -543,6 +544,8 @@ export class SuggestController implements IEditorContribution { resolveDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How long resolving took to finish' }; commandDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'How long a completion item command took' }; additionalEditsAsync: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Info about asynchronously applying additional edits' }; + index: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The order of the completion item in the list.' }; + label: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The label of the completion item.' }; }; this._telemetryService.publicLog2('suggest.acceptedSuggestion', { @@ -555,7 +558,9 @@ export class SuggestController implements IEditorContribution { resolveInfo: !item.provider.resolveCompletionItem ? -1 : itemResolved ? 1 : 0, resolveDuration: item.resolveDuration, commandDuration: commandExectionDuration, - additionalEditsAsync: additionalEditsAppliedAsync + additionalEditsAsync: additionalEditsAppliedAsync, + index, + label: item.textLabel }); } From 4a8eb1a4af456c23aceb640c6909fa051f8024b4 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 7 Aug 2024 09:24:13 +0200 Subject: [PATCH 1042/2222] Content lost on failed save (fix #224820) (#225020) --- .../browser/editors/textFileSaveErrorHandler.ts | 8 ++++---- .../contrib/files/browser/fileCommands.ts | 15 ++++++++------- .../workingCopy/common/storedFileWorkingCopy.ts | 6 +++--- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index 066e0372c6b..a598f8b7b2e 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -158,8 +158,8 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa // Save As primaryActions.push(this.instantiationService.createInstance(SaveModelAsAction, model)); - // Discard - primaryActions.push(this.instantiationService.createInstance(DiscardModelAction, model)); + // Revert + primaryActions.push(this.instantiationService.createInstance(RevertModelAction, model)); // Message if (isWriteLocked) { @@ -306,12 +306,12 @@ class RetrySaveModelAction extends Action { } } -class DiscardModelAction extends Action { +class RevertModelAction extends Action { constructor( private model: ITextFileEditorModel ) { - super('workbench.files.action.discardModel', localize('discard', "Discard")); + super('workbench.files.action.revertModel', localize('revert', "Revert")); } override async run(): Promise { diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index bfa8d97796c..1311bd1839b 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -39,7 +39,7 @@ import { EmbeddedCodeEditorWidget } from 'vs/editor/browser/widget/codeEditor/em import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; import { isCancellationError } from 'vs/base/common/errors'; -import { toAction } from 'vs/base/common/actions'; +import { IAction, toAction } from 'vs/base/common/actions'; import { EditorOpenSource, EditorResolution } from 'vs/platform/editor/common/editor'; import { hash } from 'vs/base/common/hash'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -444,16 +444,17 @@ async function doSaveEditors(accessor: ServicesAccessor, editors: IEditorIdentif await editorService.save(editors, options); } catch (error) { if (!isCancellationError(error)) { + const actions: IAction[] = [toAction({ id: 'workbench.action.files.saveEditors', label: nls.localize('retry', "Retry"), run: () => instantiationService.invokeFunction(accessor => doSaveEditors(accessor, editors, options)) })]; + const editorsToRevert = editors.filter(({ editor }) => !editor.hasCapability(EditorInputCapabilities.Untitled) /* all except untitled to prevent unexpected data-loss */); + if (editorsToRevert.length > 0) { + actions.push(toAction({ id: 'workbench.action.files.revertEditors', label: editorsToRevert.length > 1 ? nls.localize('revertAll', "Revert All") : nls.localize('revert', "Revert"), run: () => editorService.revert(editorsToRevert) })); + } + notificationService.notify({ id: editors.map(({ editor }) => hash(editor.resource?.toString())).join(), // ensure unique notification ID per set of editor severity: Severity.Error, message: nls.localize({ key: 'genericSaveError', comment: ['{0} is the resource that failed to save and {1} the error message'] }, "Failed to save '{0}': {1}", editors.map(({ editor }) => editor.getName()).join(', '), toErrorMessage(error, false)), - actions: { - primary: [ - toAction({ id: 'workbench.action.files.saveEditors', label: nls.localize('retry', "Retry"), run: () => instantiationService.invokeFunction(accessor => doSaveEditors(accessor, editors, options)) }), - toAction({ id: 'workbench.action.files.revertEditors', label: nls.localize('discard', "Discard"), run: () => editorService.revert(editors) }) - ] - } + actions: { primary: actions } }); } } diff --git a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts index b7753b5342f..80148112acc 100644 --- a/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts +++ b/src/vs/workbench/services/workingCopy/common/storedFileWorkingCopy.ts @@ -1143,7 +1143,7 @@ export class StoredFileWorkingCopy extend message = localize('staleSaveError', "Failed to save '{0}': The content of the file is newer. Do you want to overwrite the file with your changes?", this.name); primaryActions.push(toAction({ id: 'fileWorkingCopy.overwrite', label: localize('overwrite', "Overwrite"), run: () => this.save({ ...options, ignoreModifiedSince: true, reason: SaveReason.EXPLICIT }) })); - primaryActions.push(toAction({ id: 'fileWorkingCopy.revert', label: localize('discard', "Discard"), run: () => this.revert() })); + primaryActions.push(toAction({ id: 'fileWorkingCopy.revert', label: localize('revert', "Revert"), run: () => this.revert() })); } // Any other save error @@ -1196,8 +1196,8 @@ export class StoredFileWorkingCopy extend } })); - // Discard - primaryActions.push(toAction({ id: 'fileWorkingCopy.revert', label: localize('discard', "Discard"), run: () => this.revert() })); + // Revert + primaryActions.push(toAction({ id: 'fileWorkingCopy.revert', label: localize('revert', "Revert"), run: () => this.revert() })); // Message if (isWriteLocked) { From a2547631a8c71013c43ce68a4fab700fbade429e Mon Sep 17 00:00:00 2001 From: Johannes Rieken Date: Wed, 7 Aug 2024 11:07:36 +0200 Subject: [PATCH 1043/2222] make amdX more graceful when encountering faux AMD (#225017) * make amdX more graceful when encountering faux AMD * fix monaco --- src/vs/amdX.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/vs/amdX.ts b/src/vs/amdX.ts index 8433284d976..eebe82fe16c 100644 --- a/src/vs/amdX.ts +++ b/src/vs/amdX.ts @@ -84,7 +84,9 @@ class AMDModuleImporter { this._initialize(); const defineCall = await (this._isWebWorker ? this._workerLoadScript(scriptSrc) : this._isRenderer ? this._rendererLoadScript(scriptSrc) : this._nodeJSLoadScript(scriptSrc)); if (!defineCall) { - throw new Error(`Did not receive a define call from script ${scriptSrc}`); + // throw new Error(`Did not receive a define call from script ${scriptSrc}`); + console.warn(`Did not receive a define call from script ${scriptSrc}`); + return undefined; } // TODO require, exports, module if (Array.isArray(defineCall.dependencies) && defineCall.dependencies.length > 0) { From 6b0fa93c8176a2bbfdd7ca6da7a0b62a157e16bd Mon Sep 17 00:00:00 2001 From: Ladislau Szomoru <3372902+lszomoru@users.noreply.github.com> Date: Wed, 7 Aug 2024 11:20:56 +0200 Subject: [PATCH 1044/2222] Git - extension should only open repositories for resources with the `file` scheme (#225024) --- extensions/git/src/api/api1.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/extensions/git/src/api/api1.ts b/extensions/git/src/api/api1.ts index f94ecbab7b0..332c328b2d1 100644 --- a/extensions/git/src/api/api1.ts +++ b/extensions/git/src/api/api1.ts @@ -322,6 +322,10 @@ export class ApiImpl implements API { } async openRepository(root: Uri): Promise { + if (root.scheme !== 'file') { + return null; + } + await this._model.openRepository(root.fsPath); return this.getRepository(root) || null; } From 69647f087c13a082b7c324b0eb6d7ad0a3276a0c Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 7 Aug 2024 11:23:48 +0200 Subject: [PATCH 1045/2222] testWSLFeatureInstalled: spawn EPERM (#225025) --- src/vs/platform/remote/node/wsl.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/vs/platform/remote/node/wsl.ts b/src/vs/platform/remote/node/wsl.ts index 4bc71a35f0c..637ccff4fb0 100644 --- a/src/vs/platform/remote/node/wsl.ts +++ b/src/vs/platform/remote/node/wsl.ts @@ -26,7 +26,11 @@ async function testWSLFeatureInstalled(): Promise { const wslExePath = getWSLExecutablePath(); if (wslExePath) { return new Promise(s => { - cp.execFile(wslExePath, ['--status'], err => s(!err)); + try { + cp.execFile(wslExePath, ['--status'], err => s(!err)); + } catch (e) { + s(false); + } }); } } else { From 911602a8b8b737e689e1a0f838e0b93a936ec86b Mon Sep 17 00:00:00 2001 From: troy351 <914053923@qq.com> Date: Wed, 7 Aug 2024 18:04:28 +0800 Subject: [PATCH 1046/2222] fix: multiDiffEditor has wrong background color name (#224151) use correct color name Co-authored-by: Martin Aeschlimann --- src/vs/editor/browser/widget/multiDiffEditor/colors.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vs/editor/browser/widget/multiDiffEditor/colors.ts b/src/vs/editor/browser/widget/multiDiffEditor/colors.ts index 297e5e86465..c9e2cca5fb6 100644 --- a/src/vs/editor/browser/widget/multiDiffEditor/colors.ts +++ b/src/vs/editor/browser/widget/multiDiffEditor/colors.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { registerColor, editorBackground } from 'vs/platform/theme/common/colorRegistry'; export const multiDiffEditorHeaderBackground = registerColor( 'multiDiffEditor.headerBackground', @@ -14,7 +14,7 @@ export const multiDiffEditorHeaderBackground = registerColor( export const multiDiffEditorBackground = registerColor( 'multiDiffEditor.background', - 'editorBackground', + editorBackground, localize('multiDiffEditor.background', 'The background color of the multi file diff editor') ); From a135d55db1397955183b62adafa94ecde2f1db15 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 7 Aug 2024 20:24:14 +1000 Subject: [PATCH 1047/2222] Add `Input` cell headers for Inserted/Deleted cells (#225027) * Add `Input` cell headers for Inserted/Deleted cells * Same paddig for all cells --- .../notebook/browser/diff/diffComponents.ts | 219 ++++++++++++------ .../notebook/browser/diff/notebookDiff.css | 2 + .../browser/diff/notebookDiffEditorBrowser.ts | 1 + .../notebook/browser/diff/notebookDiffList.ts | 9 +- 4 files changed, 148 insertions(+), 83 deletions(-) diff --git a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts index 2aaac1987db..e9f396c4c1a 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/diffComponents.ts @@ -43,6 +43,7 @@ import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibil import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditor/diffEditorWidget'; import { ICommandService } from 'vs/platform/commands/common/commands'; +import { DiffNestedCellViewModel } from 'vs/workbench/contrib/notebook/browser/diff/diffNestedCellViewModel'; export function getOptimizedNestedCodeEditorWidgetOptions(): ICodeEditorWidgetOptions { return { @@ -240,6 +241,7 @@ abstract class AbstractElementRenderer extends Disposable { protected _ignoreMetadata: boolean = false; protected _ignoreOutputs: boolean = false; protected _cellHeaderContainer!: HTMLElement; + protected _editorContainer!: HTMLElement; protected _cellHeader!: PropertyHeader; protected _metadataHeaderContainer!: HTMLElement; protected _metadataHeader!: PropertyHeader; @@ -288,7 +290,9 @@ abstract class AbstractElementRenderer extends Disposable { this._isDisposed = false; this._metadataEditorDisposeStore = this._register(new DisposableStore()); this._outputEditorDisposeStore = this._register(new DisposableStore()); - this._register(cell.onDidLayoutChange(e => this.layout(e))); + this._register(cell.onDidLayoutChange(e => { + this.layout(e); + })); this._register(cell.onDidLayoutChange(e => this.updateBorders())); this.init(); this.buildBody(); @@ -765,10 +769,12 @@ abstract class AbstractElementRenderer extends Disposable { } abstract class SingleSideDiffElement extends AbstractElementRenderer { - + protected _editor!: CodeEditorWidget; override readonly cell: SingleSideDiffElementViewModel; override readonly templateData: CellDiffSingleSideRenderTemplate; - + abstract get nestedCellViewModel(): DiffNestedCellViewModel; + abstract get changedLabel(): string; + abstract get isEditable(): boolean; constructor( notebookEditor: INotebookTextDiffEditor, cell: SingleSideDiffElementViewModel, @@ -874,6 +880,105 @@ abstract class SingleSideDiffElement extends AbstractElementRenderer { })); } + override updateSourceEditor(): void { + this._cellHeaderContainer = this.templateData.cellHeaderContainer; + this._cellHeaderContainer.style.display = 'flex'; + this._cellHeaderContainer.innerText = ''; + this._editorContainer = this.templateData.editorContainer; + this._editorContainer.classList.add('diff'); + + const renderSourceEditor = () => { + if (this.cell.cellFoldingState === PropertyFoldingState.Collapsed) { + this._editorContainer.style.display = 'none'; + this.cell.editorHeight = 0; + return; + } + + const lineCount = this.nestedCellViewModel.textModel.textBuffer.getLineCount(); + const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; + const editorHeight = lineCount * lineHeight + fixedEditorPadding.top + fixedEditorPadding.bottom; + + this._editorContainer.style.height = `${editorHeight}px`; + this._editorContainer.style.display = 'block'; + + if (this._editor) { + const contentHeight = this._editor.getContentHeight(); + if (contentHeight >= 0) { + this.cell.editorHeight = contentHeight; + } + return; + } + + this._editor = this.templateData.sourceEditor; + this._editor.layout( + { + width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, + height: editorHeight + } + ); + if (this.isEditable) { + this._editor.updateOptions({ readOnly: false }); + } + this.cell.editorHeight = editorHeight; + + this._register(this._editor.onDidContentSizeChange((e) => { + if (this.cell.cellFoldingState === PropertyFoldingState.Expanded && e.contentHeightChanged && this.cell.layoutInfo.editorHeight !== e.contentHeight) { + this.cell.editorHeight = e.contentHeight; + } + })); + this._initializeSourceDiffEditor(this.nestedCellViewModel); + }; + + this._cellHeader = this._register(this.instantiationService.createInstance( + PropertyHeader, + this.cell, + this._cellHeaderContainer, + this.notebookEditor, + { + updateInfoRendering: () => renderSourceEditor(), + checkIfModified: (_) => ({ reason: undefined }), + getFoldingState: (cell) => cell.cellFoldingState, + updateFoldingState: (cell, state) => cell.cellFoldingState = state, + unChangedLabel: 'Input', + changedLabel: this.changedLabel, + prefix: 'input', + menuId: MenuId.NotebookDiffCellInputTitle + } + )); + this._cellHeader.buildHeader(); + renderSourceEditor(); + + this._initializeSourceDiffEditor(this.nestedCellViewModel); + } + protected calculateDiagonalFillHeight() { + return this.cell.layoutInfo.cellStatusHeight + this.cell.layoutInfo.editorHeight + this.cell.layoutInfo.editorMargin + this.cell.layoutInfo.metadataStatusHeight + this.cell.layoutInfo.metadataHeight + this.cell.layoutInfo.outputTotalHeight + this.cell.layoutInfo.outputStatusHeight; + } + + private async _initializeSourceDiffEditor(modifiedCell: DiffNestedCellViewModel) { + const modifiedRef = await this.textModelService.createModelReference(modifiedCell.uri); + + if (this._isDisposed) { + return; + } + + const modifiedTextModel = modifiedRef.object.textEditorModel; + this._register(modifiedRef); + + this._editor!.setModel(modifiedTextModel); + + const editorViewState = this.cell.getSourceEditorViewState() as editorCommon.IDiffEditorViewState | null; + if (editorViewState) { + this._editor!.restoreViewState(editorViewState); + } + + const contentHeight = this._editor!.getContentHeight(); + this.cell.editorHeight = contentHeight; + const height = `${this.calculateDiagonalFillHeight()}px`; + if (this._diagonalFill!.style.height !== height) { + this._diagonalFill!.style.height = height; + } + } + _disposeMetadata() { this.cell.metadataStatusHeight = 0; this.cell.metadataHeight = 0; @@ -966,7 +1071,6 @@ abstract class SingleSideDiffElement extends AbstractElementRenderer { } } export class DeletedElement extends SingleSideDiffElement { - private _editor!: CodeEditorWidget; constructor( notebookEditor: INotebookTextDiffEditor, cell: SingleSideDiffElementViewModel, @@ -986,53 +1090,36 @@ export class DeletedElement extends SingleSideDiffElement { super(notebookEditor, cell, templateData, 'left', instantiationService, languageService, modelService, textModelService, contextMenuService, keybindingService, notificationService, menuService, contextKeyService, configurationService); } + get nestedCellViewModel() { + return this.cell.original!; + } + get changedLabel() { + return 'Input deleted'; + } + get isEditable() { + return true; + } + styleContainer(container: HTMLElement) { container.classList.remove('inserted'); container.classList.add('removed'); } - updateSourceEditor(): void { - const originalCell = this.cell.original!; - const lineCount = originalCell.textModel.textBuffer.getLineCount(); - const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + fixedEditorPadding.top + fixedEditorPadding.bottom; - - this._editor = this.templateData.sourceEditor; - this._editor.layout({ - width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, - height: editorHeight - }); - - this.cell.editorHeight = editorHeight; - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this.cell.layoutInfo.editorHeight !== e.contentHeight) { - this.cell.editorHeight = e.contentHeight; - } - })); - - this.textModelService.createModelReference(originalCell.uri).then(ref => { - if (this._isDisposed) { - return; - } - - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - this.cell.editorHeight = this._editor.getContentHeight(); - }); - } - layout(state: IDiffElementLayoutState) { DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => { if (state.editorHeight || state.outerWidth) { + this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`; this._editor.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), height: this.cell.layoutInfo.editorHeight }); } + if (state.outerWidth && this._editor) { + this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`; + this._editor.layout(); + } + if (state.metadataHeight || state.outerWidth) { this._metadataEditor?.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), @@ -1048,13 +1135,14 @@ export class DeletedElement extends SingleSideDiffElement { } if (this._diagonalFill) { - this._diagonalFill.style.height = `${this.cell.layoutInfo.totalHeight - 32}px`; + this._diagonalFill.style.height = `${this.calculateDiagonalFillHeight()}px`; } this.layoutNotebookCell(); }); } + _buildOutputRendererContainer() { if (!this._outputViewContainer) { this._outputViewContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-view-container')); @@ -1118,7 +1206,6 @@ export class DeletedElement extends SingleSideDiffElement { } export class InsertElement extends SingleSideDiffElement { - private _editor!: CodeEditorWidget; constructor( notebookEditor: INotebookTextDiffEditor, cell: SingleSideDiffElementViewModel, @@ -1136,48 +1223,21 @@ export class InsertElement extends SingleSideDiffElement { ) { super(notebookEditor, cell, templateData, 'right', instantiationService, languageService, modelService, textModelService, contextMenuService, keybindingService, notificationService, menuService, contextKeyService, configurationService); } + get nestedCellViewModel() { + return this.cell.modified!; + } + get changedLabel() { + return 'Input inserted'; + } + get isEditable() { + return true; + } styleContainer(container: HTMLElement): void { container.classList.remove('removed'); container.classList.add('inserted'); } - updateSourceEditor(): void { - const modifiedCell = this.cell.modified!; - const lineCount = modifiedCell.textModel.textBuffer.getLineCount(); - const lineHeight = this.notebookEditor.getLayoutInfo().fontInfo.lineHeight || 17; - const editorHeight = lineCount * lineHeight + fixedEditorPadding.top + fixedEditorPadding.bottom; - - this._editor = this.templateData.sourceEditor; - this._editor.layout( - { - width: (this.notebookEditor.getLayoutInfo().width - 2 * DIFF_CELL_MARGIN) / 2 - 18, - height: editorHeight - } - ); - this._editor.updateOptions({ readOnly: false }); - this.cell.editorHeight = editorHeight; - - this._register(this._editor.onDidContentSizeChange((e) => { - if (e.contentHeightChanged && this.cell.layoutInfo.editorHeight !== e.contentHeight) { - this.cell.editorHeight = e.contentHeight; - } - })); - - this.textModelService.createModelReference(modifiedCell.uri).then(ref => { - if (this._isDisposed) { - return; - } - - this._register(ref); - - const textModel = ref.object.textEditorModel; - this._editor.setModel(textModel); - this._editor.restoreViewState(this.cell.getSourceEditorViewState() as editorCommon.ICodeEditorViewState); - this.cell.editorHeight = this._editor.getContentHeight(); - }); - } - _buildOutputRendererContainer() { if (!this._outputViewContainer) { this._outputViewContainer = DOM.append(this._outputInfoContainer, DOM.$('.output-view-container')); @@ -1230,12 +1290,18 @@ export class InsertElement extends SingleSideDiffElement { layout(state: IDiffElementLayoutState) { DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this._diffEditorContainer), () => { if (state.editorHeight || state.outerWidth) { + this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`; this._editor.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, false), height: this.cell.layoutInfo.editorHeight }); } + if (state.outerWidth && this._editor) { + this._editorContainer.style.height = `${this.cell.layoutInfo.editorHeight}px`; + this._editor.layout(); + } + if (state.metadataHeight || state.outerWidth) { this._metadataEditor?.layout({ width: this.cell.getComputedCellContainerWidth(this.notebookEditor.getLayoutInfo(), false, true), @@ -1253,7 +1319,7 @@ export class InsertElement extends SingleSideDiffElement { this.layoutNotebookCell(); if (this._diagonalFill) { - this._diagonalFill.style.height = `${this.cell.layoutInfo.editorHeight + this.cell.layoutInfo.editorMargin + this.cell.layoutInfo.metadataStatusHeight + this.cell.layoutInfo.metadataHeight + this.cell.layoutInfo.outputTotalHeight + this.cell.layoutInfo.outputStatusHeight}px`; + this._diagonalFill.style.height = `${this.calculateDiagonalFillHeight()}px`; } }); } @@ -1270,7 +1336,6 @@ export class InsertElement extends SingleSideDiffElement { export class ModifiedElement extends AbstractElementRenderer { private _editor?: DiffEditorWidget; private _editorViewStateChanged: boolean; - private _editorContainer!: HTMLElement; protected _toolbar!: ToolBar; protected _menu!: IMenu; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css index d59e7471d5b..e62e75cdfbf 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiff.css @@ -364,6 +364,7 @@ .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container .monaco-editor .margin, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .source-container .monaco-editor .monaco-editor-background, +.notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .input-header-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container .monaco-editor .margin, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.inserted .metadata-editor-container .monaco-editor .monaco-editor-background, @@ -381,6 +382,7 @@ .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container .monaco-editor .margin, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .source-container .monaco-editor .monaco-editor-background, +.notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .input-header-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container .monaco-editor .margin, .notebook-text-diff-editor .cell-body .cell-diff-editor-container.removed .metadata-editor-container .monaco-editor .monaco-editor-background, diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index 1fd8eb929ca..0de4d56b511 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -73,6 +73,7 @@ export interface CellDiffSingleSideRenderTemplate extends CellDiffCommonRenderTe readonly diagonalFill: HTMLElement; readonly elementDisposables: DisposableStore; readonly cellHeaderContainer: HTMLElement; + readonly editorContainer: HTMLElement; readonly sourceEditor: CodeEditorWidget; readonly metadataHeaderContainer: HTMLElement; readonly metadataInfoContainer: HTMLElement; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index a12cb9c2fe6..61869d202d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -84,7 +84,7 @@ export class CellDiffSingleSideRenderer implements IListRenderer Date: Wed, 7 Aug 2024 12:32:35 +0200 Subject: [PATCH 1048/2222] SCM - do not show initial commit if the repository has been published (#225031) --- src/vs/workbench/contrib/scm/browser/scmViewPane.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts index 43e1e321951..e7af6f4d1db 100644 --- a/src/vs/workbench/contrib/scm/browser/scmViewPane.ts +++ b/src/vs/workbench/contrib/scm/browser/scmViewPane.ts @@ -4156,8 +4156,10 @@ class SCMTreeHistoryProviderDataSource extends Disposable { } // If we only have one history item that contains all the labels (current, remote, base), - // we don't need to show it, unless it is the root commit (does not have any parents). - if (historyItemsElement.length === 1 && historyItemsElement[0].parentIds.length > 0) { + // we don't need to show it, unless it is the root commit (does not have any parents) and + // the repository has not been published yet. + if (historyItemsElement.length === 1 && + (historyItemsElement[0].parentIds.length > 0 || currentHistoryItemGroup.remote)) { const currentHistoryItemGroupLabels = [ currentHistoryItemGroup.name, ...currentHistoryItemGroup.remote ? [currentHistoryItemGroup.remote.name] : [], From ed73b9d5f218db6cc2c3e31e2c89164146571ecd Mon Sep 17 00:00:00 2001 From: Daniel Imms <2193314+Tyriar@users.noreply.github.com> Date: Wed, 7 Aug 2024 04:04:55 -0700 Subject: [PATCH 1049/2222] Don't pack conpty binaries into asar Part of microsoft/vscode#224488 --- build/gulpfile.vscode.js | 1 + 1 file changed, 1 insertion(+) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index 34d5d79fbe6..edca10c8420 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -288,6 +288,7 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op '**/*.node', '**/@vscode/ripgrep/bin/*', '**/node-pty/build/Release/*', + '**/node-pty/build/Release/conpty/*', '**/node-pty/lib/worker/conoutSocketWorker.js', '**/node-pty/lib/shared/conout.js', '**/*.wasm', From 91209dc8ce502a6d07fa747ace73facadb7c6f66 Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 7 Aug 2024 13:28:56 +0200 Subject: [PATCH 1050/2222] Unexpected brackets are not colored in Light High Contrast theme (#225036) --- src/vs/editor/common/core/editorColorRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/editor/common/core/editorColorRegistry.ts b/src/vs/editor/common/core/editorColorRegistry.ts index b7c71f99721..b8c8157b26b 100644 --- a/src/vs/editor/common/core/editorColorRegistry.ts +++ b/src/vs/editor/common/core/editorColorRegistry.ts @@ -80,7 +80,7 @@ export const editorBracketHighlightingForeground4 = registerColor('editorBracket export const editorBracketHighlightingForeground5 = registerColor('editorBracketHighlight.foreground5', '#00000000', nls.localize('editorBracketHighlightForeground5', 'Foreground color of brackets (5). Requires enabling bracket pair colorization.')); export const editorBracketHighlightingForeground6 = registerColor('editorBracketHighlight.foreground6', '#00000000', nls.localize('editorBracketHighlightForeground6', 'Foreground color of brackets (6). Requires enabling bracket pair colorization.')); -export const editorBracketHighlightingUnexpectedBracketForeground = registerColor('editorBracketHighlight.unexpectedBracket.foreground', { dark: new Color(new RGBA(255, 18, 18, 0.8)), light: new Color(new RGBA(255, 18, 18, 0.8)), hcDark: new Color(new RGBA(255, 50, 50, 1)), hcLight: '' }, nls.localize('editorBracketHighlightUnexpectedBracketForeground', 'Foreground color of unexpected brackets.')); +export const editorBracketHighlightingUnexpectedBracketForeground = registerColor('editorBracketHighlight.unexpectedBracket.foreground', { dark: new Color(new RGBA(255, 18, 18, 0.8)), light: new Color(new RGBA(255, 18, 18, 0.8)), hcDark: 'new Color(new RGBA(255, 50, 50, 1))', hcLight: '#B5200D' }, nls.localize('editorBracketHighlightUnexpectedBracketForeground', 'Foreground color of unexpected brackets.')); export const editorBracketPairGuideBackground1 = registerColor('editorBracketPairGuide.background1', '#00000000', nls.localize('editorBracketPairGuide.background1', 'Background color of inactive bracket pair guides (1). Requires enabling bracket pair guides.')); export const editorBracketPairGuideBackground2 = registerColor('editorBracketPairGuide.background2', '#00000000', nls.localize('editorBracketPairGuide.background2', 'Background color of inactive bracket pair guides (2). Requires enabling bracket pair guides.')); From 16cb9de381a763d18bdd3ce1ebe55a4238bcc5fc Mon Sep 17 00:00:00 2001 From: Martin Aeschlimann Date: Wed, 7 Aug 2024 13:32:56 +0200 Subject: [PATCH 1051/2222] editorOverviewRuler.findMatchForeground not set in light HC theme (#225038) --- src/vs/platform/theme/common/colors/editorColors.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/vs/platform/theme/common/colors/editorColors.ts b/src/vs/platform/theme/common/colors/editorColors.ts index cac6dea162c..cebf9ba8f88 100644 --- a/src/vs/platform/theme/common/colors/editorColors.ts +++ b/src/vs/platform/theme/common/colors/editorColors.ts @@ -426,7 +426,7 @@ export const overviewRulerCommonContentForeground = registerColor('editorOvervie nls.localize('overviewRulerCommonContentForeground', 'Common ancestor overview ruler foreground for inline merge-conflicts.')); export const overviewRulerFindMatchForeground = registerColor('editorOverviewRuler.findMatchForeground', - { dark: '#d186167e', light: '#d186167e', hcDark: '#AB5A00', hcLight: '' }, + { dark: '#d186167e', light: '#d186167e', hcDark: '#AB5A00', hcLight: '#AB5A00' }, nls.localize('overviewRulerFindMatchForeground', 'Overview ruler marker color for find matches. The color must not be opaque so as not to hide underlying decorations.'), true); export const overviewRulerSelectionHighlightForeground = registerColor('editorOverviewRuler.selectionHighlightForeground', From 1df84b22057f1292f51879c6d4d281c23f31ffb8 Mon Sep 17 00:00:00 2001 From: BABA <38986298+BABA983@users.noreply.github.com> Date: Wed, 7 Aug 2024 19:59:09 +0800 Subject: [PATCH 1052/2222] Respect the original terminal tab order after drag multiple tabs (#224591) --- .../contrib/terminal/browser/terminal.ts | 6 +- .../contrib/terminal/browser/terminalGroup.ts | 23 +++++-- .../terminal/browser/terminalGroupService.ts | 67 +++++++++++++------ .../terminal/browser/terminalService.ts | 2 +- .../terminal/browser/terminalTabsList.ts | 18 +++-- .../test/browser/workbenchTestServices.ts | 4 +- 6 files changed, 77 insertions(+), 43 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index ceda1f8b6aa..acbf92a5f0e 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -138,7 +138,7 @@ export interface ITerminalGroup { attachToElement(element: HTMLElement): void; addInstance(instance: ITerminalInstance): void; removeInstance(instance: ITerminalInstance): void; - moveInstance(instance: ITerminalInstance, index: number): void; + moveInstance(instances: ITerminalInstance | ITerminalInstance[], index: number, position: 'before' | 'after'): void; setVisible(visible: boolean): void; layout(width: number, height: number): void; addDisposable(disposable: IDisposable): void; @@ -486,8 +486,8 @@ export interface ITerminalGroupService extends ITerminalInstanceHost { * @param source The source instance to move. * @param target The target instance to move the source instance to. */ - moveGroup(source: ITerminalInstance, target: ITerminalInstance): void; - moveGroupToEnd(source: ITerminalInstance): void; + moveGroup(source: ITerminalInstance | ITerminalInstance[], target: ITerminalInstance): void; + moveGroupToEnd(source: ITerminalInstance | ITerminalInstance[]): void; moveInstance(source: ITerminalInstance, target: ITerminalInstance, side: 'before' | 'after'): void; unsplitInstance(instance: ITerminalInstance): void; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts index 8e9ddcb5b6c..da31bcf18fd 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroup.ts @@ -15,6 +15,7 @@ import { IShellLaunchConfig, ITerminalTabLayoutInfoById, TerminalLocation } from import { TerminalStatus } from 'vs/workbench/contrib/terminal/browser/terminalStatusList'; import { getWindow } from 'vs/base/browser/dom'; import { getPartByLocation } from 'vs/workbench/services/views/browser/viewsService'; +import { asArray } from 'vs/base/common/arrays'; const enum Constants { /** @@ -408,16 +409,24 @@ export class TerminalGroup extends Disposable implements ITerminalGroup { } } - moveInstance(instance: ITerminalInstance, index: number): void { - const sourceIndex = this.terminalInstances.indexOf(instance); - if (sourceIndex === -1) { + moveInstance(instances: ITerminalInstance | ITerminalInstance[], index: number, position: 'before' | 'after'): void { + instances = asArray(instances); + const hasInvalidInstance = instances.some(instance => !this.terminalInstances.includes(instance)); + if (hasInvalidInstance) { return; } - this._terminalInstances.splice(sourceIndex, 1); - this._terminalInstances.splice(index, 0, instance); + const insertIndex = position === 'before' ? index : index + 1; + this._terminalInstances.splice(insertIndex, 0, ...instances); + for (const item of instances) { + const originSourceGroupIndex = position === 'after' ? this._terminalInstances.indexOf(item) : this._terminalInstances.lastIndexOf(item); + this._terminalInstances.splice(originSourceGroupIndex, 1); + } if (this._splitPaneContainer) { - this._splitPaneContainer.remove(instance); - this._splitPaneContainer.split(instance, index); + for (let i = 0; i < instances.length; i++) { + const item = instances[i]; + this._splitPaneContainer.remove(item); + this._splitPaneContainer.split(item, index + (position === 'before' ? i : 0)); + } } this._onInstancesChanged.fire(); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts index 381bff3c367..ff22e6dca8b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalGroupService.ts @@ -19,6 +19,7 @@ import { getInstanceFromResource } from 'vs/workbench/contrib/terminal/browser/t import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; import { TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { TerminalContextKeys } from 'vs/workbench/contrib/terminal/common/terminalContextKey'; +import { asArray } from 'vs/base/common/arrays'; export class TerminalGroupService extends Disposable implements ITerminalGroupService { declare _serviceBrand: undefined; @@ -318,40 +319,66 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe this.setActiveGroupByIndex(newIndex); } - moveGroup(source: ITerminalInstance, target: ITerminalInstance) { - const sourceGroup = this.getGroupForInstance(source); + private _getValidTerminalGroups = (sources: ITerminalInstance[]): Set => { + return new Set( + sources + .map(source => this.getGroupForInstance(source)) + .filter((group) => group !== undefined) + ); + }; + + moveGroup(source: ITerminalInstance | ITerminalInstance[], target: ITerminalInstance) { + source = asArray(source); + const sourceGroups = this._getValidTerminalGroups(source); const targetGroup = this.getGroupForInstance(target); - - // Something went wrong - if (!sourceGroup || !targetGroup) { + if (!targetGroup || sourceGroups.size === 0) { return; } // The groups are the same, rearrange within the group - if (sourceGroup === targetGroup) { - const index = sourceGroup.terminalInstances.indexOf(target); - if (index !== -1) { - sourceGroup.moveInstance(source, index); - } + if (sourceGroups.size === 1 && sourceGroups.has(targetGroup)) { + const targetIndex = targetGroup.terminalInstances.indexOf(target); + const sortedSources = source.sort((a, b) => { + return targetGroup.terminalInstances.indexOf(a) - targetGroup.terminalInstances.indexOf(b); + }); + const firstTargetIndex = targetGroup.terminalInstances.indexOf(sortedSources[0]); + const position: 'before' | 'after' = firstTargetIndex < targetIndex ? 'after' : 'before'; + targetGroup.moveInstance(sortedSources, targetIndex, position); + this._onDidChangeInstances.fire(); return; } // The groups differ, rearrange groups - const sourceGroupIndex = this.groups.indexOf(sourceGroup); const targetGroupIndex = this.groups.indexOf(targetGroup); - this.groups.splice(sourceGroupIndex, 1); - this.groups.splice(targetGroupIndex, 0, sourceGroup); + const sortedSourceGroups = Array.from(sourceGroups).sort((a, b) => { + return this.groups.indexOf(a) - this.groups.indexOf(b); + }); + const firstSourceGroupIndex = this.groups.indexOf(sortedSourceGroups[0]); + const position: 'before' | 'after' = firstSourceGroupIndex < targetGroupIndex ? 'after' : 'before'; + const insertIndex = position === 'after' ? targetGroupIndex + 1 : targetGroupIndex; + this.groups.splice(insertIndex, 0, ...sortedSourceGroups); + for (const sourceGroup of sortedSourceGroups) { + const originSourceGroupIndex = position === 'after' ? this.groups.indexOf(sourceGroup) : this.groups.lastIndexOf(sourceGroup); + this.groups.splice(originSourceGroupIndex, 1); + } this._onDidChangeInstances.fire(); } - moveGroupToEnd(source: ITerminalInstance): void { - const sourceGroup = this.getGroupForInstance(source); - if (!sourceGroup) { + moveGroupToEnd(source: ITerminalInstance | ITerminalInstance[]): void { + source = asArray(source); + const sourceGroups = this._getValidTerminalGroups(source); + if (sourceGroups.size === 0) { return; } - const sourceGroupIndex = this.groups.indexOf(sourceGroup); - this.groups.splice(sourceGroupIndex, 1); - this.groups.push(sourceGroup); + const lastInstanceIndex = this.groups.length - 1; + const sortedSourceGroups = Array.from(sourceGroups).sort((a, b) => { + return this.groups.indexOf(a) - this.groups.indexOf(b); + }); + this.groups.splice(lastInstanceIndex + 1, 0, ...sortedSourceGroups); + for (const sourceGroup of sortedSourceGroups) { + const sourceGroupIndex = this.groups.indexOf(sourceGroup); + this.groups.splice(sourceGroupIndex, 1); + } this._onDidChangeInstances.fire(); } @@ -371,7 +398,7 @@ export class TerminalGroupService extends Disposable implements ITerminalGroupSe // Rearrange within the target group const index = targetGroup.terminalInstances.indexOf(target) + (side === 'after' ? 1 : 0); - targetGroup.moveInstance(source, index); + targetGroup.moveInstance(source, index, side); } unsplitInstance(instance: ITerminalInstance) { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 7247bd7cc64..2dc85842d94 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -823,7 +823,7 @@ export class TerminalService extends Disposable implements ITerminalService { if (target && side) { const index = group.terminalInstances.indexOf(target) + (side === 'after' ? 1 : 0); - group.moveInstance(source, index); + group.moveInstance(source, index, side); } // Fire events diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts index 86824099a3b..254e4cb11de 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTabsList.ts @@ -668,7 +668,11 @@ class TerminalTabsDragAndDrop extends Disposable implements IListDragAndDrop Date: Wed, 7 Aug 2024 05:32:08 -0700 Subject: [PATCH 1053/2222] Add all pwsh keywords as completions Fixes #225042 --- .../browser/media/shellIntegration.ps1 | 115 +++++++++++++++++- 1 file changed, 113 insertions(+), 2 deletions(-) diff --git a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 index 58438e4f12f..d6a560ba6f6 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 +++ b/src/vs/workbench/contrib/terminal/browser/media/shellIntegration.ps1 @@ -170,6 +170,13 @@ function Set-MappedKeyHandler { } } +function Get-KeywordCompletionResult( + $Keyword, + $Description = $null +) { + [System.Management.Automation.CompletionResult]::new($Keyword, $Keyword, [System.Management.Automation.CompletionResultType]::Keyword, $null -ne $Description ? $Description : $Keyword) +} + function Set-MappedKeyHandlers { Set-MappedKeyHandler -Chord Ctrl+Spacebar -Sequence 'F12,a' Set-MappedKeyHandler -Chord Alt+Spacebar -Sequence 'F12,b' @@ -191,8 +198,112 @@ function Set-MappedKeyHandlers { # Get commands, convert to string array to reduce the payload size and send as JSON $commands = @( [System.Management.Automation.CompletionCompleters]::CompleteCommand('') - # Keywords aren't included in CompletionCommand - [System.Management.Automation.CompletionResult]::new('exit', 'exit', [System.Management.Automation.CompletionResultType]::Keyword, "exit []") + Get-KeywordCompletionResult -Keyword 'begin' + Get-KeywordCompletionResult -Keyword 'break' + Get-KeywordCompletionResult -Keyword 'catch' -Description "catch [[][',' ]*] {}" + Get-KeywordCompletionResult -Keyword 'class' -Description @" +class [: [][,]] { + [[] [hidden] [static] ...] + [([]) + {} ...] + [[] [hidden] [static] ...] +} +"@ + Get-KeywordCompletionResult -Keyword 'clean' + Get-KeywordCompletionResult -Keyword 'continue' + Get-KeywordCompletionResult -Keyword 'data' -Description @" +data [] [-supportedCommand ] { + +} +"@ + Get-KeywordCompletionResult -Keyword 'do' -Description @" +do {} while () +do {} until () +"@ + Get-KeywordCompletionResult -Keyword 'dynamicparam' -Description "dynamicparam {}" + Get-KeywordCompletionResult -Keyword 'else' -Description @" +if () + {} +[elseif () + {}] +[else + {}] +"@ + Get-KeywordCompletionResult -Keyword 'elseif' -Description @" +if () + {} +[elseif () + {}] +[else + {}] +"@ + Get-KeywordCompletionResult -Keyword 'end' + Get-KeywordCompletionResult -Keyword 'enum' -Description @" +[[]...] [Flag()] enum [ : ] { +
${escaped ? text : escape(text, true)}\n