Repository: vscode
Issue: Code completion unexpectedly consumes closing paren (from auto-closing-brackets) that was inserted automatically when `(` triggered code completion
Labels: bug,suggest

I'm unable to reproduce this, but a user was able to provide an LSP log that makes it seem like a VS Code issue. If not, I think it at least needs some direction to help debug.

Here's what's happening:

- The user types `foo(`
- The `(` triggers code completion (because it's a trigger character) but also causes a `)` to be inserted automatically (auto closing brackets)
- The user then types `ba` and selects `bar` from the completion list
- VS Code replaces `ba)` with `bar`, instead of only `ba`

It seems like the automatically-inserted `)` is being compensated for by VS Code as if it was a character typed since the completion request was sent, so the replacement range VS Code computes is 3 characters long (`)`, `b`, `a`) but should only be two (since the `)` is on the other side of the cursor).

Here's the LSP log:

```

// User types the opening paren (
[18:47:31] [Analyzer] [Info] ==> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/s-katoh/development/playground/sample_app/lib/main.dart","version":68},"contentChanges":[{"range":{"start":{"line":9,"character":9},"end":{"line":9,"character":9}},"rangeLength":0,"text":"("}]}}

// Code completion is triggered because ( is a trigger character
[18:47:31] [Analyzer] [Info] ==> {"jsonrpc":"2.0","id":162,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///Users/s-katoh/development/playground/sample_app/lib/main.dart"},"position":{"line":9,"character":10},"context":{"triggerKind":2,"triggerCharacter":"("}}}

// A closing paren ) is also inserted (this happens after the completion request is sent)
[18:47:31] [Analyzer] [Info] ==> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/s-katoh/development/playground/sample_app/lib/main.dart","version":69},"contentChanges":[{"range":{"start":{"line":9,"character":10},"end":{"line":9,"character":10}},"rangeLength":0,"text":")"}]}}

// The server responds with "bar" which has a text edit at line 9 character 10 and a length of 0 (the entire thing will be inserted, as the user has not typed any of "bar" yet). This location corresponds to _between_ the parens since the closing paren ) was also inserted at 9:10.
[18:47:31] [Analyzer] [Info] <== {"id":162,"jsonrpc":"2.0","result":[{"label":"bar","kind":5,"detail":"bool","sortText":"999442","textEdit":{"range":{"start":{"line":9,"character":10},"end":{"line":9,"character":10}},"newText":"bar"}}

// The server is asked to resolve the completion item (it does, with no change to the range)
[18:47:32] [Analyzer] [Info] ==> {"jsonrpc":"2.0","id":168,"method":"completionItem/resolve","params":{"label":"bar","detail":"bool","insertTextFormat":1,"textEdit":{"newText":"bar","range":{"start":{"line":9,"character":10},"end":{"line":9,"character":10}}},"kind":5,"sortText":"999442"}}
[18:47:32] [Analyzer] [Info] <== {"id":168,"jsonrpc":"2.0","result":{"label":"bar","kind":5,"detail":"bool","sortText":"999442","insertTextFormat":1,"textEdit":{"range":{"start":{"line":9,"character":10},"end":{"line":9,"character":10}},"newText":"bar"}}}

// The user types "ba"
[18:47:33] [Analyzer] [Info] ==> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/s-katoh/development/playground/sample_app/lib/main.dart","version":70},"contentChanges":[{"range":{"start":{"line":9,"character":10},"end":{"line":9,"character":10}},"rangeLength":0,"text":"b"}]}}
[18:47:33] [Analyzer] [Info] ==> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/s-katoh/development/playground/sample_app/lib/main.dart","version":71},"contentChanges":[{"range":{"start":{"line":9,"character":11},"end":{"line":9,"character":11}},"rangeLength":0,"text":"a"}]}}

// User user completes "bar", which should be inserted at 9:10 but compensate for the "ba" they typed, making a replace range of 2 - however the range is 3 (as if the closing ) was somehow compensated for
[18:47:33] [Analyzer] [Info] ==> {"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"file:///Users/s-katoh/development/playground/sample_app/lib/main.dart","version":72},"contentChanges":[{"range":{"start":{"line":9,"character":10},"end":{"line":9,"character":13}},"rangeLength":3,"text":"bar"}]}}
```

Based on the log, it seems like the server has behaved correctly. It wants to insert "bar" at 9:10 and there's no reason for the `)` to be replaced. Although it was typed after the completion request was sent, it was inserted automatically after the cursor and should be excluded from the range compensation (although in my testing, it seems like it is, because everything works as expected).

I don't know of any way to debug this further - is there any way to get more information from VS Code about how its compensating for typing during async completion requests?