summaryrefslogtreecommitdiffstats
path: root/devtools/client/debugger/src/utils/editor/index.js
diff options
context:
space:
mode:
Diffstat (limited to 'devtools/client/debugger/src/utils/editor/index.js')
-rw-r--r--devtools/client/debugger/src/utils/editor/index.js242
1 files changed, 242 insertions, 0 deletions
diff --git a/devtools/client/debugger/src/utils/editor/index.js b/devtools/client/debugger/src/utils/editor/index.js
new file mode 100644
index 0000000000..7f40a36c7f
--- /dev/null
+++ b/devtools/client/debugger/src/utils/editor/index.js
@@ -0,0 +1,242 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <http://mozilla.org/MPL/2.0/>. */
+
+export * from "./source-documents";
+export * from "./get-token-location";
+export * from "./source-search";
+export * from "../ui";
+export { onMouseOver } from "./token-events";
+
+import { createEditor } from "./create-editor";
+import { findNext, findPrev } from "./source-search";
+
+import { isWasm, lineToWasmOffset, wasmOffsetToLine } from "../wasm";
+
+let editor;
+
+export function getEditor() {
+ if (editor) {
+ return editor;
+ }
+
+ editor = createEditor();
+ return editor;
+}
+
+export function removeEditor() {
+ editor = null;
+}
+
+function getCodeMirror() {
+ return editor && editor.hasCodeMirror ? editor.codeMirror : null;
+}
+
+export function startOperation() {
+ const codeMirror = getCodeMirror();
+ if (!codeMirror) {
+ return;
+ }
+
+ codeMirror.startOperation();
+}
+
+export function endOperation() {
+ const codeMirror = getCodeMirror();
+ if (!codeMirror) {
+ return;
+ }
+
+ codeMirror.endOperation();
+}
+
+export function traverseResults(e, ctx, query, dir, modifiers) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ if (dir == "prev") {
+ findPrev(ctx, query, true, modifiers);
+ } else if (dir == "next") {
+ findNext(ctx, query, true, modifiers);
+ }
+}
+
+export function toEditorLine(sourceId, lineOrOffset) {
+ if (isWasm(sourceId)) {
+ // TODO ensure offset is always "mappable" to edit line.
+ return wasmOffsetToLine(sourceId, lineOrOffset) || 0;
+ }
+
+ return lineOrOffset ? lineOrOffset - 1 : 1;
+}
+
+export function fromEditorLine(sourceId, line) {
+ if (isWasm(sourceId)) {
+ return lineToWasmOffset(sourceId, line) || 0;
+ }
+
+ return line + 1;
+}
+
+export function toEditorPosition(location) {
+ return {
+ line: toEditorLine(location.sourceId, location.line),
+ column: isWasm(location.sourceId) || !location.column ? 0 : location.column,
+ };
+}
+
+export function toEditorRange(sourceId, location) {
+ const { start, end } = location;
+ return {
+ start: toEditorPosition({ ...start, sourceId }),
+ end: toEditorPosition({ ...end, sourceId }),
+ };
+}
+
+export function toSourceLine(sourceId, line) {
+ return isWasm(sourceId) ? lineToWasmOffset(sourceId, line) : line + 1;
+}
+
+export function scrollToColumn(codeMirror, line, column) {
+ const { top, left } = codeMirror.charCoords({ line, ch: column }, "local");
+
+ if (!isVisible(codeMirror, top, left)) {
+ const scroller = codeMirror.getScrollerElement();
+ const centeredX = Math.max(left - scroller.offsetWidth / 2, 0);
+ const centeredY = Math.max(top - scroller.offsetHeight / 2, 0);
+
+ codeMirror.scrollTo(centeredX, centeredY);
+ }
+}
+
+function isVisible(codeMirror, top, left) {
+ function withinBounds(x, min, max) {
+ return x >= min && x <= max;
+ }
+
+ const scrollArea = codeMirror.getScrollInfo();
+ const charWidth = codeMirror.defaultCharWidth();
+ const fontHeight = codeMirror.defaultTextHeight();
+ const { scrollTop, scrollLeft } = codeMirror.doc;
+
+ const inXView = withinBounds(
+ left,
+ scrollLeft,
+ scrollLeft + (scrollArea.clientWidth - 30) - charWidth
+ );
+
+ const inYView = withinBounds(
+ top,
+ scrollTop,
+ scrollTop + scrollArea.clientHeight - fontHeight
+ );
+
+ return inXView && inYView;
+}
+
+export function getLocationsInViewport(
+ { codeMirror },
+ // Offset represents an allowance of characters or lines offscreen to improve
+ // perceived performance of column breakpoint rendering
+ offsetHorizontalCharacters = 100,
+ offsetVerticalLines = 20
+) {
+ // Get scroll position
+ if (!codeMirror) {
+ return {
+ start: { line: 0, column: 0 },
+ end: { line: 0, column: 0 },
+ };
+ }
+ const charWidth = codeMirror.defaultCharWidth();
+ const scrollArea = codeMirror.getScrollInfo();
+ const { scrollLeft } = codeMirror.doc;
+ const rect = codeMirror.getWrapperElement().getBoundingClientRect();
+ const topVisibleLine =
+ codeMirror.lineAtHeight(rect.top, "window") - offsetVerticalLines;
+ const bottomVisibleLine =
+ codeMirror.lineAtHeight(rect.bottom, "window") + offsetVerticalLines;
+
+ const leftColumn = Math.floor(
+ scrollLeft > 0 ? scrollLeft / charWidth - offsetHorizontalCharacters : 0
+ );
+ const rightPosition = scrollLeft + (scrollArea.clientWidth - 30);
+ const rightCharacter =
+ Math.floor(rightPosition / charWidth) + offsetHorizontalCharacters;
+
+ return {
+ start: {
+ line: topVisibleLine || 0,
+ column: leftColumn || 0,
+ },
+ end: {
+ line: bottomVisibleLine || 0,
+ column: rightCharacter,
+ },
+ };
+}
+
+export function markText({ codeMirror }, className, { start, end }) {
+ return codeMirror.markText(
+ { ch: start.column, line: start.line },
+ { ch: end.column, line: end.line },
+ { className }
+ );
+}
+
+export function lineAtHeight({ codeMirror }, sourceId, event) {
+ const _editorLine = codeMirror.lineAtHeight(event.clientY);
+ return toSourceLine(sourceId, _editorLine);
+}
+
+export function getSourceLocationFromMouseEvent({ codeMirror }, source, e) {
+ const { line, ch } = codeMirror.coordsChar({
+ left: e.clientX,
+ top: e.clientY,
+ });
+ const sourceId = source.id;
+
+ return {
+ sourceId,
+ line: fromEditorLine(sourceId, line),
+ column: isWasm(sourceId) ? 0 : ch + 1,
+ };
+}
+
+export function forEachLine(codeMirror, iter) {
+ codeMirror.operation(() => {
+ codeMirror.doc.iter(0, codeMirror.lineCount(), iter);
+ });
+}
+
+export function removeLineClass(codeMirror, line, className) {
+ codeMirror.removeLineClass(line, "wrapClass", className);
+}
+
+export function clearLineClass(codeMirror, className) {
+ forEachLine(codeMirror, line => {
+ removeLineClass(codeMirror, line, className);
+ });
+}
+
+export function getTextForLine(codeMirror, line) {
+ return codeMirror.getLine(line - 1).trim();
+}
+
+export function getCursorLine(codeMirror) {
+ return codeMirror.getCursor().line;
+}
+
+export function getCursorColumn(codeMirror) {
+ return codeMirror.getCursor().ch;
+}
+
+export function getTokenEnd(codeMirror, line, column) {
+ const token = codeMirror.getTokenAt({
+ line,
+ ch: column + 1,
+ });
+ const tokenString = token.string;
+
+ return tokenString === "{" || tokenString === "[" ? null : token.end;
+}