Spaces:
Paused
Paused
File size: 5,558 Bytes
341f1e9 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 | /*
* 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 {Position} from "codemirror";
import {IRemoteCursorManagerOptions} from "./IRemoteCursorManagerOptions";
import {RemoteCursor} from "./RemoteCursor";
import {RemoteCursorWidget} from "./RemoteCursorWidget";
import {Validation} from "./Validation";
/**
* The RemoteCursorManager class is responsible for creating and managing a
* set of indicators that show where remote users's cursors are located when
* using CodeMirror in a collaborative editing context. The RemoteCursorManager
* leverages CodeMirror's Widget concept to add cursor nodes to the editor.
*/
export class RemoteCursorManager {
/**
* The default values for optional parameters.
* @internal
*/
private static readonly DEFAULT_OPTIONS = {tooltips: true, tooltipDuration: 1};
/**
* A counter that generates unique ids for the cursor widgets.
* @internal
*/
private _nextWidgetId: number;
/**
* Tracks the current cursor widgets by the userland id.
* @internal
*/
private readonly _cursorWidgets: Map<string, any>;
/**
* The options (and defaults) used to configure this instance.
* @internal
*/
private readonly _options: IRemoteCursorManagerOptions;
/**
* Creates a new RemoteCursorManager with the supplied options.
*
* @param options
* The options that will configure the RemoteCursorManager behavior.
*/
constructor(options: IRemoteCursorManagerOptions) {
if (typeof options !== "object") {
throw new Error("'options' is a required parameter and must be an object.");
}
// Override the defaults.
options = {...RemoteCursorManager.DEFAULT_OPTIONS, ...options};
if (options.editor === undefined || options.editor === null) {
throw new Error(`options.editor must be defined but was: ${options.editor}`);
}
this._options = options;
this._cursorWidgets = new Map<string, RemoteCursorWidget>();
this._nextWidgetId = 0;
}
/**
* Adds a new remote cursor to the editor.
*
* @param id
* A unique id that will be used to reference this cursor.
* @param color
* The css color that the cursor and tooltip should be rendered in.
* @param label
* An optional label for the tooltip. If tooltips are enabled.
*
* @returns
* The remote cursor widget that will be added to the editor.
*/
public addCursor(id: string, color: string, label?: string): RemoteCursor {
Validation.assertString(id, "id");
Validation.assertString(color, "color");
if (this._options.tooltips && typeof "label" !== "string") {
throw new Error("'label' is required when tooltips are enabled.");
}
const widgetId = "" + this._nextWidgetId++;
const tooltipDurationMs = this._options.tooltipDuration * 1000;
const cursorWidget = new RemoteCursorWidget(
this._options.editor,
widgetId,
color,
label,
this._options.tooltips,
tooltipDurationMs,
() => this.removeCursor(id));
this._cursorWidgets.set(id, cursorWidget);
return new RemoteCursor(cursorWidget);
}
/**
* Removes the remote cursor from the editor.
*
* @param id
* The unique id of the cursor to remove.
*/
public removeCursor(id: string): void {
Validation.assertString(id, "id");
const remoteCursorWidget = this._getCursor(id);
if (!remoteCursorWidget.isDisposed()) {
remoteCursorWidget.dispose();
}
this._cursorWidgets.delete(id);
}
/**
* Updates the location of the specified remote cursor using a CodeMirror
* Position object.
*
* @param id
* The unique id of the cursor to remove.
* @param position
* The location of the cursor to set.
*/
public setCursorPosition(id: string, position: Position) {
Validation.assertString(id, "id");
const remoteCursorWidget = this._getCursor(id);
remoteCursorWidget.setPosition(position);
}
/**
* Updates the location of the specified remote cursor based on a zero-based
* text index.
*
* @param id
* The unique id of the cursor to remove.
* @param index
* The location of the cursor to set.
*/
public setCursorIndex(id: string, index: number) {
Validation.assertString(id, "id");
const remoteCursorWidget = this._getCursor(id);
remoteCursorWidget.setIndex(index);
}
/**
* Shows the specified cursor. Note the cursor may be scrolled out of view.
*
* @param id
* The unique id of the cursor to show.
*/
public showCursor(id: string): void {
Validation.assertString(id, "id");
const remoteCursorWidget = this._getCursor(id);
remoteCursorWidget.show();
}
/**
* Hides the specified cursor.
*
* @param id
* The unique id of the cursor to show.
*/
public hideCursor(id: string): void {
Validation.assertString(id, "id");
const remoteCursorWidget = this._getCursor(id);
remoteCursorWidget.hide();
}
/**
* A helper method that gets a cursor by id, or throws an exception.
* @internal
*/
private _getCursor(id: string): RemoteCursorWidget {
if (!this._cursorWidgets.has(id)) {
throw new Error("No such cursor: " + id);
}
return this._cursorWidgets.get(id);
}
}
|