<orderEntry type="library" name="Guava" level="project" />
<orderEntry type="library" scope="TEST" name="Mocks" level="project" />
<orderEntry type="module" module-name="xdebugger-api" />
+ <orderEntry type="library" name="xpp3-1.1.4-min" level="project" />
</component>
</module>
--- /dev/null
+/*
+ * Copyright 2000-2011 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.intellij.execution.process;
+
+import com.intellij.codeInsight.lookup.LookupManager;
+import com.intellij.execution.console.LanguageConsoleImpl;
+import com.intellij.openapi.Disposable;
+import com.intellij.openapi.actionSystem.AnAction;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.actionSystem.EmptyAction;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.PathManager;
+import com.intellij.openapi.application.Result;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.openapi.diagnostic.Logger;
+import com.intellij.openapi.editor.CaretModel;
+import com.intellij.openapi.editor.Document;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.ex.EditorEx;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.project.ex.ProjectEx;
+import com.intellij.openapi.util.Disposer;
+import com.intellij.openapi.util.text.StringHash;
+import com.intellij.openapi.util.text.StringUtil;
+import com.thoughtworks.xstream.io.HierarchicalStreamReader;
+import com.thoughtworks.xstream.io.xml.XppReader;
+import org.jetbrains.annotations.NotNull;
+import org.xmlpull.v1.XmlPullParserFactory;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.awt.event.KeyEvent;
+import java.io.*;
+import java.util.ArrayList;
+import java.util.ListIterator;
+
+/**
+ * @author gregsh
+ */
+public class ConsoleHistoryController {
+
+ private static final Logger LOG = Logger.getInstance("com.intellij.execution.process.ConsoleHistoryController");
+
+ private final String myType;
+ private final String myId;
+ private final LanguageConsoleImpl myConsole;
+ private final ConsoleHistoryModel myModel;
+ private final AnAction myHistoryNext = new MyAction(true);
+ private final AnAction myHistoryPrev = new MyAction(false);
+ private boolean myMultiline;
+ private long myLastSaveStamp;
+
+
+ public ConsoleHistoryController(final String type,
+ final String persistenceId,
+ final LanguageConsoleImpl console,
+ final ConsoleHistoryModel model) {
+ myType = type;
+ myId = "".equals(persistenceId)? console.getProject().getLocation() : persistenceId;
+ myConsole = console;
+ myModel = model;
+ }
+
+ public boolean isMultiline() {
+ return myMultiline;
+ }
+
+ public ConsoleHistoryController setMultiline(boolean multiline) {
+ myMultiline = multiline;
+ return this;
+ }
+
+ public ConsoleHistoryModel getModel() {
+ return myModel;
+ }
+
+ public void install() {
+ if (myId != null) {
+ ApplicationManager.getApplication().getMessageBus().connect(myConsole).subscribe(
+ ProjectEx.ProjectSaved.TOPIC, new ProjectEx.ProjectSaved() {
+ @Override
+ public void saved(@NotNull final Project project) {
+ saveHistory();
+ }
+ });
+ Disposer.register(myConsole, new Disposable() {
+ @Override
+ public void dispose() {
+ saveHistory();
+ }
+ });
+ loadHistory();
+ }
+ configureActions();
+ myLastSaveStamp = myModel.getModificationCount();
+ }
+
+ private void configureActions() {
+ // todo add clipboard-like history viewer/selector action
+ EmptyAction.setupAction(myHistoryNext, "Console.History.Next", null);
+ EmptyAction.setupAction(myHistoryPrev, "Console.History.Previous", null);
+ if (!myMultiline) {
+ myHistoryNext.registerCustomShortcutSet(KeyEvent.VK_UP, 0, null);
+ myHistoryPrev.registerCustomShortcutSet(KeyEvent.VK_DOWN, 0, null);
+ }
+ myHistoryNext.registerCustomShortcutSet(myHistoryNext.getShortcutSet(), myConsole.getConsoleEditor().getComponent());
+ myHistoryPrev.registerCustomShortcutSet(myHistoryPrev.getShortcutSet(), myConsole.getConsoleEditor().getComponent());
+ }
+
+ private File getFile() {
+ final StringBuilder sb = new StringBuilder().append(PathManager.getSystemPath()).append(File.separator).append("userHistory").
+ append(File.separator).append(myType).append(Long.toHexString(StringHash.calc(myId))).append(".hist.xml");
+ return new File(sb.toString());
+ }
+
+
+ private void loadHistory() {
+ final File file = getFile();
+ if (!file.exists()) return;
+ HierarchicalStreamReader xmlReader = null;
+ try {
+ xmlReader = new XppReader(new FileReader(file));
+ loadHistory(xmlReader);
+ }
+ catch (Exception ex) {
+ LOG.error(ex);
+ }
+ finally {
+ if (xmlReader != null) {
+ xmlReader.close();
+ }
+ }
+
+ }
+
+ private void saveHistory() {
+ if (myLastSaveStamp == myModel.getModificationCount()) return;
+
+ final File file = getFile();
+ final File dir = file.getParentFile();
+ if (!dir.exists() && !dir.mkdirs() || !dir.isDirectory()) {
+ LOG.error("failed to create folder: "+dir.getAbsolutePath());
+ return;
+ }
+ final File tmpFile = new File(dir, file.getName()+".tmp");
+ FileOutputStream os = null;
+ try {
+ final XmlSerializer serializer = XmlPullParserFactory.newInstance().newSerializer();
+ try {
+ serializer.setProperty("http://xmlpull.org/v1/doc/properties.html#serializer-indentation", " ");
+ }
+ catch (Exception e) {
+ // not recognized
+ }
+ serializer.setOutput(new PrintWriter(os = new FileOutputStream(tmpFile)));
+ saveHistory(serializer);
+ file.delete();
+ tmpFile.renameTo(file);
+ myLastSaveStamp = myModel.getModificationCount();
+ }
+ catch (Exception ex) {
+ LOG.error(ex);
+ }
+ finally {
+ try {
+ os.close();
+ }
+ catch (Exception e) {
+ // nothing
+ }
+ cleanupOldFiles(dir);
+ }
+ }
+
+ private static void cleanupOldFiles(final File dir) {
+ final long keep2weeks = 2 * 1000L * 60 * 60 * 24 * 7;
+ final long curTime = System.currentTimeMillis();
+ for (File file : dir.listFiles()) {
+ if (file.isFile() && file.getName().endsWith(".hist.xml") && curTime - file.lastModified() > keep2weeks) {
+ file.delete();
+ }
+ }
+ }
+
+
+ public AnAction getHistoryNext() {
+ return myHistoryNext;
+ }
+
+ public AnAction getHistoryPrev() {
+ return myHistoryPrev;
+ }
+
+
+ protected void actionTriggered(final String command) {
+ final Editor editor = myConsole.getConsoleEditor();
+ final Document document = editor.getDocument();
+ new WriteCommandAction(myConsole.getProject(), myConsole.getFile()) {
+ protected void run(final Result result) throws Throwable {
+ document.setText(StringUtil.notNullize(command));
+ editor.getCaretModel().moveToOffset(document.getTextLength());
+ }
+ }.execute();
+ }
+
+
+ private class MyAction extends AnAction {
+ private boolean myNext;
+
+ public MyAction(final boolean next) {
+ myNext = next;
+ getTemplatePresentation().setVisible(false);
+ }
+
+ @Override
+ public void actionPerformed(final AnActionEvent e) {
+ actionTriggered(myNext ? myModel.getHistoryNext() : myModel.getHistoryPrev());
+ }
+
+ @Override
+ public void update(final AnActionEvent e) {
+ super.update(e);
+ e.getPresentation().setEnabled(myModel.hasHistory(myNext) &&
+ (myMultiline || canMoveInEditor(myNext)));
+ }
+ }
+
+ private boolean canMoveInEditor(final boolean next) {
+ final EditorEx consoleEditor = myConsole.getConsoleEditor();
+ final Document document = consoleEditor.getDocument();
+ final CaretModel caretModel = consoleEditor.getCaretModel();
+
+ if (LookupManager.getActiveLookup(consoleEditor) != null) return false;
+
+ if (next) {
+ return document.getLineNumber(caretModel.getOffset()) == 0;
+ }
+ else {
+ return document.getLineNumber(caretModel.getOffset()) == document.getLineCount() - 1 &&
+ StringUtil.isEmptyOrSpaces(document.getText().substring(caretModel.getOffset()));
+ }
+ }
+
+
+ private void loadHistory(final HierarchicalStreamReader in) {
+ if (!in.getNodeName().equals("console-history")) return;
+ final String id = in.getAttribute("id");
+ if (!myId.equals(id)) return;
+ final ArrayList<String> entries = new ArrayList<String>();
+ while (in.hasMoreChildren()) {
+ in.moveDown();
+ if ("history-entry".equals(in.getNodeName())) {
+ entries.add(in.getValue());
+ }
+ in.moveUp();
+ }
+ for (ListIterator<String> iterator = entries.listIterator(entries.size()); iterator.hasPrevious(); ) {
+ final String entry = iterator.previous();
+ myModel.addToHistory(entry);
+
+ }
+ }
+
+ private void saveHistory(final XmlSerializer out) throws IOException {
+ out.startDocument(null, null);
+ out.startTag(null, "console-history");
+ out.attribute(null, "id", myId);
+ for (String s : myModel.getHistory()) {
+ out.startTag(null, "history-entry");
+ out.text(s);
+ out.endTag(null, "history-entry");
+ }
+ out.endTag(null, "console-history");
+ out.endDocument();
+ }
+}
import com.intellij.openapi.actionSystem.*;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
-import com.intellij.openapi.application.Result;
-import com.intellij.openapi.command.WriteCommandAction;
-import com.intellij.openapi.editor.CaretModel;
-import com.intellij.openapi.editor.Document;
-import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.ex.EditorEx;
import com.intellij.openapi.project.DumbAwareAction;
import com.intellij.openapi.project.Project;
-import com.intellij.openapi.util.Computable;
import com.intellij.openapi.util.IconLoader;
-import com.intellij.openapi.util.text.StringUtil;
import com.intellij.openapi.wm.IdeFocusManager;
import com.intellij.openapi.wm.ToolWindow;
import com.intellij.openapi.wm.ToolWindowManager;
import com.intellij.ui.SideBorder;
-import com.intellij.util.IJSwingUtilities;
import com.intellij.util.NotNullFunction;
-import com.intellij.util.PairProcessor;
import com.intellij.util.ui.UIUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.awt.*;
-import java.util.ArrayList;
import java.util.List;
/**
private LanguageConsoleViewImpl myConsoleView;
- private AnAction myRunAction;
-
private ConsoleExecuteActionHandler myConsoleExecuteActionHandler;
public AbstractConsoleRunnerWithHistory(@NotNull final Project project,
panel.add(actionToolbar.getComponent(), BorderLayout.WEST);
panel.add(myConsoleView.getComponent(), BorderLayout.CENTER);
+ actionToolbar.setTargetComponent(panel);
+
final RunContentDescriptor contentDescriptor =
new RunContentDescriptor(myConsoleView, myProcessHandler, panel, constructConsoleTitle(myConsoleTitle));
}
protected void finishConsole() {
- myRunAction.getTemplatePresentation().setEnabled(false);
myConsoleView.getConsole().setEditable(false);
- ApplicationManager.getApplication().invokeLater(new Runnable() {
- public void run() {
- myConsoleView.getConsole().getConsoleEditor().getComponent().updateUI();
- }
- });
}
protected abstract LanguageConsoleViewImpl createConsoleView();
final AnAction closeAction = createCloseAction(defaultExecutor, contentDescriptor);
actionList.add(closeAction);
-// run and history actions
-
- ConsoleExecutionActions executionActions = createConsoleExecutionActions();
- actionList.addAll(executionActions.getActionsAsList());
+// run action
+ actionList.add(createConsoleExecAction(getLanguageConsole(), myProcessHandler, myConsoleExecuteActionHandler));
// Help
actionList.add(CommonActionsManager.getInstance().createHelpAction("interactive_console"));
return actions;
}
- protected ConsoleExecutionActions createConsoleExecutionActions() {
- ConsoleExecutionActions executionActions =
- createConsoleActions(getLanguageConsole(), myProcessHandler, myConsoleExecuteActionHandler);
- myRunAction = executionActions.getRunAction();
- return executionActions;
- }
-
protected AnAction createCloseAction(final Executor defaultExecutor, final RunContentDescriptor myDescriptor) {
return new CloseAction(defaultExecutor, myDescriptor, myProject);
}
return myConsoleView.getConsole();
}
- public static ConsoleExecutionActions createConsoleActions(final LanguageConsoleImpl languageConsole,
- final ProcessHandler processHandler,
- final ConsoleExecuteActionHandler consoleExecuteActionHandler) {
- final AnAction runAction = new ConsoleExecuteAction(languageConsole,
- processHandler, consoleExecuteActionHandler);
-
- final PairProcessor<AnActionEvent, String> historyProcessor = new PairProcessor<AnActionEvent, String>() {
- public boolean process(final AnActionEvent e, final String s) {
- new WriteCommandAction(languageConsole.getProject(), languageConsole.getFile()) {
- protected void run(final Result result) throws Throwable {
- String text = s == null ? "" : s;
- languageConsole.getEditorDocument().setText(text);
- languageConsole.getConsoleEditor().getCaretModel().moveToOffset(text.length());
- }
- }.execute();
- return true;
- }
- };
-
- final EditorEx consoleEditor = languageConsole.getConsoleEditor();
- final AnAction upAction = ConsoleHistoryModel.createConsoleHistoryUpAction(createCanMoveUpComputable(consoleEditor),
- consoleExecuteActionHandler.getConsoleHistoryModel(),
- historyProcessor);
- final AnAction downAction = ConsoleHistoryModel.createConsoleHistoryDownAction(createCanMoveDownComputable(consoleEditor),
- consoleExecuteActionHandler.getConsoleHistoryModel(),
- historyProcessor);
-
- return new ConsoleExecutionActions(runAction, downAction, upAction);
- }
-
- public static Computable<Boolean> createCanMoveDownComputable(final Editor consoleEditor) {
- return new Computable<Boolean>() {
- @Override
- public Boolean compute() {
- final Document document = consoleEditor.getDocument();
- final CaretModel caretModel = consoleEditor.getCaretModel();
- // Check if we have focus
- if (!IJSwingUtilities.hasFocus(consoleEditor.getComponent())) {
- return true;
- }
- // Check if we have active lookup or if we can move in editor
- return LookupManager.getActiveLookup(consoleEditor) != null ||
- document.getLineNumber(caretModel.getOffset()) < document.getLineCount() - 1 &&
- !StringUtil.isEmptyOrSpaces(document.getText().substring(caretModel.getOffset()));
- }
- };
- }
-
- public static Computable<Boolean> createCanMoveUpComputable(final Editor consoleEditor) {
- return new Computable<Boolean>() {
- @Override
- public Boolean compute() {
- final Document document = consoleEditor.getDocument();
- final CaretModel caretModel = consoleEditor.getCaretModel();
- // Check if we have focus
- if (!IJSwingUtilities.hasFocus(consoleEditor.getComponent())) {
- return true;
- }
- // Check if we have active lookup or if we can move in editor
- return LookupManager.getActiveLookup(consoleEditor) != null || document.getLineNumber(caretModel.getOffset()) > 0;
- }
- };
+ public static AnAction createConsoleExecAction(final LanguageConsoleImpl languageConsole,
+ final ProcessHandler processHandler,
+ final ConsoleExecuteActionHandler consoleExecuteActionHandler) {
+ return new ConsoleExecuteAction(languageConsole, processHandler, consoleExecuteActionHandler);
}
@NotNull
protected abstract ConsoleExecuteActionHandler createConsoleExecuteActionHandler();
- public static class ConsoleExecutionActions {
- private final AnAction myRunAction;
- private final AnAction myNextAction;
- private final AnAction myPrevAction;
- private final List<AnAction> myAdditionalActions = Lists.newArrayList();
-
- public ConsoleExecutionActions(AnAction runAction, AnAction nextAction, AnAction prevAction) {
- myRunAction = runAction;
- myNextAction = nextAction;
- myPrevAction = prevAction;
- }
-
- public AnAction[] getActions() {
- return getActionsAsList().toArray(new AnAction[getActionsAsList().size()]);
- }
-
- public List<AnAction> getActionsAsList() {
- ArrayList<AnAction> list = Lists.newArrayList(myRunAction, myNextAction, myPrevAction);
- list.addAll(myAdditionalActions);
- return list;
- }
-
- public boolean addAdditionalAction(AnAction action) {
- return myAdditionalActions.add(action);
- }
-
- public AnAction getRunAction() {
- return myRunAction;
- }
- }
-
public static class ConsoleExecuteAction extends DumbAwareAction {
public static final String ACTIONS_EXECUTE_ICON = "/actions/execute.png";
public void update(final AnActionEvent e) {
final EditorEx editor = myLanguageConsole.getConsoleEditor();
final Lookup lookup = LookupManager.getActiveLookup(editor);
- e.getPresentation().setEnabled(!myProcessHandler.isProcessTerminated() &&
+ e.getPresentation().setEnabled(!editor.isRendererMode() && !myProcessHandler.isProcessTerminated() &&
(lookup == null || !(lookup.isCompletion() && lookup.isFocused())));
}
}
package com.intellij.execution.process;
-import com.intellij.openapi.actionSystem.AnAction;
-import com.intellij.openapi.actionSystem.AnActionEvent;
-import com.intellij.openapi.actionSystem.EmptyAction;
-import com.intellij.openapi.util.Computable;
-import com.intellij.util.PairProcessor;
+import com.intellij.openapi.util.ModificationTracker;
import org.jetbrains.annotations.Nullable;
-import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* @author Gregory.Shrago
*/
-public class ConsoleHistoryModel {
+public class ConsoleHistoryModel implements ModificationTracker {
public static final int DEFAULT_MAX_SIZE = 20;
private int myHistoryCursor;
private int myMaxHistorySize = DEFAULT_MAX_SIZE;
private final LinkedList<String> myHistory = new LinkedList<String>();
+ private volatile long myModificationTracker;
public void addToHistory(final String statement) {
synchronized (myHistory) {
+ myModificationTracker ++;
myHistoryCursor = -1;
myHistory.remove(statement);
if (myHistory.size() >= myMaxHistorySize) {
}
}
- public static AnAction createConsoleHistoryUpAction(final Computable<Boolean> canMoveUpInEditor,
- final ConsoleHistoryModel model,
- final PairProcessor<AnActionEvent, String> processor) {
- final AnAction upAction = new AnAction() {
- @Override
- public void actionPerformed(final AnActionEvent e) {
- processor.process(e, model.getHistoryNext());
- }
-
- @Override
- public void update(final AnActionEvent e) {
- // Check if we have anything in history
- final boolean hasHistory = model.hasHistory(true);
- if (!hasHistory){
- e.getPresentation().setEnabled(false);
- return;
- }
- e.getPresentation().setEnabled(!canMoveUpInEditor.compute());
- }
- };
- upAction.registerCustomShortcutSet(KeyEvent.VK_UP, 0, null);
- upAction.getTemplatePresentation().setVisible(false);
- return upAction;
- }
-
- public static AnAction createConsoleHistoryDownAction(final Computable<Boolean> canMoveDownInEditor,
- final ConsoleHistoryModel model,
- final PairProcessor<AnActionEvent, String> processor) {
- final AnAction downAction = new AnAction() {
- @Override
- public void actionPerformed(final AnActionEvent e) {
- processor.process(e, model.getHistoryPrev());
- }
-
- @Override
- public void update(final AnActionEvent e) {
- // Check if we have anything in history
- final boolean hasHistory = model.hasHistory(false);
- if (!hasHistory){
- e.getPresentation().setEnabled(false);
- return;
- }
- e.getPresentation().setEnabled(!canMoveDownInEditor.compute());
- }
- };
-
- downAction.registerCustomShortcutSet(KeyEvent.VK_DOWN, 0, null);
- downAction.getTemplatePresentation().setVisible(false);
- return downAction;
- }
-
- public static AnAction createHistoryAction(final ConsoleHistoryModel model, final boolean next, final PairProcessor<AnActionEvent,String> processor) {
- final AnAction action = new AnAction(null, null, null) {
- @Override
- public void actionPerformed(final AnActionEvent e) {
- processor.process(e, next ? model.getHistoryNext() : model.getHistoryPrev());
- }
-
- @Override
- public void update(final AnActionEvent e) {
- e.getPresentation().setEnabled(model.hasHistory(next));
- }
- };
- EmptyAction.setupAction(action, next? "Console.History.Next" : "Console.History.Previous", null);
- return action;
+ @Override
+ public long getModificationCount() {
+ return myModificationTracker;
}
}