提交 1e772676 编写于 作者: J Johannes Rieken

format on save to only format modifications

上级 d670a947
......@@ -125,6 +125,7 @@ export async function formatDocumentRangesWithSelectedProvider(
editorOrModel: ITextModel | IActiveCodeEditor,
rangeOrRanges: Range | Range[],
mode: FormattingMode,
progress: IProgress<DocumentRangeFormattingEditProvider>,
token: CancellationToken
): Promise<void> {
......@@ -133,6 +134,7 @@ export async function formatDocumentRangesWithSelectedProvider(
const provider = DocumentRangeFormattingEditProviderRegistry.ordered(model);
const selected = await FormattingConflicts.select(provider, model, mode);
if (selected) {
progress.report(selected);
await instaService.invokeFunction(formatDocumentRangesWithProvider, selected, editorOrModel, rangeOrRanges, token);
}
}
......
......@@ -202,7 +202,7 @@ class FormatOnPaste implements IEditorContribution {
if (this.editor.getSelections().length > 1) {
return;
}
this._instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, this.editor, range, FormattingMode.Silent, CancellationToken.None).catch(onUnexpectedError);
this._instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, this.editor, range, FormattingMode.Silent, Progress.None, CancellationToken.None).catch(onUnexpectedError);
}
}
......@@ -274,7 +274,7 @@ class FormatSelectionAction extends EditorAction {
const progressService = accessor.get(IEditorProgressService);
await progressService.showWhile(
instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, range, FormattingMode.Explicit, CancellationToken.None),
instaService.invokeFunction(formatDocumentRangesWithSelectedProvider, editor, range, FormattingMode.Explicit, Progress.None, CancellationToken.None),
250
);
}
......
......@@ -5,7 +5,7 @@
import { CancellationToken } from 'vs/base/common/cancellation';
import * as strings from 'vs/base/common/strings';
import { IActiveCodeEditor } from 'vs/editor/browser/editorBrowser';
import { IActiveCodeEditor, isCodeEditor } from 'vs/editor/browser/editorBrowser';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { trimTrailingWhitespace } from 'vs/editor/common/commands/trimTrailingWhitespaceCommand';
import { EditOperation } from 'vs/editor/common/core/editOperation';
......@@ -13,11 +13,11 @@ import { Position } from 'vs/editor/common/core/position';
import { Range } from 'vs/editor/common/core/range';
import { Selection } from 'vs/editor/common/core/selection';
import { ITextModel } from 'vs/editor/common/model';
import { CodeActionTriggerType, DocumentFormattingEditProvider, CodeActionProvider } from 'vs/editor/common/modes';
import { CodeActionTriggerType, CodeActionProvider } from 'vs/editor/common/modes';
import { getCodeActions } from 'vs/editor/contrib/codeAction/codeAction';
import { applyCodeAction } from 'vs/editor/contrib/codeAction/codeActionCommands';
import { CodeActionKind } from 'vs/editor/contrib/codeAction/types';
import { formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
import { formatDocumentRangesWithSelectedProvider, formatDocumentWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
import { SnippetController2 } from 'vs/editor/contrib/snippet/snippetController2';
import { localize } from 'vs/nls';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
......@@ -29,6 +29,8 @@ import { Disposable } from 'vs/base/common/lifecycle';
import { IWorkbenchContribution, Extensions as WorkbenchContributionsExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions';
import { Registry } from 'vs/platform/registry/common/platform';
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
import { getModifiedRanges } from 'vs/workbench/contrib/format/browser/formatModified';
import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions';
export class TrimWhitespaceParticipant implements ITextFileSaveParticipant {
......@@ -221,15 +223,14 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant {
if (!model.textEditorModel) {
return;
}
if (env.reason === SaveReason.AUTO) {
return undefined;
}
const textEditorModel = model.textEditorModel;
const overrides = { overrideIdentifier: textEditorModel.getLanguageIdentifier().language, resource: textEditorModel.uri };
if (env.reason === SaveReason.AUTO || !this.configurationService.getValue('editor.formatOnSave', overrides)) {
return undefined;
}
const nestedProgress = new Progress<DocumentFormattingEditProvider>(provider => {
const nestedProgress = new Progress<{ displayName?: string, extensionId?: ExtensionIdentifier }>(provider => {
progress.report({
message: localize(
'formatting',
......@@ -239,7 +240,19 @@ class FormatOnSaveParticipant implements ITextFileSaveParticipant {
});
});
const editorOrModel = findEditor(textEditorModel, this.codeEditorService) || textEditorModel;
const config = this.configurationService.getValue<boolean | string>('editor.formatOnSave', overrides);
if (config === true || config === 'file') {
// format the whole file
await this.instantiationService.invokeFunction(formatDocumentWithSelectedProvider, editorOrModel, FormattingMode.Silent, nestedProgress, token);
} else if (config === 'modifications') {
// format modifications
const ranges = await this.instantiationService.invokeFunction(getModifiedRanges, isCodeEditor(editorOrModel) ? editorOrModel.getModel() : editorOrModel);
if (ranges) {
await this.instantiationService.invokeFunction(formatDocumentRangesWithSelectedProvider, editorOrModel, ranges, FormattingMode.Silent, nestedProgress, token);
}
}
}
}
......
......@@ -360,8 +360,18 @@ configurationRegistry.registerConfiguration({
...editorConfigurationBaseNode,
properties: {
'editor.formatOnSave': {
'type': 'boolean',
'default': false,
'type': 'string',
'default': 'off',
'enum': [
'off',
'file',
'modifications'
],
'enumDescriptions': [
nls.localize('off', "Disable format on save."),
nls.localize('everything', "Format the whole file on save."),
nls.localize('modification', "Only format modifications (requires source control)."),
],
'description': nls.localize('formatOnSave', "Format a file on save. A formatter must be available, the file must not be saved after delay, and the editor must not be shutting down."),
scope: ConfigurationScope.LANGUAGE_OVERRIDABLE,
}
......
......@@ -5,4 +5,4 @@
import './formatActionsMultiple';
import './formatActionsNone';
import './formatChanges';
import './formatModified';
......@@ -5,84 +5,79 @@
import { isNonEmptyArray } from 'vs/base/common/arrays';
import { CancellationToken } from 'vs/base/common/cancellation';
import { isEqualOrParent } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
import { EditorAction, registerEditorAction, ServicesAccessor } from 'vs/editor/browser/editorExtensions';
import { Range } from 'vs/editor/common/core/range';
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
import { ITextModel } from 'vs/editor/common/model';
import { IEditorWorkerService } from 'vs/editor/common/services/editorWorkerService';
import { ITextModelService } from 'vs/editor/common/services/resolverService';
import { formatDocumentRangesWithSelectedProvider, FormattingMode } from 'vs/editor/contrib/format/format';
import * as nls from 'vs/nls';
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
import { ISCMProvider, ISCMService } from 'vs/workbench/contrib/scm/common/scm';
import { Progress } from 'vs/platform/progress/common/progress';
import { getOriginalResource } from 'vs/workbench/contrib/scm/browser/dirtydiffDecorator';
import { ISCMService } from 'vs/workbench/contrib/scm/common/scm';
registerEditorAction(class FormatChangedLinesAction extends EditorAction {
registerEditorAction(class FormatModifiedAction extends EditorAction {
constructor() {
super({
id: 'editor.action.formatChanges',
label: nls.localize('formatChanges', "Format Changed Lines"),
alias: 'Format Document...',
label: nls.localize('formatChanges', "Format Modified Lines"),
alias: 'Format Modified Lines',
precondition: ContextKeyExpr.and(EditorContextKeys.writable, EditorContextKeys.hasDocumentSelectionFormattingProvider),
});
}
async run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
const scmService = accessor.get(ISCMService);
const workerService = accessor.get(IEditorWorkerService);
const modelService = accessor.get(ITextModelService);
const instaService = accessor.get(IInstantiationService);
if (!editor.hasModel()) {
return;
}
const modified = editor.getModel().uri;
const provider = this._getBestProvider(scmService, modified);
if (!provider) {
return;
const ranges = await instaService.invokeFunction(getModifiedRanges, editor.getModel());
if (isNonEmptyArray(ranges)) {
return instaService.invokeFunction(
formatDocumentRangesWithSelectedProvider, editor, ranges,
FormattingMode.Explicit, Progress.None, CancellationToken.None
);
}
}
});
const original = await provider.getOriginalResource(modified);
export async function getModifiedRanges(accessor: ServicesAccessor, modified: ITextModel): Promise<Range[] | undefined> {
const scmService = accessor.get(ISCMService);
const workerService = accessor.get(IEditorWorkerService);
const modelService = accessor.get(ITextModelService);
const original = await getOriginalResource(scmService, modified.uri);
if (!original) {
return;
return undefined;
}
const ranges: Range[] = [];
const ref = await modelService.createModelReference(original);
try {
if (workerService.canComputeDirtyDiff(original, modified)) {
const changes = await workerService.computeDirtyDiff(original, modified, true);
if (isNonEmptyArray(changes)) {
if (!workerService.canComputeDirtyDiff(original, modified.uri)) {
return undefined;
}
const changes = await workerService.computeDirtyDiff(original, modified.uri, true);
if (!isNonEmptyArray(changes)) {
return undefined;
}
for (let change of changes) {
ranges.push(editor.getModel().validateRange(new Range(
ranges.push(modified.validateRange(new Range(
change.modifiedStartLineNumber, 1,
change.modifiedEndLineNumber || change.modifiedStartLineNumber /*endLineNumber is 0 when things got deleted*/, Number.MAX_SAFE_INTEGER)
));
}
}
}
} finally {
ref.dispose();
}
if (ranges.length > 0) {
return instaService.invokeFunction(
formatDocumentRangesWithSelectedProvider, editor, ranges,
FormattingMode.Explicit, CancellationToken.None
);
}
}
private _getBestProvider(scmService: ISCMService, uri: URI): ISCMProvider | undefined {
for (let repo of scmService.repositories) {
if (repo.provider.rootUri && isEqualOrParent(uri, repo.provider.rootUri)) {
return repo.provider;
}
}
return undefined;
}
});
return ranges;
}
......@@ -999,6 +999,22 @@ function createProviderComparer(uri: URI): (a: ISCMProvider, b: ISCMProvider) =>
};
}
export async function getOriginalResource(scmService: ISCMService, uri: URI): Promise<URI | null> {
const providers = scmService.repositories.map(r => r.provider);
const rootedProviders = providers.filter(p => !!p.rootUri);
rootedProviders.sort(createProviderComparer(uri));
const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri)));
if (result) {
return result;
}
const nonRootedProviders = providers.filter(p => !p.rootUri);
return first(nonRootedProviders.map(p => () => p.getOriginalResource(uri)));
}
export class DirtyDiffModel extends Disposable {
private _originalModel: IResolvedTextFileEditorModel | null = null;
......@@ -1155,19 +1171,7 @@ export class DirtyDiffModel extends Disposable {
}
const uri = this._model.resource;
const providers = this.scmService.repositories.map(r => r.provider);
const rootedProviders = providers.filter(p => !!p.rootUri);
rootedProviders.sort(createProviderComparer(uri));
const result = await first(rootedProviders.map(p => () => p.getOriginalResource(uri)));
if (result) {
return result;
}
const nonRootedProviders = providers.filter(p => !p.rootUri);
return first(nonRootedProviders.map(p => () => p.getOriginalResource(uri)));
return getOriginalResource(this.scmService, uri);
}
findNextClosestChange(lineNumber: number, inclusive = true): number {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册