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,39 @@
// *****************************************************************************
// Copyright (C) 2017 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 { ContainerModule, interfaces } from '@theia/core/shared/inversify';
import { CommandContribution, MenuContribution } from '@theia/core/lib/common';
import { WebSocketConnectionProvider, KeybindingContribution } from '@theia/core/lib/browser';
import { QuickFileOpenFrontendContribution } from './quick-file-open-contribution';
import { QuickFileOpenService } from './quick-file-open';
import { fileSearchServicePath, FileSearchService } from '../common/file-search-service';
import { QuickAccessContribution } from '@theia/core/lib/browser/quick-input/quick-access';
import { QuickFileSelectService } from './quick-file-select-service';
export default new ContainerModule((bind: interfaces.Bind) => {
bind(FileSearchService).toDynamicValue(ctx => {
const provider = ctx.container.get(WebSocketConnectionProvider);
return provider.createProxy<FileSearchService>(fileSearchServicePath);
}).inSingletonScope();
bind(QuickFileOpenFrontendContribution).toSelf().inSingletonScope();
[CommandContribution, KeybindingContribution, MenuContribution, QuickAccessContribution].forEach(serviceIdentifier =>
bind(serviceIdentifier).toService(QuickFileOpenFrontendContribution)
);
bind(QuickFileSelectService).toSelf().inSingletonScope();
bind(QuickFileOpenService).toSelf().inSingletonScope();
});

View File

@@ -0,0 +1,66 @@
// *****************************************************************************
// Copyright (C) 2017 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 { injectable, inject } from '@theia/core/shared/inversify';
import URI from '@theia/core/lib/common/uri';
import { QuickFileOpenService, quickFileOpen } from './quick-file-open';
import { CommandRegistry, CommandContribution, MenuContribution, MenuModelRegistry } from '@theia/core/lib/common';
import { KeybindingRegistry, KeybindingContribution, QuickAccessContribution } from '@theia/core/lib/browser';
import { EditorMainMenu } from '@theia/editor/lib/browser';
import { nls } from '@theia/core/lib/common/nls';
@injectable()
export class QuickFileOpenFrontendContribution implements QuickAccessContribution, CommandContribution, KeybindingContribution, MenuContribution {
@inject(QuickFileOpenService)
protected readonly quickFileOpenService: QuickFileOpenService;
registerCommands(commands: CommandRegistry): void {
commands.registerCommand(quickFileOpen, {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
execute: (...args: any[]) => {
let fileURI: string | undefined;
if (args) {
[fileURI] = args;
}
if (fileURI) {
this.quickFileOpenService.openFile(new URI(fileURI));
} else {
this.quickFileOpenService.open();
}
}
});
}
registerKeybindings(keybindings: KeybindingRegistry): void {
keybindings.registerKeybinding({
command: quickFileOpen.id,
keybinding: 'ctrlcmd+p'
});
}
registerMenus(menus: MenuModelRegistry): void {
menus.registerMenuAction(EditorMainMenu.WORKSPACE_GROUP, {
commandId: quickFileOpen.id,
label: nls.localizeByDefault('Go to File...'),
order: '1',
});
}
registerQuickAccessProvider(): void {
this.quickFileOpenService.registerQuickAccessProvider();
}
}

View File

