vysker / vscode-php-formatter

Visual Studio Code extension. A wrapper for the Sensiolabs PHP CS Fixer. Analyzes some PHP source code and tries to fix coding standards issues (PSR-1 and PSR-2 compatible).
MIT License
94 stars 11 forks source link

File is marked dirty after formatOnSave #12

Closed vysker closed 7 years ago

vysker commented 7 years ago

Plugging @jrieken here (sorry), because I'm wondering how to fix this issue I'm having with the extension.

I recently adopted the new DocumentFormattingEditProvider API to register my extension for the editor.formatOnSave functionality. However, when a file is formatted using formatOnSave, the document will be marked as dirty after formatting. The only time when saving does work, is when I close the document when it is dirty and it prompts me to "Save, Don't save, or Cancel".

It's most likely a matter of returning the proper value, or changing the order of execution in the code, but I can't figure out what.

This is how the code abstractly works:

  1. In activate(), a class implementing DocumentFormattingEditProvider is registered.
  2. The DocumentFormattingEditProvider calls the formatDocument method, which returns Thenable<TextEdit[]>, or null depending on whether changes were made.

The crucial bit happens here:

Link to code

return new Promise((resolve, reject) => {
    // [...] Magic happens here

    let textEditResult: Thenable<boolean> =
        window.activeTextEditor.edit(
            (textEditorEdit: TextEditorEdit) => {
                textEditorEdit.replace(editRange, fixedContent);
            });

    textEditResult.then(
        (success) => {
            if (success) {
                let message: string = 'Document successfully formatted (' + numSelectedLines + ' lines).';
                Helper.logDebug(message);

                let textEdits: TextEdit[] = [];
                textEdits.push(TextEdit.replace(editRange, fixedContent));
                return resolve(textEdits);
            } else {
                let message: string = 'Document failed to format (' + numSelectedLines + ' lines) [from success promise].';
                Helper.logDebug(message);
                return reject(message);
            }
        }, (reason) => {
            let message: string = 'Document failed to format (' + numSelectedLines + ' lines) [from reason promise].';
            Helper.logDebug(message);
            return reject(message);
        });
}

Do you have any idea as to what I'm doing wrong here?

Thanks in advance!

jrieken commented 7 years ago

Well, you are also modifying the document yourself by calling activeTextEditor.edit. Don't do that, simply return those edits from the provide-call.

jrieken commented 7 years ago

Also, don't do that but implement DocumentRangeFormatter which contains the current selections if available - see here and here

vysker commented 7 years ago

So, I implemented the DocumentRangeFormatter and got rid of the activeTextEditor.edit, like you rightfully suggested. Right now, the only thing I return is:

let textEdits: TextEdit[] = [];
textEdits.push(TextEdit.replace(editRange, fixedContent));
return resolve(textEdits);

The DocumentRangeFormatter definitely receives the TextEdit array. I checked that:

phpfmt_provider

And yet, nothing happens. Should I be doing anything with them afterwards? Does the subscription handle the rest?

export function activate(context: ExtensionContext): void {
    context.subscriptions.push(
        languages.registerDocumentRangeFormattingEditProvider(
            { language: 'php', scheme: 'file' },
            new PHPDocumentRangeFormattingEditProvider()
        )
    );
}
vysker commented 7 years ago

Nevermind, got it working!

For some reason I did:

public provideDocumentRangeFormattingEdits(document: TextDocument, range: Range, options: FormattingOptions, token: CancellationToken): Thenable<TextEdit[]> {
    return document.save().then(() => { // <--- I don't know why I did this
        return this.formatter.formatDocument(document, range);
    }
}

I don't know where I got that document.save() part from. But after removing that, everything is in working order.

Thank you for your time!