Introduce Intelligent Commands (#4389)

Introduce Intelligent Commands
6.19.x
Artem Zatsarynnyi 2017-03-23 18:23:17 +02:00 committed by GitHub
parent 1ff9b22221
commit bbd0c1abb2
342 changed files with 14008 additions and 4421 deletions

View File

@ -4,7 +4,7 @@
outline none
width 24px
height 16px
top 4px
top 9px
background-color $navbar-ide-iframe-button-background-color
color $light-gray-color
font-size 8px

View File

@ -298,7 +298,7 @@ che-nav-bar
.navbar-iframe-button-left-border
position absolute
top 4px
top 9px
right 0
width 0
height 16px

View File

@ -44,6 +44,7 @@ public interface IdeActions {
String GROUP_OTHER_MENU = "otherMenu";
String GROUP_LEFT_MAIN_MENU = "leftMainMenu";
@Deprecated
String GROUP_RIGHT_MAIN_MENU = "rightMainMenu";
String GROUP_CENTER_STATUS_PANEL = "centerStatusPanelGroup";

View File

@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.api.command;
import java.util.Objects;
/**
* Base implementation of the {@link CommandGoal}.
*
* @author Artem Zatsarynnyi
*/
public class BaseCommandGoal implements CommandGoal {
private final String id;
public BaseCommandGoal(String id) {
this.id = id;
}
@Override
public String getId() {
return id;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof CommandGoal)) {
return false;
}
CommandGoal other = (CommandGoal)o;
return Objects.equals(getId(), other.getId());
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}

View File

@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.api.command;
import org.eclipse.che.api.core.model.machine.Command;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.ide.api.macro.Macro;
/**
* Allows to execute a command.
*
* @author Artem Zatsarynnyi
*/
public interface CommandExecutor {
/**
* Sends the the given {@code command} to the specified {@code machine} for execution.
* <p><b>Note</b> that all {@link Macro}s will be expanded into
* real values before sending the {@code command} for execution.
*
* @param command
* command to execute
* @param machine
* machine to execute the command
* @see Macro
*/
void executeCommand(Command command, Machine machine);
/**
* Sends the the given {@code command} for execution.
* <p>If any machine is currently selected it will be used as execution target.
* Otherwise user will be asked for choosing execution target.
* <p><b>Note</b> that all {@link Macro}s will be expanded into
* real values before sending the {@code command} for execution.
*
* @param command
* command to execute
* @see Macro
*/
void executeCommand(CommandImpl command);
}

View File

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.api.command;
/**
* Contract for command goal.
*
* @author Artem Zatsarynnyi
*/
public interface CommandGoal {
/** Returns goal ID. */
String getId();
}

View File

@ -0,0 +1,53 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.api.command;
import org.eclipse.che.commons.annotation.Nullable;
import java.util.List;
import java.util.Optional;
/**
* Registry of command goals.
*
* @author Artem Zatsarynnyi
*/
public interface CommandGoalRegistry {
/** Returns all registered predefined {@link CommandGoal}s. */
List<CommandGoal> getAllPredefinedGoals();
/** Returns the default command goal which is used for grouping commands which doesn't belong to any goal. */
CommandGoal getDefaultGoal();
/**
* Returns an optional {@link CommandGoal} by the given ID
* or {@code Optional.absent()} if none was registered.
*
* @param id
* the ID of the predefined command goal to get
* @return an optional {@link CommandGoal} or {@code Optional.absent()} if none was registered
*/
Optional<CommandGoal> getPredefinedGoalById(String id);
/**
* Returns a predefined {@link CommandGoal} by the given ID
* or the custom (non-predefined) goal if none was registered
* or the default goal if the given {@code id} is {@code null} or empty string.
*
* @param id
* the ID of the command goal
* @return a predefined {@link CommandGoal} with the given ID
* or the custom (non-predefined) goal if none was registered
* or the default goal if the given {@code id} is {@code null} or empty string. Never null.
*/
CommandGoal getGoalForId(@Nullable String id);
}

View File

@ -11,42 +11,38 @@
package org.eclipse.che.ide.api.command;
import org.eclipse.che.api.core.model.machine.Command;
import org.eclipse.che.commons.annotation.Nullable;
import java.util.Collections;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
/**
* Model of the command.
*
* @author Artem Zatsarynnyi
*/
import static java.util.Collections.unmodifiableList;
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_GOAL_ATTRIBUTE_NAME;
/** Data object for {@link Command}. */
public class CommandImpl implements Command {
private final String type;
private final String typeId;
private final ApplicableContext context;
private String name;
private String commandLine;
private Map<String, String> attributes;
/**
* Creates new command of the specified type with the given name and command line.
*
* @param name
* command name
* @param commandLine
* command line
* @param type
* type of the command
*/
public CommandImpl(String name, String commandLine, String type) {
this(name, commandLine, type, Collections.<String, String>emptyMap());
/** Creates new {@link CommandImpl} based on the given data. */
public CommandImpl(Command command, ApplicableContext context) {
this(command.getName(),
command.getCommandLine(),
command.getType(),
new HashMap<>(command.getAttributes()),
context);
}
public CommandImpl(String name, String commandLine, String type, Map<String, String> attributes) {
this.name = name;
this.commandLine = commandLine;
this.type = type;
this.attributes = attributes;
/** Creates new {@link CommandImpl} based on the provided data. */
public CommandImpl(String name, String commandLine, String typeId) {
this(name, commandLine, typeId, new HashMap<>());
}
/** Creates copy of the given {@link Command}. */
@ -54,7 +50,34 @@ public class CommandImpl implements Command {
this(command.getName(),
command.getCommandLine(),
command.getType(),
command.getAttributes());
new HashMap<>(command.getAttributes()));
}
/** Creates copy of the given {@code command}. */
public CommandImpl(CommandImpl command) {
this(command.getName(),
command.getCommandLine(),
command.getType(),
new HashMap<>(command.getAttributes()),
new ApplicableContext(command.getApplicableContext()));
}
/** Creates new {@link CommandImpl} based on the provided data. */
public CommandImpl(String name, String commandLine, String typeId, Map<String, String> attributes) {
this.name = name;
this.commandLine = commandLine;
this.typeId = typeId;
this.attributes = attributes;
this.context = new ApplicableContext();
}
/** Creates new {@link CommandImpl} based on the provided data. */
public CommandImpl(String name, String commandLine, String typeId, Map<String, String> attributes, ApplicableContext context) {
this.name = name;
this.commandLine = commandLine;
this.typeId = typeId;
this.attributes = attributes;
this.context = context;
}
@Override
@ -77,7 +100,7 @@ public class CommandImpl implements Command {
@Override
public String getType() {
return type;
return typeId;
}
@Override
@ -89,6 +112,27 @@ public class CommandImpl implements Command {
this.attributes = attributes;
}
/** Returns ID of the command's goal or {@code null} if none. */
@Nullable
public String getGoal() {
return getAttributes().get(COMMAND_GOAL_ATTRIBUTE_NAME);
}
/** Sets command's goal ID. */
public void setGoal(String goalId) {
getAttributes().put(COMMAND_GOAL_ATTRIBUTE_NAME, goalId);
}
/** Returns command's applicable context. */
public ApplicableContext getApplicableContext() {
return context;
}
/**
* {@inheritDoc}
*
* @see #equalsIgnoreContext(CommandImpl)
*/
@Override
public boolean equals(Object o) {
if (this == o) {
@ -102,13 +146,109 @@ public class CommandImpl implements Command {
CommandImpl other = (CommandImpl)o;
return Objects.equals(getName(), other.getName())
&& Objects.equals(type, other.type)
&& Objects.equals(typeId, other.typeId)
&& Objects.equals(commandLine, other.commandLine)
&& Objects.equals(getAttributes(), other.getAttributes());
&& Objects.equals(getAttributes(), other.getAttributes())
&& Objects.equals(getApplicableContext(), other.getApplicableContext());
}
/**
* Compares this {@link CommandImpl} to another {@link CommandImpl}, ignoring applicable context considerations.
*
* @param anotherCommand
* the {@link CommandImpl} to compare this {@link CommandImpl} against
* @return {@code true} if the argument represents an equivalent {@link CommandImpl}
* ignoring applicable context; {@code false} otherwise
*/
public boolean equalsIgnoreContext(CommandImpl anotherCommand) {
if (this == anotherCommand) {
return true;
}
return Objects.equals(getName(), anotherCommand.getName())
&& Objects.equals(typeId, anotherCommand.typeId)
&& Objects.equals(commandLine, anotherCommand.commandLine)
&& Objects.equals(getAttributes(), anotherCommand.getAttributes());
}
@Override
public int hashCode() {
return Objects.hash(name, type, commandLine, getAttributes());
return Objects.hash(name, typeId, commandLine, getAttributes(), getApplicableContext());
}
/** Defines the context in which command is applicable. */
public static class ApplicableContext {
private boolean workspaceApplicable;
private List<String> projects;
/** Creates new {@link ApplicableContext} which is workspace applicable. */
public ApplicableContext() {
workspaceApplicable = true;
projects = new ArrayList<>();
}
/** Creates new {@link ApplicableContext} which is applicable to the single project only. */
public ApplicableContext(String projectPath) {
projects = new ArrayList<>();
projects.add(projectPath);
}
/** Creates new {@link ApplicableContext} based on the provided data. */
public ApplicableContext(boolean workspaceApplicable, List<String> projects) {
this.workspaceApplicable = workspaceApplicable;
this.projects = projects;
}
/** Creates copy of the given {@code context}. */
public ApplicableContext(ApplicableContext context) {
this(context.isWorkspaceApplicable(), new ArrayList<>(context.getApplicableProjects()));
}
/** Returns {@code true} if command is applicable to the workspace and {@code false} otherwise. */
public boolean isWorkspaceApplicable() {
return workspaceApplicable;
}
/** Sets whether the command should be applicable to the workspace or not. */
public void setWorkspaceApplicable(boolean applicable) {
this.workspaceApplicable = applicable;
}
/** Returns <b>immutable</b> list of the paths of the applicable projects. */
public List<String> getApplicableProjects() {
return unmodifiableList(projects);
}
/** Adds applicable project's path. */
public void addProject(String path) {
projects.add(path);
}
/** Removes applicable project's path. */
public void removeProject(String path) {
projects.remove(path);
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof ApplicableContext)) {
return false;
}
ApplicableContext other = (ApplicableContext)o;
return workspaceApplicable == other.workspaceApplicable &&
Objects.equals(projects, other.projects);
}
@Override
public int hashCode() {
return Objects.hash(workspaceApplicable, projects);
}
}
}

View File

@ -10,73 +10,158 @@
*******************************************************************************/
package org.eclipse.che.ide.api.command;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.ide.api.macro.Macro;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.command.CommandImpl.ApplicableContext;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* Facade for command related operations.
* Facade for command management.
*
* @author Artem Zatsarynnyi
* @see CommandImpl
*/
public interface CommandManager {
/** Returns all commands. */
List<CommandImpl> getCommands();
/**
* Creates new command of the specified type.
* <p><b>Note</b> that command's name will be generated by {@link CommandManager}
* and command line will be provided by an appropriate {@link CommandType}.
*/
Promise<CommandImpl> create(String type);
/** Returns optional command by the specified name or {@link Optional#empty()} if none. */
Optional<CommandImpl> getCommand(String name);
/** Returns commands which are applicable to the current IDE context. */
List<CommandImpl> getApplicableCommands();
/** Checks whether the given {@code command} is applicable to the current IDE context or not. */
boolean isCommandApplicable(CommandImpl command);
/**
* Creates new command with the specified arguments.
* <p><b>Note</b> that name of the created command may differ from
* the specified {@code desirableName} in order to prevent name duplication.
* Creates new command based on the given data.
* Command's name and command line will be generated automatically.
* Command will be bound to the workspace.
*
* @param goalId
* ID of the goal to which created command should belong
* @param typeId
* ID of the type to which created command should belong
* @return created command
*/
Promise<CommandImpl> create(String desirableName, String commandLine, String type, Map<String, String> attributes);
Promise<CommandImpl> createCommand(String goalId, String typeId);
/**
* Creates new command based on the given data.
* Command's name and command line will be generated automatically.
*
* @param goalId
* ID of the goal to which created command should belong
* @param typeId
* ID of the type to which created command should belong
* @param context
* command's {@link ApplicableContext}
* @return created command
*/
Promise<CommandImpl> createCommand(String goalId, String typeId, ApplicableContext context);
/**
* Creates new command based on the given data. Command will be bound to the workspace.
*
* @param goalId
* ID of the goal to which created command should belong
* @param typeId
* ID of the type to which created command should belong
* @param name
* command's name.
* <strong>Note</strong> that actual name may differ from the given one in order to prevent duplication.
* If {@code null}, name will be generated automatically.
* @param commandLine
* actual command line. If {@code null}, command line will be generated by the corresponding command type.
* @param attributes
* command's attributes
* @return created command
*/
Promise<CommandImpl> createCommand(String goalId,
String typeId,
@Nullable String name,
@Nullable String commandLine,
Map<String, String> attributes);
/**
* Creates new command based on the given data.
*
* @param goalId
* ID of the goal to which created command should belong
* @param typeId
* ID of the type to which created command should belong
* @param name
* command's name.
* <strong>Note</strong> that actual name may differ from the given one in order to prevent duplication.
* If {@code null}, name will be generated automatically.
* @param commandLine
* actual command line. If {@code null}, command line will be generated by the corresponding command type.
* @param attributes
* command's attributes
* @param context
* command's {@link ApplicableContext}
* @return created command
*/
Promise<CommandImpl> createCommand(String goalId,
String typeId,
@Nullable String name,
@Nullable String commandLine,
Map<String, String> attributes,
ApplicableContext context);
/**
* Creates copy of the given {@code command}.
* <p><b>Note</b> that name of the created command may differ from
* the given {@code command}'s name in order to prevent name duplication.
*/
Promise<CommandImpl> createCommand(CommandImpl command);
/**
* Updates the command with the specified {@code name} by replacing it with the given {@code command}.
* <p><b>Note</b> that name of the updated command may differ from the name provided by the given {@code command}
* in order to prevent name duplication.
*/
Promise<CommandImpl> update(String name, CommandImpl command);
Promise<CommandImpl> updateCommand(String name, CommandImpl command);
/** Removes the command with the specified {@code name}. */
Promise<Void> remove(String name);
/** Removes command with the specified {@code commandName}. */
Promise<Void> removeCommand(String commandName);
/** Returns the pages for editing command of the specified {@code type}. */
List<CommandPage> getPages(String type);
void addCommandLoadedListener(CommandLoadedListener listener);
/**
* Sends the the given {@code command} to the specified {@code machine} for execution.
* <p><b>Note</b> that all {@link Macro}s will be expanded into
* real values before sending the {@code command} for execution.
*
* @param command
* command to execute
* @param machine
* machine to execute the command
* @see Macro
*/
void executeCommand(CommandImpl command, Machine machine);
void removeCommandLoadedListener(CommandLoadedListener listener);
void addCommandChangedListener(CommandChangedListener listener);
void removeCommandChangedListener(CommandChangedListener listener);
/** Listener that will be called when command has been changed. */
/** Listener to notify when all commands have been loaded. */
interface CommandLoadedListener {
/** Called when all commands have been loaded. */
void onCommandsLoaded();
}
/** Listener to notify when command has been changed. */
interface CommandChangedListener {
/** Called when command has been added. */
void onCommandAdded(CommandImpl command);
void onCommandUpdated(CommandImpl command);
/**
* Called when command has been updated.
*
* @param previousCommand
* command before updating
* @param command
* updated command
*/
void onCommandUpdated(CommandImpl previousCommand, CommandImpl command);
/** Called when command has been removed. */
void onCommandRemoved(CommandImpl command);
}
}

View File

@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.che.ide.api.editor;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.IsWidget;
import org.eclipse.che.ide.api.parts.AbstractPartPresenter;
@ -89,4 +90,9 @@ public abstract class AbstractEditorPresenter extends AbstractPartPresenter impl
public void onFileChanged() {
firePropertyChange(TITLE_PROPERTY);
}
@Override
public void onClosing(AsyncCallback<Void> callback) {
callback.onSuccess(null);
}
}

View File

@ -112,4 +112,13 @@ public interface EditorPartPresenter extends PartPresenter {
* <code>true</code> if unsaved changed should be saved, and <code>false</code> if unsaved changed should be discarded
*/
void close(boolean save);
/**
* Called when part is going to closing.
* Part can deny closing, by calling {@code callback#onFailure}.
*
* @param callback
* callback to allow or deny closing the part
*/
void onClosing(AsyncCallback<Void> callback);
}

View File

@ -76,7 +76,7 @@ public class DocumentStorageImpl implements DocumentStorage {
@Override
public void apply(Void arg) throws OperationException {
Log.debug(DocumentStorageImpl.class, "Document saved (" + file.getLocation() + ").");
DocumentStorageImpl.this.eventBus.fireEvent(FileEvent.createSaveFileEvent(file));
DocumentStorageImpl.this.eventBus.fireEvent(FileEvent.createFileSavedEvent(file));
try {
callback.onSuccess(editorInput);
} catch (final Exception e) {

View File

@ -79,6 +79,18 @@ public interface EditorWidget extends IsWidget,
*/
void setReadOnly(boolean isReadOnly);
/** Sets whether the annotation ruler is visible. */
void setAnnotationRulerVisible(boolean show);
/** Sets whether the folding ruler is visible. */
void setFoldingRulerVisible(boolean show);
/** Sets whether the zoom ruler is visible. */
void setZoomRulerVisible(boolean show);
/** Sets whether the overview ruler is visible. */
void setOverviewRulerVisible(boolean show);
/**
* Returns the readonly state of the editor.
*

View File

@ -10,19 +10,21 @@
*******************************************************************************/
package org.eclipse.che.ide.api.event;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.parts.EditorTab;
import org.eclipse.che.ide.api.resources.VirtualFile;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.parts.EditorTab;
import org.eclipse.che.ide.api.resources.VirtualFile;
import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.CLOSE;
import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.OPEN;
import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.SAVE;
/**
* Event that describes the fact that file is going to be opened.
* Event that describes the fact that file is opened/closed/saved.
*
* @author Nikolay Zamosenchuk
* @author Artem Zatsarynnyi
@ -30,11 +32,6 @@ import static org.eclipse.che.ide.api.event.FileEvent.FileOperation.SAVE;
*/
public class FileEvent extends GwtEvent<FileEvent.FileEventHandler> {
/** Handles OpenFileEvent */
public interface FileEventHandler extends EventHandler {
void onFileOperation(FileEvent event);
}
public static Type<FileEventHandler> TYPE = new Type<>();
private VirtualFile file;
private FileOperation fileOperation;
@ -69,6 +66,12 @@ public class FileEvent extends GwtEvent<FileEvent.FileEventHandler> {
/**
* Creates a event for {@code FileOperation.OPEN}.
*/
public static FileEvent createFileOpenedEvent(VirtualFile file) {
return new FileEvent(file, OPEN);
}
/** @deprecated use {@link EditorAgent#openEditor(org.eclipse.che.ide.api.resources.VirtualFile)} */
@Deprecated
public static FileEvent createOpenFileEvent(VirtualFile file) {
return new FileEvent(file, OPEN);
}
@ -80,6 +83,12 @@ public class FileEvent extends GwtEvent<FileEvent.FileEventHandler> {
* @param tab
* tab of the file to close
*/
public static FileEvent createFileClosedEvent(EditorTab tab) {
return new FileEvent(tab, CLOSE);
}
/** @deprecated use {@link EditorAgent#closeEditor(EditorPartPresenter)} */
@Deprecated
public static FileEvent createCloseFileEvent(EditorTab tab) {
return new FileEvent(tab, CLOSE);
}
@ -87,6 +96,11 @@ public class FileEvent extends GwtEvent<FileEvent.FileEventHandler> {
/**
* Creates a event for {@code FileOperation.SAVE}.
*/
public static FileEvent createFileSavedEvent(VirtualFile file) {
return new FileEvent(file, SAVE);
}
@Deprecated
public static FileEvent createSaveFileEvent(VirtualFile file) {
return new FileEvent(file, SAVE);
}
@ -120,4 +134,9 @@ public class FileEvent extends GwtEvent<FileEvent.FileEventHandler> {
public enum FileOperation {
OPEN, SAVE, CLOSE
}
/** Handles OpenFileEvent */
public interface FileEventHandler extends EventHandler {
void onFileOperation(FileEvent event);
}
}

View File

@ -14,11 +14,10 @@ import com.google.gwt.core.client.GWT;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.Image;
import org.eclipse.che.commons.annotation.Nullable;
import org.vectomatic.dom.svg.ui.SVGImage;
import org.vectomatic.dom.svg.ui.SVGResource;
import org.eclipse.che.commons.annotation.Nullable;
/**
* Icon.
*
@ -129,4 +128,14 @@ public class Icon {
}
return new SVGImage(svgResource);
}
/**
* Returns {@link SVGResource} widget.
*
* @return {@link SVGResource} widget
*/
@Nullable
public SVGResource getSVGResource() {
return svgResource;
}
}

View File

@ -8,11 +8,13 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.extension.machine.client.processes;
package org.eclipse.che.ide.api.machine.events;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import org.eclipse.che.api.core.model.machine.Machine;
/**
* @author Dmitry Shnurenko
*/
@ -27,16 +29,22 @@ public class ProcessFinishedEvent extends GwtEvent<ProcessFinishedEvent.Handler>
public static final Type<ProcessFinishedEvent.Handler> TYPE = new Type<>();
private final int processID;
private final int processID;
private final Machine machine;
public ProcessFinishedEvent(int processID) {
public ProcessFinishedEvent(int processID, Machine machine) {
this.processID = processID;
this.machine = machine;
}
public int getProcessID() {
return processID;
}
public Machine getMachine() {
return machine;
}
@Override
public Type<ProcessFinishedEvent.Handler> getAssociatedType() {
return TYPE;

View File

@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.api.machine.events;
import com.google.gwt.event.shared.EventHandler;
import com.google.gwt.event.shared.GwtEvent;
import org.eclipse.che.api.core.model.machine.Machine;
public class ProcessStartedEvent extends GwtEvent<ProcessStartedEvent.Handler> {
public static final Type<ProcessStartedEvent.Handler> TYPE = new Type<>();
private final int processID;
private final Machine machine;
public ProcessStartedEvent(int processID, Machine machine) {
this.processID = processID;
this.machine = machine;
}
public int getProcessID() {
return processID;
}
public Machine getMachine() {
return machine;
}
@Override
public Type<ProcessStartedEvent.Handler> getAssociatedType() {
return TYPE;
}
@Override
protected void dispatch(Handler handler) {
handler.onProcessStarted(this);
}
public interface Handler extends EventHandler {
void onProcessStarted(ProcessStartedEvent event);
}
}

View File

@ -8,32 +8,29 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.macro;
package org.eclipse.che.ide.api.macro;
import com.google.common.annotations.Beta;
import com.google.common.base.Objects;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.js.Promises;
import org.eclipse.che.ide.api.macro.Macro;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Custom macro provider which allows to register user's macro with the provided value.
* Base implementation of {@link Macro}.
*
* @author Vlad Zhukovskyi
* @see Macro
* @since 4.7.0
*/
@Beta
public class CustomMacro implements Macro {
public class BaseMacro implements Macro {
private final String key;
private final String value;
private final String description;
public CustomMacro(String key, String value, String description) {
public BaseMacro(String key, String value, String description) {
this.key = checkNotNull(key, "Key should not be null");
this.value = checkNotNull(value, "Value should not be null");
this.description = checkNotNull(description, "Description should not be null");
@ -61,7 +58,7 @@ public class CustomMacro implements Macro {
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
CustomMacro that = (CustomMacro)o;
BaseMacro that = (BaseMacro)o;
return Objects.equal(key, that.key) &&
Objects.equal(value, that.value) &&
Objects.equal(description, that.description);
@ -76,7 +73,7 @@ public class CustomMacro implements Macro {
/** {@inheritDoc} */
@Override
public String toString() {
return "CustomMacro{" +
return "BaseMacro{" +
"key='" + key + '\'' +
", value='" + value + '\'' +
", description='" + description + '\'' +

View File

@ -22,6 +22,7 @@ import java.util.Set;
* Also macro can be registered in 'runtime' with {@link MacroRegistry#register(Set)}.
*
* @author Artem Zatsarynnyi
* @see BaseMacro
* @see MacroProcessor#expandMacros(String)
* @see MacroRegistry
*/

View File

@ -10,17 +10,13 @@
*******************************************************************************/
package org.eclipse.che.ide.api.macro;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
/**
* Expands all {@link Macro}s in the given string.
*
* @author Artem Zatsarynnyi
* @see Macro
* @see CommandManager#executeCommand(CommandImpl, Machine)
*/
public interface MacroProcessor {

View File

@ -22,8 +22,7 @@ import org.eclipse.che.ide.resource.Path;
* Implementation for {@link VirtualFile} which describe resource which doesn't exist on file system and is auto generated.
* For example it may be effective version of such resource.
* <p/>
* This file is read only and doesn't have link to the content url.
* Calling {@link #updateContent(String)} will cause {@link UnsupportedOperationException}.
* This file doesn't have link to the content url.
*
* @author Vlad Zhukovskiy
* @see VirtualFile
@ -77,7 +76,9 @@ public class SyntheticFile implements VirtualFile {
@Override
public Promise<Void> updateContent(String content) {
throw new UnsupportedOperationException("Synthetic file is read only");
this.content = content;
return Promises.resolve(null);
}
@Override

View File

@ -8,13 +8,10 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.machine;
package org.eclipse.che.ide.api.macro;
import com.google.gwtmockito.GwtMockitoTestRunner;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.ide.macro.CustomMacro;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@ -22,42 +19,39 @@ import org.junit.runner.RunWith;
import static org.junit.Assert.assertSame;
/**
* Unit tests for the {@link CustomMacro}
* Unit tests for the {@link BaseMacro}
*
* @author Vlad Zhukovskyi
*/
@RunWith(GwtMockitoTestRunner.class)
public class CustomMacroTest {
public class BaseMacroTest {
public static final String KEY = "key";
public static final String VALUE = "value";
public static final String NAME = "name";
public static final String VALUE = "value";
public static final String DESCRIPTION = "description";
private CustomMacro provider;
private BaseMacro macro;
@Before
public void init() throws Exception {
provider = new CustomMacro(KEY, VALUE, DESCRIPTION);
macro = new BaseMacro(NAME, VALUE, DESCRIPTION);
}
@Test
public void getKey() throws Exception {
assertSame(provider.getName(), KEY);
assertSame(macro.getName(), NAME);
}
@Test
public void getValue() throws Exception {
provider.expand().then(new Operation<String>() {
@Override
public void apply(String value) throws OperationException {
assertSame(value, VALUE);
}
macro.expand().then(value -> {
assertSame(value, VALUE);
});
}
@Test
public void getDescription() throws Exception {
assertSame(provider.getDescription(), DESCRIPTION);
assertSame(macro.getDescription(), DESCRIPTION);
}
}

View File

@ -279,27 +279,6 @@
</execution>
</executions>
</plugin>
<!--<plugin>-->
<!--<groupId>org.eclipse.che.core</groupId>-->
<!--<artifactId>che-core-api-dto-maven-plugin</artifactId>-->
<!--<version>${project.version}</version>-->
<!--<executions>-->
<!--<execution>-->
<!--<phase>process-sources</phase>-->
<!--<goals>-->
<!--<goal>generate</goal>-->
<!--</goals>-->
<!--</execution>-->
<!--</executions>-->
<!--<configuration>-->
<!--<dtoPackages>-->
<!--<package>org.eclipse.che.ide.statepersistance.dto</package>-->
<!--</dtoPackages>-->
<!--<outputDirectory>${dto-generator-out-directory}</outputDirectory>-->
<!--<genClassName>org.eclipse.che.ide.statepersistance.dto.DtoClientImpls</genClassName>-->
<!--<impl>client</impl>-->
<!--</configuration>-->
<!--</plugin>-->
<plugin>
<groupId>org.eclipse.che.core</groupId>
<artifactId>che-core-dyna-provider-generator-maven-plugin</artifactId>

View File

@ -15,6 +15,7 @@ import com.google.gwt.resources.client.CssResource.NotStrict;
import com.google.gwt.resources.client.TextResource;
import org.eclipse.che.ide.api.parts.PartStackUIResources;
import org.eclipse.che.ide.command.CommandResources;
import org.eclipse.che.ide.menu.MenuResources;
import org.eclipse.che.ide.notification.NotificationResources;
import org.eclipse.che.ide.projecttype.wizard.ProjectWizardResources;
@ -46,7 +47,8 @@ public interface Resources extends Tree.Resources,
CellTreeResources,
CategoriesList.Resources,
ButtonLoaderResources,
ProjectWizardResources {
ProjectWizardResources,
CommandResources {
@Source({"Core.css", "org/eclipse/che/ide/ui/constants.css", "org/eclipse/che/ide/api/ui/style.css"})
@NotStrict
@ -208,5 +210,8 @@ public interface Resources extends Tree.Resources,
String createWsTagsPopup();
String tagsPanel();
@ClassName("codeassistant-highlight")
String codeassistantHighlight();
}
}

View File

@ -12,13 +12,12 @@ package org.eclipse.che.ide.actions;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.ide.Resources;
import org.eclipse.che.ide.api.action.AbstractPerspectiveAction;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.event.FileEvent;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.resources.File;
import org.eclipse.che.ide.api.resources.Resource;
@ -38,17 +37,16 @@ import static org.eclipse.che.ide.workspace.perspectives.project.ProjectPerspect
@Singleton
public class EditFileAction extends AbstractPerspectiveAction {
private final AppContext appContext;
private final EventBus eventBus;
private final AppContext appContext;
private final EditorAgent editorAgent;
@Inject
public EditFileAction(AppContext appContext,
EventBus eventBus,
Resources resources) {
Resources resources,
EditorAgent editorAgent) {
super(singletonList(PROJECT_PERSPECTIVE_ID), "Edit file", null, null, resources.defaultFile());
this.appContext = appContext;
this.eventBus = eventBus;
this.editorAgent = editorAgent;
}
/** {@inheritDoc} */
@ -59,7 +57,7 @@ public class EditFileAction extends AbstractPerspectiveAction {
checkState(resources != null && resources.length == 1 && resources[0] instanceof File,
"Files only are allowed to be opened in editor");
eventBus.fireEvent(FileEvent.createOpenFileEvent((File)resources[0]));
editorAgent.openEditor((File)resources[0]);
}
/** {@inheritDoc} */

View File

@ -28,10 +28,10 @@ import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.PromisableAction;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.event.ActivePartChangedEvent;
import org.eclipse.che.ide.api.event.ActivePartChangedHandler;
import org.eclipse.che.ide.api.event.FileEvent;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.resources.File;
import org.eclipse.che.ide.resource.Path;
@ -43,6 +43,7 @@ import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAI
/**
* TODO maybe rename it to factory open file?
*
* @author Sergii Leschenko
* @author Vlad Zhukovskyi
*/
@ -56,6 +57,7 @@ public class OpenFileAction extends Action implements PromisableAction {
private final CoreLocalizationConstant localization;
private final NotificationManager notificationManager;
private final AppContext appContext;
private final EditorAgent editorAgent;
private Callback<Void, Throwable> actionCompletedCallback;
@ -63,11 +65,13 @@ public class OpenFileAction extends Action implements PromisableAction {
public OpenFileAction(EventBus eventBus,
CoreLocalizationConstant localization,
NotificationManager notificationManager,
AppContext appContext) {
AppContext appContext,
EditorAgent editorAgent) {
this.eventBus = eventBus;
this.localization = localization;
this.notificationManager = notificationManager;
this.appContext = appContext;
this.editorAgent = editorAgent;
}
@Override
@ -91,7 +95,7 @@ public class OpenFileAction extends Action implements PromisableAction {
actionCompletedCallback.onSuccess(null);
}
eventBus.fireEvent(FileEvent.createOpenFileEvent(optionalFile.get()));
editorAgent.openEditor(optionalFile.get());
} else {
if (actionCompletedCallback != null) {
actionCompletedCallback.onFailure(null);

View File

@ -10,11 +10,10 @@
*******************************************************************************/
package org.eclipse.che.ide.client;
import com.google.gwt.dom.client.Document;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.user.client.Window;
@ -97,7 +96,7 @@ public class BootstrapController {
@Inject
private void startComponents(Map<String, Provider<Component>> components) {
startComponents(components.entrySet().iterator());
startComponents(components.values().iterator());
}
@Inject
@ -132,37 +131,23 @@ public class BootstrapController {
});
}
private void startComponents(final Iterator<Map.Entry<String, Provider<Component>>> componentIterator) {
if (componentIterator.hasNext()) {
Map.Entry<String, Provider<Component>> entry = componentIterator.next();
final String componentName = entry.getKey();
private void startComponents(final Iterator<Provider<Component>> componentProviderIterator) {
if (componentProviderIterator.hasNext()) {
Provider<Component> componentProvider = componentProviderIterator.next();
try {
Provider<Component> componentProvider = entry.getValue();
final Component component = componentProvider.get();
component.start(new Callback<Component, Exception>() {
@Override
public void onSuccess(Component result) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
startComponents(componentIterator);
}
});
}
@Override
public void onFailure(Exception reason) {
Log.error(getClass(), "Unable to start " + componentName, reason);
initializationFailed(reason.getMessage());
}
});
} catch (Exception e) {
Log.error(getClass(), "Unable to start " + componentName, e);
initializationFailed(e.getMessage());
}
final Component component = componentProvider.get();
component.start(new Callback<Component, Exception>() {
@Override
public void onSuccess(Component result) {
startComponents(componentProviderIterator);
}
@Override
public void onFailure(Exception reason) {
Log.error(component.getClass(), reason);
initializationFailed(reason.getMessage());
}
});
} else {
startExtensionsAndDisplayUI();
}

View File

@ -13,10 +13,63 @@ package org.eclipse.che.ide.command;
import com.google.gwt.inject.client.AbstractGinModule;
import com.google.gwt.inject.client.assistedinject.GinFactoryModuleBuilder;
import com.google.gwt.inject.client.multibindings.GinMapBinder;
import com.google.gwt.inject.client.multibindings.GinMultibinder;
import com.google.inject.Provides;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.eclipse.che.ide.Resources;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandGoalRegistry;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandType;
import org.eclipse.che.ide.api.command.CommandTypeRegistry;
import org.eclipse.che.ide.api.component.Component;
import org.eclipse.che.ide.api.filetypes.FileType;
import org.eclipse.che.ide.command.editor.CommandEditorView;
import org.eclipse.che.ide.command.editor.CommandEditorViewImpl;
import org.eclipse.che.ide.command.editor.page.goal.GoalPageView;
import org.eclipse.che.ide.command.editor.page.goal.GoalPageViewImpl;
import org.eclipse.che.ide.command.editor.page.name.NamePageView;
import org.eclipse.che.ide.command.editor.page.name.NamePageViewImpl;
import org.eclipse.che.ide.command.editor.page.project.ProjectsPageView;
import org.eclipse.che.ide.command.editor.page.project.ProjectsPageViewImpl;
import org.eclipse.che.ide.command.editor.page.text.PageWithTextEditorView;
import org.eclipse.che.ide.command.editor.page.text.PageWithTextEditorViewImpl;
import org.eclipse.che.ide.command.execute.ExecuteCommandActionFactory;
import org.eclipse.che.ide.command.execute.ExecuteCommandActionManager;
import org.eclipse.che.ide.command.execute.GoalPopUpGroupFactory;
import org.eclipse.che.ide.command.explorer.CommandsExplorerPresenter;
import org.eclipse.che.ide.command.explorer.CommandsExplorerView;
import org.eclipse.che.ide.command.explorer.CommandsExplorerViewImpl;
import org.eclipse.che.ide.command.goal.BuildGoal;
import org.eclipse.che.ide.command.goal.CommandGoalRegistryImpl;
import org.eclipse.che.ide.command.goal.CommonGoal;
import org.eclipse.che.ide.command.goal.DebugGoal;
import org.eclipse.che.ide.command.goal.DeployGoal;
import org.eclipse.che.ide.command.goal.RunGoal;
import org.eclipse.che.ide.command.goal.TestGoal;
import org.eclipse.che.ide.command.manager.CommandManagerImpl;
import org.eclipse.che.ide.command.node.NodeFactory;
import org.eclipse.che.ide.command.palette.CommandsPaletteView;
import org.eclipse.che.ide.command.palette.CommandsPaletteViewImpl;
import org.eclipse.che.ide.command.producer.CommandProducerActionFactory;
import org.eclipse.che.ide.command.producer.CommandProducerActionManager;
import org.eclipse.che.ide.command.toolbar.CommandToolbarView;
import org.eclipse.che.ide.command.toolbar.CommandToolbarViewImpl;
import org.eclipse.che.ide.command.toolbar.ToolbarButtonsFactory;
import org.eclipse.che.ide.command.toolbar.commands.ExecuteCommandView;
import org.eclipse.che.ide.command.toolbar.commands.ExecuteCommandViewImpl;
import org.eclipse.che.ide.command.toolbar.commands.button.PopupItemFactory;
import org.eclipse.che.ide.command.toolbar.previews.PreviewsView;
import org.eclipse.che.ide.command.toolbar.previews.PreviewsViewImpl;
import org.eclipse.che.ide.command.toolbar.processes.ProcessesListView;
import org.eclipse.che.ide.command.toolbar.processes.ProcessesListViewImpl;
import org.eclipse.che.ide.command.type.CommandTypeRegistryImpl;
import org.eclipse.che.ide.command.type.chooser.CommandTypeChooserView;
import org.eclipse.che.ide.command.type.chooser.CommandTypeChooserViewImpl;
import static org.eclipse.che.ide.command.node.CommandFileNode.FILE_TYPE_EXT;
/**
* GIN module for configuring Command API components.
@ -27,11 +80,67 @@ public class CommandApiModule extends AbstractGinModule {
@Override
protected void configure() {
bind(CommandTypeRegistry.class).to(CommandTypeRegistryImpl.class).in(Singleton.class);
GinMultibinder.newSetBinder(binder(), CommandType.class);
// predefined goals
GinMultibinder<CommandGoal> goalBinder = GinMultibinder.newSetBinder(binder(), CommandGoal.class);
goalBinder.addBinding().to(BuildGoal.class);
goalBinder.addBinding().to(TestGoal.class);
goalBinder.addBinding().to(RunGoal.class);
goalBinder.addBinding().to(DebugGoal.class);
goalBinder.addBinding().to(DeployGoal.class);
goalBinder.addBinding().to(CommonGoal.class);
bind(CommandTypeRegistry.class).to(CommandTypeRegistryImpl.class).in(Singleton.class);
bind(CommandGoalRegistry.class).to(CommandGoalRegistryImpl.class).in(Singleton.class);
bind(CommandManager.class).to(CommandManagerImpl.class).in(Singleton.class);
// start-up components
GinMapBinder<String, Component> componentBinder = GinMapBinder.newMapBinder(binder(), String.class, Component.class);
componentBinder.addBinding("CommandManagerImpl").to(CommandManagerImpl.class);
componentBinder.addBinding("CommandsExplorerPresenter").to(CommandsExplorerPresenter.class);
componentBinder.addBinding("CommandProducerActionManager").to(CommandProducerActionManager.class);
componentBinder.addBinding("ExecuteCommandActionManager").to(ExecuteCommandActionManager.class);
install(new GinFactoryModuleBuilder().build(ExecuteCommandActionFactory.class));
install(new GinFactoryModuleBuilder().build(GoalPopUpGroupFactory.class));
install(new GinFactoryModuleBuilder().build(NodeFactory.class));
install(new GinFactoryModuleBuilder().build(CommandProducerActionFactory.class));
GinMapBinder<String, Component> componentsBinder = GinMapBinder.newMapBinder(binder(), String.class, Component.class);
componentsBinder.addBinding("CommandProducerActionManager").to(CommandProducerActionManager.class);
bind(CommandsExplorerView.class).to(CommandsExplorerViewImpl.class).in(Singleton.class);
bind(CommandTypeChooserView.class).to(CommandTypeChooserViewImpl.class);
bind(CommandsPaletteView.class).to(CommandsPaletteViewImpl.class).in(Singleton.class);
// command editor
bind(CommandEditorView.class).to(CommandEditorViewImpl.class);
bind(NamePageView.class).to(NamePageViewImpl.class);
bind(GoalPageView.class).to(GoalPageViewImpl.class);
bind(ProjectsPageView.class).to(ProjectsPageViewImpl.class);
bind(PageWithTextEditorView.class).to(PageWithTextEditorViewImpl.class);
// toolbar
bind(CommandToolbarView.class).to(CommandToolbarViewImpl.class).in(Singleton.class);
bind(ExecuteCommandView.class).to(ExecuteCommandViewImpl.class).in(Singleton.class);
bind(ProcessesListView.class).to(ProcessesListViewImpl.class).in(Singleton.class);
bind(PreviewsView.class).to(PreviewsViewImpl.class).in(Singleton.class);
install(new GinFactoryModuleBuilder().build(ToolbarButtonsFactory.class));
install(new GinFactoryModuleBuilder().build(PopupItemFactory.class));
}
@Provides
@Singleton
@Named("CommandFileType")
protected FileType provideCommandFileType(Resources resources) {
return new FileType(resources.defaultImage(), FILE_TYPE_EXT);
}
/** Provides the goal which is used for grouping commands which doesn't belong to any goal. */
@Provides
@Named("default")
@Singleton
protected CommandGoal provideDefaultGoal(CommonGoal commonGoal) {
return commonGoal;
}
}

View File

@ -0,0 +1,99 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.DataResource;
import org.vectomatic.dom.svg.ui.SVGResource;
/**
* Client bundle for Command related resources.
*
* @author Artem Zatsarynnyi
*/
public interface CommandResources extends ClientBundle {
/** Resource is used as CSS constant's value for setting 'background-image' property. */
@DataResource.MimeType("image/svg+xml")
@Source("magnifier.svg")
DataResource magnifier();
@Source("explorer/explorer-part.svg")
SVGResource explorerPart();
@Source("explorer/add-command-button.svg")
SVGResource addCommand();
@Source("explorer/duplicate-command-button.svg")
SVGResource duplicateCommand();
@Source("explorer/remove-command-button.svg")
SVGResource removeCommand();
@Source({"explorer/styles.css", "org/eclipse/che/ide/api/ui/style.css"})
ExplorerCSS commandsExplorerCss();
@Source({"palette/styles.css", "org/eclipse/che/ide/api/ui/style.css"})
PaletteCSS commandsPaletteCss();
@Source({"toolbar/processes/styles.css", "org/eclipse/che/ide/api/ui/style.css"})
ToolbarCSS commandToolbarCss();
@Source({"editor/styles.css", "org/eclipse/che/ide/api/ui/style.css"})
EditorCSS editorCss();
@Source({"type/styles.css", "org/eclipse/che/ide/api/ui/style.css"})
CommandTypeChooserCSS commandTypeChooserCss();
interface ExplorerCSS extends CssResource {
String commandGoalNode();
String commandNode();
String commandNodeText();
String commandNodeButtonsPanel();
}
interface PaletteCSS extends CssResource {
String filterField();
}
interface ToolbarCSS extends CssResource {
String toolbarButton();
String processesListLabel();
String processWidgetText();
String processWidgetMachineNameLabel();
String processWidgetCommandNameLabel();
String processWidgetPidLabel();
String processWidgetActionButton();
String previewUrlWidget();
}
interface EditorCSS extends CssResource {
String sectionLabel();
String section();
}
interface CommandTypeChooserCSS extends CssResource {
String chooserPopup();
}
}

View File

@ -0,0 +1,112 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandType;
import org.eclipse.che.ide.api.command.CommandTypeRegistry;
import org.eclipse.che.ide.api.command.CommandGoalRegistry;
import org.eclipse.che.ide.api.icon.Icon;
import org.eclipse.che.ide.api.icon.IconRegistry;
import org.vectomatic.dom.svg.ui.SVGImage;
import org.vectomatic.dom.svg.ui.SVGResource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
/**
* A smattering of useful methods to work with commands.
*
* @author Artem Zatsarynnyi
*/
@Singleton
public class CommandUtils {
private final CommandGoalRegistry goalRegistry;
private final CommandTypeRegistry commandTypeRegistry;
private final IconRegistry iconRegistry;
@Inject
public CommandUtils(CommandGoalRegistry commandGoalRegistry,
CommandTypeRegistry commandTypeRegistry,
IconRegistry iconRegistry) {
this.goalRegistry = commandGoalRegistry;
this.commandTypeRegistry = commandTypeRegistry;
this.iconRegistry = iconRegistry;
}
/**
* Groups the given {@code commands} by its goal.
*
* @return map that contains the given {@code commands} grouped by its goal
*/
public Map<CommandGoal, List<CommandImpl>> groupCommandsByGoal(List<CommandImpl> commands) {
final Map<CommandGoal, List<CommandImpl>> commandsByGoal = new HashMap<>();
for (CommandImpl command : commands) {
final String goalId = command.getGoal();
final CommandGoal commandGoal = goalRegistry.getGoalForId(goalId);
commandsByGoal.computeIfAbsent(commandGoal, key -> new ArrayList<>())
.add(command);
}
return commandsByGoal;
}
/** Returns the icon for the given command type ID or {@code null} if none. */
@Nullable
public SVGResource getCommandTypeIcon(String typeId) {
final CommandType commandType = commandTypeRegistry.getCommandTypeById(typeId);
if (commandType != null) {
final Icon icon = iconRegistry.getIconIfExist("command.type." + commandType.getId());
if (icon != null) {
final SVGImage svgImage = icon.getSVGImage();
if (svgImage != null) {
return icon.getSVGResource();
}
}
}
return null;
}
/** Returns the icon for the given command goal ID or {@code null} if none. */
@Nullable
public SVGResource getCommandGoalIcon(String goalId) {
final Optional<CommandGoal> goalOptional = goalRegistry.getPredefinedGoalById(goalId);
if (goalOptional.isPresent()) {
final Icon icon = iconRegistry.getIconIfExist("command.goal." + goalOptional.get().getId());
if (icon != null) {
final SVGImage svgImage = icon.getSVGImage();
if (svgImage != null) {
return icon.getSVGResource();
}
}
}
return null;
}
}

View File

@ -0,0 +1,305 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor;
import com.google.common.annotations.VisibleForTesting;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.CoreLocalizationConstant;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandManager.CommandChangedListener;
import org.eclipse.che.ide.api.dialogs.DialogFactory;
import org.eclipse.che.ide.api.editor.AbstractEditorPresenter;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.editor.EditorInput;
import org.eclipse.che.ide.api.icon.Icon;
import org.eclipse.che.ide.api.icon.IconRegistry;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.parts.WorkspaceAgent;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.eclipse.che.ide.command.editor.page.CommandEditorPage;
import org.eclipse.che.ide.command.editor.page.commandline.CommandLinePage;
import org.eclipse.che.ide.command.editor.page.goal.GoalPage;
import org.eclipse.che.ide.command.editor.page.name.NamePage;
import org.eclipse.che.ide.command.editor.page.previewurl.PreviewUrlPage;
import org.eclipse.che.ide.command.editor.page.project.ProjectsPage;
import org.eclipse.che.ide.command.node.CommandFileNode;
import org.eclipse.che.ide.command.node.NodeFactory;
import org.vectomatic.dom.svg.ui.SVGImage;
import org.vectomatic.dom.svg.ui.SVGResource;
import java.util.LinkedList;
import java.util.List;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.EMERGE_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.WARNING;
/** Presenter for command editor. */
public class CommandEditor extends AbstractEditorPresenter implements CommandEditorView.ActionDelegate,
CommandChangedListener {
private final CommandEditorView view;
private final WorkspaceAgent workspaceAgent;
private final IconRegistry iconRegistry;
private final CommandManager commandManager;
private final NotificationManager notificationManager;
private final DialogFactory dialogFactory;
private final EditorAgent editorAgent;
private final CoreLocalizationConstant coreMessages;
private final EditorMessages messages;
private final NodeFactory nodeFactory;
private final List<CommandEditorPage> pages;
/** Edited command. */
@VisibleForTesting
protected CommandImpl editedCommand;
/** Initial (before any modification) name of the edited command. */
private String commandNameInitial;
@Inject
public CommandEditor(CommandEditorView view,
WorkspaceAgent workspaceAgent,
IconRegistry iconRegistry,
CommandManager commandManager,
NamePage namePage,
ProjectsPage projectsPage,
CommandLinePage commandLinePage,
GoalPage goalPage,
PreviewUrlPage previewUrlPage,
NotificationManager notificationManager,
DialogFactory dialogFactory,
EditorAgent editorAgent,
CoreLocalizationConstant coreMessages,
EditorMessages messages,
NodeFactory nodeFactory) {
this.view = view;
this.workspaceAgent = workspaceAgent;
this.iconRegistry = iconRegistry;
this.commandManager = commandManager;
this.notificationManager = notificationManager;
this.dialogFactory = dialogFactory;
this.editorAgent = editorAgent;
this.coreMessages = coreMessages;
this.messages = messages;
this.nodeFactory = nodeFactory;
view.setDelegate(this);
commandManager.addCommandChangedListener(this);
pages = new LinkedList<>();
pages.add(previewUrlPage);
pages.add(projectsPage);
pages.add(goalPage);
pages.add(commandLinePage);
pages.add(namePage);
}
@Override
public void go(AcceptsOneWidget container) {
container.setWidget(getView());
}
@Override
protected void initializeEditor(EditorAgent.OpenEditorCallback callback) {
final VirtualFile file = getEditorInput().getFile();
if (file instanceof CommandFileNode) {
// make a copy of the given command to avoid modifying of the provided command
editedCommand = new CommandImpl(((CommandFileNode)file).getData());
initializePages();
pages.forEach(page -> view.addPage(page.getView(), page.getTitle()));
} else {
callback.onInitializationFailed();
}
}
/** Initialize editor's pages with the edited command. */
private void initializePages() {
commandNameInitial = editedCommand.getName();
pages.forEach(page -> {
page.edit(editedCommand);
page.setDirtyStateListener(() -> {
updateDirtyState(isDirtyPage());
view.setSaveEnabled(isDirtyPage());
});
});
}
/** Checks whether any page is dirty. */
private boolean isDirtyPage() {
for (CommandEditorPage page : pages) {
if (page.isDirty()) {
return true;
}
}
return false;
}
@Nullable
@Override
public SVGResource getTitleImage() {
final VirtualFile file = getEditorInput().getFile();
if (file instanceof CommandFileNode) {
final CommandImpl command = ((CommandFileNode)file).getData();
final Icon icon = iconRegistry.getIconIfExist("command.type." + command.getType());
if (icon != null) {
final SVGImage svgImage = icon.getSVGImage();
if (svgImage != null) {
return icon.getSVGResource();
}
}
}
return input.getSVGResource();
}
@Override
public String getTitle() {
return (isDirty() ? "* " : "") + input.getName();
}
@Override
public IsWidget getView() {
return view;
}
@Nullable
@Override
public String getTitleToolTip() {
return input.getName();
}
@Override
public void doSave() {
doSave(new AsyncCallback<EditorInput>() {
@Override
public void onFailure(Throwable caught) {
}
@Override
public void onSuccess(EditorInput result) {
}
});
}
@Override
public void doSave(AsyncCallback<EditorInput> callback) {
commandManager.updateCommand(commandNameInitial, editedCommand).then(arg -> {
updateDirtyState(false);
// according to the CommandManager#updateCommand contract
// command's name after updating may differ from the proposed name
// in order to prevent name duplication
editedCommand.setName(arg.getName());
if (!commandNameInitial.equals(editedCommand.getName())) {
input.setFile(nodeFactory.newCommandFileNode(editedCommand));
}
initializePages();
callback.onSuccess(getEditorInput());
}).catchError((Operation<PromiseError>)arg -> {
notificationManager.notify(messages.editorMessageUnableToSave(),
arg.getMessage(),
WARNING,
EMERGE_MODE);
callback.onFailure(arg.getCause());
throw new OperationException(arg.getMessage());
});
}
@Override
public void doSaveAs() {
}
@Override
public void activate() {
}
@Override
public void close(boolean save) {
workspaceAgent.removePart(this);
}
@Override
public void onClosing(AsyncCallback<Void> callback) {
if (!isDirty()) {
callback.onSuccess(null);
} else {
dialogFactory.createChoiceDialog(
coreMessages.askWindowCloseTitle(),
coreMessages.messagesSaveChanges(getEditorInput().getName()),
coreMessages.yesButtonTitle(),
coreMessages.noButtonTitle(),
coreMessages.cancelButton(),
() -> doSave(new AsyncCallback<EditorInput>() {
@Override
public void onSuccess(EditorInput result) {
callback.onSuccess(null);
}
@Override
public void onFailure(Throwable caught) {
callback.onFailure(null);
}
}),
() -> callback.onSuccess(null),
() -> callback.onFailure(null)).show();
}
}
@Override
public void onCommandCancel() {
close(false);
}
@Override
public void onCommandSave() {
doSave();
}
@Override
public void onCommandAdded(CommandImpl command) {
}
@Override
public void onCommandUpdated(CommandImpl previousCommand, CommandImpl command) {
}
@Override
public void onCommandRemoved(CommandImpl command) {
if (command.getName().equals(editedCommand.getName())) {
editorAgent.closeEditor(this);
Scheduler.get().scheduleDeferred(() -> commandManager.removeCommandChangedListener(this));
}
}
}

View File

@ -0,0 +1,49 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor;
import com.google.inject.Inject;
import com.google.inject.Provider;
import org.eclipse.che.ide.api.editor.EditorPartPresenter;
import org.eclipse.che.ide.api.editor.EditorProvider;
/**
* Provides {@link CommandEditor} instances.
*
* @author Artem Zatsarynnyi
*/
public class CommandEditorProvider implements EditorProvider {
private final Provider<CommandEditor> editorProvider;
private final EditorMessages editorMessages;
@Inject
public CommandEditorProvider(Provider<CommandEditor> editorProvider, EditorMessages editorMessages) {
this.editorProvider = editorProvider;
this.editorMessages = editorMessages;
}
@Override
public String getId() {
return "che_command_editor";
}
@Override
public String getDescription() {
return editorMessages.editorDescription();
}
@Override
public EditorPartPresenter getEditor() {
return editorProvider.get();
}
}

View File

@ -0,0 +1,51 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor;
import com.google.gwt.user.client.ui.IsWidget;
import org.eclipse.che.ide.api.mvp.View;
/**
* The view for {@link CommandEditor}.
*
* @author Artem Zatsarynnyi
*/
public interface CommandEditorView extends View<CommandEditorView.ActionDelegate> {
/**
* Add page to the view. New page will be added to the top.
*
* @param page
* page to add
* @param title
* text that should be used as page's title
*/
void addPage(IsWidget page, String title);
/**
* Set whether saving command is enabled or not.
*
* @param enable
* {@code true} if command saving is enabled and {@code false} otherwise
*/
void setSaveEnabled(boolean enable);
/** The action delegate for this view. */
interface ActionDelegate {
/** Called when reverting command changes is requested. */
void onCommandCancel();
/** Called when saving command is requested. */
void onCommandSave();
}
}

View File

@ -0,0 +1,109 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import org.eclipse.che.ide.command.CommandResources;
import org.eclipse.che.ide.ui.window.Window;
/**
* Implementation of {@link CommandEditorView}.
*
* @author Artem Zatsarynnyi
*/
public class CommandEditorViewImpl extends Composite implements CommandEditorView {
private static final CommandEditorViewImplUiBinder UI_BINDER = GWT.create(CommandEditorViewImplUiBinder.class);
private static final Window.Resources WINDOW_RESOURCES = GWT.create(Window.Resources.class);
private final CommandResources resources;
@UiField
Button cancelButton;
@UiField
Button saveButton;
@UiField
ScrollPanel scrollPanel;
@UiField
FlowPanel pagesPanel;
/** The delegate to receive events from this view. */
private ActionDelegate delegate;
@Inject
public CommandEditorViewImpl(CommandResources resources) {
this.resources = resources;
initWidget(UI_BINDER.createAndBindUi(this));
setSaveEnabled(false);
saveButton.addStyleName(WINDOW_RESOURCES.windowCss().primaryButton());
}
@Override
public void addPage(IsWidget page, String title) {
page.asWidget().addStyleName(resources.editorCss().section());
pagesPanel.insert(page, 0);
if (!title.isEmpty()) {
Label label = new Label(title);
label.addStyleName(resources.editorCss().sectionLabel());
pagesPanel.insert(label, 0);
}
// editor must be scrolled to the top immediately after opening
new Timer() {
@Override
public void run() {
scrollPanel.scrollToTop();
}
}.schedule(1000);
}
@Override
public void setSaveEnabled(boolean enable) {
saveButton.setEnabled(enable);
}
@UiHandler("cancelButton")
public void handleCancelButton(ClickEvent clickEvent) {
delegate.onCommandCancel();
}
@UiHandler("saveButton")
public void handleSaveButton(ClickEvent clickEvent) {
delegate.onCommandSave();
}
@Override
public void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
interface CommandEditorViewImplUiBinder extends UiBinder<Widget, CommandEditorViewImpl> {
}
}

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:with field='messages' type='org.eclipse.che.ide.command.editor.EditorMessages'/>
<ui:style>
@eval partBackground org.eclipse.che.ide.api.theme.Style.theme.partBackground();
@eval tabsPanelBackground org.eclipse.che.ide.api.theme.Style.theme.tabsPanelBackground();
.mainPanel {
background-color: partBackground;
}
.header {
height: 38px;
padding-top: 5px;
background-color: tabsPanelBackground;
}
.button {
float: right;
margin: 5px 0 0 10px;
font-weight: bold;
}
</ui:style>
<g:DockLayoutPanel addStyleNames="{style.mainPanel}">
<g:center>
<g:ScrollPanel ui:field="scrollPanel">
<g:FlowPanel width="700px" ui:field="pagesPanel"/>
</g:ScrollPanel>
</g:center>
<g:south size="44">
<g:FlowPanel addStyleNames="{style.header}">
<g:Button ui:field="cancelButton"
text="{messages.buttonCancelText}"
addStyleNames="{style.button}"
debugId="command-editor-button-cancel"/>
<g:Button ui:field="saveButton"
text="{messages.buttonSaveText}"
addStyleNames="{style.button}"
debugId="command-editor-button-save"/>
</g:FlowPanel>
</g:south>
</g:DockLayoutPanel>
</ui:UiBinder>

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor;
import com.google.gwt.i18n.client.Messages;
/**
* I18n messages for the Command Editor.
*
* @author Artem Zatsarynnyi
*/
public interface EditorMessages extends Messages {
@Key("editor.description")
String editorDescription();
@Key("editor.message.unable_save")
String editorMessageUnableToSave();
@Key("button.test.text")
String buttonRunText();
@Key("button.save.text")
String buttonSaveText();
@Key("button.cancel.text")
String buttonCancelText();
@Key("page.name.title")
String pageNameTitle();
@Key("page.command_line.title")
String pageCommandLineTitle();
@Key("page.goal.title")
String pageGoalTitle();
@Key("page.projects.title")
String pageProjectsTitle();
@Key("page.projects.table.header.project.label")
String pageProjectsTableHeaderProjectLabel();
@Key("page.projects.table.header.applicable.label")
String pageProjectsTableHeaderApplicableLabel();
@Key("page.with_text_editor.macros")
String pageWithTextEditorMacros();
@Key("page.preview_url.title")
String pagePreviewUrlTitle();
}

View File

@ -0,0 +1,66 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page;
import org.eclipse.che.ide.api.command.CommandImpl;
/**
* Abstract {@link CommandEditorPage} that provides basic functionality.
*
* @author Artem Zatsarynnyi
*/
public abstract class AbstractCommandEditorPage implements CommandEditorPage {
private final String title;
protected CommandImpl editedCommand;
private DirtyStateListener listener;
/** Creates new page with the given title and tooltip. */
protected AbstractCommandEditorPage(String title) {
this.title = title;
}
@Override
public String getTitle() {
return title;
}
@Override
public void edit(CommandImpl command) {
editedCommand = command;
initialize();
notifyDirtyStateChanged();
}
/**
* This method is called every time when command is opening in the editor.
* Typically, implementor should do initial setup of the page with the {@link #editedCommand}.
*/
protected abstract void initialize();
@Override
public void setDirtyStateListener(DirtyStateListener listener) {
this.listener = listener;
}
/**
* Should be called by page every time when any command
* modifications on the page have been performed.
*/
protected void notifyDirtyStateChanged() {
if (listener != null) {
listener.onDirtyStateChanged();
}
}
}

View File

@ -0,0 +1,56 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page;
import com.google.gwt.user.client.ui.IsWidget;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.command.editor.CommandEditor;
/**
* Defines the requirements for the page for {@link CommandEditor}.
*
* @author Artem Zatsarynnyi
* @see CommandEditor
*/
public interface CommandEditorPage {
/** Returns page's title. */
String getTitle();
/** Returns page's view. */
IsWidget getView();
/**
* This method is called every time when command is opening in the editor.
* Typically, implementor should hold the given {@code command}
* instance for subsequent modifying it directly and do pages's initial setup.
*/
void edit(CommandImpl command);
/**
* Whether the page has been modified or not?
*
* @return {@code true} if page is modified, and {@code false} - otherwise
*/
boolean isDirty();
/** Sets {@link DirtyStateListener}. */
void setDirtyStateListener(DirtyStateListener listener);
/**
* Listener that should be called by page every time when
* any command modifications on the page have been performed.
*/
interface DirtyStateListener {
void onDirtyStateChanged();
}
}

View File

@ -0,0 +1,62 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.commandline;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.editor.defaulteditor.EditorBuilder;
import org.eclipse.che.ide.api.filetypes.FileTypeRegistry;
import org.eclipse.che.ide.command.editor.EditorMessages;
import org.eclipse.che.ide.command.editor.page.CommandEditorPage;
import org.eclipse.che.ide.command.editor.page.text.AbstractPageWithTextEditor;
import org.eclipse.che.ide.command.editor.page.text.MacroEditorConfiguration;
import org.eclipse.che.ide.command.editor.page.text.PageWithTextEditorView;
import org.eclipse.che.ide.macro.chooser.MacroChooser;
/**
* Presenter for {@link CommandEditorPage} which allows to edit command line.
*
* @author Artem Zatsarynnyi
*/
public class CommandLinePage extends AbstractPageWithTextEditor {
@Inject
public CommandLinePage(PageWithTextEditorView view,
EditorBuilder editorBuilder,
FileTypeRegistry fileTypeRegistry,
MacroChooser macroChooser,
EditorMessages messages,
MacroEditorConfiguration editorConfiguration) {
super(view,
editorBuilder,
fileTypeRegistry,
macroChooser,
messages.pageCommandLineTitle(),
editorConfiguration);
view.asWidget().getElement().setId("command_editor-command_line");
}
@Override
protected String getCommandPropertyValue() {
return editedCommand.getCommandLine();
}
@Override
protected void updateCommandPropertyValue(String content) {
editedCommand.setCommandLine(content);
}
@Override
protected String getType() {
return ".sh";
}
}

View File

@ -0,0 +1,112 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.goal;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.BaseCommandGoal;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandGoalRegistry;
import org.eclipse.che.ide.command.editor.EditorMessages;
import org.eclipse.che.ide.command.editor.page.AbstractCommandEditorPage;
import org.eclipse.che.ide.command.editor.page.CommandEditorPage;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
import static com.google.common.base.Strings.isNullOrEmpty;
/**
* {@link CommandEditorPage} which allows to edit command's goal.
*
* @author Artem Zatsarynnyi
*/
public class GoalPage extends AbstractCommandEditorPage implements GoalPageView.ActionDelegate {
private final GoalPageView view;
private final CommandGoalRegistry goalRegistry;
private final CommandManager commandManager;
/** Initial value of the command's goal. */
private String goalInitial;
@Inject
public GoalPage(GoalPageView view,
CommandGoalRegistry commandGoalRegistry,
CommandManager commandManager,
EditorMessages messages) {
super(messages.pageGoalTitle());
this.view = view;
this.goalRegistry = commandGoalRegistry;
this.commandManager = commandManager;
view.setDelegate(this);
}
@Override
public IsWidget getView() {
return view;
}
@Override
protected void initialize() {
final String goalId = editedCommand.getGoal();
final CommandGoal commandGoal = goalRegistry.getGoalForId(goalId);
goalInitial = commandGoal.getId();
final Set<CommandGoal> goals = new HashSet<>();
goals.addAll(goalRegistry.getAllPredefinedGoals());
goals.addAll(getCustomGoals());
view.setAvailableGoals(goals);
view.setGoal(commandGoal.getId());
}
@Override
public boolean isDirty() {
if (editedCommand == null) {
return false;
}
final CommandGoal commandGoal = goalRegistry.getGoalForId(editedCommand.getGoal());
return !(goalInitial.equals(commandGoal.getId()));
}
@Override
public void onGoalChanged(String goalId) {
editedCommand.setGoal(goalId);
notifyDirtyStateChanged();
}
/** Returns all custom (non-predefined) command goals. */
private Set<CommandGoal> getCustomGoals() {
final Set<CommandGoal> list = new HashSet<>();
for (CommandImpl command : commandManager.getCommands()) {
final String goalId = command.getGoal();
final Optional<CommandGoal> goalOptional = goalRegistry.getPredefinedGoalById(goalId);
if (!goalOptional.isPresent() && !isNullOrEmpty(goalId)) {
list.add(new BaseCommandGoal(goalId));
}
}
return list;
}
}

View File

@ -0,0 +1,42 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.goal;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.mvp.View;
import java.util.Set;
/**
* The view of {@link GoalPage}.
*
* @author Artem Zatsarynnyi
*/
public interface GoalPageView extends View<GoalPageView.ActionDelegate> {
/** Set the list of goals which are available to set for command. */
void setAvailableGoals(Set<CommandGoal> goals);
/** Sets the command's goal value. */
void setGoal(String goalId);
/** The action delegate for this view. */
interface ActionDelegate {
/**
* Called when command goal has been changed.
*
* @param goalId
* new value of the command goal
*/
void onGoalChanged(String goalId);
}
}

View File

@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.goal;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.ui.listbox.CustomComboBox;
import java.util.Set;
/**
* Implementation of {@link GoalPageView}.
*
* @author Artem Zatsarynnyi
*/
public class GoalPageViewImpl extends Composite implements GoalPageView {
private static final GoalPageViewImplUiBinder UI_BINDER = GWT.create(GoalPageViewImplUiBinder.class);
@UiField
CustomComboBox goalComboBox;
private ActionDelegate delegate;
@Inject
public GoalPageViewImpl() {
initWidget(UI_BINDER.createAndBindUi(this));
}
@Override
public void setAvailableGoals(Set<CommandGoal> goals) {
goalComboBox.clear();
goals.forEach(g -> goalComboBox.addItem(g.getId()));
}
@Override
public void setGoal(String goalId) {
goalComboBox.setValue(goalId);
}
@Override
public void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
@UiHandler({"goalComboBox"})
void onGoalKeyUp(KeyUpEvent event) {
delegate.onGoalChanged(goalComboBox.getValue());
}
@UiHandler({"goalComboBox"})
void onGoalChanged(ChangeEvent event) {
delegate.onGoalChanged(goalComboBox.getValue());
}
interface GoalPageViewImplUiBinder extends UiBinder<Widget, GoalPageViewImpl> {
}
}

View File

@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:che='urn:import:org.eclipse.che.ide.ui.listbox'>
<ui:style>
.combo-box {
display: block;
}
</ui:style>
<che:CustomComboBox ui:field="goalComboBox" width="100%" addStyleNames="{style.combo-box}" debugId="command_editor-goal"/>
</ui:UiBinder>

View File

@ -0,0 +1,76 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.name;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.CommandExecutor;
import org.eclipse.che.ide.command.editor.EditorMessages;
import org.eclipse.che.ide.command.editor.page.AbstractCommandEditorPage;
import org.eclipse.che.ide.command.editor.page.CommandEditorPage;
/**
* Presenter for {@link CommandEditorPage} which allows to edit command's name.
*
* @author Artem Zatsarynnyi
*/
public class NamePage extends AbstractCommandEditorPage implements NamePageView.ActionDelegate {
private final NamePageView view;
private final CommandExecutor commandExecutor;
/** Initial value of the command's name. */
private String commandNameInitial;
@Inject
public NamePage(NamePageView view, EditorMessages messages, CommandExecutor commandExecutor) {
super(messages.pageNameTitle());
this.view = view;
this.commandExecutor = commandExecutor;
view.setDelegate(this);
}
@Override
public IsWidget getView() {
return view;
}
@Override
protected void initialize() {
commandNameInitial = editedCommand.getName();
view.setCommandName(editedCommand.getName());
}
@Override
public boolean isDirty() {
if (editedCommand == null) {
return false;
}
return !(commandNameInitial.equals(editedCommand.getName()));
}
@Override
public void onNameChanged(String name) {
editedCommand.setName(name);
notifyDirtyStateChanged();
}
@Override
public void onCommandRun() {
commandExecutor.executeCommand(editedCommand);
}
}

View File

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.name;
import org.eclipse.che.ide.api.mvp.View;
/**
* The view for {@link NamePage}.
*
* @author Artem Zatsarynnyi
*/
public interface NamePageView extends View<NamePageView.ActionDelegate> {
/** Sets the command's name value. */
void setCommandName(String name);
/** The action delegate for this view. */
interface ActionDelegate {
/**
* Called when command's name has been changed.
*
* @param name
* changed value of the command's name
*/
void onNameChanged(String name);
/** Called when executing command is requested. */
void onCommandRun();
}
}

View File

@ -0,0 +1,69 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.name;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
/**
* Implementation of {@link NamePageView}.
*
* @author Artem Zatsarynnyi
*/
public class NamePageViewImpl extends Composite implements NamePageView {
private static final NamePageViewImplUiBinder UI_BINDER = GWT.create(NamePageViewImplUiBinder.class);
@UiField
TextBox commandName;
@UiField
Button runButton;
private ActionDelegate delegate;
@Inject
public NamePageViewImpl() {
initWidget(UI_BINDER.createAndBindUi(this));
}
@Override
public void setCommandName(String name) {
commandName.setValue(name);
}
@Override
public void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
@UiHandler({"commandName"})
void onNameChanged(KeyUpEvent event) {
delegate.onNameChanged(commandName.getValue());
}
@UiHandler("runButton")
public void handleRunButton(ClickEvent clickEvent) {
delegate.onCommandRun();
}
interface NamePageViewImplUiBinder extends UiBinder<Widget, NamePageViewImpl> {
}
}

View File

@ -0,0 +1,38 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:with field='messages' type='org.eclipse.che.ide.command.editor.EditorMessages'/>
<ui:style>
.button {
float: right;
margin-top: 1px;
font-weight: bold;
background: #51b200;
}
.button:hover {
background: #51b200;
}
</ui:style>
<g:FlowPanel width="100%">
<g:Button ui:field="runButton"
text="{messages.buttonRunText}"
addStyleNames="{style.button}"
debugId="command-editor-button-run"/>
<g:TextBox ui:field="commandName" width="88%"/>
</g:FlowPanel>
</ui:UiBinder>

View File

@ -0,0 +1,61 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.previewurl;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.editor.defaulteditor.EditorBuilder;
import org.eclipse.che.ide.api.filetypes.FileTypeRegistry;
import org.eclipse.che.ide.command.editor.EditorMessages;
import org.eclipse.che.ide.command.editor.page.CommandEditorPage;
import org.eclipse.che.ide.command.editor.page.text.AbstractPageWithTextEditor;
import org.eclipse.che.ide.command.editor.page.text.MacroEditorConfiguration;
import org.eclipse.che.ide.command.editor.page.text.PageWithTextEditorView;
import org.eclipse.che.ide.macro.chooser.MacroChooser;
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_PREVIEW_URL_ATTRIBUTE_NAME;
/**
* Presenter for {@link CommandEditorPage} which allows to edit command's preview URL.
*
* @author Artem Zatsarynnyi
*/
public class PreviewUrlPage extends AbstractPageWithTextEditor {
@Inject
public PreviewUrlPage(PageWithTextEditorView view,
EditorBuilder editorBuilder,
FileTypeRegistry fileTypeRegistry,
MacroChooser macroChooser,
EditorMessages messages,
MacroEditorConfiguration editorConfiguration) {
super(view,
editorBuilder,
fileTypeRegistry,
macroChooser,
messages.pagePreviewUrlTitle(),
editorConfiguration);
view.asWidget().getElement().setId("command_editor-preview_url");
}
@Override
protected String getCommandPropertyValue() {
final String previewUrl = editedCommand.getAttributes().get(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME);
return previewUrl != null ? previewUrl : "";
}
@Override
protected void updateCommandPropertyValue(String content) {
editedCommand.getAttributes().put(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME, content);
}
}

View File

@ -0,0 +1,68 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.project;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.HasValue;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import org.eclipse.che.ide.ui.switcher.Switcher;
/**
* Switcher widget which is associated with some project name.
*
* @author Artem Zatsarynnyi
*/
public class ProjectSwitcher extends Composite implements HasValue<Boolean> {
private static final ProjectSwitcherUiBinder UI_BINDER = GWT.create(ProjectSwitcherUiBinder.class);
@UiField
Label label;
@UiField
Switcher switcher;
ProjectSwitcher(String projectName) {
initWidget(UI_BINDER.createAndBindUi(this));
label.setText(projectName);
}
@Override
public Boolean getValue() {
return switcher.getValue();
}
@Override
public void setValue(Boolean value) {
switcher.setValue(value);
}
@Override
public void setValue(Boolean value, boolean fireEvents) {
switcher.setValue(value, fireEvents);
}
@Override
public HandlerRegistration addValueChangeHandler(ValueChangeHandler<Boolean> handler) {
return switcher.addValueChangeHandler(handler);
}
interface ProjectSwitcherUiBinder extends UiBinder<Widget, ProjectSwitcher> {
}
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:che='urn:import:org.eclipse.che.ide.ui.switcher'>
<ui:style>
.panel {
background-color: #3D4650;
padding: 5px 0 5px 0;
}
.label {
display: inline-block;
width: 150px;
margin: 0 0 0 10px;
}
.switcher {
display: inline-block;
margin-left: 10px;
vertical-align: middle;
}
</ui:style>
<g:FlowPanel addStyleNames="{style.panel}">
<g:Label ui:field="label" addStyleNames="{style.label}"/>
<che:Switcher ui:field="switcher" addStyleNames="{style.switcher}"/>
</g:FlowPanel>
</ui:UiBinder>

View File

@ -0,0 +1,133 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.project;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandImpl.ApplicableContext;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.api.resources.ResourceChangedEvent;
import org.eclipse.che.ide.api.resources.ResourceChangedEvent.ResourceChangedHandler;
import org.eclipse.che.ide.api.resources.ResourceDelta;
import org.eclipse.che.ide.command.editor.EditorMessages;
import org.eclipse.che.ide.command.editor.page.AbstractCommandEditorPage;
import org.eclipse.che.ide.command.editor.page.CommandEditorPage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/** Presenter for {@link CommandEditorPage} which allows to edit command's applicable projects. */
public class ProjectsPage extends AbstractCommandEditorPage implements ProjectsPageView.ActionDelegate,
ResourceChangedHandler {
private final ProjectsPageView view;
private final AppContext appContext;
/** Initial value of the applicable projects list. */
private List<String> applicableProjectsInitial;
@Inject
public ProjectsPage(ProjectsPageView view,
AppContext appContext,
EditorMessages messages,
EventBus eventBus) {
super(messages.pageProjectsTitle());
this.view = view;
this.appContext = appContext;
eventBus.addHandler(ResourceChangedEvent.getType(), this);
view.setDelegate(this);
}
@Override
public IsWidget getView() {
return view;
}
@Override
protected void initialize() {
final ApplicableContext context = editedCommand.getApplicableContext();
applicableProjectsInitial = new ArrayList<>(context.getApplicableProjects());
refreshProjects();
}
/** Refresh 'Projects' section in the view. */
private void refreshProjects() {
final Map<Project, Boolean> projectsState = new HashMap<>();
for (Project project : appContext.getProjects()) {
ApplicableContext context = editedCommand.getApplicableContext();
boolean applicable = context.getApplicableProjects().contains(project.getPath());
projectsState.put(project, applicable);
}
view.setProjects(projectsState);
}
@Override
public boolean isDirty() {
if (editedCommand == null) {
return false;
}
ApplicableContext context = editedCommand.getApplicableContext();
return !(applicableProjectsInitial.equals(context.getApplicableProjects()));
}
@Override
public void onApplicableProjectChanged(Project project, boolean applicable) {
final ApplicableContext context = editedCommand.getApplicableContext();
if (applicable) {
// if command is bound with one project at least
// then remove command from the workspace
if (context.getApplicableProjects().isEmpty()) {
context.setWorkspaceApplicable(false);
}
context.addProject(project.getPath());
} else {
context.removeProject(project.getPath());
// if command isn't bound to any project
// then save it to the workspace
if (context.getApplicableProjects().isEmpty()) {
context.setWorkspaceApplicable(true);
}
}
notifyDirtyStateChanged();
}
@Override
public void onResourceChanged(ResourceChangedEvent event) {
final ResourceDelta delta = event.getDelta();
final Resource resource = delta.getResource();
if (resource.isProject()) {
// defer refreshing the projects section since appContext#getProjects may return old data
Scheduler.get().scheduleDeferred(this::refreshProjects);
}
}
}

View File

@ -0,0 +1,34 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.project;
import org.eclipse.che.ide.api.mvp.View;
import org.eclipse.che.ide.api.resources.Project;
import java.util.Map;
/**
* The view for {@link ProjectsPage}.
*
* @author Artem Zatsarynnyi
*/
public interface ProjectsPageView extends View<ProjectsPageView.ActionDelegate> {
/** Sets the applicable projects. */
void setProjects(Map<Project, Boolean> projects);
/** The action delegate for this view. */
interface ActionDelegate {
/** Called when applicable project has been changed. */
void onApplicableProjectChanged(Project project, boolean applicable);
}
}

View File

@ -0,0 +1,72 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.project;
import com.google.gwt.core.client.GWT;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.resources.Project;
import java.util.Map;
/**
* Implementation of {@link ProjectsPageView}.
*
* @author Artem Zatsarynnyi
*/
public class ProjectsPageViewImpl extends Composite implements ProjectsPageView {
private static final ProjectsPageViewImplUiBinder UI_BINDER = GWT.create(ProjectsPageViewImplUiBinder.class);
@UiField
FlowPanel mainPanel;
@UiField
FlowPanel projectsPanel;
private ActionDelegate delegate;
@Inject
public ProjectsPageViewImpl() {
initWidget(UI_BINDER.createAndBindUi(this));
mainPanel.setVisible(false);
}
@Override
public void setProjects(Map<Project, Boolean> projects) {
projectsPanel.clear();
mainPanel.setVisible(!projects.isEmpty());
projects.entrySet().forEach(entry -> addProjectSwitcherToPanel(entry.getKey(), entry.getValue()));
}
private void addProjectSwitcherToPanel(Project project, boolean applicable) {
final ProjectSwitcher switcher = new ProjectSwitcher(project.getName());
switcher.setValue(applicable);
switcher.addValueChangeHandler(event -> delegate.onApplicableProjectChanged(project, event.getValue()));
projectsPanel.add(switcher);
}
@Override
public void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
interface ProjectsPageViewImplUiBinder extends UiBinder<Widget, ProjectsPageViewImpl> {
}
}

View File

@ -0,0 +1,44 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:with field='messages' type='org.eclipse.che.ide.command.editor.EditorMessages'/>
<ui:style>
@eval textFieldBorderColor org.eclipse.che.ide.api.theme.Style.theme.toolButtonActiveBorder();
.table {
border: textFieldBorderColor;
}
.table-header {
background-color: #2E353B;
}
.column-header {
display: inline-block;
width: 150px;
margin-left: 10px;
margin-top: 3px;
}
</ui:style>
<g:FlowPanel width="100%" ui:field="mainPanel" addStyleNames="{style.table}" debugId="command_editor-projects">
<g:FlowPanel addStyleNames="{style.table-header}">
<g:Label text="{messages.pageProjectsTableHeaderProjectLabel}" addStyleNames="{style.column-header}"/>
<g:Label text="{messages.pageProjectsTableHeaderApplicableLabel}" addStyleNames="{style.column-header}"/>
</g:FlowPanel>
<g:FlowPanel ui:field="projectsPanel"/>
</g:FlowPanel>
</ui:UiBinder>

View File

@ -0,0 +1,154 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.text;
import com.google.gwt.user.client.ui.IsWidget;
import org.eclipse.che.ide.api.editor.OpenEditorCallbackImpl;
import org.eclipse.che.ide.api.editor.defaulteditor.EditorBuilder;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.editorconfig.TextEditorConfiguration;
import org.eclipse.che.ide.api.editor.texteditor.TextEditor;
import org.eclipse.che.ide.api.filetypes.FileTypeRegistry;
import org.eclipse.che.ide.api.resources.SyntheticFile;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.eclipse.che.ide.command.editor.page.AbstractCommandEditorPage;
import org.eclipse.che.ide.command.editor.page.CommandEditorPage;
import org.eclipse.che.ide.macro.chooser.MacroChooser;
import static org.eclipse.che.ide.api.editor.EditorPartPresenter.PROP_DIRTY;
import static org.eclipse.che.ide.api.editor.EditorPartPresenter.PROP_INPUT;
/**
* Abstract {@link CommandEditorPage} which allows to edit a command's property
* with a text editor that provides autocompletion for macros names.
*
* @author Artem Zatsarynnyi
*/
public abstract class AbstractPageWithTextEditor extends AbstractCommandEditorPage implements PageWithTextEditorView.ActionDelegate {
private final PageWithTextEditorView view;
private final FileTypeRegistry fileTypeRegistry;
private final MacroChooser macroChooser;
private final TextEditorConfiguration editorConfiguration;
private TextEditor editor;
/** Initial value of the edited command's property. */
private String initialValue;
protected AbstractPageWithTextEditor(PageWithTextEditorView view,
EditorBuilder editorBuilder,
FileTypeRegistry fileTypeRegistry,
MacroChooser macroChooser,
String title,
TextEditorConfiguration editorConfiguration) {
super("");
this.view = view;
this.fileTypeRegistry = fileTypeRegistry;
this.macroChooser = macroChooser;
this.editorConfiguration = editorConfiguration;
view.setDelegate(this);
view.setHeight(getHeight());
view.setEditorTitle(title);
initializeEditor(editorBuilder);
}
private void initializeEditor(EditorBuilder editorBuilder) {
editor = editorBuilder.buildEditor();
editor.initialize(editorConfiguration);
editor.activate();
editor.addPropertyListener((source, propId) -> {
switch (propId) {
case PROP_INPUT:
editor.go(view.getEditorContainer());
editor.getEditorWidget().setAnnotationRulerVisible(false);
editor.getEditorWidget().setFoldingRulerVisible(false);
editor.getEditorWidget().setZoomRulerVisible(false);
editor.getEditorWidget().setOverviewRulerVisible(false);
break;
case PROP_DIRTY:
updateCommandPropertyValue(editor.getDocument().getContents());
notifyDirtyStateChanged();
break;
default:
}
});
}
@Override
public IsWidget getView() {
return view;
}
@Override
protected void initialize() {
initialValue = getCommandPropertyValue();
setContent(initialValue);
}
/** Sets editor's content. */
private void setContent(String content) {
VirtualFile file = new SyntheticFile(editedCommand.getName() + getType(), content);
editor.init(new EditorInputImpl(fileTypeRegistry.getFileTypeByFile(file), file), new OpenEditorCallbackImpl());
}
@Override
public boolean isDirty() {
if (editedCommand == null) {
return false;
}
return !initialValue.equals(getCommandPropertyValue());
}
/** Returns the current value of the edited command's property. */
protected abstract String getCommandPropertyValue();
/**
* Updates the value of the edited command's property.
*
* @param newValue
* new value of the edited command's property
*/
protected abstract void updateCommandPropertyValue(String newValue);
/** Returns height of the page in pixels. Default height is 150 px. */
protected int getHeight() {
return 150;
}
/**
* Returns type of the edited content.
* Type must be specified as file's extension e.g.: .sh, .css.
* Default type is text/plain.
*/
protected String getType() {
return "";
}
@Override
public void onExploreMacros() {
macroChooser.show(macro -> {
Document document = editor.getDocument();
document.replace(document.getCursorOffset(), 0, macro.getName());
});
}
}

View File

@ -0,0 +1,52 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.text;
import org.eclipse.che.ide.api.editor.EditorInput;
import org.eclipse.che.ide.api.filetypes.FileType;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.vectomatic.dom.svg.ui.SVGResource;
class EditorInputImpl implements EditorInput {
private VirtualFile file;
private FileType fileType;
EditorInputImpl(FileType fileType, VirtualFile file) {
this.fileType = fileType;
this.file = file;
}
@Override
public String getToolTipText() {
return null;
}
@Override
public String getName() {
return file.getDisplayName();
}
@Override
public SVGResource getSVGResource() {
return fileType.getImage();
}
@Override
public VirtualFile getFile() {
return file;
}
@Override
public void setFile(VirtualFile file) {
this.file = file;
}
}

View File

@ -0,0 +1,119 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.text;
import org.eclipse.che.ide.Resources;
import org.eclipse.che.ide.api.editor.codeassist.CodeAssistCallback;
import org.eclipse.che.ide.api.editor.codeassist.CodeAssistProcessor;
import org.eclipse.che.ide.api.editor.codeassist.CompletionProposal;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.text.TextPosition;
import org.eclipse.che.ide.api.editor.texteditor.TextEditor;
import org.eclipse.che.ide.api.macro.Macro;
import org.eclipse.che.ide.api.macro.MacroRegistry;
import org.eclipse.che.ide.filters.FuzzyMatches;
import org.eclipse.che.ide.filters.Match;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.List;
/** Code assist processor for macro names. */
public class MacroCodeAssistProcessor implements CodeAssistProcessor {
private MacroRegistry registry;
private FuzzyMatches fuzzyMatches;
private Resources resources;
private LastCompletion lastCompletion;
@Inject
public MacroCodeAssistProcessor(MacroRegistry registry, FuzzyMatches fuzzyMatches, Resources resources) {
this.registry = registry;
this.fuzzyMatches = fuzzyMatches;
this.resources = resources;
lastCompletion = new LastCompletion();
}
@Override
public void computeCompletionProposals(TextEditor editor, int offset, boolean triggered, CodeAssistCallback callback) {
Document document = editor.getDocument();
TextPosition position = document.getPositionFromIndex(offset);
String currentLine = editor.getDocument().getLineContent(position.getLine());
final String currentWord = getCurrentWord(currentLine, position.getCharacter());
List<CompletionProposal> result = new ArrayList<>();
if (triggered && !lastCompletion.isGoodFor(currentWord, offset)) {
lastCompletion.offset = offset;
lastCompletion.wordStartOffset = offset - currentWord.length(); // start completion word
lastCompletion.word = currentWord;
}
List<Macro> macros = registry.getMacros();
for (Macro macro : macros) {
List<Match> matches = fuzzyMatches.fuzzyMatch(currentWord, macro.getName());
if (matches != null) {
MacroCompletionProposal proposal = new MacroCompletionProposal(macro,
matches,
resources,
lastCompletion.wordStartOffset,
currentWord.length());
result.add(proposal);
}
}
result.sort((o1, o2) -> {
MacroCompletionProposal p1 = ((MacroCompletionProposal)o1);
MacroCompletionProposal p2 = ((MacroCompletionProposal)o2);
return p1.getMacro().getName().compareTo(p2.getMacro().getName());
});
callback.proposalComputed(result);
}
private String getCurrentWord(String text, int offset) {
int i = offset - 1;
while (i >= 0 && isWordChar(text.charAt(i))) {
i--;
}
return text.substring(i + 1, offset);
}
private boolean isWordChar(char c) {
return c >= 'a' && c <= 'z' ||
c >= 'A' && c <= 'Z' ||
c >= '0' && c <= '9' ||
c >= '\u007f' && c <= '\u00ff' ||
c == '$' ||
c == '{' ||
c == '.' ||
c == '_' ||
c == '-';
}
@Override
public String getErrorMessage() {
return null;
}
private class LastCompletion {
String word = "";
int wordStartOffset;
int offset;
boolean isGoodFor(String currentWord, int offset) {
return currentWord.startsWith(word) &&
offset - this.offset == currentWord.length() - word.length();
}
}
}

View File

@ -0,0 +1,121 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.text;
import com.google.gwt.dom.client.Style;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Widget;
import org.eclipse.che.ide.Resources;
import org.eclipse.che.ide.api.editor.codeassist.Completion;
import org.eclipse.che.ide.api.editor.codeassist.CompletionProposal;
import org.eclipse.che.ide.api.editor.document.Document;
import org.eclipse.che.ide.api.editor.text.LinearRange;
import org.eclipse.che.ide.api.icon.Icon;
import org.eclipse.che.ide.api.macro.Macro;
import org.eclipse.che.ide.filters.Match;
import java.util.List;
/** Completion proposal for {@link Macro} that will insert {@link Macro#getName()} value. */
class MacroCompletionProposal implements CompletionProposal {
private final Macro macro;
private final List<Match> matches;
private Resources resources;
private int offset;
private int length;
MacroCompletionProposal(Macro macro, List<Match> matches, Resources resources, int offset, int length) {
this.macro = macro;
this.matches = matches;
this.resources = resources;
this.offset = offset;
this.length = length;
}
@Override
public void getAdditionalProposalInfo(AsyncCallback<Widget> callback) {
String documentation = macro.getDescription();
if (documentation == null || documentation.trim().isEmpty()) {
documentation = "No documentation found.";
}
Label label = new Label(documentation);
label.setWordWrap(true);
label.getElement().getStyle().setFontSize(13, Style.Unit.PX);
label.getElement().getStyle().setMarginLeft(4, Style.Unit.PX);
label.setSize("100%", "100%");
callback.onSuccess(label);
}
@Override
public String getDisplayString() {
SafeHtmlBuilder builder = new SafeHtmlBuilder();
String label = macro.getName();
int pos = 0;
for (Match highlight : matches) {
if (highlight.getStart() == highlight.getEnd()) {
continue;
}
if (pos < highlight.getStart()) {
appendPlain(builder, label.substring(pos, highlight.getStart()));
}
appendHighlighted(builder, label.substring(highlight.getStart(), highlight.getEnd()));
pos = highlight.getEnd();
}
if (pos < label.length()) {
appendPlain(builder, label.substring(pos));
}
return builder.toSafeHtml().asString();
}
private void appendPlain(SafeHtmlBuilder builder, String text) {
builder.appendEscaped(text);
}
private void appendHighlighted(SafeHtmlBuilder builder, String text) {
builder.appendHtmlConstant("<span class=\"" + resources.coreCss().codeassistantHighlight() + "\">");
builder.appendEscaped(text);
builder.appendHtmlConstant("</span>");
}
@Override
public Icon getIcon() {
return null;
}
@Override
public void getCompletion(final CompletionCallback callback) {
callback.onCompletion(new Completion() {
@Override
public void apply(Document document) {
document.replace(offset, length, macro.getName());
}
@Override
public LinearRange getSelection(Document document) {
LinearRange.PartialLinearRange start = LinearRange.createWithStart(offset + macro.getName().length());
return start.andLength(0);
}
});
}
public Macro getMacro() {
return macro;
}
}

View File

@ -0,0 +1,41 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.text;
import org.eclipse.che.ide.api.editor.codeassist.CodeAssistProcessor;
import org.eclipse.che.ide.api.editor.editorconfig.DefaultTextEditorConfiguration;
import org.eclipse.che.ide.api.editor.editorconfig.TextEditorConfiguration;
import org.eclipse.che.ide.api.editor.partition.DocumentPartitioner;
import javax.inject.Inject;
import java.util.HashMap;
import java.util.Map;
/**
* {@link TextEditorConfiguration} which provides {@link CodeAssistProcessor} for macros names.
*/
public class MacroEditorConfiguration extends DefaultTextEditorConfiguration {
private MacroCodeAssistProcessor codeAssistProcessor;
@Inject
public MacroEditorConfiguration(MacroCodeAssistProcessor codeAssistProcessor) {
this.codeAssistProcessor = codeAssistProcessor;
}
@Override
public Map<String, CodeAssistProcessor> getContentAssistantProcessors() {
Map<String, CodeAssistProcessor> map = new HashMap<>();
map.put(DocumentPartitioner.DEFAULT_CONTENT_TYPE, codeAssistProcessor);
return map;
}
}

View File

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.text;
import com.google.gwt.user.client.ui.SimpleLayoutPanel;
import org.eclipse.che.ide.api.mvp.View;
/**
* View for {@link AbstractPageWithTextEditor}.
*
* @author Artem Zatsarynnyi
*/
public interface PageWithTextEditorView extends View<PageWithTextEditorView.ActionDelegate> {
/** Returns the container where the editor should be placed. */
SimpleLayoutPanel getEditorContainer();
/** Sets height of the view. */
void setHeight(int height);
/** Sets title for the editor. */
void setEditorTitle(String title);
/** The action delegate for this view. */
interface ActionDelegate {
/** Called when exploring macros is requested. */
void onExploreMacros();
}
}

View File

@ -0,0 +1,82 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.editor.page.text;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.Hyperlink;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SimpleLayoutPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
/**
* Implementation of {@link PageWithTextEditorView}.
*
* @author Artem Zatsarynnyi
*/
public class PageWithTextEditorViewImpl extends Composite implements PageWithTextEditorView {
private static final PageWithTextEditorViewImplUiBinder UI_BINDER = GWT.create(PageWithTextEditorViewImplUiBinder.class);
@UiField
DockLayoutPanel mainPanel;
@UiField
Label title;
@UiField
Hyperlink exploreMacrosLink;
@UiField
SimpleLayoutPanel editorPanel;
/** The delegate to receive events from this view. */
private ActionDelegate delegate;
@Inject
public PageWithTextEditorViewImpl() {
initWidget(UI_BINDER.createAndBindUi(this));
}
@Override
public void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
@Override
public SimpleLayoutPanel getEditorContainer() {
return editorPanel;
}
@Override
public void setHeight(int height) {
mainPanel.setHeight(height + "px");
}
@Override
public void setEditorTitle(String title) {
this.title.setText(title);
}
@UiHandler("exploreMacrosLink")
public void handleExploreMacrosLinkClick(ClickEvent event) {
delegate.onExploreMacros();
}
interface PageWithTextEditorViewImplUiBinder extends UiBinder<Widget, PageWithTextEditorViewImpl> {
}
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'>
<ui:with field='messages' type='org.eclipse.che.ide.command.editor.EditorMessages'/>
<ui:with field='resources' type='org.eclipse.che.ide.command.CommandResources'/>
<ui:style>
@eval textFieldBorderColor org.eclipse.che.ide.api.theme.Style.theme.toolButtonActiveBorder();
.editor {
border: textFieldBorderColor;
}
.title {
float: left;
margin-left: 0;
}
.link a, a:visited {
float: right;
margin: 8px 10px 8px 0;
color: #4990E2;
font-size: 11px;
}
</ui:style>
<g:DockLayoutPanel ui:field="mainPanel" width="100%">
<g:north size="30">
<g:FlowPanel>
<g:Label ui:field="title" addStyleNames="{resources.editorCss.sectionLabel} {style.title}"/>
<g:Hyperlink ui:field="exploreMacrosLink"
text="{messages.pageWithTextEditorMacros}"
addStyleNames="{style.link}"
debugId="link-explore_macros"/>
</g:FlowPanel>
</g:north>
<g:center>
<g:SimpleLayoutPanel ui:field="editorPanel" addStyleNames="{style.editor}"/>
</g:center>
</g:DockLayoutPanel>
</ui:UiBinder>

View File

@ -0,0 +1,64 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.execute;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.DefaultActionGroup;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.api.selection.Selection;
import org.eclipse.che.ide.api.selection.SelectionAgent;
import org.eclipse.che.ide.resources.tree.ResourceNode;
/**
* Action group that contains all actions for executing commands.
*
* @author Artem Zatsarynnyi
*/
@Singleton
public class CommandsActionGroup extends DefaultActionGroup {
private final SelectionAgent selectionAgent;
@Inject
public CommandsActionGroup(ActionManager actionManager,
SelectionAgent selectionAgent,
ExecMessages messages) {
super(messages.actionCommandsTitle(), true, actionManager);
this.selectionAgent = selectionAgent;
}
@Override
public void update(ActionEvent e) {
e.getPresentation().setEnabledAndVisible(false);
// action group should be visible when current selection is machine or project
final Selection<?> selection = selectionAgent.getSelection();
if (selection != null && !selection.isEmpty() && selection.isSingleSelection()) {
final Object possibleNode = selection.getHeadElement();
if (possibleNode instanceof Machine) {
e.getPresentation().setEnabledAndVisible(true);
} else if (possibleNode instanceof ResourceNode) {
final Resource selectedResource = ((ResourceNode)possibleNode).getData();
e.getPresentation().setEnabledAndVisible(selectedResource.isProject());
}
}
}
}

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.execute;
import com.google.gwt.i18n.client.Messages;
/**
* I18n messages relate to the command execution.
*
* @author Artem Zatsarynnyi
*/
public interface ExecMessages extends Messages {
@Key("action.commands.title")
String actionCommandsTitle();
}

View File

@ -0,0 +1,57 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.execute;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.command.CommandExecutor;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.command.CommandUtils;
import org.vectomatic.dom.svg.ui.SVGResource;
/** Action for executing a {@link CommandImpl}. */
class ExecuteCommandAction extends Action {
private final CommandImpl command;
private final CommandExecutor commandExecutor;
private final CommandManager commandManager;
@Inject
ExecuteCommandAction(@Assisted CommandImpl command,
CommandUtils commandUtils,
CommandExecutor commandExecutor,
CommandManager commandManager) {
super(command.getName());
this.command = command;
this.commandExecutor = commandExecutor;
this.commandManager = commandManager;
final SVGResource commandIcon = commandUtils.getCommandTypeIcon(command.getType());
if (commandIcon != null) {
getTemplatePresentation().setSVGResource(commandIcon);
}
}
@Override
public void update(ActionEvent e) {
e.getPresentation().setEnabledAndVisible(commandManager.isCommandApplicable(command));
}
@Override
public void actionPerformed(ActionEvent e) {
commandExecutor.executeCommand(command);
}
}

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.execute;
import org.eclipse.che.ide.api.command.CommandImpl;
/**
* Factory for creating {@link ExecuteCommandAction} instances.
*
* @author Artem Zatsarynnyi
*/
public interface ExecuteCommandActionFactory {
/** Creates new instance of {@link ExecuteCommandAction} for executing the specified {@code command}. */
ExecuteCommandAction create(CommandImpl command);
}

View File

@ -0,0 +1,185 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.execute;
import com.google.gwt.core.client.Callback;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.DefaultActionGroup;
import org.eclipse.che.ide.api.command.CommandGoalRegistry;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandManager.CommandChangedListener;
import org.eclipse.che.ide.api.command.CommandManager.CommandLoadedListener;
import org.eclipse.che.ide.api.component.Component;
import java.util.HashMap;
import java.util.Map;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.ide.api.action.IdeActions.GROUP_CONSOLES_TREE_CONTEXT_MENU;
import static org.eclipse.che.ide.api.action.IdeActions.GROUP_EDITOR_TAB_CONTEXT_MENU;
import static org.eclipse.che.ide.api.action.IdeActions.GROUP_MAIN_CONTEXT_MENU;
/**
* Manager listens for creating/removing commands and adds/removes
* related {@link ExecuteCommandAction}s in the context menus.
*/
@Singleton
public class ExecuteCommandActionManager implements Component, CommandLoadedListener, CommandChangedListener {
private static final String COMMANDS_ACTION_GROUP_ID_PREFIX = "commandsActionGroup";
private static final String COMMAND_ACTION_ID_PREFIX = "command_";
private static final String GOAL_ACTION_GROUP_ID_PREFIX = "goal_";
private final CommandManager commandManager;
private final ActionManager actionManager;
private final CommandsActionGroup commandsActionGroup;
private final GoalPopUpGroupFactory goalPopUpGroupFactory;
private final ExecuteCommandActionFactory commandActionFactory;
private final CommandGoalRegistry goalRegistry;
/** Map of command's name to an appropriate {@link ExecuteCommandAction}. */
private final Map<String, Action> commandActions;
/** Map of command goal's ID to an appropriate action group. */
private final Map<String, DefaultActionGroup> goalPopUpGroups;
@Inject
public ExecuteCommandActionManager(CommandManager commandManager,
ActionManager actionManager,
CommandsActionGroup commandsActionGroup,
GoalPopUpGroupFactory goalPopUpGroupFactory,
ExecuteCommandActionFactory commandActionFactory,
CommandGoalRegistry goalRegistry) {
this.commandManager = commandManager;
this.actionManager = actionManager;
this.commandsActionGroup = commandsActionGroup;
this.goalPopUpGroupFactory = goalPopUpGroupFactory;
this.commandActionFactory = commandActionFactory;
this.goalRegistry = goalRegistry;
commandActions = new HashMap<>();
goalPopUpGroups = new HashMap<>();
}
@Override
public void start(Callback<Component, Exception> callback) {
callback.onSuccess(this);
commandManager.addCommandLoadedListener(this);
commandManager.addCommandChangedListener(this);
actionManager.registerAction(COMMANDS_ACTION_GROUP_ID_PREFIX, commandsActionGroup);
// inject 'Commands' menu into context menus
((DefaultActionGroup)actionManager.getAction(GROUP_MAIN_CONTEXT_MENU)).add(commandsActionGroup);
((DefaultActionGroup)actionManager.getAction(GROUP_EDITOR_TAB_CONTEXT_MENU)).add(commandsActionGroup);
((DefaultActionGroup)actionManager.getAction(GROUP_CONSOLES_TREE_CONTEXT_MENU)).add(commandsActionGroup);
}
@Override
public void onCommandsLoaded() {
commandManager.getCommands().forEach(this::addAction);
}
@Override
public void onCommandAdded(CommandImpl command) {
addAction(command);
}
/**
* Creates action for executing the given command and
* adds created action to the appropriate action group.
*/
private void addAction(CommandImpl command) {
final ExecuteCommandAction action = commandActionFactory.create(command);
actionManager.registerAction(COMMAND_ACTION_ID_PREFIX + command.getName(), action);
commandActions.put(command.getName(), action);
getActionGroupForCommand(command).add(action);
}
/**
* Returns the action group which is appropriate for placing the action for executing the given command.
* If appropriate action group doesn't exist it will be created and added to the right place.
*/
private DefaultActionGroup getActionGroupForCommand(CommandImpl command) {
String goalId = command.getGoal();
if (isNullOrEmpty(goalId)) {
goalId = goalRegistry.getDefaultGoal().getId();
}
DefaultActionGroup commandGoalPopUpGroup = goalPopUpGroups.get(goalId);
if (commandGoalPopUpGroup == null) {
commandGoalPopUpGroup = goalPopUpGroupFactory.create(goalId);
actionManager.registerAction(GOAL_ACTION_GROUP_ID_PREFIX + goalId, commandGoalPopUpGroup);
goalPopUpGroups.put(goalId, commandGoalPopUpGroup);
commandsActionGroup.add(commandGoalPopUpGroup);
}
return commandGoalPopUpGroup;
}
@Override
public void onCommandUpdated(CommandImpl previousCommand, CommandImpl command) {
removeAction(previousCommand);
addAction(command);
}
@Override
public void onCommandRemoved(CommandImpl command) {
removeAction(command);
}
/**
* Removes action for executing the given command and
* removes the appropriate action group in case it's empty.
*/
private void removeAction(CommandImpl command) {
final Action commandAction = commandActions.remove(command.getName());
if (commandAction != null) {
final String commandActionId = actionManager.getId(commandAction);
if (commandActionId != null) {
actionManager.unregisterAction(commandActionId);
}
// remove action from it's action group
String goalId = command.getGoal();
if (isNullOrEmpty(goalId)) {
goalId = goalRegistry.getDefaultGoal().getId();
}
// remove action group if it's empty
final DefaultActionGroup goalPopUpGroup = goalPopUpGroups.remove(goalId);
if (goalPopUpGroup != null) {
goalPopUpGroup.remove(commandAction);
if (goalPopUpGroup.getChildrenCount() == 0) {
final String goalActionId = actionManager.getId(goalPopUpGroup);
if (goalActionId != null) {
actionManager.unregisterAction(goalActionId);
}
commandsActionGroup.remove(goalPopUpGroup);
}
}
}
}
}

View File

@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.execute;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.action.ActionManager;
import org.eclipse.che.ide.api.action.DefaultActionGroup;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandGoalRegistry;
import org.eclipse.che.ide.api.icon.Icon;
import org.eclipse.che.ide.api.icon.IconRegistry;
import org.vectomatic.dom.svg.ui.SVGImage;
import org.vectomatic.dom.svg.ui.SVGResource;
/**
* Action group that represents command goal.
*
* @author Artem Zatsarynnyi
*/
class GoalPopUpGroup extends DefaultActionGroup {
private final CommandGoal commandGoal;
private final IconRegistry iconRegistry;
@Inject
GoalPopUpGroup(@Assisted String goalId,
ActionManager actionManager,
CommandGoalRegistry goalRegistry,
IconRegistry iconRegistry) {
super(actionManager);
this.iconRegistry = iconRegistry;
commandGoal = goalRegistry.getGoalForId(goalId);
setPopup(true);
// set icon
final SVGResource commandTypeIcon = getCommandGoalIcon();
if (commandTypeIcon != null) {
getTemplatePresentation().setSVGResource(commandTypeIcon);
}
}
@Override
public void update(ActionEvent e) {
e.getPresentation().setText(commandGoal.getId() + " (" + getChildrenCount() + ")");
}
private SVGResource getCommandGoalIcon() {
final String goalId = commandGoal.getId();
final Icon icon = iconRegistry.getIconIfExist("command.goal." + goalId);
if (icon != null) {
final SVGImage svgImage = icon.getSVGImage();
if (svgImage != null) {
return icon.getSVGResource();
}
}
return null;
}
}

View File

@ -0,0 +1,22 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.execute;
/**
* Factory for creating {@link GoalPopUpGroup} instances.
*
* @author Artem Zatsarynnyi
*/
public interface GoalPopUpGroupFactory {
/** Creates new {@link GoalPopUpGroup} for the command goal with the given {@code id}. */
GoalPopUpGroup create(String id);
}

View File

@ -0,0 +1,305 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.explorer;
import com.google.common.annotations.VisibleForTesting;
import com.google.gwt.core.client.Callback;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.google.gwt.user.client.ui.IsWidget;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.promises.client.Operation;
import org.eclipse.che.api.promises.client.OperationException;
import org.eclipse.che.api.promises.client.PromiseError;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.DelayedTask;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandGoalRegistry;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandImpl.ApplicableContext;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandManager.CommandChangedListener;
import org.eclipse.che.ide.api.command.CommandManager.CommandLoadedListener;
import org.eclipse.che.ide.api.command.CommandType;
import org.eclipse.che.ide.api.component.Component;
import org.eclipse.che.ide.api.constraints.Constraints;
import org.eclipse.che.ide.api.dialogs.DialogFactory;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.notification.NotificationManager;
import org.eclipse.che.ide.api.parts.WorkspaceAgent;
import org.eclipse.che.ide.api.parts.base.BasePresenter;
import org.eclipse.che.ide.command.CommandResources;
import org.eclipse.che.ide.command.CommandUtils;
import org.eclipse.che.ide.command.node.NodeFactory;
import org.eclipse.che.ide.command.type.chooser.CommandTypeChooser;
import org.vectomatic.dom.svg.ui.SVGResource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.eclipse.che.ide.api.notification.StatusNotification.DisplayMode.EMERGE_MODE;
import static org.eclipse.che.ide.api.notification.StatusNotification.Status.FAIL;
import static org.eclipse.che.ide.api.parts.PartStackType.NAVIGATION;
/** Presenter for Commands Explorer. */
@Singleton
public class CommandsExplorerPresenter extends BasePresenter implements CommandsExplorerView.ActionDelegate,
Component,
CommandChangedListener,
CommandLoadedListener {
private final CommandsExplorerView view;
private final CommandResources resources;
private final WorkspaceAgent workspaceAgent;
private final CommandManager commandManager;
private final NotificationManager notificationManager;
private final CommandTypeChooser commandTypeChooser;
private final ExplorerMessages messages;
private final RefreshViewTask refreshViewTask;
private final DialogFactory dialogFactory;
private final NodeFactory nodeFactory;
private final EditorAgent editorAgent;
private final AppContext appContext;
@Inject
public CommandsExplorerPresenter(CommandsExplorerView view,
CommandResources commandResources,
WorkspaceAgent workspaceAgent,
CommandManager commandManager,
NotificationManager notificationManager,
CommandTypeChooser commandTypeChooser,
ExplorerMessages messages,
RefreshViewTask refreshViewTask,
DialogFactory dialogFactory,
NodeFactory nodeFactory,
EditorAgent editorAgent,
AppContext appContext) {
this.view = view;
this.resources = commandResources;
this.workspaceAgent = workspaceAgent;
this.commandManager = commandManager;
this.notificationManager = notificationManager;
this.commandTypeChooser = commandTypeChooser;
this.messages = messages;
this.refreshViewTask = refreshViewTask;
this.dialogFactory = dialogFactory;
this.nodeFactory = nodeFactory;
this.editorAgent = editorAgent;
this.appContext = appContext;
view.setDelegate(this);
}
@Override
public void start(Callback<Component, Exception> callback) {
workspaceAgent.openPart(this, NAVIGATION, Constraints.LAST);
commandManager.addCommandLoadedListener(this);
commandManager.addCommandChangedListener(this);
callback.onSuccess(this);
}
@Override
public void go(AcceptsOneWidget container) {
refreshView();
container.setWidget(getView());
}
@Override
public String getTitle() {
return messages.partTitle();
}
@Override
public IsWidget getView() {
return view;
}
@Nullable
@Override
public String getTitleToolTip() {
return messages.partTooltip();
}
@Nullable
@Override
public SVGResource getTitleImage() {
return resources.explorerPart();
}
@Override
public void onCommandAdd(int left, int top) {
commandTypeChooser.show(left, top).then(createCommand(getDefaultContext()));
}
/** Returns the default {@link ApplicableContext} for the new command. */
private ApplicableContext getDefaultContext() {
final ApplicableContext context = new ApplicableContext();
if (appContext.getProjects().length > 0) {
context.setWorkspaceApplicable(false);
Arrays.stream(appContext.getProjects())
.forEach(p -> context.addProject(p.getPath()));
}
return context;
}
/** Returns an operation which creates a command with the given context. */
private Operation<CommandType> createCommand(ApplicableContext context) {
return selectedCommandType -> {
final CommandGoal selectedGoal = view.getSelectedGoal();
if (selectedGoal == null) {
return;
}
commandManager.createCommand(selectedGoal.getId(), selectedCommandType.getId(), context)
.then(command -> {
refreshViewAndSelectCommand(command);
editorAgent.openEditor(nodeFactory.newCommandFileNode(command));
})
.catchError(showErrorNotification(messages.unableCreate()));
};
}
@Override
public void onCommandDuplicate(CommandImpl command) {
commandManager.createCommand(command)
.then(this::refreshViewAndSelectCommand)
.catchError(showErrorNotification(messages.unableDuplicate()));
}
@Override
public void onCommandRemove(CommandImpl command) {
dialogFactory.createConfirmDialog(messages.removeCommandConfirmationTitle(),
messages.removeCommandConfirmationMessage(command.getName()),
() -> commandManager.removeCommand(command.getName())
.catchError(showErrorNotification(messages.unableRemove())),
null).show();
}
/** Returns an operation which shows an error notification with the given title. */
private Operation<PromiseError> showErrorNotification(String title) {
return err -> {
notificationManager.notify(title, err.getMessage(), FAIL, EMERGE_MODE);
throw new OperationException(err.getMessage());
};
}
@Override
public void onCommandsLoaded() {
refreshView();
}
@Override
public void onCommandAdded(CommandImpl command) {
refreshView();
}
@Override
public void onCommandUpdated(CommandImpl previousCommand, CommandImpl command) {
refreshView();
}
@Override
public void onCommandRemoved(CommandImpl command) {
refreshView();
}
/** Refresh view and preserve current selection. */
private void refreshView() {
refreshViewAndSelectCommand(null);
}
private void refreshViewAndSelectCommand(CommandImpl command) {
refreshViewTask.delayAndSelectCommand(command);
}
/**
* {@link DelayedTask} for refreshing the view and optionally selecting the specified command.
* <p>Tree widget in the view works asynchronously using events
* and it needs some time to be fully rendered.
* So successive refreshing view must be called with some delay.
*/
// since GIN can't instantiate inner classes
// made it nested in order to allow injection
@VisibleForTesting
static class RefreshViewTask extends DelayedTask {
// 300 milliseconds should be enough to fully refreshing the tree
private static final int DELAY_MILLIS = 300;
private final CommandsExplorerView view;
private final CommandGoalRegistry goalRegistry;
private final CommandManager commandManager;
private final CommandUtils commandUtils;
private CommandImpl commandToSelect;
@Inject
public RefreshViewTask(CommandsExplorerView view,
CommandGoalRegistry goalRegistry,
CommandManager commandManager,
CommandUtils commandUtils) {
this.view = view;
this.goalRegistry = goalRegistry;
this.commandManager = commandManager;
this.commandUtils = commandUtils;
}
@Override
public void onExecute() {
refreshView();
if (commandToSelect != null) {
// wait some time while tree in the view will be fully refreshed
new Timer() {
@Override
public void run() {
view.selectCommand(commandToSelect);
}
}.schedule(DELAY_MILLIS);
}
}
void delayAndSelectCommand(@Nullable CommandImpl command) {
if (command != null) {
commandToSelect = command;
}
delay(DELAY_MILLIS);
}
private void refreshView() {
final Map<CommandGoal, List<CommandImpl>> commandsByGoals = new HashMap<>();
// all predefined commandToSelect goals must be shown in the view
// so populate map by all registered commandToSelect goals
for (CommandGoal goal : goalRegistry.getAllPredefinedGoals()) {
commandsByGoals.put(goal, new ArrayList<>());
}
commandsByGoals.putAll(commandUtils.groupCommandsByGoal(commandManager.getCommands()));
view.setCommands(commandsByGoals);
}
}
}

View File

@ -0,0 +1,70 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.explorer;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.mvp.View;
import org.eclipse.che.ide.api.parts.base.BaseActionDelegate;
import java.util.List;
import java.util.Map;
/**
* The view for {@link CommandsExplorerPresenter}.
*
* @author Artem Zatsarynnyi
*/
public interface CommandsExplorerView extends View<CommandsExplorerView.ActionDelegate> {
/**
* Sets the commands to display in the view.
*
* @param commands
* commands grouped by its type
*/
void setCommands(Map<CommandGoal, List<CommandImpl>> commands);
/** Returns the currently selected command goal or {@code null} if none. */
@Nullable
CommandGoal getSelectedGoal();
/** Returns the currently selected command or {@code null} if none. */
@Nullable
CommandImpl getSelectedCommand();
/** Select the given {@code command}. */
void selectCommand(CommandImpl command);
/** The action delegate for this view. */
interface ActionDelegate extends BaseActionDelegate {
/** Called when adding new command is requested. */
void onCommandAdd(int left, int top);
/**
* Called when duplicating command is requested.
*
* @param command
* command duplication of which is requested
*/
void onCommandDuplicate(CommandImpl command);
/**
* Called when removing command is requested.
*
* @param command
* command removing of which is requested
*/
void onCommandRemove(CommandImpl command);
}
}

View File

@ -0,0 +1,170 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.explorer;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.Resources;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.data.tree.Node;
import org.eclipse.che.ide.api.parts.base.BaseView;
import org.eclipse.che.ide.command.CommandResources;
import org.eclipse.che.ide.command.node.CommandFileNode;
import org.eclipse.che.ide.command.node.CommandGoalNode;
import org.eclipse.che.ide.command.node.NodeFactory;
import org.eclipse.che.ide.ui.smartTree.NodeLoader;
import org.eclipse.che.ide.ui.smartTree.NodeStorage;
import org.eclipse.che.ide.ui.smartTree.Tree;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import static java.util.Collections.singletonList;
import static org.eclipse.che.ide.ui.smartTree.SelectionModel.Mode.SINGLE;
/**
* Implementation of {@link CommandsExplorerView}.
*
* @author Artem Zatsarynnyi
*/
@Singleton
public class CommandsExplorerViewImpl extends BaseView<CommandsExplorerView.ActionDelegate> implements CommandsExplorerView {
private static final CommandsExplorerViewImplUiBinder UI_BINDER = GWT.create(CommandsExplorerViewImplUiBinder.class);
private final CommandsTreeRenderer treeRenderer;
private final NodeFactory nodeFactory;
/** Mapping of the commands to the rendered tree nodes. */
private final Map<CommandImpl, CommandFileNode> commandNodes;
@UiField(provided = true)
Tree tree;
@Inject
public CommandsExplorerViewImpl(Resources coreResources,
ExplorerMessages messages,
CommandResources resources,
NodeFactory nodeFactory) {
super(coreResources);
this.nodeFactory = nodeFactory;
commandNodes = new HashMap<>();
setTitle(messages.viewTitle());
tree = new Tree(new NodeStorage(), new NodeLoader());
tree.ensureDebugId("commands-explorer");
treeRenderer = new CommandsTreeRenderer(tree.getTreeStyles(), resources, delegate);
tree.setPresentationRenderer(treeRenderer);
tree.getSelectionModel().setSelectionMode(SINGLE);
tree.getSelectionModel().addSelectionHandler(event -> {
for (Node node : tree.getNodeStorage().getAll()) {
final Element nodeContainerElement = tree.getNodeDescriptor(node).getNodeContainerElement();
if (nodeContainerElement != null) {
nodeContainerElement.removeAttribute("selected");
}
}
tree.getNodeDescriptor(event.getSelectedItem())
.getNodeContainerElement()
.setAttribute("selected", "selected");
});
setContentWidget(UI_BINDER.createAndBindUi(this));
}
@Override
protected void focusView() {
tree.setFocus(true);
}
@Override
public void setCommands(Map<CommandGoal, List<CommandImpl>> commands) {
treeRenderer.setDelegate(delegate);
renderCommands(commands);
}
private void renderCommands(Map<CommandGoal, List<CommandImpl>> commands) {
commandNodes.clear();
tree.getNodeStorage().clear();
for (Entry<CommandGoal, List<CommandImpl>> entry : commands.entrySet()) {
List<CommandFileNode> commandNodes = new ArrayList<>(entry.getValue().size());
for (CommandImpl command : entry.getValue()) {
final CommandFileNode commandFileNode = nodeFactory.newCommandFileNode(command);
commandNodes.add(commandFileNode);
this.commandNodes.put(command, commandFileNode);
}
final CommandGoalNode commandGoalNode = nodeFactory.newCommandGoalNode(entry.getKey(), commandNodes);
tree.getNodeStorage().add(commandGoalNode);
}
tree.expandAll();
}
@Nullable
@Override
public CommandGoal getSelectedGoal() {
final List<Node> selectedNodes = tree.getSelectionModel().getSelectedNodes();
if (!selectedNodes.isEmpty()) {
final Node selectedNode = selectedNodes.get(0);
if (selectedNode instanceof CommandGoalNode) {
return ((CommandGoalNode)selectedNode).getData();
}
}
return null;
}
@Nullable
@Override
public CommandImpl getSelectedCommand() {
final List<Node> selectedNodes = tree.getSelectionModel().getSelectedNodes();
if (!selectedNodes.isEmpty()) {
final Node selectedNode = selectedNodes.get(0);
if (selectedNode instanceof CommandFileNode) {
return ((CommandFileNode)selectedNode).getData();
}
}
return null;
}
@Override
public void selectCommand(CommandImpl command) {
tree.getSelectionModel().setSelection(singletonList(commandNodes.get(command)));
}
interface CommandsExplorerViewImplUiBinder extends UiBinder<Widget, CommandsExplorerViewImpl> {
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:che='urn:import:org.eclipse.che.ide.ui'>
<che:smartTree.Tree ui:field="tree"/>
</ui:UiBinder>

View File

@ -0,0 +1,137 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.explorer;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.user.client.Event;
import org.eclipse.che.ide.api.data.tree.Node;
import org.eclipse.che.ide.command.CommandResources;
import org.eclipse.che.ide.command.explorer.CommandsExplorerView.ActionDelegate;
import org.eclipse.che.ide.command.node.CommandFileNode;
import org.eclipse.che.ide.command.node.CommandGoalNode;
import org.eclipse.che.ide.ui.smartTree.Tree;
import org.eclipse.che.ide.ui.smartTree.TreeStyles;
import org.eclipse.che.ide.ui.smartTree.presentation.DefaultPresentationRenderer;
import org.vectomatic.dom.svg.ui.SVGResource;
import static com.google.gwt.user.client.Event.ONCLICK;
/**
* Renderer for the commands tree.
*
* @author Artem Zatsarynnyi
*/
class CommandsTreeRenderer extends DefaultPresentationRenderer<Node> {
private final CommandResources resources;
private ActionDelegate delegate;
CommandsTreeRenderer(TreeStyles treeStyles, CommandResources resources, ActionDelegate delegate) {
super(treeStyles);
this.resources = resources;
this.delegate = delegate;
}
/** Sets the delegate that will handle events from the rendered DOM elements. */
void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
@Override
public Element getPresentableTextContainer(Element content) {
final Element presentableTextContainer = super.getPresentableTextContainer(content);
presentableTextContainer.addClassName(resources.commandsExplorerCss().commandNodeText());
return presentableTextContainer;
}
@Override
public Element render(Node node, String domID, Tree.Joint joint, int depth) {
final Element element = super.render(node, domID, joint, depth);
final Element nodeContainerElement = element.getFirstChildElement();
if (node instanceof CommandFileNode) {
CommandFileNode commandNode = (CommandFileNode)node;
nodeContainerElement.setId("command_" + commandNode.getDisplayName());
renderCommandNode(commandNode, nodeContainerElement);
} else if (node instanceof CommandGoalNode) {
CommandGoalNode goalNode = (CommandGoalNode)node;
nodeContainerElement.setId("goal_" + goalNode.getName());
renderCommandGoalNode(nodeContainerElement);
}
return element;
}
private void renderCommandNode(CommandFileNode node, Element nodeContainerElement) {
nodeContainerElement.addClassName(resources.commandsExplorerCss().commandNode());
final Element removeCommandButton = createButton(resources.removeCommand());
Event.setEventListener(removeCommandButton, event -> {
if (ONCLICK == event.getTypeInt()) {
event.stopPropagation();
delegate.onCommandRemove(node.getData());
}
});
final Element duplicateCommandButton = createButton(resources.duplicateCommand());
Event.setEventListener(duplicateCommandButton, event -> {
if (ONCLICK == event.getTypeInt()) {
event.stopPropagation();
delegate.onCommandDuplicate(node.getData());
}
});
final Element buttonsPanel = Document.get().createSpanElement();
buttonsPanel.setClassName(resources.commandsExplorerCss().commandNodeButtonsPanel());
buttonsPanel.appendChild(removeCommandButton);
buttonsPanel.appendChild(duplicateCommandButton);
nodeContainerElement.appendChild(buttonsPanel);
removeCommandButton.setId("commands_tree-button-remove");
duplicateCommandButton.setId("commands_tree-button-duplicate");
}
private void renderCommandGoalNode(Element nodeContainerElement) {
nodeContainerElement.addClassName(resources.commandsExplorerCss().commandGoalNode());
final Element addCommandButton = createButton(resources.addCommand());
Event.setEventListener(addCommandButton, event -> {
if (ONCLICK == event.getTypeInt()) {
event.stopPropagation();
delegate.onCommandAdd(addCommandButton.getAbsoluteLeft(), addCommandButton.getAbsoluteTop());
}
});
nodeContainerElement.appendChild(addCommandButton);
addCommandButton.setId("commands_tree-button-add");
}
private Element createButton(SVGResource icon) {
final Element button = Document.get().createSpanElement();
button.appendChild(icon.getSvg().getElement());
Event.sinkEvents(button, ONCLICK);
return button;
}
}

View File

@ -0,0 +1,45 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.explorer;
import com.google.gwt.i18n.client.Messages;
/**
* I18n messages for the Command Explorer.
*
* @author Artem Zatsarynnyi
*/
public interface ExplorerMessages extends Messages {
@Key("explorer.part.title")
String partTitle();
@Key("explorer.part.tooltip")
String partTooltip();
@Key("explorer.view.title")
String viewTitle();
@Key("explorer.message.unable_create")
String unableCreate();
@Key("explorer.message.unable_duplicate")
String unableDuplicate();
@Key("explorer.message.unable_remove")
String unableRemove();
@Key("explorer.remove_confirmation.title")
String removeCommandConfirmationTitle();
@Key("explorer.remove_confirmation.message")
String removeCommandConfirmationMessage(String commandName);
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.goal;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.BaseCommandGoal;
/**
* Represents predefined 'Build' goal.
*
* @author Artem Zatsarynnyi
*/
public class BuildGoal extends BaseCommandGoal {
@Inject
public BuildGoal() {
super("Build");
}
}

View File

@ -0,0 +1,90 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.goal;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.inject.name.Named;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.command.BaseCommandGoal;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandGoalRegistry;
import org.eclipse.che.ide.util.loging.Log;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.Collections.unmodifiableList;
import static java.util.Optional.ofNullable;
/**
* Implementation of {@link CommandGoalRegistry}.
*
* @author Artem Zatsarynnyi
*/
@Singleton
public class CommandGoalRegistryImpl implements CommandGoalRegistry {
private final CommandGoal defaultGoal;
private final GoalMessages messages;
private final Map<String, CommandGoal> commandGoals;
@Inject
public CommandGoalRegistryImpl(@Named("default") CommandGoal defaultCommandGoal, GoalMessages messages) {
defaultGoal = defaultCommandGoal;
this.messages = messages;
commandGoals = new HashMap<>();
}
@Inject(optional = true)
private void register(Set<CommandGoal> goals) {
for (CommandGoal type : goals) {
final String id = type.getId();
if (commandGoals.containsKey(id)) {
Log.warn(getClass(), messages.messageGoalAlreadyRegistered(id));
} else {
commandGoals.put(id, type);
}
}
}
@Override
public List<CommandGoal> getAllPredefinedGoals() {
return unmodifiableList(new ArrayList<>(commandGoals.values()));
}
@Override
public CommandGoal getDefaultGoal() {
return defaultGoal;
}
@Override
public Optional<CommandGoal> getPredefinedGoalById(String id) {
return ofNullable(commandGoals.get(id));
}
@Override
public CommandGoal getGoalForId(@Nullable String id) {
if (isNullOrEmpty(id)) {
return getDefaultGoal();
}
return getPredefinedGoalById(id).orElse(new BaseCommandGoal(id));
}
}

View File

@ -0,0 +1,29 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.goal;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.BaseCommandGoal;
/**
* Represents predefined 'Common' goal.
* By default it's used for grouping commands which doesn't belong to any goal.
*
* @author Artem Zatsarynnyi
*/
public class CommonGoal extends BaseCommandGoal {
@Inject
public CommonGoal() {
super("Common");
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.goal;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.BaseCommandGoal;
/**
* Represents predefined 'Debug' goal.
*
* @author Artem Zatsarynnyi
*/
public class DebugGoal extends BaseCommandGoal {
@Inject
public DebugGoal() {
super("Debug");
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.goal;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.BaseCommandGoal;
/**
* Represents predefined 'Deploy' goal.
*
* @author Artem Zatsarynnyi
*/
public class DeployGoal extends BaseCommandGoal {
@Inject
public DeployGoal() {
super("Deploy");
}
}

View File

@ -0,0 +1,24 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.goal;
import com.google.gwt.i18n.client.Messages;
/**
* I18n messages relate to the command goals.
*
* @author Artem Zatsarynnyi
*/
public interface GoalMessages extends Messages {
@Key("message.goal_already_registered")
String messageGoalAlreadyRegistered(String goalId);
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.goal;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.BaseCommandGoal;
/**
* Represents predefined 'Run' goal.
*
* @author Artem Zatsarynnyi
*/
public class RunGoal extends BaseCommandGoal {
@Inject
public RunGoal() {
super("Run");
}
}

View File

@ -0,0 +1,28 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.goal;
import com.google.inject.Inject;
import org.eclipse.che.ide.api.command.BaseCommandGoal;
/**
* Represents predefined 'Test' goal.
*
* @author Artem Zatsarynnyi
*/
public class TestGoal extends BaseCommandGoal {
@Inject
public TestGoal() {
super("Test");
}
}

View File

@ -0,0 +1,456 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.manager;
import elemental.util.ArrayOf;
import elemental.util.Collections;
import com.google.gwt.core.client.Callback;
import com.google.gwt.core.client.Scheduler;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import com.google.web.bindery.event.shared.EventBus;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseProvider;
import org.eclipse.che.commons.annotation.Nullable;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandImpl.ApplicableContext;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandType;
import org.eclipse.che.ide.api.command.CommandTypeRegistry;
import org.eclipse.che.ide.api.component.Component;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.api.resources.Resource;
import org.eclipse.che.ide.api.selection.Selection;
import org.eclipse.che.ide.api.selection.SelectionAgent;
import org.eclipse.che.ide.api.workspace.WorkspaceReadyEvent;
import org.eclipse.che.ide.api.workspace.WorkspaceReadyEvent.WorkspaceReadyHandler;
import org.eclipse.che.ide.util.loging.Log;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static com.google.common.base.Strings.isNullOrEmpty;
import static java.util.stream.Collectors.toList;
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_GOAL_ATTRIBUTE_NAME;
import static org.eclipse.che.api.workspace.shared.Constants.COMMAND_PREVIEW_URL_ATTRIBUTE_NAME;
/** Implementation of {@link CommandManager}. */
@Singleton
public class CommandManagerImpl implements CommandManager, Component, WorkspaceReadyHandler {
private final AppContext appContext;
private final PromiseProvider promiseProvider;
private final CommandTypeRegistry commandTypeRegistry;
private final ProjectCommandManagerDelegate projectCommandManager;
private final WorkspaceCommandManagerDelegate workspaceCommandManager;
private final EventBus eventBus;
private final SelectionAgent selectionAgent;
/** Map of the commands' names to the commands. */
private final Map<String, CommandImpl> commands;
private final Set<CommandLoadedListener> commandLoadedListeners;
private final Set<CommandChangedListener> commandChangedListeners;
@Inject
public CommandManagerImpl(AppContext appContext,
PromiseProvider promiseProvider,
CommandTypeRegistry commandTypeRegistry,
ProjectCommandManagerDelegate projectCommandManagerDelegate,
WorkspaceCommandManagerDelegate workspaceCommandManagerDelegate,
EventBus eventBus,
SelectionAgent selectionAgent) {
this.appContext = appContext;
this.promiseProvider = promiseProvider;
this.commandTypeRegistry = commandTypeRegistry;
this.projectCommandManager = projectCommandManagerDelegate;
this.workspaceCommandManager = workspaceCommandManagerDelegate;
this.eventBus = eventBus;
this.selectionAgent = selectionAgent;
commands = new HashMap<>();
commandLoadedListeners = new HashSet<>();
commandChangedListeners = new HashSet<>();
}
@Override
public void start(Callback<Component, Exception> callback) {
eventBus.addHandler(WorkspaceReadyEvent.getType(), this);
callback.onSuccess(this);
}
@Override
public void onWorkspaceReady(WorkspaceReadyEvent event) {
fetchCommands();
}
private void fetchCommands() {
// get all commands related to the workspace
workspaceCommandManager.getCommands(appContext.getWorkspaceId()).then(workspaceCommands -> {
workspaceCommands.forEach(workspaceCommand -> commands.put(workspaceCommand.getName(),
new CommandImpl(workspaceCommand, new ApplicableContext())));
// get all commands related to the projects
Arrays.stream(appContext.getProjects())
.forEach(project -> projectCommandManager.getCommands(project).forEach(projectCommand -> {
final CommandImpl existedCommand = commands.get(projectCommand.getName());
if (existedCommand == null) {
commands.put(projectCommand.getName(),
new CommandImpl(projectCommand, new ApplicableContext(project.getPath())));
} else {
if (projectCommand.equalsIgnoreContext(existedCommand)) {
existedCommand.getApplicableContext().addProject(project.getPath());
} else {
// normally, should never happen
Log.error(CommandManagerImpl.this.getClass(), "Different commands with the same names found");
}
}
}));
notifyCommandsLoaded();
});
}
@Override
public List<CommandImpl> getCommands() {
return commands.values()
.stream()
.map(CommandImpl::new)
.collect(toList());
}
@Override
public java.util.Optional<CommandImpl> getCommand(String name) {
return commands.values()
.stream()
.filter(command -> name.equals(command.getName()))
.findFirst();
}
@Override
public List<CommandImpl> getApplicableCommands() {
return commands.values()
.stream()
.filter(this::isCommandApplicable)
.map(CommandImpl::new)
.collect(toList());
}
@Override
public boolean isCommandApplicable(CommandImpl command) {
return isMachineSelected() || isCommandApplicableToCurrentProject(command);
}
/** Checks whether the machine is currently selected. */
private boolean isMachineSelected() {
final Selection<?> selection = selectionAgent.getSelection();
if (selection != null && !selection.isEmpty() && selection.isSingleSelection()) {
return selection.getHeadElement() instanceof Machine;
}
return false;
}
/** Checks whether the given command is applicable to the current project. */
private boolean isCommandApplicableToCurrentProject(CommandImpl command) {
final List<String> applicableProjects = command.getApplicableContext().getApplicableProjects();
if (applicableProjects.isEmpty()) {
return true;
}
final Resource currentResource = appContext.getResource();
if (currentResource != null) {
final Project currentProject = currentResource.getProject();
if (currentProject != null) {
return applicableProjects.contains(currentProject.getPath());
}
}
return false;
}
@Override
public Promise<CommandImpl> createCommand(String goalId, String typeId) {
return createCommand(goalId,
typeId,
null,
null,
new HashMap<>(),
new ApplicableContext());
}
@Override
public Promise<CommandImpl> createCommand(String goalId, String typeId, ApplicableContext context) {
return createCommand(goalId, typeId, null, null, new HashMap<>(), context);
}
@Override
public Promise<CommandImpl> createCommand(String goalId,
String typeId,
String name,
String commandLine,
Map<String, String> attributes) {
return createCommand(goalId, typeId, name, commandLine, attributes, new ApplicableContext());
}
@Override
public Promise<CommandImpl> createCommand(String goalId,
String typeId,
@Nullable String name,
@Nullable String commandLine,
Map<String, String> attributes,
ApplicableContext context) {
final CommandType commandType = commandTypeRegistry.getCommandTypeById(typeId);
if (commandType == null) {
return promiseProvider.reject(new Exception("Unknown command type: '" + typeId + "'"));
}
final Map<String, String> attr = new HashMap<>(attributes);
attr.put(COMMAND_PREVIEW_URL_ATTRIBUTE_NAME, commandType.getPreviewUrlTemplate());
attr.put(COMMAND_GOAL_ATTRIBUTE_NAME, goalId);
return createCommand(new CommandImpl(getUniqueCommandName(typeId, name),
commandLine != null ? commandLine : commandType.getCommandLineTemplate(),
typeId,
attr,
context));
}
@Override
public Promise<CommandImpl> createCommand(CommandImpl command) {
return doCreateCommand(command).then((Function<CommandImpl, CommandImpl>)newCommand -> {
// postpone the notification because
// listeners should be notified after returning from #createCommand method
Scheduler.get().scheduleDeferred(() -> notifyCommandAdded(newCommand));
return newCommand;
});
}
/** Does the actual work for command creation. Doesn't notify listeners. */
private Promise<CommandImpl> doCreateCommand(CommandImpl command) {
final ApplicableContext context = command.getApplicableContext();
if (!context.isWorkspaceApplicable() && context.getApplicableProjects().isEmpty()) {
return promiseProvider.reject(new Exception("Command has to be applicable to the workspace or at least one project"));
}
final CommandType commandType = commandTypeRegistry.getCommandTypeById(command.getType());
if (commandType == null) {
return promiseProvider.reject(new Exception("Unknown command type: '" + command.getType() + "'"));
}
final CommandImpl newCommand = new CommandImpl(command);
newCommand.setName(getUniqueCommandName(command.getType(), command.getName()));
final ArrayOf<Promise<?>> commandPromises = Collections.arrayOf();
if (context.isWorkspaceApplicable()) {
Promise<CommandImpl> p = workspaceCommandManager.createCommand(newCommand)
.then((Function<CommandImpl, CommandImpl>)arg -> {
newCommand.getApplicableContext().setWorkspaceApplicable(true);
return newCommand;
});
commandPromises.push(p);
}
for (final String projectPath : context.getApplicableProjects()) {
final Project project = getProjectByPath(projectPath);
if (project == null) {
continue;
}
Promise<CommandImpl> p = projectCommandManager.createCommand(project, newCommand)
.then((Function<CommandImpl, CommandImpl>)arg -> {
newCommand.getApplicableContext().addProject(projectPath);
return newCommand;
});
commandPromises.push(p);
}
return promiseProvider.all2(commandPromises)
.then((Function<ArrayOf<?>, CommandImpl>)ignore -> {
commands.put(newCommand.getName(), newCommand);
return newCommand;
});
}
@Override
public Promise<CommandImpl> updateCommand(String name, CommandImpl commandToUpdate) {
final CommandImpl existedCommand = commands.get(name);
if (existedCommand == null) {
return promiseProvider.reject(new Exception("Command '" + name + "' does not exist."));
}
return doRemoveCommand(name).thenPromise(aVoid -> doCreateCommand(commandToUpdate)
.then((Function<CommandImpl, CommandImpl>)updatedCommand -> {
// listeners should be notified after returning from #updateCommand method
// so let's postpone notification
Scheduler.get().scheduleDeferred(() -> notifyCommandUpdated(existedCommand, updatedCommand));
return updatedCommand;
}));
}
@Override
public Promise<Void> removeCommand(String name) {
final CommandImpl command = commands.get(name);
if (command == null) {
return promiseProvider.reject(new Exception("Command '" + name + "' does not exist."));
}
return doRemoveCommand(name).then(aVoid -> {
// listeners should be notified after returning from #removeCommand method
// so let's postpone notification
Scheduler.get().scheduleDeferred(() -> notifyCommandRemoved(command));
});
}
/** Removes the command without notifying listeners. */
private Promise<Void> doRemoveCommand(String name) {
final CommandImpl command = commands.get(name);
if (command == null) {
return promiseProvider.reject(new Exception("Command '" + name + "' does not exist."));
}
final ApplicableContext context = command.getApplicableContext();
final ArrayOf<Promise<?>> commandPromises = Collections.arrayOf();
if (context.isWorkspaceApplicable()) {
final Promise<Void> p = workspaceCommandManager.removeCommand(name).then((Function<Void, Void>)aVoid -> {
command.getApplicableContext().setWorkspaceApplicable(false);
return null;
});
commandPromises.push(p);
}
for (final String projectPath : context.getApplicableProjects()) {
final Project project = getProjectByPath(projectPath);
if (project == null) {
continue;
}
final Promise<Void> p = projectCommandManager.removeCommand(project, name).then((Function<Void, Void>)aVoid -> {
command.getApplicableContext().removeProject(projectPath);
return null;
});
commandPromises.push(p);
}
return promiseProvider.all2(commandPromises)
.then((Function<ArrayOf<?>, Void>)arg -> {
commands.remove(command.getName());
return null;
});
}
@Nullable
private Project getProjectByPath(String path) {
for (Project project : appContext.getProjects()) {
if (path.equals(project.getPath())) {
return project;
}
}
return null;
}
@Override
public void addCommandLoadedListener(CommandLoadedListener listener) {
commandLoadedListeners.add(listener);
}
@Override
public void removeCommandLoadedListener(CommandLoadedListener listener) {
commandLoadedListeners.remove(listener);
}
@Override
public void addCommandChangedListener(CommandChangedListener listener) {
commandChangedListeners.add(listener);
}
@Override
public void removeCommandChangedListener(CommandChangedListener listener) {
commandChangedListeners.remove(listener);
}
private void notifyCommandsLoaded() {
commandLoadedListeners.forEach(CommandLoadedListener::onCommandsLoaded);
}
private void notifyCommandAdded(CommandImpl command) {
commandChangedListeners.forEach(listener -> listener.onCommandAdded(command));
}
private void notifyCommandRemoved(CommandImpl command) {
commandChangedListeners.forEach(listener -> listener.onCommandRemoved(command));
}
private void notifyCommandUpdated(CommandImpl prevCommand, CommandImpl command) {
commandChangedListeners.forEach(listener -> listener.onCommandUpdated(prevCommand, command));
}
/**
* Returns {@code customName} if it's unique within the given {@code commandTypeId}
* or newly generated name if it isn't unique within the given {@code commandTypeId}.
*/
private String getUniqueCommandName(String commandTypeId, @Nullable String customName) {
final CommandType commandType = commandTypeRegistry.getCommandTypeById(commandTypeId);
final Set<String> commandNames = commands.keySet();
final String newCommandName;
if (isNullOrEmpty(customName)) {
newCommandName = "new" + commandType.getDisplayName();
} else {
if (!commandNames.contains(customName)) {
return customName;
}
newCommandName = customName + " copy";
}
if (!commandNames.contains(newCommandName)) {
return newCommandName;
}
for (int count = 1; count < 1000; count++) {
if (!commandNames.contains(newCommandName + "-" + count)) {
return newCommandName + "-" + count;
}
}
return newCommandName;
}
}

View File

@ -0,0 +1,151 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.manager;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.machine.shared.dto.CommandDto;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseProvider;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandType;
import org.eclipse.che.ide.api.project.MutableProjectConfig;
import org.eclipse.che.ide.api.resources.Project;
import org.eclipse.che.ide.dto.DtoFactory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.eclipse.che.api.project.shared.Constants.COMMANDS_ATTRIBUTE_NAME;
/** Responsible for managing the commands which are stored with projects. */
@Singleton
class ProjectCommandManagerDelegate {
private final DtoFactory dtoFactory;
private final PromiseProvider promiseProvider;
@Inject
ProjectCommandManagerDelegate(DtoFactory dtoFactory, PromiseProvider promiseProvider) {
this.dtoFactory = dtoFactory;
this.promiseProvider = promiseProvider;
}
/** Returns commands for the specified {@code project}. */
List<CommandImpl> getCommands(Project project) {
List<String> attrValues = project.getAttributes(COMMANDS_ATTRIBUTE_NAME);
if (attrValues == null) {
return new ArrayList<>();
}
Map<String, CommandImpl> commands = new HashMap<>(attrValues.size());
for (String commandJson : attrValues) {
CommandDto command = dtoFactory.createDtoFromJson(commandJson, CommandDto.class);
commands.put(command.getName(), new CommandImpl(command));
}
return new ArrayList<>(commands.values());
}
/**
* Creates new command of the specified type.
* <p><b>Note</b> that command's name will be generated by {@link CommandManager}
* and command line will be provided by an appropriate {@link CommandType}.
*/
Promise<CommandImpl> createCommand(Project project, final CommandImpl newCommand) {
final List<CommandImpl> commands = getCommands(project);
for (CommandImpl projectCommand : commands) {
if (projectCommand.getName().equals(newCommand.getName())) {
return promiseProvider.reject(new Exception("Command '" + newCommand.getName() +
"' is already associated to the project '" + project.getName() + "'"));
}
}
commands.add(newCommand);
return updateProject(project, commands).then((Function<Void, CommandImpl>)arg -> newCommand);
}
/** Updates the specified {@code project} with the given {@code commands}. */
private Promise<Void> updateProject(Project project, List<CommandImpl> commands) {
MutableProjectConfig config = new MutableProjectConfig(project);
Map<String, List<String>> attributes = config.getAttributes();
List<String> attrValue = new ArrayList<>(attributes.size());
for (CommandImpl command : commands) {
CommandDto commandDto = dtoFactory.createDto(CommandDto.class)
.withName(command.getName())
.withType(command.getType())
.withCommandLine(command.getCommandLine())
.withAttributes(command.getAttributes());
attrValue.add(dtoFactory.toJson(commandDto));
}
attributes.put(COMMANDS_ATTRIBUTE_NAME, attrValue);
return project.update()
.withBody(config)
.send()
.then((Function<Project, Void>)arg -> null);
}
/**
* Updates the command with the specified {@code name} by replacing it with the given {@code command}.
* <p><b>Note</b> that name of the updated command may differ from the name provided by the given {@code command}
* in order to prevent name duplication.
*/
Promise<CommandImpl> updateCommand(Project project, final CommandImpl command) {
final List<CommandImpl> projectCommands = getCommands(project);
if (projectCommands.isEmpty()) {
return promiseProvider.reject(new Exception("Command '" + command.getName() + "' is not associated with the project '" +
project.getName() + "'"));
}
final List<CommandImpl> commandsToUpdate = new ArrayList<>();
for (CommandImpl projectCommand : projectCommands) {
// skip existed command with the same name
if (!command.getName().equals(projectCommand.getName())) {
commandsToUpdate.add(projectCommand);
}
}
commandsToUpdate.add(command);
return updateProject(project, new ArrayList<>(commandsToUpdate))
.then((Function<Void, CommandImpl>)arg -> command);
}
/** Removes the command with the specified {@code commandName}. */
Promise<Void> removeCommand(Project project, String commandName) {
final List<CommandImpl> commands = getCommands(project);
if (commands.isEmpty()) {
return promiseProvider.reject(new Exception("Command '" + commandName + "' is not associated with the project '" +
project.getName() + "'"));
}
final List<CommandImpl> commandsToUpdate = new ArrayList<>();
for (CommandImpl projectCommand : commands) {
if (!commandName.equals(projectCommand.getName())) {
commandsToUpdate.add(projectCommand);
}
}
return updateProject(project, new ArrayList<>(commandsToUpdate));
}
}

View File

@ -0,0 +1,94 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.manager;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.machine.shared.dto.CommandDto;
import org.eclipse.che.api.promises.client.Function;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.workspace.shared.dto.WorkspaceDto;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandType;
import org.eclipse.che.ide.api.workspace.WorkspaceServiceClient;
import org.eclipse.che.ide.dto.DtoFactory;
import java.util.List;
import static java.util.stream.Collectors.toList;
/** Responsible for managing the commands which are stored with workspace. */
@Singleton
class WorkspaceCommandManagerDelegate {
private final DtoFactory dtoFactory;
private final WorkspaceServiceClient workspaceServiceClient;
private final AppContext appContext;
@Inject
WorkspaceCommandManagerDelegate(DtoFactory dtoFactory,
WorkspaceServiceClient workspaceServiceClient,
AppContext appContext) {
this.dtoFactory = dtoFactory;
this.workspaceServiceClient = workspaceServiceClient;
this.appContext = appContext;
}
/** Returns commands which are stored in the workspace with the specified {@code workspaceId}. */
Promise<List<CommandImpl>> getCommands(String workspaceId) {
return workspaceServiceClient.getCommands(workspaceId)
.then((Function<List<CommandDto>,
List<CommandImpl>>)commands -> commands.stream()
.map(CommandImpl::new)
.collect(toList()));
}
/**
* Creates new command of the specified type.
* <p><b>Note</b> that command's name will be generated by {@link CommandManager}
* and command line will be provided by an appropriate {@link CommandType}.
*/
Promise<CommandImpl> createCommand(final CommandImpl command) {
final CommandDto commandDto = dtoFactory.createDto(CommandDto.class)
.withName(command.getName())
.withCommandLine(command.getCommandLine())
.withType(command.getType())
.withAttributes(command.getAttributes());
return workspaceServiceClient.addCommand(appContext.getWorkspaceId(), commandDto)
.then((Function<WorkspaceDto, CommandImpl>)arg -> command);
}
/**
* Updates the command with the specified {@code name} by replacing it with the given {@code command}.
* <p><b>Note</b> that name of the updated command may differ from the name provided by the given {@code command}
* in order to prevent name duplication.
*/
Promise<CommandImpl> updateCommand(final CommandImpl command) {
final CommandDto commandDto = dtoFactory.createDto(CommandDto.class)
.withName(command.getName())
.withCommandLine(command.getCommandLine())
.withType(command.getType())
.withAttributes(command.getAttributes());
return workspaceServiceClient.updateCommand(appContext.getWorkspaceId(), command.getName(), commandDto)
.then((Function<WorkspaceDto, CommandImpl>)arg -> command);
}
/** Removes the command with the specified {@code commandName}. */
Promise<Void> removeCommand(String commandName) {
return workspaceServiceClient.deleteCommand(appContext.getWorkspaceId(), commandName)
.then((Function<WorkspaceDto, Void>)arg -> null);
}
}

View File

@ -0,0 +1,60 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.node;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.data.tree.Node;
import org.eclipse.che.ide.api.data.tree.settings.NodeSettings;
import org.eclipse.che.ide.command.CommandUtils;
import org.eclipse.che.ide.project.node.SyntheticNode;
import org.eclipse.che.ide.ui.smartTree.presentation.NodePresentation;
import org.vectomatic.dom.svg.ui.SVGResource;
import java.util.List;
/** Abstract tree node that represents {@link CommandImpl}. */
class AbstractCommandNode extends SyntheticNode<CommandImpl> {
private final CommandUtils commandUtils;
AbstractCommandNode(CommandImpl data, NodeSettings nodeSettings, CommandUtils commandUtils) {
super(data, nodeSettings);
this.commandUtils = commandUtils;
}
@Override
public void updatePresentation(NodePresentation presentation) {
presentation.setPresentableText(getName());
final SVGResource commandTypeIcon = commandUtils.getCommandTypeIcon(getData().getType());
if (commandTypeIcon != null) {
presentation.setPresentableIcon(commandTypeIcon);
}
}
@Override
public String getName() {
return getData().getName();
}
@Override
public boolean isLeaf() {
return true;
}
@Override
protected Promise<List<Node>> getChildrenImpl() {
return null;
}
}

View File

@ -0,0 +1,89 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.node;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.data.tree.HasAction;
import org.eclipse.che.ide.api.editor.EditorAgent;
import org.eclipse.che.ide.api.resources.VirtualFile;
import org.eclipse.che.ide.command.CommandUtils;
import org.eclipse.che.ide.resource.Path;
import org.eclipse.che.ide.ui.smartTree.presentation.NodePresentation;
/** Extension of {@link AbstractCommandNode} that also acts as a {@link VirtualFile} for using it in editor. */
public class CommandFileNode extends AbstractCommandNode implements HasAction, VirtualFile {
/** Extension for the file type that represents a command. */
public static final String FILE_TYPE_EXT = "che_command_internal";
private final EditorAgent editorAgent;
@Inject
public CommandFileNode(@Assisted CommandImpl data,
CommandUtils commandUtils,
EditorAgent editorAgent) {
super(data, null, commandUtils);
this.editorAgent = editorAgent;
}
@Override
public void updatePresentation(NodePresentation presentation) {
super.updatePresentation(presentation);
presentation.setPresentableText(getDisplayName());
presentation.setPresentableTextCss("overflow: hidden; text-overflow: ellipsis;");
}
@Override
public void actionPerformed() {
editorAgent.openEditor(this);
}
@Override
public Path getLocation() {
return Path.valueOf("commands/" + getData().getType() + "/" + getData().getName());
}
@Override
public String getName() {
return getData().getName() + "." + FILE_TYPE_EXT;
}
@Override
public String getDisplayName() {
return getData().getName();
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public String getContentUrl() {
return null;
}
@Override
public Promise<String> getContent() {
return null;
}
@Override
public Promise<Void> updateContent(String content) {
return null;
}
}

View File

@ -0,0 +1,75 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.node;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.che.api.promises.client.Promise;
import org.eclipse.che.api.promises.client.PromiseProvider;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.data.tree.Node;
import org.eclipse.che.ide.command.CommandUtils;
import org.eclipse.che.ide.project.node.SyntheticNode;
import org.eclipse.che.ide.ui.smartTree.presentation.NodePresentation;
import org.vectomatic.dom.svg.ui.SVGResource;
import java.util.ArrayList;
import java.util.List;
/** Tree node that represents {@link CommandGoal}. */
public class CommandGoalNode extends SyntheticNode<CommandGoal> {
private final List<? extends AbstractCommandNode> commands;
private final PromiseProvider promiseProvider;
private final CommandUtils commandUtils;
@Inject
public CommandGoalNode(@Assisted CommandGoal data,
@Assisted List<? extends AbstractCommandNode> commands,
PromiseProvider promiseProvider,
CommandUtils commandUtils) {
super(data, null);
this.commands = commands;
this.promiseProvider = promiseProvider;
this.commandUtils = commandUtils;
}
@Override
public void updatePresentation(NodePresentation presentation) {
presentation.setPresentableText(getName().toUpperCase() + " (" + commands.size() + ")");
presentation.setPresentableTextCss("font-weight: bold;");
final SVGResource goalIcon = commandUtils.getCommandGoalIcon(getData().getId());
if (goalIcon != null) {
presentation.setPresentableIcon(goalIcon);
}
}
@Override
public String getName() {
return getData().getId();
}
@Override
public boolean isLeaf() {
return false;
}
@Override
protected Promise<List<Node>> getChildrenImpl() {
List<Node> children = new ArrayList<>();
children.addAll(commands);
return promiseProvider.resolve(children);
}
}

View File

@ -0,0 +1,46 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.node;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.data.tree.HasAction;
import org.eclipse.che.ide.command.CommandUtils;
/**
* Extension of {@link AbstractCommandNode} that can execute
* a command when performing an action is requested.
*/
public class ExecutableCommandNode extends AbstractCommandNode implements HasAction {
private final ActionDelegate actionDelegate;
@Inject
public ExecutableCommandNode(@Assisted CommandImpl data,
@Assisted ActionDelegate actionDelegate,
CommandUtils commandUtils) {
super(data, null, commandUtils);
this.actionDelegate = actionDelegate;
}
@Override
public void actionPerformed() {
actionDelegate.actionPerformed();
}
/** Interface for delegating performing action on node. */
public interface ActionDelegate {
void actionPerformed();
}
}

View File

@ -0,0 +1,26 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.node;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandImpl;
import java.util.List;
/** Factory for different command tree nodes. */
public interface NodeFactory {
CommandGoalNode newCommandGoalNode(CommandGoal data, List<? extends AbstractCommandNode> commands);
ExecutableCommandNode newExecutableCommandNode(CommandImpl command, ExecutableCommandNode.ActionDelegate actionDelegate);
CommandFileNode newCommandFileNode(CommandImpl data);
}

View File

@ -0,0 +1,117 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.palette;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.api.core.model.workspace.WorkspaceRuntime;
import org.eclipse.che.ide.api.app.AppContext;
import org.eclipse.che.ide.api.command.CommandExecutor;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.dialogs.DialogFactory;
import org.eclipse.che.ide.command.CommandUtils;
import org.eclipse.che.ide.machine.chooser.MachineChooser;
import java.util.List;
import java.util.ListIterator;
import static java.util.Collections.emptyList;
import static org.eclipse.che.ide.util.StringUtils.containsIgnoreCase;
/**
* Presenter for Commands Palette.
*
* @author Artem Zatsarynnyi
*/
@Singleton
public class CommandsPalettePresenter implements CommandsPaletteView.ActionDelegate {
private final CommandsPaletteView view;
private final CommandManager commandManager;
private final CommandExecutor commandExecutor;
private final DialogFactory dialogFactory;
private final AppContext appContext;
private final MachineChooser machineChooser;
private final CommandUtils commandUtils;
private final PaletteMessages messages;
@Inject
public CommandsPalettePresenter(CommandsPaletteView view,
CommandManager commandManager,
CommandExecutor commandExecutor,
DialogFactory dialogFactory,
AppContext appContext,
MachineChooser machineChooser,
CommandUtils commandUtils,
PaletteMessages messages) {
this.view = view;
this.commandManager = commandManager;
this.commandExecutor = commandExecutor;
this.dialogFactory = dialogFactory;
this.appContext = appContext;
this.machineChooser = machineChooser;
this.commandUtils = commandUtils;
this.messages = messages;
view.setDelegate(this);
}
public void showDialog() {
view.show();
view.setCommands(commandUtils.groupCommandsByGoal(commandManager.getCommands()));
}
@Override
public void onFilterChanged(String filterValue) {
final List<CommandImpl> filteredCommands = commandManager.getCommands();
if (!filterValue.isEmpty()) {
final ListIterator<CommandImpl> it = filteredCommands.listIterator();
while (it.hasNext()) {
final CommandImpl command = it.next();
if (!containsIgnoreCase(command.getName(), filterValue)) {
it.remove();
}
}
}
view.setCommands(commandUtils.groupCommandsByGoal(filteredCommands));
}
@Override
public void onCommandExecute(CommandImpl command) {
view.close();
if (getMachines().isEmpty()) {
// should not happen, but let's play safe
dialogFactory.createMessageDialog("", messages.messageNoMachine(), null).show();
} else {
machineChooser.show().then(machine -> {
commandExecutor.executeCommand(command, machine);
});
}
}
private List<? extends Machine> getMachines() {
final WorkspaceRuntime runtime = appContext.getWorkspace().getRuntime();
if (runtime != null) {
return runtime.getMachines();
}
return emptyList();
}
}

View File

@ -0,0 +1,50 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.palette;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.mvp.View;
import java.util.List;
import java.util.Map;
/**
* The view for {@link CommandsPalettePresenter}.
*
* @author Artem Zatsarynnyi
*/
public interface CommandsPaletteView extends View<CommandsPaletteView.ActionDelegate> {
/** Show the view. */
void show();
/** Close the view. */
void close();
/**
* Sets the commands to display in the view.
*
* @param commands
* commands grouped by type
*/
void setCommands(Map<CommandGoal, List<CommandImpl>> commands);
/** The action delegate for this view. */
interface ActionDelegate {
/** Called when filtering commands is requested. */
void onFilterChanged(String filterValue);
/** Called when command execution is requested. */
void onCommandExecute(CommandImpl command);
}
}

View File

@ -0,0 +1,190 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.palette;
import elemental.html.DivElement;
import elemental.html.SpanElement;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.ide.FontAwesome;
import org.eclipse.che.ide.api.command.CommandGoal;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.data.tree.Node;
import org.eclipse.che.ide.command.node.CommandGoalNode;
import org.eclipse.che.ide.command.node.ExecutableCommandNode;
import org.eclipse.che.ide.command.node.NodeFactory;
import org.eclipse.che.ide.ui.smartTree.NodeLoader;
import org.eclipse.che.ide.ui.smartTree.NodeStorage;
import org.eclipse.che.ide.ui.smartTree.Tree;
import org.eclipse.che.ide.ui.window.Window;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import static com.google.gwt.event.dom.client.KeyCodes.KEY_DOWN;
import static com.google.gwt.event.dom.client.KeyCodes.KEY_ENTER;
import static com.google.gwt.event.dom.client.KeyCodes.KEY_UP;
import static org.eclipse.che.ide.ui.smartTree.SelectionModel.Mode.SINGLE;
import static org.eclipse.che.ide.util.dom.Elements.createDivElement;
import static org.eclipse.che.ide.util.dom.Elements.createSpanElement;
import static org.eclipse.che.ide.util.dom.Elements.createTextNode;
/** Implementation of {@link CommandsPaletteView}. */
@Singleton
public class CommandsPaletteViewImpl extends Window implements CommandsPaletteView {
private static final CommandsPaletteViewImplUiBinder UI_BINDER = GWT.create(CommandsPaletteViewImplUiBinder.class);
private final NodeFactory nodeFactory;
@UiField
TextBox filterField;
@UiField(provided = true)
Tree tree;
@UiField
Label hintLabel;
private ActionDelegate delegate;
@Inject
public CommandsPaletteViewImpl(NodeFactory nodeFactory, PaletteMessages messages) {
this.nodeFactory = nodeFactory;
tree = new Tree(new NodeStorage(), new NodeLoader());
tree.getSelectionModel().setSelectionMode(SINGLE);
setWidget(UI_BINDER.createAndBindUi(this));
setTitle(messages.viewTitle());
filterField.getElement().setAttribute("placeholder", messages.filterPlaceholder());
initHintLabel();
getFooter().removeFromParent();
}
private void initHintLabel() {
final SpanElement upKeyLabel = createKeyLabel();
upKeyLabel.setInnerHTML(FontAwesome.ARROW_UP);
final SpanElement downKeyLabel = createKeyLabel();
downKeyLabel.setInnerHTML(FontAwesome.ARROW_DOWN);
final SpanElement enterKeyLabel = createKeyLabel();
enterKeyLabel.getStyle().setPadding("0px 1px 1px 4px");
enterKeyLabel.setInnerText(" Enter ");
final DivElement hintElement = createDivElement();
hintElement.appendChild(upKeyLabel);
hintElement.appendChild(downKeyLabel);
hintElement.appendChild(createTextNode(" to select and "));
hintElement.appendChild(enterKeyLabel);
hintElement.appendChild(createTextNode(" to execute"));
hintLabel.getElement().appendChild((Element)hintElement);
}
/** Creates an html element for displaying keyboard key. */
private SpanElement createKeyLabel() {
SpanElement element = createSpanElement();
element.getStyle().setFontWeight("bold");
element.getStyle().setPadding("0 4px 1px 4px");
element.getStyle().setMargin("0 3px");
element.getStyle().setBorderWidth("1px");
element.getStyle().setBorderStyle("solid");
element.getStyle().setProperty("border-radius", "3px");
return element;
}
@Override
public void show() {
super.show();
filterField.setValue("");
filterField.setFocus(true);
}
@Override
public void close() {
hide();
}
@Override
public void setCommands(Map<CommandGoal, List<CommandImpl>> commands) {
renderCommands(commands);
}
/** Render commands grouped by goals. */
private void renderCommands(Map<CommandGoal, List<CommandImpl>> commands) {
tree.getNodeStorage().clear();
for (Entry<CommandGoal, List<CommandImpl>> entry : commands.entrySet()) {
List<ExecutableCommandNode> commandNodes = new ArrayList<>(entry.getValue().size());
for (final CommandImpl command : entry.getValue()) {
commandNodes.add(nodeFactory.newExecutableCommandNode(command, () -> delegate.onCommandExecute(command)));
}
final CommandGoalNode commandGoalNode = nodeFactory.newCommandGoalNode(entry.getKey(), commandNodes);
tree.getNodeStorage().add(commandGoalNode);
}
tree.expandAll();
}
@Override
public void setDelegate(ActionDelegate delegate) {
this.delegate = delegate;
}
@UiHandler({"filterField"})
void onFilterChanged(KeyUpEvent event) {
switch (event.getNativeKeyCode()) {
case KEY_UP:
tree.getSelectionModel().selectPrevious();
break;
case KEY_DOWN:
tree.getSelectionModel().selectNext();
break;
case KEY_ENTER:
final List<Node> selectedNodes = tree.getSelectionModel().getSelectedNodes();
if (!selectedNodes.isEmpty()) {
final Node node = selectedNodes.get(0);
if (node instanceof ExecutableCommandNode) {
delegate.onCommandExecute(((ExecutableCommandNode)node).getData());
}
}
break;
default:
delegate.onFilterChanged(filterField.getValue());
}
}
interface CommandsPaletteViewImplUiBinder extends UiBinder<Widget, CommandsPaletteViewImpl> {
}
}

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Copyright (c) 2012-2017 Codenvy, S.A.
All rights reserved. This program and the accompanying materials
are made available under the terms of the Eclipse Public License v1.0
which accompanies this distribution, and is available at
http://www.eclipse.org/legal/epl-v10.html
Contributors:
Codenvy, S.A. - initial API and implementation
-->
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
xmlns:g='urn:import:com.google.gwt.user.client.ui'
xmlns:che='urn:import:org.eclipse.che.ide.ui'>
<ui:with field='res' type='org.eclipse.che.ide.Resources'/>
<ui:with field='messages' type='org.eclipse.che.ide.command.palette.PaletteMessages'/>
<ui:style>
@eval partBackground org.eclipse.che.ide.api.theme.Style.theme.partBackground();
@eval borderColor org.eclipse.che.ide.api.theme.Style.theme.getTextFieldBackgroundColor();
.tree {
border: 1px solid;
border-color: borderColor;
background-color: partBackground;
margin-top: 7px;
min-width: 99%;
}
.hint-label {
margin: 7px auto;
}
</ui:style>
<g:DockLayoutPanel unit="PX" width="300px" height="300px" debugId="commands_palette">
<g:north size="25">
<g:TextBox width="87%"
ui:field="filterField"
addStyleNames="{res.commandsPaletteCss.filterField}"
debugId="commands_palette-filter"/>
</g:north>
<g:center>
<che:smartTree.Tree ui:field="tree" addStyleNames="{style.tree}"/>
</g:center>
<g:south size="50">
<g:FlowPanel>
<g:Label width="190px" text="{messages.viewHintText}" addStyleNames="{style.hint-label}"/>
<g:Label width="230px" ui:field="hintLabel" addStyleNames="{style.hint-label}"/>
</g:FlowPanel>
</g:south>
</g:DockLayoutPanel>
</ui:UiBinder>

View File

@ -0,0 +1,39 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.palette;
import com.google.gwt.i18n.client.Messages;
/**
* I18n messages for the Commands Palette.
*
* @author Artem Zatsarynnyi
*/
public interface PaletteMessages extends Messages {
@Key("action.show_palette.title")
String actionShowPaletteTitle();
@Key("action.show_palette.description")
String actionShowPaletteDescription();
@Key("view.title")
String viewTitle();
@Key("view.filter.placeholder")
String filterPlaceholder();
@Key("view.hint.text")
String viewHintText();
@Key("message.no_machine")
String messageNoMachine();
}

View File

@ -0,0 +1,43 @@
/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command.palette;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
/**
* Action for opening Commands Palette.
*
* @author Artem Zatsarynnyi
*/
@Singleton
public class ShowCommandsPaletteAction extends Action {
private final CommandsPalettePresenter presenter;
@Inject
public ShowCommandsPaletteAction(PaletteMessages messages, CommandsPalettePresenter presenter) {
super(messages.actionShowPaletteTitle(),
messages.actionShowPaletteDescription(),
null,
null);
this.presenter = presenter;
}
@Override
public void actionPerformed(ActionEvent e) {
presenter.showDialog();
}
}

View File

@ -8,7 +8,7 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command;
package org.eclipse.che.ide.command.producer;
import com.google.inject.Inject;
import com.google.inject.assistedinject.Assisted;
@ -17,7 +17,7 @@ import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.ide.api.action.Action;
import org.eclipse.che.ide.api.action.ActionEvent;
import org.eclipse.che.ide.api.command.CommandImpl;
import org.eclipse.che.ide.api.command.CommandManager;
import org.eclipse.che.ide.api.command.CommandExecutor;
import org.eclipse.che.ide.api.command.CommandProducer;
/**
@ -30,18 +30,18 @@ public class CommandProducerAction extends Action {
private final CommandProducer commandProducer;
private final Machine machine;
private final CommandManager commandManager;
private final CommandExecutor commandExecutor;
@Inject
public CommandProducerAction(@Assisted String name,
@Assisted CommandProducer commandProducer,
@Assisted Machine machine,
CommandManager commandManager) {
CommandExecutor commandExecutor) {
super(name);
this.commandProducer = commandProducer;
this.machine = machine;
this.commandManager = commandManager;
this.commandExecutor = commandExecutor;
}
@Override
@ -52,6 +52,6 @@ public class CommandProducerAction extends Action {
@Override
public void actionPerformed(ActionEvent e) {
CommandImpl command = commandProducer.createCommand(machine);
commandManager.executeCommand(command, machine);
commandExecutor.executeCommand(command, machine);
}
}

View File

@ -8,7 +8,7 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command;
package org.eclipse.che.ide.command.producer;
import org.eclipse.che.api.core.model.machine.Machine;
import org.eclipse.che.ide.api.command.CommandProducer;

View File

@ -8,7 +8,7 @@
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.command;
package org.eclipse.che.ide.command.producer;
import com.google.gwt.core.client.Callback;
import com.google.inject.Inject;
@ -43,7 +43,7 @@ import static org.eclipse.che.ide.api.action.IdeActions.GROUP_MAIN_TOOLBAR;
import static org.eclipse.che.ide.api.constraints.Anchor.AFTER;
/**
* Manages actions for the contextual commands.
* Manages actions for the commands.
* <p>Manager gets all registered {@link CommandProducer}s and creates related actions in context menus.
* <p>Manager listens all machines's state (running/destroyed) in order to
* create/remove actions for the related {@link CommandProducer}s in case
@ -53,12 +53,15 @@ import static org.eclipse.che.ide.api.constraints.Anchor.AFTER;
* @see CommandProducer
*/
@Singleton
public class CommandProducerActionManager implements MachineStateEvent.Handler, WsAgentStateHandler, Component {
public class CommandProducerActionManager implements MachineStateEvent.Handler,
WsAgentStateHandler,
Component {
private final ActionManager actionManager;
private final CommandProducerActionFactory commandProducerActionFactory;
private final AppContext appContext;
private final Resources resources;
private final ProducerMessages messages;
private final List<Machine> machines;
private final Set<CommandProducer> commandProducers;
@ -73,11 +76,13 @@ public class CommandProducerActionManager implements MachineStateEvent.Handler,
ActionManager actionManager,
CommandProducerActionFactory commandProducerActionFactory,
AppContext appContext,
Resources resources) {
Resources resources,
ProducerMessages messages) {
this.actionManager = actionManager;
this.commandProducerActionFactory = commandProducerActionFactory;
this.appContext = appContext;
this.resources = resources;
this.messages = messages;
machines = new ArrayList<>();
commandProducers = new HashSet<>();
@ -93,10 +98,10 @@ public class CommandProducerActionManager implements MachineStateEvent.Handler,
private void start(Set<CommandProducer> commandProducers) {
this.commandProducers.addAll(commandProducers);
commandActionsPopUpGroup = new DefaultActionGroup("Commands", true, actionManager);
commandActionsPopUpGroup = new DefaultActionGroup(messages.actionCommandsTitle(), true, actionManager);
actionManager.registerAction("commandActionsPopUpGroup", commandActionsPopUpGroup);
commandActionsPopUpGroup.getTemplatePresentation().setSVGResource(resources.compile());
commandActionsPopUpGroup.getTemplatePresentation().setDescription("Execute command");
commandActionsPopUpGroup.getTemplatePresentation().setDescription(messages.actionCommandsDescription());
DefaultActionGroup mainContextMenu = (DefaultActionGroup)actionManager.getAction(GROUP_MAIN_CONTEXT_MENU);
mainContextMenu.add(commandActionsPopUpGroup);
@ -183,11 +188,7 @@ public class CommandProducerActionManager implements MachineStateEvent.Handler,
CommandProducerAction machineAction = commandProducerActionFactory.create(machine.getConfig().getName(),
commandProducer,
machine);
List<Action> actionList = actionsByMachines.get(machine);
if (actionList == null) {
actionList = new ArrayList<>();
actionsByMachines.put(machine, actionList);
}
final List<Action> actionList = actionsByMachines.computeIfAbsent(machine, key -> new ArrayList<>());
actionList.add(machineAction);
actionManager.registerAction(machine.getConfig().getName(), machineAction);

Some files were not shown because too many files have changed in this diff Show More