api: remove all circular dependencies

This commit is contained in:
Grégoire Geis 2021-11-06 00:18:55 +01:00
parent d17c6cdebd
commit a3213bdb37
25 changed files with 426 additions and 399 deletions

View File

@ -4,21 +4,19 @@ module.exports = {
{
name: "no-circular",
severity: "error",
from: {
pathNot: "^src/api",
},
from: {},
to: {
circular: true,
},
},
{
name: "api-only-depends-on-api/index-and-utils",
name: "api-only-depends-on-api-and-utils",
severity: "error",
from: {
path: "^src/api/(?!index)",
},
to: {
pathNot: "^src/(api/index|utils)",
pathNot: "^src/(api/(?!index)|utils)",
},
},
{
@ -32,10 +30,10 @@ module.exports = {
},
},
{
name: "only-api/index-depends-on-api",
name: "only-api-depends-on-api",
severity: "error",
from: {
pathNot: "^src/api/index",
pathNot: "^src/api",
},
to: {
path: "^src/api/(?!index)",

View File

@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { Context } from ".";
import { Context } from "./context";
/**
* Copies the given text to the clipboard.

View File

@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { SelectionBehavior, Selections } from ".";
import { SelectionBehavior } from "./types";
import type { CommandDescriptor } from "../commands";
import type { PerEditorState } from "../state/editors";
import type { Extension } from "../state/extension";
@ -302,7 +302,7 @@ export class Context extends ContextWithoutActiveEditor {
const editor = this.editor as vscode.TextEditor;
if (this.selectionBehavior === SelectionBehavior.Character) {
return Selections.fromCharacterMode(editor.selections, editor.document);
return selectionsFromCharacterMode(editor.selections, editor.document);
}
return editor.selections;
@ -318,7 +318,7 @@ export class Context extends ContextWithoutActiveEditor {
const editor = this.editor as vscode.TextEditor;
if (this.selectionBehavior === SelectionBehavior.Character) {
selections = Selections.toCharacterMode(selections, editor.document);
selections = selectionsToCharacterMode(selections, editor.document);
}
editor.selections = selections as vscode.Selection[];
@ -333,7 +333,7 @@ export class Context extends ContextWithoutActiveEditor {
const editor = this.editor as vscode.TextEditor;
if (this.selectionBehavior === SelectionBehavior.Character) {
return Selections.fromCharacterMode([editor.selection], editor.document)[0];
return selectionsFromCharacterMode([editor.selection], editor.document)[0];
}
return editor.selection;
@ -581,3 +581,221 @@ export function insertUndoStop(editor?: vscode.TextEditor) {
return Context.current.insertUndoStop();
}
/**
* Transforms a list of caret-mode selections (that is, regular selections as
* manipulated internally) into a list of character-mode selections (that is,
* selections modified to include a block character in them).
*
* This function should be used before setting the selections of a
* `vscode.TextEditor` if the selection behavior is `Character`.
*
* ### Example
* Forward-facing, non-empty selections are reduced by one character.
*
* ```js
* // One-character selection becomes empty.
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 0, 0, 1)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 0),
* ]);
*
* // One-character selection becomes empty (at line break).
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 1, 1, 0)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 1),
* ]);
*
* // Forward-facing selection becomes shorter.
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 0, 1, 1)]), "to satisfy", [
* expect.it("to have anchor at coords", 0, 0).and("to have cursor at coords", 1, 0),
* ]);
*
* // One-character selection becomes empty (reversed).
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 1, 0, 0)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 0),
* ]);
*
* // One-character selection becomes empty (reversed, at line break).
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(1, 0, 0, 1)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 1),
* ]);
*
* // Reversed selection stays as-is.
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(1, 1, 0, 0)]), "to satisfy", [
* expect.it("to have anchor at coords", 1, 1).and("to have cursor at coords", 0, 0),
* ]);
*
* // Empty selection stays as-is.
* expect(Selections.toCharacterMode([Selections.empty(1, 1)]), "to satisfy", [
* expect.it("to be empty at coords", 1, 1),
* ]);
* ```
*
* With:
* ```
* a
* b
* ```
*/
export function selectionsToCharacterMode(
selections: readonly vscode.Selection[],
document?: vscode.TextDocument,
) {
const characterModeSelections = [] as vscode.Selection[];
for (const selection of selections) {
const selectionActive = selection.active,
selectionActiveLine = selectionActive.line,
selectionActiveCharacter = selectionActive.character,
selectionAnchor = selection.anchor,
selectionAnchorLine = selectionAnchor.line,
selectionAnchorCharacter = selectionAnchor.character;
let active = selectionActive,
anchor = selectionAnchor,
changed = false;
if (selectionAnchorLine === selectionActiveLine) {
if (selectionAnchorCharacter + 1 === selectionActiveCharacter) {
// Selection is one-character long: make it empty.
active = selectionAnchor;
changed = true;
} else if (selectionAnchorCharacter - 1 === selectionActiveCharacter) {
// Selection is reversed and one-character long: make it empty.
anchor = selectionActive;
changed = true;
} else if (selectionAnchorCharacter < selectionActiveCharacter) {
// Selection is strictly forward-facing: make it shorter.
active = new vscode.Position(selectionActiveLine, selectionActiveCharacter - 1);
changed = true;
} else {
// Selection is reversed or empty: do nothing.
}
} else if (selectionAnchorLine < selectionActiveLine) {
// Selection is strictly forward-facing: make it shorter.
if (selectionActiveCharacter > 0) {
active = new vscode.Position(selectionActiveLine, selectionActiveCharacter - 1);
changed = true;
} else {
// The active character is the first one, so we have to get some
// information from the document.
if (document === undefined) {
document = Context.current.document;
}
const activePrevLine = selectionActiveLine - 1,
activePrevLineLength = document.lineAt(activePrevLine).text.length;
active = new vscode.Position(activePrevLine, activePrevLineLength);
changed = true;
}
} else if (selectionAnchorLine === selectionActiveLine + 1
&& selectionAnchorCharacter === 0
&& selectionActiveCharacter === (document ?? (document = Context.current.document))
.lineAt(selectionActiveLine).text.length) {
// Selection is reversed and one-character long: make it empty.
anchor = selectionActive;
changed = true;
} else {
// Selection is reversed: do nothing.
}
characterModeSelections.push(changed ? new vscode.Selection(anchor, active) : selection);
}
return characterModeSelections;
}
/**
* Reverses the changes made by `toCharacterMode` by increasing by one the
* length of every empty or forward-facing selection.
*
* This function should be used on the selections of a `vscode.TextEditor` if
* the selection behavior is `Character`.
*
* ### Example
* Selections remain empty in empty documents.
*
* ```js
* expect(Selections.fromCharacterMode([Selections.empty(0, 0)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 0),
* ]);
* ```
*
* With:
* ```
* ```
*
* ### Example
* Empty selections automatically become 1-character selections.
*
* ```js
* expect(Selections.fromCharacterMode([Selections.empty(0, 0)]), "to satisfy", [
* expect.it("to have anchor at coords", 0, 0).and("to have cursor at coords", 0, 1),
* ]);
*
* // At the end of the line, it selects the line ending:
* expect(Selections.fromCharacterMode([Selections.empty(0, 1)]), "to satisfy", [
* expect.it("to have anchor at coords", 0, 1).and("to have cursor at coords", 1, 0),
* ]);
*
* // But it does nothing at the end of the document:
* expect(Selections.fromCharacterMode([Selections.empty(2, 0)]), "to satisfy", [
* expect.it("to be empty at coords", 2, 0),
* ]);
* ```
*
* With:
* ```
* a
* b
*
* ```
*/
export function selectionsFromCharacterMode(
selections: readonly vscode.Selection[],
document?: vscode.TextDocument,
) {
const caretModeSelections = [] as vscode.Selection[];
for (const selection of selections) {
const selectionActive = selection.active,
selectionActiveLine = selectionActive.line,
selectionActiveCharacter = selectionActive.character,
selectionAnchor = selection.anchor,
selectionAnchorLine = selectionAnchor.line,
selectionAnchorCharacter = selectionAnchor.character;
let active = selectionActive,
changed = false;
const isEmptyOrForwardFacing = selectionAnchorLine < selectionActiveLine
|| (selectionAnchorLine === selectionActiveLine
&& selectionAnchorCharacter <= selectionActiveCharacter);
if (isEmptyOrForwardFacing) {
// Selection is empty or forward-facing: extend it if possible.
if (document === undefined) {
document = Context.current.document;
}
const lineLength = document.lineAt(selectionActiveLine).text.length;
if (selectionActiveCharacter === lineLength) {
// Character is at the end of the line.
if (selectionActiveLine + 1 < document.lineCount) {
// This is not the last line: we can extend the selection.
active = new vscode.Position(selectionActiveLine + 1, 0);
changed = true;
} else {
// This is the last line: we cannot do anything.
}
} else {
// Character is not at the end of the line: we can extend the selection.
active = new vscode.Position(selectionActiveLine, selectionActiveCharacter + 1);
changed = true;
}
}
caretModeSelections.push(changed ? new vscode.Selection(selectionAnchor, active) : selection);
}
return caretModeSelections;
}

View File

@ -1,6 +1,8 @@
import * as vscode from "vscode";
import { Context, edit, Positions, Selections } from "..";
import { Context, edit } from "../context";
import { Positions } from "../positions";
import { Selections } from "../selections";
import { TrackedSelection } from "../../utils/tracked-selection";
const enum Constants {

View File

@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { Context, edit } from "..";
import { Context, edit } from "../context";
import { blankCharacters } from "../../utils/charset";
/**

View File

@ -1,4 +1,4 @@
import { Context } from ".";
import { Context } from "./context";
export * from "../utils/errors";

View File

@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { Context } from ".";
import { Context } from "./context";
/**
* Un-does the last action.

View File

@ -23,61 +23,7 @@ export * from "./search/pairs";
export * from "./search/range";
export * from "./search/word";
export * from "./selections";
/**
* Direction of an operation.
*/
export const enum Direction {
/**
* Forward direction (`1`).
*/
Forward = 1,
/**
* Backward direction (`-1`).
*/
Backward = -1,
}
/**
* Behavior of a shift.
*/
export const enum Shift {
/**
* Jump to the position.
*/
Jump,
/**
* Select to the position.
*/
Select,
/**
* Extend to the position.
*/
Extend,
}
/**
* Selection behavior of an operation.
*/
export const enum SelectionBehavior {
/**
* VS Code-like caret selections.
*/
Caret = 1,
/**
* Kakoune-like character selections.
*/
Character = 2,
}
export const Forward = Direction.Forward,
Backward = Direction.Backward,
Jump = Shift.Jump,
Select = Shift.Select,
Extend = Shift.Extend;
export * from "./types";
/**
* Returns the module exported by the extension with the given identifier.

View File

@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { Context } from ".";
import { Context } from "./context";
/**
* Returns the 0-based number of the first visible line in the current editor.

View File

@ -1,6 +1,7 @@
import * as vscode from "vscode";
import { Context, keypress, prompt } from ".";
import { Context } from "./context";
import { keypress, prompt } from "./prompt";
export interface Menu {
readonly items: Menu.Items;

View File

@ -1,4 +1,4 @@
import { Context } from ".";
import { Context } from "./context";
/**
* Switches to the mode with the given name.

View File

@ -1,6 +1,7 @@
import * as vscode from "vscode";
import { Context, Direction } from ".";
import { Context } from "./context";
import { Direction } from "./types";
/**
* Returns the position right after the given position, or `undefined` if

View File

@ -1,6 +1,7 @@
import * as vscode from "vscode";
import { Context, Selections } from ".";
import { Context } from "./context";
import { Selections } from "./selections";
import type { Input, SetInput } from "../commands";
import { CancellationError } from "../utils/errors";

View File

@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { Context } from ".";
import { Context } from "./context";
import type { Register } from "../state/registers";
/**

View File

@ -1,6 +1,6 @@
import * as vscode from "vscode";
import { Context } from ".";
import { Context } from "./context";
import type { CommandDescriptor } from "../commands";
import { parseRegExpWithReplacement } from "../utils/regexp";

View File

@ -1,6 +1,8 @@
import * as vscode from "vscode";
import { Context, Direction, Positions } from "..";
import { Context } from "../context";
import { Positions } from "../positions";
import { Direction } from "../types";
import { canMatchLineFeed, execLast, matchesStaticStrings } from "../../utils/regexp";
/**

View File

@ -1,6 +1,8 @@
import * as vscode from "vscode";
import { Context, lineByLine, Positions } from "..";
import { lineByLine } from "./move";
import { Context } from "../context";
import { Positions } from "../positions";
/**
* Returns the range of lines matching the given `RegExp` before and after

View File

@ -1,6 +1,8 @@
import * as vscode from "vscode";
import { Context, Direction, Positions } from "..";
import { Context } from "../context";
import { Positions } from "../positions";
import { Direction } from "../types";
/**
* Moves the given position towards the given direction until the given string

View File

@ -1,6 +1,8 @@
import * as vscode from "vscode";
import { Context, Direction, Positions } from "..";
import { Context } from "../context";
import { Positions } from "../positions";
import { Direction } from "../types";
/**
* Moves the given position towards the given direction as long as the given

View File

@ -1,6 +1,9 @@
import * as vscode from "vscode";
import { Context, Direction, Positions, search } from "..";
import { search } from "./index";
import { Context } from "../context";
import { Positions } from "../positions";
import { Direction } from "../types";
import { ArgumentError } from "../../utils/errors";
import { anyRegExp, escapeForRegExp } from "../../utils/regexp";

View File

@ -1,6 +1,10 @@
import * as vscode from "vscode";
import { Context, Direction, Lines, moveWhile, Positions } from "..";
import { moveWhile } from "./move";
import { Context } from "../context";
import { Lines } from "../lines";
import { Positions } from "../positions";
import { Direction } from "../types";
import { CharSet, getCharSetFunction } from "../../utils/charset";
import { CharCodes } from "../../utils/regexp";

View File

@ -1,6 +1,8 @@
import * as vscode from "vscode";
import { Context, Direction, SelectionBehavior, skipEmptyLines } from "..";
import { skipEmptyLines } from "./move";
import { Context } from "../context";
import { Direction, SelectionBehavior } from "../types";
import { CharSet, getCharSetFunction } from "../../utils/charset";
const enum WordCategory {

View File

@ -1,6 +1,9 @@
import * as vscode from "vscode";
import { Context, Direction, Lines, NotASelectionError, Positions, SelectionBehavior, Shift } from ".";
import { Context, selectionsFromCharacterMode, selectionsToCharacterMode } from "./context";
import { NotASelectionError } from "./errors";
import { Positions } from "./positions";
import { Direction, SelectionBehavior, Shift } from "./types";
import { execRange, splitRange } from "../utils/regexp";
/**
@ -1768,222 +1771,8 @@ export namespace Selections {
}
}
/**
* Transforms a list of caret-mode selections (that is, regular selections as
* manipulated internally) into a list of character-mode selections (that is,
* selections modified to include a block character in them).
*
* This function should be used before setting the selections of a
* `vscode.TextEditor` if the selection behavior is `Character`.
*
* ### Example
* Forward-facing, non-empty selections are reduced by one character.
*
* ```js
* // One-character selection becomes empty.
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 0, 0, 1)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 0),
* ]);
*
* // One-character selection becomes empty (at line break).
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 1, 1, 0)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 1),
* ]);
*
* // Forward-facing selection becomes shorter.
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 0, 1, 1)]), "to satisfy", [
* expect.it("to have anchor at coords", 0, 0).and("to have cursor at coords", 1, 0),
* ]);
*
* // One-character selection becomes empty (reversed).
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 1, 0, 0)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 0),
* ]);
*
* // One-character selection becomes empty (reversed, at line break).
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(1, 0, 0, 1)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 1),
* ]);
*
* // Reversed selection stays as-is.
* expect(Selections.toCharacterMode([Selections.fromAnchorActive(1, 1, 0, 0)]), "to satisfy", [
* expect.it("to have anchor at coords", 1, 1).and("to have cursor at coords", 0, 0),
* ]);
*
* // Empty selection stays as-is.
* expect(Selections.toCharacterMode([Selections.empty(1, 1)]), "to satisfy", [
* expect.it("to be empty at coords", 1, 1),
* ]);
* ```
*
* With:
* ```
* a
* b
* ```
*/
export function toCharacterMode(
selections: readonly vscode.Selection[],
document?: vscode.TextDocument,
) {
const characterModeSelections = [] as vscode.Selection[];
for (const selection of selections) {
const selectionActive = selection.active,
selectionActiveLine = selectionActive.line,
selectionActiveCharacter = selectionActive.character,
selectionAnchor = selection.anchor,
selectionAnchorLine = selectionAnchor.line,
selectionAnchorCharacter = selectionAnchor.character;
let active = selectionActive,
anchor = selectionAnchor,
changed = false;
if (selectionAnchorLine === selectionActiveLine) {
if (selectionAnchorCharacter + 1 === selectionActiveCharacter) {
// Selection is one-character long: make it empty.
active = selectionAnchor;
changed = true;
} else if (selectionAnchorCharacter - 1 === selectionActiveCharacter) {
// Selection is reversed and one-character long: make it empty.
anchor = selectionActive;
changed = true;
} else if (selectionAnchorCharacter < selectionActiveCharacter) {
// Selection is strictly forward-facing: make it shorter.
active = new vscode.Position(selectionActiveLine, selectionActiveCharacter - 1);
changed = true;
} else {
// Selection is reversed or empty: do nothing.
}
} else if (selectionAnchorLine < selectionActiveLine) {
// Selection is strictly forward-facing: make it shorter.
if (selectionActiveCharacter > 0) {
active = new vscode.Position(selectionActiveLine, selectionActiveCharacter - 1);
changed = true;
} else {
// The active character is the first one, so we have to get some
// information from the document.
if (document === undefined) {
document = Context.current.document;
}
const activePrevLine = selectionActiveLine - 1,
activePrevLineLength = document.lineAt(activePrevLine).text.length;
active = new vscode.Position(activePrevLine, activePrevLineLength);
changed = true;
}
} else if (selectionAnchorLine === selectionActiveLine + 1
&& selectionAnchorCharacter === 0
&& selectionActiveCharacter === Lines.length(selectionActiveLine, document)) {
// Selection is reversed and one-character long: make it empty.
anchor = selectionActive;
changed = true;
} else {
// Selection is reversed: do nothing.
}
characterModeSelections.push(changed ? new vscode.Selection(anchor, active) : selection);
}
return characterModeSelections;
}
/**
* Reverses the changes made by `toCharacterMode` by increasing by one the
* length of every empty or forward-facing selection.
*
* This function should be used on the selections of a `vscode.TextEditor` if
* the selection behavior is `Character`.
*
* ### Example
* Selections remain empty in empty documents.
*
* ```js
* expect(Selections.fromCharacterMode([Selections.empty(0, 0)]), "to satisfy", [
* expect.it("to be empty at coords", 0, 0),
* ]);
* ```
*
* With:
* ```
* ```
*
* ### Example
* Empty selections automatically become 1-character selections.
*
* ```js
* expect(Selections.fromCharacterMode([Selections.empty(0, 0)]), "to satisfy", [
* expect.it("to have anchor at coords", 0, 0).and("to have cursor at coords", 0, 1),
* ]);
*
* // At the end of the line, it selects the line ending:
* expect(Selections.fromCharacterMode([Selections.empty(0, 1)]), "to satisfy", [
* expect.it("to have anchor at coords", 0, 1).and("to have cursor at coords", 1, 0),
* ]);
*
* // But it does nothing at the end of the document:
* expect(Selections.fromCharacterMode([Selections.empty(2, 0)]), "to satisfy", [
* expect.it("to be empty at coords", 2, 0),
* ]);
* ```
*
* With:
* ```
* a
* b
*
* ```
*/
export function fromCharacterMode(
selections: readonly vscode.Selection[],
document?: vscode.TextDocument,
) {
const caretModeSelections = [] as vscode.Selection[];
for (const selection of selections) {
const selectionActive = selection.active,
selectionActiveLine = selectionActive.line,
selectionActiveCharacter = selectionActive.character,
selectionAnchor = selection.anchor,
selectionAnchorLine = selectionAnchor.line,
selectionAnchorCharacter = selectionAnchor.character;
let active = selectionActive,
changed = false;
const isEmptyOrForwardFacing = selectionAnchorLine < selectionActiveLine
|| (selectionAnchorLine === selectionActiveLine
&& selectionAnchorCharacter <= selectionActiveCharacter);
if (isEmptyOrForwardFacing) {
// Selection is empty or forward-facing: extend it if possible.
if (document === undefined) {
document = Context.current.document;
}
const lineLength = document.lineAt(selectionActiveLine).text.length;
if (selectionActiveCharacter === lineLength) {
// Character is at the end of the line.
if (selectionActiveLine + 1 < document.lineCount) {
// This is not the last line: we can extend the selection.
active = new vscode.Position(selectionActiveLine + 1, 0);
changed = true;
} else {
// This is the last line: we cannot do anything.
}
} else {
// Character is not at the end of the line: we can extend the selection.
active = new vscode.Position(selectionActiveLine, selectionActiveCharacter + 1);
changed = true;
}
}
caretModeSelections.push(changed ? new vscode.Selection(selectionAnchor, active) : selection);
}
return caretModeSelections;
}
export const fromCharacterMode = selectionsFromCharacterMode,
toCharacterMode = selectionsToCharacterMode;
}
function sortTopToBottom(a: vscode.Selection, b: vscode.Selection) {

54
src/api/types.ts Normal file
View File

@ -0,0 +1,54 @@
/**
* Direction of an operation.
*/
export const enum Direction {
/**
* Forward direction (`1`).
*/
Forward = 1,
/**
* Backward direction (`-1`).
*/
Backward = -1,
}
/**
* Behavior of a shift.
*/
export const enum Shift {
/**
* Jump to the position.
*/
Jump,
/**
* Select to the position.
*/
Select,
/**
* Extend to the position.
*/
Extend,
}
/**
* Selection behavior of an operation.
*/
export const enum SelectionBehavior {
/**
* VS Code-like caret selections.
*/
Caret = 1,
/**
* Kakoune-like character selections.
*/
Character = 2,
}
export const Forward = Direction.Forward,
Backward = Direction.Backward,
Jump = Shift.Jump,
Select = Shift.Select,
Extend = Shift.Extend;

194
test/suite/api.test.ts generated
View File

@ -89,6 +89,103 @@ suite("API tests", function () {
// No expected end document.
});
test("function selectionsToCharacterMode", async function () {
const editorState = extension.editors.getState(editor)!,
context = new Context(editorState, cancellationToken),
before = ExpectedDocument.parseIndented(14, String.raw`
a
b
`);
await before.apply(editor);
await context.runAsync(async () => {
// One-character selection becomes empty.
expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 0, 0, 1)]), "to satisfy", [
expect.it("to be empty at coords", 0, 0),
]);
// One-character selection becomes empty (at line break).
expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 1, 1, 0)]), "to satisfy", [
expect.it("to be empty at coords", 0, 1),
]);
// Forward-facing selection becomes shorter.
expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 0, 1, 1)]), "to satisfy", [
expect.it("to have anchor at coords", 0, 0).and("to have cursor at coords", 1, 0),
]);
// One-character selection becomes empty (reversed).
expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 1, 0, 0)]), "to satisfy", [
expect.it("to be empty at coords", 0, 0),
]);
// One-character selection becomes empty (reversed, at line break).
expect(Selections.toCharacterMode([Selections.fromAnchorActive(1, 0, 0, 1)]), "to satisfy", [
expect.it("to be empty at coords", 0, 1),
]);
// Reversed selection stays as-is.
expect(Selections.toCharacterMode([Selections.fromAnchorActive(1, 1, 0, 0)]), "to satisfy", [
expect.it("to have anchor at coords", 1, 1).and("to have cursor at coords", 0, 0),
]);
// Empty selection stays as-is.
expect(Selections.toCharacterMode([Selections.empty(1, 1)]), "to satisfy", [
expect.it("to be empty at coords", 1, 1),
]);
});
// No expected end document.
});
test("function selectionsFromCharacterMode", async function () {
const editorState = extension.editors.getState(editor)!,
context = new Context(editorState, cancellationToken),
before = ExpectedDocument.parseIndented(14, String.raw`
`);
await before.apply(editor);
await context.runAsync(async () => {
expect(Selections.fromCharacterMode([Selections.empty(0, 0)]), "to satisfy", [
expect.it("to be empty at coords", 0, 0),
]);
});
// No expected end document.
});
test("function selectionsFromCharacterMode#1", async function () {
const editorState = extension.editors.getState(editor)!,
context = new Context(editorState, cancellationToken),
before = ExpectedDocument.parseIndented(14, String.raw`
a
b
`);
await before.apply(editor);
await context.runAsync(async () => {
expect(Selections.fromCharacterMode([Selections.empty(0, 0)]), "to satisfy", [
expect.it("to have anchor at coords", 0, 0).and("to have cursor at coords", 0, 1),
]);
// At the end of the line, it selects the line ending:
expect(Selections.fromCharacterMode([Selections.empty(0, 1)]), "to satisfy", [
expect.it("to have anchor at coords", 0, 1).and("to have cursor at coords", 1, 0),
]);
// But it does nothing at the end of the document:
expect(Selections.fromCharacterMode([Selections.empty(2, 0)]), "to satisfy", [
expect.it("to be empty at coords", 2, 0),
]);
});
// No expected end document.
});
});
suite("./src/api/functional.ts", function () {
@ -1308,102 +1405,5 @@ suite("API tests", function () {
// No expected end document.
});
test("function toCharacterMode", async function () {
const editorState = extension.editors.getState(editor)!,
context = new Context(editorState, cancellationToken),
before = ExpectedDocument.parseIndented(14, String.raw`
a
b
`);
await before.apply(editor);
await context.runAsync(async () => {
// One-character selection becomes empty.
expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 0, 0, 1)]), "to satisfy", [
expect.it("to be empty at coords", 0, 0),
]);
// One-character selection becomes empty (at line break).
expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 1, 1, 0)]), "to satisfy", [
expect.it("to be empty at coords", 0, 1),
]);
// Forward-facing selection becomes shorter.
expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 0, 1, 1)]), "to satisfy", [
expect.it("to have anchor at coords", 0, 0).and("to have cursor at coords", 1, 0),
]);
// One-character selection becomes empty (reversed).
expect(Selections.toCharacterMode([Selections.fromAnchorActive(0, 1, 0, 0)]), "to satisfy", [
expect.it("to be empty at coords", 0, 0),
]);
// One-character selection becomes empty (reversed, at line break).
expect(Selections.toCharacterMode([Selections.fromAnchorActive(1, 0, 0, 1)]), "to satisfy", [
expect.it("to be empty at coords", 0, 1),
]);
// Reversed selection stays as-is.
expect(Selections.toCharacterMode([Selections.fromAnchorActive(1, 1, 0, 0)]), "to satisfy", [
expect.it("to have anchor at coords", 1, 1).and("to have cursor at coords", 0, 0),
]);
// Empty selection stays as-is.
expect(Selections.toCharacterMode([Selections.empty(1, 1)]), "to satisfy", [
expect.it("to be empty at coords", 1, 1),
]);
});
// No expected end document.
});
test("function fromCharacterMode", async function () {
const editorState = extension.editors.getState(editor)!,
context = new Context(editorState, cancellationToken),
before = ExpectedDocument.parseIndented(14, String.raw`
`);
await before.apply(editor);
await context.runAsync(async () => {
expect(Selections.fromCharacterMode([Selections.empty(0, 0)]), "to satisfy", [
expect.it("to be empty at coords", 0, 0),
]);
});
// No expected end document.
});
test("function fromCharacterMode#1", async function () {
const editorState = extension.editors.getState(editor)!,
context = new Context(editorState, cancellationToken),
before = ExpectedDocument.parseIndented(14, String.raw`
a
b
`);
await before.apply(editor);
await context.runAsync(async () => {
expect(Selections.fromCharacterMode([Selections.empty(0, 0)]), "to satisfy", [
expect.it("to have anchor at coords", 0, 0).and("to have cursor at coords", 0, 1),
]);
// At the end of the line, it selects the line ending:
expect(Selections.fromCharacterMode([Selections.empty(0, 1)]), "to satisfy", [
expect.it("to have anchor at coords", 0, 1).and("to have cursor at coords", 1, 0),
]);
// But it does nothing at the end of the document:
expect(Selections.fromCharacterMode([Selections.empty(2, 0)]), "to satisfy", [
expect.it("to be empty at coords", 2, 0),
]);
});
// No expected end document.
});
});
});