diff --git a/packages/cursorless-vscode/package.json b/packages/cursorless-vscode/package.json index b1bfb27578..25c489c049 100644 --- a/packages/cursorless-vscode/package.json +++ b/packages/cursorless-vscode/package.json @@ -245,20 +245,20 @@ }, { "id": "cursorless.scopeContentBackground", - "description": "Background color to use for calibrating timing when recording a video", + "description": "Background color to use for visualizing scopes", "defaults": { - "dark": "#230026", - "light": "#230026", - "highContrast": "#230026" + "dark": "#ea00ff57", + "light": "#ea00ff57", + "highContrast": "#ea00ff57" } }, { "id": "cursorless.scopeContentBorder", - "description": "Border color to use for calibrating timing when recording a video", + "description": "Border color to use for visualizing scopes", "defaults": { - "dark": "#230026", - "light": "#230026", - "highContrast": "#230026" + "dark": "#ebdeec84", + "light": "#ebdeec84", + "highContrast": "#ebdeec84" } } ], diff --git a/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/borderStyles.ts b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/borderStyles.ts new file mode 100644 index 0000000000..cb0ff67ace --- /dev/null +++ b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/borderStyles.ts @@ -0,0 +1,56 @@ +import { Borders } from "./getDecorationRanges.types"; + +export const NONE: Borders = { + top: false, + right: false, + bottom: false, + left: false, +}; +export const TOP: Borders = { + top: true, + left: false, + right: false, + bottom: false, +}; +export const BOTTOM: Borders = { + bottom: true, + top: false, + left: false, + right: false, +}; +export const TOP_LEFT: Borders = { + top: true, + left: true, + right: false, + bottom: false, +}; +export const TOP_BOTTOM: Borders = { + top: true, + bottom: true, + left: false, + right: false, +}; +export const BOTTOM_RIGHT: Borders = { + bottom: true, + right: true, + top: false, + left: false, +}; +export const TOP_BOTTOM_LEFT: Borders = { + top: true, + bottom: true, + left: true, + right: false, +}; +export const TOP_BOTTOM_RIGHT: Borders = { + top: true, + bottom: true, + right: true, + left: false, +}; +export const FULL: Borders = { + top: true, + right: true, + bottom: true, + left: true, +}; diff --git a/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/generateDecorationsForRange.ts b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/generateDecorationsForRange.ts index b5fe9f26f2..4c2e28e476 100644 --- a/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/generateDecorationsForRange.ts +++ b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/generateDecorationsForRange.ts @@ -1,6 +1,10 @@ import { Range, TextEditor } from "@cursorless/common"; import { range } from "lodash"; -import { Borders, DecoratedRange } from "./getDecorationRanges.types"; +import { DecoratedRange } from "./getDecorationRanges.types"; +import { FULL } from "./borderStyles"; +import { handleTwoLines } from "./handleTwoLines"; +import { handleThreeLines } from "./handleThreeLines"; +import { handleManyLines } from "./handleManyLines"; export function* generateDecorationsForRange( editor: TextEditor, @@ -15,6 +19,7 @@ export function* generateDecorationsForRange( } const { document } = editor; + // TODO: We don't actually need anything other than first and last two lines const lineRanges = range(tokenRange.start.line, tokenRange.end.line + 1).map( (lineNumber) => document.lineAt(lineNumber).range, ); @@ -31,114 +36,10 @@ export function* generateDecorationsForRange( yield* handleTwoLines(lineRanges); break; case 3: - break; - case 4: + yield* handleThreeLines(lineRanges); break; default: + yield* handleManyLines(lineRanges); break; } } - -function* handleTwoLines(lineRanges: Range[]): Iterable { - const [firstLine, secondLine] = lineRanges; - const { - start: { character: firstLineStart, line: firstLineNumber }, - end: { character: firstLineEnd }, - } = firstLine; - const { - start: { character: secondLineStart, line: secondLineNumber }, - end: { character: secondLineEnd }, - } = secondLine; - - if (firstLineStart >= secondLineEnd) { - yield { - range: firstLine, - style: TOP_BOTTOM_LEFT, - }; - - yield { - range: secondLine, - style: TOP_BOTTOM_RIGHT, - }; - - return; - } - - yield { - range: singleLineRange( - firstLineNumber, - firstLineStart, - Math.min(firstLineEnd, secondLineEnd), - ), - style: TOP_LEFT, - }; - - if (firstLineEnd > secondLineEnd) { - yield { - range: singleLineRange(firstLineNumber, secondLineEnd, firstLineEnd), - style: TOP_BOTTOM, - }; - } - - if (secondLineStart < firstLineStart) { - yield { - range: singleLineRange(secondLineNumber, secondLineStart, firstLineStart), - style: TOP_BOTTOM, - }; - } - - yield { - range: singleLineRange( - secondLineNumber, - Math.max(firstLineStart, secondLineStart), - secondLineEnd, - ), - style: BOTTOM_RIGHT, - }; -} - -const TOP_LEFT: Borders = { - top: true, - left: true, - right: false, - bottom: false, -}; - -const TOP_BOTTOM: Borders = { - top: true, - bottom: true, - left: false, - right: false, -}; - -const BOTTOM_RIGHT: Borders = { - bottom: true, - right: true, - top: false, - left: false, -}; - -const TOP_BOTTOM_LEFT: Borders = { - top: true, - bottom: true, - left: true, - right: false, -}; - -const TOP_BOTTOM_RIGHT: Borders = { - top: true, - bottom: true, - right: true, - left: false, -}; - -const FULL: Borders = { - top: true, - right: true, - bottom: true, - left: true, -}; - -function singleLineRange(line: number, start: number, end: number): Range { - return new Range(line, start, line, end); -} diff --git a/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleManyLines.ts b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleManyLines.ts new file mode 100644 index 0000000000..077e0d9bf6 --- /dev/null +++ b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleManyLines.ts @@ -0,0 +1,63 @@ +import { Range } from "@cursorless/common"; +import { DecoratedRange } from "./getDecorationRanges.types"; +import { BOTTOM, BOTTOM_RIGHT, NONE, TOP, TOP_LEFT } from "./borderStyles"; +import { singleLineRange } from "./singleLineRange"; + +export function* handleManyLines( + lineRanges: Range[], +): Iterable { + const [firstLine, secondLine, ...lastLines] = lineRanges; + const secondLastLine = lastLines[lastLines.length - 2]; + const lastLine = lastLines[lastLines.length - 1]; + const { + start: { character: firstLineStart }, + } = firstLine; + const { + start: { line: secondLineNumber }, + } = secondLine; + const { + start: { line: secondLastLineNumber }, + end: { character: secondLastLineEnd }, + } = secondLastLine; + const { + end: { character: lastLineEnd }, + } = lastLine; + + yield { + range: firstLine, + style: TOP_LEFT, + }; + + if (firstLineStart > 0) { + yield { + range: singleLineRange(secondLineNumber, 0, firstLineStart), + style: TOP, + }; + } + + yield { + range: new Range( + secondLineNumber, + firstLineStart, + secondLastLineNumber, + Math.min(secondLastLineEnd, lastLineEnd), + ), + style: NONE, + }; + + if (secondLastLineEnd > lastLineEnd) { + yield { + range: singleLineRange( + secondLastLineNumber, + lastLineEnd, + secondLastLineEnd, + ), + style: BOTTOM, + }; + } + + yield { + range: lastLine, + style: BOTTOM_RIGHT, + }; +} diff --git a/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleThreeLines.ts b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleThreeLines.ts new file mode 100644 index 0000000000..97cbe8b82b --- /dev/null +++ b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleThreeLines.ts @@ -0,0 +1,97 @@ +import { Range } from "@cursorless/common"; +import { Borders, DecoratedRange } from "./getDecorationRanges.types"; +import { TOP_LEFT, BOTTOM_RIGHT, TOP } from "./borderStyles"; +import { singleLineRange } from "./singleLineRange"; + +export function* handleThreeLines( + lineRanges: Range[], +): Iterable { + const [firstLine, secondLine, thirdLine] = lineRanges; + const { + start: { character: firstLineStart }, + } = firstLine; + const { + start: { line: secondLineNumber }, + end: { character: secondLineEnd }, + } = secondLine; + const { + end: { character: thirdLineEnd }, + } = thirdLine; + + yield { + range: firstLine, + style: TOP_LEFT, + }; + + yield* handleSecondLine( + secondLineNumber, + firstLineStart, + secondLineEnd, + thirdLineEnd, + ); + + yield { + range: thirdLine, + style: BOTTOM_RIGHT, + }; +} + +function* handleSecondLine( + secondLineNumber: number, + firstLineStart: number, + secondLineEnd: number, + thirdLineEnd: number, +): Iterable { + let currentDecoration: Borders = TOP; + let currentOffset = 0; + + const events: Event[] = [ + { + offset: firstLineStart, + type: EventType.firstLineStart, + }, + { + offset: secondLineEnd, + type: EventType.secondLineEnd, + }, + { + offset: thirdLineEnd, + type: EventType.thirdLineEnd, + }, + ]; + + events.sort((a, b) => a.offset - b.offset); + + for (const { offset, type } of events) { + if (offset > currentOffset) { + yield { + range: singleLineRange(secondLineNumber, currentOffset, offset), + style: currentDecoration, + }; + } + + switch (type) { + case EventType.firstLineStart: + currentDecoration = { ...currentDecoration, top: false }; + break; + case EventType.secondLineEnd: + return; + case EventType.thirdLineEnd: + currentDecoration = { ...currentDecoration, bottom: true }; + break; + } + + currentOffset = offset; + } +} + +interface Event { + offset: number; + type: EventType; +} + +enum EventType { + firstLineStart, + secondLineEnd, + thirdLineEnd, +} diff --git a/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleTwoLines.ts b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleTwoLines.ts new file mode 100644 index 0000000000..fdd0436380 --- /dev/null +++ b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/handleTwoLines.ts @@ -0,0 +1,64 @@ +import { Range } from "@cursorless/common"; +import { DecoratedRange } from "./getDecorationRanges.types"; +import { + TOP_BOTTOM_LEFT, + TOP_BOTTOM_RIGHT, + TOP_LEFT, + TOP_BOTTOM, + BOTTOM_RIGHT, +} from "./borderStyles"; +import { singleLineRange } from "./singleLineRange"; + +export function* handleTwoLines(lineRanges: Range[]): Iterable { + const [firstLine, secondLine] = lineRanges; + const { + start: { character: firstLineStart, line: firstLineNumber }, + end: { character: firstLineEnd }, + } = firstLine; + const { + start: { line: secondLineNumber }, + end: { character: secondLineEnd }, + } = secondLine; + + if (firstLineStart >= secondLineEnd) { + yield { + range: firstLine, + style: TOP_BOTTOM_LEFT, + }; + + yield { + range: secondLine, + style: TOP_BOTTOM_RIGHT, + }; + + return; + } + + yield { + range: singleLineRange( + firstLineNumber, + firstLineStart, + Math.min(firstLineEnd, secondLineEnd), + ), + style: TOP_LEFT, + }; + + if (firstLineEnd > secondLineEnd) { + yield { + range: singleLineRange(firstLineNumber, secondLineEnd, firstLineEnd), + style: TOP_BOTTOM, + }; + } + + if (firstLineStart > 0) { + yield { + range: singleLineRange(secondLineNumber, 0, firstLineStart), + style: TOP_BOTTOM, + }; + } + + yield { + range: singleLineRange(secondLineNumber, firstLineStart, secondLineEnd), + style: BOTTOM_RIGHT, + }; +} diff --git a/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/singleLineRange.ts b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/singleLineRange.ts new file mode 100644 index 0000000000..9658b0490b --- /dev/null +++ b/packages/cursorless-vscode/src/ide/vscode/getDecorationRanges/singleLineRange.ts @@ -0,0 +1,9 @@ +import { Range } from "@cursorless/common"; + +export function singleLineRange( + line: number, + start: number, + end: number, +): Range { + return new Range(line, start, line, end); +}