Skip to content
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

[core][monaco] Implement Save without Formatting command #8543

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions packages/core/src/browser/common-frontend-contribution.ts
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ export namespace CommonCommands {
category: FILE_CATEGORY,
label: 'Save',
};
export const SAVE_WITHOUT_FORMATTING: Command = {
id: 'core.saveWithoutFormatting',
category: FILE_CATEGORY,
label: 'Save without Formatting',
};
export const SAVE_ALL: Command = {
id: 'core.saveAll',
category: FILE_CATEGORY,
Expand Down Expand Up @@ -445,6 +450,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
registry.registerMenuAction(CommonMenus.FILE_SAVE, {
commandId: CommonCommands.SAVE.id
});
registry.registerMenuAction(CommonMenus.FILE_SAVE, {
commandId: CommonCommands.SAVE_WITHOUT_FORMATTING.id
});
registry.registerMenuAction(CommonMenus.FILE_SAVE, {
commandId: CommonCommands.SAVE_ALL.id
});
Expand Down Expand Up @@ -747,6 +755,9 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
commandRegistry.registerCommand(CommonCommands.SAVE, {
execute: () => this.shell.save()
});
commandRegistry.registerCommand(CommonCommands.SAVE_WITHOUT_FORMATTING, {
execute: () => this.shell.save({ skipFormatting: true })
});
commandRegistry.registerCommand(CommonCommands.SAVE_ALL, {
execute: () => this.shell.saveAll()
});
Expand Down Expand Up @@ -924,6 +935,10 @@ export class CommonFrontendContribution implements FrontendApplicationContributi
command: CommonCommands.SAVE.id,
keybinding: 'ctrlcmd+s'
},
{
command: CommonCommands.SAVE_WITHOUT_FORMATTING.id,
keybinding: 'ctrlcmd+k s'
},
{
command: CommonCommands.SAVE_ALL.id,
keybinding: 'ctrlcmd+alt+s'
Expand Down
13 changes: 10 additions & 3 deletions packages/core/src/browser/saveable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface Saveable {
/**
* Saves dirty changes.
*/
save(): MaybePromise<void>;
save(options?: SaveOptions): MaybePromise<void>;
/**
* Reverts dirty changes.
*/
Expand Down Expand Up @@ -87,10 +87,10 @@ export namespace Saveable {
return !!getDirty(arg);
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
export async function save(arg: any): Promise<void> {
export async function save(arg: any, options?: SaveOptions): Promise<void> {
const saveable = get(arg);
if (saveable) {
await saveable.save();
await saveable.save(options);
}
}
export function apply(widget: Widget): SaveableWidget | undefined {
Expand Down Expand Up @@ -179,6 +179,13 @@ export namespace SaveableWidget {
}
}

export interface SaveOptions {
DucNgn marked this conversation as resolved.
Show resolved Hide resolved
/**
* Controls whether formatting should be applied upon saving
*/
readonly skipFormatting?: boolean;
}

/**
* The class name added to the dirty widget's title.
*/
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/browser/shell/application-shell.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import { Message } from '@phosphor/messaging';
import { IDragEvent } from '@phosphor/dragdrop';
import { RecursivePartial, Event as CommonEvent, DisposableCollection, Disposable } from '../../common';
import { animationFrame } from '../browser';
import { Saveable, SaveableWidget } from '../saveable';
import { Saveable, SaveableWidget, SaveOptions } from '../saveable';
import { StatusBarImpl, StatusBarEntry, StatusBarAlignment } from '../status-bar/status-bar';
import { TheiaDockPanel, BOTTOM_AREA_ID, MAIN_AREA_ID } from './theia-dock-panel';
import { SidePanelHandler, SidePanel, SidePanelHandlerFactory } from './side-panel-handler';
Expand Down Expand Up @@ -1731,8 +1731,8 @@ export class ApplicationShell extends Widget {
/**
* Save the current widget if it is dirty.
*/
async save(): Promise<void> {
await Saveable.save(this.currentWidget);
async save(options?: SaveOptions): Promise<void> {
await Saveable.save(this.currentWidget, options);
}

/**
Expand All @@ -1746,7 +1746,7 @@ export class ApplicationShell extends Widget {
* Save all dirty widgets.
*/
async saveAll(): Promise<void> {
await Promise.all(this.tracker.widgets.map(Saveable.save));
await Promise.all(this.tracker.widgets.map(widget => Saveable.save(widget)));
paul-marechal marked this conversation as resolved.
Show resolved Hide resolved
}

/**
Expand Down
19 changes: 10 additions & 9 deletions packages/monaco/src/browser/monaco-editor-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { Emitter, Event } from '@theia/core/lib/common/event';
import { CancellationTokenSource, CancellationToken } from '@theia/core/lib/common/cancellation';
import { Resource, ResourceError, ResourceVersion } from '@theia/core/lib/common/resource';
import { Range } from 'vscode-languageserver-types';
import { Saveable } from '@theia/core/lib/browser/saveable';
import { Saveable, SaveOptions } from '@theia/core/lib/browser/saveable';
import { MonacoToProtocolConverter } from './monaco-to-protocol-converter';
import { ProtocolToMonacoConverter } from './protocol-to-monaco-converter';
import { ILogger, Loggable, Log } from '@theia/core/lib/common/logger';
Expand All @@ -36,6 +36,7 @@ type ITextEditorModel = monaco.editor.ITextEditorModel;
export interface WillSaveMonacoModelEvent {
readonly model: MonacoEditorModel;
readonly reason: TextDocumentSaveReason;
readonly options?: SaveOptions;
waitUntil(thenable: Thenable<monaco.editor.IIdentifiedSingleEditOperation[]>): void;
}

Expand Down Expand Up @@ -283,8 +284,8 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument {
return this;
}

save(): Promise<void> {
return this.scheduleSave(TextDocumentSaveReason.Manual);
save(options?: SaveOptions): Promise<void> {
return this.scheduleSave(TextDocumentSaveReason.Manual, undefined, undefined, options);
}

protected pendingOperation = Promise.resolve();
Expand Down Expand Up @@ -397,8 +398,8 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument {
return this.saveCancellationTokenSource.token;
}

protected scheduleSave(reason: TextDocumentSaveReason, token: CancellationToken = this.cancelSave(), overwriteEncoding?: boolean): Promise<void> {
return this.run(() => this.doSave(reason, token, overwriteEncoding));
protected scheduleSave(reason: TextDocumentSaveReason, token: CancellationToken = this.cancelSave(), overwriteEncoding?: boolean, options?: SaveOptions): Promise<void> {
return this.run(() => this.doSave(reason, token, overwriteEncoding, options));
}

protected ignoreContentChanges = false;
Expand Down Expand Up @@ -457,12 +458,12 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument {
}
}

protected async doSave(reason: TextDocumentSaveReason, token: CancellationToken, overwriteEncoding?: boolean): Promise<void> {
protected async doSave(reason: TextDocumentSaveReason, token: CancellationToken, overwriteEncoding?: boolean, options?: SaveOptions): Promise<void> {
if (token.isCancellationRequested || !this.resource.saveContents) {
return;
}

await this.fireWillSaveModel(reason, token);
await this.fireWillSaveModel(reason, token, options);
if (token.isCancellationRequested) {
return;
}
Expand Down Expand Up @@ -496,7 +497,7 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument {
}
}

protected async fireWillSaveModel(reason: TextDocumentSaveReason, token: CancellationToken): Promise<void> {
protected async fireWillSaveModel(reason: TextDocumentSaveReason, token: CancellationToken, options?: SaveOptions): Promise<void> {
type EditContributor = Thenable<monaco.editor.IIdentifiedSingleEditOperation[]>;

const firing = this.onWillSaveModelEmitter.sequence(async listener => {
Expand All @@ -507,7 +508,7 @@ export class MonacoEditorModel implements ITextEditorModel, TextEditorDocument {
const { version } = this;

const event = {
model: this, reason,
model: this, reason, options,
waitUntil: (thenable: EditContributor) => {
if (Object.isFrozen(waitables)) {
throw new Error('waitUntil cannot be called asynchronously.');
Expand Down
3 changes: 3 additions & 0 deletions packages/monaco/src/browser/monaco-editor-provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,9 @@ export class MonacoEditorProvider {
if (event.reason !== TextDocumentSaveReason.Manual) {
return [];
}
if (event.options?.skipFormatting) {
return [];
}
const overrideIdentifier = editor.document.languageId;
const uri = editor.uri.toString();
const formatOnSave = this.editorPreferences.get({ preferenceName: 'editor.formatOnSave', overrideIdentifier }, undefined, uri)!;
Expand Down