Skip to content

Commit

Permalink
Merge pull request #2419 from BetterThanTomorrow/wip/rayat/multicurso…
Browse files Browse the repository at this point in the history
…r/prep/refactor-testing-utils

Paredit Multicursor - Testing Utils - textNotation changes and diagnostic utils
  • Loading branch information
PEZ authored Mar 8, 2024
2 parents 32f1cef + 2f80256 commit 519fa75
Show file tree
Hide file tree
Showing 9 changed files with 530 additions and 109 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ Changes to Calva.

## [Unreleased]

- Internal textNotation testing system supports multiple selections, addressing [#610](https://github.com/BetterThanTomorrow/calva/issues/610)
- Add new Calva development utility commands to create textNotations from open buffers, and vice versa

## [2.0.415] - 2024-03-08

- Refactor some internal document and selection APIs in preparation for multiple selections, addressing [#610](https://github.com/BetterThanTomorrow/calva/issues/610)
Expand Down
10 changes: 10 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -1170,6 +1170,16 @@
"title": "Toggle nREPL Logging Enabled",
"category": "Calva Diagnostics"
},
{
"command": "calva.diagnostics.printTextNotationFromDocument",
"title": "Print TextNotation from the current document to Calva says",
"category": "Calva Diagnostics"
},
{
"command": "calva.diagnostics.createDocumentFromTextNotation",
"title": "Create a new Clojure Document from TextNotation",
"category": "Calva Diagnostics"
},
{
"command": "calva.linting.resolveMacroAs",
"title": "Resolve Macro As",
Expand Down
125 changes: 118 additions & 7 deletions src/cursor-doc/model.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Scanner, Token, ScannerState } from './clojure-lexer';
import { LispTokenCursor } from './token-cursor';
import { deepEqual as equal } from '../util/object';
import { isUndefined } from 'lodash';
import { isNumber, isUndefined } from 'lodash';
import { TextDocument, Selection } from 'vscode';

let scanner: Scanner;

Expand Down Expand Up @@ -65,22 +66,55 @@ export class ModelEdit<T extends ModelEditFunction> {
export class ModelEditSelection {
private _anchor: number;
private _active: number;

constructor(anchor: number, active?: number) {
this._anchor = anchor;
if (active !== undefined) {
this._active = active;
private _start: number;
private _end: number;
private _isReversed: boolean;

constructor(anchor: number, active?: number, start?: number, end?: number, isReversed?: boolean);
constructor(selection: Selection, doc: TextDocument);
constructor(
anchorOrSelection: number | Selection,
activeOrDoc?: number | TextDocument,
start?: number,
end?: number,
isReversed?: boolean
) {
if (isNumber(anchorOrSelection)) {
const anchor = anchorOrSelection;
this._anchor = anchor;
if (activeOrDoc !== undefined && isNumber(activeOrDoc)) {
this._active = activeOrDoc;
} else {
this._active = anchor;
}
const _isReversed = isReversed ?? this._anchor > this._active;
this._isReversed = _isReversed;
this._start = start ?? Math.min(anchor, this._active);
this._end = end ?? Math.max(anchor, this._active);
} else {
this._active = anchor;
const { active, anchor, start, end, isReversed } = anchorOrSelection;
const doc = activeOrDoc as TextDocument;
this._active = doc.offsetAt(active);
this._anchor = doc.offsetAt(anchor);
this._start = doc.offsetAt(start);
this._end = doc.offsetAt(end);
this._isReversed = isReversed;
}
}

private _updateDirection() {
this._start = Math.min(this._anchor, this._active);
this._end = Math.max(this._anchor, this._active);
this._isReversed = this._active < this._anchor;
}

get anchor() {
return this._anchor;
}

set anchor(v: number) {
this._anchor = v;
this._updateDirection();
}

get active() {
Expand All @@ -89,11 +123,83 @@ export class ModelEditSelection {

set active(v: number) {
this._active = v;
this._updateDirection();
}

get start() {
this._updateDirection();
return this._start;
}

get end() {
this._updateDirection();
return this._end;
}

get isCursor() {
return this.anchor === this.active;
}

get isSelection() {
return this.anchor !== this.active;
}

get isReversed() {
this._updateDirection();
return this._isReversed;
}

set isReversed(isReversed: boolean) {
this._isReversed = isReversed;
if (this._isReversed) {
this._start = this._active;
this._end = this._anchor;
} else {
this._start = this._anchor;
this._end = this._active;
}
}

get distance() {
return this._end - this._start;
}

clone() {
return new ModelEditSelection(this._anchor, this._active);
}

/**
* Returns a simple 2-item tuple representing the
* [leftmost/earliest/start position, rightmost, farthest, end position].
*/
get asRange() {
return [this.start, this.end] as [start: number, end: number];
}

/**
* Same as `ModelEditSelection.asRange` but with the leftmost item being the anchor position, and the rightmost item
* being the active position. This way, you can tell if it's reversed by checking if the leftmost item is greater
* than the rightmost item.
*/
get asDirectedRange() {
return [this.anchor, this.active] as [anchor: number, active: number];
}

/**
* Mutates itself!
* Very basic, offsets both active/anchor by a positive or negative number lol, with no attempt at clamping.
*
* Returns self for convenience
* @param offset number
*/
reposition(offset: number) {
this.active += offset;
this.anchor += offset;

this._updateDirection();

return this;
}
}

export type ModelEditOptions = {
Expand All @@ -105,6 +211,7 @@ export type ModelEditOptions = {

export interface EditableModel {
readonly lineEndingLength: number;
readonly lineEnding: string;

/**
* Performs a model edit batch.
Expand Down Expand Up @@ -135,6 +242,10 @@ export class LineInputModel implements EditableModel {
/** How many characters in the line endings of the text of this model? */
constructor(readonly lineEndingLength: number = 1, private document?: EditableDocument) {}

get lineEnding() {
return this.lineEndingLength === 1 ? '\n' : '\r\n';
}

/** The input lines. */
lines: TextLine[] = [new TextLine('', this.getStateForLine(0))];

Expand Down
33 changes: 22 additions & 11 deletions src/doc-mirror/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ export class DocumentModel implements EditableModel {
this.lineInputModel = new LineInputModel(this.lineEndingLength);
}

get lineEnding() {
return this.lineEndingLength == 2 ? '\r\n' : '\n';
}

edit(modelEdits: ModelEdit<ModelEditFunction>[], options: ModelEditOptions): Thenable<boolean> {
const editor = utilities.getActiveTextEditor(),
undoStopBefore = !!options.undoStopBefore;
Expand Down Expand Up @@ -146,21 +150,28 @@ export class MirroredDocument implements EditableDocument {
});
}

set selections(selections: ModelEditSelection[]) {
get selections(): ModelEditSelection[] {
const editor = utilities.getActiveTextEditor(),
document = editor.document,
anchor = document.positionAt(selections[0].anchor),
active = document.positionAt(selections[0].active);
editor.selections = [new vscode.Selection(anchor, active)];
editor.revealRange(new vscode.Range(active, active));
document = editor.document;
return editor.selections.map((sel) => {
const anchor = document.offsetAt(sel.anchor),
active = document.offsetAt(sel.active);
return new ModelEditSelection(anchor, active);
});
}

get selections(): ModelEditSelection[] {
set selections(selections: ModelEditSelection[]) {
const editor = utilities.getActiveTextEditor(),
document = editor.document,
anchor = document.offsetAt(editor.selections[0].anchor),
active = document.offsetAt(editor.selections[0].active);
return [new ModelEditSelection(anchor, active)];
document = editor.document;
editor.selections = selections.map((selection) => {
const anchor = document.positionAt(selection.anchor),
active = document.positionAt(selection.active);
return new vscode.Selection(anchor, active);
});

const primarySelection = selections[0];
const active = document.positionAt(primarySelection.active);
editor.revealRange(new vscode.Range(active, active));
}

public getSelectionText() {
Expand Down
Loading

0 comments on commit 519fa75

Please sign in to comment.