deploy: current vibn theia state
Some checks failed
Playwright Tests / Playwright Tests (ubuntu-22.04, Node.js 22.x) (push) Has been cancelled
3PP License Check / 3PP License Check (11, 22.x, ubuntu-22.04) (push) Has been cancelled
Publish packages to NPM / Perform Publishing (push) Has been cancelled

Made-with: Cursor
This commit is contained in:
2026-02-27 12:01:08 -08:00
commit 8bb5110148
3782 changed files with 640947 additions and 0 deletions

View File

@@ -0,0 +1,101 @@
// *****************************************************************************
// Copyright (C) 2019 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { inject, injectable, postConstruct } from '../../../shared/inversify';
import { Emitter, Event } from '../../common';
import { Disposable, DisposableCollection } from '../../common/disposable';
import { Coordinate } from '../context-menu-renderer';
import { RendererHost } from '../widgets/react-renderer';
import { Styles } from './breadcrumbs-constants';
export interface BreadcrumbPopupContainerFactory {
(parent: HTMLElement, breadcrumbId: string, position: Coordinate): BreadcrumbPopupContainer;
}
export const BreadcrumbPopupContainerFactory = Symbol('BreadcrumbPopupContainerFactory');
export type BreadcrumbID = string;
export const BreadcrumbID = Symbol('BreadcrumbID');
/**
* This class creates a popup container at the given position
* so that contributions can attach their HTML elements
* as children of `BreadcrumbPopupContainer#container`.
*
* - `dispose()` is called on blur or on hit on escape
*/
@injectable()
export class BreadcrumbPopupContainer implements Disposable {
@inject(RendererHost) protected readonly parent: RendererHost;
@inject(BreadcrumbID) public readonly breadcrumbId: BreadcrumbID;
@inject(Coordinate) protected readonly position: Coordinate;
protected onDidDisposeEmitter = new Emitter<void>();
protected toDispose: DisposableCollection = new DisposableCollection(this.onDidDisposeEmitter);
get onDidDispose(): Event<void> {
return this.onDidDisposeEmitter.event;
}
protected _container: HTMLElement;
get container(): HTMLElement {
return this._container;
}
protected _isOpen: boolean;
get isOpen(): boolean {
return this._isOpen;
}
@postConstruct()
protected init(): void {
this._container = this.createPopupDiv(this.position);
document.addEventListener('keyup', this.escFunction);
this._container.focus();
this._isOpen = true;
}
protected createPopupDiv(position: Coordinate): HTMLDivElement {
const result = window.document.createElement('div');
result.className = Styles.BREADCRUMB_POPUP;
result.style.left = `${position.x}px`;
result.style.top = `${position.y}px`;
result.tabIndex = 0;
result.addEventListener('focusout', this.onFocusOut);
this.parent.appendChild(result);
return result;
}
protected onFocusOut = (event: FocusEvent) => {
if (!(event.relatedTarget instanceof Element) || !this._container.contains(event.relatedTarget)) {
this.dispose();
}
};
protected escFunction = (event: KeyboardEvent) => {
if (event.key === 'Escape' || event.key === 'Esc') {
this.dispose();
}
};
dispose(): void {
if (!this.toDispose.disposed) {
this.onDidDisposeEmitter.fire();
this.toDispose.dispose();
this._container.remove();
this._isOpen = false;
document.removeEventListener('keyup', this.escFunction);
}
}
}

View File

@@ -0,0 +1,41 @@
// *****************************************************************************
// Copyright (C) 2019 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import * as React from 'react';
import { injectable } from 'inversify';
import { Breadcrumb, Styles } from './breadcrumbs-constants';
export const BreadcrumbRenderer = Symbol('BreadcrumbRenderer');
export interface BreadcrumbRenderer {
/**
* Renders the given breadcrumb. If `onClick` is given, it is called on breadcrumb click.
*/
render(breadcrumb: Breadcrumb, onMouseDown?: (breadcrumb: Breadcrumb, event: React.MouseEvent) => void): React.ReactNode;
}
@injectable()
export class DefaultBreadcrumbRenderer implements BreadcrumbRenderer {
render(breadcrumb: Breadcrumb, onMouseDown?: (breadcrumb: Breadcrumb, event: React.MouseEvent) => void): React.ReactNode {
return <li key={breadcrumb.id} title={breadcrumb.longLabel}
className={Styles.BREADCRUMB_ITEM + (!onMouseDown ? '' : ' ' + Styles.BREADCRUMB_ITEM_HAS_POPUP)}
onMouseDown={event => onMouseDown && onMouseDown(breadcrumb, event)}
tabIndex={0}
data-breadcrumb-id={breadcrumb.id}
>
{breadcrumb.iconClass && <span className={breadcrumb.iconClass}></span>} <span> {breadcrumb.label}</span>
</li >;
}
}

