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

Position of the cursor after sending []TextEdit through provideOnTypeFormattingEdits #3088

Closed
yageek opened this issue Feb 17, 2016 · 10 comments
Assignees

Comments

@yageek
Copy link

yageek commented Feb 17, 2016

Would it be possible to reopen this ticket ? I think I am experiencing the same problem but with
provideOnTypeFormattingEdits.

I would like to automatically add // after the user has returned Enter.

To do so, I subscribe with registerOnTypeFormattingEditProvider and I provide a listener with this implementation:

'use strict';

import { OnTypeFormattingEditProvider, TextDocument, Position, FormattingOptions, CancellationToken, TextEdit , Range} from 'vscode';

export class GoCommentsProvider implements OnTypeFormattingEditProvider {

    public provideOnTypeFormattingEdits(document: TextDocument, position: Position, ch: string, options: FormattingOptions, token: CancellationToken): Thenable<TextEdit[]>{

       //Detect if we are in a case of comments
        return new Promise((resolve, reject) => {

            let previousLine = document.lineAt(position.line - 1);

            if (previousLine.text.startsWith("//")) {
                let commentedAt = TextEdit.insert(position, "//");
                resolve([commentedAt]);
            }
        });
    } 
}

The problem here is that the final position of the cursor is the following:

// First line of comment -> Press entry
|//  Cursor is located before the addition

What I would like to achieve:

// First line of comment -> Press entry
// |  Cursor is located after the addition

EDIT: I think it is related to #755

@alexdima
Copy link
Member

@yageek IMHO this does not really fit a format edit. A format edit should do transformations that do not affect the program logic (mostly adding/removing whitespace). Maybe what you want can be achieved (a lot easier) with onEnterRules?:

languages.setLanguageConfiguration(modeID, {
    onEnterRules: [
        {
            beforeText: /^\s*\/\/.*$/,
            action: { indentAction: IndentAction.None, appendText: '// ' }
        }
    ]
});

onenter

@yageek
Copy link
Author

yageek commented Feb 22, 2016

Could be. I saw in the API that there is also an afterText condition. Is it possible to use both conditions at the same time ?

I would like to perform this addition but only if the next line contains some specific keywords.
May be I'm too bad at Regex, but I can not manage to find the appropriate afterText Regex.

For example, with a try for the type keyword, I put:
afterText: /\s*\ntype.*/g,

but nothing is appended to the source code.

@alexdima
Copy link
Member

The afterText is just about matching the text on the current line after the cursor position:

var x = 3|45;
// beforeText: "var x = 3"
// afterText; "45;"

I mean those are the values that are matched against the regexes

@alexdima
Copy link
Member

If your logic doesn't match the onEnterRules, you could alternatively listen to onDidChangeTextDocument, figure out if the changed document is the one in the activeTextEditor and then use activeTextEditor.edit(() => ...).then(() => activeTextEditor.selection = ...)

@alexdima
Copy link
Member

Closing this for now as it is unclear to me what is the use case. If none of our concepts fit the use case, an extension could register a keybinding rule for 'Enter' and do all the logic for 'Enter' on its own.

@siegebell
Copy link

@alexandrudima Here's a use-case (it's been requested by users of vscoq and is a feature in the equivalent emacs extension; language: coq):

Before:

    *      tactic.|

After

    *      tactic.
           |

Effect: the cursor is indented to a position that is dependent on the previous line.

@siegebell
Copy link

@alexandrudima another use-case:
Before:

// comment
//     indented|

After:

// comment
//     indented
//     |

For this case, onEnterRules could be extended to support matching groups like find/replace currently does...

languages.setLanguageConfiguration(modeID, {
    onEnterRules: [
        { // insert comment on new line while preserving the indent
            beforeText: /^\s*(\/\/\s*)\S.*$/,
            action: { indentAction: IndentAction.None, appendText: '${1}' }
        }
    ]
});

@siegebell
Copy link

And to support the use-case for aligning to the tactic after a bullet, appendText could accept a function that takes a RegExpExecArray as an argument:

onEnterRules: [
    { // align indent to prior-line's tactic
        beforeText: /^\s*(\*\s*)\S.*$/,
        action: { indentAction: vscode.IndentAction.None, appendText: (beforeMatches) => ' '.repeat(beforeMatches[1].length) }
    }      
]

@alexdima
Copy link
Member

@siegebell I found the idea to support capture groups really good. I've opened a new issue #17281 to track this. The idea to support a callback is IMHO unrealistic because the callback is defined in the extension host process and the handling of pressing enter and of the rules is running on the renderer process (i.e. cannot invoke appendText synchronously, as part of handling the Enter keypress)

@siegebell
Copy link

@alexandrudima I agree - It seems to me that the existence of onEnterRules is to facilitate a snappy text-editing experience, so if it had to wait for a Thenable callback there would be no advantage over onDidChangeTextDocument.

That said, here's another use-case (3):

const x =   'some ' +
            'long ' +
            'string ';

and (4)

const x = 'some '
        + 'long '
        + 'string ';

Maybe there needs to be a more flexible way to specify an alignment point. For example:

onEnterRules: [
    { // case (3): align indent to after the prior-line's = or + if it is not terminated by ';' or is terminated by +, ||, or &&
        beforeText: /^.*(?:=|\+)\s*.*(?:[^;]|[+]|\|\||&&)$/,
        action: { indentAction: vscode.IndentAction.Align, alignTo: /^.*=\s*(.)/ }
    },
    { // case (4): align indent to prior-line's = or + if it is terminated by a string and no ';'
        beforeText: /^.*(?:=|\+)\s*.*['"`]\s*$/,
        action: { indentAction: vscode.IndentAction.Align, alignTo: /=\+/, appendText: '+ ' }
    },
    { // case (1): align indent to prior-line's tactic
        beforeText: /^\s*\*+\s*\S.*$/,
        action: { indentAction: vscode.IndentAction.Align, alignTo: /^\s*\*+\s(\S)/ }
    }      
]

The behavior of alignTo: x would be to either indent to the point where x is first matched or to the beginning of the first matched group if x is a regular expression with capturing groups.

@vscodebot vscodebot bot locked and limited conversation to collaborators Nov 18, 2017
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants