Bug 553565 - Add path param to LSP command handler

Added additional context IPath parameter to command to handle via
LSPCommandHandler.

Change-Id: Ia1604c52599f5ac6d4e08338f8a51da3408dfcf8
Signed-off-by: Max Bureck <max.bureck@fokus.fraunhofer.de>
diff --git a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/operations/codelens/LSPCodeMiningTest.java b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/operations/codelens/LSPCodeMiningTest.java
index 9a414eb..0197d77 100644
--- a/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/operations/codelens/LSPCodeMiningTest.java
+++ b/org.eclipse.lsp4e.test/src/org/eclipse/lsp4e/test/operations/codelens/LSPCodeMiningTest.java
@@ -25,6 +25,7 @@
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IProject;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.jface.text.BadLocationException;
 import org.eclipse.jface.text.IDocument;
 import org.eclipse.lsp4e.LanguageServersRegistry;
@@ -77,12 +78,14 @@
 		final CodeLens lens = createCodeLens(commandID);
 
 		final AtomicReference<Command> actualCommand = new AtomicReference<>(null);
+		final AtomicReference<IPath> actualPath = new AtomicReference<>(null);
 
 		// Create and register handler
 		IHandler handler = new LSPCommandHandler() {
 			@Override
-			public Object execute(ExecutionEvent event, Command command) throws ExecutionException {
+			public Object execute(ExecutionEvent event, Command command, IPath context) throws ExecutionException {
 				actualCommand.set(command);
+				actualPath.set(context);
 				return null;
 			}
 		};
@@ -101,6 +104,7 @@
 		sut.getAction().accept(mouseEvent);
 
 		assertEquals(lens.getCommand(), actualCommand.get());
+		assertEquals(file.getFullPath(), actualPath.get());
 	}
 
 	@Test
diff --git a/org.eclipse.lsp4e/plugin.xml b/org.eclipse.lsp4e/plugin.xml
index 5a30505..fea5e27 100644
--- a/org.eclipse.lsp4e/plugin.xml
+++ b/org.eclipse.lsp4e/plugin.xml
@@ -532,9 +532,14 @@
             id="org.eclipse.lsp4e.commandParameterType"
             type="org.eclipse.lsp4j.Command">
       </commandParameterType>
+      <commandParameterType
+            converter="org.eclipse.lsp4e.command.internal.PathConverter"
+            id="org.eclipse.lsp4e.pathParameterType"
+            type="org.eclipse.core.runtime.IPath">
+      </commandParameterType>
       <category
             id="org.eclipse.lsp4e.commandCategory"
-            name="CodeLens">
+            name="Command">
       </category>
    </extension>
 </plugin>
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/LSPCommandHandler.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/LSPCommandHandler.java
index 15ad089..dcf6c4c 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/LSPCommandHandler.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/LSPCommandHandler.java
@@ -15,6 +15,7 @@
 import org.eclipse.core.commands.ExecutionEvent;
 import org.eclipse.core.commands.ExecutionException;
 import org.eclipse.core.commands.IHandler;
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.lsp4j.Command;
 import org.eclipse.lsp4j.jsonrpc.validation.NonNull;
 import org.eclipse.ui.handlers.IHandlerService;
@@ -29,24 +30,36 @@
  * </br>
  * This class is a shortcut for implementing an {@link IHandler} and in the
  * {@link IHandler#execute(ExecutionEvent)} method get the Command by calling:
- * {@code (Command)event.getObjectParameterForExecution(LSPCommandHandler.LSP_COMMAND_PARAMETER_ID)}.
+ * {@code (Command)event.getObjectParameterForExecution(LSPCommandHandler.LSP_COMMAND_PARAMETER_ID)} and
+ * {@code (IPath)event.getObjectParameterForExecution(LSPCommandHandler.LSP_PATH_PARAMETER_ID)}.
  */
 public abstract class LSPCommandHandler extends AbstractHandler {
 
 	/**
 	 * ID of the {@link Command} parameter in a handled {@link ExecutionEvent}. Can
-	 * be used to access a CodeLens via
+	 * be used to access a Command via
 	 * {@link ExecutionEvent#getObjectParameterForExecution(String)}.
 	 */
 	public static final String LSP_COMMAND_PARAMETER_ID = "org.eclipse.lsp4e.command.param"; //$NON-NLS-1$
 
+	/**
+	 * ID of the {@link IPath} parameter in a handled {@linkplain ExecutionEvent}.
+	 * Can be used to access the
+	 * {@link ExecutionEvent#getObjectParameterForExecution(String)}.
+	 */
+	public static final String LSP_PATH_PARAMETER_ID = "org.eclipse.lsp4e.path.param"; //$NON-NLS-1$
+
 	@Override
 	public final Object execute(ExecutionEvent event) throws ExecutionException {
 		Command command = (Command) event.getObjectParameterForExecution(LSP_COMMAND_PARAMETER_ID);
-		if(command == null) {
+		if (command == null) {
 			return null;
 		}
-		return execute(event, command);
+		IPath path = (IPath) event.getObjectParameterForExecution(LSP_PATH_PARAMETER_ID);
+		if (path == null) {
+			return null;
+		}
+		return execute(event, command, path);
 	}
 
 	/**
@@ -57,9 +70,14 @@
 	 *            the application; must not be null.
 	 * @param command
 	 *            The Command to be executed on client side.
+	 * @param path
+	 *            The path to a resource that is the context for the command to be
+	 *            executed. This can either be a resource in the workspace or a file
+	 *            system path.
 	 * @return The result of the execution. Reserved for future use, must be null.
 	 * @throws ExecutionException
 	 *             if an exception occurred during execution.
 	 */
-	public abstract Object execute(ExecutionEvent event, @NonNull Command command) throws ExecutionException;
+	public abstract Object execute(ExecutionEvent event, @NonNull Command command, IPath path)
+			throws ExecutionException;
 }
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/CommandExecutor.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/CommandExecutor.java
index 5abccd1..fd4e7f6 100644
--- a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/CommandExecutor.java
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/CommandExecutor.java
@@ -9,6 +9,7 @@
 package org.eclipse.lsp4e.command.internal;
 
 import static org.eclipse.lsp4e.command.LSPCommandHandler.LSP_COMMAND_PARAMETER_ID;
+import static org.eclipse.lsp4e.command.LSPCommandHandler.LSP_PATH_PARAMETER_ID;
 
 import java.io.IOException;
 import java.net.URI;
@@ -29,6 +30,7 @@
 import org.eclipse.core.commands.ParameterizedCommand;
 import org.eclipse.core.commands.common.NotDefinedException;
 import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
 import org.eclipse.jdt.annotation.NonNull;
 import org.eclipse.jdt.annotation.Nullable;
 import org.eclipse.jface.text.IDocument;
@@ -61,6 +63,7 @@
 
 	private static final String LSP_COMMAND_CATEGORY_ID = "org.eclipse.lsp4e.commandCategory"; //$NON-NLS-1$
 	private static final String LSP_COMMAND_PARAMETER_TYPE_ID = "org.eclipse.lsp4e.commandParameterType"; //$NON-NLS-1$
+	private static final String LSP_PATH_PARAMETER_TYPE_ID = "org.eclipse.lsp4e.pathParameterType"; //$NON-NLS-1$
 
 	/**
 	 * Will execute the given {@code command} either on a language server,
@@ -147,7 +150,8 @@
 		if (workbench == null) {
 			return false;
 		}
-		ParameterizedCommand parameterizedCommand = createEclipseCoreCommand(command, workbench);
+		IPath context = LSPEclipseUtils.toPath(document);
+		ParameterizedCommand parameterizedCommand = createEclipseCoreCommand(command, context, workbench);
 		if (parameterizedCommand == null) {
 			return false;
 		}
@@ -167,7 +171,7 @@
 		return true;
 	}
 
-	private static ParameterizedCommand createEclipseCoreCommand(@NonNull Command command,
+	private static ParameterizedCommand createEclipseCoreCommand(@NonNull Command command, IPath context,
 			@NonNull IWorkbench workbench) {
 		// Usually commands are defined via extension point, but we synthesize one on
 		// the fly for the command ID, since we do not want downstream users
@@ -177,15 +181,18 @@
 		ICommandService commandService = workbench.getService(ICommandService.class);
 		org.eclipse.core.commands.Command coreCommand = commandService.getCommand(commandId);
 		if (!coreCommand.isDefined()) {
-			ParameterType paramType = commandService.getParameterType(LSP_COMMAND_PARAMETER_TYPE_ID);
+			ParameterType commandParamType = commandService.getParameterType(LSP_COMMAND_PARAMETER_TYPE_ID);
+			ParameterType pathParamType = commandService.getParameterType(LSP_PATH_PARAMETER_TYPE_ID);
 			Category category = commandService.getCategory(LSP_COMMAND_CATEGORY_ID);
 			IParameter[] parameters = {
-					new CommandEventParameter(paramType, command.getTitle(), LSP_COMMAND_PARAMETER_ID) };
+					new CommandEventParameter(commandParamType, command.getTitle(), LSP_COMMAND_PARAMETER_ID),
+					new CommandEventParameter(pathParamType, command.getTitle(), LSP_PATH_PARAMETER_ID)};
 			coreCommand.define(commandId, null, category, parameters);
 		}
 
 		Map<Object, Object> parameters = new HashMap<>();
 		parameters.put(LSP_COMMAND_PARAMETER_ID, command);
+		parameters.put(LSP_PATH_PARAMETER_ID, context);
 		ParameterizedCommand parameterizedCommand = ParameterizedCommand.generateCommand(coreCommand, parameters);
 		return parameterizedCommand;
 	}
diff --git a/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/PathConverter.java b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/PathConverter.java
new file mode 100644
index 0000000..a0646dc
--- /dev/null
+++ b/org.eclipse.lsp4e/src/org/eclipse/lsp4e/command/internal/PathConverter.java
@@ -0,0 +1,33 @@
+/*******************************************************************************
+ * Copyright (c) 2019 Fraunhofer FOKUS and others.
+ * This program and the accompanying materials are made
+ * available under the terms of the Eclipse Public License 2.0
+ * which is available at https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.lsp4e.command.internal;
+
+import java.util.Objects;
+
+import org.eclipse.core.commands.AbstractParameterValueConverter;
+import org.eclipse.core.commands.ParameterValueConversionException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+/**
+ * Serializes {@link IPath} instances and de-serializes them to {@link Path} instances.
+ */
+public class PathConverter extends AbstractParameterValueConverter {
+
+	@Override
+	public Object convertToObject(String parameterValue) throws ParameterValueConversionException {
+		return new Path(parameterValue);
+	}
+
+	@Override
+	public String convertToString(Object parameterValue) throws ParameterValueConversionException {
+		return Objects.toString(parameterValue);
+	}
+
+}