From ad9b1a8170e1325e5ca6b5a2e7b141c14ad9288b Mon Sep 17 00:00:00 2001 From: Ben Klein Date: Thu, 15 Jun 2023 01:29:26 -0400 Subject: [PATCH 1/6] init scaffold of fuzzy finder --- .../coderibbon-theia-frontend-module.ts | 15 ++++ .../src/browser/cr-fuzzy-file-opener.tsx | 71 +++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx diff --git a/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts b/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts index be9bf7b..efce115 100644 --- a/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts +++ b/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts @@ -15,6 +15,7 @@ import { } from "@theia/core/lib/browser"; import { PreferenceContribution } from "@theia/core/lib/browser/preferences"; import { ApplicationShell } from "@theia/core/lib/browser/shell/application-shell"; +import { bindViewContribution, FrontendApplicationContribution, WidgetFactory } from '@theia/core/lib/browser'; // import { CodeRibbonTheiaRibbonViewContribution } from './coderibbon-theia-ribbon'; import { CodeRibbonTheiaCommandContribution } from "./coderibbon-theia-commands"; @@ -24,6 +25,10 @@ import { CodeRibbonTheiaManager } from "./coderibbon-theia-manager"; import { CodeRibbonTheiaRibbonPanel } from "./cr-ribbon"; import { CodeRibbonApplicationShell } from "./cr-application-shell"; import { CodeRibbonTheiaKeybindingContribution } from "./coderibbon-theia-keybinds"; +import { + CodeRibbonFuzzyFileOpenerWidget, + CodeRibbonFuzzyFileOpenerContribution, +} from "./cr-fuzzy-file-opener"; import "../../src/browser/style/ribbon.less"; // temp CSS @@ -39,6 +44,16 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MenuContribution).to(CodeRibbonTheiaMenuContribution); bind(KeybindingContribution).to(CodeRibbonTheiaKeybindingContribution); + bindViewContribution(bind, CodeRibbonFuzzyFileOpenerContribution); + bind(FrontendApplicationContribution).toService(CodeRibbonFuzzyFileOpenerContribution); + bind(CodeRibbonFuzzyFileOpenerWidget).toSelf(); + bind(WidgetFactory).toDynamicValue(ctx => ({ + id: CodeRibbonFuzzyFileOpenerWidget.ID, + createWidget: () => ctx.container.get( + CodeRibbonFuzzyFileOpenerWidget + ), + })).inSingletonScope(); + // TODO fix prefs // bind(PreferenceContribution).toConstantValue({ // schema: CodeRibbonTheiaPreferenceSchema}); diff --git a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx new file mode 100644 index 0000000..8a62989 --- /dev/null +++ b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx @@ -0,0 +1,71 @@ +import * as React from 'react'; +import { injectable, postConstruct, inject } from '@theia/core/shared/inversify'; +import { AlertMessage } from '@theia/core/lib/browser/widgets/alert-message'; +import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; +import { MessageService } from '@theia/core'; +import { Message } from '@theia/core/lib/browser'; +import { AbstractViewContribution } from '@theia/core/lib/browser'; +import { Command, CommandRegistry } from '@theia/core/lib/common/command'; + +@injectable() +export class CodeRibbonFuzzyFileOpenerWidget extends ReactWidget { + static readonly ID = "coderibbon:fuzzy-file-opener"; + static readonly LABEL = "Fuzzy File Finder"; + + @postConstruct() + protected async init(): Promise { + this.id = CodeRibbonFuzzyFileOpenerWidget.ID; + this.title.label = CodeRibbonFuzzyFileOpenerWidget.LABEL; + this.title.caption = CodeRibbonFuzzyFileOpenerWidget.LABEL; + this.title.closable = true; + this.title.iconClass = "fa fa-window-maximize"; // example widget icon. + this.update(); + } + + render(): React.ReactElement { + const header = `This is a sample widget which simply calls the messageService in order to display an info message to end users.`; + return ( +
+ + +
+ ); + } + + @inject(MessageService) + protected readonly messageService!: MessageService; + + protected displayMessage(): void { + this.messageService.info( + "Congratulations: My Widget Successfully Created!", + ); + } +} + +export const TestOpenFFOCommand: Command = { id: 'coderibbon:test-ffo' }; + +@injectable() +export class CodeRibbonFuzzyFileOpenerContribution extends AbstractViewContribution { + constructor() { + super({ + widgetId: CodeRibbonFuzzyFileOpenerWidget.ID, + widgetName: CodeRibbonFuzzyFileOpenerWidget.LABEL, + defaultWidgetOptions: { area: 'main' }, + toggleCommandId: TestOpenFFOCommand.id + }); + } + + override registerCommands(commands: CommandRegistry): void { + commands.registerCommand(TestOpenFFOCommand, { + execute: () => super.openView({ activate: false, reveal: true }) + }); + } + + // registerMenus(menus:) +} From a8af534633eddee27d5afc2f63016c8cfabcc925 Mon Sep 17 00:00:00 2001 From: Ben Klein Date: Fri, 23 Feb 2024 00:36:33 -0500 Subject: [PATCH 2/6] restructure debug commands, working view --- .../src/browser/coderibbon-theia-commands.ts | 28 +++++++++++++------ .../src/browser/coderibbon-theia-menus.ts | 8 ++++-- .../src/browser/cr-fuzzy-file-opener.tsx | 2 +- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/coderibbon-theia/src/browser/coderibbon-theia-commands.ts b/coderibbon-theia/src/browser/coderibbon-theia-commands.ts index 12a8965..5770d6a 100644 --- a/coderibbon-theia/src/browser/coderibbon-theia-commands.ts +++ b/coderibbon-theia/src/browser/coderibbon-theia-commands.ts @@ -13,9 +13,15 @@ import { CodeRibbonTheiaRibbonStrip } from "./cr-ribbon-strip"; import { crdebug } from "./cr-logger"; -export const CodeRibbonHelloWorldCommand = { - id: "CodeRibbon.HelloWorld", - label: "Hello, CodeRibbon.", +export const CodeRibbonDebuggingCommands = { + helloWorldCommand: { + id: "CodeRibbon.HelloWorld", + label: "Hello, CodeRibbon.", + }, + testFuzzyFinderCommand: { + id: "CodeRibbon.dev.test_ff", + label: "CodeRibbon Test FuzzyFinder", + }, }; export const CodeRibbonDevGetPanelCommand = { @@ -136,18 +142,22 @@ export class CodeRibbonTheiaCommandContribution implements CommandContribution { ) {} registerCommands(registry: CommandRegistry): void { - registry.registerCommand(CodeRibbonHelloWorldCommand, { + + // === NOTE: Debugging section + // TODO: only register these in debug mode + + registry.registerCommand(CodeRibbonDebuggingCommands.helloWorldCommand, { execute: () => { this.messageService.info("CodeRibbon says hello!"); crdebug("Hello console! CommandContribution:", this); // crdebug("CRAS is:", this.cras); }, }); - // registry.registerCommand(CodeRibbonDevGetPanelCommand, { - // execute: () => { - // crdebug(); - // } - // }); + registry.registerCommand(CodeRibbonDebuggingCommands.testFuzzyFinderCommand, { + execute: () => { + crdebug(); + } + }); // === NOTE: Nav section diff --git a/coderibbon-theia/src/browser/coderibbon-theia-menus.ts b/coderibbon-theia/src/browser/coderibbon-theia-menus.ts index 55d9f7c..b5b5f54 100644 --- a/coderibbon-theia/src/browser/coderibbon-theia-menus.ts +++ b/coderibbon-theia/src/browser/coderibbon-theia-menus.ts @@ -10,7 +10,7 @@ import { } from "@theia/core/lib/common"; import { - CodeRibbonHelloWorldCommand, + CodeRibbonDebuggingCommands, CodeRibbonNavigationCommands, CodeRibbonManipulationCommands, CodeRibbonArrangementCommands, @@ -36,9 +36,11 @@ export class CodeRibbonTheiaMenuContribution implements MenuContribution { registerMenus(menus: MenuModelRegistry): void { menus.registerSubmenu(CodeRibbonTopMenuPath, "CodeRibbon"); + // General + menus.registerMenuAction(CodeRibbonTopMenuPath, { - commandId: CodeRibbonHelloWorldCommand.id, - label: "Say Hello", + commandId: CodeRibbonDebuggingCommands.testFuzzyFinderCommand.id, + label: "Test FuzzyFinder", }); // Navigation diff --git a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx index 8a62989..4449305 100644 --- a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx +++ b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx @@ -10,7 +10,7 @@ import { Command, CommandRegistry } from '@theia/core/lib/common/command'; @injectable() export class CodeRibbonFuzzyFileOpenerWidget extends ReactWidget { static readonly ID = "coderibbon:fuzzy-file-opener"; - static readonly LABEL = "Fuzzy File Finder"; + static readonly LABEL = "CodeRibbon Fuzzy File Finder"; @postConstruct() protected async init(): Promise { From f58a876254e07079112148892bf4305c11c7d822 Mon Sep 17 00:00:00 2001 From: Ben Klein Date: Fri, 23 Feb 2024 01:19:37 -0500 Subject: [PATCH 3/6] no async init for widget https://github.com/eclipse-theia/theia/blob/master/doc/Migration.md#inversify-60 --- coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx index 4449305..b2a2a84 100644 --- a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx +++ b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx @@ -13,7 +13,7 @@ export class CodeRibbonFuzzyFileOpenerWidget extends ReactWidget { static readonly LABEL = "CodeRibbon Fuzzy File Finder"; @postConstruct() - protected async init(): Promise { + protected init(): void { this.id = CodeRibbonFuzzyFileOpenerWidget.ID; this.title.label = CodeRibbonFuzzyFileOpenerWidget.LABEL; this.title.caption = CodeRibbonFuzzyFileOpenerWidget.LABEL; From 320fcfce0c35a4dc02648a80a73f91baf5200776 Mon Sep 17 00:00:00 2001 From: Ben Klein Date: Tue, 30 Sep 2025 02:13:12 -0400 Subject: [PATCH 4/6] basic test of widget factory works --- .../src/browser/coderibbon-theia-commands.ts | 19 +++++----- .../coderibbon-theia-frontend-module.ts | 33 ++++++++++++----- .../src/browser/coderibbon-theia-menus.ts | 5 ++- .../src/browser/cr-fuzzy-file-opener.tsx | 35 ++++++++++++------- coderibbon-theia/src/browser/cr-patch.ts | 3 ++ 5 files changed, 62 insertions(+), 33 deletions(-) diff --git a/coderibbon-theia/src/browser/coderibbon-theia-commands.ts b/coderibbon-theia/src/browser/coderibbon-theia-commands.ts index 26c5cfc..7e625aa 100644 --- a/coderibbon-theia/src/browser/coderibbon-theia-commands.ts +++ b/coderibbon-theia/src/browser/coderibbon-theia-commands.ts @@ -20,10 +20,10 @@ export const CodeRibbonDebuggingCommands = { id: "CodeRibbon.HelloWorld", label: "Hello, CodeRibbon.", }, - testFuzzyFinderCommand: { - id: "CodeRibbon.dev.test_ff", - label: "CodeRibbon Test FuzzyFinder", - }, + // testFuzzyFinderCommand: { + // id: "CodeRibbon.dev.test_ff", + // label: "CodeRibbon Test FuzzyFinder", + // }, }; export const CodeRibbonDevGetPanelCommand = { @@ -144,7 +144,6 @@ export class CodeRibbonTheiaCommandContribution implements CommandContribution { ) {} registerCommands(registry: CommandRegistry): void { - // === NOTE: Debugging section // TODO: only register these in debug mode @@ -155,11 +154,11 @@ export class CodeRibbonTheiaCommandContribution implements CommandContribution { // crdebug("CRAS is:", this.cras); }, }); - registry.registerCommand(CodeRibbonDebuggingCommands.testFuzzyFinderCommand, { - execute: () => { - crdebug(); - } - }); + // registry.registerCommand(CodeRibbonDebuggingCommands.testFuzzyFinderCommand, { + // execute: () => { + // crdebug(); + // } + // }); // === NOTE: Nav section diff --git a/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts b/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts index a57d91c..744f7f2 100644 --- a/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts +++ b/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts @@ -17,7 +17,11 @@ import { } from "@theia/core/lib/browser"; import { PreferenceContribution } from "@theia/core/lib/common/preferences"; import { ApplicationShell } from "@theia/core/lib/browser/shell/application-shell"; -import { bindViewContribution, FrontendApplicationContribution, WidgetFactory } from '@theia/core/lib/browser'; +import { + bindViewContribution, + FrontendApplicationContribution, + WidgetFactory, +} from "@theia/core/lib/browser"; // import { CodeRibbonTheiaRibbonViewContribution } from './coderibbon-theia-ribbon'; import { CodeRibbonTheiaCommandContribution } from "./coderibbon-theia-commands"; @@ -35,6 +39,7 @@ import { import "../../src/browser/style/ribbon.less"; // temp CSS import "../../src/browser/style/debug.less"; +import { crdebug } from "./cr-logger"; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(CodeRibbonApplicationShell).toSelf().inSingletonScope(); @@ -46,15 +51,25 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(MenuContribution).to(CodeRibbonTheiaMenuContribution); bind(KeybindingContribution).to(CodeRibbonTheiaKeybindingContribution); - bindViewContribution(bind, CodeRibbonFuzzyFileOpenerContribution); - bind(FrontendApplicationContribution).toService(CodeRibbonFuzzyFileOpenerContribution); + crdebug("now binding the CRFFO widget"); + + // CRFFO widget bind(CodeRibbonFuzzyFileOpenerWidget).toSelf(); - bind(WidgetFactory).toDynamicValue(ctx => ({ - id: CodeRibbonFuzzyFileOpenerWidget.ID, - createWidget: () => ctx.container.get( - CodeRibbonFuzzyFileOpenerWidget - ), - })).inSingletonScope(); + bind(WidgetFactory) + .toDynamicValue((ctx) => ({ + id: CodeRibbonFuzzyFileOpenerWidget.ID, + createWidget: () => + ctx.container.get( + CodeRibbonFuzzyFileOpenerWidget, + ), + })) + .inSingletonScope(); + + // opener + bindViewContribution(bind, CodeRibbonFuzzyFileOpenerContribution); + bind(FrontendApplicationContribution).toService( + CodeRibbonFuzzyFileOpenerContribution, + ); // TODO fix prefs // bind(PreferenceContribution).toConstantValue({ diff --git a/coderibbon-theia/src/browser/coderibbon-theia-menus.ts b/coderibbon-theia/src/browser/coderibbon-theia-menus.ts index 1abf067..ab37ebc 100644 --- a/coderibbon-theia/src/browser/coderibbon-theia-menus.ts +++ b/coderibbon-theia/src/browser/coderibbon-theia-menus.ts @@ -18,6 +18,8 @@ import { CodeRibbonArrangementCommands, } from "./coderibbon-theia-commands"; +import { TestOpenFFOCommand } from "./cr-fuzzy-file-opener"; + export const CodeRibbonTopMenuPath = [...MAIN_MENU_BAR, "7_coderibbon"]; export const CodeRibbonNavigationMenu = [ ...CodeRibbonTopMenuPath, @@ -41,7 +43,8 @@ export class CodeRibbonTheiaMenuContribution implements MenuContribution { // General menus.registerMenuAction(CodeRibbonTopMenuPath, { - commandId: CodeRibbonDebuggingCommands.testFuzzyFinderCommand.id, + // commandId: CodeRibbonDebuggingCommands.testFuzzyFinderCommand.id, + commandId: TestOpenFFOCommand.id, label: "Test FuzzyFinder", }); diff --git a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx index b2a2a84..03c37a0 100644 --- a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx +++ b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx @@ -1,11 +1,16 @@ -import * as React from 'react'; -import { injectable, postConstruct, inject } from '@theia/core/shared/inversify'; -import { AlertMessage } from '@theia/core/lib/browser/widgets/alert-message'; -import { ReactWidget } from '@theia/core/lib/browser/widgets/react-widget'; -import { MessageService } from '@theia/core'; -import { Message } from '@theia/core/lib/browser'; -import { AbstractViewContribution } from '@theia/core/lib/browser'; -import { Command, CommandRegistry } from '@theia/core/lib/common/command'; +import * as React from "react"; +import { + injectable, + postConstruct, + inject, +} from "@theia/core/shared/inversify"; +import { AlertMessage } from "@theia/core/lib/browser/widgets/alert-message"; +import { ReactWidget } from "@theia/core/lib/browser/widgets/react-widget"; +import { MessageService } from "@theia/core"; +import { Message } from "@theia/core/lib/browser"; +import { AbstractViewContribution } from "@theia/core/lib/browser"; +import { Command, CommandRegistry } from "@theia/core/lib/common/command"; +import { crdebug } from "./cr-logger"; @injectable() export class CodeRibbonFuzzyFileOpenerWidget extends ReactWidget { @@ -14,6 +19,7 @@ export class CodeRibbonFuzzyFileOpenerWidget extends ReactWidget { @postConstruct() protected init(): void { + crdebug("CRFFO postConstruct"); this.id = CodeRibbonFuzzyFileOpenerWidget.ID; this.title.label = CodeRibbonFuzzyFileOpenerWidget.LABEL; this.title.caption = CodeRibbonFuzzyFileOpenerWidget.LABEL; @@ -48,7 +54,7 @@ export class CodeRibbonFuzzyFileOpenerWidget extends ReactWidget { } } -export const TestOpenFFOCommand: Command = { id: 'coderibbon:test-ffo' }; +export const TestOpenFFOCommand: Command = { id: "coderibbon:test-ffo" }; @injectable() export class CodeRibbonFuzzyFileOpenerContribution extends AbstractViewContribution { @@ -56,14 +62,17 @@ export class CodeRibbonFuzzyFileOpenerContribution extends AbstractViewContribut super({ widgetId: CodeRibbonFuzzyFileOpenerWidget.ID, widgetName: CodeRibbonFuzzyFileOpenerWidget.LABEL, - defaultWidgetOptions: { area: 'main' }, - toggleCommandId: TestOpenFFOCommand.id + defaultWidgetOptions: { area: "main" }, + toggleCommandId: TestOpenFFOCommand.id, }); } - override registerCommands(commands: CommandRegistry): void { + registerCommands(commands: CommandRegistry): void { commands.registerCommand(TestOpenFFOCommand, { - execute: () => super.openView({ activate: false, reveal: true }) + execute: () => { + crdebug("command: TestOpenFFOCommand executes"); + super.openView({ activate: false, reveal: true }); + }, }); } diff --git a/coderibbon-theia/src/browser/cr-patch.ts b/coderibbon-theia/src/browser/cr-patch.ts index 4f6ecd6..4e9c255 100644 --- a/coderibbon-theia/src/browser/cr-patch.ts +++ b/coderibbon-theia/src/browser/cr-patch.ts @@ -141,6 +141,9 @@ export class CodeRibbonTheiaPatch extends TabPanel { crdebug("Patch activate", this); if (this.contentful_widget) { this.contentful_widget.activate(); + } else { + // if we have no content, we should display something that can hold focus + // TODO eventually this should be replaced with the FuzzyFinder feature } // TODO find a better place to trigger this From 680001970ade4cf0e1ad38d9e7e4e3d112c86815 Mon Sep 17 00:00:00 2001 From: Ben Klein Date: Sun, 5 Oct 2025 05:55:49 -0400 Subject: [PATCH 5/6] rework DI to support use of WidgetManager in patch also some other minor moves and cleanup now that I understand the theia inversify container --- .../coderibbon-theia-frontend-module.ts | 32 +++++++++--- .../src/browser/cr-application-shell.ts | 31 +++++++++--- .../src/browser/cr-fuzzy-file-opener.tsx | 49 ++++++++++++++++--- coderibbon-theia/src/browser/cr-interfaces.ts | 8 --- coderibbon-theia/src/browser/cr-patch.ts | 20 ++++++-- coderibbon-theia/src/browser/cr-ribbon.ts | 48 ++++++++++++++---- 6 files changed, 145 insertions(+), 43 deletions(-) diff --git a/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts b/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts index 744f7f2..3639cc8 100644 --- a/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts +++ b/coderibbon-theia/src/browser/coderibbon-theia-frontend-module.ts @@ -9,18 +9,17 @@ import { CommandContribution, MenuModelRegistry, MessageService, + CorePreferences, } from "@theia/core/lib/common"; -import { - // FrontendApplicationContribution, - // bindViewContribution, WidgetFactory, - KeybindingContribution, -} from "@theia/core/lib/browser"; import { PreferenceContribution } from "@theia/core/lib/common/preferences"; import { ApplicationShell } from "@theia/core/lib/browser/shell/application-shell"; import { bindViewContribution, + DockPanel, FrontendApplicationContribution, + KeybindingContribution, WidgetFactory, + WidgetManager, } from "@theia/core/lib/browser"; // import { CodeRibbonTheiaRibbonViewContribution } from './coderibbon-theia-ribbon'; @@ -28,8 +27,9 @@ import { CodeRibbonTheiaCommandContribution } from "./coderibbon-theia-commands" import { CodeRibbonTheiaMenuContribution } from "./coderibbon-theia-menus"; import { CodeRibbonTheiaPreferenceSchema } from "./coderibbon-theia-preferences"; import { CodeRibbonTheiaManager } from "./coderibbon-theia-manager"; -import { CodeRibbonTheiaRibbonPanel } from "./cr-ribbon"; import { CodeRibbonApplicationShell } from "./cr-application-shell"; +import { CodeRibbonTheiaRibbonPanel } from "./cr-ribbon"; +import { CodeRibbonTheiaPatch } from "./cr-patch"; import { CodeRibbonTheiaKeybindingContribution } from "./coderibbon-theia-keybinds"; import { CodeRibbonFuzzyFileOpenerWidget, @@ -40,6 +40,7 @@ import "../../src/browser/style/ribbon.less"; // temp CSS import "../../src/browser/style/debug.less"; import { crdebug } from "./cr-logger"; +import { TheiaDockPanel } from "@theia/core/lib/browser/shell/theia-dock-panel"; export default new ContainerModule((bind, unbind, isBound, rebind) => { bind(CodeRibbonApplicationShell).toSelf().inSingletonScope(); @@ -47,12 +48,27 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => { // @ts-ignore rebind(ApplicationShell).to(CodeRibbonApplicationShell).inSingletonScope(); + // this is how we sneak the inversify container into the non-injectable CR classes + // (instead of doing the cheap/unstable window.theia.container workaround) + bind(CodeRibbonTheiaRibbonPanel.Factory).toFactory( + ({ container }) => + (options: Partial) => { + return new CodeRibbonTheiaRibbonPanel({ + alignment: "start", + direction: "left-to-right", + spacing: 0, + mode: "multiple-document", + container, + ...options, + }); + }, + ); + bind(CommandContribution).to(CodeRibbonTheiaCommandContribution); bind(MenuContribution).to(CodeRibbonTheiaMenuContribution); bind(KeybindingContribution).to(CodeRibbonTheiaKeybindingContribution); - crdebug("now binding the CRFFO widget"); - + // crdebug("now binding the CRFFO widget"); // CRFFO widget bind(CodeRibbonFuzzyFileOpenerWidget).toSelf(); bind(WidgetFactory) diff --git a/coderibbon-theia/src/browser/cr-application-shell.ts b/coderibbon-theia/src/browser/cr-application-shell.ts index 22e86c2..8a9fcaf 100644 --- a/coderibbon-theia/src/browser/cr-application-shell.ts +++ b/coderibbon-theia/src/browser/cr-application-shell.ts @@ -24,7 +24,7 @@ import { FrontendApplicationStateService } from "@theia/core/lib/browser/fronten import { CorePreferences } from "@theia/core/lib/common/core-preferences"; import { TheiaDockPanel, - BOTTOM_AREA_ID, + // BOTTOM_AREA_ID, MAIN_AREA_ID, // MAXIMIZED_CLASS, } from "@theia/core/lib/browser/shell/theia-dock-panel"; @@ -56,10 +56,25 @@ export class CodeRibbonApplicationShell extends ApplicationShell { // @ts-expect-error TS2416: Property in type is not assignable to the same property in base type override mainPanel: CodeRibbonTheiaRibbonPanel; + // ApplicationShell only supplies the DockPanel renderer + // other constructor requirements are in the factory itself + @inject(CodeRibbonTheiaRibbonPanel.Factory) + protected readonly ribbonPanelFactory: ({ + renderer, + }: { + renderer: DockPanelRenderer; + }) => CodeRibbonTheiaRibbonPanel; + /** * Create the dock panel in the main shell area. * * Override the default from using TheiaDockPanel to CodeRibbonTheiaRibbonPanel + * this is a modification of: + * https://github.com/eclipse-theia/theia/blob/008c8340465f7e42298839881d814863bef0b039/packages/core/src/browser/shell/application-shell.ts#L579-L591 + * expect to have to update this if the upstream behavior changes + * + * Q: Why not just override the dockPanelFactory? + * A: because it's also used to create the bottom panel & it's not indicated at construction which it will be */ // @ts-expect-error TS2416: Property in type is not assignable to the same property in base type override createMainPanel(): CodeRibbonTheiaRibbonPanel { @@ -91,11 +106,15 @@ export class CodeRibbonApplicationShell extends ApplicationShell { // }, this.corePreferences); // what I want, based on BoxPanel - const ribbonPanel = new CodeRibbonTheiaRibbonPanel({ - alignment: "start", - direction: "left-to-right", - spacing: 0, - mode: "multiple-document", + // const ribbonPanel = new CodeRibbonTheiaRibbonPanel({ + // alignment: "start", + // direction: "left-to-right", + // spacing: 0, + // mode: "multiple-document", + // renderer, + // // widgetManager: this.widgetManager, + // }); + const ribbonPanel = this.ribbonPanelFactory({ renderer, }); diff --git a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx index 03c37a0..9526494 100644 --- a/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx +++ b/coderibbon-theia/src/browser/cr-fuzzy-file-opener.tsx @@ -7,16 +7,39 @@ import { import { AlertMessage } from "@theia/core/lib/browser/widgets/alert-message"; import { ReactWidget } from "@theia/core/lib/browser/widgets/react-widget"; import { MessageService } from "@theia/core"; -import { Message } from "@theia/core/lib/browser"; +import { Message, QuickInputService, InputBox } from "@theia/core/lib/browser"; import { AbstractViewContribution } from "@theia/core/lib/browser"; import { Command, CommandRegistry } from "@theia/core/lib/common/command"; import { crdebug } from "./cr-logger"; +import { QuickFileSelectService } from "@theia/file-search/lib/browser/quick-file-select-service"; +import { MonacoQuickInputService } from "@theia/monaco/lib/browser/monaco-quick-input-service"; + +/** + * since this is registered in theia's WidgetManager / WidgetFactory it is part of the inversify container (hence injectable) + */ @injectable() export class CodeRibbonFuzzyFileOpenerWidget extends ReactWidget { static readonly ID = "coderibbon:fuzzy-file-opener"; static readonly LABEL = "CodeRibbon Fuzzy File Finder"; + // protected current_search?: string = undefined; + // TODO this should be the monaco input / quick open equivalent + // protected inputElement?: HTMLInputElement; + protected inputElementRef: React.RefObject; + + @inject(QuickInputService) + protected readonly quickInputService: QuickInputService; + + @inject(QuickFileSelectService) + protected readonly quickFileSelectService: QuickFileSelectService; + + @inject(MonacoQuickInputService) + protected readonly monacoQuickInputService: MonacoQuickInputService; + + @inject(MessageService) + protected readonly messageService!: MessageService; + @postConstruct() protected init(): void { crdebug("CRFFO postConstruct"); @@ -25,28 +48,40 @@ export class CodeRibbonFuzzyFileOpenerWidget extends ReactWidget { this.title.caption = CodeRibbonFuzzyFileOpenerWidget.LABEL; this.title.closable = true; this.title.iconClass = "fa fa-window-maximize"; // example widget icon. + + this.inputElementRef = React.createRef(); + // TODO how? + // this.inputBox = this.quickInputService.createInputBox(); this.update(); } + activate(): void { + this.inputElementRef.current?.focus(); + } + render(): React.ReactElement { - const header = `This is a sample widget which simply calls the messageService in order to display an info message to end users.`; + const header = `Does not work yet. Eventually this should be the same UI as Ctrl-P.`; + const show_fuzzy_search: boolean = true; return (
- + */} +
); } - @inject(MessageService) - protected readonly messageService!: MessageService; - protected displayMessage(): void { this.messageService.info( "Congratulations: My Widget Successfully Created!", diff --git a/coderibbon-theia/src/browser/cr-interfaces.ts b/coderibbon-theia/src/browser/cr-interfaces.ts index c333b34..fac2858 100644 --- a/coderibbon-theia/src/browser/cr-interfaces.ts +++ b/coderibbon-theia/src/browser/cr-interfaces.ts @@ -66,14 +66,6 @@ export namespace RibbonPanel { * areas, and those areas can be individually resized by the user. */ | "multiple-document"; - export interface IOptions { - direction?: BoxLayout.Direction; // only horizontal - alignment?: BoxLayout.Alignment; // only ... - spacing?: number; - layout?: CodeRibbonTheiaRibbonLayout; - mode: RibbonPanel.Mode; - renderer?: DockLayout.IRenderer; - } export interface ILayoutConfig { // TODO actual definition of serializable layout config // NOTE probably a sequence of RibbonStrip.ILayoutConfig ??? diff --git a/coderibbon-theia/src/browser/cr-patch.ts b/coderibbon-theia/src/browser/cr-patch.ts index 4e9c255..e5ba89e 100644 --- a/coderibbon-theia/src/browser/cr-patch.ts +++ b/coderibbon-theia/src/browser/cr-patch.ts @@ -1,7 +1,5 @@ /** @format */ -// import { injectable, inject, postConstruct } from '@theia/core/shared/inversify'; - import { TabBar, Widget, @@ -20,9 +18,11 @@ import { Drag } from "@lumino/dragdrop"; import { MimeData } from "@lumino/coreutils"; import { ElementExt } from "@lumino/domutils"; import { IDisposable } from "@lumino/disposable"; +import { MessageLoop } from "@lumino/messaging"; +import { WidgetManager } from "@theia/core/lib/browser"; import { crdebug } from "./cr-logger"; -import { MessageLoop } from "@lumino/messaging"; +import { CodeRibbonFuzzyFileOpenerWidget } from "./cr-fuzzy-file-opener"; export class CodeRibbonTheiaPatch extends TabPanel { private _renderer?: DockLayout.IRenderer; @@ -143,7 +143,19 @@ export class CodeRibbonTheiaPatch extends TabPanel { this.contentful_widget.activate(); } else { // if we have no content, we should display something that can hold focus - // TODO eventually this should be replaced with the FuzzyFinder feature + // if (!this._cr_ffo_widget) { + // // make a new CRFFO to use + // this._cr_ffo_widget = ; + // } + // this.addWidget(this._cr_ffo_widget); + // if (!this.widgetManager) { + // console.warn("CR: patch: no widgetmanager got injected!"); + // } else { + // // this.widgetManager.getOrCreateWidget(CodeRibbonFuzzyFileOpenerWidget.ID).then((w) => { + // // this.addWidget(w); + // // }); + // } + // TODO getOrCreateWidget for the CRFFO instance from the ribbon } // TODO find a better place to trigger this diff --git a/coderibbon-theia/src/browser/cr-ribbon.ts b/coderibbon-theia/src/browser/cr-ribbon.ts index eb40624..0c56781 100644 --- a/coderibbon-theia/src/browser/cr-ribbon.ts +++ b/coderibbon-theia/src/browser/cr-ribbon.ts @@ -4,6 +4,7 @@ import { injectable, inject, postConstruct, + interfaces as InversifyInterfaces, } from "@theia/core/shared/inversify"; import { Signal } from "@lumino/signaling"; @@ -64,7 +65,13 @@ const VISIBLE_MENU_MAXIMIZED_CLASS = "theia-visible-menu-maximized"; // based primarily on TheiaDockPanel implementation, since that's what it replaces // as such, license here falls to // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 -// @injectable() +/** + * Why is this *not* injectable? A: + * This mimicks the structure and relationship of Theia's `ApplicationShell`(injectable) + * and the `DockPanel`, where DockPanel is constructed in the conventional way. + * + * In order to pick out access to bound services and whatnot, we just keep ._container + */ export class CodeRibbonTheiaRibbonPanel extends BoxPanel implements EventListenerObject @@ -90,6 +97,7 @@ export class CodeRibbonTheiaRibbonPanel private _edges: CodeRibbonTheiaRibbonPanel.IEdges; private _renderer?: DockLayout.IRenderer; + protected _container: InversifyInterfaces.Container; // theia's inversify container private _mode: RibbonPanel.Mode; // robobenklein: copied in as response to error: @@ -115,12 +123,7 @@ export class CodeRibbonTheiaRibbonPanel // drag-drop overlay: readonly overlay: DockPanel.IOverlay; - constructor( - options?: RibbonPanel.IOptions, - @inject(CorePreferences) protected readonly preferences?: CorePreferences, - // TODO: why isn't this getting injected in? - @inject(MessageService) private readonly messageService?: MessageService, - ) { + constructor(options: CodeRibbonTheiaRibbonPanel.IOptions) { // @ts-expect-error TS2322: Type 'CodeRibbonTheiaRibbonLayout' is not assignable to type 'BoxLayout'. super({ layout: Private.createLayout(options) }); // Replaced super call with super.super, @@ -157,6 +160,7 @@ export class CodeRibbonTheiaRibbonPanel this._renderer = options?.renderer; crdebug("Ribbon: renderer", this._renderer); this._mode = options?.mode || "multiple-document"; + this._container = options.container; // this.autoAdjustRibbonTailLength(); } @@ -755,7 +759,7 @@ export class CodeRibbonTheiaRibbonPanel }) .catch((e) => { crdebug("scrollStripIntoView fail reason:", e); - throw Error("Failed to scrollStripIntoView"); + throw e; }); } else if (widget instanceof CodeRibbonTheiaRibbonStrip) { strip = widget; @@ -766,7 +770,7 @@ export class CodeRibbonTheiaRibbonPanel }) .catch((e) => { crdebug("scrollStripIntoView fail reason:", e); - throw Error("Failed to scrollStripIntoView"); + throw e; }); } else { let w_parent = widget.parent; @@ -1276,6 +1280,16 @@ export class CodeRibbonTheiaRibbonPanel // } } + // NOTE === Theia's inversify container usage section === NOTE // + + get messageService(): MessageService { + return this._container.get(MessageService); + } + + get preferences(): CorePreferences { + return this._container.get(CorePreferences); + } + // NOTE === theia DockPanel API compatility section === NOTE // isElectron(): boolean { @@ -1476,9 +1490,23 @@ export class CodeRibbonTheiaRibbonPanel } export namespace CodeRibbonTheiaRibbonPanel { + export const Factory = Symbol("CodeRibbonTheiaRibbonPanel#Factory"); + export interface Factory { + (options: IOptions): CodeRibbonTheiaRibbonPanel; + } + export interface IInitOptions { // shell: ApplicationShell ; } + export interface IOptions { + direction?: BoxLayout.Direction; // only horizontal + alignment?: BoxLayout.Alignment; // only ... + spacing?: number; + layout?: CodeRibbonTheiaRibbonLayout; + mode: RibbonPanel.Mode; + renderer?: DockLayout.IRenderer; + container: InversifyInterfaces.Container; // theia's inversify container + } export interface IRibbonLayoutConfig { type: "ribbon-area"; // compatibility for dock* @@ -1521,7 +1549,7 @@ export namespace CodeRibbonTheiaRibbonPanel { namespace Private { export function createLayout( - options: RibbonPanel.IOptions, + options: CodeRibbonTheiaRibbonPanel.IOptions, ): CodeRibbonTheiaRibbonLayout { return options.layout || new CodeRibbonTheiaRibbonLayout(options); } From 285c0b2be9b75c0c7ad88ce25eeee38508653cdd Mon Sep 17 00:00:00 2001 From: Ben Klein Date: Sun, 5 Oct 2025 18:16:29 -0400 Subject: [PATCH 6/6] CRFFO widget now moves around with focus! --- coderibbon-theia/src/browser/cr-patch.ts | 35 ++++++++++++++----- .../src/browser/cr-ribbon-strip.ts | 14 ++++---- coderibbon-theia/src/browser/cr-ribbon.ts | 5 ++- 3 files changed, 39 insertions(+), 15 deletions(-) diff --git a/coderibbon-theia/src/browser/cr-patch.ts b/coderibbon-theia/src/browser/cr-patch.ts index e5ba89e..ef20532 100644 --- a/coderibbon-theia/src/browser/cr-patch.ts +++ b/coderibbon-theia/src/browser/cr-patch.ts @@ -1,5 +1,6 @@ /** @format */ +import { interfaces as InversifyInterfaces } from "@theia/core/shared/inversify"; import { TabBar, Widget, @@ -23,23 +24,21 @@ import { WidgetManager } from "@theia/core/lib/browser"; import { crdebug } from "./cr-logger"; import { CodeRibbonFuzzyFileOpenerWidget } from "./cr-fuzzy-file-opener"; +import { option } from "yargs"; export class CodeRibbonTheiaPatch extends TabPanel { - private _renderer?: DockLayout.IRenderer; + private _renderer: DockLayout.IRenderer; readonly tabBar: TabBar; private _tabBarMutationObserver?: MutationObserver; + private _container: InversifyInterfaces.Container; - constructor(options: CodeRibbonTheiaPatch.IOptions = {}) { + constructor(options: CodeRibbonTheiaPatch.IOptions) { super(); this.addClass("cr-RibbonPatch"); crdebug("Patch constructor", this); this._renderer = options.renderer; - - if (!this._renderer) { - crdebug("WARN: Patch: I didn't get the renderer!", this._renderer); - throw "expected to have the renderer!"; - } + this._container = options.container; // crdebug("makin that new tabBar!", this); this.tabBar.dispose(); @@ -156,6 +155,21 @@ export class CodeRibbonTheiaPatch extends TabPanel { // // }); // } // TODO getOrCreateWidget for the CRFFO instance from the ribbon + this.widgetManager + .getOrCreateWidget( + CodeRibbonFuzzyFileOpenerWidget.ID, + ) + .then((w) => { + this.addWidget(w); + w.activate(); + }) + .catch((reason) => { + console.error( + "CR: failed to add a CRFFO to this patch!", + this, + reason, + ); + }); } // TODO find a better place to trigger this @@ -176,6 +190,10 @@ export class CodeRibbonTheiaPatch extends TabPanel { } } + get widgetManager(): WidgetManager { + return this._container.get(WidgetManager); + } + saveLayout(): CodeRibbonTheiaPatch.ILayoutConfig { crdebug("Patch saveLayout"); return { @@ -198,7 +216,8 @@ export class CodeRibbonTheiaPatch extends TabPanel { export namespace CodeRibbonTheiaPatch { export interface IOptions { - renderer?: DockLayout.IRenderer; + renderer: DockLayout.IRenderer; + container: InversifyInterfaces.Container; } export interface IInitOptions { diff --git a/coderibbon-theia/src/browser/cr-ribbon-strip.ts b/coderibbon-theia/src/browser/cr-ribbon-strip.ts index 43fc3de..61438d8 100644 --- a/coderibbon-theia/src/browser/cr-ribbon-strip.ts +++ b/coderibbon-theia/src/browser/cr-ribbon-strip.ts @@ -4,6 +4,7 @@ import { injectable, inject, postConstruct, + interfaces as InversifyInterfaces, } from "@theia/core/shared/inversify"; import { Signal } from "@lumino/signaling"; @@ -44,7 +45,6 @@ import { ImprovedBoxLayout } from "./improvedboxlayout"; // based primarily on TheiaDockPanel implementation, since that's what it replaces // as such, license here falls to // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 -@injectable() export class CodeRibbonTheiaRibbonStrip extends ImprovedBoxPanel { /** * Emitted when a widget is added to the panel. @@ -65,13 +65,12 @@ export class CodeRibbonTheiaRibbonStrip extends ImprovedBoxPanel { protected readonly tracker = new FocusTracker(); // readonly patchAdded = new Signal(this); + protected _container: InversifyInterfaces.Container; + // protected readonly onDidToggleMaximizedEmitter = new Emitter(); // readonly onDidToggleMaximized = this.onDidToggleMaximizedEmitter.event; - constructor( - options?: CodeRibbonTheiaRibbonStrip.IOptions, - @inject(CorePreferences) protected readonly preferences?: CorePreferences, - ) { + constructor(options: CodeRibbonTheiaRibbonStrip.IOptions) { super({ alignment: "start", direction: "top-to-bottom", @@ -79,6 +78,7 @@ export class CodeRibbonTheiaRibbonStrip extends ImprovedBoxPanel { }); this.addClass("cr-RibbonStrip"); crdebug("RibbonStrip constructor:", this, options); + this._container = options.container; } cr_init(options?: CodeRibbonTheiaRibbonStrip.IInitOptions) { @@ -129,6 +129,7 @@ export class CodeRibbonTheiaRibbonStrip extends ImprovedBoxPanel { let new_patch = new CodeRibbonTheiaPatch({ ...options, renderer: this._renderer as DockPanel.IRenderer, + container: this._container, }); super.addWidget(new_patch); if (args.index != undefined) { @@ -414,7 +415,8 @@ export class CodeRibbonTheiaRibbonStrip extends ImprovedBoxPanel { export namespace CodeRibbonTheiaRibbonStrip { export interface IOptions { quota?: number; - renderer?: DockLayout.IRenderer; + renderer: DockLayout.IRenderer; + container: InversifyInterfaces.Container; } export interface ICreatePatchArgs { diff --git a/coderibbon-theia/src/browser/cr-ribbon.ts b/coderibbon-theia/src/browser/cr-ribbon.ts index 0c56781..28e5507 100644 --- a/coderibbon-theia/src/browser/cr-ribbon.ts +++ b/coderibbon-theia/src/browser/cr-ribbon.ts @@ -96,6 +96,8 @@ export class CodeRibbonTheiaRibbonPanel private _pressData: Private.IPressData | null = null; private _edges: CodeRibbonTheiaRibbonPanel.IEdges; + // TODO if _renderer is undefined, display some human-readable error state instead of crashing the entire shell + // perhaps "Incompatible Theia Modifications Detected: no DockLayout Renderer was available"? private _renderer?: DockLayout.IRenderer; protected _container: InversifyInterfaces.Container; // theia's inversify container private _mode: RibbonPanel.Mode; @@ -820,7 +822,8 @@ export class CodeRibbonTheiaRibbonPanel let new_strip; new_strip = new CodeRibbonTheiaRibbonStrip({ ...options, - renderer: this._renderer, + renderer: this._renderer!, + container: this._container, }); if (index === undefined) { // append to ribbon