-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] [editor] Renaming a file should rename a corresponding editor #582
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -56,7 +56,8 @@ export class WidgetManager { | |
|
||
constructor( | ||
@inject(ContributionProvider) @named(WidgetFactory) protected readonly factoryProvider: ContributionProvider<WidgetFactory>, | ||
@inject(ILogger) protected logger: ILogger) { | ||
@inject(ILogger) protected logger: ILogger | ||
) { | ||
} | ||
|
||
getWidgets(factoryId: string): Widget[] { | ||
|
@@ -69,6 +70,10 @@ export class WidgetManager { | |
return result; | ||
} | ||
|
||
getWidgetsMap(): Map<string, Widget> { | ||
return this.widgets; | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this is wrong you should not expose the internal map |
||
/* | ||
* creates or returns the widget for the given description. | ||
*/ | ||
|
@@ -105,7 +110,7 @@ export class WidgetManager { | |
return undefined; | ||
} | ||
|
||
protected toKey(options: WidgetConstructionOptions) { | ||
public toKey(options: WidgetConstructionOptions) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should not need this public either |
||
return JSON.stringify(options); | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ import { EditorCommandHandlers } from "./editor-command"; | |
import { EditorKeybindingContribution, EditorKeybindingContext } from "./editor-keybinding"; | ||
import { bindEditorPreferences } from './editor-preferences'; | ||
import { WidgetFactory } from '@theia/core/lib/browser/widget-manager'; | ||
// import { WorkspaceCommandContribution } from '@theia/workspace/lib/browser/workspace-commands'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove |
||
|
||
export default new ContainerModule(bind => { | ||
bindEditorPreferences(bind); | ||
|
@@ -30,4 +31,5 @@ export default new ContainerModule(bind => { | |
bind(EditorKeybindingContext).toSelf().inSingletonScope(); | ||
bind(KeybindingContext).toDynamicValue(context => context.container.get(EditorKeybindingContext)); | ||
bind(KeybindingContribution).to(EditorKeybindingContribution); | ||
// bind(WorkspaceCommandContribution).toSelf().inSingletonScope(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove |
||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,10 +14,12 @@ import { TextEditorProvider, Range, Position } from "./editor"; | |
import { WidgetFactory, WidgetManager } from '@theia/core/lib/browser/widget-manager'; | ||
import { Widget } from '@phosphor/widgets'; | ||
import { FileIconProvider } from '@theia/filesystem/lib/browser/icons/file-icons'; | ||
import { WorkspaceCommandContribution } from "@theia/workspace/lib/browser/workspace-commands"; | ||
|
||
export const EditorManager = Symbol("EditorManager"); | ||
|
||
export interface EditorManager extends OpenHandler { | ||
|
||
/** | ||
* All opened editors. | ||
*/ | ||
|
@@ -27,6 +29,7 @@ export interface EditorManager extends OpenHandler { | |
* Reject if the given input is not an editor input or an editor cannot be opened. | ||
*/ | ||
open(uri: URI, input?: EditorInput): Promise<EditorWidget>; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove |
||
/** | ||
* The most recently focused editor. | ||
*/ | ||
|
@@ -64,10 +67,14 @@ export class EditorManagerImpl implements EditorManager, WidgetFactory { | |
@inject(SelectionService) protected readonly selectionService: SelectionService, | ||
@inject(FrontendApplication) protected readonly app: FrontendApplication, | ||
@inject(WidgetManager) protected readonly widgetManager: WidgetManager, | ||
@inject(FileIconProvider) protected readonly iconProvider: FileIconProvider | ||
@inject(FileIconProvider) protected readonly iconProvider: FileIconProvider, | ||
@inject(WorkspaceCommandContribution) protected readonly uiEvent: WorkspaceCommandContribution | ||
) { | ||
this.currentObserver = new EditorManagerImpl.Observer('current', app); | ||
this.activeObserver = new EditorManagerImpl.Observer('active', app); | ||
uiEvent.onIUriChanged(event => { | ||
this.renameWidget(event.factoryId.toString(), event.options, event.newOptions, event.newLabel); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should get the FileMovedEvent from FS here. |
||
}); | ||
} | ||
|
||
get editors() { | ||
|
@@ -105,6 +112,28 @@ export class EditorManagerImpl implements EditorManager, WidgetFactory { | |
}); | ||
} | ||
|
||
/* | ||
* check if a widget exists already and rename it | ||
*/ | ||
renameWidget(factoryId: string, options: any, newOptions: any, newLabel: any): void { | ||
let key = this.widgetManager.toKey({ factoryId, options }); | ||
let existingWidget = this.widgetManager.getWidgetsMap().get(key); | ||
if (existingWidget) { | ||
existingWidget.title.label = newLabel; | ||
this.updateWidgetKey(key, existingWidget, factoryId, options, newOptions); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is just externalizing your original implementation into the widget manager. |
||
} | ||
|
||
/* | ||
* update a widget key following a rename | ||
*/ | ||
private updateWidgetKey(key: any, widget: Widget, factoryId: string, options: any, newOptions: any): void { | ||
this.widgetManager.getWidgetsMap().delete(key); | ||
options = newOptions; | ||
key = this.widgetManager.toKey({ factoryId, options }); | ||
this.widgetManager.getWidgetsMap().set(key, widget); | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto... this one makes sense in the widget-manager |
||
|
||
// don't call directly, but use WidgetManager | ||
createWidget(uriAsString: string): Promise<Widget> { | ||
const uri = new URI(uriAsString); | ||
|
@@ -155,7 +184,6 @@ export class EditorManagerImpl implements EditorManager, WidgetFactory { | |
} | ||
return undefined; | ||
} | ||
|
||
} | ||
|
||
export namespace EditorManagerImpl { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ import { UriSelection } from '@theia/filesystem/lib/common/filesystem-selection' | |
import { SingleTextInputDialog, ConfirmDialog } from "@theia/core/lib/browser/dialogs"; | ||
import { OpenerService, OpenHandler, open } from "@theia/core/lib/browser"; | ||
import { WorkspaceService } from './workspace-service'; | ||
import { Emitter, Event } from '@theia/core/lib/common/event'; | ||
|
||
export namespace WorkspaceCommands { | ||
export const NEW_FILE = 'file:newFile'; | ||
|
@@ -39,6 +40,22 @@ export namespace FileMenus { | |
export const OPEN_GROUP = [...FILE, '2_open']; | ||
} | ||
|
||
// @injectable() | ||
// export class UiEvent { | ||
// public onIUriChangedEmitter = new Emitter<IUriChangedEvent>(); | ||
|
||
// get onIUriChanged(): Event<IUriChangedEvent> { | ||
// return this.onIUriChangedEmitter.event; | ||
// } | ||
// } | ||
|
||
export interface IUriChangedEvent { | ||
factoryId: string; | ||
options: any; | ||
newOptions: any; | ||
newLabel: any; | ||
} | ||
|
||
@injectable() | ||
export class FileMenuContribution implements MenuContribution { | ||
|
||
|
@@ -64,6 +81,15 @@ export class WorkspaceCommandContribution implements CommandContribution { | |
@inject(OpenerService) protected readonly openerService: OpenerService | ||
) { } | ||
|
||
|
||
|
||
|
||
protected readonly onIUriChangedEmitter = new Emitter<IUriChangedEvent>(); | ||
|
||
get onIUriChanged(): Event<IUriChangedEvent> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should move this to FileSytem and call it FileMovedEvent and it should be about what moved. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once the EditorManager receives it it can check if it has a editor for that URI and update it. |
||
return this.onIUriChangedEmitter.event; | ||
} | ||
|
||
registerCommands(registry: CommandRegistry): void { | ||
registry.registerCommand({ | ||
id: WorkspaceCommands.NEW_FILE, | ||
|
@@ -104,9 +130,17 @@ export class WorkspaceCommandContribution implements CommandContribution { | |
initialValue: uri.path.base, | ||
validate: name => this.validateFileName(name, parent) | ||
}); | ||
dialog.open().then(name => | ||
this.fileSystem.move(uri.toString(), uri.parent.resolve(name).toString()) | ||
); | ||
dialog.open().then(name => { | ||
this.fileSystem.move(uri.toString(), uri.parent.resolve(name).toString()).then(() => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think @svenefftinge meant that you only should close an existing editor and open new: const newUri = uri.parent.resolve(name);
this.fileSystem.move(uri.toString(), newUri.toString()).then(() => {
const widget = this.editorService.getEditor(uri);
if (editor) {
const selection = widget.editor.selection;
widget.close();
open(this.openerService, newUri, {selection});
}
}); Ideally, it should be done not here but on move/rename events from the filesystem in the editor extension. Making it here:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, I did not mean that. A real rename seems better to me in general. But we have to take care of all the loose ends. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe we should have an generic key to reference the widget directly to avoid lose ends ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you elaborate on what you mean? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean that if something has to keep a reference to an editor widget it should reference the widget object directly or we should provide a key that is not bound to any resource to refer to it. Like some abstract id. So the URI would be used as key to open a new widget or find a widget for that URI but once you get a reference to it, it should be the id or the object directly, a bit like files names and files descriptors.. you open(filename) get a file descriptor so you could getWidgetForURI() get a widget for a URI but use getWidgetId() if you already had a widget and now know only it's id. I don't see where however that would be useful.. just keeping the object seems ok since serializing that doesn't make sense as the widget-manager is in the frontend.
Why would it try to reload with an old uri ? if you updated it before you serialized the widget ? Granted if it's changed between save and restore there's nothing we can do but that is true whatever we do... |
||
let event: IUriChangedEvent = { | ||
factoryId: "code-editor-opener", | ||
options: uri.parent.resolve(uri.path.base).toString(), | ||
newOptions: uri.parent.resolve(name).toString(), | ||
newLabel: name | ||
}; | ||
this.onIUriChangedEmitter.fire(event); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually thinking more about this it would make more sense to have FileSytem.move() fire this event and have the event emitter there. |
||
}); | ||
}); | ||
}) | ||
})); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -36,8 +36,11 @@ export default new ContainerModule((bind: interfaces.Bind, unbind: interfaces.Un | |
(props: FileDialogProps) => | ||
createFileDialog(ctx.container, props) | ||
); | ||
bind(WorkspaceCommandContribution).toSelf().inSingletonScope(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should not be there |
||
bind(CommandContribution).to(WorkspaceCommandContribution).inSingletonScope(); | ||
bind(MenuContribution).to(FileMenuContribution).inSingletonScope(); | ||
|
||
|
||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. remove |
||
rebind(StorageService).to(WorkspaceStorageService).inSingletonScope(); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no need to change this