Ensure markers use right LS to compute resolutions

+ Code cleanup and fun with streams

Change-Id: I1cd99ddfe316f3c66244ee4401c1a6221785dfe7
Signed-off-by: Mickael Istria <mistria@redhat.com>
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java
index 67fbfb6..ec1f3a6 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServersRegistry.java
@@ -13,8 +13,6 @@
 import java.io.IOException;
 import java.util.AbstractMap.SimpleEntry;
 import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -199,18 +197,18 @@
 		connections.add(new ContentTypeToLanguageServerDefinition(contentType, serverDefinition));
 	}
 
-	public List<ContentTypeToLSPLaunchConfigEntry> getContentTypeToLSPLaunches() {
-		return Arrays.asList(this.connections.stream().filter(element -> element instanceof ContentTypeToLSPLaunchConfigEntry).toArray(size -> new ContentTypeToLSPLaunchConfigEntry[size]));
-	}
-
 	public void setAssociations(List<ContentTypeToLSPLaunchConfigEntry> wc) {
-		this.connections.removeIf(entry -> entry instanceof ContentTypeToLSPLaunchConfigEntry);
+		this.connections.removeIf(ContentTypeToLSPLaunchConfigEntry.class::isInstance);
 		this.connections.addAll(wc);
 		persistContentTypeToLaunchConfigurationMapping();
 	}
 