View File

@@ -0,0 +1,79 @@
// *****************************************************************************
// Copyright (C) 2019 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { MaybePromise, Event } from '../../common';
import { Disposable } from '../../../shared/vscode-languageserver-protocol';
import URI from '../../common/uri';
export namespace Styles {
export const BREADCRUMBS = 'theia-breadcrumbs';
export const BREADCRUMB_ITEM = 'theia-breadcrumb-item';
export const BREADCRUMB_POPUP_OVERLAY_CONTAINER = 'theia-breadcrumbs-popups-overlay';
export const BREADCRUMB_POPUP = 'theia-breadcrumbs-popup';
export const BREADCRUMB_ITEM_HAS_POPUP = 'theia-breadcrumb-item-haspopup';
}
/** A single breadcrumb in the breadcrumbs bar. */
export interface Breadcrumb {
/** An ID of this breadcrumb that should be unique in the breadcrumbs bar. */
readonly id: string;
/** The breadcrumb type. Should be the same as the contribution type `BreadcrumbsContribution#type`. */
readonly type: symbol;
/** The text that will be rendered as label. */
readonly label: string;
/** A longer text that will be used as tooltip text. */
readonly longLabel: string;
/** A CSS class for the icon. */
readonly iconClass?: string;
/** CSS classes for the container. */
readonly containerClass?: string;
}
export const BreadcrumbsContribution = Symbol('BreadcrumbsContribution');
export interface BreadcrumbsContribution {
/**
* The breadcrumb type. Breadcrumbs returned by `#computeBreadcrumbs(uri)` should have this as `Breadcrumb#type`.
*/
readonly type: symbol;
/**
* The priority of this breadcrumbs contribution. Contributions are rendered left to right in order of ascending priority.
*/
readonly priority: number;
/**
* An event emitter that should fire when breadcrumbs change for a given URI.
*/
readonly onDidChangeBreadcrumbs: Event<URI>;
/**
* Computes breadcrumbs for a given URI.
*/
computeBreadcrumbs(uri: URI): MaybePromise<Breadcrumb[]>;
/**
* Attaches the breadcrumb popup content for the given breadcrumb as child to the given parent.
* If it returns a Disposable, it is called when the popup closes.
*/
attachPopupContent(breadcrumb: Breadcrumb, parent: HTMLElement): Promise<Disposable | undefined>;
}

View File

