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

fixes multiple problems with split / preview editors #241

Merged
merged 9 commits into from
Feb 17, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ private static synchronized LanguageServerWrapper updateLanguageWrapperContainer
* @param editor the editor.
*/
public static void editorClosed(Editor editor) {
VirtualFile file = FileDocumentManager.getInstance().getFile(editor.getDocument());
VirtualFile file = FileUtils.virtualFileFromEditor(editor);
if (!FileUtils.isFileSupported(file)) {
LOG.debug("Handling close on a editor which host a LightVirtual/Null file");
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,8 @@ public void telemetryEvent(Object o) {
public void publishDiagnostics(PublishDiagnosticsParams publishDiagnosticsParams) {
String uri = FileUtils.sanitizeURI(publishDiagnosticsParams.getUri());
List<Diagnostic> diagnostics = publishDiagnosticsParams.getDiagnostics();
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
if (manager != null) {
Set<EditorEventManager> managers = EditorEventManagerBase.managersForUri(uri);
for (EditorEventManager manager: managers) {
manager.diagnostics(diagnostics);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
* An enum representing a server status
*/
public enum ServerStatus {
STOPPED, STARTING, STARTED, INITIALIZED
STOPPED, STARTING, STARTED, INITIALIZED, STOPPING
}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import com.intellij.openapi.application.ex.ApplicationUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.fileEditor.FileEditorManager;
import com.intellij.openapi.progress.ProcessCanceledException;
import com.intellij.openapi.progress.ProgressIndicatorProvider;
import com.intellij.openapi.vfs.VirtualFile;
Expand Down Expand Up @@ -89,9 +90,9 @@ public boolean invokeAutoPopup(@NotNull PsiElement position, char typeChar) {
return false;
}

String uri = FileUtils.VFSToURI(file);
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
if (manager == null) {
Editor editor = FileEditorManager.getInstance(position.getProject()).getSelectedTextEditor();
EditorEventManager manager = EditorEventManagerBase.forEditor(editor);
if (editor == null || manager == null) {
return false;
}
for (String triggerChar : manager.completionTriggers) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.wso2.lsp4intellij.IntellijLanguageClient;
import org.wso2.lsp4intellij.client.languageserver.ServerStatus;
import org.wso2.lsp4intellij.client.languageserver.wrapper.LanguageServerWrapper;
import org.wso2.lsp4intellij.editor.EditorEventManager;
import org.wso2.lsp4intellij.editor.EditorEventManagerBase;
import org.wso2.lsp4intellij.utils.DocumentUtils;
Expand All @@ -52,11 +54,14 @@ public Object collectInformation(@NotNull PsiFile file, @NotNull Editor editor,
if (!FileUtils.isFileSupported(virtualFile) || !IntellijLanguageClient.isExtensionSupported(virtualFile)) {
return null;
}
String uri = FileUtils.VFSToURI(virtualFile);
EditorEventManager eventManager = EditorEventManagerBase.forUri(uri);
EditorEventManager eventManager = EditorEventManagerBase.forEditor(editor);

if (eventManager == null) {
return null;
}

// If the diagnostics list is locked, we need to skip annotating the file.
if (eventManager == null || !(eventManager.isDiagnosticSyncRequired() || eventManager.isCodeActionSyncRequired())) {
if (!(eventManager.isDiagnosticSyncRequired() || eventManager.isCodeActionSyncRequired())) {
return null;
}
return RESULT;
Expand All @@ -74,13 +79,15 @@ public Object doAnnotate(Object collectedInfo) {
@Override
public void apply(@NotNull PsiFile file, Object annotationResult, @NotNull AnnotationHolder holder) {

if (LanguageServerWrapper.forVirtualFile(file.getVirtualFile(), file.getProject()).getStatus() != ServerStatus.INITIALIZED) {
return;
}

VirtualFile virtualFile = file.getVirtualFile();
if (FileUtils.isFileSupported(virtualFile) && IntellijLanguageClient.isExtensionSupported(virtualFile)) {
String uri = FileUtils.VFSToURI(virtualFile);
// TODO annotations are applied to a file / document not to an editor. so store them by file and not by editor..
EditorEventManager eventManager = EditorEventManagerBase.forUri(uri);
if (eventManager == null) {
return;
}

if (eventManager.isCodeActionSyncRequired()) {
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public void invoke(@NotNull Project project, Editor editor, PsiFile psiFile) {
if (codeAction.getEdit() != null) {
WorkspaceEditHandler.applyEdit(codeAction.getEdit(), codeAction.getTitle());
}
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
EditorEventManager manager = EditorEventManagerBase.forEditor(editor);
if (manager != null) {
manager.executeCommands(Collections.singletonList(codeAction.getCommand()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile psiF

@Override
public void invoke(@NotNull Project project, Editor editor, PsiFile psiFile) {
EditorEventManager manager = EditorEventManagerBase.forUri(uri);
EditorEventManager manager = EditorEventManagerBase.forEditor(editor);
if (manager != null) {
manager.executeCommands(singletonList(command));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@

public class LSPDefaultIconProvider extends LSPIconProvider {

private static Icon STARTED = IconLoader.getIcon("/images/started.png");
private static Icon STARTING = IconLoader.getIcon("/images/starting.png");
private static Icon STOPPED = IconLoader.getIcon("/images/stopped.png");
private static Icon GREEN = IconLoader.getIcon("/images/started.png");
private static Icon YELLOW = IconLoader.getIcon("/images/starting.png");
private static Icon RED = IconLoader.getIcon("/images/stopped.png");

public Icon getCompletionIcon(CompletionItemKind kind) {

Expand Down Expand Up @@ -103,10 +103,11 @@ public Icon getSymbolIcon(SymbolKind kind) {

public Map<ServerStatus, Icon> getStatusIcons() {
Map<ServerStatus, Icon> statusIconMap = new HashMap<>();
statusIconMap.put(ServerStatus.STOPPED, STOPPED);
statusIconMap.put(ServerStatus.STARTING, STARTING);
statusIconMap.put(ServerStatus.STARTED, STARTING);
statusIconMap.put(ServerStatus.INITIALIZED, STARTED);
statusIconMap.put(ServerStatus.STOPPED, RED);
statusIconMap.put(ServerStatus.STARTING, YELLOW);
statusIconMap.put(ServerStatus.STARTED, YELLOW);
statusIconMap.put(ServerStatus.INITIALIZED, GREEN);
statusIconMap.put(ServerStatus.STOPPING, YELLOW);
return statusIconMap;
}

Expand Down
164 changes: 164 additions & 0 deletions src/main/java/org/wso2/lsp4intellij/editor/DocumentEventManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/*
* Copyright (c) 2019, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wso2.lsp4intellij.editor;

import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.editor.Document;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.event.DocumentEvent;
import com.intellij.openapi.editor.event.DocumentListener;
import com.intellij.openapi.fileEditor.FileDocumentManager;
import com.intellij.openapi.util.text.StringUtil;
import org.eclipse.lsp4j.DidChangeTextDocumentParams;
import org.eclipse.lsp4j.DidCloseTextDocumentParams;
import org.eclipse.lsp4j.DidOpenTextDocumentParams;
import org.eclipse.lsp4j.Position;
import org.eclipse.lsp4j.Range;
import org.eclipse.lsp4j.TextDocumentContentChangeEvent;
import org.eclipse.lsp4j.TextDocumentIdentifier;
import org.eclipse.lsp4j.TextDocumentItem;
import org.eclipse.lsp4j.TextDocumentSyncKind;
import org.eclipse.lsp4j.VersionedTextDocumentIdentifier;
import org.wso2.lsp4intellij.client.languageserver.requestmanager.RequestManager;
import org.wso2.lsp4intellij.client.languageserver.wrapper.LanguageServerWrapper;
import org.wso2.lsp4intellij.utils.ApplicationUtils;
import org.wso2.lsp4intellij.utils.DocumentUtils;
import org.wso2.lsp4intellij.utils.FileUtils;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class DocumentEventManager {
private final Document document;
private final DocumentListener documentListener;
private final TextDocumentSyncKind syncKind;
private final LanguageServerWrapper wrapper;
private final TextDocumentIdentifier identifier;
private int version = -1;
protected Logger LOG = Logger.getInstance(EditorEventManager.class);
private static final Map<String, DocumentEventManager> uriToDocumentEventManager = new HashMap<>();

private final Set<Document> openDocuments = new HashSet<>();

DocumentEventManager(Document document, DocumentListener documentListener, TextDocumentSyncKind syncKind, LanguageServerWrapper wrapper) {
this.document = document;
this.documentListener = documentListener;
this.syncKind = syncKind;
this.wrapper = wrapper;
this.identifier = new TextDocumentIdentifier(FileUtils.documentToUri(document));
}

public static void clearState() {
uriToDocumentEventManager.clear();
}

public static DocumentEventManager getOrCreateDocumentManager(Document document, DocumentListener listener, TextDocumentSyncKind syncKind, LanguageServerWrapper wrapper) {
DocumentEventManager manager = uriToDocumentEventManager.get(FileUtils.documentToUri(document));
if (manager != null) {
return manager;
}

manager = new DocumentEventManager(document, listener, syncKind, wrapper);

uriToDocumentEventManager.put(FileUtils.documentToUri(document), manager);
return manager;
}

public void removeListeners() {
document.removeDocumentListener(documentListener);
}

public void registerListeners() {
document.addDocumentListener(documentListener);
}

public int getDocumentVersion() {
return this.version;
}

public void documentChanged(DocumentEvent event) {

DidChangeTextDocumentParams changesParams = new DidChangeTextDocumentParams(new VersionedTextDocumentIdentifier(),
Collections.singletonList(new TextDocumentContentChangeEvent()));
changesParams.getTextDocument().setUri(identifier.getUri());

changesParams.getTextDocument().setVersion(version++);

if (syncKind == TextDocumentSyncKind.Incremental) {
TextDocumentContentChangeEvent changeEvent = changesParams.getContentChanges().get(0);
CharSequence newText = event.getNewFragment();
int offset = event.getOffset();
int newTextLength = event.getNewLength();
EditorEventManager editorEventManager = EditorEventManagerBase.managersForUri(FileUtils.documentToUri(document)).iterator().next();
if (editorEventManager == null) {
LOG.warn("no editor associated with document");
return;
}
Editor editor = editorEventManager.editor;
Position lspPosition = DocumentUtils.offsetToLSPPos(editor, offset);
int startLine = lspPosition.getLine();
int startColumn = lspPosition.getCharacter();
CharSequence oldText = event.getOldFragment();

//if text was deleted/replaced, calculate the end position of inserted/deleted text
int endLine, endColumn;
if (oldText.length() > 0) {
endLine = startLine + StringUtil.countNewLines(oldText);
String content = oldText.toString();
String[] oldLines = content.split("\n");
int oldTextLength = oldLines.length == 0 ? 0 : oldLines[oldLines.length - 1].length();
endColumn = content.endsWith("\n") ? 0 : oldLines.length == 1 ? startColumn + oldTextLength : oldTextLength;
} else { //if insert or no text change, the end position is the same
endLine = startLine;
endColumn = startColumn;
}
Range range = new Range(new Position(startLine, startColumn), new Position(endLine, endColumn));
changeEvent.setRange(range);
changeEvent.setRangeLength(newTextLength);
changeEvent.setText(newText.toString());
} else if (syncKind == TextDocumentSyncKind.Full) {
changesParams.getContentChanges().get(0).setText(document.getText());
}
ApplicationUtils.pool(() -> wrapper.getRequestManager().didChange(changesParams));
}

public void documentOpened() {
if (openDocuments.contains(document)) {
LOG.warn("trying to send open notification for document which was already opened!");
} else {
openDocuments.add(document);
final String extension = FileDocumentManager.getInstance().getFile(document).getExtension();
wrapper.getRequestManager().didOpen(new DidOpenTextDocumentParams(new TextDocumentItem(identifier.getUri(),
wrapper.serverDefinition.languageIdFor(extension),
version++,
document.getText())));
}
}

public void documentClosed() {
if (!openDocuments.contains(document)) {
LOG.warn("trying to close document which is not open");
} else if (EditorEventManagerBase.managersForUri(FileUtils.documentToUri(document)).size() > 1) {
LOG.warn("trying to close document which is still open in another editor!");
} else {
openDocuments.remove(document);
wrapper.getRequestManager().didClose(new DidCloseTextDocumentParams(identifier));
}
}
}
Loading