/** * Copyright (c) Microsoft Corporation. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ import { ActionEntry } from '../../../cli/traceViewer/traceModel'; import * as React from 'react'; import { useAsyncMemo } from './helpers'; import './sourceTab.css'; import '../../../third_party/highlightjs/highlightjs/tomorrow.css'; import * as highlightjs from '../../../third_party/highlightjs/highlightjs'; import { StackFrame } from '../../../common/types'; type StackInfo = string | { frames: StackFrame[]; fileContent: Map; }; export const SourceTab: React.FunctionComponent<{ actionEntry: ActionEntry | undefined, }> = ({ actionEntry }) => { const [lastAction, setLastAction] = React.useState(); const [selectedFrame, setSelectedFrame] = React.useState(0); const [needReveal, setNeedReveal] = React.useState(false); if (lastAction !== actionEntry) { setLastAction(actionEntry); setSelectedFrame(0); setNeedReveal(true); } const stackInfo = React.useMemo(() => { if (!actionEntry) return ''; const { action } = actionEntry; if (!action.stack) return ''; const frames = action.stack; return { frames, fileContent: new Map(), }; }, [actionEntry]); const content = useAsyncMemo(async () => { let value: string; if (typeof stackInfo === 'string') { value = stackInfo; } else { const filePath = stackInfo.frames[selectedFrame].file; if (!stackInfo.fileContent.has(filePath)) stackInfo.fileContent.set(filePath, await fetch(`/file?${filePath}`).then(response => response.text()).catch(e => ``)); value = stackInfo.fileContent.get(filePath)!; } const result = []; let continuation: any; for (const line of (value || '').split('\n')) { const highlighted = highlightjs.highlight('javascript', line, true, continuation); continuation = highlighted.top; result.push(highlighted.value); } return result; }, [stackInfo, selectedFrame], []); const targetLine = typeof stackInfo === 'string' ? -1 : stackInfo.frames[selectedFrame].line; const targetLineRef = React.createRef(); React.useLayoutEffect(() => { if (needReveal && targetLineRef.current) { targetLineRef.current.scrollIntoView({ block: 'center', inline: 'nearest' }); setNeedReveal(false); } }, [needReveal, targetLineRef]); return
{ content.map((markup, index) => { const isTargetLine = (index + 1) === targetLine; return
{index + 1}
; }) }
{typeof stackInfo !== 'string' &&
{ stackInfo.frames.map((frame, index) => { return
{ setSelectedFrame(index); setNeedReveal(true); }} > {frame.function || '(anonymous)'} {frame.file} {':' + frame.line}
; }) }
}
; };