More clean up, doc updating vs/base

This commit is contained in:
Daniel Imms
2024-07-09 13:53:16 -07:00
parent 381200bce1
commit b401865957
7 changed files with 34 additions and 497 deletions
@@ -2,6 +2,7 @@
const { dirname } = require("path");
const ts = require("typescript");
const fs = require("fs");
function findUnusedSymbols(
/** @type string */ tsconfigPath
@@ -17,7 +18,22 @@ function findUnusedSymbols(
});
const sourceFiles = program.getSourceFiles();
const usedBaseSourceFiles = sourceFiles.filter(e => e.fileName.includes('src/vs/base/'));
console.log('Source files used in src/vs/base/:', usedBaseSourceFiles.map(e => e.fileName.replace(/^.+\/src\//, 'src/')).sort((a, b) => a.localeCompare(b)));
const usedFilesInBase = usedBaseSourceFiles.map(e => e.fileName.replace(/^.+\/src\//, 'src/')).sort((a, b) => a.localeCompare(b));
// console.log('Source files used in src/vs/base/:', used);
// Get an array of all files that exist in src/vs/base/
const allFilesInBase = (
fs.readdirSync('src/vs/base', { recursive: true, withFileTypes: true })
.filter(e => e.isFile())
.map(e => `${e.parentPath}/${e.name}`.replace(/\\/g, '/'))
);
const unusedFilesInBase = allFilesInBase.filter(e => !usedFilesInBase.includes(e));
console.log({
allFilesInBase,
usedFilesInBase,
unusedFilesInBase
});
}
// Example usage
+17 -3
View File
@@ -1,9 +1,23 @@
This folder contains the `base/` module from the [Visual Studio Code repository](https://github.com/microsoft/vscode) which has many helpers that are useful to xterm.js.
To update against upstream:
Rarely we want to update these sources when an important bug is fixed upstream or when there is a new feature we want to leverage. To update against upstream:
```
./bin/update_vs_base.ps1
./bin/vs_base_update.ps1
```
TODO: Mention review `src/vs/base` in xterm.js
If new functions are being used from the project then import them from another project.
Before committing we need to clean up the diff so that files that aren't being used are not inlcuded. The following script uses the typescript compiler to find any files that are not being imported into the project:
```
node ./bin/vs_base_find_unused.js
```
The last step is to do a once over of the resulting bundled xterm.js file to ensure it isn't too large:
1. Run `yarn esbuild`
2. Open up `xterm.mjs`
3. Search for `src/vs/base/`
This will show you all the parts of base that will be included in the final minified bundle. Unfortunately tree shaking doesn't find everything, be on the lookout for large arrays or classes that aren't being used. If your editor has find decorations in the scroll bar it's easy to find which parts of base are consuming a lot of lines.
-272
View File
@@ -1,272 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
export namespace inputLatency {
// Measurements are recorded as totals, the average is calculated when the final measurements
// are created.
interface ICumulativeMeasurement {
total: number;
min: number;
max: number;
}
const totalKeydownTime: ICumulativeMeasurement = { total: 0, min: Number.MAX_VALUE, max: 0 };
const totalInputTime: ICumulativeMeasurement = { ...totalKeydownTime };
const totalRenderTime: ICumulativeMeasurement = { ...totalKeydownTime };
const totalInputLatencyTime: ICumulativeMeasurement = { ...totalKeydownTime };
let measurementsCount = 0;
// The state of each event, this helps ensure the integrity of the measurement and that
// something unexpected didn't happen that could skew the measurement.
const enum EventPhase {
Before = 0,
InProgress = 1,
Finished = 2
}
const state = {
keydown: EventPhase.Before,
input: EventPhase.Before,
render: EventPhase.Before,
};
/**
* Record the start of the keydown event.
*/
export function onKeyDown() {
/** Direct Check C. See explanation in {@link recordIfFinished} */
recordIfFinished();
performance.mark('inputlatency/start');
performance.mark('keydown/start');
state.keydown = EventPhase.InProgress;
queueMicrotask(markKeyDownEnd);
}
/**
* Mark the end of the keydown event.
*/
function markKeyDownEnd() {
if (state.keydown === EventPhase.InProgress) {
performance.mark('keydown/end');
state.keydown = EventPhase.Finished;
}
}
/**
* Record the start of the beforeinput event.
*/
export function onBeforeInput() {
performance.mark('input/start');
state.input = EventPhase.InProgress;
/** Schedule Task A. See explanation in {@link recordIfFinished} */
scheduleRecordIfFinishedTask();
}
/**
* Record the start of the input event.
*/
export function onInput() {
if (state.input === EventPhase.Before) {
// it looks like we didn't receive a `beforeinput`
onBeforeInput();
}
queueMicrotask(markInputEnd);
}
function markInputEnd() {
if (state.input === EventPhase.InProgress) {
performance.mark('input/end');
state.input = EventPhase.Finished;
}
}
/**
* Record the start of the keyup event.
*/
export function onKeyUp() {
/** Direct Check D. See explanation in {@link recordIfFinished} */
recordIfFinished();
}
/**
* Record the start of the selectionchange event.
*/
export function onSelectionChange() {
/** Direct Check E. See explanation in {@link recordIfFinished} */
recordIfFinished();
}
/**
* Record the start of the animation frame performing the rendering.
*/
export function onRenderStart() {
// Render may be triggered during input, but we only measure the following animation frame
if (state.keydown === EventPhase.Finished && state.input === EventPhase.Finished && state.render === EventPhase.Before) {
// Only measure the first render after keyboard input
performance.mark('render/start');
state.render = EventPhase.InProgress;
queueMicrotask(markRenderEnd);
/** Schedule Task B. See explanation in {@link recordIfFinished} */
scheduleRecordIfFinishedTask();
}
}
/**
* Mark the end of the animation frame performing the rendering.
*/
function markRenderEnd() {
if (state.render === EventPhase.InProgress) {
performance.mark('render/end');
state.render = EventPhase.Finished;
}
}
function scheduleRecordIfFinishedTask() {
// Here we can safely assume that the `setTimeout` will not be
// artificially delayed by 4ms because we schedule it from
// event handlers
setTimeout(recordIfFinished);
}
/**
* Record the input latency sample if input handling and rendering are finished.
*
* The challenge here is that we want to record the latency in such a way that it includes
* also the layout and painting work the browser does during the animation frame task.
*
* Simply scheduling a new task (via `setTimeout`) from the animation frame task would
* schedule the new task at the end of the task queue (after other code that uses `setTimeout`),
* so we need to use multiple strategies to make sure our task runs before others:
*
* We schedule tasks (A and B):
* - we schedule a task A (via a `setTimeout` call) when the input starts in `markInputStart`.
* If the animation frame task is scheduled quickly by the browser, then task A has a very good
* chance of being the very first task after the animation frame and thus will record the input latency.
* - however, if the animation frame task is scheduled a bit later, then task A might execute
* before the animation frame task. We therefore schedule another task B from `markRenderStart`.
*
* We do direct checks in browser event handlers (C, D, E):
* - if the browser has multiple keydown events queued up, they will be scheduled before the `setTimeout` tasks,
* so we do a direct check in the keydown event handler (C).
* - depending on timing, sometimes the animation frame is scheduled even before the `keyup` event, so we
* do a direct check there too (E).
* - the browser oftentimes emits a `selectionchange` event after an `input`, so we do a direct check there (D).
*/
function recordIfFinished() {
if (state.keydown === EventPhase.Finished && state.input === EventPhase.Finished && state.render === EventPhase.Finished) {
performance.mark('inputlatency/end');
performance.measure('keydown', 'keydown/start', 'keydown/end');
performance.measure('input', 'input/start', 'input/end');
performance.measure('render', 'render/start', 'render/end');
performance.measure('inputlatency', 'inputlatency/start', 'inputlatency/end');
addMeasure('keydown', totalKeydownTime);
addMeasure('input', totalInputTime);
addMeasure('render', totalRenderTime);
addMeasure('inputlatency', totalInputLatencyTime);
// console.info(
// `input latency=${performance.getEntriesByName('inputlatency')[0].duration.toFixed(1)} [` +
// `keydown=${performance.getEntriesByName('keydown')[0].duration.toFixed(1)}, ` +
// `input=${performance.getEntriesByName('input')[0].duration.toFixed(1)}, ` +
// `render=${performance.getEntriesByName('render')[0].duration.toFixed(1)}` +
// `]`
// );
measurementsCount++;
reset();
}
}
function addMeasure(entryName: string, cumulativeMeasurement: ICumulativeMeasurement): void {
const duration = performance.getEntriesByName(entryName)[0].duration;
cumulativeMeasurement.total += duration;
cumulativeMeasurement.min = Math.min(cumulativeMeasurement.min, duration);
cumulativeMeasurement.max = Math.max(cumulativeMeasurement.max, duration);
}
/**
* Clear the current sample.
*/
function reset() {
performance.clearMarks('keydown/start');
performance.clearMarks('keydown/end');
performance.clearMarks('input/start');
performance.clearMarks('input/end');
performance.clearMarks('render/start');
performance.clearMarks('render/end');
performance.clearMarks('inputlatency/start');
performance.clearMarks('inputlatency/end');
performance.clearMeasures('keydown');
performance.clearMeasures('input');
performance.clearMeasures('render');
performance.clearMeasures('inputlatency');
state.keydown = EventPhase.Before;
state.input = EventPhase.Before;
state.render = EventPhase.Before;
}
export interface IInputLatencyMeasurements {
keydown: IInputLatencySingleMeasurement;
input: IInputLatencySingleMeasurement;
render: IInputLatencySingleMeasurement;
total: IInputLatencySingleMeasurement;
sampleCount: number;
}
export interface IInputLatencySingleMeasurement {
average: number;
min: number;
max: number;
}
/**
* Gets all input latency samples and clears the internal buffers to start recording a new set
* of samples.
*/
export function getAndClearMeasurements(): IInputLatencyMeasurements | undefined {
if (measurementsCount === 0) {
return undefined;
}
// Assemble the result
const result = {
keydown: cumulativeToFinalMeasurement(totalKeydownTime),
input: cumulativeToFinalMeasurement(totalInputTime),
render: cumulativeToFinalMeasurement(totalRenderTime),
total: cumulativeToFinalMeasurement(totalInputLatencyTime),
sampleCount: measurementsCount
};
// Clear the cumulative measurements
clearCumulativeMeasurement(totalKeydownTime);
clearCumulativeMeasurement(totalInputTime);
clearCumulativeMeasurement(totalRenderTime);
clearCumulativeMeasurement(totalInputLatencyTime);
measurementsCount = 0;
return result;
}
function cumulativeToFinalMeasurement(cumulative: ICumulativeMeasurement): IInputLatencySingleMeasurement {
return {
average: cumulative.total / measurementsCount,
max: cumulative.max,
min: cumulative.min,
};
}
function clearCumulativeMeasurement(cumulative: ICumulativeMeasurement): void {
cumulative.total = 0;
cumulative.min = Number.MAX_VALUE;
cumulative.max = 0;
}
}
-114
View File
@@ -1,114 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { getWindowId, onDidUnregisterWindow } from 'vs/base/browser/dom';
import { Emitter, Event } from 'vs/base/common/event';
import { Disposable, markAsSingleton } from 'vs/base/common/lifecycle';
/**
* See https://developer.mozilla.org/en-US/docs/Web/API/Window/devicePixelRatio#monitoring_screen_resolution_or_zoom_level_changes
*/
class DevicePixelRatioMonitor extends Disposable {
private readonly _onDidChange = this._register(new Emitter<void>());
readonly onDidChange = this._onDidChange.event;
private readonly _listener: () => void;
private _mediaQueryList: MediaQueryList | null;
constructor(targetWindow: Window) {
super();
this._listener = () => this._handleChange(targetWindow, true);
this._mediaQueryList = null;
this._handleChange(targetWindow, false);
}
private _handleChange(targetWindow: Window, fireEvent: boolean): void {
this._mediaQueryList?.removeEventListener('change', this._listener);
this._mediaQueryList = targetWindow.matchMedia(`(resolution: ${targetWindow.devicePixelRatio}dppx)`);
this._mediaQueryList.addEventListener('change', this._listener);
if (fireEvent) {
this._onDidChange.fire();
}
}
}
export interface IPixelRatioMonitor {
readonly value: number;
readonly onDidChange: Event<number>;
}
class PixelRatioMonitorImpl extends Disposable implements IPixelRatioMonitor {
private readonly _onDidChange = this._register(new Emitter<number>());
readonly onDidChange = this._onDidChange.event;
private _value: number;
get value(): number {
return this._value;
}
constructor(targetWindow: Window) {
super();
this._value = this._getPixelRatio(targetWindow);
const dprMonitor = this._register(new DevicePixelRatioMonitor(targetWindow));
this._register(dprMonitor.onDidChange(() => {
this._value = this._getPixelRatio(targetWindow);
this._onDidChange.fire(this._value);
}));
}
private _getPixelRatio(targetWindow: Window): number {
const ctx: any = document.createElement('canvas').getContext('2d');
const dpr = targetWindow.devicePixelRatio || 1;
const bsr = ctx.webkitBackingStorePixelRatio ||
ctx.mozBackingStorePixelRatio ||
ctx.msBackingStorePixelRatio ||
ctx.oBackingStorePixelRatio ||
ctx.backingStorePixelRatio || 1;
return dpr / bsr;
}
}
class PixelRatioMonitorFacade {
private readonly mapWindowIdToPixelRatioMonitor = new Map<number, PixelRatioMonitorImpl>();
private _getOrCreatePixelRatioMonitor(targetWindow: Window): PixelRatioMonitorImpl {
const targetWindowId = getWindowId(targetWindow);
let pixelRatioMonitor = this.mapWindowIdToPixelRatioMonitor.get(targetWindowId);
if (!pixelRatioMonitor) {
pixelRatioMonitor = markAsSingleton(new PixelRatioMonitorImpl(targetWindow));
this.mapWindowIdToPixelRatioMonitor.set(targetWindowId, pixelRatioMonitor);
markAsSingleton(Event.once(onDidUnregisterWindow)(({ vscodeWindowId }) => {
if (vscodeWindowId === targetWindowId) {
pixelRatioMonitor?.dispose();
this.mapWindowIdToPixelRatioMonitor.delete(targetWindowId);
}
}));
}
return pixelRatioMonitor;
}
getInstance(targetWindow: Window): IPixelRatioMonitor {
return this._getOrCreatePixelRatioMonitor(targetWindow);
}
}
/**
* Returns the pixel ratio.
*
* This is useful for rendering <canvas> elements at native screen resolution or for being used as
* a cache key when storing font measurements. Fonts might render differently depending on resolution
* and any measurements need to be discarded for example when a window is moved from a monitor to another.
*/
export const PixelRatio = new PixelRatioMonitorFacade();
-35
View File
@@ -1,35 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import { onUnexpectedError } from 'vs/base/common/errors';
export function createTrustedTypesPolicy<Options extends TrustedTypePolicyOptions>(
policyName: string,
policyOptions?: Options,
): undefined | Pick<TrustedTypePolicy<Options>, 'name' | Extract<keyof Options, keyof TrustedTypePolicyOptions>> {
interface IMonacoEnvironment {
createTrustedTypesPolicy<Options extends TrustedTypePolicyOptions>(
policyName: string,
policyOptions?: Options,
): undefined | Pick<TrustedTypePolicy<Options>, 'name' | Extract<keyof Options, keyof TrustedTypePolicyOptions>>;
}
const monacoEnvironment: IMonacoEnvironment | undefined = (globalThis as any).MonacoEnvironment;
if (monacoEnvironment?.createTrustedTypesPolicy) {
try {
return monacoEnvironment.createTrustedTypesPolicy(policyName, policyOptions);
} catch (err) {
onUnexpectedError(err);
return undefined;
}
}
try {
return (globalThis as any).trustedTypes?.createPolicy(policyName, policyOptions);
} catch (err) {
onUnexpectedError(err);
return undefined;
}
}
@@ -1,72 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/* Arrows */
.monaco-scrollable-element > .scrollbar > .scra {
cursor: pointer;
font-size: 11px !important;
}
.monaco-scrollable-element > .visible {
opacity: 1;
/* Background rule added for IE9 - to allow clicks on dom node */
background:rgba(0,0,0,0);
transition: opacity 100ms linear;
/* In front of peek view */
z-index: 11;
}
.monaco-scrollable-element > .invisible {
opacity: 0;
pointer-events: none;
}
.monaco-scrollable-element > .invisible.fade {
transition: opacity 800ms linear;
}
/* Scrollable Content Inset Shadow */
.monaco-scrollable-element > .shadow {
position: absolute;
display: none;
}
.monaco-scrollable-element > .shadow.top {
display: block;
top: 0;
left: 3px;
height: 3px;
width: 100%;
box-shadow: var(--vscode-scrollbar-shadow) 0 6px 6px -6px inset;
}
.monaco-scrollable-element > .shadow.left {
display: block;
top: 3px;
left: 0;
height: 100%;
width: 3px;
box-shadow: var(--vscode-scrollbar-shadow) 6px 0 6px -6px inset;
}
.monaco-scrollable-element > .shadow.top-left-corner {
display: block;
top: 0;
left: 0;
height: 3px;
width: 3px;
}
.monaco-scrollable-element > .shadow.top.left {
box-shadow: var(--vscode-scrollbar-shadow) 6px 0 6px -6px inset;
}
.monaco-scrollable-element > .scrollbar > .slider {
background: var(--vscode-scrollbarSlider-background);
}
.monaco-scrollable-element > .scrollbar > .slider:hover {
background: var(--vscode-scrollbarSlider-hoverBackground);
}
.monaco-scrollable-element > .scrollbar > .slider.active {
background: var(--vscode-scrollbarSlider-activeBackground);
}