Ramesh-vani's picture
Upload 32 files
341f1e9 verified
/*
* 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
});
}
}