mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-10 12:56:32 +00:00
ATL-1106: Made all non-workspace editors read-only.
Signed-off-by: Akos Kitta <kittaakos@typefox.io>
This commit is contained in:
parent
c75b954041
commit
f712ec986f
@ -67,8 +67,7 @@ import { ListItemRenderer } from './widgets/component-list/list-item-renderer';
|
||||
import { ColorContribution } from '@theia/core/lib/browser/color-application-contribution';
|
||||
import { MonacoThemingService } from '@theia/monaco/lib/browser/monaco-theming-service';
|
||||
import { ArduinoDaemonPath, ArduinoDaemon } from '../common/protocol/arduino-daemon';
|
||||
import { EditorManager as TheiaEditorManager, EditorCommandContribution as TheiaEditorCommandContribution } from '@theia/editor/lib/browser';
|
||||
import { EditorManager } from './theia/editor/editor-manager';
|
||||
import { EditorCommandContribution as TheiaEditorCommandContribution } from '@theia/editor/lib/browser';
|
||||
import { FrontendConnectionStatusService, ApplicationConnectionStatusContribution } from './theia/core/connection-status-service';
|
||||
import {
|
||||
FrontendConnectionStatusService as TheiaFrontendConnectionStatusService,
|
||||
@ -153,6 +152,8 @@ import { SearchInWorkspaceWidget as TheiaSearchInWorkspaceWidget } from '@theia/
|
||||
import { SearchInWorkspaceWidget } from './theia/search-in-workspace/search-in-workspace-widget';
|
||||
import { SearchInWorkspaceResultTreeWidget as TheiaSearchInWorkspaceResultTreeWidget } from '@theia/search-in-workspace/lib/browser/search-in-workspace-result-tree-widget';
|
||||
import { SearchInWorkspaceResultTreeWidget } from './theia/search-in-workspace/search-in-workspace-result-tree-widget';
|
||||
import { MonacoEditorProvider } from './theia/monaco/monaco-editor-provider';
|
||||
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
||||
|
||||
const ElementQueries = require('css-element-queries/src/ElementQueries');
|
||||
|
||||
@ -305,6 +306,8 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
rebind(TheiaOutputChannelRegistryMainImpl).toService(OutputChannelRegistryMainImpl);
|
||||
bind(MonacoTextModelService).toSelf().inSingletonScope();
|
||||
rebind(TheiaMonacoTextModelService).toService(MonacoTextModelService);
|
||||
bind(MonacoEditorProvider).toSelf().inSingletonScope();
|
||||
rebind(TheiaMonacoEditorProvider).toService(MonacoEditorProvider);
|
||||
|
||||
bind(SearchInWorkspaceWidget).toSelf();
|
||||
rebind(TheiaSearchInWorkspaceWidget).toService(SearchInWorkspaceWidget);
|
||||
@ -321,10 +324,6 @@ export default new ContainerModule((bind, unbind, isBound, rebind) => {
|
||||
bind(FrontendConnectionStatusService).toSelf().inSingletonScope();
|
||||
rebind(TheiaFrontendConnectionStatusService).toService(FrontendConnectionStatusService);
|
||||
|
||||
// Editor customizations. Sets the editor to `readOnly` if under the data dir.
|
||||
bind(EditorManager).toSelf().inSingletonScope();
|
||||
rebind(TheiaEditorManager).toService(EditorManager);
|
||||
|
||||
// Decorator customizations
|
||||
bind(TabBarDecoratorService).toSelf().inSingletonScope();
|
||||
rebind(TheiaTabBarDecoratorService).toService(TabBarDecoratorService);
|
||||
|
@ -1,31 +0,0 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { EditorManager as TheiaEditorManager, EditorOpenerOptions } from '@theia/editor/lib/browser/editor-manager';
|
||||
import { ConfigService } from '../../../common/protocol/config-service';
|
||||
import { EditorWidget } from '@theia/editor/lib/browser';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
|
||||
@injectable()
|
||||
export class EditorManager extends TheiaEditorManager {
|
||||
|
||||
@inject(ConfigService)
|
||||
protected readonly configService: ConfigService;
|
||||
|
||||
async open(uri: URI, options?: EditorOpenerOptions): Promise<EditorWidget> {
|
||||
const [widget, readOnly] = await Promise.all([super.open(uri, options), this.isReadOnly(uri)]);
|
||||
if (readOnly) {
|
||||
const { editor } = widget;
|
||||
if (editor instanceof MonacoEditor) {
|
||||
const codeEditor = editor.getControl();
|
||||
codeEditor.updateOptions({ readOnly });
|
||||
}
|
||||
}
|
||||
return widget;
|
||||
}
|
||||
|
||||
protected async isReadOnly(uri: URI): Promise<boolean> {
|
||||
const config = await this.configService.getConfiguration();
|
||||
return new URI(config.dataDirUri).isEqualOrParent(uri)
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,64 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Disposable, DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { MonacoEditor } from '@theia/monaco/lib/browser/monaco-editor';
|
||||
import { MonacoEditorProvider as TheiaMonacoEditorProvider } from '@theia/monaco/lib/browser/monaco-editor-provider';
|
||||
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
type CancelablePromise = Promise<monaco.referenceSearch.ReferencesModel> & { cancel: () => void };
|
||||
interface EditorFactory {
|
||||
(override: monaco.editor.IEditorOverrideServices, toDispose: DisposableCollection): Promise<MonacoEditor>;
|
||||
}
|
||||
|
||||
@injectable()
|
||||
export class MonacoEditorProvider extends TheiaMonacoEditorProvider {
|
||||
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
protected async doCreateEditor(uri: URI, factory: EditorFactory): Promise<MonacoEditor> {
|
||||
const editor = await super.doCreateEditor(uri, factory);
|
||||
const toDispose = new DisposableCollection();
|
||||
toDispose.push(this.installCustomReferencesController(editor));
|
||||
toDispose.push(editor.onDispose(() => toDispose.dispose()));
|
||||
return editor;
|
||||
}
|
||||
|
||||
private installCustomReferencesController(editor: MonacoEditor): Disposable {
|
||||
const control = editor.getControl();
|
||||
const referencesController = control._contributions['editor.contrib.referencesController'];
|
||||
const originalToggleWidget = referencesController.toggleWidget;
|
||||
const toDispose = new DisposableCollection();
|
||||
const toDisposeBeforeToggleWidget = new DisposableCollection();
|
||||
referencesController.toggleWidget = (range: monaco.Range, modelPromise: CancelablePromise, peekMode: boolean) => {
|
||||
toDisposeBeforeToggleWidget.dispose();
|
||||
originalToggleWidget.bind(referencesController)(range, modelPromise, peekMode);
|
||||
if (referencesController._widget) {
|
||||
if ('onDidClose' in referencesController._widget) {
|
||||
toDisposeBeforeToggleWidget.push((referencesController._widget as any).onDidClose(() => toDisposeBeforeToggleWidget.dispose()));
|
||||
}
|
||||
const preview = (referencesController._widget as any)._preview as monaco.editor.ICodeEditor;
|
||||
if (preview) {
|
||||
toDisposeBeforeToggleWidget.push(preview.onDidChangeModel(() => this.updateReadOnlyState(preview)))
|
||||
this.updateReadOnlyState(preview);
|
||||
}
|
||||
}
|
||||
};
|
||||
toDispose.push(Disposable.create(() => toDisposeBeforeToggleWidget.dispose()));
|
||||
toDispose.push(Disposable.create(() => referencesController.toggleWidget = originalToggleWidget));
|
||||
return toDispose;
|
||||
}
|
||||
|
||||
private updateReadOnlyState(editor: monaco.editor.ICodeEditor | undefined): void {
|
||||
if (!editor) {
|
||||
return;
|
||||
}
|
||||
const model = editor.getModel();
|
||||
if (!model) {
|
||||
return;
|
||||
}
|
||||
const readOnly = this.sketchesServiceClient.isReadOnly(model.uri);
|
||||
editor.updateOptions({ readOnly });
|
||||
}
|
||||
|
||||
}
|
@ -1,22 +1,29 @@
|
||||
import { injectable } from 'inversify';
|
||||
import { inject, injectable } from 'inversify';
|
||||
import { Resource } from '@theia/core/lib/common/resource';
|
||||
import { MaybePromise } from '@theia/core/lib/common/types';
|
||||
import { Log, Loggable } from '@theia/core/lib/common/logger';
|
||||
import { ILogger, Log, Loggable } from '@theia/core/lib/common/logger';
|
||||
import { MonacoEditorModel } from '@theia/monaco/lib/browser/monaco-editor-model';
|
||||
import { EditorPreferences } from '@theia/editor/lib/browser/editor-preferences';
|
||||
import { MonacoToProtocolConverter } from '@theia/monaco/lib/browser/monaco-to-protocol-converter';
|
||||
import { ProtocolToMonacoConverter } from '@theia/monaco/lib/browser/protocol-to-monaco-converter';
|
||||
import { MonacoTextModelService as TheiaMonacoTextModelService } from '@theia/monaco/lib/browser/monaco-text-model-service';
|
||||
import { SketchesServiceClientImpl } from '../../../common/protocol/sketches-service-client-impl';
|
||||
|
||||
@injectable()
|
||||
export class MonacoTextModelService extends TheiaMonacoTextModelService {
|
||||
|
||||
protected createModel(resource: Resource): MaybePromise<MonacoEditorModel> {
|
||||
@inject(SketchesServiceClientImpl)
|
||||
protected readonly sketchesServiceClient: SketchesServiceClientImpl;
|
||||
|
||||
protected async createModel(resource: Resource): Promise<MonacoEditorModel> {
|
||||
const factory = this.factories.getContributions().find(({ scheme }) => resource.uri.scheme === scheme);
|
||||
return factory ? factory.createModel(resource) : new SilentMonacoEditorModel(resource, this.m2p, this.p2m, this.logger);
|
||||
const readOnly = this.sketchesServiceClient.isReadOnly(resource.uri);
|
||||
return factory ? factory.createModel(resource) : new MaybeReadonlyMonacoEditorModel(resource, this.m2p, this.p2m, this.logger, undefined, readOnly);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// https://github.com/eclipse-theia/theia/pull/8491
|
||||
export class SilentMonacoEditorModel extends MonacoEditorModel {
|
||||
class SilentMonacoEditorModel extends MonacoEditorModel {
|
||||
|
||||
protected trace(loggable: Loggable): void {
|
||||
if (this.logger) {
|
||||
@ -27,3 +34,41 @@ export class SilentMonacoEditorModel extends MonacoEditorModel {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MaybeReadonlyMonacoEditorModel extends SilentMonacoEditorModel {
|
||||
|
||||
constructor(
|
||||
protected readonly resource: Resource,
|
||||
protected readonly m2p: MonacoToProtocolConverter,
|
||||
protected readonly p2m: ProtocolToMonacoConverter,
|
||||
protected readonly logger?: ILogger,
|
||||
protected readonly editorPreferences?: EditorPreferences,
|
||||
protected readonly _readOnly?: boolean,
|
||||
) {
|
||||
super(resource, m2p, p2m, logger, editorPreferences)
|
||||
}
|
||||
|
||||
get readOnly(): boolean {
|
||||
if (typeof this._readOnly === 'boolean') {
|
||||
return this._readOnly;
|
||||
}
|
||||
return this.resource.saveContents === undefined;
|
||||
}
|
||||
|
||||
protected setDirty(dirty: boolean): void {
|
||||
if (this._readOnly === true) {
|
||||
// NOOP
|
||||
return;
|
||||
}
|
||||
if (dirty === this._dirty) {
|
||||
return;
|
||||
}
|
||||
this._dirty = dirty;
|
||||
if (dirty === false) {
|
||||
(this as any).updateSavedVersionId();
|
||||
}
|
||||
this.onDirtyChangedEmitter.fire(undefined);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
@ -1,14 +1,15 @@
|
||||
import { inject, injectable } from 'inversify';
|
||||
import URI from '@theia/core/lib/common/uri';
|
||||
import { Emitter } from '@theia/core/lib/common/event';
|
||||
import { notEmpty } from '@theia/core/lib/common/objects';
|
||||
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
||||
import { MessageService } from '@theia/core/lib/common/message-service';
|
||||
import { FileChangeType } from '@theia/filesystem/lib/common/files';
|
||||
import { WorkspaceService } from '@theia/workspace/lib/browser/workspace-service';
|
||||
import { DisposableCollection } from '@theia/core/lib/common/disposable';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application';
|
||||
import { Sketch, SketchesService } from '../../common/protocol';
|
||||
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
|
||||
import { ConfigService } from './config-service';
|
||||
import { DisposableCollection, Emitter } from '@theia/core';
|
||||
import { FileChangeType } from '@theia/filesystem/lib/browser';
|
||||
import { SketchContainer } from './sketches-service';
|
||||
|
||||
@injectable()
|
||||
@ -129,4 +130,13 @@ export class SketchesServiceClientImpl implements FrontendApplicationContributio
|
||||
}, 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if the `uri` is not contained in any of the opened workspaces. Otherwise, `false`.
|
||||
*/
|
||||
isReadOnly(uri: URI | monaco.Uri | string): boolean {
|
||||
const toCheck = uri instanceof URI ? uri : new URI(uri);
|
||||
const readOnly = !this.workspaceService.tryGetRoots().some(({ resource }) => resource.isEqualOrParent(toCheck));
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user