diff --git a/packages/core/src/common/preferences/preference-scope.ts b/packages/core/src/common/preferences/preference-scope.ts index 8af78ac3db241..2b24938c2b8dd 100644 --- a/packages/core/src/common/preferences/preference-scope.ts +++ b/packages/core/src/common/preferences/preference-scope.ts @@ -60,6 +60,8 @@ export namespace PreferenceScope { return PreferenceScope.Folder; case 'resource': return PreferenceScope.Folder; + case 'language-overridable': + return PreferenceScope.Folder; } } } diff --git a/packages/filesystem/src/browser/filesystem-preferences.ts b/packages/filesystem/src/browser/filesystem-preferences.ts index 5c8e872b6dc14..48d39dd61a761 100644 --- a/packages/filesystem/src/browser/filesystem-preferences.ts +++ b/packages/filesystem/src/browser/filesystem-preferences.ts @@ -78,6 +78,12 @@ These have precedence over the default associations of the languages installed.' type: 'number', default: MAX_FILE_SIZE_MB, markdownDescription: 'Controls the max file size in MB which is possible to open.' + }, + 'files.trimTrailingWhitespace': { + 'type': 'boolean', + 'default': false, + 'description': 'When enabled, will trim trailing whitespace when saving a file.', + 'scope': 'language-overridable' } } }; @@ -91,6 +97,7 @@ export interface FileSystemConfiguration { 'files.autoGuessEncoding': boolean; 'files.participants.timeout': number; 'files.maxFileSizeMB': number; + 'files.trimTrailingWhitespace': boolean; } export const FileSystemPreferences = Symbol('FileSystemPreferences'); diff --git a/packages/monaco/src/browser/monaco-editor-provider.ts b/packages/monaco/src/browser/monaco-editor-provider.ts index 52418fa18c151..40febe552ca85 100644 --- a/packages/monaco/src/browser/monaco-editor-provider.ts +++ b/packages/monaco/src/browser/monaco-editor-provider.ts @@ -41,6 +41,7 @@ import { MonacoResolvedKeybinding } from './monaco-resolved-keybinding'; import { HttpOpenHandlerOptions } from '@theia/core/lib/browser/http-open-handler'; import { MonacoToProtocolConverter } from './monaco-to-protocol-converter'; import { ProtocolToMonacoConverter } from './protocol-to-monaco-converter'; +import { FileSystemPreferences } from '@theia/filesystem/lib/browser'; export const MonacoEditorFactory = Symbol('MonacoEditorFactory'); export interface MonacoEditorFactory { @@ -67,6 +68,9 @@ export class MonacoEditorProvider { @inject(OpenerService) protected readonly openerService: OpenerService; + @inject(FileSystemPreferences) + protected readonly filePreferences: FileSystemPreferences; + protected _current: MonacoEditor | undefined; /** * Returns the last focused MonacoEditor. @@ -319,14 +323,17 @@ export class MonacoEditorProvider { const overrideIdentifier = editor.document.languageId; const uri = editor.uri.toString(); const formatOnSave = this.editorPreferences.get({ preferenceName: 'editor.formatOnSave', overrideIdentifier }, undefined, uri)!; - if (!formatOnSave) { - return []; + if (formatOnSave) { + const formatOnSaveTimeout = this.editorPreferences.get({ preferenceName: 'editor.formatOnSaveTimeout', overrideIdentifier }, undefined, uri)!; + await Promise.race([ + new Promise((_, reject) => setTimeout(() => reject(new Error(`Aborted format on save after ${formatOnSaveTimeout}ms`)), formatOnSaveTimeout)), + editor.runAction('editor.action.formatDocument') + ]); + } + const shouldRemoveWhiteSpace = this.filePreferences.get({ preferenceName: 'files.trimTrailingWhitespace', overrideIdentifier }, undefined, uri); + if (shouldRemoveWhiteSpace) { + await editor.runAction('editor.action.trimTrailingWhitespace'); } - const formatOnSaveTimeout = this.editorPreferences.get({ preferenceName: 'editor.formatOnSaveTimeout', overrideIdentifier }, undefined, uri)!; - await Promise.race([ - new Promise((_, reject) => setTimeout(() => reject(new Error(`Aborted format on save after ${formatOnSaveTimeout}ms`)), formatOnSaveTimeout)), - editor.runAction('editor.action.formatDocument') - ]); return []; }