LSP: Fix language server desynchronisation when whole file contents change

When the whole file contents are changed, for example because of changing
the highlight definition when there is several candidates, the language
server will behave as if the file's content was the result of
concatenating two times the file's content (leading to duplicate
declarations errors for C++).

The issue is that when this happens, the didChange message indicate
only the added portion of the change without the removal of the whole file
beforehand (ie: the range is empty and the text is the whole file content).

This is because charRemoved and charAdded are equal to the length of the
document which is not exactly the number of /visibles/ characters, but also
including some invisible formating characters according to QTBUG-80662.

This causes the QTextCursor to ignore the second setPosition because of
invalid position out of range.

Fix this using the same code as QWidgetTextControlPrivate::_q_contentsChanged
that is to use qMin to ensure the end position is not after the end of
the document according to QTextCursor.
For charsAdded, document->textAt takes care of that itself, via
Utils::Text::textAt.

See also: QTBUG-32583, QTBUG-80662

Change-Id: If781707d42253f4659005754efa73d872577f738
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Alexis Murzeau
2020-04-26 22:35:26 +02:00
parent e430bfa05a
commit 6118463d39

View File

@@ -506,7 +506,14 @@ void Client::documentContentsChanged(TextEditor::TextDocument *document,
DidChangeTextDocumentParams::TextDocumentContentChangeEvent change;
QTextDocument oldDoc(m_openedDocument[document]);
QTextCursor cursor(&oldDoc);
cursor.setPosition(position + charsRemoved);
// Workaround https://bugreports.qt.io/browse/QTBUG-80662
// The contentsChanged gives a character count that can be wrong for QTextCursor
// when there are special characters removed/added (like formating characters).
// Also, characterCount return the number of characters + 1 because of the hidden
// paragraph separator character.
// This implementation is based on QWidgetTextControlPrivate::_q_contentsChanged.
// For charsAdded, textAt handles the case itself.
cursor.setPosition(qMin(oldDoc.characterCount() - 1, position + charsRemoved));
cursor.setPosition(position, QTextCursor::KeepAnchor);
change.setRange(Range(cursor));
change.setRangeLength(cursor.selectionEnd() - cursor.selectionStart());