diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 3efb9d5738f3a..47fbfec59b905 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -33,6 +33,8 @@ export class StickyScrollController extends Disposable implements IEditorContrib this._stickyLineCandidateProvider = new StickyLineCandidateProvider(this._editor, _languageFeaturesService); this._widgetState = new StickyScrollWidgetState([], 0); + this._register(this._stickyScrollWidget); + this._register(this._stickyLineCandidateProvider); this._register(this._editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.stickyScroll)) { this.readConfiguration(); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index fb8b468fc4e96..7531613e835eb 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -13,6 +13,8 @@ import { RunOnceScheduler } from 'vs/base/common/async'; import { Range } from 'vs/editor/common/core/range'; import { Emitter } from 'vs/base/common/event'; import { binarySearch } from 'vs/base/common/arrays'; +import { FoldingController } from 'vs/editor/contrib/folding/browser/folding'; +import { FoldingModel } from 'vs/editor/contrib/folding/browser/foldingModel'; export class StickyRange { constructor( @@ -92,7 +94,24 @@ export class StickyLineCandidateProvider extends Disposable { if (token.isCancellationRequested) { return; } - this._outlineModel = StickyOutlineElement.fromOutlineModel(outlineModel, -1); + if (outlineModel.children.size !== 0) { + this._outlineModel = StickyOutlineElement.fromOutlineModel(outlineModel, -1); + } else { + const foldingController = FoldingController.get(this._editor); + const foldingModel = await foldingController?.getFoldingModel(); + if (token.isCancellationRequested) { + return; + } + if (foldingModel && foldingModel.regions.length !== 0) { + this._outlineModel = StickyOutlineElement.fromFoldingModel(foldingModel); + } else { + this._outlineModel = new StickyOutlineElement( + new StickyRange(-1, -1), + [], + undefined + ); + } + } this._modelVersionId = modelVersionId; } } @@ -191,9 +210,44 @@ class StickyOutlineElement { } return new StickyOutlineElement( range, - children + children, + undefined ); } + + public static fromFoldingModel(foldingModel: FoldingModel): StickyOutlineElement { + const regions = foldingModel.regions; + const length = regions.length; + let range: StickyRange | undefined; + const stackOfParents: StickyRange[] = []; + + const stickyOutlineElement = new StickyOutlineElement( + undefined, + [], + undefined + ); + let parentStickyOutlineElement = stickyOutlineElement; + + for (let i = 0; i < length; i++) { + range = new StickyRange(regions.getStartLineNumber(i), regions.getEndLineNumber(i)); + while (stackOfParents.length !== 0 && (range.startLineNumber < stackOfParents[stackOfParents.length - 1].startLineNumber || range.endLineNumber > stackOfParents[stackOfParents.length - 1].endLineNumber)) { + stackOfParents.pop(); + if (parentStickyOutlineElement.parent !== undefined) { + parentStickyOutlineElement = parentStickyOutlineElement.parent; + } + } + const child = new StickyOutlineElement( + range, + [], + parentStickyOutlineElement + ); + parentStickyOutlineElement.children.push(child); + parentStickyOutlineElement = child; + stackOfParents.push(range); + } + return stickyOutlineElement; + } + constructor( /** * Range of line numbers spanned by the current scope @@ -202,7 +256,11 @@ class StickyOutlineElement { /** * Must be sorted by start line number */ - public readonly children: readonly StickyOutlineElement[], + public readonly children: StickyOutlineElement[], + /** + * Parent sticky outline element + */ + public readonly parent: StickyOutlineElement | undefined ) { } } diff --git a/src/vs/editor/test/browser/testCodeEditor.ts b/src/vs/editor/test/browser/testCodeEditor.ts index 4757ff43be02d..bdcc66b5f1e9d 100644 --- a/src/vs/editor/test/browser/testCodeEditor.ts +++ b/src/vs/editor/test/browser/testCodeEditor.ts @@ -133,7 +133,7 @@ function isTextModel(arg: ITextModel | string | string[] | ITextBufferFactory): function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => void): void; function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise): Promise; -function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise | void): Promise | void { +async function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFactory, options: TestCodeEditorInstantiationOptions, callback: (editor: ITestCodeEditor, viewModel: ViewModel, instantiationService: TestInstantiationService) => Promise | void): Promise | void> { const disposables = new DisposableStore(); const instantiationService = createCodeEditorServices(disposables, options.serviceCollection); delete options.serviceCollection; @@ -149,12 +149,12 @@ function _withTestCodeEditor(arg: ITextModel | string | string[] | ITextBufferFa const editor = disposables.add(instantiateTestCodeEditor(instantiationService, model, options)); const viewModel = editor.getViewModel()!; viewModel.setHasFocus(true); - const result = callback(editor, editor.getViewModel()!, instantiationService); - if (result) { - return result.then(() => disposables.dispose()); + try { + const result = callback(editor, editor.getViewModel()!, instantiationService); + await result; + } finally { + disposables.dispose(); } - - disposables.dispose(); } export function createCodeEditorServices(disposables: DisposableStore, services: ServiceCollection = new ServiceCollection()): TestInstantiationService { diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index 00a7696f69d32..df2baa8a16197 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -194,7 +194,7 @@ export class TestTextResourceEditor extends TextResourceEditor { export class TestTextFileEditor extends TextFileEditor { protected override createEditorControl(parent: HTMLElement, configuration: any): void { - this.editorControl = this.instantiationService.createInstance(TestCodeEditor, parent, configuration, {}); + this.editorControl = this.instantiationService.createInstance(TestCodeEditor, parent, configuration, { contributions: [] }); } setSelection(selection: Selection | undefined, reason: EditorPaneSelectionChangeReason): void {