Spaces:
Paused
Paused
| /* | |
| * Copyright (c) 2019 Convergence Labs, Inc. | |
| * | |
| * This file is part of the CodeMirror Collaborative Extensions, which is | |
| * released under the terms of the MIT license. A copy of the MIT license | |
| * is usually provided as part of this source code package in the LICENCE | |
| * file. If it was not, please see <https://opensource.org/licenses/MIT> | |
| */ | |
| import { TinyColor } from "@ctrl/tinycolor"; | |
| import {Editor, Position, TextMarker} from "codemirror"; | |
| import {OnDisposed} from "./OnDisposed"; | |
| import {Validation} from "./Validation"; | |
| export class RemoteSelection { | |
| /** | |
| * A helper method to add a style tag to the head of the document that will | |
| * style the color of the selection. CodeMirror only allows setting the | |
| * class name of decorations, so we can not set a style property directly. | |
| * This method will create, add, and return the style tag for this element. | |
| * | |
| * @param className | |
| * The className to use as the css selector. | |
| * @param color | |
| * The color to set for the selection. | |
| * @returns | |
| * The style element that was added to the document head. | |
| * | |
| * @private | |
| * @internal | |
| */ | |
| private static _addDynamicStyleElement(className: string, color: string): HTMLStyleElement { | |
| Validation.assertString(className, "className"); | |
| Validation.assertString(color, "color"); | |
| const rgbaColor = new TinyColor(color).setAlpha(0.3).toRgbString(); | |
| const css = | |
| `.${className} { | |
| background-color: ${rgbaColor}; | |
| }`.trim(); | |
| const styleElement = document.createElement("style"); | |
| styleElement.innerText = css; | |
| document.head.appendChild(styleElement); | |
| return styleElement; | |
| } | |
| /** | |
| * A helper method to ensure the start position is before the end position. | |
| * | |
| * @param start | |
| * The current start position. | |
| * @param end | |
| * The current end position. | |
| * @return | |
| * An object containing the correctly ordered start and end positions. | |
| * | |
| * @private | |
| * @internal | |
| */ | |
| private static _swapIfNeeded(start: Position, end: Position): { start: Position, end: Position } { | |
| if (start.line < end.line || (start.line === end.line && start.ch <= end.ch)) { | |
| return {start, end}; | |
| } else { | |
| return {start: end, end: start}; | |
| } | |
| } | |
| /** | |
| * The userland id of the selection. | |
| * @internal | |
| */ | |
| private readonly _id: string; | |
| /** | |
| * The css classname to apply to the CodeMirror marker. | |
| * @internal | |
| */ | |
| private readonly _className: string; | |
| /** | |
| * The HTML Style element added to the document to color the selection. | |
| * @internal | |
| */ | |
| private readonly _styleElement: HTMLStyleElement; | |
| /** | |
| * The CodeMirror editor instance to render selection into. | |
| * @internal | |
| */ | |
| private readonly _editor: Editor; | |
| /** | |
| * An internal callback used to dispose of the selection. | |
| * @internal | |
| */ | |
| private readonly _onDisposed: OnDisposed; | |
| /** | |
| * The current start position of the selection. | |
| * @internal | |
| */ | |
| private _startPosition: Position; | |
| /** | |
| * The current end position of the selection. | |
| * @internal | |
| */ | |
| private _endPosition: Position; | |
| /** | |
| * The id's of the current CodeMirror marker rendering the selection. | |
| * @internal | |
| */ | |
| private _marker: TextMarker; | |
| /** | |
| * A flag determining if the selection has been disposed. | |
| * @internal | |
| */ | |
| private _disposed: boolean; | |
| /** | |
| * Constructs a new remote selection. | |
| * | |
| * @internal | |
| */ | |
| constructor( | |
| codeEditor: Editor, | |
| id: string, | |
| classId: number, | |
| color: string, | |
| onDisposed: OnDisposed | |
| ) { | |
| this._editor = codeEditor; | |
| this._id = id; | |
| this._className = `codemirror-remote-selection-${classId}`; | |
| this._styleElement = RemoteSelection._addDynamicStyleElement(this._className, color); | |
| this._onDisposed = onDisposed; | |
| } | |
| /** | |
| * Gets the userland id of this selection. | |
| */ | |
| public getId(): string { | |
| return this._id; | |
| } | |
| /** | |
| * Gets the start position of the selection. | |
| * | |
| * @returns | |
| * The start position of the selection. | |
| */ | |
| public getStartPosition(): Position { | |
| return {...this._startPosition}; | |
| } | |
| /** | |
| * Gets the start position of the selection. | |
| * | |
| * @returns | |
| * The start position of the selection. | |
| */ | |
| public getEndPosition(): Position { | |
| return {...this._endPosition}; | |
| } | |
| /** | |
| * Sets the selection using zero-based text indices. | |
| * | |
| * @param start | |
| * The start index to set the selection to. | |
| * @param end | |
| * The end index to set the selection to. | |
| */ | |
| public setIndices(start: number, end: number): void { | |
| const startPosition = this._editor.posFromIndex(start); | |
| const endPosition = this._editor.posFromIndex(end); | |
| this.setPositions(startPosition, endPosition); | |
| } | |
| /** | |
| * Sets the selection using CodeMirrors's line / ch coordinate system. | |
| * | |
| * @param start | |
| * The start position to set the selection to. | |
| * @param end | |
| * The end position to set the selection to. | |
| */ | |
| public setPositions(start: Position, end: Position): void { | |
| // this._decorations = this._editor.deltaDecorations(this._decorations, []); | |
| const ordered = RemoteSelection._swapIfNeeded(start, end); | |
| this._startPosition = ordered.start; | |
| this._endPosition = ordered.end; | |
| this._render(); | |
| } | |
| /** | |
| * Makes the selection visible if it is hidden. | |
| */ | |
| public show(): void { | |
| this._render(); | |
| } | |
| /** | |
| * Makes the selection hidden if it is visible. | |
| */ | |
| public hide(): void { | |
| if (this._marker) { | |
| this._marker.clear(); | |
| } | |
| } | |
| /** | |
| * Determines if the selection has been permanently removed from the editor. | |
| * | |
| * @returns | |
| * True if the selection has been disposed, false otherwise. | |
| */ | |
| public isDisposed(): boolean { | |
| return this._disposed; | |
| } | |
| /** | |
| * Permanently removes the selection from the editor. | |
| */ | |
| public dispose(): void { | |
| if (!this._disposed) { | |
| this._styleElement.parentElement.removeChild(this._styleElement); | |
| this.hide(); | |
| this._disposed = true; | |
| this._onDisposed(); | |
| } | |
| } | |
| /** | |
| * A helper method that actually renders the selection as a marker within | |
| * the CodeMirror Editor. | |
| * | |
| * @private | |
| * @internal | |
| */ | |
| private _render(): void { | |
| if (this._marker) { | |
| this._marker.clear(); | |
| } | |
| this._marker = this._editor.markText(this._startPosition, this._endPosition, { | |
| className: this._className | |
| }); | |
| } | |
| } | |