From a3981ea0b9b4e73353d53bc37a74552723237ff3 Mon Sep 17 00:00:00 2001 From: Rayat Date: Sat, 2 Apr 2022 14:50:15 -0700 Subject: [PATCH] Post forward or up commands rebase fixes; multi cursor seems to work! --- src/cursor-doc/paredit.ts | 197 +++++++++++------- .../unit/cursor-doc/indent-test.ts | 46 ++-- src/paredit/extension.ts | 6 +- 3 files changed, 153 insertions(+), 96 deletions(-) diff --git a/src/cursor-doc/paredit.ts b/src/cursor-doc/paredit.ts index 549537ef2..4171ee747 100644 --- a/src/cursor-doc/paredit.ts +++ b/src/cursor-doc/paredit.ts @@ -91,12 +91,14 @@ export function selectRight(doc: EditableDocument) { } export function selectForwardSexpOrUp(doc: EditableDocument) { - const rangeFn = - doc.selection.active >= doc.selection.anchor - ? forwardSexpOrUpRange - : (doc: EditableDocument) => forwardSexpOrUpRange(doc, doc.selection.active, true); - - selectRangeForward(doc, rangeFn(doc)); + const ranges = doc.selections.map((selection) => { + const rangeFn = + selection.active >= selection.anchor + ? (doc) => forwardSexpOrUpRange(doc, selection.end) + : (doc: EditableDocument) => forwardSexpOrUpRange(doc, selection.active, true); + return rangeFn(doc); + }); + selectRangeForward(doc, ranges); } export function selectBackwardSexp(doc: EditableDocument) { @@ -147,11 +149,14 @@ export function selectBackwardUpSexp(doc: EditableDocument) { } export function selectBackwardSexpOrUp(doc: EditableDocument) { - const rangeFn = - doc.selection.active <= doc.selection.anchor - ? (doc: EditableDocument) => backwardSexpOrUpRange(doc, doc.selection.active, false) - : (doc: EditableDocument) => backwardSexpOrUpRange(doc, doc.selection.active, false); - selectRangeBackward(doc, rangeFn(doc)); + const ranges = doc.selections.map((selection) => { + const rangeFn = + selection.active <= selection.anchor + ? (doc: EditableDocument) => backwardSexpOrUpRange(doc, selection.active, false) + : (doc: EditableDocument) => backwardSexpOrUpRange(doc, selection.active, false); + return rangeFn(doc); + }); + selectRangeBackward(doc, ranges); } export function selectCloseList(doc: EditableDocument) { @@ -197,34 +202,36 @@ enum GoUpSexpOption { */ function _forwardSexpRange( doc: EditableDocument, - offset = Math.max(doc.selection.anchor, doc.selection.active), + offsets = doc.selections.map((s) => s.end), goUpSexp: GoUpSexpOption, goPastWhitespace = false -): [number, number] { - const cursor = doc.getTokenCursor(offset); +): Array<[number, number]> { + return offsets.map((offset) => { + const cursor = doc.getTokenCursor(offset); - if (goUpSexp == GoUpSexpOption.Never || goUpSexp == GoUpSexpOption.WhenAtLimit) { - // Normalize our position by scooting to the beginning of the closest sexp - cursor.forwardWhitespace(); + if (goUpSexp == GoUpSexpOption.Never || goUpSexp == GoUpSexpOption.WhenAtLimit) { + // Normalize our position by scooting to the beginning of the closest sexp + cursor.forwardWhitespace(); - if (cursor.forwardSexp(true, true)) { - if (goPastWhitespace) { - cursor.forwardWhitespace(); + if (cursor.forwardSexp(true, true)) { + if (goPastWhitespace) { + cursor.forwardWhitespace(); + } + return [offset, cursor.offsetStart]; } - return [offset, cursor.offsetStart]; } - } - if (goUpSexp == GoUpSexpOption.Required || goUpSexp == GoUpSexpOption.WhenAtLimit) { - cursor.forwardList(); - if (cursor.upList()) { - if (goPastWhitespace) { - cursor.forwardWhitespace(); + if (goUpSexp == GoUpSexpOption.Required || goUpSexp == GoUpSexpOption.WhenAtLimit) { + cursor.forwardList(); + if (cursor.upList()) { + if (goPastWhitespace) { + cursor.forwardWhitespace(); + } + return [offset, cursor.offsetStart]; } - return [offset, cursor.offsetStart]; } - } - return [offset, offset]; + return [offset, offset]; + }); } /** @@ -232,57 +239,83 @@ function _forwardSexpRange( */ function _backwardSexpRange( doc: EditableDocument, - offset: number = Math.min(doc.selection.anchor, doc.selection.active), + offsets: number[] = doc.selections.map((s) => s.start), goUpSexp: GoUpSexpOption, goPastWhitespace = false -): [number, number] { - const cursor = doc.getTokenCursor(offset); - - if (goUpSexp == GoUpSexpOption.Never || goUpSexp == GoUpSexpOption.WhenAtLimit) { - if (!cursor.isWhiteSpace() && cursor.offsetStart < offset) { - // This is because cursor.backwardSexp() can't move backwards when "on" the first sexp inside a list - // TODO: Try to fix this in LispTokenCursor instead. - cursor.forwardSexp(); - } - cursor.backwardWhitespace(); +): Array<[number, number]> { + return offsets.map((offset) => { + const cursor = doc.getTokenCursor(offset); + + if (goUpSexp == GoUpSexpOption.Never || goUpSexp == GoUpSexpOption.WhenAtLimit) { + if (!cursor.isWhiteSpace() && cursor.offsetStart < offset) { + // This is because cursor.backwardSexp() can't move backwards when "on" the first sexp inside a list + // TODO: Try to fix this in LispTokenCursor instead. + cursor.forwardSexp(); + } + cursor.backwardWhitespace(); - if (cursor.backwardSexp(true, true)) { - if (goPastWhitespace) { - cursor.backwardWhitespace(); + if (cursor.backwardSexp(true, true)) { + if (goPastWhitespace) { + cursor.backwardWhitespace(); + } + return [cursor.offsetStart, offset]; } - return [cursor.offsetStart, offset]; } - } - if (goUpSexp == GoUpSexpOption.Required || goUpSexp == GoUpSexpOption.WhenAtLimit) { - cursor.backwardList(); - if (cursor.backwardUpList()) { - cursor.forwardSexp(true, true); - cursor.backwardSexp(true, true); - if (goPastWhitespace) { - cursor.backwardWhitespace(); + if (goUpSexp == GoUpSexpOption.Required || goUpSexp == GoUpSexpOption.WhenAtLimit) { + cursor.backwardList(); + if (cursor.backwardUpList()) { + cursor.forwardSexp(true, true); + cursor.backwardSexp(true, true); + if (goPastWhitespace) { + cursor.backwardWhitespace(); + } + return [cursor.offsetStart, offset]; } - return [cursor.offsetStart, offset]; } - } - return [offset, offset]; + return [offset, offset]; + }); } export function forwardSexpRange( doc: EditableDocument, - offset = Math.max(doc.selection.anchor, doc.selection.active), + offsets?: number[], + goPastWhitespace?: boolean +): Array<[number, number]>; +export function forwardSexpRange( + doc: EditableDocument, + offset?: number, + goPastWhitespace?: boolean +): [number, number]; +export function forwardSexpRange( + doc: EditableDocument, + oneOrMoreOffsets: number[]|number = doc.selections.map((s) => s.end), goPastWhitespace = false -): [number, number] { - return _forwardSexpRange(doc, offset, GoUpSexpOption.Never, goPastWhitespace); +): Array<[number, number]> | [number, number] { + const offsets = Array.isArray(oneOrMoreOffsets) ? oneOrMoreOffsets : [oneOrMoreOffsets]; + const ranges = _forwardSexpRange(doc, offsets, GoUpSexpOption.Never, goPastWhitespace); + return Array.isArray(oneOrMoreOffsets) ? ranges : ranges[0]; } export function backwardSexpRange( doc: EditableDocument, - offset: number = Math.min(doc.selection.anchor, doc.selection.active), + offsets?: number[], + goPastWhitespace?: boolean +): Array<[number, number]>; +export function backwardSexpRange( + doc: EditableDocument, + offset?: number, + goPastWhitespace?: boolean +): [number, number]; +export function backwardSexpRange( + doc: EditableDocument, + oneOrMoreOffsets: number[] | number = doc.selections.map((s) => s.start), goPastWhitespace = false -): [number, number] { - return _backwardSexpRange(doc, offset, GoUpSexpOption.Never, goPastWhitespace); +): Array<[number, number]> | [number, number] { + const offsets = Array.isArray(oneOrMoreOffsets) ? oneOrMoreOffsets : [oneOrMoreOffsets]; + const ranges = _backwardSexpRange(doc, offsets, GoUpSexpOption.Never, goPastWhitespace); + return Array.isArray(oneOrMoreOffsets) ? ranges : ranges[0]; } export function forwardListRange( @@ -418,7 +451,7 @@ export function rangeToForwardUpList( offset: number = doc.selections[0].end, goPastWhitespace = false ): [number, number] { - return _forwardSexpRange(doc, offset, GoUpSexpOption.Required, goPastWhitespace); + return _forwardSexpRange(doc, [offset], GoUpSexpOption.Required, goPastWhitespace)[0]; } export function rangeToBackwardUpList( @@ -427,23 +460,47 @@ export function rangeToBackwardUpList( offset: number = doc.selections[0].start, goPastWhitespace = false ): [number, number] { - return _backwardSexpRange(doc, offset, GoUpSexpOption.Required, goPastWhitespace); + return _backwardSexpRange(doc, [offset], GoUpSexpOption.Required, goPastWhitespace)[0]; } export function forwardSexpOrUpRange( doc: EditableDocument, - offset = Math.max(doc.selection.anchor, doc.selection.active), + offsets?: number[], + goPastWhitespace?: boolean +): Array<[number, number]>; +export function forwardSexpOrUpRange( + doc: EditableDocument, + offset?: number, + goPastWhitespace?: boolean +): [number, number]; +export function forwardSexpOrUpRange( + doc: EditableDocument, + oneOrMoreOffsets: number[] | number = doc.selections.map((s) => s.end), goPastWhitespace = false -): [number, number] { - return _forwardSexpRange(doc, offset, GoUpSexpOption.WhenAtLimit, goPastWhitespace); +): Array<[number, number]> | [number, number] { + const offsets = isNumber(oneOrMoreOffsets) ? [oneOrMoreOffsets] : oneOrMoreOffsets; + const ranges = _forwardSexpRange(doc, offsets, GoUpSexpOption.WhenAtLimit, goPastWhitespace); + return isNumber(oneOrMoreOffsets) ? ranges[0] : ranges; } export function backwardSexpOrUpRange( doc: EditableDocument, - offset: number = Math.min(doc.selection.anchor, doc.selection.active), + offsets?: number[], + goPastWhitespace?: boolean +): Array<[number, number]>; +export function backwardSexpOrUpRange( + doc: EditableDocument, + offset?: number, + goPastWhitespace?: boolean +): [number, number]; +export function backwardSexpOrUpRange( + doc: EditableDocument, + oneOrMoreOffsets: number[] | number = doc.selections.map((s) => s.start), goPastWhitespace = false -): [number, number] { - return _backwardSexpRange(doc, offset, GoUpSexpOption.WhenAtLimit, goPastWhitespace); +): Array<[number, number]> | [number, number] { + const offsets = isNumber(oneOrMoreOffsets) ? [oneOrMoreOffsets] : oneOrMoreOffsets; + const ranges = _backwardSexpRange(doc, offsets, GoUpSexpOption.WhenAtLimit, goPastWhitespace); + return isNumber(oneOrMoreOffsets) ? ranges[0] : ranges; } export function rangeToForwardDownList( diff --git a/src/extension-test/unit/cursor-doc/indent-test.ts b/src/extension-test/unit/cursor-doc/indent-test.ts index 1b75a0580..954545f02 100644 --- a/src/extension-test/unit/cursor-doc/indent-test.ts +++ b/src/extension-test/unit/cursor-doc/indent-test.ts @@ -11,22 +11,22 @@ describe('indent', () => { describe('lists', () => { it('calculates indents for cursor in empty list', () => { const doc = docFromTextNotation('(|)'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(1); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(1); }); it('calculates indents for cursor in empty list prepended by text', () => { const doc = docFromTextNotation(' a b (|)'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(7); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(7); }); it('calculates indents for empty list inside vector', () => { const doc = docFromTextNotation('[(|)]'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(2); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(2); }); it("calculates indents for cursor in at arg 0 in `[['inner' 0]]`", () => { const doc = docFromTextNotation('(foo|)'); expect( indent.getIndent( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig({ '#"^\\w"': [['inner', 0]], }) @@ -38,7 +38,7 @@ describe('indent', () => { expect( indent.getIndent( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig({ '#"^\\w"': [['inner', 0]], }) @@ -50,7 +50,7 @@ describe('indent', () => { expect( indent.getIndent( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig({ '#"^\\w"': [['block', 1]], }) @@ -62,7 +62,7 @@ describe('indent', () => { expect( indent.getIndent( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig({ '#"^\\w"': [['block', 1]], }) @@ -74,11 +74,11 @@ describe('indent', () => { describe('vectors', () => { it('calculates indents for cursor in empty vector', () => { const doc = docFromTextNotation('[|]'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(1); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(1); }); it('calculates indents for cursor in empty vector inside list', () => { const doc = docFromTextNotation('([|])'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(2); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(2); }); it('does not use indent rules for vectors with symbols at ”call” position', () => { // https://github.com/BetterThanTomorrow/calva/issues/1622 @@ -86,7 +86,7 @@ describe('indent', () => { expect( indent.getIndent( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig({ '#"^\\w"': [['inner', 0]], }) @@ -98,11 +98,11 @@ describe('indent', () => { describe('maps', () => { it('calculates indents for cursor in empty map', () => { const doc = docFromTextNotation('{|}'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(1); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(1); }); it('calculates indents for cursor in empty map inside list inside a vector', () => { const doc = docFromTextNotation('([{|}])'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(3); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(3); }); it('does not use indent rules for maps with symbols at ”call” position', () => { // https://github.com/BetterThanTomorrow/calva/issues/1622 @@ -110,7 +110,7 @@ describe('indent', () => { expect( indent.getIndent( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig({ '#"^\\w"': [['inner', 0]], }) @@ -122,11 +122,11 @@ describe('indent', () => { describe('sets', () => { it('calculates indents for cursor in empty set', () => { const doc = docFromTextNotation('#{|}'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(2); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(2); }); it('calculates indents for cursor in empty set inside list inside a vector', () => { const doc = docFromTextNotation('([#{|}])'); - expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0])).toEqual(4); + expect(indent.getIndent(doc.model, textAndSelection(doc)[1][0][0])).toEqual(4); }); it('does not use indent rules for maps with symbols at ”call” position', () => { // https://github.com/BetterThanTomorrow/calva/issues/1622 @@ -134,7 +134,7 @@ describe('indent', () => { expect( indent.getIndent( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig({ '#"^\\w"': [['inner', 0]], }) @@ -153,7 +153,7 @@ describe('indent', () => { }; const state: indent.IndentInformation[] = indent.collectIndents( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig(rules) ); expect(state.length).toEqual(1); @@ -166,7 +166,7 @@ describe('indent', () => { }; const state: indent.IndentInformation[] = indent.collectIndents( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig(rules) ); expect(state.length).toEqual(1); @@ -183,7 +183,7 @@ describe('indent', () => { }; const state: indent.IndentInformation[] = indent.collectIndents( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig(rules) ); expect(state.length).toEqual(1); @@ -196,7 +196,7 @@ describe('indent', () => { }; const state: indent.IndentInformation[] = indent.collectIndents( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig(rules) ); expect(state.length).toEqual(1); @@ -210,7 +210,7 @@ describe('indent', () => { }; const state: indent.IndentInformation[] = indent.collectIndents( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig(rules) ); expect(state.length).toEqual(1); @@ -228,7 +228,7 @@ describe('indent', () => { }; const state: indent.IndentInformation[] = indent.collectIndents( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig(rules) ); expect(state.length).toEqual(1); @@ -246,7 +246,7 @@ describe('indent', () => { }; const state: indent.IndentInformation[] = indent.collectIndents( doc.model, - textAndSelection(doc)[1][0], + textAndSelection(doc)[1][0][0], mkConfig(rules) ); expect(state.length).toEqual(1); diff --git a/src/paredit/extension.ts b/src/paredit/extension.ts index 0d2f2ded5..3b6f45293 100644 --- a/src/paredit/extension.ts +++ b/src/paredit/extension.ts @@ -12,7 +12,7 @@ import { } from 'vscode'; import * as paredit from '../cursor-doc/paredit'; import * as docMirror from '../doc-mirror/index'; -import { EditableDocument } from '../cursor-doc/model'; +import { EditableDocument, ModelEditResult, ModelEditSelection } from '../cursor-doc/model'; import { assertIsDefined } from '../utilities'; const onPareditKeyMapChangedEmitter = new EventEmitter(); @@ -40,7 +40,7 @@ function shouldKillAlsoCutToClipboard() { type PareditCommand = { command: string; - handler: (doc: EditableDocument) => void; + handler: (doc: EditableDocument) => void | Thenable | Thenable; }; const pareditCommands: PareditCommand[] = [ // NAVIGATING @@ -439,7 +439,7 @@ function wrapPareditCommand(command: PareditCommand) { if (!enabled || !languages.has(textEditor.document.languageId)) { return; } - command.handler(mDoc); + void command.handler(mDoc); } catch (e) { console.error(e.message); }