Merge pull request #5096 from Tyriar/tyriar/esbuild4

Integrate base/ platform from VS Code and adopt scroll bar
This commit is contained in:
Daniel Imms
2024-07-11 09:42:44 -07:00
committed by GitHub
97 changed files with 19847 additions and 610 deletions
+2
View File
@@ -11,6 +11,7 @@
"src/browser/tsconfig.json",
"src/common/tsconfig.json",
"src/headless/tsconfig.json",
"src/vs/tsconfig.json",
"test/benchmark/tsconfig.json",
"test/playwright/tsconfig.json",
"addons/addon-attach/src/tsconfig.json",
@@ -43,6 +44,7 @@
},
"ignorePatterns": [
"addons/*/src/third-party/*.ts",
"src/vs/*",
"out/*",
"out-test/*",
"out-esbuild/*",
+2
View File
@@ -4,7 +4,9 @@
},
// Hide output files from the file explorer, comment this out to see the build output
"files.exclude": {
"**/.nyc_output": true,
"**/lib": true,
"**/dist": true,
"**/out": true,
"**/out-*": true,
},
+4 -2
View File
@@ -6,6 +6,7 @@
import type { Terminal, ITerminalAddon } from '@xterm/xterm';
import type { FitAddon as IFitApi } from '@xterm/addon-fit';
import { IRenderDimensions } from 'browser/renderer/shared/Types';
import { ViewportConstants } from 'browser/shared/Constants';
interface ITerminalDimensions {
/**
@@ -64,8 +65,9 @@ export class FitAddon implements ITerminalAddon , IFitApi {
return undefined;
}
const scrollbarWidth = this._terminal.options.scrollback === 0 ?
0 : core.viewport.scrollBarWidth;
const scrollbarWidth = (this._terminal.options.scrollback === 0
? 0
: (this._terminal.options.overviewRulerWidth || ViewportConstants.DEFAULT_SCROLL_BAR_WIDTH));
const parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement);
const parentElementHeight = parseInt(parentElementStyle.getPropertyValue('height'));
+13 -6
View File
@@ -20,9 +20,11 @@ const config = {
/** @type {esbuild.BuildOptions} */
const commonOptions = {
bundle: true,
format: 'esm',
target: 'es2021',
sourcemap: true,
treeShaking: true,
logLevel: 'debug',
};
@@ -34,8 +36,6 @@ const devOptions = {
/** @type {esbuild.BuildOptions} */
const prodOptions = {
minify: true,
treeShaking: true,
logLevel: 'debug',
legalComments: 'none',
// TODO: Mangling private and protected properties will reduce bundle size quite a bit, we must
// make sure we don't cast privates to `any` in order to prevent regressions.
@@ -80,20 +80,21 @@ function getAddonEntryPoint(addon) {
/** @type {esbuild.BuildOptions} */
let bundleConfig = {
bundle: true,
...commonOptions,
...(config.isProd ? prodOptions : devOptions)
};
/** @type {esbuild.BuildOptions} */
let outConfig = {
format: 'cjs'
format: 'cjs',
sourcemap: true,
}
let skipOut = false;
/** @type {esbuild.BuildOptions} */
let outTestConfig = {
format: 'cjs'
format: 'cjs',
sourcemap: true,
}
let skipOutTest = false;
@@ -171,7 +172,13 @@ if (config.addon) {
};
outConfig = {
...outConfig,
entryPoints: ['src/**/*.ts'],
entryPoints: [
'src/browser/**/*.ts',
'src/common/**/*.ts',
'src/headless/**/*.ts',
'src/vs/base/**/*.ts',
'src/vs/patches/**/*.ts'
],
outdir: 'out-esbuild/'
};
outTestConfig = {
+8 -1
View File
@@ -34,7 +34,14 @@ const checkCoverage = flagArgs.indexOf('--coverage') >= 0;
if (checkCoverage) {
flagArgs.splice(flagArgs.indexOf('--coverage'), 1);
const executable = npmBinScript('nyc');
const args = ['--check-coverage', `--lines=${COVERAGE_LINES_THRESHOLD}`, npmBinScript('mocha'), ...testFiles, ...flagArgs];
const args = [
'--check-coverage',
`--lines=${COVERAGE_LINES_THRESHOLD}`,
'--exclude=out-esbuild/vs/**',
npmBinScript('mocha'),
...testFiles,
...flagArgs
];
console.info('executable', executable);
console.info('args', args);
const run = cp.spawnSync(
+41
View File
@@ -0,0 +1,41 @@
// @ts-check
const { dirname } = require("path");
const ts = require("typescript");
const fs = require("fs");
function findUnusedSymbols(
/** @type string */ tsconfigPath
) {
// Initialize a program using the project's tsconfig.json
const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
const parsedConfig = ts.parseJsonConfigFileContent(configFile.config, ts.sys, dirname(tsconfigPath));
// Initialize a program with the parsed configuration
const program = ts.createProgram(parsedConfig.fileNames, {
...parsedConfig.options,
noUnusedLocals: true
});
const sourceFiles = program.getSourceFiles();
const usedBaseSourceFiles = sourceFiles.filter(e => e.fileName.includes('src/vs/base/'));
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())
// @ts-ignore HACK: This is only available in Node 20
.map(e => `${e.parentPath}/${e.name}`.replace(/\\/g, '/'))
);
const unusedFilesInBase = allFilesInBase.filter(e => !usedFilesInBase.includes(e));
console.log({
allFilesInBase,
usedFilesInBase,
unusedFilesInBase
});
}
// Example usage
findUnusedSymbols("./src/browser/tsconfig.json");
+61
View File
@@ -0,0 +1,61 @@
# Get latest vscode repo
if (Test-Path -Path "src/vs/temp") {
Write-Host "`e[32m> Fetching latest`e[0m"
git -C src/vs/temp checkout
git -C src/vs/temp pull
} else {
Write-Host "`e[32m> Cloning microsoft/vscode`e[0m"
$null = New-Item -ItemType Directory -Path "src/vs/temp" -Force
git clone https://github.com/microsoft/vscode src/vs/temp
}
# Delete old base
Write-Host "`e[32m> Deleting old base`e[0m"
$null = Remove-Item -Recurse -Force "src/vs/base"
# Copy base
Write-Host "`e[32m> Copying base`e[0m"
Copy-Item -Path "src/vs/temp/src/vs/base" -Destination "src/vs/base" -Recurse
# Comment out any CSS imports
Write-Host "`e[32m> Commenting out CSS imports" -NoNewline
$baseFiles = Get-ChildItem -Path "src/vs/base" -Recurse -File
$count = 0
foreach ($file in $baseFiles) {
$content = Get-Content -Path $file.FullName
$updatedContent = $content | ForEach-Object {
if ($_ -match "^import 'vs/css!") {
Write-Host "`e[32m." -NoNewline
$count++
"// $_"
} else {
$_
}
}
$updatedContent | Set-Content -Path $file.FullName
}
Write-Host " $count files patched`e[0m"
# Replace `monaco-*` with `xterm-*`, this will help avoid any styling conflicts when monaco and
# xterm.js are used in the same project.
Write-Host "`e[32m> Replacing monaco-* class names with xterm-* `e[0m" -NoNewline
$baseFiles = Get-ChildItem -Path "src/vs/base" -Recurse -File
$count = 0
foreach ($file in $baseFiles) {
$content = Get-Content -Path $file.FullName
if ($content -match "monaco-([a-zA-Z\-]+)") {
$updatedContent = $content -replace "monaco-([a-zA-Z\-]+)", 'xterm-$1'
Write-Host "`e[32m." -NoNewline
$count++
$updatedContent | Set-Content -Path $file.FullName
}
}
Write-Host " $count files patched`e[0m"
# Copy typings
Write-Host "`e[32m> Copying typings`e[0m"
Copy-Item -Path "src/vs/temp/src/typings" -Destination "src/vs" -Recurse -Force
# Deleting unwanted typings
Write-Host "`e[32m> Deleting unwanted typings`e[0m"
$null = Remove-Item -Path "src/vs/typings/vscode-globals-modules.d.ts" -Force
+65 -4
View File
@@ -112,10 +112,6 @@
top: 0;
}
.xterm .xterm-scroll-area {
visibility: hidden;
}
.xterm-char-measure-element {
display: inline-block;
visibility: hidden;
@@ -222,3 +218,68 @@
z-index: 2;
position: relative;
}
/* Derived from vs/base/browser/ui/scrollbar/media/scrollbar.css */
/* xterm.js customization: Override xterm's cursor style */
.xterm .xterm-scrollable-element > .scrollbar {
cursor: default;
}
/* Arrows */
.xterm .xterm-scrollable-element > .scrollbar > .scra {
cursor: pointer;
font-size: 11px !important;
}
.xterm .xterm-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;
}
.xterm .xterm-scrollable-element > .invisible {
opacity: 0;
pointer-events: none;
}
.xterm .xterm-scrollable-element > .invisible.fade {
transition: opacity 800ms linear;
}
/* Scrollable Content Inset Shadow */
.xterm .xterm-scrollable-element > .shadow {
position: absolute;
display: none;
}
.xterm .xterm-scrollable-element > .shadow.top {
display: block;
top: 0;
left: 3px;
height: 3px;
width: 100%;
box-shadow: var(--vscode-scrollbar-shadow, #000) 0 6px 6px -6px inset;
}
.xterm .xterm-scrollable-element > .shadow.left {
display: block;
top: 3px;
left: 0;
height: 100%;
width: 3px;
box-shadow: var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset;
}
.xterm .xterm-scrollable-element > .shadow.top-left-corner {
display: block;
top: 0;
left: 0;
height: 3px;
width: 3px;
}
.xterm .xterm-scrollable-element > .shadow.top.left {
box-shadow: var(--vscode-scrollbar-shadow, #000) 6px 0 6px -6px inset;
}
+4 -2
View File
@@ -437,12 +437,13 @@ function initOptions(term: Terminal): void {
'logger',
'theme',
'windowOptions',
'windowsPty'
'windowsPty',
// Deprecated
'fastScrollModifier'
];
const stringOptions = {
cursorStyle: ['block', 'underline', 'bar'],
cursorInactiveStyle: ['outline', 'block', 'bar', 'underline', 'none'],
fastScrollModifier: ['none', 'alt', 'ctrl', 'shift'],
fontFamily: null,
fontWeight: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
fontWeightBold: ['normal', 'bold', '100', '200', '300', '400', '500', '600', '700', '800', '900'],
@@ -575,6 +576,7 @@ function initOptions(term: Terminal): void {
cursor: '#333333',
cursorAccent: '#ffffff',
selectionBackground: '#add6ff',
overviewRulerBorder: '#aaaaaa',
black: '#000000',
blue: '#0451a5',
brightBlack: '#666666',
-62
View File
@@ -1,62 +0,0 @@
/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*/
// @ts-check
const path = require('path');
/**
* This webpack config does a production build for xterm.js. It works by taking the output from tsc
* (via `yarn watch` or `yarn prebuild`) which are put into `out/` and webpacks them into a
* production mode umd library module in `lib/`. The aliases are used fix up the absolute paths
* output by tsc (because of `baseUrl` and `paths` in `tsconfig.json`.
*
* @type {import('webpack').Configuration}
*/
const config = {
entry: path.resolve(__dirname, 'client.ts'),
devtool: 'inline-source-map',
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
},
{
test: /\.js$/,
use: ["source-map-loader"],
enforce: "pre",
exclude: /node_modules/
}
]
},
resolve: {
modules: [
'node_modules',
path.resolve(__dirname, '..'),
path.resolve(__dirname, '../addons')
],
extensions: [ '.tsx', '.ts', '.js' ],
alias: {
common: path.resolve('./out/common'),
browser: path.resolve('./out/browser')
},
fallback: {
// The ligature modules contains fallbacks for node environments, we never want to browserify them
stream: false,
util: false,
os: false,
path: false,
fs: false
}
},
output: {
filename: 'client-bundle.js',
path: path.resolve(__dirname, 'dist')
},
mode: 'development'
};
module.exports = config;
+2 -12
View File
@@ -27,15 +27,11 @@
"setup": "npm run build",
"presetup": "npm run install-addons",
"install-addons": "node ./bin/install-addons.js",
"start": "node demo/start",
"build-demo": "webpack --config ./demo/webpack.config.js",
"build": "npm run tsc",
"watch": "npm run tsc-watch",
"tsc": "tsc -b ./tsconfig.all.json",
"tsc-watch": "tsc -b -w ./tsconfig.all.json --preserveWatchOutput",
"esbuild": "node bin/esbuild_all.mjs",
"esbuild-watch": "node bin/esbuild_all.mjs --watch",
"esbuild-package": "node bin/esbuild_all.mjs --prod",
@@ -43,33 +39,26 @@
"esbuild-package-headless-only": "node bin/esbuild.mjs --prod --headless",
"esbuild-demo": "node bin/esbuild.mjs --demo-client",
"esbuild-demo-watch": "node bin/esbuild.mjs --demo-client --watch",
"test": "npm run test-unit",
"posttest": "npm run lint",
"lint": "eslint -c .eslintrc.json --max-warnings 0 --ext .ts src/ addons/",
"lint-api": "eslint --no-eslintrc -c .eslintrc.json.typings --max-warnings 0 --no-ignore --ext .d.ts typings/",
"test-unit": "node ./bin/test_unit.js",
"test-unit-coverage": "node ./bin/test_unit.js --coverage",
"test-unit-dev": "cross-env NODE_PATH='./out' mocha",
"test-integration": "node ./bin/test_integration.js --workers=75%",
"test-integration-chromium": "node ./bin/test_integration.js --workers=75% \"--project=ChromeStable\"",
"test-integration-firefox": "node ./bin/test_integration.js --workers=75% \"--project=FirefoxStable\"",
"test-integration-webkit": "node ./bin/test_integration.js --workers=75% \"--project=WebKit\"",
"test-integration-debug": "node ./bin/test_integration.js --workers=1 --headed --timeout=30000",
"benchmark": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json",
"benchmark-baseline": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --baseline out-tsc/test-benchmark/test/benchmark/*benchmark.js",
"benchmark-eval": "NODE_PATH=./out xterm-benchmark -r 5 -c test/benchmark/benchmark.json --eval out-tsc/test-benchmark/test/benchmark/*benchmark.js",
"clean": "rm -rf lib out addons/*/lib addons/*/out",
"vtfeatures": "node bin/extract_vtfeatures.js src/**/*.ts src/*.ts",
"prepackage": "npm run build",
"package": "webpack",
"postpackage":"npm run esbuild-package",
"postpackage": "npm run esbuild-package",
"prepackage-headless": "npm run esbuild-package-headless-only",
"package-headless": "webpack --config ./webpack.config.headless.js",
"postpackage-headless": "node ./bin/package_headless.js",
@@ -88,6 +77,7 @@
"@types/jsdom": "^16.2.13",
"@types/mocha": "^9.0.0",
"@types/node": "^18.16.0",
"@types/trusted-types": "^1.0.6",
"@types/utf8": "^3.0.0",
"@types/webpack": "^5.28.0",
"@types/ws": "^8.2.0",
+53 -68
View File
@@ -21,12 +21,12 @@
* http://linux.die.net/man/7/urxvt
*/
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from '@xterm/xterm';
import { copyHandler, handlePasteEvent, moveTextAreaUnderMouseCursor, paste, rightClickHandler } from 'browser/Clipboard';
import { addDisposableDomListener } from 'browser/Lifecycle';
import { Linkifier } from './Linkifier';
import * as Strings from 'browser/LocalizableStrings';
import { OscLinkProvider } from 'browser/OscLinkProvider';
import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal, IViewport } from 'browser/Types';
import { CharacterJoinerHandler, CustomKeyEventHandler, CustomWheelEventHandler, IBrowser, IBufferRange, ICompositionHelper, ILinkifier2, ITerminal } from 'browser/Types';
import { Viewport } from 'browser/Viewport';
import { BufferDecorationRenderer } from 'browser/decorations/BufferDecorationRenderer';
import { OverviewRulerRenderer } from 'browser/decorations/OverviewRulerRenderer';
@@ -36,6 +36,7 @@ import { IRenderer } from 'browser/renderer/shared/Types';
import { CharSizeService } from 'browser/services/CharSizeService';
import { CharacterJoinerService } from 'browser/services/CharacterJoinerService';
import { CoreBrowserService } from 'browser/services/CoreBrowserService';
import { LinkProviderService } from 'browser/services/LinkProviderService';
import { MouseService } from 'browser/services/MouseService';
import { RenderService } from 'browser/services/RenderService';
import { SelectionService } from 'browser/services/SelectionService';
@@ -46,7 +47,7 @@ import { CoreTerminal } from 'common/CoreTerminal';
import { EventEmitter, IEvent, forwardEvent } from 'common/EventEmitter';
import { MutableDisposable, toDisposable } from 'common/Lifecycle';
import * as Browser from 'common/Platform';
import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType, IColorEvent, ITerminalOptions, KeyboardResultType, ScrollSource, SpecialColorIndex } from 'common/Types';
import { ColorRequestType, CoreMouseAction, CoreMouseButton, CoreMouseEventType, IColorEvent, ITerminalOptions, KeyboardResultType, SpecialColorIndex } from 'common/Types';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { IBuffer } from 'common/buffer/Types';
import { C0, C1_ESCAPED } from 'common/data/EscapeSequences';
@@ -54,10 +55,9 @@ import { evaluateKeyboardEvent } from 'common/input/Keyboard';
import { toRgbString } from 'common/input/XParseColor';
import { DecorationService } from 'common/services/DecorationService';
import { IDecorationService } from 'common/services/Services';
import { IDecoration, IDecorationOptions, IDisposable, ILinkProvider, IMarker } from '@xterm/xterm';
import { WindowsOptionsReportType } from '../common/InputHandler';
import { AccessibilityManager } from './AccessibilityManager';
import { LinkProviderService } from 'browser/services/LinkProviderService';
import { Linkifier } from './Linkifier';
export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
public textarea: HTMLTextAreaElement | undefined;
@@ -65,13 +65,13 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
public screenElement: HTMLElement | undefined;
private _document: Document | undefined;
private _viewportScrollArea: HTMLElement | undefined;
private _viewportElement: HTMLElement | undefined;
private _helperContainer: HTMLElement | undefined;
private _compositionView: HTMLElement | undefined;
public linkifier: ILinkifier2 | undefined;
private _overviewRulerRenderer: OverviewRulerRenderer | undefined;
private _viewport: Viewport | undefined;
public browser: IBrowser = Browser as any;
@@ -118,7 +118,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
*/
private _unprocessedDeadKey: boolean = false;
public viewport: IViewport | undefined;
private _compositionHelper: ICompositionHelper | undefined;
private _accessibilityManager: MutableDisposable<AccessibilityManager> = this.register(new MutableDisposable());
@@ -427,10 +426,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this._viewportElement.classList.add('xterm-viewport');
fragment.appendChild(this._viewportElement);
this._viewportScrollArea = this._document.createElement('div');
this._viewportScrollArea.classList.add('xterm-scroll-area');
this._viewportElement.appendChild(this._viewportScrollArea);
this.screenElement = this._document.createElement('div');
this.screenElement.classList.add('xterm-screen');
this.register(addDisposableDomListener(this.screenElement, 'mousemove', (ev: MouseEvent) => this.updateCursorStyle(ev)));
@@ -503,11 +498,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this._renderService.setRenderer(this._createRenderer());
}
this.viewport = this._instantiationService.createInstance(Viewport, this._viewportElement, this._viewportScrollArea);
this.viewport.onRequestScrollLines(e => this.scrollLines(e.amount, e.suppressScrollEvent, ScrollSource.VIEWPORT)),
this.register(this._inputHandler.onRequestSyncScrollBar(() => this.viewport!.syncScrollArea()));
this.register(this.viewport);
this.register(this.onCursorMove(() => {
this._renderService!.handleCursorMove();
this._syncTextArea();
@@ -515,7 +505,9 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this.register(this.onResize(() => this._renderService!.handleResize(this.cols, this.rows)));
this.register(this.onBlur(() => this._renderService!.handleBlur()));
this.register(this.onFocus(() => this._renderService!.handleFocus()));
this.register(this._renderService.onDimensionsChange(() => this.viewport!.syncScrollArea()));
this._viewport = this.register(this._instantiationService.createInstance(Viewport, this.element, this.screenElement));
this.register(this._viewport.onRequestScrollLines(e => super.scrollLines(e, false)));
this._selectionService = this.register(this._instantiationService.createInstance(SelectionService,
this.element,
@@ -534,11 +526,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
this.textarea!.focus();
this.textarea!.select();
}));
this.register(this._onScroll.event(ev => {
this.viewport!.syncScrollArea();
this._selectionService!.refresh();
}));
this.register(addDisposableDomListener(this._viewportElement, 'scroll', () => this._selectionService!.refresh()));
this.register(this._onScroll.event(() => this._selectionService!.refresh()));
this.register(this._instantiationService.createInstance(BufferDecorationRenderer, this.screenElement));
this.register(addDisposableDomListener(this.element, 'mousedown', (e: MouseEvent) => this._selectionService!.handleMouseDown(e)));
@@ -642,13 +630,11 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
if (self._customWheelEventHandler && self._customWheelEventHandler(ev as WheelEvent) === false) {
return false;
}
const amount = self.viewport!.getLinesScrolled(ev as WheelEvent);
if (amount === 0) {
const deltaY = (ev as WheelEvent).deltaY;
if (deltaY === 0) {
return false;
}
action = (ev as WheelEvent).deltaY < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN;
action = deltaY < 0 ? CoreMouseAction.UP : CoreMouseAction.DOWN;
but = CoreMouseButton.WHEEL;
break;
default:
@@ -807,42 +793,23 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
if (!this.buffer.hasScrollback) {
// Convert wheel events into up/down events when the buffer does not have scrollback, this
// enables scrolling in apps hosted in the alt buffer such as vim or tmux.
const amount = this.viewport!.getLinesScrolled(ev);
// enables scrolling in apps hosted in the alt buffer such as vim or tmux even when mouse
// events are not enabled.
// This used implementation used get the actual lines/partial lines scrolled from the
// viewport but since moving to the new viewport implementation has been simplified to
// simply send a single up or down sequence.
// Do nothing if there's no vertical scroll
if (amount === 0) {
return;
const deltaY = (ev as WheelEvent).deltaY;
if (deltaY === 0) {
return false;
}
// Construct and send sequences
const sequence = C0.ESC + (this.coreService.decPrivateModes.applicationCursorKeys ? 'O' : '[') + (ev.deltaY < 0 ? 'A' : 'B');
let data = '';
for (let i = 0; i < Math.abs(amount); i++) {
data += sequence;
}
this.coreService.triggerDataEvent(data, true);
this.coreService.triggerDataEvent(sequence, true);
return this.cancel(ev, true);
}
// normal viewport scrolling
// conditionally stop event, if the viewport still had rows to scroll within
if (this.viewport!.handleWheel(ev)) {
return this.cancel(ev);
}
}, { passive: false }));
this.register(addDisposableDomListener(el, 'touchstart', (ev: TouchEvent) => {
if (this.coreMouseService.areMouseEventsActive) return;
this.viewport!.handleTouchStart(ev);
return this.cancel(ev);
}, { passive: true }));
this.register(addDisposableDomListener(el, 'touchmove', (ev: TouchEvent) => {
if (this.coreMouseService.areMouseEventsActive) return;
if (!this.viewport!.handleTouchMove(ev)) {
return this.cancel(ev);
}
}, { passive: false }));
}
@@ -878,12 +845,36 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
}
}
public scrollLines(disp: number, suppressScrollEvent?: boolean, source = ScrollSource.TERMINAL): void {
if (source === ScrollSource.VIEWPORT) {
super.scrollLines(disp, suppressScrollEvent, source);
this.refresh(0, this.rows - 1);
public scrollLines(disp: number, suppressScrollEvent?: boolean): void {
// All scrollLines methods need to go via the viewport in order to support smooth scroll
if (this._viewport) {
this._viewport.scrollLines(disp);
} else {
this.viewport?.scrollLines(disp);
super.scrollLines(disp, suppressScrollEvent);
}
this.refresh(0, this.rows - 1);
}
public scrollPages(pageCount: number): void {
this.scrollLines(pageCount * (this.rows - 1));
}
public scrollToTop(): void {
this.scrollLines(-this._bufferService.buffer.ydisp);
}
public scrollToBottom(disableSmoothScroll?: boolean): void {
if (disableSmoothScroll && this._viewport) {
this._viewport.scrollToLine(this.buffer.ybase, true);
} else {
this.scrollLines(this._bufferService.buffer.ybase - this._bufferService.buffer.ydisp);
}
}
public scrollToLine(line: number): void {
const scrollAmount = line - this._bufferService.buffer.ydisp;
if (scrollAmount !== 0) {
this.scrollLines(scrollAmount);
}
}
@@ -1011,7 +1002,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
if (!shouldIgnoreComposition && !this._compositionHelper!.keydown(event)) {
if (this.options.scrollOnUserInput && this.buffer.ybase !== this.buffer.ydisp) {
this.scrollToBottom();
this.scrollToBottom(true);
}
return false;
}
@@ -1212,10 +1203,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
private _afterResize(x: number, y: number): void {
this._charSizeService?.measure();
// Sync the scroll area to make sure scroll events don't fire and scroll the viewport to an
// invalid location
this.viewport?.syncScrollArea(true);
}
/**
@@ -1237,8 +1224,7 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
}
// IMPORTANT: Fire scroll event before viewport is reset. This ensures embedders get the clear
// scroll event and that the viewport's state will be valid for immediate writes.
this._onScroll.fire({ position: this.buffer.ydisp, source: ScrollSource.TERMINAL });
this.viewport?.reset();
this._onScroll.fire({ position: this.buffer.ydisp });
this.refresh(0, this.rows - 1);
}
@@ -1263,7 +1249,6 @@ export class CoreBrowserTerminal extends CoreTerminal implements ITerminal {
super.reset();
this._selectionService?.reset();
this._decorationService.reset();
this.viewport?.reset();
// reattach
this._customKeyEventHandler = customKeyEventHandler;
+4 -6
View File
@@ -3,14 +3,13 @@
* @license MIT
*/
import { MockCompositionHelper, MockRenderer, MockViewport, TestTerminal } from 'browser/TestUtils.test';
import type { IBrowser } from 'browser/Types';
import { assert } from 'chai';
import { MockViewport, MockCompositionHelper, MockRenderer, TestTerminal } from 'browser/TestUtils.test';
import { DEFAULT_ATTR_DATA } from 'common/buffer/BufferLine';
import { CellData } from 'common/buffer/CellData';
import { MockUnicodeService } from 'common/TestUtils.test';
import { IMarker, ScrollSource } from 'common/Types';
import { ICoreService } from 'common/services/Services';
import type { IBrowser } from 'browser/Types';
import { IMarker } from 'common/Types';
const INIT_COLS = 80;
const INIT_ROWS = 24;
@@ -29,8 +28,7 @@ describe('Terminal', () => {
term = new TestTerminal(termOptions);
term.refresh = () => { };
(term as any).renderer = new MockRenderer();
term.viewport = new MockViewport();
term.viewport.onRequestScrollLines(e => term.scrollLines(e.amount, e.suppressScrollEvent, ScrollSource.VIEWPORT));
(term as any).viewport = new MockViewport();
(term as any)._compositionHelper = new MockCompositionHelper();
(term as any).element = {
classList: {
+4 -1
View File
@@ -19,7 +19,6 @@ export interface ITerminal extends InternalPassthroughApis, ICoreTerminal {
browser: IBrowser;
buffer: IBuffer;
linkifier: ILinkifier2 | undefined;
viewport: IViewport | undefined;
options: Required<ITerminalOptions>;
onBlur: IEvent<void>;
@@ -67,6 +66,10 @@ export interface IColorSet {
selectionBackgroundOpaque: IColor;
selectionInactiveBackgroundTransparent: IColor;
selectionInactiveBackgroundOpaque: IColor;
scrollbarSliderBackground: IColor;
scrollbarSliderHoverBackground: IColor;
scrollbarSliderActiveBackground: IColor;
overviewRulerBorder: IColor;
ansi: IColor[];
/** Maps original colors to colors that respect minimum contrast ratio. */
contrastCache: IColorContrastCache;
+151 -379
View File
File diff suppressed because it is too large Load Diff
@@ -4,10 +4,14 @@
*--------------------------------------------------------------------------------------------*/
import { ColorZoneStore, IColorZone, IColorZoneStore } from 'browser/decorations/ColorZoneStore';
import { ICoreBrowserService, IRenderService } from 'browser/services/Services';
import { ICoreBrowserService, IRenderService, IThemeService } from 'browser/services/Services';
import { Disposable, toDisposable } from 'common/Lifecycle';
import { IBufferService, IDecorationService, IOptionsService } from 'common/services/Services';
const enum Constants {
OVERVIEW_RULER_BORDER_WIDTH = 1
}
// Helper objects to avoid excessive calculation and garbage collection during rendering. These are
// static values for each render and can be accessed using the decoration position as the key.
const drawHeight = {
@@ -51,6 +55,7 @@ export class OverviewRulerRenderer extends Disposable {
@IDecorationService private readonly _decorationService: IDecorationService,
@IRenderService private readonly _renderService: IRenderService,
@IOptionsService private readonly _optionsService: IOptionsService,
@IThemeService private readonly _themeService: IThemeService,
@ICoreBrowserService private readonly _coreBrowserService: ICoreBrowserService
) {
super();
@@ -58,33 +63,18 @@ export class OverviewRulerRenderer extends Disposable {
this._canvas.classList.add('xterm-decoration-overview-ruler');
this._refreshCanvasDimensions();
this._viewportElement.parentElement?.insertBefore(this._canvas, this._viewportElement);
this.register(toDisposable(() => this._canvas?.remove()));
const ctx = this._canvas.getContext('2d');
if (!ctx) {
throw new Error('Ctx cannot be null');
} else {
this._ctx = ctx;
}
this._registerDecorationListeners();
this._registerBufferChangeListeners();
this._registerDimensionChangeListeners();
this.register(toDisposable(() => {
this._canvas?.remove();
}));
}
/**
* On decoration add or remove, redraw
*/
private _registerDecorationListeners(): void {
this.register(this._decorationService.onDecorationRegistered(() => this._queueRefresh(undefined, true)));
this.register(this._decorationService.onDecorationRemoved(() => this._queueRefresh(undefined, true)));
}
/**
* On buffer change, redraw
* and hide the canvas if the alt buffer is active
*/
private _registerBufferChangeListeners(): void {
this.register(this._renderService.onRenderedViewportChange(() => this._queueRefresh()));
this.register(this._bufferService.buffers.onBufferActivate(() => {
this._canvas!.style.display = this._bufferService.buffer === this._bufferService.buffers.alt ? 'none' : 'block';
@@ -95,31 +85,25 @@ export class OverviewRulerRenderer extends Disposable {
this._refreshColorZonePadding();
}
}));
}
/**
* On dimension change, update canvas dimensions
* and then redraw
*/
private _registerDimensionChangeListeners(): void {
// container height changed
// Container height changed
this.register(this._renderService.onRender((): void => {
if (!this._containerHeight || this._containerHeight !== this._screenElement.clientHeight) {
this._queueRefresh(true);
this._containerHeight = this._screenElement.clientHeight;
}
}));
// overview ruler width changed
this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true)));
// device pixel ratio changed
this.register(this._coreBrowserService.onDprChange(() => this._queueRefresh(true)));
// set the canvas dimensions
this.register(this._optionsService.onSpecificOptionChange('overviewRulerWidth', () => this._queueRefresh(true)));
this.register(this._themeService.onChangeColors(() => this._queueRefresh()));
this._queueRefresh(true);
}
private _refreshDrawConstants(): void {
// width
const outerWidth = Math.floor(this._canvas.width / 3);
const innerWidth = Math.ceil(this._canvas.width / 3);
const outerWidth = Math.floor((this._canvas.width - Constants.OVERVIEW_RULER_BORDER_WIDTH) / 3);
const innerWidth = Math.ceil((this._canvas.width - Constants.OVERVIEW_RULER_BORDER_WIDTH) / 3);
drawWidth.full = this._canvas.width;
drawWidth.left = outerWidth;
drawWidth.center = innerWidth;
@@ -127,10 +111,10 @@ export class OverviewRulerRenderer extends Disposable {
// height
this._refreshDrawHeightConstants();
// x
drawX.full = 0;
drawX.left = 0;
drawX.center = drawWidth.left;
drawX.right = drawWidth.left + drawWidth.center;
drawX.full = Constants.OVERVIEW_RULER_BORDER_WIDTH;
drawX.left = Constants.OVERVIEW_RULER_BORDER_WIDTH;
drawX.center = Constants.OVERVIEW_RULER_BORDER_WIDTH + drawWidth.left;
drawX.right = Constants.OVERVIEW_RULER_BORDER_WIDTH + drawWidth.left + drawWidth.center;
}
private _refreshDrawHeightConstants(): void {
@@ -173,6 +157,7 @@ export class OverviewRulerRenderer extends Disposable {
this._colorZoneStore.addDecoration(decoration);
}
this._ctx.lineWidth = 1;
this._renderRulerOutline();
const zones = this._colorZoneStore.zones;
for (const zone of zones) {
if (zone.position !== 'full') {
@@ -188,6 +173,11 @@ export class OverviewRulerRenderer extends Disposable {
this._shouldUpdateAnchor = false;
}
private _renderRulerOutline(): void {
this._ctx.fillStyle = this._themeService.colors.overviewRulerBorder.css;
this._ctx.fillRect(0, 0, Constants.OVERVIEW_RULER_BORDER_WIDTH, this._canvas.height);
}
private _renderColorZone(zone: IColorZone): void {
this._ctx.fillStyle = zone.color;
this._ctx.fillRect(
@@ -21,6 +21,10 @@ export function generateConfig(deviceCellWidth: number, deviceCellHeight: number
selectionBackgroundOpaque: NULL_COLOR,
selectionInactiveBackgroundTransparent: NULL_COLOR,
selectionInactiveBackgroundOpaque: NULL_COLOR,
overviewRulerBorder: NULL_COLOR,
scrollbarSliderBackground: NULL_COLOR,
scrollbarSliderHoverBackground: NULL_COLOR,
scrollbarSliderActiveBackground: NULL_COLOR,
// For the static char atlas, we only use the first 16 colors, but we need all 256 for the
// dynamic character atlas.
ansi: colors.ansi.slice(),
+10 -1
View File
@@ -23,11 +23,12 @@ interface IRestoreColorSet {
const DEFAULT_FOREGROUND = css.toColor('#ffffff');
const DEFAULT_BACKGROUND = css.toColor('#000000');
const DEFAULT_CURSOR = css.toColor('#ffffff');
const DEFAULT_CURSOR_ACCENT = css.toColor('#000000');
const DEFAULT_CURSOR_ACCENT = DEFAULT_BACKGROUND;
const DEFAULT_SELECTION = {
css: 'rgba(255, 255, 255, 0.3)',
rgba: 0xFFFFFF4D
};
const DEFAULT_OVERVIEW_RULER_BORDER = DEFAULT_FOREGROUND;
export class ThemeService extends Disposable implements IThemeService {
public serviceBrand: undefined;
@@ -57,6 +58,10 @@ export class ThemeService extends Disposable implements IThemeService {
selectionBackgroundOpaque: color.blend(DEFAULT_BACKGROUND, DEFAULT_SELECTION),
selectionInactiveBackgroundTransparent: DEFAULT_SELECTION,
selectionInactiveBackgroundOpaque: color.blend(DEFAULT_BACKGROUND, DEFAULT_SELECTION),
scrollbarSliderBackground: color.opacity(DEFAULT_FOREGROUND, 0.2),
scrollbarSliderHoverBackground: color.opacity(DEFAULT_FOREGROUND, 0.4),
scrollbarSliderActiveBackground: color.opacity(DEFAULT_FOREGROUND, 0.5),
overviewRulerBorder: DEFAULT_FOREGROUND,
ansi: DEFAULT_ANSI_COLORS.slice(),
contrastCache: this._contrastCache,
halfContrastCache: this._halfContrastCache
@@ -100,6 +105,10 @@ export class ThemeService extends Disposable implements IThemeService {
const opacity = 0.3;
colors.selectionInactiveBackgroundTransparent = color.opacity(colors.selectionInactiveBackgroundTransparent, opacity);
}
colors.scrollbarSliderBackground = parseColor(theme.scrollbarSliderBackground, color.opacity(colors.foreground, 0.2));
colors.scrollbarSliderHoverBackground = parseColor(theme.scrollbarSliderHoverBackground, color.opacity(colors.foreground, 0.4));
colors.scrollbarSliderActiveBackground = parseColor(theme.scrollbarSliderActiveBackground, color.opacity(colors.foreground, 0.5));
colors.overviewRulerBorder = parseColor(theme.overviewRulerBorder, DEFAULT_OVERVIEW_RULER_BORDER);
colors.ansi = DEFAULT_ANSI_COLORS.slice();
colors.ansi[0] = parseColor(theme.black, DEFAULT_ANSI_COLORS[0]);
colors.ansi[1] = parseColor(theme.red, DEFAULT_ANSI_COLORS[1]);
+8
View File
@@ -0,0 +1,8 @@
/**
* Copyright (c) 2024 The xterm.js authors. All rights reserved.
* @license MIT
*/
export const enum ViewportConstants {
DEFAULT_SCROLL_BAR_WIDTH = 14
}
+6 -3
View File
@@ -7,11 +7,13 @@
],
"outDir": "../../out",
"types": [
"../../node_modules/@types/mocha"
"../../node_modules/@types/mocha",
"../vs/typings/thenable.d.ts"
],
"baseUrl": "..",
"paths": {
"common/*": [ "./common/*" ]
"common/*": [ "./common/*" ],
"vs/*": [ "./vs/*" ]
}
},
"include": [
@@ -19,6 +21,7 @@
"../../typings/xterm.d.ts"
],
"references": [
{ "path": "../common" }
{ "path": "../common" },
{ "path": "../vs" }
]
}

Some files were not shown because too many files have changed in this diff Show More