Change:
It would be useful for navigating code to have a command to jump to previous/next foldable line. This would allow me to easily jump to the lines that I can either fold/unfold.
Awesome! Do I need to update my version to add this? I'm on macos Version: 1.58.2 currently and tried to my shortcuts, but didn't get it to work.
to get it you need to install Insiders.
Ahh I see the tag now, thanks.

Discussion:
import { ITextModel } from 'vs/editor/common/model'; import { registerEditorAction, registerEditorContribution, ServicesAccessor, EditorAction, registerInstantiatedEditorAction } from 'vs/editor/browser/editorExtensions'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; <del> import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, setCollapseStateForRest, toggleCollapseState, setCollapseStateUp } from 'vs/editor/contrib/folding/foldingModel'; </del> <ins> import { FoldingModel, setCollapseStateAtLevel, CollapseMemento, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateForType, setCollapseStateForRest, toggleCollapseState, setCollapseStateUp, getJumpToParentFoldLine, getJumpToPreviousFoldLine, getJumpToNextFoldLine } from 'vs/editor/contrib/folding/foldingModel'; </ins> import { FoldingDecorationProvider, foldingCollapsedIcon, foldingExpandedIcon } from './foldingDecorations'; import { FoldingRegions, FoldingRegion } from './foldingRanges'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';

} } <ins> /** Action to jump to the parent fold of current line */ class JumpToParentFoldAction extends FoldingAction<void> { constructor() { super({ id: 'editor.jumpToParentFold', label: nls.localize('jumpToParentFold.label', "Jump to Parent Fold"), alias: 'Jump to Parent Fold', precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, weight: KeybindingWeight.EditorContrib } }); } invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void { let selectedLines = this.getSelectedLines(editor); if (selectedLines.length > 0) { let startLineNumber = getJumpToParentFoldLine(selectedLines[0], foldingModel); if (startLineNumber !== null) { editor.setSelection({ startLineNumber: startLineNumber, startColumn: 1, endLineNumber: startLineNumber, endColumn: 1 }); } } } } /** Action to jump to the previous fold of current line */ class JumpToPreviousFoldAction extends FoldingAction<void> { constructor() { super({ id: 'editor.jumpToPreviousFold', label: nls.localize('jumpToPreviousFold.label', "Jump to Previous Fold"), alias: 'Jump to Previous Fold', precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, weight: KeybindingWeight.EditorContrib } }); } invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void { let selectedLines = this.getSelectedLines(editor); if (selectedLines.length > 0) { let startLineNumber = getJumpToPreviousFoldLine(selectedLines[0], foldingModel); if (startLineNumber !== null) { editor.setSelection({ startLineNumber: startLineNumber, startColumn: 1, endLineNumber: startLineNumber, endColumn: 1 }); } } } } /** Action to jump to the next fold of current line */ class JumpToNextFoldAction extends FoldingAction<void> { constructor() { super({ id: 'editor.jumpToNextFold', label: nls.localize('jumpToNextFold.label', "Jump to Next Fold"), alias: 'Jump to Next Fold', precondition: CONTEXT_FOLDING_ENABLED, kbOpts: { kbExpr: EditorContextKeys.editorTextFocus, weight: KeybindingWeight.EditorContrib } }); } invoke(_foldingController: FoldingController, foldingModel: FoldingModel, editor: ICodeEditor): void { let selectedLines = this.getSelectedLines(editor); if (selectedLines.length > 0) { let startLineNumber = getJumpToNextFoldLine(selectedLines[0], foldingModel); if (startLineNumber !== null) { editor.setSelection({ startLineNumber: startLineNumber, startColumn: 1, endLineNumber: startLineNumber, endColumn: 1 }); } } } } </ins> registerEditorContribution(FoldingController.ID, FoldingController); registerEditorAction(UnfoldAction); registerEditorAction(UnFoldRecursivelyAction);

