Skip to content

Commit f712ec9

Browse files
Akos Kittakittaakos
Akos Kitta
authored andcommitted
ATL-1106: Made all non-workspace editors read-only.
Signed-off-by: Akos Kitta <[email protected]>
1 parent c75b954 commit f712ec9

File tree

5 files changed

+133
-46
lines changed

5 files changed

+133
-46
lines changed

Diff for: arduino-ide-extension/src/browser/arduino-ide-frontend-module.ts

+5-6
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ import { ListItemRenderer } from './widgets/component-list/list-item-renderer';
6767
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
6868
import { MonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
6969
import { ArduinoDaemonPath, ArduinoDaemon } from '../common/protocol/arduino-daemon';
70-
import { EditorManager as TheiaEditorManager, EditorCommandContribution as TheiaEditorCommandContribution } from '@theia/editor/lib/browser';
71-
import { EditorManager } from './theia/editor/editor-manager';
70+
import { EditorCommandContribution as TheiaEditorCommandContribution } from '@theia/editor/lib/browser';
7271
import { FrontendConnectionStatusService, ApplicationConnectionStatusContribution } from './theia/core/connection-status-service';
7372
import {
7473
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
@@ -153,6 +152,8 @@ import { SearchInWorkspaceWidget as TheiaSearchInWorkspaceWidget } from '@theia/
153152
import { SearchInWorkspaceWidget } from './theia/search-in-workspace/search-in-workspace-widget';
154153
import { SearchInWorkspaceResultTreeWidget as TheiaSearchInWorkspaceResultTreeWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-result-tree-widget';
155154
import { SearchInWorkspaceResultTreeWidget } from './theia/search-in-workspace/search-in-workspace-result-tree-widget';
155+
import { MonacoEditorProvider } from './theia/monaco/monaco-editor-provider';
156+
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
156157

157158
const ElementQueries = require('css-element-queries/src/ElementQueries');
158159

@@ -305,6 +306,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
305306
rebind(TheiaOutputChannelRegistryMainImpl).toService(OutputChannelRegistryMainImpl);
306307
bind(MonacoTextModelService).toSelf().inSingletonScope();
307308
rebind(TheiaMonacoTextModelService).toService(MonacoTextModelService);
309+
bind(MonacoEditorProvider).toSelf().inSingletonScope();
310+
rebind(TheiaMonacoEditorProvider).toService(MonacoEditorProvider);
308311

309312
bind(SearchInWorkspaceWidget).toSelf();
310313
rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget);
@@ -321,10 +324,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
321324
bind(FrontendConnectionStatusService).toSelf().inSingletonScope();
322325
rebind(TheiaFrontendConnectionStatusService).toService(FrontendConnectionStatusService);
323326

324-
// Editor customizations. Sets the editor to `readOnly` if under the data dir.
325-
bind(EditorManager).toSelf().inSingletonScope();
326-
rebind(TheiaEditorManager).toService(EditorManager);
327-
328327
// Decorator customizations
329328
bind(TabBarDecoratorService).toSelf().inSingletonScope();
330329
rebind(TheiaTabBarDecoratorService).toService(TabBarDecoratorService);

Diff for: arduino-ide-extension/src/browser/theia/editor/editor-manager.ts

-31
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import { inject, injectable } from 'inversify';
2+
import URI from '@theia/core/lib/common/uri';
3+
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
4+
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
5+
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
6+
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
7+
8+
type CancelablePromise = Promise<monaco.referenceSearch.ReferencesModel> & { cancel: () => void };
9+
interface EditorFactory {
10+
(override: monaco.editor.IEditorOverrideServices, toDispose: DisposableCollection): Promise<MonacoEditor>;
11+
}
12+
13+
@injectable()
14+
export class MonacoEditorProvider extends TheiaMonacoEditorProvider {
15+
16+
@inject(SketchesServiceClientImpl)
17+
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
18+
19+
protected async doCreateEditor(uri: URI, factory: EditorFactory): Promise<MonacoEditor> {
20+
const editor = await super.doCreateEditor(uri, factory);
21+
const toDispose = new DisposableCollection();
22+
toDispose.push(this.installCustomReferencesController(editor));
23+
toDispose.push(editor.onDispose(() => toDispose.dispose()));
24+
return editor;
25+
}
26+
27+
private installCustomReferencesController(editor: MonacoEditor): Disposable {
28+
const control = editor.getControl();
29+
const referencesController = control._contributions['editor.contrib.referencesController'];
30+
const originalToggleWidget = referencesController.toggleWidget;
31+
const toDispose = new DisposableCollection();
32+
const toDisposeBeforeToggleWidget = new DisposableCollection();
33+
referencesController.toggleWidget = (range: monaco.Range, modelPromise: CancelablePromise, peekMode: boolean) => {
34+
toDisposeBeforeToggleWidget.dispose();
35+
originalToggleWidget.bind(referencesController)(range, modelPromise, peekMode);
36+
if (referencesController._widget) {
37+
if ('onDidClose' in referencesController._widget) {
38+
toDisposeBeforeToggleWidget.push((referencesController._widget as any).onDidClose(() => toDisposeBeforeToggleWidget.dispose()));
39+
}
40+
const preview = (referencesController._widget as any)._preview as monaco.editor.ICodeEditor;
41+
if (preview) {
42+
toDisposeBeforeToggleWidget.push(preview.onDidChangeModel(() => this.updateReadOnlyState(preview)))
43+
this.updateReadOnlyState(preview);
44+
}
45+
}
46+
};
47+
toDispose.push(Disposable.create(() => toDisposeBeforeToggleWidget.dispose()));
48+
toDispose.push(Disposable.create(() => referencesController.toggleWidget = originalToggleWidget));
49+
return toDispose;
50+
}
51+
52+
private updateReadOnlyState(editor: monaco.editor.ICodeEditor | undefined): void {
53+
if (!editor) {
54+
return;
55+
}
56+
const model = editor.getModel();
57+
if (!model) {
58+
return;
59+
}
60+
const readOnly = this.sketchesServiceClient.isReadOnly(model.uri);
61+
editor.updateOptions({ readOnly });
62+
}
63+
64+
}
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,29 @@
1-
import { injectable } from 'inversify';
1+
import { inject, injectable } from 'inversify';
22
import { Resource } from '@theia/core/lib/common/resource';
3-
import { MaybePromise } from '@theia/core/lib/common/types';
4-
import { Log, Loggable } from '@theia/core/lib/common/logger';
3+
import { ILogger, Log, Loggable } from '@theia/core/lib/common/logger';
54
import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
5+
import { EditorPreferences } from '@theia/editor/lib/browser/editor-preferences';
6+
import { MonacoToProtocolConverter } from '@theia/monaco/lib/browser/monaco-to-protocol-converter';
7+
import { ProtocolToMonacoConverter } from '@theia/monaco/lib/browser/protocol-to-monaco-converter';
68
import { MonacoTextModelService as TheiaMonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
9+
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
710

811
@injectable()
912
export class MonacoTextModelService extends TheiaMonacoTextModelService {
1013

11-
protected createModel(resource: Resource): MaybePromise<MonacoEditorModel> {
14+
@inject(SketchesServiceClientImpl)
15+
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
16+
17+
protected async createModel(resource: Resource): Promise<MonacoEditorModel> {
1218
const factory = this.factories.getContributions().find(({ scheme }) => resource.uri.scheme === scheme);
13-
return factory ? factory.createModel(resource) : new SilentMonacoEditorModel(resource, this.m2p, this.p2m, this.logger);
19+
const readOnly = this.sketchesServiceClient.isReadOnly(resource.uri);
20+
return factory ? factory.createModel(resource) : new MaybeReadonlyMonacoEditorModel(resource, this.m2p, this.p2m, this.logger, undefined, readOnly);
1421
}
1522

1623
}
1724

1825
// https://github.com/eclipse-theia/theia/pull/8491
19-
export class SilentMonacoEditorModel extends MonacoEditorModel {
26+
class SilentMonacoEditorModel extends MonacoEditorModel {
2027

2128
protected trace(loggable: Loggable): void {
2229
if (this.logger) {
@@ -27,3 +34,41 @@ export class SilentMonacoEditorModel extends MonacoEditorModel {
2734
}
2835

2936
}
37+
38+
class MaybeReadonlyMonacoEditorModel extends SilentMonacoEditorModel {
39+
40+
constructor(
41+
protected readonly resource: Resource,
42+
protected readonly m2p: MonacoToProtocolConverter,
43+
protected readonly p2m: ProtocolToMonacoConverter,
44+
protected readonly logger?: ILogger,
45+
protected readonly editorPreferences?: EditorPreferences,
46+
protected readonly _readOnly?: boolean,
47+
) {
48+
super(resource, m2p, p2m, logger, editorPreferences)
49+
}
50+
51+
get readOnly(): boolean {
52+
if (typeof this._readOnly === 'boolean') {
53+
return this._readOnly;
54+
}
55+
return this.resource.saveContents === undefined;
56+
}
57+
58+
protected setDirty(dirty: boolean): void {
59+
if (this._readOnly === true) {
60+
// NOOP
61+
return;
62+
}
63+
if (dirty === this._dirty) {
64+
return;
65+
}
66+
this._dirty = dirty;
67+
if (dirty === false) {
68+
(this as any).updateSavedVersionId();
69+
}
70+
this.onDirtyChangedEmitter.fire(undefined);
71+
}
72+
73+
74+
}

Diff for: arduino-ide-extension/src/common/protocol/sketches-service-client-impl.ts

+13-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import { inject, injectable } from 'inversify';
22
import URI from '@theia/core/lib/common/uri';
3+
import { Emitter } from '@theia/core/lib/common/event';
34
import { notEmpty } from '@theia/core/lib/common/objects';
45
import { FileService } from '@theia/filesystem/lib/browser/file-service';
56
import { MessageService } from '@theia/core/lib/common/message-service';
7+
import { FileChangeType } from '@theia/filesystem/lib/common/files';
68
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
9+
import { DisposableCollection } from '@theia/core/lib/common/disposable';
10+
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
711
import { Sketch, SketchesService } from '../../common/protocol';
8-
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
912
import { ConfigService } from './config-service';
10-
import { DisposableCollection, Emitter } from '@theia/core';
11-
import { FileChangeType } from '@theia/filesystem/lib/browser';
1213
import { SketchContainer } from './sketches-service';
1314

1415
@injectable()
@@ -129,4 +130,13 @@ export class SketchesServiceClientImpl implements FrontendApplicationContributio
129130
}, 100);
130131
}
131132

133+
/**
134+
* `true` if the `uri` is not contained in any of the opened workspaces. Otherwise, `false`.
135+
*/
136+
isReadOnly(uri: URI | monaco.Uri | string): boolean {
137+
const toCheck = uri instanceof URI ? uri : new URI(uri);
138+
const readOnly = !this.workspaceService.tryGetRoots().some(({ resource }) => resource.isEqualOrParent(toCheck));
139+
return readOnly;
140+
}
141+
132142
}

0 commit comments

Comments
 (0)