Provide an option to insert a final newline (fixes #1666)

This commit is contained in:
Benjamin Pasero
2016-11-02 07:33:57 +01:00
parent 1437e44ce4
commit f9cbe207a3
2 changed files with 77 additions and 24 deletions

View File

@@ -7,6 +7,7 @@
import { TPromise } from 'vs/base/common/winjs.base';
import { sequence } from 'vs/base/common/async';
import * as strings from 'vs/base/common/strings';
import { ICodeEditorService } from 'vs/editor/common/services/codeEditorService';
import { IThreadService } from 'vs/workbench/services/thread/common/threadService';
import { ISaveParticipant, ITextFileEditorModel, SaveReason } from 'vs/workbench/services/textfile/common/textfiles';
@@ -21,6 +22,7 @@ import { EditOperationsCommand } from 'vs/editor/contrib/format/common/formatCom
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { TextFileEditorModel } from 'vs/workbench/services/textfile/common/textFileEditorModel';
import { ExtHostContext, ExtHostDocumentSaveParticipantShape } from './extHost.protocol';
import { EditOperation } from 'vs/editor/common/core/editOperation';
interface INamedSaveParticpant extends ISaveParticipant {
readonly name: string;
@@ -47,29 +49,18 @@ class TrimWhitespaceParticipant implements INamedSaveParticpant {
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
const cursors: IPosition[] = [];
// Find `prevSelection` in any case do ensure a good undo stack when pushing the edit
// Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump
if (model.isAttachedToEditor()) {
const allEditors = this.codeEditorService.listCodeEditors();
for (let i = 0, len = allEditors.length; i < len; i++) {
const editor = allEditors[i];
const editorModel = editor.getModel();
if (!editorModel) {
continue; // empty editor
}
if (model === editorModel) {
prevSelection = editor.getSelections();
if (isAutoSaved) {
cursors.push(...prevSelection.map(s => {
return {
lineNumber: s.positionLineNumber,
column: s.positionColumn
};
}));
}
}
let editor = findEditor(model, this.codeEditorService);
if (editor) {
// Find `prevSelection` in any case do ensure a good undo stack when pushing the edit
// Collect active cursors in `cursors` only if `isAutoSaved` to avoid having the cursors jump
prevSelection = editor.getSelections();
if (isAutoSaved) {
cursors.push(...prevSelection.map(s => {
return {
lineNumber: s.positionLineNumber,
column: s.positionColumn
};
}));
}
}
@@ -82,6 +73,62 @@ class TrimWhitespaceParticipant implements INamedSaveParticpant {
}
}
function findEditor(model: IModel, codeEditorService: ICodeEditorService): ICommonCodeEditor {
if (model.isAttachedToEditor()) {
const allEditors = codeEditorService.listCodeEditors();
for (let i = 0, len = allEditors.length; i < len; i++) {
const editor = allEditors[i];
const editorModel = editor.getModel();
if (!editorModel) {
continue; // empty editor
}
if (model === editorModel) {
return editor;
}
}
}
return null;
}
class FinalNewLineParticipant implements INamedSaveParticpant {
readonly name = 'FinalNewLineParticipant';
constructor(
@IConfigurationService private configurationService: IConfigurationService,
@ICodeEditorService private codeEditorService: ICodeEditorService
) {
// Nothing
}
public participate(model: ITextFileEditorModel, env: { reason: SaveReason }): any {
if (this.configurationService.lookup('files.insertFinalNewline').value) {
this.doInsertFinalNewLine(model.textEditorModel);
}
}
private doInsertFinalNewLine(model: IModel): void {
const lineCount = model.getLineCount();
const lastLine = model.getLineContent(lineCount);
const lastLineIsEmptyOrWhitespace = strings.lastNonWhitespaceIndex(lastLine) === -1;
if (lineCount <= 1 || lastLineIsEmptyOrWhitespace) {
return;
}
let prevSelection: Selection[] = [new Selection(1, 1, 1, 1)];
const editor = findEditor(model, this.codeEditorService);
if (editor) {
prevSelection = editor.getSelections();
}
model.pushEditOperations(prevSelection, [EditOperation.insert({ lineNumber: lineCount + 1, column: 0 }, model.getEOL())], (edits) => prevSelection);
}
}
class FormatOnSaveParticipant implements INamedSaveParticpant {
readonly name = 'FormatOnSaveParticipant';
@@ -204,6 +251,7 @@ export class SaveParticipant implements ISaveParticipant {
this._saveParticipants = [
instantiationService.createInstance(TrimWhitespaceParticipant),
instantiationService.createInstance(FinalNewLineParticipant),
instantiationService.createInstance(FormatOnSaveParticipant),
instantiationService.createInstance(ExtHostSaveParticipant)
];