registerEditorAction(FoldAllRegionsExceptAction); registerEditorAction(UnfoldAllRegionsExceptAction); registerEditorAction(ToggleFoldAction); <ins> registerEditorAction(JumpToParentFoldAction); registerEditorAction(JumpToPreviousFoldAction); registerEditorAction(JumpToNextFoldAction); </ins> for (let i = 1; i <= 7; i++) { registerInstantiatedEditorAction(

} foldingModel.toggleCollapseState(toToggle); } <ins> /** * Get line to jump to for parent fold of current line * @param lineNumber the current line number * @param foldingModel the folding model * * @return Parent fold start line */ export function getJumpToParentFoldLine(lineNumber: number, foldingModel: FoldingModel): number | null { let startLineNumber: number | null = null; let foldingRegion = foldingModel.getRegionAtLine(lineNumber); if (foldingRegion !== null) { startLineNumber = foldingRegion.startLineNumber; // If current line is not the start of the current fold, go to top line of current fold. If not, go to parent fold if (lineNumber === startLineNumber) { let parentFoldingIdx = foldingRegion.parentIndex; if (parentFoldingIdx !== -1) { startLineNumber = foldingModel.regions.getStartLineNumber(parentFoldingIdx); } else { startLineNumber = null; } } } return startLineNumber; } /** * Get line to jump to for previous fold at the same level of current line * @param lineNumber the current line number * @param foldingModel the folding model * * @return Previous fold start line */ export function getJumpToPreviousFoldLine(lineNumber: number, foldingModel: FoldingModel): number | null { let foldingRegion = foldingModel.getRegionAtLine(lineNumber); if (foldingRegion !== null) { // If current line is not the start of the current fold, go to top line of current fold. If not, go to previous fold. if (lineNumber !== foldingRegion.startLineNumber) { return foldingRegion.startLineNumber; } else { // Find min line number to stay within parent. let expectedParentIndex = foldingRegion.parentIndex; if (expectedParentIndex === -1) return null; let minLineNumber = foldingModel.regions.getStartLineNumber(foldingRegion.parentIndex); // Find fold at same level. while (foldingRegion !== null) { if (foldingRegion.regionIndex > 0) { foldingRegion = foldingModel.regions.toRegion(foldingRegion.regionIndex - 1); // Keep at same level. if (foldingRegion.startLineNumber <= minLineNumber) return null; else if (foldingRegion.parentIndex === expectedParentIndex) return foldingRegion.startLineNumber; } else { return null; } } } } return null; } /** * Get line to jump to for next fold at the same level of current line * @param lineNumber the current line number * @param foldingModel the folding model * * @return Next fold start line */ export function getJumpToNextFoldLine(lineNumber: number, foldingModel: FoldingModel): number | null { let foldingRegion = foldingModel.getRegionAtLine(lineNumber); if (foldingRegion !== null) { // Find max line number to stay within parent. let expectedParentIndex = foldingRegion.parentIndex; if (expectedParentIndex === -1) return null; let maxLineNumber = foldingModel.regions.getEndLineNumber(foldingRegion.parentIndex); // Find fold at same level. while (foldingRegion !== null) { if (foldingRegion.regionIndex < foldingModel.regions.length) { foldingRegion = foldingModel.regions.toRegion(foldingRegion.regionIndex + 1); // Keep at same level. if (foldingRegion.startLineNumber >= maxLineNumber) return null; else if (foldingRegion.parentIndex === expectedParentIndex) return foldingRegion.startLineNumber; } else { return null; } } } return null; } </ins>

*  Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; <del> import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateUp, setCollapseStateForRest } from 'vs/editor/contrib/folding/foldingModel'; </del> <ins> import { FoldingModel, setCollapseStateAtLevel, setCollapseStateLevelsDown, setCollapseStateLevelsUp, setCollapseStateForMatchingLines, setCollapseStateUp, setCollapseStateForRest, getJumpToParentFoldLine, getJumpToPreviousFoldLine, getJumpToNextFoldLine } from 'vs/editor/contrib/folding/foldingModel'; </ins> import { ModelDecorationOptions } from 'vs/editor/common/model/textModel'; import { createTextModel } from 'vs/editor/test/common/editorTestUtils'; import { computeRanges } from 'vs/editor/contrib/folding/indentRangeProvider';

}); <ins> test('fold jumping', () => { let lines = [ /* 1*/	'class A {', /* 2*/	'  void foo() {', /* 3*/	'    if (1) {', /* 4*/	'      a();', /* 5*/	'    } else if (2) {', /* 6*/	'      if (true) {', /* 7*/	'        b();', /* 8*/	'      }', /* 9*/	'    } else {', /* 10*/	'      c();', /* 11*/	'    }', /* 12*/	'  }', /* 13*/	'}' ]; let textModel = createTextModel(lines.join('n')); try { let foldingModel = new FoldingModel(textModel, new TestDecorationProvider(textModel)); let ranges = computeRanges(textModel, false, undefined); foldingModel.update(ranges); let r1 = r(1, 12, false); let r2 = r(2, 11, false); let r3 = r(3, 4, false); let r4 = r(5, 8, false); let r5 = r(6, 7, false); let r6 = r(9, 10, false); assertRanges(foldingModel, [r1, r2, r3, r4, r5, r6]); // Test jump to parent. assert.strictEqual(getJumpToParentFoldLine(7, foldingModel), 6); assert.strictEqual(getJumpToParentFoldLine(6, foldingModel), 5); assert.strictEqual(getJumpToParentFoldLine(5, foldingModel), 2); assert.strictEqual(getJumpToParentFoldLine(2, foldingModel), 1); assert.strictEqual(getJumpToParentFoldLine(1, foldingModel), null); // Test jump to previous. assert.strictEqual(getJumpToPreviousFoldLine(10, foldingModel), 9); assert.strictEqual(getJumpToPreviousFoldLine(9, foldingModel), 5); assert.strictEqual(getJumpToPreviousFoldLine(5, foldingModel), 3); assert.strictEqual(getJumpToPreviousFoldLine(3, foldingModel), null); // Test jump to next. assert.strictEqual(getJumpToNextFoldLine(3, foldingModel), 5); assert.strictEqual(getJumpToNextFoldLine(4, foldingModel), 5); assert.strictEqual(getJumpToNextFoldLine(5, foldingModel), 9); assert.strictEqual(getJumpToNextFoldLine(9, foldingModel), null); } finally { textModel.dispose(); } }); </ins> });