-
Notifications
You must be signed in to change notification settings - Fork 91
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
Finalizes incremental settings #359
Finalizes incremental settings #359
Conversation
88d04f9
to
b2a76d8
Compare
org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/commons/TextDocuments.java
Outdated
Show resolved
Hide resolved
org.eclipse.lsp4xml/src/test/java/org/eclipse/lsp4xml/services/IncrementalParsingTest.java
Outdated
Show resolved
Hide resolved
b2a76d8
to
785554e
Compare
@fbricon updated |
785554e
to
619c9b5
Compare
Current issues, a large file will break incremental parsing. If many characters are typed quickly the document on the server side ends up misrepresenting the actual document. Difficult to debug this. |
@NikolasKomonen please see #369 which is a PR just with an idea which could fix perhaps bug with large file. Not sure it works well, but this PR is just to open discussion |
619c9b5
to
3a311e2
Compare
Nick i seen that you integrated my work . could you give me feedback please if it fix your problem which is difficult to debug. Thanks |
@angelozerr I've been testing it on a file with 140,000 lines and it seems to be working properly. There are no more synchronization issues. The file, nasa.xml, in src/test/resources/xml will crash the extension. I'm not sure if that is on VSCode or lsp4xml. Otherwise, from a regular use case it looks good to me for now. |
Thanks for your feedback. Could you explain more the use cases which crashes vscode please. Imho we need to improve performance and or memory problem because i see more and more issues about that |
For the nasa.xml file, while waiting for initialization of the server to finish VS and return to the client side I get an error from VSCode "Extension host terminated unexpectedly" (I've set the server memory to 4GB by the way) Error:
From other issues I've looked at it could be to do with node and |
Looks like vscode doesn't have enough memory to open the document, if you can reproduce that after disabling all the vscode extensions, then you should open a ticket in https://github.com/Microsoft/vscode/issues. |
Per @fbricon 's recommendation, I tested the nasa.xml file with the HTML language server and that had the same error with the HTMLLS crashing too. |
3a311e2
to
b986420
Compare
cbc7776
to
d9390c5
Compare
org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/commons/TextDocument.java
Outdated
Show resolved
Hide resolved
org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/commons/TextDocuments.java
Outdated
Show resolved
Hide resolved
...clipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/settings/XMLIncrementalSupportSettings.java
Show resolved
Hide resolved
@@ -139,6 +139,8 @@ public void update(List<TextDocumentContentChangeEvent> changes) { | |||
try { | |||
synchronized (buffer) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove buffer field because the TextDocument stores 2 xml content in this case (buffer and text field). It's not good for memory (2 XML content stored). So the idea is to create StringBuilder instance from the current text in the update method to benefit with StringBuilder#replace method. and synchronized to a lock field instance (not to the buffer field which must be removed)
private final Object lock = new Object();
...
public void setIncremental(boolean incremental) {
this.incremental = incremental;
}
public boolean isIncremental() {
return incremental;
}
...
public void update(List<TextDocumentContentChangeEvent> changes) {
if (isIncremental()) {
try {
StringBuilder buffer = new StringBuilder(getText());
synchronized (lock) {
...
An another change is to set lineTracker as final because the call of getLineTracker can be created several time when there are several threads which are called it in the same time, we could use synchronized but we need not that because lineTracker.set exists as methods.
private final ListLineTracker lineTracker;
public TextDocument(String text, String uri) {
this.lineTracker = new ListLineTracker();
super.setUri(uri);
setText(text);
}
...
@Override
public void setText(String text) {
super.setText(text);
lineTracker.set(text);
}
Here the final TextDocument with those 2 changes:
/**
* Copyright (c) 2018 Angelo ZERR.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* Angelo Zerr <[email protected]> - initial API and implementation
*/
package org.eclipse.lsp4xml.commons;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
import org.eclipse.lsp4j.TextDocumentItem;
/**
* Text document extends LSP4j {@link TextDocumentItem} to provide methods to
* retrieve position.
*
*/
public class TextDocument extends TextDocumentItem {
private static String DEFAULT_DELIMTER = System.lineSeparator();
private final ListLineTracker lineTracker;
private boolean incremental;
private final Object lock = new Object();
public TextDocument(TextDocumentItem document) {
this(document.getText(), document.getUri());
super.setVersion(document.getVersion());
super.setLanguageId(document.getLanguageId());
}
public TextDocument(String text, String uri) {
this.lineTracker = new ListLineTracker();
super.setUri(uri);
setText(text);
}
public void setIncremental(boolean incremental) {
this.incremental = incremental;
}
public boolean isIncremental() {
return incremental;
}
@Override
public void setText(String text) {
super.setText(text);
lineTracker.set(text);
}
public Position positionAt(int position) throws BadLocationException {
ListLineTracker lineTracker = getLineTracker();
return lineTracker.getPositionAt(position);
}
public int offsetAt(Position position) throws BadLocationException {
ListLineTracker lineTracker = getLineTracker();
return lineTracker.getOffsetAt(position);
}
public String lineText(int lineNumber) throws BadLocationException {
ListLineTracker lineTracker = getLineTracker();
Line line = lineTracker.getLineInformation(lineNumber);
String text = super.getText();
return text.substring(line.offset, line.offset + line.length);
}
public String lineDelimiter(int lineNumber) throws BadLocationException {
ListLineTracker lineTracker = getLineTracker();
String lineDelimiter = lineTracker.getLineDelimiter(lineNumber);
if (lineDelimiter == null) {
if (lineTracker.getNumberOfLines() > 0) {
lineDelimiter = lineTracker.getLineInformation(0).delimiter;
}
}
if (lineDelimiter == null) {
lineDelimiter = DEFAULT_DELIMTER;
}
return lineDelimiter;
}
public Range getWordRangeAt(int textOffset, Pattern wordDefinition) {
try {
Position pos = positionAt(textOffset);
ListLineTracker lineTracker = getLineTracker();
Line line = lineTracker.getLineInformation(pos.getLine());
String text = super.getText();
String lineText = text.substring(line.offset, textOffset);
int position = lineText.length();
Matcher m = wordDefinition.matcher(lineText);
int currentPosition = 0;
while (currentPosition != position) {
if (m.find()) {
currentPosition = m.end();
if (currentPosition == position) {
return new Range(new Position(pos.getLine(), m.start()), pos);
}
} else {
currentPosition++;
}
m.region(currentPosition, position);
}
return new Range(pos, pos);
} catch (BadLocationException e) {
return null;
}
}
private ListLineTracker getLineTracker() {
return lineTracker;
}
/**
* Update text of the document by using the changes and according the
* incremental support.
*
* @param changes the text document changes.
*/
public void update(List<TextDocumentContentChangeEvent> changes) {
if (changes.size() < 1) {
// no changes, ignore it.
return;
}
if (isIncremental()) {
try {
synchronized (lock) {
// Initialize buffer and line tracker from the current text document
String initialText = getText();
StringBuilder buffer = new StringBuilder(initialText);
ListLineTracker lt = new ListLineTracker();
lt.set(initialText);
// Loop for each changes and update the buffer
for (int i = 0; i < changes.size(); i++) {
if (i > 0) {
lt.set(buffer.toString());
}
TextDocumentContentChangeEvent changeEvent = changes.get(i);
Range range = changeEvent.getRange();
int length = 0;
if (range != null) {
length = changeEvent.getRangeLength().intValue();
} else {
// range is optional and if not given, the whole file content is replaced
length = buffer.length();
range = new Range(lt.getPositionAt(0), lt.getPositionAt(length));
}
String text = changeEvent.getText();
int startOffset = lt.getOffsetAt(range.getStart());
buffer.replace(startOffset, startOffset + length, text);
}
// Update the new text content from the updated buffer
setText(buffer.toString());
}
} catch (BadLocationException e) {
// Should never occurs.
}
} else {
// like vscode does, get the last changes
// see
// https://github.com/Microsoft/vscode-languageserver-node/blob/master/server/src/main.ts
TextDocumentContentChangeEvent last = changes.size() > 0 ? changes.get(changes.size() - 1) : null;
if (last != null) {
setText(last.getText());
}
}
}
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@NikolasKomonen are you sure that you have done the update of TextDocument like I suggest below?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@angelozerr I'm getting offset issues with the changes. Did you try running tests with your implementation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it should work. What is your test (and traces) which failed ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My failures are that it "Couldnt get the position at Offset", and BadLocation errors.
I pushed the changes to this PR, I'm sorry if I missed anything from your changes.
d9390c5
to
11843bf
Compare
org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/commons/TextDocument.java
Outdated
Show resolved
Hide resolved
org.eclipse.lsp4xml/src/main/java/org/eclipse/lsp4xml/commons/TextDocument.java
Outdated
Show resolved
Hide resolved
11843bf
to
0abccf3
Compare
Fixes eclipse#133 Incremental sync is off by default preference to turn on is: "xml.experimental.incrementalSupport.enabled": true|false Signed-off-by: Nikolas <[email protected]>
0abccf3
to
4604224
Compare
Fixes #133