Adds moved code tests

This commit is contained in:
Henning Dieterichs
2023-10-06 12:38:06 +02:00
committed by Henning Dieterichs
parent e82448c344
commit da33fe42cd
17 changed files with 2191 additions and 2 deletions
@@ -42,7 +42,7 @@ suite('diffing fixtures', () => {
const diffingAlgo = diffingAlgoName === 'legacy' ? new LegacyLinesDiffComputer() : new DefaultLinesDiffComputer();
const ignoreTrimWhitespace = folder.indexOf('trimws') >= 0;
const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false });
const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: true });
function getDiffs(changes: readonly DetailedLineRangeMapping[]): IDetailedDiff[] {
return changes.map<IDetailedDiff>(c => ({
@@ -155,5 +155,5 @@ interface IMoveInfo {
originalRange: string; // [startLineNumber, endLineNumberExclusive)
modifiedRange: string; // [startLineNumber, endLineNumberExclusive)
changes?: IDetailedDiff[];
changes: IDetailedDiff[];
}
@@ -112,5 +112,113 @@
}
]
}
],
"moves": [
{
"originalRange": "[73,96)",
"modifiedRange": "[75,98)",
"changes": [
{
"originalRange": "[73,85)",
"modifiedRange": "[75,87)",
"innerChanges": [
{
"originalRange": "[73,1 -> 73,1]",
"modifiedRange": "[75,1 -> 75,2]"
},
{
"originalRange": "[74,1 -> 74,1]",
"modifiedRange": "[76,1 -> 76,2]"
},
{
"originalRange": "[75,1 -> 75,1]",
"modifiedRange": "[77,1 -> 77,2]"
},
{
"originalRange": "[76,1 -> 76,1]",
"modifiedRange": "[78,1 -> 78,2]"
},
{
"originalRange": "[77,1 -> 77,1]",
"modifiedRange": "[79,1 -> 79,2]"
},
{
"originalRange": "[78,1 -> 78,1]",
"modifiedRange": "[80,1 -> 80,2]"
},
{
"originalRange": "[79,1 -> 79,1]",
"modifiedRange": "[81,1 -> 81,2]"
},
{
"originalRange": "[80,1 -> 80,1]",
"modifiedRange": "[82,1 -> 82,2]"
},
{
"originalRange": "[81,1 -> 81,1]",
"modifiedRange": "[83,1 -> 83,2]"
},
{
"originalRange": "[82,1 -> 82,1]",
"modifiedRange": "[84,1 -> 84,2]"
},
{
"originalRange": "[83,1 -> 83,1]",
"modifiedRange": "[85,1 -> 85,2]"
},
{
"originalRange": "[84,1 -> 84,1]",
"modifiedRange": "[86,1 -> 86,2]"
}
]
},
{
"originalRange": "[86,96)",
"modifiedRange": "[88,98)",
"innerChanges": [
{
"originalRange": "[86,1 -> 86,1]",
"modifiedRange": "[88,1 -> 88,2]"
},
{
"originalRange": "[87,1 -> 87,1]",
"modifiedRange": "[89,1 -> 89,2]"
},
{
"originalRange": "[88,1 -> 88,1]",
"modifiedRange": "[90,1 -> 90,2]"
},
{
"originalRange": "[89,1 -> 89,1]",
"modifiedRange": "[91,1 -> 91,2]"
},
{
"originalRange": "[90,1 -> 90,1]",
"modifiedRange": "[92,1 -> 92,2]"
},
{
"originalRange": "[91,1 -> 91,1]",
"modifiedRange": "[93,1 -> 93,2]"
},
{
"originalRange": "[92,1 -> 92,1]",
"modifiedRange": "[94,1 -> 94,2]"
},
{
"originalRange": "[93,1 -> 93,1]",
"modifiedRange": "[95,1 -> 95,2]"
},
{
"originalRange": "[94,1 -> 94,1]",
"modifiedRange": "[96,1 -> 96,2]"
},
{
"originalRange": "[95,1 -> 95,1]",
"modifiedRange": "[97,1 -> 97,2]"
}
]
}
]
}
]
}
@@ -0,0 +1,116 @@
{
"original": {
"content": "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { CompareResult } from 'vs/base/common/arrays';\nimport { autorun, derived } from 'vs/base/common/observable';\nimport { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from 'vs/editor/common/model';\nimport { localize } from 'vs/nls';\nimport { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';\nimport { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange';\nimport { applyObservableDecorations, join } from 'vs/workbench/contrib/mergeEditor/browser/utils';\nimport { handledConflictMinimapOverViewRulerColor, unhandledConflictMinimapOverViewRulerColor } from 'vs/workbench/contrib/mergeEditor/browser/view/colors';\nimport { CodeEditorView } from './codeEditorView';\n\nexport class ResultCodeEditorView extends CodeEditorView {\n\tprivate readonly decorations = derived('result.decorations', reader => {\n\t\tconst viewModel = this.viewModel.read(reader);\n\t\tif (!viewModel) {\n\t\t\treturn [];\n\t\t}\n\t\tconst model = viewModel.model;\n\t\tconst result = new Array<IModelDeltaDecoration>();\n\n\t\tconst baseRangeWithStoreAndTouchingDiffs = join(\n\t\t\tmodel.modifiedBaseRanges.read(reader),\n\t\t\tmodel.resultDiffs.read(reader),\n\t\t\t(baseRange, diff) => baseRange.baseRange.touches(diff.inputRange)\n\t\t\t\t? CompareResult.neitherLessOrGreaterThan\n\t\t\t\t: LineRange.compareByStart(\n\t\t\t\t\tbaseRange.baseRange,\n\t\t\t\t\tdiff.inputRange\n\t\t\t\t)\n\t\t);\n\n\t\tconst activeModifiedBaseRange = viewModel.activeModifiedBaseRange.read(reader);\n\n\t\tfor (const m of baseRangeWithStoreAndTouchingDiffs) {\n\t\t\tconst modifiedBaseRange = m.left;\n\n\t\t\tif (modifiedBaseRange) {\n\t\t\t\tconst range = model.getRangeInResult(modifiedBaseRange.baseRange, reader).toInclusiveRange();\n\t\t\t\tif (range) {\n\t\t\t\t\tconst blockClassNames = ['merge-editor-block'];\n\t\t\t\t\tconst isHandled = model.isHandled(modifiedBaseRange).read(reader);\n\t\t\t\t\tif (isHandled) {\n\t\t\t\t\t\tblockClassNames.push('handled');\n\t\t\t\t\t}\n\t\t\t\t\tif (modifiedBaseRange === activeModifiedBaseRange) {\n\t\t\t\t\t\tblockClassNames.push('focused');\n\t\t\t\t\t}\n\t\t\t\t\tblockClassNames.push('result');\n\n\t\t\t\t\tresult.push({\n\t\t\t\t\t\trange,\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tisWholeLine: true,\n\t\t\t\t\t\t\tblockClassName: blockClassNames.join(' '),\n\t\t\t\t\t\t\tdescription: 'Result Diff',\n\t\t\t\t\t\t\tminimap: {\n\t\t\t\t\t\t\t\tposition: MinimapPosition.Gutter,\n\t\t\t\t\t\t\t\tcolor: { id: isHandled ? handledConflictMinimapOverViewRulerColor : unhandledConflictMinimapOverViewRulerColor },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\toverviewRuler: {\n\t\t\t\t\t\t\t\tposition: OverviewRulerLane.Center,\n\t\t\t\t\t\t\t\tcolor: { id: isHandled ? handledConflictMinimapOverViewRulerColor : unhandledConflictMinimapOverViewRulerColor },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const diff of m.rights) {\n\t\t\t\tconst range = diff.outputRange.toInclusiveRange();\n\t\t\t\tif (range) {\n\t\t\t\t\tresult.push({\n\t\t\t\t\t\trange,\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tclassName: `merge-editor-diff result`,\n\t\t\t\t\t\t\tdescription: 'Merge Editor',\n\t\t\t\t\t\t\tisWholeLine: true,\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tif (diff.rangeMappings) {\n\t\t\t\t\tfor (const d of diff.rangeMappings) {\n\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\trange: d.outputRange,\n\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\tclassName: `merge-editor-diff-word result`,\n\t\t\t\t\t\t\t\tdescription: 'Merge Editor'\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t});\n\n\tconstructor(\n\t\t@IInstantiationService instantiationService: IInstantiationService\n\t) {\n\t\tsuper(instantiationService);\n\n\t\tthis._register(applyObservableDecorations(this.editor, this.decorations));\n\n\n\t\tthis._register(autorun('update remainingConflicts label', reader => {\n\t\t\tconst model = this.model.read(reader);\n\t\t\tif (!model) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst count = model.unhandledConflictsCount.read(reader);\n\n\t\t\tthis.htmlElements.detail.innerText = count === 1\n\t\t\t\t? localize(\n\t\t\t\t\t'mergeEditor.remainingConflicts',\n\t\t\t\t\t'{0} Conflict Remaining',\n\t\t\t\t\tcount\n\t\t\t\t)\n\t\t\t\t: localize(\n\t\t\t\t\t'mergeEditor.remainingConflict',\n\t\t\t\t\t'{0} Conflicts Remaining ',\n\t\t\t\t\tcount\n\t\t\t\t);\n\n\t\t}));\n\t}\n}\n",
"fileName": "./1.tst"
},
"modified": {
"content": "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport { CompareResult } from 'vs/base/common/arrays';\nimport { autorun, derived } from 'vs/base/common/observable';\nimport { IModelDeltaDecoration, MinimapPosition, OverviewRulerLane } from 'vs/editor/common/model';\nimport { localize } from 'vs/nls';\nimport { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';\nimport { LineRange } from 'vs/workbench/contrib/mergeEditor/browser/model/lineRange';\nimport { applyObservableDecorations, join } from 'vs/workbench/contrib/mergeEditor/browser/utils';\nimport { handledConflictMinimapOverViewRulerColor, unhandledConflictMinimapOverViewRulerColor } from 'vs/workbench/contrib/mergeEditor/browser/view/colors';\nimport { CodeEditorView } from './codeEditorView';\n\nexport class ResultCodeEditorView extends CodeEditorView {\n\tprivate readonly decorations = derived('result.decorations', reader => {\n\t\tconst viewModel = this.viewModel.read(reader);\n\t\tif (!viewModel) {\n\t\t\treturn [];\n\t\t}\n\t\tconst model = viewModel.model;\n\t\tconst result = new Array<IModelDeltaDecoration>();\n\n\t\tconst baseRangeWithStoreAndTouchingDiffs = join(\n\t\t\tmodel.modifiedBaseRanges.read(reader),\n\t\t\tmodel.resultDiffs.read(reader),\n\t\t\t(baseRange, diff) => baseRange.baseRange.touches(diff.inputRange)\n\t\t\t\t? CompareResult.neitherLessOrGreaterThan\n\t\t\t\t: LineRange.compareByStart(\n\t\t\t\t\tbaseRange.baseRange,\n\t\t\t\t\tdiff.inputRange\n\t\t\t\t)\n\t\t);\n\n\t\tconst activeModifiedBaseRange = viewModel.activeModifiedBaseRange.read(reader);\n\n\t\tfor (const m of baseRangeWithStoreAndTouchingDiffs) {\n\t\t\tconst modifiedBaseRange = m.left;\n\n\t\t\tif (modifiedBaseRange) {\n\t\t\t\tconst range = model.getRangeInResult(modifiedBaseRange.baseRange, reader).toInclusiveRange();\n\t\t\t\tif (range) {\n\t\t\t\t\tconst blockClassNames = ['merge-editor-block'];\n\t\t\t\t\tconst isHandled = model.isHandled(modifiedBaseRange).read(reader);\n\t\t\t\t\tif (isHandled) {\n\t\t\t\t\t\tblockClassNames.push('handled');\n\t\t\t\t\t}\n\t\t\t\t\tif (modifiedBaseRange === activeModifiedBaseRange) {\n\t\t\t\t\t\tblockClassNames.push('focused');\n\t\t\t\t\t}\n\t\t\t\t\tblockClassNames.push('result');\n\n\t\t\t\t\tresult.push({\n\t\t\t\t\t\trange,\n\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\tisWholeLine: true,\n\t\t\t\t\t\t\tblockClassName: blockClassNames.join(' '),\n\t\t\t\t\t\t\tdescription: 'Result Diff',\n\t\t\t\t\t\t\tminimap: {\n\t\t\t\t\t\t\t\tposition: MinimapPosition.Gutter,\n\t\t\t\t\t\t\t\tcolor: { id: isHandled ? handledConflictMinimapOverViewRulerColor : unhandledConflictMinimapOverViewRulerColor },\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\toverviewRuler: {\n\t\t\t\t\t\t\t\tposition: OverviewRulerLane.Center,\n\t\t\t\t\t\t\t\tcolor: { id: isHandled ? handledConflictMinimapOverViewRulerColor : unhandledConflictMinimapOverViewRulerColor },\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t\tif (!modifiedBaseRange || modifiedBaseRange.isConflicting) {\n\t\t\t\tfor (const diff of m.rights) {\n\t\t\t\t\tconst range = diff.outputRange.toInclusiveRange();\n\t\t\t\t\tif (range) {\n\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\trange,\n\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\tclassName: `merge-editor-diff result`,\n\t\t\t\t\t\t\t\tdescription: 'Merge Editor',\n\t\t\t\t\t\t\t\tisWholeLine: true,\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\tif (diff.rangeMappings) {\n\t\t\t\t\t\tfor (const d of diff.rangeMappings) {\n\t\t\t\t\t\t\tresult.push({\n\t\t\t\t\t\t\t\trange: d.outputRange,\n\t\t\t\t\t\t\t\toptions: {\n\t\t\t\t\t\t\t\t\tclassName: `merge-editor-diff-word result`,\n\t\t\t\t\t\t\t\t\tdescription: 'Merge Editor'\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t});\n\n\tconstructor(\n\t\t@IInstantiationService instantiationService: IInstantiationService\n\t) {\n\t\tsuper(instantiationService);\n\n\t\tthis._register(applyObservableDecorations(this.editor, this.decorations));\n\n\n\t\tthis._register(autorun('update remainingConflicts label', reader => {\n\t\t\tconst model = this.model.read(reader);\n\t\t\tif (!model) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst count = model.unhandledConflictsCount.read(reader);\n\n\t\t\tthis.htmlElements.detail.innerText = count === 1\n\t\t\t\t? localize(\n\t\t\t\t\t'mergeEditor.remainingConflicts',\n\t\t\t\t\t'{0} Conflict Remaining',\n\t\t\t\t\tcount\n\t\t\t\t)\n\t\t\t\t: localize(\n\t\t\t\t\t'mergeEditor.remainingConflict',\n\t\t\t\t\t'{0} Conflicts Remaining ',\n\t\t\t\t\tcount\n\t\t\t\t);\n\n\t\t}));\n\t}\n}\n",
"fileName": "./2.tst"
},
"diffs": [
{
"originalRange": "[73,85)",
"modifiedRange": "[73,87)",
"innerChanges": [
{
"originalRange": "[73,1 -> 73,1]",
"modifiedRange": "[73,1 -> 75,1]"
},
{
"originalRange": "[73,1 -> 73,1]",
"modifiedRange": "[75,1 -> 75,2]"
},
{
"originalRange": "[74,1 -> 74,1]",
"modifiedRange": "[76,1 -> 76,2]"
},
{
"originalRange": "[75,1 -> 75,1]",
"modifiedRange": "[77,1 -> 77,2]"
},
{
"originalRange": "[76,1 -> 76,1]",
"modifiedRange": "[78,1 -> 78,2]"
},
{
"originalRange": "[77,1 -> 77,1]",
"modifiedRange": "[79,1 -> 79,2]"
},
{
"originalRange": "[78,1 -> 78,1]",
"modifiedRange": "[80,1 -> 80,2]"
},
{
"originalRange": "[79,1 -> 79,1]",
"modifiedRange": "[81,1 -> 81,2]"
},
{
"originalRange": "[80,1 -> 80,1]",
"modifiedRange": "[82,1 -> 82,2]"
},
{
"originalRange": "[81,1 -> 81,1]",
"modifiedRange": "[83,1 -> 83,2]"
},
{
"originalRange": "[82,1 -> 82,1]",
"modifiedRange": "[84,1 -> 84,2]"
},
{
"originalRange": "[83,1 -> 83,1]",
"modifiedRange": "[85,1 -> 85,2]"
},
{
"originalRange": "[84,1 -> 84,1]",
"modifiedRange": "[86,1 -> 86,2]"
}
]
},
{
"originalRange": "[86,95)",
"modifiedRange": "[88,98)",
"innerChanges": [
{
"originalRange": "[86,1 -> 86,1]",
"modifiedRange": "[88,1 -> 88,2]"
},
{
"originalRange": "[87,1 -> 87,1]",
"modifiedRange": "[89,1 -> 89,2]"
},
{
"originalRange": "[88,1 -> 88,1]",
"modifiedRange": "[90,1 -> 90,2]"
},
{
"originalRange": "[89,1 -> 89,1]",
"modifiedRange": "[91,1 -> 91,2]"
},
{
"originalRange": "[90,1 -> 90,1]",
"modifiedRange": "[92,1 -> 92,2]"
},
{
"originalRange": "[91,1 -> 91,1]",
"modifiedRange": "[93,1 -> 93,2]"
},
{
"originalRange": "[92,1 -> 92,1]",
"modifiedRange": "[94,1 -> 94,2]"
},
{
"originalRange": "[93,1 -> 93,1]",
"modifiedRange": "[95,1 -> 95,2]"
},
{
"originalRange": "[94,1 -> 94,1]",
"modifiedRange": "[96,1 -> 96,2]"
},
{
"originalRange": "[95,1 -> 95,1]",
"modifiedRange": "[97,1 -> 98,1]"
}
]
}
]
}
@@ -48,5 +48,31 @@
}
]
}
],
"moves": [
{
"originalRange": "[223,226)",
"modifiedRange": "[158,161)",
"changes": [
{
"originalRange": "[223,226)",
"modifiedRange": "[158,161)",
"innerChanges": [
{
"originalRange": "[223,1 -> 223,3]",
"modifiedRange": "[158,1 -> 158,1]"
},
{
"originalRange": "[224,1 -> 224,3]",
"modifiedRange": "[159,1 -> 159,1]"
},
{
"originalRange": "[225,1 -> 225,3]",
"modifiedRange": "[160,1 -> 160,1]"
}
]
}
]
}
]
}
File diff suppressed because one or more lines are too long
@@ -80,5 +80,67 @@
}
]
}
],
"moves": [
{
"originalRange": "[12,24)",
"modifiedRange": "[13,25)",
"changes": [
{
"originalRange": "[12,24)",
"modifiedRange": "[13,25)",
"innerChanges": [
{
"originalRange": "[12,1 -> 12,1]",
"modifiedRange": "[13,1 -> 13,2]"
},
{
"originalRange": "[13,1 -> 13,1]",
"modifiedRange": "[14,1 -> 14,2]"
},
{
"originalRange": "[14,1 -> 14,1]",
"modifiedRange": "[15,1 -> 15,2]"
},
{
"originalRange": "[15,1 -> 15,1]",
"modifiedRange": "[16,1 -> 16,2]"
},
{
"originalRange": "[16,1 -> 16,1]",
"modifiedRange": "[17,1 -> 17,2]"
},
{
"originalRange": "[17,1 -> 17,1]",
"modifiedRange": "[18,1 -> 18,2]"
},
{
"originalRange": "[18,1 -> 18,1]",
"modifiedRange": "[19,1 -> 19,2]"
},
{
"originalRange": "[19,1 -> 19,1]",
"modifiedRange": "[20,1 -> 20,2]"
},
{
"originalRange": "[20,1 -> 20,1]",
"modifiedRange": "[21,1 -> 21,2]"
},
{
"originalRange": "[21,1 -> 21,1]",
"modifiedRange": "[22,1 -> 22,2]"
},
{
"originalRange": "[22,1 -> 22,1]",
"modifiedRange": "[23,1 -> 23,2]"
},
{
"originalRange": "[23,1 -> 23,1]",
"modifiedRange": "[24,1 -> 24,2]"
}
]
}
]
}
]
}
@@ -0,0 +1,84 @@
{
"original": {
"content": "export function lineRangeMappingFromRangeMappings(alignments: RangeMapping[]): LineRangeMapping[] {\n\tconst changes: LineRangeMapping[] = [];\n\tfor (const g of group(\n\t\talignments,\n\t\t(a1, a2) =>\n\t\t\t(a2.originalRange.startLineNumber - (a1.originalRange.endLineNumber - (a1.originalRange.endColumn > 1 ? 0 : 1)) <= 1)\n\t\t\t|| (a2.modifiedRange.startLineNumber - (a1.modifiedRange.endLineNumber - (a1.modifiedRange.endColumn > 1 ? 0 : 1)) <= 1)\n\t)) {\n\t\tconst first = g[0];\n\t\tconst last = g[g.length - 1];\n\n\t\tchanges.push(new LineRangeMapping(\n\t\t\tnew LineRange(\n\t\t\t\tfirst.originalRange.startLineNumber,\n\t\t\t\tlast.originalRange.endLineNumber + (last.originalRange.endColumn > 1 || last.modifiedRange.endColumn > 1 ? 1 : 0)\n\t\t\t),\n\t\t\tnew LineRange(\n\t\t\t\tfirst.modifiedRange.startLineNumber,\n\t\t\t\tlast.modifiedRange.endLineNumber + (last.originalRange.endColumn > 1 || last.modifiedRange.endColumn > 1 ? 1 : 0)\n\t\t\t),\n\t\t\tg\n\t\t));\n\t}\n\n\tassertFn(() => {\n\t\treturn checkAdjacentItems(changes,\n\t\t\t(m1, m2) => m2.originalRange.startLineNumber - m1.originalRange.endLineNumberExclusive === m2.modifiedRange.startLineNumber - m1.modifiedRange.endLineNumberExclusive &&\n\t\t\t\t// There has to be an unchanged line in between (otherwise both diffs should have been joined)\n\t\t\t\tm1.originalRange.endLineNumberExclusive < m2.originalRange.startLineNumber &&\n\t\t\t\tm1.modifiedRange.endLineNumberExclusive < m2.modifiedRange.startLineNumber,\n\t\t);\n\t});\n\n\n\treturn changes;\n}",
"fileName": "./1.tst"
},
"modified": {
"content": "export function lineRangeMappingFromRangeMappings(alignments: RangeMapping[]): LineRangeMapping[] {\n\tconst changes: LineRangeMapping[] = [];\n\tfor (const g of group(\n\t\talignments,\n\t\t(a1, a2) =>\n\t\t\t(a2.originalRange.startLineNumber - (a1.originalRange.endLineNumber - (a1.originalRange.endColumn > 1 ? 0 : 1)) <= 1)\n\t\t\t|| (a2.modifiedRange.startLineNumber - (a1.modifiedRange.endLineNumber - (a1.modifiedRange.endColumn > 1 ? 0 : 1)) <= 1)\n\t)) {\n\t\tif (true) {\n\t\t\tconst first = g[0];\n\t\t\tconst last = g[g.length - 1];\n\n\t\t\tchanges.push(new LineRangeMapping(\n\t\t\t\tnew LineRange(\n\t\t\t\t\tfirst.originalRange.startLineNumber,\n\t\t\t\t\tlast.originalRange.endLineNumber + (last.originalRange.endColumn > 1 || last.modifiedRange.endColumn > 1 ? 1 : 0)\n\t\t\t\t),\n\t\t\t\tnew LineRange(\n\t\t\t\t\tfirst.modifiedRange.startLineNumber,\n\t\t\t\t\tlast.modifiedRange.endLineNumber + (last.originalRange.endColumn > 1 || last.modifiedRange.endColumn > 1 ? 1 : 0)\n\t\t\t\t),\n\t\t\t\tg\n\t\t\t));\n\t\t}\n\t}\n\n\tassertFn(() => {\n\t\treturn checkAdjacentItems(changes,\n\t\t\t(m1, m2) => m2.originalRange.startLineNumber - m1.originalRange.endLineNumberExclusive === m2.modifiedRange.startLineNumber - m1.modifiedRange.endLineNumberExclusive &&\n\t\t\t\t// There has to be an unchanged line in between (otherwise both diffs should have been joined)\n\t\t\t\tm1.originalRange.endLineNumberExclusive < m2.originalRange.startLineNumber &&\n\t\t\t\tm1.modifiedRange.endLineNumberExclusive < m2.modifiedRange.startLineNumber,\n\t\t);\n\t});\n\n\n\treturn changes;\n}",
"fileName": "./2.tst"
},
"diffs": [
{
"originalRange": "[9,11)",
"modifiedRange": "[9,12)",
"innerChanges": [
{
"originalRange": "[9,1 -> 9,1]",
"modifiedRange": "[9,1 -> 10,1]"
},
{
"originalRange": "[9,1 -> 9,1]",
"modifiedRange": "[10,1 -> 10,2]"
},
{
"originalRange": "[10,1 -> 10,1]",
"modifiedRange": "[11,1 -> 11,2]"
}
]
},
{
"originalRange": "[12,23)",
"modifiedRange": "[13,25)",
"innerChanges": [
{
"originalRange": "[12,1 -> 12,1]",
"modifiedRange": "[13,1 -> 13,2]"
},
{
"originalRange": "[13,1 -> 13,1]",
"modifiedRange": "[14,1 -> 14,2]"
},
{
"originalRange": "[14,1 -> 14,1]",
"modifiedRange": "[15,1 -> 15,2]"
},
{
"originalRange": "[15,1 -> 15,1]",
"modifiedRange": "[16,1 -> 16,2]"
},
{
"originalRange": "[16,1 -> 16,1]",
"modifiedRange": "[17,1 -> 17,2]"
},
{
"originalRange": "[17,1 -> 17,1]",
"modifiedRange": "[18,1 -> 18,2]"
},
{
"originalRange": "[18,1 -> 18,1]",
"modifiedRange": "[19,1 -> 19,2]"
},
{
"originalRange": "[19,1 -> 19,1]",
"modifiedRange": "[20,1 -> 20,2]"
},
{
"originalRange": "[20,1 -> 20,1]",
"modifiedRange": "[21,1 -> 21,2]"
},
{
"originalRange": "[21,1 -> 21,1]",
"modifiedRange": "[22,1 -> 22,2]"
},
{
"originalRange": "[22,1 -> 22,1]",
"modifiedRange": "[23,1 -> 23,2]"
},
{
"originalRange": "[23,1 -> 23,1]",
"modifiedRange": "[24,1 -> 25,1]"
}
]
}
]
}
@@ -42,5 +42,47 @@
}
]
}
],
"moves": [
{
"originalRange": "[26,33)",
"modifiedRange": "[35,42)",
"changes": [
{
"originalRange": "[26,33)",
"modifiedRange": "[35,42)",
"innerChanges": [
{
"originalRange": "[26,3 -> 26,10]",
"modifiedRange": "[35,3 -> 35,4]"
},
{
"originalRange": "[27,1 -> 27,1]",
"modifiedRange": "[36,1 -> 36,2]"
},
{
"originalRange": "[28,1 -> 28,1]",
"modifiedRange": "[37,1 -> 37,2]"
},
{
"originalRange": "[29,1 -> 29,1]",
"modifiedRange": "[38,1 -> 38,2]"
},
{
"originalRange": "[30,1 -> 30,1]",
"modifiedRange": "[39,1 -> 39,2]"
},
{
"originalRange": "[31,1 -> 31,1]",
"modifiedRange": "[40,1 -> 40,2]"
},
{
"originalRange": "[32,1 -> 32,1]",
"modifiedRange": "[41,1 -> 41,2]"
}
]
}
]
}
]
}
@@ -0,0 +1,46 @@
{
"original": {
"content": "\n\tprivate doAddView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {\n\t\tif (this.state !== State.Idle) {\n\t\t\tthrow new Error('Cant modify splitview');\n\t\t}\n\n\t\tthis.state = State.Busy;\n\n\t\t// Add view\n\t\tconst container = $('.split-view-view');\n\n\t\tif (index === this.viewItems.length) {\n\t\t\tthis.viewContainer.appendChild(container);\n\t\t} else {\n\t\t\tthis.viewContainer.insertBefore(container, this.viewContainer.children.item(index));\n\t\t}\n\n\t\tconst onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size));\n\t\tconst containerDisposable = toDisposable(() => this.viewContainer.removeChild(container));\n\t\tconst disposable = combinedDisposable(onChangeDisposable, containerDisposable);\n\n\t\tlet viewSize: ViewItemSize;\n\n\t\tif (typeof size === 'number') {\n\t\t\tviewSize = size;\n\t\t} else if (size.type === 'split') {\n\t\t\tviewSize = this.getViewSize(size.index) / 2;\n\t\t} else if (size.type === 'invisible') {\n\t\t\tviewSize = { cachedVisibleSize: size.cachedVisibleSize };\n\t\t} else {\n\t\t\tviewSize = view.minimumSize;\n\t\t}\n\n\t\tconst item = this.orientation === Orientation.VERTICAL\n\t\t\t? new VerticalViewItem(container, view, viewSize, disposable)\n\t\t\t: new HorizontalViewItem(container, view, viewSize, disposable);\n\n\t\tthis.viewItems.splice(index, 0, item);\n\n",
"fileName": "./1.txt"
},
"modified": {
"content": "\n\tprivate doAddView(view: IView<TLayoutContext>, size: number | Sizing, index = this.viewItems.length, skipLayout?: boolean): void {\n\t\tif (this.state !== State.Idle) {\n\t\t\tthrow new Error('Cant modify splitview');\n\t\t}\n\n\t\tthis.state = State.Busy;\n\n\t\t// Add view\n\t\tconst container = $('.split-view-view');\n\n\t\tif (index === this.viewItems.length) {\n\t\t\tthis.viewContainer.appendChild(container);\n\t\t} else {\n\t\t\tthis.viewContainer.insertBefore(container, this.viewContainer.children.item(index));\n\t\t}\n\n\t\tconst onChangeDisposable = view.onDidChange(size => this.onViewChange(item, size));\n\t\tconst containerDisposable = toDisposable(() => this.viewContainer.removeChild(container));\n\t\tconst disposable = combinedDisposable(onChangeDisposable, containerDisposable);\n\n\t\tlet viewSize: ViewItemSize;\n\n\t\tif (typeof size === 'number') {\n\t\t\tviewSize = size;\n\t\t} else {\n\t\t\tif (size.type === 'auto') {\n\t\t\t\tif (this.areViewsDistributed()) {\n\t\t\t\t\tsize = { type: 'distribute' };\n\t\t\t\t} else {\n\t\t\t\t\tsize = { type: 'split', index: size.index };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (size.type === 'split') {\n\t\t\t\tviewSize = this.getViewSize(size.index) / 2;\n\t\t\t} else if (size.type === 'invisible') {\n\t\t\t\tviewSize = { cachedVisibleSize: size.cachedVisibleSize };\n\t\t\t} else {\n\t\t\t\tviewSize = view.minimumSize;\n\t\t\t}\n\t\t}\n\n\t\tconst item = this.orientation === Orientation.VERTICAL\n\t\t\t? new VerticalViewItem(container, view, viewSize, disposable)\n\t\t\t: new HorizontalViewItem(container, view, viewSize, disposable);\n\n\t\tthis.viewItems.splice(index, 0, item);\n\n",
"fileName": "./2.txt"
},
"diffs": [
{
"originalRange": "[26,32)",
"modifiedRange": "[26,42)",
"innerChanges": [
{
"originalRange": "[26,10 -> 26,10]",
"modifiedRange": "[26,10 -> 35,4]"
},
{
"originalRange": "[27,1 -> 27,1]",
"modifiedRange": "[36,1 -> 36,2]"
},
{
"originalRange": "[28,1 -> 28,1]",
"modifiedRange": "[37,1 -> 37,2]"
},
{
"originalRange": "[29,1 -> 29,1]",
"modifiedRange": "[38,1 -> 38,2]"
},
{
"originalRange": "[30,1 -> 30,1]",
"modifiedRange": "[39,1 -> 39,2]"
},
{
"originalRange": "[31,1 -> 31,1]",
"modifiedRange": "[40,1 -> 40,2]"
},
{
"originalRange": "[32,1 -> 32,1]",
"modifiedRange": "[41,1 -> 42,1]"
}
]
}
]
}
@@ -28,5 +28,23 @@
}
]
}
],
"moves": [
{
"originalRange": "[24,28)",
"modifiedRange": "[70,74)",
"changes": [
{
"originalRange": "[26,27)",
"modifiedRange": "[72,73)",
"innerChanges": [
{
"originalRange": "[26,36 -> 26,40 EOL]",
"modifiedRange": "[72,36 -> 72,40 EOL]"
}
]
}
]
}
]
}
@@ -0,0 +1,32 @@
{
"original": {
"content": "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport * as arrays from 'vs/base/common/arrays';\nimport { IdleDeadline, runWhenIdle } from 'vs/base/common/async';\nimport { BugIndicatingError, onUnexpectedError } from 'vs/base/common/errors';\nimport { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';\nimport { setTimeout0 } from 'vs/base/common/platform';\nimport { StopWatch } from 'vs/base/common/stopwatch';\nimport { countEOL } from 'vs/editor/common/core/eolCounter';\nimport { Position } from 'vs/editor/common/core/position';\nimport { IRange } from 'vs/editor/common/core/range';\nimport { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes';\nimport { EncodedTokenizationResult, IBackgroundTokenizationStore, IBackgroundTokenizer, ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/languages';\nimport { nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize';\nimport { ITextModel } from 'vs/editor/common/model';\nimport { TextModel } from 'vs/editor/common/model/textModel';\nimport { TokenizationTextModelPart } from 'vs/editor/common/model/tokenizationTextModelPart';\nimport { IModelContentChangedEvent, IModelLanguageChangedEvent } from 'vs/editor/common/textModelEvents';\nimport { ContiguousMultilineTokensBuilder } from 'vs/editor/common/tokens/contiguousMultilineTokensBuilder';\nimport { LineTokens } from 'vs/editor/common/tokens/lineTokens';\n\nconst enum Constants {\n\tCHEAP_TOKENIZATION_LENGTH_LIMIT = 2048\n}\n\n/**\n * An array that avoids being sparse by always\n * filling up unused indices with a default value.\n */\nexport class ContiguousGrowingArray<T> {\n\n\tprivate _store: T[] = [];\n\n\tconstructor(\n\t\tprivate readonly _default: T\n\t) { }\n\n\tpublic get(index: number): T {\n\t\tif (index < this._store.length) {\n\t\t\treturn this._store[index];\n\t\t}\n\t\treturn this._default;\n\t}\n\n\tpublic set(index: number, value: T): void {\n\t\twhile (index >= this._store.length) {\n\t\t\tthis._store[this._store.length] = this._default;\n\t\t}\n\t\tthis._store[index] = value;\n\t}\n\n\t// TODO have `replace` instead of `delete` and `insert`\n\tpublic delete(deleteIndex: number, deleteCount: number): void {\n\t\tif (deleteCount === 0 || deleteIndex >= this._store.length) {\n\t\t\treturn;\n\t\t}\n\t\tthis._store.splice(deleteIndex, deleteCount);\n\t}\n\n\tpublic insert(insertIndex: number, insertCount: number): void {\n\t\tif (insertCount === 0 || insertIndex >= this._store.length) {\n\t\t\treturn;\n\t\t}\n\t\tconst arr: T[] = [];\n\t\tfor (let i = 0; i < insertCount; i++) {\n\t\t\tarr[i] = this._default;\n\t\t}\n\t\tthis._store = arrays.arrayInsert(this._store, insertIndex, arr);\n\t}\n}\n\n/**\n * Stores the states at the start of each line and keeps track of which lines\n * must be re-tokenized. Also uses state equality to quickly validate lines\n * that don't need to be re-tokenized.\n *\n * For example, when typing on a line, the line gets marked as needing to be tokenized.\n * Once the line is tokenized, the end state is checked for equality against the begin\n * state of the next line. If the states are equal, tokenization doesn't need to run\n * again over the rest of the file. If the states are not equal, the next line gets marked\n * as needing to be tokenized.\n */\nexport class TokenizationStateStore {\n\trequestTokens(startLineNumber: number, endLineNumberExclusive: number): void {\n\t\tfor (let lineNumber = startLineNumber; lineNumber < endLineNumberExclusive; lineNumber++) {\n\t\t\tthis._stateStore.markMustBeTokenized(lineNumber - 1);\n\t\t}\n\t}\n}\n",
"fileName": "./1.tst"
},
"modified": {
"content": "/*---------------------------------------------------------------------------------------------\n * Copyright (c) Microsoft Corporation. All rights reserved.\n * Licensed under the MIT License. See License.txt in the project root for license information.\n *--------------------------------------------------------------------------------------------*/\n\nimport * as arrays from 'vs/base/common/arrays';\nimport { IdleDeadline, runWhenIdle } from 'vs/base/common/async';\nimport { BugIndicatingError, onUnexpectedError } from 'vs/base/common/errors';\nimport { Disposable, MutableDisposable } from 'vs/base/common/lifecycle';\nimport { setTimeout0 } from 'vs/base/common/platform';\nimport { StopWatch } from 'vs/base/common/stopwatch';\nimport { countEOL } from 'vs/editor/common/core/eolCounter';\nimport { Position } from 'vs/editor/common/core/position';\nimport { IRange } from 'vs/editor/common/core/range';\nimport { StandardTokenType } from 'vs/editor/common/encodedTokenAttributes';\nimport { EncodedTokenizationResult, IBackgroundTokenizationStore, IBackgroundTokenizer, ILanguageIdCodec, IState, ITokenizationSupport, TokenizationRegistry } from 'vs/editor/common/languages';\nimport { nullTokenizeEncoded } from 'vs/editor/common/languages/nullTokenize';\nimport { ITextModel } from 'vs/editor/common/model';\nimport { TextModel } from 'vs/editor/common/model/textModel';\nimport { TokenizationTextModelPart } from 'vs/editor/common/model/tokenizationTextModelPart';\nimport { IModelContentChangedEvent, IModelLanguageChangedEvent } from 'vs/editor/common/textModelEvents';\nimport { ContiguousMultilineTokensBuilder } from 'vs/editor/common/tokens/contiguousMultilineTokensBuilder';\nimport { LineTokens } from 'vs/editor/common/tokens/lineTokens';\n\n/**\n * An array that avoids being sparse by always\n * filling up unused indices with a default value.\n */\nexport class ContiguousGrowingArray<T> {\n\n\tprivate _store: T[] = [];\n\n\tconstructor(\n\t\tprivate readonly _default: T\n\t) { }\n\n\tpublic get(index: number): T {\n\t\tif (index < this._store.length) {\n\t\t\treturn this._store[index];\n\t\t}\n\t\treturn this._default;\n\t}\n\n\tpublic set(index: number, value: T): void {\n\t\twhile (index >= this._store.length) {\n\t\t\tthis._store[this._store.length] = this._default;\n\t\t}\n\t\tthis._store[index] = value;\n\t}\n\n\t// TODO have `replace` instead of `delete` and `insert`\n\tpublic delete(deleteIndex: number, deleteCount: number): void {\n\t\tif (deleteCount === 0 || deleteIndex >= this._store.length) {\n\t\t\treturn;\n\t\t}\n\t\tthis._store.splice(deleteIndex, deleteCount);\n\t}\n\n\tpublic insert(insertIndex: number, insertCount: number): void {\n\t\tif (insertCount === 0 || insertIndex >= this._store.length) {\n\t\t\treturn;\n\t\t}\n\t\tconst arr: T[] = [];\n\t\tfor (let i = 0; i < insertCount; i++) {\n\t\t\tarr[i] = this._default;\n\t\t}\n\t\tthis._store = arrays.arrayInsert(this._store, insertIndex, arr);\n\t}\n}\n\nconst enum Constants {\n\tCHEAP_TOKENIZATION_LENGTH_LIMIT = 1024\n}\n\n/**\n * Stores the states at the start of each line and keeps track of which lines\n * must be re-tokenized. Also uses state equality to quickly validate lines\n * that don't need to be re-tokenized.\n *\n * For example, when typing on a line, the line gets marked as needing to be tokenized.\n * Once the line is tokenized, the end state is checked for equality against the begin\n * state of the next line. If the states are equal, tokenization doesn't need to run\n * again over the rest of the file. If the states are not equal, the next line gets marked\n * as needing to be tokenized.\n */\nexport class TokenizationStateStore {\n\trequestTokens(startLineNumber: number, endLineNumberExclusive: number): void {\n\t\tfor (let lineNumber = startLineNumber; lineNumber < endLineNumberExclusive; lineNumber++) {\n\t\t\tthis._stateStore.markMustBeTokenized(lineNumber - 1);\n\t\t}\n\t}\n}\n",
"fileName": "./2.tst"
},
"diffs": [
{
"originalRange": "[24,28)",
"modifiedRange": "[24,24)",
"innerChanges": [
{
"originalRange": "[24,1 -> 28,1 EOL]",
"modifiedRange": "[24,1 -> 24,1 EOL]"
}
]
},
{
"originalRange": "[74,74)",
"modifiedRange": "[70,74)",
"innerChanges": [
{
"originalRange": "[74,1 -> 74,1 EOL]",
"modifiedRange": "[70,1 -> 74,1 EOL]"
}
]
}
]
}
@@ -0,0 +1,544 @@
contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.disposables);
this.disposables.add(Event.filter(viewsRegistry.onDidChangeViewWelcomeContent, id => id === this.id)(this.onDidChangeViewWelcomeContent, this, this.disposables));
this.onDidChangeViewWelcomeContent();
}
private onDidChangeViewWelcomeContent(): void {
const descriptors = viewsRegistry.getViewWelcomeContent(this.id);
this.items = [];
for (const descriptor of descriptors) {
if (descriptor.when === 'default') {
this.defaultItem = { descriptor, visible: true };
} else {
const visible = descriptor.when ? this.contextKeyService.contextMatchesRules(descriptor.when) : true;
this.items.push({ descriptor, visible });
}
}
this._onDidChange.fire();
}
private onDidChangeContext(): void {
let didChange = false;
for (const item of this.items) {
if (!item.descriptor.when || item.descriptor.when === 'default') {
continue;
}
const visible = this.contextKeyService.contextMatchesRules(item.descriptor.when);
if (item.visible === visible) {
continue;
}
item.visible = visible;
didChange = true;
}
if (didChange) {
this._onDidChange.fire();
}
}
dispose(): void {
this.disposables.dispose();
}
}
export abstract class ViewPane extends Pane implements IView {
private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
private _onDidFocus = this._register(new Emitter<void>());
readonly onDidFocus: Event<void> = this._onDidFocus.event;
private _onDidBlur = this._register(new Emitter<void>());
readonly onDidBlur: Event<void> = this._onDidBlur.event;
private _onDidChangeBodyVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeBodyVisibility: Event<boolean> = this._onDidChangeBodyVisibility.event;
protected _onDidChangeTitleArea = this._register(new Emitter<void>());
readonly onDidChangeTitleArea: Event<void> = this._onDidChangeTitleArea.event;
protected _onDidChangeViewWelcomeState = this._register(new Emitter<void>());
readonly onDidChangeViewWelcomeState: Event<void> = this._onDidChangeViewWelcomeState.event;
private _isVisible: boolean = false;
readonly id: string;
private _title: string;
public get title(): string {
return this._title;
}
private _titleDescription: string | undefined;
public get titleDescription(): string | undefined {
return this._titleDescription;
}
readonly menuActions: CompositeMenuActions;
private progressBar!: ProgressBar;
private progressIndicator!: IProgressIndicator;
private toolbar?: WorkbenchToolBar;
private readonly showActions: ViewPaneShowActions;
private headerContainer?: HTMLElement;
private titleContainer?: HTMLElement;
private titleDescriptionContainer?: HTMLElement;
private iconContainer?: HTMLElement;
protected twistiesContainer?: HTMLElement;
private bodyContainer!: HTMLElement;
private viewWelcomeContainer!: HTMLElement;
private viewWelcomeDisposable: IDisposable = Disposable.None;
private viewWelcomeController: ViewWelcomeController;
protected readonly scopedContextKeyService: IContextKeyService;
constructor(
options: IViewPaneOptions,
@IKeybindingService protected keybindingService: IKeybindingService,
@IContextMenuService protected contextMenuService: IContextMenuService,
@IConfigurationService protected readonly configurationService: IConfigurationService,
@IContextKeyService protected contextKeyService: IContextKeyService,
@IViewDescriptorService protected viewDescriptorService: IViewDescriptorService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IOpenerService protected openerService: IOpenerService,
@IThemeService protected themeService: IThemeService,
@ITelemetryService protected telemetryService: ITelemetryService,
) {
super({ ...options, ...{ orientation: viewDescriptorService.getViewLocationById(options.id) === ViewContainerLocation.Panel ? Orientation.HORIZONTAL : Orientation.VERTICAL } });
this.id = options.id;
this._title = options.title;
this._titleDescription = options.titleDescription;
this.showActions = options.showActions ?? ViewPaneShowActions.Default;
this.scopedContextKeyService = this._register(contextKeyService.createScoped(this.element));
this.scopedContextKeyService.createKey('view', this.id);
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 }));
this._register(this.menuActions.onDidChange(() => this.updateActions()));
this.viewWelcomeController = this._register(new ViewWelcomeController(this.id, contextKeyService));
}
override get headerVisible(): boolean {
return super.headerVisible;
}
override set headerVisible(visible: boolean) {
super.headerVisible = visible;
this.element.classList.toggle('merged-header', !visible);
}
setVisible(visible: boolean): void {
if (this._isVisible !== visible) {
this._isVisible = visible;
if (this.isExpanded()) {
this._onDidChangeBodyVisibility.fire(visible);
}
}
}
isVisible(): boolean {
return this._isVisible;
}
isBodyVisible(): boolean {
return this._isVisible && this.isExpanded();
}
override setExpanded(expanded: boolean): boolean {
const changed = super.setExpanded(expanded);
if (changed) {
this._onDidChangeBodyVisibility.fire(expanded);
}
if (this.twistiesContainer) {
this.twistiesContainer.classList.remove(...ThemeIcon.asClassNameArray(this.getTwistyIcon(!expanded)));
this.twistiesContainer.classList.add(...ThemeIcon.asClassNameArray(this.getTwistyIcon(expanded)));
}
return changed;
}
override render(): void {
super.render();
const focusTracker = trackFocus(this.element);
this._register(focusTracker);
this._register(focusTracker.onDidFocus(() => this._onDidFocus.fire()));
this._register(focusTracker.onDidBlur(() => this._onDidBlur.fire()));
}
protected renderHeader(container: HTMLElement): void {
this.headerContainer = container;
this.twistiesContainer = append(container, $(ThemeIcon.asCSSSelector(this.getTwistyIcon(this.isExpanded()))));
this.renderHeaderTitle(container, this.title);
const actions = append(container, $('.actions'));
actions.classList.toggle('show-always', this.showActions === ViewPaneShowActions.Always);
actions.classList.toggle('show-expanded', this.showActions === ViewPaneShowActions.WhenExpanded);
this.toolbar = this.instantiationService.createInstance(WorkbenchToolBar, actions, {
orientation: ActionsOrientation.HORIZONTAL,
actionViewItemProvider: action => this.getActionViewItem(action),
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title),
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
renderDropdownAsChildElement: true,
actionRunner: this.getActionRunner(),
resetMenu: this.menuActions.menuId
});
this._register(this.toolbar);
this.setActions();
this._register(addDisposableListener(actions, EventType.CLICK, e => e.preventDefault()));
const viewContainerModel = this.viewDescriptorService.getViewContainerByViewId(this.id);
if (viewContainerModel) {
this._register(this.viewDescriptorService.getViewContainerModel(viewContainerModel).onDidChangeContainerInfo(({ title }) => this.updateTitle(this.title)));
} else {
console.error(`View container model not found for view ${this.id}`);
}
const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewPane.AlwaysShowActionsConfig));
this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this));
this.updateActionsVisibility();
}
protected getTwistyIcon(expanded: boolean): ThemeIcon {
return expanded ? viewPaneContainerExpandedIcon : viewPaneContainerCollapsedIcon;
}
override style(styles: IPaneStyles): void {
super.style(styles);
const icon = this.getIcon();
if (this.iconContainer) {
const fgColor = asCssValueWithDefault(styles.headerForeground, asCssVariable(foreground));
if (URI.isUri(icon)) {
// Apply background color to activity bar item provided with iconUrls
this.iconContainer.style.backgroundColor = fgColor;
this.iconContainer.style.color = '';
} else {
// Apply foreground color to activity bar items provided with codicons
this.iconContainer.style.color = fgColor;
this.iconContainer.style.backgroundColor = '';
}
}
}
private getIcon(): ThemeIcon | URI {
return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || defaultViewIcon;
}
protected renderHeaderTitle(container: HTMLElement, title: string): void {
this.iconContainer = append(container, $('.icon', undefined));
const icon = this.getIcon();
let cssClass: string | undefined = undefined;
if (URI.isUri(icon)) {
cssClass = `view-${this.id.replace(/[\.\:]/g, '-')}`;
const iconClass = `.pane-header .icon.${cssClass}`;
createCSSRule(iconClass, `
mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
mask-size: 24px;
-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
-webkit-mask-size: 16px;
`);
} else if (ThemeIcon.isThemeIcon(icon)) {
cssClass = ThemeIcon.asClassName(icon);
}
if (cssClass) {
this.iconContainer.classList.add(...cssClass.split(' '));
}
const calculatedTitle = this.calculateTitle(title);
this.titleContainer = append(container, $('h3.title', { title: calculatedTitle }, calculatedTitle));
if (this._titleDescription) {
this.setTitleDescription(this._titleDescription);
}
this.iconContainer.title = calculatedTitle;
this.iconContainer.setAttribute('aria-label', calculatedTitle);
}
protected updateTitle(title: string): void {
const calculatedTitle = this.calculateTitle(title);
if (this.titleContainer) {
this.titleContainer.textContent = calculatedTitle;
this.titleContainer.setAttribute('title', calculatedTitle);
}
if (this.iconContainer) {
this.iconContainer.title = calculatedTitle;
this.iconContainer.setAttribute('aria-label', calculatedTitle);
}
this._title = title;
this._onDidChangeTitleArea.fire();
}
private setTitleDescription(description: string | undefined) {
if (this.titleDescriptionContainer) {
this.titleDescriptionContainer.textContent = description ?? '';
this.titleDescriptionContainer.setAttribute('title', description ?? '');
}
else if (description && this.titleContainer) {
this.titleDescriptionContainer = after(this.titleContainer, $('span.description', { title: description }, description));
}
}
protected updateTitleDescription(description?: string | undefined): void {
this.setTitleDescription(description);
this._titleDescription = description;
this._onDidChangeTitleArea.fire();
}
private calculateTitle(title: string): string {
const viewContainer = this.viewDescriptorService.getViewContainerByViewId(this.id)!;
const model = this.viewDescriptorService.getViewContainerModel(viewContainer);
const viewDescriptor = this.viewDescriptorService.getViewDescriptorById(this.id);
const isDefault = this.viewDescriptorService.getDefaultContainerById(this.id) === viewContainer;
if (!isDefault && viewDescriptor?.containerTitle && model.title !== viewDescriptor.containerTitle) {
return `${viewDescriptor.containerTitle}: ${title}`;
}
return title;
}
private scrollableElement!: DomScrollableElement;
protected renderBody(container: HTMLElement): void {
this.bodyContainer = container;
const viewWelcomeContainer = append(container, $('.welcome-view'));
this.viewWelcomeContainer = $('.welcome-view-content', { tabIndex: 0 });
this.scrollableElement = this._register(new DomScrollableElement(this.viewWelcomeContainer, {
alwaysConsumeMouseWheel: true,
horizontal: ScrollbarVisibility.Hidden,
vertical: ScrollbarVisibility.Visible,
}));
append(viewWelcomeContainer, this.scrollableElement.getDomNode());
const onViewWelcomeChange = Event.any(this.viewWelcomeController.onDidChange, this.onDidChangeViewWelcomeState);
this._register(onViewWelcomeChange(this.updateViewWelcome, this));
this.updateViewWelcome();
}
protected layoutBody(height: number, width: number): void {
if (this.shouldShowWelcome()) {
this.viewWelcomeContainer.style.height = `${height}px`;
this.viewWelcomeContainer.style.width = `${width}px`;
this.viewWelcomeContainer.classList.toggle('wide', width > 640);
this.scrollableElement.scanDomNode();
}
}
onDidScrollRoot() {
// noop
}
getProgressIndicator() {
if (this.progressBar === undefined) {
// Progress bar
this.progressBar = this._register(new ProgressBar(this.element, defaultProgressBarStyles));
this.progressBar.hide();
}
if (this.progressIndicator === undefined) {
const that = this;
this.progressIndicator = new ScopedProgressIndicator(assertIsDefined(this.progressBar), new class extends AbstractProgressScope {
constructor() {
super(that.id, that.isBodyVisible());
this._register(that.onDidChangeBodyVisibility(isVisible => isVisible ? this.onScopeOpened(that.id) : this.onScopeClosed(that.id)));
}
}());
}
return this.progressIndicator;
}
protected getProgressLocation(): string {
return this.viewDescriptorService.getViewContainerByViewId(this.id)!.id;
}
protected getBackgroundColor(): string {
switch (this.viewDescriptorService.getViewLocationById(this.id)) {
case ViewContainerLocation.Panel:
return PANEL_BACKGROUND;
case ViewContainerLocation.Sidebar:
case ViewContainerLocation.AuxiliaryBar:
return SIDE_BAR_BACKGROUND;
}
return SIDE_BAR_BACKGROUND;
}
focus(): void {
if (this.shouldShowWelcome()) {
this.viewWelcomeContainer.focus();
} else if (this.element) {
this.element.focus();
this._onDidFocus.fire();
}
}
private setActions(): void {
if (this.toolbar) {
const primaryActions = [...this.menuActions.getPrimaryActions()];
if (this.shouldShowFilterInHeader()) {
primaryActions.unshift(VIEWPANE_FILTER_ACTION);
}
this.toolbar.setActions(prepareActions(primaryActions), prepareActions(this.menuActions.getSecondaryActions()));
this.toolbar.context = this.getActionsContext();
}
}
private updateActionsVisibility(): void {
if (!this.headerContainer) {
return;
}
const shouldAlwaysShowActions = this.configurationService.getValue<boolean>('workbench.view.alwaysShowHeaderActions');
this.headerContainer.classList.toggle('actions-always-visible', shouldAlwaysShowActions);
}
protected updateActions(): void {
this.setActions();
this._onDidChangeTitleArea.fire();
}
getActionViewItem(action: IAction, options?: IDropdownMenuActionViewItemOptions): IActionViewItem | undefined {
if (action.id === VIEWPANE_FILTER_ACTION.id) {
const that = this;
return new class extends BaseActionViewItem {
constructor() { super(null, action); }
override setFocusable(): void { /* noop input elements are focusable by default */ }
override get trapsArrowNavigation(): boolean { return true; }
override render(container: HTMLElement): void {
container.classList.add('viewpane-filter-container');
append(container, that.getFilterWidget()!.element);
}
};
}
return createActionViewItem(this.instantiationService, action, { ...options, ...{ menuAsChild: action instanceof SubmenuItemAction } });
}
getActionsContext(): unknown {
return undefined;
}
getActionRunner(): IActionRunner | undefined {
return undefined;
}
getOptimalWidth(): number {
return 0;
}
saveState(): void {
// Subclasses to implement for saving state
}
private updateViewWelcome(): void {
this.viewWelcomeDisposable.dispose();
if (!this.shouldShowWelcome()) {
this.bodyContainer.classList.remove('welcome');
this.viewWelcomeContainer.innerText = '';
this.scrollableElement.scanDomNode();
return;
}
const contents = this.viewWelcomeController.contents;
if (contents.length === 0) {
this.bodyContainer.classList.remove('welcome');
this.viewWelcomeContainer.innerText = '';
this.scrollableElement.scanDomNode();
return;
}
const disposables = new DisposableStore();
this.bodyContainer.classList.add('welcome');
this.viewWelcomeContainer.innerText = '';
for (const { content, precondition } of contents) {
const lines = content.split('\n');
for (let line of lines) {
line = line.trim();
if (!line) {
continue;
}
const linkedText = parseLinkedText(line);
if (linkedText.nodes.length === 1 && typeof linkedText.nodes[0] !== 'string') {
const node = linkedText.nodes[0];
const buttonContainer = append(this.viewWelcomeContainer, $('.button-container'));
const button = new Button(buttonContainer, { title: node.title, supportIcons: true, ...defaultButtonStyles });
button.label = node.label;
button.onDidClick(_ => {
this.telemetryService.publicLog2<{ viewId: string; uri: string }, WelcomeActionClassification>('views.welcomeAction', { viewId: this.id, uri: node.href });
this.openerService.open(node.href, { allowCommands: true });
}, null, disposables);
disposables.add(button);
if (precondition) {
const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition);
updateEnablement();
const keys = new Set();
precondition.keys().forEach(key => keys.add(key));
const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys));
onDidChangeContext(updateEnablement, null, disposables);
}
} else {
const p = append(this.viewWelcomeContainer, $('p'));
for (const node of linkedText.nodes) {
if (typeof node === 'string') {
append(p, document.createTextNode(node));
} else {
const link = disposables.add(this.instantiationService.createInstance(Link, p, node, {}));
if (precondition && node.href.startsWith('command:')) {
const updateEnablement = () => link.enabled = this.contextKeyService.contextMatchesRules(precondition);
updateEnablement();
const keys = new Set();
precondition.keys().forEach(key => keys.add(key));
const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys));
onDidChangeContext(updateEnablement, null, disposables);
}
}
}
}
}
}
this.scrollableElement.scanDomNode();
this.viewWelcomeDisposable = disposables;
}
shouldShowWelcome(): boolean {
return false;
}
getFilterWidget()
@@ -0,0 +1,565 @@
layout(height: number, width: number) {
if (!this.enabled) {
return;
}
this.element!.style.height = `${height}px`;
this.element!.style.width = `${width}px`;
this.element!.classList.toggle('wide', width > 640);
this.scrollableElement!.scanDomNode();
}
focus() {
if (!this.enabled) {
return;
}
this.element!.focus();
}
private onDidChangeViewWelcomeState(): void {
const enabled = this.delegate.shouldShowWelcome();
if (this.enabled === enabled) {
return;
}
this.enabled = enabled;
if (!enabled) {
this.enabledDisposables.clear();
return;
}
this.container.classList.add('welcome');
const viewWelcomeContainer = append(this.container, $('.welcome-view'));
this.element = $('.welcome-view-content', { tabIndex: 0 });
this.scrollableElement = new DomScrollableElement(this.element, { alwaysConsumeMouseWheel: true, horizontal: ScrollbarVisibility.Hidden, vertical: ScrollbarVisibility.Visible, });
append(viewWelcomeContainer, this.scrollableElement.getDomNode());
this.enabledDisposables.add(toDisposable(() => {
this.container.classList.remove('welcome');
this.scrollableElement!.dispose();
viewWelcomeContainer.remove();
this.scrollableElement = undefined;
this.element = undefined;
}));
this.contextKeyService.onDidChangeContext(this.onDidChangeContext, this, this.enabledDisposables);
Event.chain(viewsRegistry.onDidChangeViewWelcomeContent, $ => $.filter(id => id === this.delegate.id))
(this.onDidChangeViewWelcomeContent, this, this.enabledDisposables);
this.onDidChangeViewWelcomeContent();
}
private onDidChangeViewWelcomeContent(): void {
const descriptors = viewsRegistry.getViewWelcomeContent(this.delegate.id);
this.items = [];
for (const descriptor of descriptors) {
if (descriptor.when === 'default') {
this.defaultItem = { descriptor, visible: true };
} else {
const visible = descriptor.when ? this.contextKeyService.contextMatchesRules(descriptor.when) : true;
this.items.push({ descriptor, visible });
}
}
this.render();
}
private onDidChangeContext(): void {
let didChange = false;
for (const item of this.items) {
if (!item.descriptor.when || item.descriptor.when === 'default') {
continue;
}
const visible = this.contextKeyService.contextMatchesRules(item.descriptor.when);
if (item.visible === visible) {
continue;
}
item.visible = visible;
didChange = true;
}
if (didChange) {
this.render();
}
}
private render(): void {
this.renderDisposables.clear();
const contents = this.getContentDescriptors();
if (contents.length === 0) {
this.container.classList.remove('welcome');
this.element!.innerText = '';
this.scrollableElement!.scanDomNode();
return;
}
this.container.classList.add('welcome');
this.element!.innerText = '';
for (const { content, precondition } of contents) {
const lines = content.split('\n');
for (let line of lines) {
line = line.trim();
if (!line) {
continue;
}
const linkedText = parseLinkedText(line);
if (linkedText.nodes.length === 1 && typeof linkedText.nodes[0] !== 'string') {
const node = linkedText.nodes[0];
const buttonContainer = append(this.element!, $('.button-container'));
const button = new Button(buttonContainer, { title: node.title, supportIcons: true, ...defaultButtonStyles });
button.label = node.label;
button.onDidClick(_ => {
this.telemetryService.publicLog2<{ viewId: string; uri: string }, WelcomeActionClassification>('views.welcomeAction', { viewId: this.delegate.id, uri: node.href });
this.openerService.open(node.href, { allowCommands: true });
}, null, this.renderDisposables);
this.renderDisposables.add(button);
if (precondition) {
const updateEnablement = () => button.enabled = this.contextKeyService.contextMatchesRules(precondition);
updateEnablement();
const keys = new Set(precondition.keys());
const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys));
onDidChangeContext(updateEnablement, null, this.renderDisposables);
}
} else {
const p = append(this.element!, $('p'));
for (const node of linkedText.nodes) {
if (typeof node === 'string') {
append(p, document.createTextNode(node));
} else {
const link = this.renderDisposables.add(this.instantiationService.createInstance(Link, p, node, {}));
if (precondition && node.href.startsWith('command:')) {
const updateEnablement = () => link.enabled = this.contextKeyService.contextMatchesRules(precondition);
updateEnablement();
const keys = new Set(precondition.keys());
const onDidChangeContext = Event.filter(this.contextKeyService.onDidChangeContext, e => e.affectsSome(keys));
onDidChangeContext(updateEnablement, null, this.renderDisposables);
}
}
}
}
}
}
this.scrollableElement!.scanDomNode();
}
private getContentDescriptors(): IViewContentDescriptor[] {
const visibleItems = this.items.filter(v => v.visible);
if (visibleItems.length === 0 && this.defaultItem) {
return [this.defaultItem.descriptor];
}
return visibleItems.map(v => v.descriptor);
}
dispose(): void {
this.disposables.dispose();
}
}
export abstract class ViewPane extends Pane implements IView {
private static readonly AlwaysShowActionsConfig = 'workbench.view.alwaysShowHeaderActions';
private _onDidFocus = this._register(new Emitter<void>());
readonly onDidFocus: Event<void> = this._onDidFocus.event;
private _onDidBlur = this._register(new Emitter<void>());
readonly onDidBlur: Event<void> = this._onDidBlur.event;
private _onDidChangeBodyVisibility = this._register(new Emitter<boolean>());
readonly onDidChangeBodyVisibility: Event<boolean> = this._onDidChangeBodyVisibility.event;
protected _onDidChangeTitleArea = this._register(new Emitter<void>());
readonly onDidChangeTitleArea: Event<void> = this._onDidChangeTitleArea.event;
protected _onDidChangeViewWelcomeState = this._register(new Emitter<void>());
readonly onDidChangeViewWelcomeState: Event<void> = this._onDidChangeViewWelcomeState.event;
private _isVisible: boolean = false;
readonly id: string;
private _title: string;
public get title(): string {
return this._title;
}
private _titleDescription: string | undefined;
public get titleDescription(): string | undefined {
return this._titleDescription;
}
readonly menuActions: CompositeMenuActions;
private progressBar!: ProgressBar;
private progressIndicator!: IProgressIndicator;
private toolbar?: WorkbenchToolBar;
private readonly showActions: ViewPaneShowActions;
private headerContainer?: HTMLElement;
private titleContainer?: HTMLElement;
private titleDescriptionContainer?: HTMLElement;
private iconContainer?: HTMLElement;
protected twistiesContainer?: HTMLElement;
private viewWelcomeController!: ViewWelcomeController;
protected readonly scopedContextKeyService: IContextKeyService;
constructor(
options: IViewPaneOptions,
@IKeybindingService protected keybindingService: IKeybindingService,
@IContextMenuService protected contextMenuService: IContextMenuService,
@IConfigurationService protected readonly configurationService: IConfigurationService,
@IContextKeyService protected contextKeyService: IContextKeyService,
@IViewDescriptorService protected viewDescriptorService: IViewDescriptorService,
@IInstantiationService protected instantiationService: IInstantiationService,
@IOpenerService protected openerService: IOpenerService,
@IThemeService protected themeService: IThemeService,
@ITelemetryService protected telemetryService: ITelemetryService,
) {
super({ ...options, ...{ orientation: viewDescriptorService.getViewLocationById(options.id) === ViewContainerLocation.Panel ? Orientation.HORIZONTAL : Orientation.VERTICAL } });
this.id = options.id;
this._title = options.title;
this._titleDescription = options.titleDescription;
this.showActions = options.showActions ?? ViewPaneShowActions.Default;
this.scopedContextKeyService = this._register(contextKeyService.createScoped(this.element));
this.scopedContextKeyService.createKey('view', this.id);
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 }));
this._register(this.menuActions.onDidChange(() => this.updateActions()));
}
override get headerVisible(): boolean {
return super.headerVisible;
}
override set headerVisible(visible: boolean) {
super.headerVisible = visible;
this.element.classList.toggle('merged-header', !visible);
}
setVisible(visible: boolean): void {
if (this._isVisible !== visible) {
this._isVisible = visible;
if (this.isExpanded()) {
this._onDidChangeBodyVisibility.fire(visible);
}
}
}
isVisible(): boolean {
return this._isVisible;
}
isBodyVisible(): boolean {
return this._isVisible && this.isExpanded();
}
override setExpanded(expanded: boolean): boolean {
const changed = super.setExpanded(expanded);
if (changed) {
this._onDidChangeBodyVisibility.fire(expanded);
}
if (this.twistiesContainer) {
this.twistiesContainer.classList.remove(...ThemeIcon.asClassNameArray(this.getTwistyIcon(!expanded)));
this.twistiesContainer.classList.add(...ThemeIcon.asClassNameArray(this.getTwistyIcon(expanded)));
}
return changed;
}
override render(): void {
super.render();
const focusTracker = trackFocus(this.element);
this._register(focusTracker);
this._register(focusTracker.onDidFocus(() => this._onDidFocus.fire()));
this._register(focusTracker.onDidBlur(() => this._onDidBlur.fire()));
}
protected renderHeader(container: HTMLElement): void {
this.headerContainer = container;
this.twistiesContainer = append(container, $(ThemeIcon.asCSSSelector(this.getTwistyIcon(this.isExpanded()))));
this.renderHeaderTitle(container, this.title);
const actions = append(container, $('.actions'));
actions.classList.toggle('show-always', this.showActions === ViewPaneShowActions.Always);
actions.classList.toggle('show-expanded', this.showActions === ViewPaneShowActions.WhenExpanded);
this.toolbar = this.instantiationService.createInstance(WorkbenchToolBar, actions, {
orientation: ActionsOrientation.HORIZONTAL,
actionViewItemProvider: action => this.getActionViewItem(action),
ariaLabel: nls.localize('viewToolbarAriaLabel', "{0} actions", this.title),
getKeyBinding: action => this.keybindingService.lookupKeybinding(action.id),
renderDropdownAsChildElement: true,
actionRunner: this.getActionRunner(),
resetMenu: this.menuActions.menuId
});
this._register(this.toolbar);
this.setActions();
this._register(addDisposableListener(actions, EventType.CLICK, e => e.preventDefault()));
const viewContainerModel = this.viewDescriptorService.getViewContainerByViewId(this.id);
if (viewContainerModel) {
this._register(this.viewDescriptorService.getViewContainerModel(viewContainerModel).onDidChangeContainerInfo(({ title }) => this.updateTitle(this.title)));
} else {
console.error(`View container model not found for view ${this.id}`);
}
const onDidRelevantConfigurationChange = Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration(ViewPane.AlwaysShowActionsConfig));
this._register(onDidRelevantConfigurationChange(this.updateActionsVisibility, this));
this.updateActionsVisibility();
}
protected getTwistyIcon(expanded: boolean): ThemeIcon {
return expanded ? viewPaneContainerExpandedIcon : viewPaneContainerCollapsedIcon;
}
override style(styles: IPaneStyles): void {
super.style(styles);
const icon = this.getIcon();
if (this.iconContainer) {
const fgColor = asCssValueWithDefault(styles.headerForeground, asCssVariable(foreground));
if (URI.isUri(icon)) {
// Apply background color to activity bar item provided with iconUrls
this.iconContainer.style.backgroundColor = fgColor;
this.iconContainer.style.color = '';
} else {
// Apply foreground color to activity bar items provided with codicons
this.iconContainer.style.color = fgColor;
this.iconContainer.style.backgroundColor = '';
}
}
}
private getIcon(): ThemeIcon | URI {
return this.viewDescriptorService.getViewDescriptorById(this.id)?.containerIcon || defaultViewIcon;
}
protected renderHeaderTitle(container: HTMLElement, title: string): void {
this.iconContainer = append(container, $('.icon', undefined));
const icon = this.getIcon();
let cssClass: string | undefined = undefined;
if (URI.isUri(icon)) {
cssClass = `view-${this.id.replace(/[\.\:]/g, '-')}`;
const iconClass = `.pane-header .icon.${cssClass}`;
createCSSRule(iconClass, `
mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
mask-size: 24px;
-webkit-mask: ${asCSSUrl(icon)} no-repeat 50% 50%;
-webkit-mask-size: 16px;
`);
} else if (ThemeIcon.isThemeIcon(icon)) {
cssClass = ThemeIcon.asClassName(icon);
}
if (cssClass) {
this.iconContainer.classList.add(...cssClass.split(' '));
}
const calculatedTitle = this.calculateTitle(title);
this.titleContainer = append(container, $('h3.title', { title: calculatedTitle }, calculatedTitle));
if (this._titleDescription) {
this.setTitleDescription(this._titleDescription);
}
this.iconContainer.title = calculatedTitle;
this.iconContainer.setAttribute('aria-label', calculatedTitle);
}
protected updateTitle(title: string): void {
const calculatedTitle = this.calculateTitle(title);
if (this.titleContainer) {
this.titleContainer.textContent = calculatedTitle;
this.titleContainer.setAttribute('title', calculatedTitle);
}
if (this.iconContainer) {
this.iconContainer.title = calculatedTitle;
this.iconContainer.setAttribute('aria-label', calculatedTitle);
}
this._title = title;
this._onDidChangeTitleArea.fire();
}
private setTitleDescription(description: string | undefined) {
if (this.titleDescriptionContainer) {
this.titleDescriptionContainer.textContent = description ?? '';
this.titleDescriptionContainer.setAttribute('title', description ?? '');
}
else if (description && this.titleContainer) {
this.titleDescriptionContainer = after(this.titleContainer, $('span.description', { title: description }, description));
}
}
protected updateTitleDescription(description?: string | undefined): void {
this.setTitleDescription(description);
this._titleDescription = description;
this._onDidChangeTitleArea.fire();
}
private calculateTitle(title: string): string {
const viewContainer = this.viewDescriptorService.getViewContainerByViewId(this.id)!;
const model = this.viewDescriptorService.getViewContainerModel(viewContainer);
const viewDescriptor = this.viewDescriptorService.getViewDescriptorById(this.id);
const isDefault = this.viewDescriptorService.getDefaultContainerById(this.id) === viewContainer;
if (!isDefault && viewDescriptor?.containerTitle && model.title !== viewDescriptor.containerTitle) {
return `${viewDescriptor.containerTitle}: ${title}`;
}
return title;
}
protected renderBody(container: HTMLElement): void {
this.viewWelcomeController = this._register(new ViewWelcomeController(container, this, this.instantiationService, this.openerService, this.telemetryService, this.contextKeyService));
}
protected layoutBody(height: number, width: number): void {
this.viewWelcomeController.layout(height, width);
}
onDidScrollRoot() {
// noop
}
getProgressIndicator() {
if (this.progressBar === undefined) {
// Progress bar
this.progressBar = this._register(new ProgressBar(this.element, defaultProgressBarStyles));
this.progressBar.hide();
}
if (this.progressIndicator === undefined) {
const that = this;
this.progressIndicator = new ScopedProgressIndicator(assertIsDefined(this.progressBar), new class extends AbstractProgressScope {
constructor() {
super(that.id, that.isBodyVisible());
this._register(that.onDidChangeBodyVisibility(isVisible => isVisible ? this.onScopeOpened(that.id) : this.onScopeClosed(that.id)));
}
}());
}
return this.progressIndicator;
}
protected getProgressLocation(): string {
return this.viewDescriptorService.getViewContainerByViewId(this.id)!.id;
}
protected getBackgroundColor(): string {
switch (this.viewDescriptorService.getViewLocationById(this.id)) {
case ViewContainerLocation.Panel:
return PANEL_BACKGROUND;
case ViewContainerLocation.Sidebar:
case ViewContainerLocation.AuxiliaryBar:
return SIDE_BAR_BACKGROUND;
}
return SIDE_BAR_BACKGROUND;
}
focus(): void {
if (this.shouldShowWelcome()) {
this.viewWelcomeController.focus();
} else if (this.element) {
this.element.focus();
this._onDidFocus.fire();
}
}
private setActions(): void {
if (this.toolbar) {
const primaryActions = [...this.menuActions.getPrimaryActions()];
if (this.shouldShowFilterInHeader()) {
primaryActions.unshift(VIEWPANE_FILTER_ACTION);
}
this.toolbar.setActions(prepareActions(primaryActions), prepareActions(this.menuActions.getSecondaryActions()));
this.toolbar.context = this.getActionsContext();
}
}
private updateActionsVisibility(): void {
if (!this.headerContainer) {
return;
}
const shouldAlwaysShowActions = this.configurationService.getValue<boolean>('workbench.view.alwaysShowHeaderActions');
this.headerContainer.classList.toggle('actions-always-visible', shouldAlwaysShowActions);
}
protected updateActions(): void {
this.setActions();
this._onDidChangeTitleArea.fire();
}
getActionViewItem(action: IAction, options?: IDropdownMenuActionViewItemOptions): IActionViewItem | undefined {
if (action.id === VIEWPANE_FILTER_ACTION.id) {
const that = this;
return new class extends BaseActionViewItem {
constructor() { super(null, action); }
override setFocusable(): void { /* noop input elements are focusable by default */ }
override get trapsArrowNavigation(): boolean { return true; }
override render(container: HTMLElement): void {
container.classList.add('viewpane-filter-container');
append(container, that.getFilterWidget()!.element);
}
};
}
return createActionViewItem(this.instantiationService, action, { ...options, ...{ menuAsChild: action instanceof SubmenuItemAction } });
}
getActionsContext(): unknown {
return undefined;
}
getActionRunner(): IActionRunner | undefined {
return undefined;
}
getOptimalWidth(): number {
return 0;
}
saveState(): void {
// Subclasses to implement for saving state
}
shouldShowWelcome(): boolean {
return false;
}
getFilterWidget()
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long