Michaël Zasso 6bd756d7c6
deps: update V8 to 10.7.193.13
PR-URL: https://github.com/nodejs/node/pull/44741
Fixes: https://github.com/nodejs/node/issues/44650
Fixes: https://github.com/nodejs/node/issues/37472
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Jiawen Geng <technicalcute@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
2022-10-11 07:24:33 +02:00

284 lines
11 KiB
TypeScript

// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import { Source } from "../source";
import { GenericPosition, SourceResolver } from "../source-resolver";
import { SelectionBroker } from "../selection/selection-broker";
import { CodeMode, View } from "./view";
import { SelectionMap } from "../selection/selection-map";
import { ViewElements } from "../common/view-elements";
import { ClearableHandler, SourcePositionSelectionHandler } from "../selection/selection-handler";
import { SourcePosition } from "../position";
interface PR {
prettyPrint(_: unknown, el: HTMLElement): void;
}
declare global {
const PR: PR;
}
export class CodeView extends View {
broker: SelectionBroker;
source: Source;
sourceResolver: SourceResolver;
codeMode: CodeMode;
sourcePositionToHtmlElements: Map<string, Array<HTMLElement>>;
showAdditionalInliningPosition: boolean;
sourcePositionSelection: SelectionMap;
sourcePositionSelectionHandler: SourcePositionSelectionHandler & ClearableHandler;
constructor(parent: HTMLElement, broker: SelectionBroker, sourceFunction: Source,
sourceResolver: SourceResolver, codeMode: CodeMode) {
super(parent);
this.broker = broker;
this.source = sourceFunction;
this.sourceResolver = sourceResolver;
this.codeMode = codeMode;
this.sourcePositionToHtmlElements = new Map<string, Array<HTMLElement>>();
this.showAdditionalInliningPosition = false;
this.sourcePositionSelection = new SelectionMap((gp: GenericPosition) => gp.toString());
this.sourcePositionSelectionHandler = this.initializeSourcePositionSelectionHandler();
broker.addSourcePositionHandler(this.sourcePositionSelectionHandler);
this.initializeCode();
}
public createViewElement(): HTMLDivElement {
const sourceContainer = document.createElement("div");
sourceContainer.classList.add("source-container");
return sourceContainer;
}
private initializeCode(): void {
const view = this;
const source = this.source;
const sourceText = source.sourceText;
if (!sourceText) return;
const sourceContainer = view.divNode;
if (this.codeMode == CodeMode.MainSource) {
sourceContainer.classList.add("main-source");
} else {
sourceContainer.classList.add("inlined-source");
}
const codeHeader = document.createElement("div");
codeHeader.setAttribute("id", this.getCodeHeaderHtmlElementName());
codeHeader.classList.add("code-header");
const codeFileFunction = document.createElement("div");
codeFileFunction.classList.add("code-file-function");
codeFileFunction.innerHTML = `${source.sourceName}:${source.functionName}`;
codeHeader.appendChild(codeFileFunction);
const codeModeDiv = document.createElement("div");
codeModeDiv.classList.add("code-mode");
codeModeDiv.innerHTML = this.codeMode;
codeHeader.appendChild(codeModeDiv);
const clearDiv = document.createElement("div");
clearDiv.style.clear = "both";
codeHeader.appendChild(clearDiv);
sourceContainer.appendChild(codeHeader);
const codePre = document.createElement("pre");
codePre.setAttribute("id", this.getCodeHtmlElementName());
codePre.classList.add("prettyprint");
sourceContainer.appendChild(codePre);
codeHeader.onclick = function myFunction() {
if (codePre.style.display === "none") {
codePre.style.display = "block";
} else {
codePre.style.display = "none";
}
};
if (sourceText !== "") {
codePre.classList.add("linenums");
codePre.textContent = sourceText;
try {
// Wrap in try to work when offline.
PR.prettyPrint(undefined, sourceContainer);
} catch (e) {
console.log(e);
}
view.divNode.onclick = function (e: MouseEvent) {
if (e.target instanceof Element && e.target.tagName === "DIV") {
const targetDiv = e.target as HTMLDivElement;
if (targetDiv.classList.contains("line-number")) {
e.stopPropagation();
view.onSelectLine(Number(targetDiv.dataset.lineNumber), !e.shiftKey);
}
} else {
view.sourcePositionSelectionHandler.clear();
}
};
const base: number = source.startPosition;
let current = 0;
const lineListDiv = this.getHtmlCodeLines();
let newlineAdjust = 0;
for (let i = 0; i < lineListDiv.length; i++) {
// Line numbers are not zero-based.
const lineNumber = i + 1;
const currentLineElement = lineListDiv[i];
currentLineElement.id = `li${i}`;
currentLineElement.dataset.lineNumber = String(lineNumber);
const spans = currentLineElement.childNodes;
for (const currentSpan of spans) {
if (currentSpan instanceof HTMLSpanElement) {
const pos = base + current;
const end = pos + currentSpan.textContent.length;
current += currentSpan.textContent.length;
this.insertSourcePositions(currentSpan, lineNumber, pos, end, newlineAdjust);
newlineAdjust = 0;
}
}
this.insertLineNumber(currentLineElement, lineNumber);
while ((current < sourceText.length) &&
(sourceText[current] === "\n" || sourceText[current] === "\r")) {
++current;
++newlineAdjust;
}
}
}
}
private initializeSourcePositionSelectionHandler(): SourcePositionSelectionHandler
& ClearableHandler {
const view = this;
const broker = this.broker;
const sourceResolver = this.sourceResolver;
return {
select: function (sourcePositions: Array<SourcePosition>, selected: boolean) {
const locations = new Array<SourcePosition>();
for (const sourcePosition of sourcePositions) {
locations.push(sourcePosition);
sourceResolver.addInliningPositions(sourcePosition, locations);
}
if (locations.length == 0) return;
view.sourcePositionSelection.select(locations, selected);
view.updateSelection();
broker.broadcastSourcePositionSelect(this, locations, selected);
},
clear: function () {
view.sourcePositionSelection.clear();
view.updateSelection();
broker.broadcastClear(this);
},
brokeredSourcePositionSelect: function (locations: Array<SourcePosition>, selected: boolean) {
const firstSelect = view.sourcePositionSelection.isEmpty();
for (const location of locations) {
const translated = sourceResolver.translateToSourceId(view.source.sourceId, location);
if (!translated) continue;
view.sourcePositionSelection.select([translated], selected);
}
view.updateSelection(firstSelect);
},
brokeredClear: function () {
view.sourcePositionSelection.clear();
view.updateSelection();
},
};
}
private addHtmlElementToSourcePosition(sourcePosition: GenericPosition, element: HTMLElement):
void {
const key = sourcePosition.toString();
if (!this.sourcePositionToHtmlElements.has(key)) {
this.sourcePositionToHtmlElements.set(key, new Array<HTMLElement>());
}
this.sourcePositionToHtmlElements.get(key).push(element);
}
private updateSelection(scrollIntoView: boolean = false): void {
const mkVisible = new ViewElements(this.divNode.parentNode as HTMLElement);
for (const [sp, els] of this.sourcePositionToHtmlElements.entries()) {
const isSelected = this.sourcePositionSelection.isKeySelected(sp);
for (const el of els) {
mkVisible.consider(el, isSelected);
el.classList.toggle("selected", isSelected);
}
}
mkVisible.apply(scrollIntoView);
}
private getCodeHtmlElementName(): string {
return `source-pre-${this.source.sourceId}`;
}
private getCodeHeaderHtmlElementName(): string {
return `source-pre-${this.source.sourceId}-header`;
}
private getHtmlCodeLines(): NodeListOf<HTMLElement> {
const orderList = this.divNode.querySelector(`#${this.getCodeHtmlElementName()} ol`);
return orderList.childNodes as NodeListOf<HTMLElement>;
}
private onSelectLine(lineNumber: number, doClear: boolean) {
if (doClear) {
this.sourcePositionSelectionHandler.clear();
}
const positions = this.sourceResolver.lineToSourcePositions(lineNumber - 1);
if (positions !== undefined) {
this.sourcePositionSelectionHandler.select(positions, undefined);
}
}
private onSelectSourcePosition(sourcePosition: SourcePosition, doClear: boolean) {
if (doClear) {
this.sourcePositionSelectionHandler.clear();
}
this.sourcePositionSelectionHandler.select([sourcePosition], undefined);
}
private insertSourcePositions(currentSpan: HTMLSpanElement, lineNumber: number,
pos: number, end: number, adjust: number): void {
const view = this;
const sps = this.sourceResolver.sourcePositionsInRange(this.source.sourceId, pos - adjust, end);
let offset = 0;
for (const sourcePosition of sps) {
// Internally, line numbers are 0-based so we have to substract 1 from the line number. This
// path in only taken by non-Wasm code. Wasm code relies on setSourceLineToBytecodePosition.
this.sourceResolver.addAnyPositionToLine(lineNumber - 1, sourcePosition);
const textNode = currentSpan.tagName === "SPAN" ? currentSpan.lastChild : currentSpan;
if (!(textNode instanceof Text)) continue;
const splitLength = Math.max(0, sourcePosition.scriptOffset - pos - offset);
offset += splitLength;
const replacementNode = textNode.splitText(splitLength);
const span = document.createElement("span");
span.setAttribute("scriptOffset", sourcePosition.scriptOffset.toString());
span.classList.add("source-position");
const marker = document.createElement("span");
marker.classList.add("marker");
span.appendChild(marker);
const inlining = this.sourceResolver.getInliningForPosition(sourcePosition);
if (inlining && view.showAdditionalInliningPosition) {
const sourceName = this.sourceResolver.getSourceName(inlining.sourceId);
const inliningMarker = document.createElement("span");
inliningMarker.classList.add("inlining-marker");
inliningMarker.setAttribute("data-descr", `${sourceName} was inlined here`);
span.appendChild(inliningMarker);
}
span.onclick = function (e: MouseEvent) {
e.stopPropagation();
view.onSelectSourcePosition(sourcePosition, !e.shiftKey);
};
view.addHtmlElementToSourcePosition(sourcePosition, span);
textNode.parentNode.insertBefore(span, replacementNode);
}
}
private insertLineNumber(lineElement: HTMLElement, lineNumber: number): void {
const lineNumberElement = document.createElement("div");
lineNumberElement.classList.add("line-number");
lineNumberElement.dataset.lineNumber = String(lineNumber);
lineNumberElement.innerText = String(lineNumber);
lineElement.insertBefore(lineNumberElement, lineElement.firstChild);
for (const sourcePosition of this.sourceResolver.lineToSourcePositions(lineNumber - 1)) {
this.addHtmlElementToSourcePosition(sourcePosition, lineElement);
}
}
}