@@ -0,0 +1,185 @@
// *****************************************************************************
// Copyright (C) 2019 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import * as React from 'react';
import { injectable, inject, postConstruct } from 'inversify';
import { ReactRenderer } from '../widgets';
import { BreadcrumbsService } from './breadcrumbs-service';
import { BreadcrumbRenderer } from './breadcrumb-renderer';
import PerfectScrollbar from 'perfect-scrollbar';
import URI from '../../common/uri';
import { Emitter, Event } from '../../common';
import { BreadcrumbPopupContainer } from './breadcrumb-popup-container';
import { CorePreferences } from '../../common/core-preferences';
import { Breadcrumb, Styles } from './breadcrumbs-constants';
import { LabelProvider } from '../label-provider';
interface Cancelable {
canceled: boolean;
}
@injectable()
export class BreadcrumbsRenderer extends ReactRenderer {
@inject(BreadcrumbsService)
protected readonly breadcrumbsService: BreadcrumbsService;
@inject(BreadcrumbRenderer)
protected readonly breadcrumbRenderer: BreadcrumbRenderer;
@inject(CorePreferences)
protected readonly corePreferences: CorePreferences;
@inject(LabelProvider)
protected readonly labelProvider: LabelProvider;
protected readonly onDidChangeActiveStateEmitter = new Emitter<boolean>();
get onDidChangeActiveState(): Event<boolean> {
return this.onDidChangeActiveStateEmitter.event;
}
protected uri: URI | undefined;
protected breadcrumbs: Breadcrumb[] = [];
protected popup: BreadcrumbPopupContainer | undefined;
protected scrollbar: PerfectScrollbar | undefined;
get active(): boolean {
return !!this.breadcrumbs.length;
}
protected get breadCrumbsContainer(): Element | undefined {
return this.host.firstElementChild ?? undefined;
}
protected refreshCancellationMarker: Cancelable = { canceled: true };
@postConstruct()
protected init(): void {
this.toDispose.push(this.onDidChangeActiveStateEmitter);
this.toDispose.push(this.breadcrumbsService.onDidChangeBreadcrumbs(uri => {
if (this.uri?.isEqual(uri)) {
this.refresh(this.uri);
}
}));
this.toDispose.push(this.corePreferences.onPreferenceChanged(change => {
if (change.preferenceName === 'breadcrumbs.enabled') {
this.refresh(this.uri);
}
}));
this.toDispose.push(this.labelProvider.onDidChange(() => this.refresh(this.uri)));
}
override dispose(): void {
super.dispose();
this.toDispose.dispose();
if (this.popup) { this.popup.dispose(); }
if (this.scrollbar) {
this.scrollbar.destroy();
this.scrollbar = undefined;
}
}
async refresh(uri?: URI): Promise<void> {
this.uri = uri;
this.refreshCancellationMarker.canceled = true;
const currentCallCanceled = { canceled: false };
this.refreshCancellationMarker = currentCallCanceled;
let breadcrumbs: Breadcrumb[];
if (uri && this.corePreferences['breadcrumbs.enabled']) {
breadcrumbs = await this.breadcrumbsService.getBreadcrumbs(uri);
} else {
breadcrumbs = [];
}
if (currentCallCanceled.canceled) {
return;
}
const wasActive = this.active;
this.breadcrumbs = breadcrumbs;
const isActive = this.active;
if (wasActive !== isActive) {
this.onDidChangeActiveStateEmitter.fire(isActive);
}
this.update();
}
protected update(): void {
this.render();
if (!this.scrollbar) {
this.createScrollbar();
} else {
this.scrollbar.update();
}
this.scrollToEnd();
}
protected createScrollbar(): void {
const { breadCrumbsContainer } = this;
if (breadCrumbsContainer) {
this.scrollbar = new PerfectScrollbar(breadCrumbsContainer, {
handlers: ['drag-thumb', 'keyboard', 'wheel', 'touch'],
useBothWheelAxes: true,
scrollXMarginOffset: 4,
suppressScrollY: true
});
}
}
protected scrollToEnd(): void {
const { breadCrumbsContainer } = this;
if (breadCrumbsContainer) {
breadCrumbsContainer.scrollLeft = breadCrumbsContainer.scrollWidth;
}
}
protected override doRender(): React.ReactNode {
return <ul className={Styles.BREADCRUMBS}>{this.renderBreadcrumbs()}</ul>;
}
protected renderBreadcrumbs(): React.ReactNode {
return this.breadcrumbs.map(breadcrumb => this.breadcrumbRenderer.render(breadcrumb, this.togglePopup));
}
protected togglePopup = (breadcrumb: Breadcrumb, event: React.MouseEvent) => {
event.stopPropagation();
event.preventDefault();
let openPopup = true;
if (this.popup?.isOpen) {
this.popup.dispose();
// There is a popup open. If the popup is the popup that belongs to the currently clicked breadcrumb
// just close the popup. If another breadcrumb was clicked, open the new popup immediately.
openPopup = this.popup.breadcrumbId !== breadcrumb.id;
} else {
this.popup = undefined;
}
if (openPopup) {
const { currentTarget } = event;
const breadcrumbElement = currentTarget.closest(`.${Styles.BREADCRUMB_ITEM}`);
if (breadcrumbElement) {
const { left: x, bottom: y } = breadcrumbElement.getBoundingClientRect();
this.breadcrumbsService.openPopup(breadcrumb, { x, y }).then(popup => { this.popup = popup; });
}
}
};
}
export const BreadcrumbsRendererFactory = Symbol('BreadcrumbsRendererFactory');
export interface BreadcrumbsRendererFactory {
(): BreadcrumbsRenderer;
}

View File

