diff options
Diffstat (limited to 'devtools/client/debugger/src/utils/editor/index.js')
-rw-r--r-- | devtools/client/debugger/src/utils/editor/index.js | 242 |
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; +} |