@@ -0,0 +1,208 @@
// *****************************************************************************
// Copyright (C) 2017 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 { CommonCommands, KeybindingRegistry, OpenerService, QuickAccessProvider, QuickAccessRegistry } from '@theia/core/lib/browser';
import { QuickInputService, QuickPickItem, QuickPicks } from '@theia/core/lib/browser/quick-input/quick-input-service';
import { CancellationToken, Command, nls } from '@theia/core/lib/common';
import { MessageService } from '@theia/core/lib/common/message-service';
import URI from '@theia/core/lib/common/uri';
import { inject, injectable, optional, postConstruct } from '@theia/core/shared/inversify';
import { EditorOpenerOptions, EditorWidget, Position, Range } from '@theia/editor/lib/browser';
import { NavigationLocationService } from '@theia/editor/lib/browser/navigation/navigation-location-service';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { QuickFileSelectService } from './quick-file-select-service';
export const quickFileOpen = Command.toDefaultLocalizedCommand({
id: 'file-search.openFile',
category: CommonCommands.FILE_CATEGORY,
label: 'Open File...'
});
export interface FilterAndRange {
filter: string;
range?: Range;
}
// Supports patterns of <path><#|:><line><#|:|,><col?>
const LINE_COLON_PATTERN = /\s?[#:\(](?:line )?(\d*)(?:[#:,](\d*))?\)?\s*$/;
export type FileQuickPickItem = QuickPickItem & { uri: URI };
@injectable()
export class QuickFileOpenService implements QuickAccessProvider {
static readonly PREFIX = '';
@inject(KeybindingRegistry)
protected readonly keybindingRegistry: KeybindingRegistry;
@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;
@inject(OpenerService)
protected readonly openerService: OpenerService;
@inject(QuickInputService) @optional()
protected readonly quickInputService: QuickInputService;
@inject(QuickAccessRegistry)
protected readonly quickAccessRegistry: QuickAccessRegistry;
@inject(NavigationLocationService)
protected readonly navigationLocationService: NavigationLocationService;
@inject(MessageService)
protected readonly messageService: MessageService;
@inject(QuickFileSelectService)
protected readonly quickFileSelectService: QuickFileSelectService;
registerQuickAccessProvider(): void {
this.quickAccessRegistry.registerQuickAccessProvider({
getInstance: () => this,
prefix: QuickFileOpenService.PREFIX,
placeholder: this.getPlaceHolder(),
helpEntries: [{ description: 'Open File', needsEditor: false }]
});
}
/**
* Whether to hide .gitignored (and other ignored) files.
*/
protected hideIgnoredFiles = true;
/**
* Whether the dialog is currently open.
*/
protected isOpen = false;
private updateIsOpen = true;
protected filterAndRangeDefault = { filter: '', range: undefined };
/**
* Tracks the user file search filter and location range e.g. fileFilter:line:column or fileFilter:line,column
*/
protected filterAndRange: FilterAndRange = this.filterAndRangeDefault;
@postConstruct()
protected init(): void {
this.quickInputService?.onHide(() => {
if (this.updateIsOpen) {
this.isOpen = false;
} else {
this.updateIsOpen = true;
}
});
}
isEnabled(): boolean {
return this.workspaceService.opened;
}
open(): void {
// Triggering the keyboard shortcut while the dialog is open toggles
// showing the ignored files.
if (this.isOpen) {
this.hideIgnoredFiles = !this.hideIgnoredFiles;
this.hideQuickPick();
} else {
this.hideIgnoredFiles = true;
this.filterAndRange = this.filterAndRangeDefault;
this.isOpen = true;
}
this.quickInputService?.open(this.filterAndRange.filter);
}
protected hideQuickPick(): void {
this.updateIsOpen = false;
this.quickInputService?.hide();
}
/**
* Get a string (suitable to show to the user) representing the keyboard
* shortcut used to open the quick file open menu.
*/
protected getKeyCommand(): string | undefined {
const keyCommand = this.keybindingRegistry.getKeybindingsForCommand(quickFileOpen.id);
if (keyCommand) {
// We only consider the first keybinding.
const accel = this.keybindingRegistry.acceleratorFor(keyCommand[0], '+');
return accel.join(' ');
}
return undefined;
}
async getPicks(filter: string, token: CancellationToken): Promise<QuickPicks> {
this.filterAndRange = this.splitFilterAndRange(filter);
const fileFilter = this.filterAndRange.filter;
return this.quickFileSelectService.getPicks(fileFilter, token, {
hideIgnoredFiles: this.hideIgnoredFiles,
onSelect: item => this.openFile(item.uri)
},
);
}
openFile(uri: URI): void {
const options = this.buildOpenerOptions();
const closedEditor = this.navigationLocationService.closedEditorsStack.find(editor => editor.uri.path.toString() === uri.path.toString());
this.openerService.getOpener(uri, options)
.then(opener => opener.open(uri, options))
.then(widget => {
// Attempt to restore the editor state if it exists, and no selection is explicitly requested.
if (widget instanceof EditorWidget && closedEditor && !options.selection) {
widget.editor.restoreViewState(closedEditor.viewState);
}
})
.catch(error => {
console.warn(error);
this.messageService.error(nls.localizeByDefault("Unable to open '{0}'", uri.path.toString()));
});
}
protected buildOpenerOptions(): EditorOpenerOptions {
return { selection: this.filterAndRange.range };
}
private getPlaceHolder(): string {
let placeholder = nls.localizeByDefault('Search files by name (append {0} to go to line or {1} to go to symbol)', ':', '@');
const keybinding = this.getKeyCommand();
if (keybinding) {
placeholder += nls.localize('theia/file-search/toggleIgnoredFiles', ' (Press {0} to show/hide ignored files)', keybinding);
}
return placeholder;
}
/**
* Splits the given expression into a structure of search-file-filter and
* location-range.
*
* @param expression patterns of <path><#|:><line><#|:|,><col?>
*/
protected splitFilterAndRange(expression: string): FilterAndRange {
let filter = expression;
let range = undefined;
// Find line and column number from the expression using RegExp.
const patternMatch = LINE_COLON_PATTERN.exec(expression);
if (patternMatch) {
const line = parseInt(patternMatch[1] ?? '', 10);
if (Number.isFinite(line)) {
const lineNumber = line > 0 ? line - 1 : 0;
const column = parseInt(patternMatch[2] ?? '', 10);
const startColumn = Number.isFinite(column) && column > 0 ? column - 1 : 0;
const position = Position.create(lineNumber, startColumn);
filter = expression.substring(0, patternMatch.index);
range = Range.create(position, position);
}
}
return { filter, range };
}
}

View File

@@ -0,0 +1,298 @@
// *****************************************************************************
// Copyright (C) 2017 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 { KeybindingRegistry, OpenerService, QuickAccessRegistry } from '@theia/core/lib/browser';
import { LabelProvider } from '@theia/core/lib/browser/label-provider';
import { findMatches, QuickInputService, QuickPickItem, QuickPicks } from '@theia/core/lib/browser/quick-input/quick-input-service';
import { CancellationToken, nls, PreferenceService, QuickPickSeparator } from '@theia/core/lib/common';
import { MessageService } from '@theia/core/lib/common/message-service';
import URI from '@theia/core/lib/common/uri';
import * as fuzzy from '@theia/core/shared/fuzzy';
import { inject, injectable, optional } from '@theia/core/shared/inversify';
import { Position, Range } from '@theia/editor/lib/browser';
import { NavigationLocationService } from '@theia/editor/lib/browser/navigation/navigation-location-service';
import { FileSystemPreferences } from '@theia/filesystem/lib/common';
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
import { FileSearchService, WHITESPACE_QUERY_SEPARATOR } from '../common/file-search-service';
export interface FilterAndRange {
filter: string;
range?: Range;
}
export interface QuickFileSelectOptions {
/** Whether to hide .gitignored (and other ignored) files. */
hideIgnoredFiles?: boolean;
/** Executed when the item is selected. */
onSelect?: (item: FileQuickPickItem) => void;
}
// Supports patterns of <path><#|:><line><#|:|,><col?>
const LINE_COLON_PATTERN = /\s?[#:\(](?:line )?(\d*)(?:[#:,](\d*))?\)?\s*$/;
export type FileQuickPickItem = QuickPickItem & { uri: URI };
export namespace FileQuickPickItem {
export function is(obj: QuickPickItem | QuickPickSeparator): obj is FileQuickPickItem {
return obj && 'uri' in obj;
}
}
@injectable()
export class QuickFileSelectService {
@inject(KeybindingRegistry)
protected readonly keybindingRegistry: KeybindingRegistry;
@inject(WorkspaceService)
protected readonly workspaceService: WorkspaceService;
@inject(OpenerService)
protected readonly openerService: OpenerService;
@inject(QuickInputService) @optional()
protected readonly quickInputService: QuickInputService;
@inject(QuickAccessRegistry)
protected readonly quickAccessRegistry: QuickAccessRegistry;
@inject(FileSearchService)
protected readonly fileSearchService: FileSearchService;
@inject(LabelProvider)
protected readonly labelProvider: LabelProvider;
@inject(NavigationLocationService)
protected readonly navigationLocationService: NavigationLocationService;
@inject(MessageService)
protected readonly messageService: MessageService;
@inject(FileSystemPreferences)
protected readonly fsPreferences: FileSystemPreferences;
@inject(PreferenceService)
protected readonly preferences: PreferenceService;
/**
* The score constants when comparing file search results.
*/
private static readonly Scores = {
max: 1000, // represents the maximum score from fuzzy matching (Infinity).
exact: 500, // represents the score assigned to exact matching.
partial: 250 // represents the score assigned to partial matching.
};
async getPicks(
fileFilter: string = '',
token: CancellationToken = CancellationToken.None,
options: QuickFileSelectOptions = {
hideIgnoredFiles: true
}
): Promise<QuickPicks> {
const roots = this.workspaceService.tryGetRoots();
const alreadyCollected = new Set<string>();
const recentlyUsedItems: QuickPicks = [];
if (this.preferences.get('search.quickOpen.includeHistory')) {
const locations = [...this.navigationLocationService.locations()].reverse();
for (const location of locations) {
const uriString = location.uri.toString();
if (location.uri.scheme === 'file' && !alreadyCollected.has(uriString) && fuzzy.test(fileFilter, uriString)) {
if (recentlyUsedItems.length === 0) {
recentlyUsedItems.push({
type: 'separator',
label: nls.localizeByDefault('recently opened')
});
}
const item = this.toItem(fileFilter, location.uri, options.onSelect);
recentlyUsedItems.push(item);
alreadyCollected.add(uriString);
}
}
}
if (fileFilter.length > 0) {
const handler = async (results: string[]) => {
if (token.isCancellationRequested || results.length <= 0) {
return [];
}
const result = [...recentlyUsedItems];
const fileSearchResultItems: FileQuickPickItem[] = [];
for (const fileUri of results) {
if (!alreadyCollected.has(fileUri)) {
const item = this.toItem(fileFilter, fileUri, options.onSelect);
fileSearchResultItems.push(item);
alreadyCollected.add(fileUri);
}
}
// Create a copy of the file search results and sort.
const sortedResults = fileSearchResultItems.slice();
sortedResults.sort((a, b) => this.compareItems(a, b, fileFilter));
if (sortedResults.length > 0) {
result.push({
type: 'separator',
label: nls.localizeByDefault('file results')
});
result.push(...sortedResults);
}
// Return the recently used items, followed by the search results.
return result;
};
return this.fileSearchService.find(fileFilter, {
rootUris: roots.map(r => r.resource.toString()),
fuzzyMatch: true,
limit: 200,
useGitIgnore: options.hideIgnoredFiles,
excludePatterns: options.hideIgnoredFiles
? Object.keys(this.fsPreferences['files.exclude'])
: undefined,
}, token).then(handler);
} else {
return roots.length !== 0 ? recentlyUsedItems : [];
}
}
protected compareItems(
left: FileQuickPickItem,
right: FileQuickPickItem,
fileFilter: string
): number {
/**
* Score a given string.
*
* @param str the string to score on.
* @returns the score.
*/
function score(str: string | undefined): number {
if (!str) {
return 0;
}
let exactMatch = true;
const partialMatches = querySplit.reduce((matched, part) => {
const partMatches = str.includes(part);
exactMatch = exactMatch && partMatches;
return partMatches ? matched + QuickFileSelectService.Scores.partial : matched;
}, 0);
// Check fuzzy matches.
const fuzzyMatch = fuzzy.match(queryJoin, str) ?? { score: 0 };
if (fuzzyMatch.score === Infinity && exactMatch) {
return Number.MAX_SAFE_INTEGER;
}
return fuzzyMatch.score + partialMatches + (exactMatch ? QuickFileSelectService.Scores.exact : 0);
}
const query: string = normalize(fileFilter);
// Adjust for whitespaces in the query.
const querySplit = query.split(WHITESPACE_QUERY_SEPARATOR);
const queryJoin = querySplit.join('');
const compareByLabelScore = (l: FileQuickPickItem, r: FileQuickPickItem) => score(r.label) - score(l.label);
const compareByLabelIndex = (l: FileQuickPickItem, r: FileQuickPickItem) => r.label.indexOf(query) - l.label.indexOf(query);
const compareByLabel = (l: FileQuickPickItem, r: FileQuickPickItem) => l.label.localeCompare(r.label);
const compareByPathScore = (l: FileQuickPickItem, r: FileQuickPickItem) => score(r.uri.path.toString()) - score(l.uri.path.toString());
const compareByPathIndex = (l: FileQuickPickItem, r: FileQuickPickItem) => r.uri.path.toString().indexOf(query) - l.uri.path.toString().indexOf(query);
const compareByPathLabel = (l: FileQuickPickItem, r: FileQuickPickItem) => l.uri.path.toString().localeCompare(r.uri.path.toString());
return compareWithDiscriminators(left, right, compareByLabelScore, compareByLabelIndex, compareByLabel, compareByPathScore, compareByPathIndex, compareByPathLabel);
}
private toItem(lookFor: string, uriOrString: URI | string, onSelect?: ((item: FileQuickPickItem) => void) | undefined): FileQuickPickItem {
const uri = uriOrString instanceof URI ? uriOrString : new URI(uriOrString);
const label = this.labelProvider.getName(uri);
const description = this.getItemDescription(uri);
const iconClasses = this.getItemIconClasses(uri);
const item = <FileQuickPickItem>{
label,
description,
highlights: {
label: findMatches(label, lookFor),
description: findMatches(description, lookFor)
},
iconClasses,
uri
};
return {
...item,
execute: () => onSelect ? onSelect(item) : undefined
};
}
private getItemIconClasses(uri: URI): string[] | undefined {
const icon = this.labelProvider.getIcon(uri).split(' ').filter(v => v.length > 0);
if (icon.length > 0) {
icon.push('file-icon');
}
return icon;
}
private getItemDescription(uri: URI): string {
return this.labelProvider.getDetails(uri);
}
/**
* Splits the given expression into a structure of search-file-filter and
* location-range.
*
* @param expression patterns of <path><#|:><line><#|:|,><col?>
*/
protected splitFilterAndRange(expression: string): FilterAndRange {
let filter = expression;
let range = undefined;
// Find line and column number from the expression using RegExp.
const patternMatch = LINE_COLON_PATTERN.exec(expression);
if (patternMatch) {
const line = parseInt(patternMatch[1] ?? '', 10);
if (Number.isFinite(line)) {
const lineNumber = line > 0 ? line - 1 : 0;
const column = parseInt(patternMatch[2] ?? '', 10);
const startColumn = Number.isFinite(column) && column > 0 ? column - 1 : 0;
const position = Position.create(lineNumber, startColumn);
filter = expression.substring(0, patternMatch.index);
range = Range.create(position, position);
}
}
return { filter, range };
}
}
/**
* Normalize a given string.
*
* @param str the raw string value.
* @returns the normalized string value.
*/
function normalize(str: string): string {
return str.trim().toLowerCase();
}
function compareWithDiscriminators<T>(left: T, right: T, ...discriminators: ((left: T, right: T) => number)[]): number {
let comparisonValue = 0;
let i = 0;
while (comparisonValue === 0 && i < discriminators.length) {
comparisonValue = discriminators[i](left, right);
i++;
}
return comparisonValue;
}