@@ -0,0 +1,108 @@
// *****************************************************************************
// Copyright (C) 2019 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
import { inject, injectable, named, postConstruct } from 'inversify';
import { ContributionProvider, Prioritizeable, Emitter, Event } from '../../common';
import URI from '../../common/uri';
import { Coordinate } from '../context-menu-renderer';
import { BreadcrumbPopupContainer, BreadcrumbPopupContainerFactory } from './breadcrumb-popup-container';
import { BreadcrumbsContribution, Styles, Breadcrumb } from './breadcrumbs-constants';
@injectable()
export class BreadcrumbsService {
@inject(ContributionProvider) @named(BreadcrumbsContribution)
protected readonly contributions: ContributionProvider<BreadcrumbsContribution>;
@inject(BreadcrumbPopupContainerFactory) protected readonly breadcrumbPopupContainerFactory: BreadcrumbPopupContainerFactory;
protected hasSubscribed = false;
protected popupsOverlayContainer: HTMLDivElement;
protected readonly onDidChangeBreadcrumbsEmitter = new Emitter<URI>();
@postConstruct()
init(): void {
this.createOverlayContainer();
}
protected createOverlayContainer(): void {
this.popupsOverlayContainer = window.document.createElement('div');
this.popupsOverlayContainer.id = Styles.BREADCRUMB_POPUP_OVERLAY_CONTAINER;
if (window.document.body) {
window.document.body.appendChild(this.popupsOverlayContainer);
}
}
/**
* Subscribe to this event emitter to be notified when the breadcrumbs have changed.
* The URI is the URI of the editor the breadcrumbs have changed for.
*/
get onDidChangeBreadcrumbs(): Event<URI> {
// This lazy subscription is to address problems in inversify's instantiation routine
// related to use of the IconThemeService by different components instantiated by the
// ContributionProvider.
if (!this.hasSubscribed) {
this.subscribeToContributions();
}
return this.onDidChangeBreadcrumbsEmitter.event;
}
/**
* Subscribes to the onDidChangeBreadcrumbs events for all contributions.
*/
protected subscribeToContributions(): void {
this.hasSubscribed = true;
for (const contribution of this.contributions.getContributions()) {
contribution.onDidChangeBreadcrumbs(uri => this.onDidChangeBreadcrumbsEmitter.fire(uri));
}
}
/**
* Returns the breadcrumbs for a given URI, possibly an empty list.
*/
async getBreadcrumbs(uri: URI): Promise<Breadcrumb[]> {
const result: Breadcrumb[] = [];
for (const contribution of await this.prioritizedContributions()) {
result.push(...await contribution.computeBreadcrumbs(uri));
}
return result;
}
protected async prioritizedContributions(): Promise<BreadcrumbsContribution[]> {
const prioritized = await Prioritizeable.prioritizeAll(
this.contributions.getContributions(), contribution => contribution.priority);
return prioritized.map(p => p.value).reverse();
}
/**
* Opens a popup for the given breadcrumb at the given position.
*/
async openPopup(breadcrumb: Breadcrumb, position: Coordinate): Promise<BreadcrumbPopupContainer | undefined> {
const contribution = this.contributions.getContributions().find(c => c.type === breadcrumb.type);
if (contribution) {
const popup = this.breadcrumbPopupContainerFactory(this.popupsOverlayContainer, breadcrumb.id, position);
const popupContent = await contribution.attachPopupContent(breadcrumb, popup.container);
if (popupContent && popup.isOpen) {
popup.onDidDispose(() => popupContent.dispose());
} else {
popupContent?.dispose();
}
return popup;
}
}
}

View File

@@ -0,0 +1,21 @@
// *****************************************************************************
// Copyright (C) 2019 TypeFox and others.
//
// This program and the accompanying materials are made available under the
// terms of the Eclipse Public License v. 2.0 which is available at
// http://www.eclipse.org/legal/epl-2.0.
//
// This Source Code may also be made available under the following Secondary
// Licenses when the conditions for such availability set forth in the Eclipse
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
// with the GNU Classpath Exception which is available at
// https://www.gnu.org/software/classpath/license.html.
//
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
// *****************************************************************************
export * from './breadcrumb-popup-container';
export * from './breadcrumb-renderer';
export * from './breadcrumbs-renderer';
export * from './breadcrumbs-service';
export * from './breadcrumbs-constants';