Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,10 @@ export class Server extends IPCServer {
Server.Clients.delete(id);
}

onDidClientReconnect.dispose();
// Let every disconnect listener run before disposing the emitter.
// Disposing during fire clears its delivery queue, leaving the old
// channel server subscribed after a renderer reload.
queueMicrotask(() => onDidClientReconnect.dispose());
});
const protocol = new ElectronProtocol(webContents, onMessage);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,17 @@ export class DiffEditorDecorations extends Disposable {

const originalDecorations: IModelDeltaDecoration[] = [];
const modifiedDecorations: IModelDeltaDecoration[] = [];
if (diff.changeHighlights) {
for (const line of diff.changeHighlights.originalLines ?? []) {
originalDecorations.push({ range: { startLineNumber: line, startColumn: 1, endLineNumber: line, endColumn: 1 }, options: diffLineDeleteDecorationBackground });
}
for (const line of diff.changeHighlights.modifiedLines ?? []) {
modifiedDecorations.push({ range: { startLineNumber: line, startColumn: 1, endLineNumber: line, endColumn: 1 }, options: diffLineAddDecorationBackground });
}
for (const range of diff.changeHighlights.original) originalDecorations.push({ range, options: diffDeleteDecoration });
for (const range of diff.changeHighlights.modified) modifiedDecorations.push({ range, options: diffAddDecoration });
return { originalDecorations, modifiedDecorations };
}
if (!movedTextToCompare) {
for (const m of diff.mappings) {
if (!m.lineRangeMapping.original.isEmpty) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,11 @@ export class DiffEditorEditors extends Disposable {

this._register(editor.onDidContentSizeChange(e => {
const width = this.original.getContentWidth() + this.modified.getContentWidth() + OverviewRulerFeature.ENTIRE_DIFF_OVERVIEW_WIDTH;
const height = Math.max(this.modified.getContentHeight(), this.original.getContentHeight());
// Inline deleted code is rendered into the modified editor. The hidden
// original editor may retain an empty-model line or alignment zones.
const height = this._options.renderSideBySide.get()
? Math.max(this.modified.getContentHeight(), this.original.getContentHeight())
: this.modified.getContentHeight();

this._onDidContentSizeChange.fire({
contentHeight: height,
Expand Down Expand Up @@ -190,7 +194,7 @@ export class DiffEditorEditors extends Disposable {

// Clone scrollbar options before changing them
clonedOptions.scrollbar = { ...(clonedOptions.scrollbar || {}) };
clonedOptions.folding = false;
clonedOptions.folding = this._options.editorOptions.get().experimentalDiffFolding === true;
clonedOptions.codeLens = this._options.diffCodeLens.get();
clonedOptions.fixedOverflowWidgets = true;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { projectSourceAlignment } from "../../../../../common/diff/sourceLineAlignment.js";
import { $, addDisposableListener } from '../../../../../../base/browser/dom.js';
import { ArrayQueue } from '../../../../../../base/common/arrays.js';
import { RunOnceScheduler } from '../../../../../../base/common/async.js';
Expand All @@ -19,7 +20,7 @@ import { DiffEditorViewModel, DiffMapping } from '../../diffEditorViewModel.js';
import { DiffEditorWidget } from '../../diffEditorWidget.js';
import { InlineDiffDeletedCodeMargin } from './inlineDiffDeletedCodeMargin.js';
import { LineSource, RenderOptions, renderLines } from './renderLines.js';
import { IObservableViewZone, animatedObservable, joinCombine } from '../../utils.js';
import { IObservableViewZone, animatedObservable, bandZoneHeightPx, joinCombine } from '../../utils.js';
import { EditorOption } from '../../../../../common/config/editorOptions.js';
import { LineRange } from '../../../../../common/core/ranges/lineRange.js';
import { Position } from '../../../../../common/core/position.js';
Expand Down Expand Up @@ -81,6 +82,8 @@ export class DiffEditorViewZones extends Disposable {

this._register(this._editors.original.onDidChangeViewZones((_args) => { if (!this._canIgnoreViewZoneUpdateEvent()) { updateImmediately.schedule(); } }));
this._register(this._editors.modified.onDidChangeViewZones((_args) => { if (!this._canIgnoreViewZoneUpdateEvent()) { updateImmediately.schedule(); } }));
this._register(this._editors.original.onDidChangeHiddenAreas(() => updateImmediately.schedule()));
this._register(this._editors.modified.onDidChangeHiddenAreas(() => updateImmediately.schedule()));
this._register(this._editors.original.onDidChangeConfiguration((args) => {
if (args.hasChanged(EditorOption.wrappingInfo) || args.hasChanged(EditorOption.lineHeight)) { updateImmediately.schedule(); }
}));
Expand All @@ -100,6 +103,24 @@ export class DiffEditorViewZones extends Disposable {
state.read(reader);
const renderSideBySide = this._options.renderSideBySide.read(reader);
const innerHunkAlignment = renderSideBySide;
if (renderSideBySide && diff.sourceLineAlignment) {
const regions = diffModel.unchangedRegions.read(reader);
const compactMode = this._options.compactMode.read(reader);
const bandLineHeight = this._editors.modified.getOption(EditorOption.lineHeight);
// A one-sided band is a zone on its own side only. Its height counts as the height of its first hidden line,
// so the row that starts the fold gets a filler of exactly that height on the other side.
const originalBands = new Map<number, number>(), modifiedBands = new Map<number, number>();
regions.forEach((region, index) => {
if (region.kind === 'unchanged') { return; }
const height = bandZoneHeightPx(regions, index, compactMode, bandLineHeight, reader);
if (height === undefined) { return; }
const [bands, first] = region.kind === 'removed'
? [originalBands, region.getHiddenOriginalRange(reader).startLineNumber]
: [modifiedBands, region.getHiddenModifiedRange(reader).startLineNumber];
bands.set(first, (bands.get(first) ?? 0) + height);
});
return computeSourceAlignment(this._editors.original, this._editors.modified, diff.sourceLineAlignment, this._origViewZonesToIgnore, this._modViewZonesToIgnore, originalBands, modifiedBands);
}
return computeRangeAlignment(
this._editors.original,
this._editors.modified,
Expand Down Expand Up @@ -184,7 +205,7 @@ export class DiffEditorViewZones extends Disposable {
if (i > originalModel.getLineCount()) {
return { orig: origViewZones, mod: modViewZones };
}
deletedCodeLineBreaksComputer?.addRequest(i, null);
if (this._editors.original._getViewModel()!.coordinatesConverter.getModelLineViewLineCount(i) > 0) deletedCodeLineBreaksComputer?.addRequest(i, null);
}
}
}
Expand All @@ -201,41 +222,66 @@ export class DiffEditorViewZones extends Disposable {
const mightContainRTL = this._editors.original.getModel()?.mightContainRTL() ?? false;
const renderOptions = RenderOptions.fromEditor(this._editors.modified);

const changeHighlights = this._diffModel.read(reader)?.diff.read(reader)?.changeHighlights;
for (const a of alignmentsVal) {
if (a.diff && !renderSideBySide && (!this._options.useTrueInlineDiffRendering.read(reader) || !allowsTrueInlineDiffRendering(a.diff))) {
if (!a.originalRange.isEmpty) {
if (!a.originalRange.isEmpty && a.originalRange.mapToLineArray(l => this._editors.original._getViewModel()!.coordinatesConverter.getModelLineViewLineCount(l)).some(height => height > 0)) {
originalModelTokenizationCompleted.read(reader); // Update view-zones once tokenization completes

const deletedCodeDomNode = document.createElement('div');
deletedCodeDomNode.classList.add('view-lines', 'line-delete', 'line-delete-selectable', 'monaco-mouse-cursor-text');
deletedCodeDomNode.classList.add('view-lines', 'line-delete-selectable', 'monaco-mouse-cursor-text');
if (!changeHighlights) deletedCodeDomNode.classList.add('line-delete');
const originalModel = this._editors.original.getModel()!;
// `a.originalRange` can be out of bound when the diff has not been updated yet.
// In this case, we do an early return.
// TODO@hediet: Fix this by applying the edit directly to the diff model, so that the diff is always valid.
if (a.originalRange.endLineNumberExclusive - 1 > originalModel.getLineCount()) {
return { orig: origViewZones, mod: modViewZones };
}
const visibleOriginalLines = a.originalRange.mapToLineArray(l => l).filter(l => this._editors.original._getViewModel()!.coordinatesConverter.getModelLineViewLineCount(l) > 0);
const source = new LineSource(
a.originalRange.mapToLineArray(l => originalModel.tokenization.getLineTokens(l)),
a.originalRange.mapToLineArray(_ => lineBreakData[lineBreakDataIdx++]),
visibleOriginalLines.map(l => originalModel.tokenization.getLineTokens(l)),
visibleOriginalLines.map(_ => lineBreakData[lineBreakDataIdx++]),
mightContainNonBasicASCII,
mightContainRTL,
);
const decorations: InlineDecoration[] = [];
for (const i of a.diff.innerChanges || []) {
decorations.push(new InlineDecoration(
i.originalRange.delta(-(a.diff.original.startLineNumber - 1)),
diffDeleteDecoration.className!,
InlineDecorationType.Regular
));
if (changeHighlights) {
// Source lines can be hidden by folds. Map paint into the compact
// deleted-code buffer rather than treating it as contiguous source.
const visibleRows = new Map(visibleOriginalLines.map((line, i) => [line, i + 1]));
for (const highlight of changeHighlights.original) {
for (let line = highlight.startLineNumber; line <= highlight.endLineNumber; line++) {
const row = visibleRows.get(line);
if (row === undefined) continue;
const start = line === highlight.startLineNumber ? highlight.startColumn : 1;
const end = line === highlight.endLineNumber ? highlight.endColumn : originalModel.getLineMaxColumn(line);
if (end > start) decorations.push(new InlineDecoration(new Range(row, start, row, end), diffDeleteDecoration.className!, InlineDecorationType.Regular));
}
}
} else {
for (const i of a.diff.innerChanges || []) {
decorations.push(new InlineDecoration(i.originalRange.delta(-(a.diff.original.startLineNumber - 1)), diffDeleteDecoration.className!, InlineDecorationType.Regular));
}
}
const result = renderLines(source, renderOptions, decorations, deletedCodeDomNode, false, changeHighlights !== undefined);
if (changeHighlights) {
const novelLines = new Set(changeHighlights.originalLines);
const viewLines = deletedCodeDomNode.querySelectorAll('.view-line');
let viewRow = 0;
for (let i = 0; i < visibleOriginalLines.length; i++) {
for (let wrappedRow = 0; wrappedRow < result.viewLineCounts[i]; wrappedRow++, viewRow++) {
if (novelLines.has(visibleOriginalLines[i])) viewLines[viewRow].classList.add('line-delete');

}
}
}
const result = renderLines(source, renderOptions, decorations, deletedCodeDomNode);

const marginDomNode = document.createElement('div');
marginDomNode.className = 'inline-deleted-margin-view-zone';
marginDomNode.className = changeHighlights ? 'inline-original-margin-view-zone' : 'inline-deleted-margin-view-zone';
applyFontInfo(marginDomNode, renderOptions.fontInfo);

if (this._options.renderIndicators.read(reader)) {
if (!changeHighlights && this._options.renderIndicators.read(reader)) {
for (let i = 0; i < result.heightInLines; i++) {
const marginElement = document.createElement('div');
marginElement.className = `delete-sign ${ThemeIcon.asClassName(diffRemoveIcon)}`;
Expand All @@ -257,6 +303,7 @@ export class DiffEditorViewZones extends Disposable {
this._editors.original.getModel()!,
this._contextMenuService,
this._clipboardService,
visibleOriginalLines,
)
);

Expand All @@ -265,7 +312,7 @@ export class DiffEditorViewZones extends Disposable {
// Account for wrapped lines in the (collapsed) original editor (which doesn't wrap lines).
if (count > 1) {
origViewZones.push({
afterLineNumber: a.originalRange.startLineNumber + i,
afterLineNumber: visibleOriginalLines[i],
domNode: createFakeLinesDiv(),
heightInPx: (count - 1) * modLineHeight,
showInHiddenAreas: true,
Expand All @@ -287,7 +334,7 @@ export class DiffEditorViewZones extends Disposable {
}

const marginDomNode = document.createElement('div');
marginDomNode.className = 'gutter-delete';
marginDomNode.className = changeHighlights ? '' : 'gutter-delete';

origViewZones.push({
afterLineNumber: a.originalRange.endLineNumberExclusive - 1,
Expand Down Expand Up @@ -612,13 +659,14 @@ function getAdditionalLineHeights(editor: CodeEditorWidget, viewZonesToIgnore: R
const viewZoneHeights: { lineNumber: number; heightInPx: number }[] = [];
const wrappingZoneHeights: { lineNumber: number; heightInPx: number }[] = [];

const hasWrapping = editor.getOption(EditorOption.wrappingInfo).wrappingColumn !== -1;
const coordinatesConverter = editor._getViewModel()!.coordinatesConverter;
const editorLineHeight = editor.getOption(EditorOption.lineHeight);
if (hasWrapping) {
{
// Folded lines contribute no height, even when wrapping is disabled.
// Inline alignment must subtract them before sizing the opposite gutter.
for (let i = 1; i <= editor.getModel()!.getLineCount(); i++) {
const lineCount = coordinatesConverter.getModelLineViewLineCount(i);
if (lineCount > 1) {
if (lineCount !== 1) {
wrappingZoneHeights.push({ lineNumber: i, heightInPx: editorLineHeight * (lineCount - 1) });
}
}
Expand Down Expand Up @@ -657,3 +705,18 @@ export function allowsTrueInlineDiffRendering(mapping: DetailedLineRangeMapping)
export function rangeIsSingleLine(range: Range): boolean {
return range.startLineNumber === range.endLineNumber;
}

/** Project existing correspondence through folding and wrapping, without rematching. */
/** `originalBands` and `modifiedBands` are band heights by the band's first hidden line, one-based, on the side that shows it. */
function computeSourceAlignment(original: CodeEditorWidget, modified: CodeEditorWidget, rows: readonly (readonly [number | null, number | null])[], originalZonesToIgnore: ReadonlySet<string>, modifiedZonesToIgnore: ReadonlySet<string>, originalBands: ReadonlyMap<number, number>, modifiedBands: ReadonlyMap<number, number>): ILineRangeAlignment[] {
const leftView = original._getViewModel()!.coordinatesConverter;
const rightView = modified._getViewModel()!.coordinatesConverter;
const leftHeight = original.getOption(EditorOption.lineHeight);
const rightHeight = modified.getOption(EditorOption.lineHeight);
const leftExtra = new Map(getAdditionalLineHeights(original, originalZonesToIgnore).map(info => [info.lineNumber, info.heightInPx]));
const rightExtra = new Map(getAdditionalLineHeights(modified, modifiedZonesToIgnore).map(info => [info.lineNumber, info.heightInPx]));
return projectSourceAlignment(rows,
l => (leftView.getModelLineViewLineCount(l + 1) === 0 ? 0 : leftHeight + (leftExtra.get(l + 1) ?? 0)) + (originalBands.get(l + 1) ?? 0),
r => (rightView.getModelLineViewLineCount(r + 1) === 0 ? 0 : rightHeight + (rightExtra.get(r + 1) ?? 0)) + (modifiedBands.get(r + 1) ?? 0),
).filter(s => s.leftHeight !== s.rightHeight).map(s => ({ originalRange: new LineRange(s.leftStart + 1, s.leftEnd + 1), modifiedRange: new LineRange(s.rightStart + 1, s.rightEnd + 1), originalHeightInPx: s.leftHeight, modifiedHeightInPx: s.rightHeight, diff: undefined }));
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
private readonly _originalTextModel: ITextModel,
private readonly _contextMenuService: IContextMenuService,
private readonly _clipboardService: IClipboardService,
private readonly _originalLineNumbers?: readonly number[],
) {
super();

Expand Down Expand Up @@ -171,7 +172,7 @@ export class InlineDiffDeletedCodeMargin extends Disposable {
for (let i = 0; i < this._renderLinesResult.viewLineCounts.length; i++) {
acc += this._renderLinesResult.viewLineCounts[i];
if (lineNumberOffset < acc) {
return i;
return this._originalLineNumbers ? this._originalLineNumbers[i] - this._diff.original.startLineNumber : i;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ import { getColumnOfNodeOffset } from '../../../../viewParts/viewLines/viewLine.

const ttPolicy = createTrustedTypesPolicy('diffEditorWidget', { createHTML: value => value });

export function renderLines(source: LineSource, options: RenderOptions, decorations: InlineDecoration[], domNode: HTMLElement, noExtra = false): RenderLinesResult {
export function renderLines(source: LineSource, options: RenderOptions, decorations: InlineDecoration[], domNode: HTMLElement, noExtra = false, explicitChangeHighlights = false): RenderLinesResult {
applyFontInfo(domNode, options.fontInfo);

const hasCharChanges = (decorations.length > 0);
// An explicit empty highlight set means unchanged, not a whole-line deletion.
const hasCharChanges = explicitChangeHighlights || (decorations.length > 0);

const sb = new StringBuilder(10000);
let maxCharsPerLine = 0;
Expand Down
Loading