+	public List<ContentTypeToLSPLaunchConfigEntry> getContentTypeToLSPLaunches() {
+		return this.connections.stream().filter(ContentTypeToLSPLaunchConfigEntry.class::isInstance).map(ContentTypeToLSPLaunchConfigEntry.class::cast).collect(Collectors.toList());
+	}
+
 	public List<ContentTypeToLanguageServerDefinition> getContentTypeToLSPExtensions() {
-		return Collections.unmodifiableList(connections);
+		return this.connections.stream().filter(mapping -> mapping.getValue() instanceof ExtensionLanguageServerDefinition).collect(Collectors.toList());
 	}
 
 }
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServiceAccessor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServiceAccessor.java
index 2119db2..c23ec3d 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServiceAccessor.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/LanguageServiceAccessor.java
@@ -190,7 +190,7 @@
 		if (file.exists()) {
 			fileUri = LSPEclipseUtils.toUri(file);
 			try {
-				ProjectSpecificLanguageServerWrapper wrapper = getLSWrapper(file, capabilityRequest);
+				ProjectSpecificLanguageServerWrapper wrapper = getLSWrapper(file, capabilityRequest, null);
 				if (wrapper != null) {
 					wrapper.connect(file.getLocation(), document);
 					@Nullable
@@ -209,8 +209,8 @@
 		return null;
 	}
 
-	public static LanguageServer getLanguageServer(@NonNull IFile file, Predicate<ServerCapabilities> request) throws Exception {
-		ProjectSpecificLanguageServerWrapper wrapper = getLSWrapper(file, request);
+	public static @Nullable LanguageServer getLanguageServer(@NonNull IFile file, Predicate<ServerCapabilities> request) throws IOException {
+		ProjectSpecificLanguageServerWrapper wrapper = getLSWrapper(file, request, null);
 		if (wrapper != null) {
 			wrapper.connect(file.getLocation(), null);
 			return wrapper.getServer();
@@ -218,8 +218,27 @@
 		return null;
 	}
 
-	@Nullable private static ProjectSpecificLanguageServerWrapper getLSWrapper(@NonNull IFile file, @Nullable Predicate<ServerCapabilities> request) throws IOException {
+	/**
+	 *
+	 * @param file
+	 * @param request
+	 * @param serverId
+	 * @return a LanguageServer for the given file, which is defined with provided server ID and conforms to specified requst
+	 */
+	public static @Nullable LanguageServer getLanguageServer(@NonNull IFile file, Predicate<ServerCapabilities> request, @NonNull String serverId) throws IOException {
+		ProjectSpecificLanguageServerWrapper wrapper = getLSWrapper(file, request, serverId);
+		if (wrapper != null) {
+			wrapper.connect(file.getLocation(), null);
+			return wrapper.getServer();
+		}
+		return null;
+	}
+
+	@Nullable private static ProjectSpecificLanguageServerWrapper getLSWrapper(@NonNull IFile file, @Nullable Predicate<ServerCapabilities> request, @Nullable String serverId) throws IOException {
 		IProject project = file.getProject();
+		if (project == null) {
+			return null;
+		}
 		IContentType[] fileContentTypes = null;
 		try (InputStream contents = file.getContents()) {
 			fileContentTypes = Platform.getContentTypeManager().findContentTypesFor(contents, file.getName()); //TODO consider using document as inputstream
@@ -227,14 +246,17 @@
 			LanguageServerPlugin.logError(e);
 			return null;
 		}
-		ProjectSpecificLanguageServerWrapper wrapper = getMatchingStartedWrapper(project, fileContentTypes, request);
+		ProjectSpecificLanguageServerWrapper wrapper = getMatchingStartedWrapper(project, fileContentTypes, request, serverId);
 		if (wrapper != null) {
 			return wrapper;
 		}
 
 		for (IContentType contentType : fileContentTypes) {
+			if (contentType == null) {
+				continue;
+			}
 			for (LanguageServerDefinition serverDefinition : LanguageServersRegistry.getInstance().findProviderFor(contentType)) {
-				if (serverDefinition != null) {
+				if (serverDefinition != null && (serverId == null || serverDefinition.getId().equals(serverId))) {
 					wrapper = getLSWrapperForConnection(project, contentType, serverDefinition);
 					if (request == null
 						|| wrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */
@@ -282,14 +304,14 @@
 	}
 
 	private static ProjectSpecificLanguageServerWrapper getMatchingStartedWrapper(IProject project,
-	        IContentType[] fileContentTypes, Predicate<ServerCapabilities> request) {
+	        IContentType[] fileContentTypes, Predicate<ServerCapabilities> request, @Nullable String serverId) {
 		for (IContentType contentType : fileContentTypes) {
 			WrapperEntryKey key = new WrapperEntryKey(project, contentType);
 			if (!projectServers.containsKey(key)) {
 				projectServers.put(key, new ArrayList<>());
 			}
 			for (ProjectSpecificLanguageServerWrapper aWrapper : projectServers.get(key)) {
-				if (aWrapper != null && (request == null
+				if (aWrapper != null && (serverId == null || aWrapper.serverDefinition.getId().equals(serverId)) && (request == null
 						|| aWrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */
 						|| request.test(aWrapper.getServerCapabilities())
 					)) {
@@ -319,7 +341,7 @@
 						continue;
 					}
 					if ((request == null
-						|| wrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */
+					    || wrapper.getServerCapabilities() == null /* null check is workaround for https://github.com/TypeFox/ls-api/issues/47 */
 					    || request.test(wrapper.getServerCapabilities()))) {
 						serverInfos.add(new LSPServerInfo(project, server, wrapper.getServerCapabilities()));
 					}
@@ -333,4 +355,5 @@
 	protected static LanguageServerDefinition getInfo(@NonNull StreamConnectionProvider provider) {
 		return connectionsInfo.get(provider);
 	}
+
 }
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionMarkerResolution.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionMarkerResolution.java
index f0fe661..eb06b14 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionMarkerResolution.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/codeactions/LSPCodeActionMarkerResolution.java
@@ -42,7 +42,6 @@
 public class LSPCodeActionMarkerResolution implements IMarkerResolutionGenerator2 {
 
 	private static final String LSP_REMEDIATION = "lspCodeActions"; //$NON-NLS-1$
-	private static final String LS = "languageServer"; //$NON-NLS-1$
 
 	private static final IMarkerResolution2 COMPUTING = new IMarkerResolution2() {
 
@@ -74,11 +73,9 @@
 	@Override
 	public IMarkerResolution[] getResolutions(IMarker marker) {
 		Object att;
-		LanguageServer languageServer = null;
 		try {
 			checkMarkerResoultion(marker);
 			att = marker.getAttribute(LSP_REMEDIATION);
-			languageServer = (LanguageServer) marker.getAttribute(LS);
 		} catch (Exception e) {
 			LanguageServerPlugin.logError(e);
 			return new IMarkerResolution[0];
@@ -102,27 +99,37 @@
 	private void checkMarkerResoultion(IMarker marker) throws Exception {
 		if (marker.getAttribute(LSP_REMEDIATION) != null) {
 			return;
-		} else if (marker.getResource().getType() == IResource.FILE) {
-			LanguageServer lsp = LanguageServiceAccessor.getLanguageServer((IFile)marker.getResource(), (capabilities) -> Boolean.TRUE.equals(capabilities.getCodeActionProvider()));
-			if (lsp != null) {
-				marker.setAttribute(LSP_REMEDIATION, COMPUTING);
-				marker.setAttribute(LS, lsp);
-				Diagnostic diagnostic = (Diagnostic)marker.getAttribute(LSPDiagnosticsToMarkers.LSP_DIAGNOSTIC);
-				CodeActionContext context = new CodeActionContext(Collections.singletonList(diagnostic));
-				CodeActionParams params = new CodeActionParams();
-				params.setContext(context);
-				params.setTextDocument(new TextDocumentIdentifier(LSPEclipseUtils.toUri(marker.getResource()).toString()));
-				params.setRange(diagnostic.getRange());
-				CompletableFuture<List<? extends Command>> codeAction = lsp.getTextDocumentService().codeAction(params);
-				codeAction.thenAccept(actions -> {
-					try {
-						marker.setAttribute(LSP_REMEDIATION, actions);
-					} catch (CoreException e) {
-						LanguageServerPlugin.logError(e);
-					}
-				});
-				// wait a bit to avoid showing too much "Computing" without looking like a freeze
-				codeAction.get(300, TimeUnit.MILLISECONDS);
+		} else {
+			IResource res = marker.getResource();
+			if (res != null && res.getType() == IResource.FILE) {
+				IFile file = (IFile)res;
+				String languageServerId = marker.getAttribute(LSPDiagnosticsToMarkers.LANGUAGE_SERVER_ID, null);
+				LanguageServer ls = null;
+				if (languageServerId != null) { // try to use same LS as the one that created the marker
+					LanguageServiceAccessor.getLanguageServer(file, (capabilities) -> Boolean.TRUE.equals(capabilities.getCodeActionProvider()), languageServerId);
+				}
+				if (ls == null) { // if it's not there, try any other server
+					ls = LanguageServiceAccessor.getLanguageServer(file, (capabilities) -> Boolean.TRUE.equals(capabilities.getCodeActionProvider()));
+				}
+				if (ls != null) {
+					marker.setAttribute(LSP_REMEDIATION, COMPUTING);
+					Diagnostic diagnostic = (Diagnostic)marker.getAttribute(LSPDiagnosticsToMarkers.LSP_DIAGNOSTIC);
+					CodeActionContext context = new CodeActionContext(Collections.singletonList(diagnostic));
+					CodeActionParams params = new CodeActionParams();
+					params.setContext(context);
+					params.setTextDocument(new TextDocumentIdentifier(LSPEclipseUtils.toUri(marker.getResource()).toString()));
+					params.setRange(diagnostic.getRange());
+					CompletableFuture<List<? extends Command>> codeAction = ls.getTextDocumentService().codeAction(params);
+					codeAction.thenAccept(actions -> {
+						try {
+							marker.setAttribute(LSP_REMEDIATION, actions);
+						} catch (CoreException e) {
+							LanguageServerPlugin.logError(e);
+						}
+					});
+					// wait a bit to avoid showing too much "Computing" without looking like a freeze
+					codeAction.get(300, TimeUnit.MILLISECONDS);
+				}
 			}
 		}
 	}
@@ -132,7 +139,7 @@
 		try {
 			checkMarkerResoultion(marker);
 			Object remediation = marker.getAttribute(LSP_REMEDIATION);
-			return remediation == COMPUTING || (remediation instanceof Collection && !((Collection)remediation).isEmpty());
+			return remediation == COMPUTING || (remediation instanceof Collection && !((Collection<?>)remediation).isEmpty());
 		} catch (Exception ex) {
 			LanguageServerPlugin.logError(ex);
 		}
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/LSPDiagnosticsToMarkers.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/LSPDiagnosticsToMarkers.java
index c73d282..1aa187a 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/LSPDiagnosticsToMarkers.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/operations/diagnostics/LSPDiagnosticsToMarkers.java
@@ -38,7 +38,7 @@
 public class LSPDiagnosticsToMarkers implements Consumer<PublishDiagnosticsParams> {
 
 	public static final String LSP_DIAGNOSTIC = "lspDiagnostic"; //$NON-NLS-1$
-	private static final String LSP_SERVER_ID = "languageServerId"; //$NON-NLS-1$
+	public static final String LANGUAGE_SERVER_ID = "languageServerId"; //$NON-NLS-1$
 	public static final String LS_DIAGNOSTIC_MARKER_TYPE = "org.eclipse.lsp4e.diagnostic"; //$NON-NLS-1$
 	private final @NonNull IProject project;
 	private final @NonNull String languageServerId;
@@ -59,7 +59,7 @@
 			}
 			Set<IMarker> remainingMarkers = new HashSet<>(
 					Arrays.asList(resource.findMarkers(LS_DIAGNOSTIC_MARKER_TYPE, false, IResource.DEPTH_ONE)));
-			remainingMarkers.removeIf(marker -> !Objects.equals(marker.getAttribute(LSP_SERVER_ID, ""), languageServerId)); //$NON-NLS-1$
+			remainingMarkers.removeIf(marker -> !Objects.equals(marker.getAttribute(LANGUAGE_SERVER_ID, ""), languageServerId)); //$NON-NLS-1$
 			for (Diagnostic diagnostic : diagnostics.getDiagnostics()) {
 				IMarker associatedMarker = getExistingMarkerFor(resource, diagnostic, remainingMarkers);
 				if (associatedMarker == null) {
@@ -80,7 +80,7 @@
 	protected void updateMarker(IResource resource, Diagnostic diagnostic, IMarker marker) {
 		try {
 			marker.setAttribute(LSP_DIAGNOSTIC, diagnostic);
-			marker.setAttribute(LSP_SERVER_ID, this.languageServerId);
+			marker.setAttribute(LANGUAGE_SERVER_ID, this.languageServerId);
 			marker.setAttribute(IMarker.MESSAGE, diagnostic.getMessage());
 			marker.setAttribute(IMarker.SEVERITY, LSPEclipseUtils.toEclipseMarkerSeverity(diagnostic.getSeverity()));
 			if (resource.getType() != IResource.FILE) {
@@ -125,7 +125,7 @@
 						&& LSPEclipseUtils.toOffset(diagnostic.getRange().getStart(), document) == startOffset + 1
 						&& LSPEclipseUtils.toOffset(diagnostic.getRange().getEnd(), document) == endOffset + 1
 						&& Objects.equals(marker.getAttribute(IMarker.MESSAGE), diagnostic.getMessage())
-						&& Objects.equals(marker.getAttribute(LSP_SERVER_ID), this.languageServerId)) {
+						&& Objects.equals(marker.getAttribute(LANGUAGE_SERVER_ID), this.languageServerId)) {
 					return marker;
 				}
 			} catch (CoreException | BadLocationException e) {