Permalink
Browse files

Merge branch 'feature/vim-keybindings'

  • Loading branch information...
2 parents 2fb6402 + 643bf7b commit 31385f987de16d6d9a69e9433806ffb2ee68464a @jjallaire jjallaire committed Aug 21, 2012
View
@@ -31,6 +31,7 @@ We've also added support for building packages with [Rcpp](http://dirk.eddelbuet
### Source Editor
+- Vim editing mode
- New options to show whitespace and indent guides
### Miscellaneous
@@ -110,14 +110,30 @@ oop.inherits(RStudioEditSession, EditSession);
this.replace(new Range(lineNum, 0, lineNum, matchLen), indent);
};
- this.$disableOverwrite = false;
this.setDisableOverwrite = function(disableOverwrite) {
- this.$disableOverwrite = disableOverwrite;
- this.setOverwrite(this.$overwrite);
- };
- this.setOverwrite = function(overwrite) {
- EditSession.prototype.setOverwrite(overwrite && !this.$disableOverwrite);
+ // Note that 'this' refers to the instance, not the prototype. It's
+ // important that we override set/getOverwrite on a per-instance basis
+ // only.
+
+ if (disableOverwrite) {
+ // jcheng 08/21/2012: The old way we did this (see git history) caused
+ // a weird bug: the console would pick up the overwrite/insert mode of
+ // the active source document iff vim mode was enabled. I could not
+ // figure out why.
+
+ // In case we are already in overwrite mode; set it to false so events
+ // will be fired.
+ this.setOverwrite(false);
+
+ this.setOverwrite = function() { /* no-op */ };
+ this.getOverwrite = function() { return false; }
+ }
+ else {
+ // Restore the standard methods
+ this.setOverwrite = EditSession.prototype.setOverwrite;
+ this.getOverwrite = EditSession.prototype.getOverwrite;
+ }
};
}).call(RStudioEditSession.prototype);
@@ -14,7 +14,7 @@
}
.lessSpaced {
- margin-bottom: 10px;
+ margin-bottom: 7px;
}
.extraSpaced {
@@ -105,6 +105,10 @@ public void onUiPrefsChanged(UiPrefsChangedEvent e)
showIndentGuides().setGlobalValue(
newUiPrefs.showIndentGuides().getGlobalValue());
+ // use vim mode
+ useVimMode().setGlobalValue(
+ newUiPrefs.useVimMode().getGlobalValue());
+
// insert matching
insertMatching().setGlobalValue(
newUiPrefs.insertMatching().getGlobalValue());
@@ -82,6 +82,11 @@ public UIPrefsAccessor(JsObject uiPrefs, JsObject projectUiPrefs)
return bool("show_indent_guides", false);
}
+ public PrefValue<Boolean> useVimMode()
+ {
+ return bool("use_vim_mode", false);
+ }
+
public PrefValue<Boolean> insertMatching()
{
return bool("insert_matching", true);
@@ -49,6 +49,7 @@ public EditingPreferencesPane(SourceServerOperations server,
add(indent(marginCol_ = numericPref("Margin column", prefs.printMarginColumn())));
add(checkboxPref("Show whitespace characters", prefs_.showInvisibles()));
add(checkboxPref("Show indent guides", prefs_.showIndentGuides()));
+ add(checkboxPref("Enable vim editing mode", prefs_.useVimMode()));
add(checkboxPref("Insert matching parens/quotes", prefs_.insertMatching()));
add(checkboxPref("Soft-wrap R source files", prefs_.softWrapRFiles()));
add(checkboxPref("Show syntax highlighting in console input", prefs_.syntaxColorConsole()));
@@ -227,6 +227,9 @@ public void onFoldChange(FoldChangeEvent event)
@Override
public void onKeyDown(KeyDownEvent event)
{
+ if (useVimMode_)
+ return;
+
int mod = KeyboardShortcut.getModifierValue(event.getNativeEvent());
if (mod == KeyboardShortcut.CTRL)
{
@@ -390,14 +393,32 @@ private void updateLanguage(CompletionManager completionManager)
completionManager_ = completionManager;
- widget_.getEditor().setKeyboardHandler(
- new AceCompletionAdapter(completionManager_).getKeyboardHandler());
-
+ updateKeyboardHandlers();
+
getSession().setEditorMode(
fileType_.getEditorLanguage().getParserName(),
Desktop.isDesktop() && Desktop.getFrame().suppressSyntaxHighlighting());
getSession().setUseWrapMode(fileType_.getWordWrap());
+ }
+
+ private void updateKeyboardHandlers()
+ {
+ // create a keyboard previewer for our special hooks
+ AceKeyboardPreviewer previewer = new AceKeyboardPreviewer(
+ completionManager_);
+ // reset keyboard handlers
+ widget_.getEditor().setKeyboardHandler(null);
+
+ // if required add vim handlers (to main editor and our previewer)
+ if (useVimMode_)
+ {
+ widget_.getEditor().addKeyboardHandler(KeyboardHandler.vim());
+ previewer.addHandler(new AceVimCommandHandler());
+ }
+
+ // add the previewer's handler
+ widget_.getEditor().addKeyboardHandler(previewer.getKeyboardHandler());
}
public String getCode()
@@ -1087,6 +1108,13 @@ public void setShowPrintMargin(boolean on)
{
widget_.getEditor().getRenderer().setShowPrintMargin(on);
}
+
+ @Override
+ public void setUseVimMode(boolean use)
+ {
+ useVimMode_ = use;
+ updateKeyboardHandlers();
+ }
public void setPadding(int padding)
{
@@ -1494,6 +1522,7 @@ public void setDisableOverwrite(boolean disableOverwrite)
private CodeToolsServerOperations server_;
private TextFileType fileType_;
private boolean passwordMode_;
+ private boolean useVimMode_ = false;
private RnwCompletionContext rnwContext_;
private static final ExternalJavaScriptLoader aceLoader_ =
@@ -1,5 +1,5 @@
/*
- * AceCompletionAdapter.java
+ * AceKeyboardPreviewer.java
*
* Copyright (C) 2009-12 by RStudio, Inc.
*
@@ -12,33 +12,62 @@
*/
package org.rstudio.studio.client.workbench.views.source.editors.text;
+import java.util.ArrayList;
+
+import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.NativeEvent;
+
import org.rstudio.studio.client.workbench.views.console.shell.assist.CompletionManager;
import org.rstudio.studio.client.workbench.views.source.editors.text.ace.KeyboardHandler;
-public class AceCompletionAdapter
+public class AceKeyboardPreviewer
{
- public AceCompletionAdapter(CompletionManager completionManager)
+ public interface Handler
{
- completionManager_ = completionManager;
+ boolean previewKeyDown(JavaScriptObject data, NativeEvent event) ;
+ boolean previewKeyPress(JavaScriptObject data, char charCode) ;
}
+
+ public AceKeyboardPreviewer(final CompletionManager completionManager)
+ {
+ addHandler(new Handler() {
+ @Override
+ public boolean previewKeyDown(JavaScriptObject data, NativeEvent event)
+ {
+ return completionManager.previewKeyDown(event);
+ }
+
+ @Override
+ public boolean previewKeyPress(JavaScriptObject data, char charCode)
+ {
+ return completionManager.previewKeyPress(charCode);
+ }
+ });
+ }
+
+ public void addHandler(Handler handler)
+ {
+ handlers_.add(handler);
+ }
+
+
public native final KeyboardHandler getKeyboardHandler() /*-{
var event = $wnd.require("ace/lib/event");
var self = this;
var noop = {command: "null"};
return {
handleKeyboard: $entry(function(data, hashId, keyOrText, keyCode, e) {
if (hashId != -1 || keyCode) {
- if (self.@org.rstudio.studio.client.workbench.views.source.editors.text.AceCompletionAdapter::onKeyDown(Lcom/google/gwt/dom/client/NativeEvent;)(e)) {
+ if (self.@org.rstudio.studio.client.workbench.views.source.editors.text.AceKeyboardPreviewer::onKeyDown(Lcom/google/gwt/core/client/JavaScriptObject;Lcom/google/gwt/dom/client/NativeEvent;)(data, e)) {
event.stopEvent(e);
return noop; // perform a no-op
}
else
return false; // allow default behavior
}
else {
- if (self.@org.rstudio.studio.client.workbench.views.source.editors.text.AceCompletionAdapter::onTextInput(Ljava/lang/String;)(keyOrText))
+ if (self.@org.rstudio.studio.client.workbench.views.source.editors.text.AceKeyboardPreviewer::onTextInput(Lcom/google/gwt/core/client/JavaScriptObject;Ljava/lang/String;)(data, keyOrText))
return noop;
else
return false;
@@ -47,12 +76,18 @@ public native final KeyboardHandler getKeyboardHandler() /*-{
};
}-*/;
- private boolean onKeyDown(NativeEvent e)
+ private boolean onKeyDown(JavaScriptObject data, NativeEvent e)
{
- return completionManager_.previewKeyDown(e);
+ for (Handler handler : handlers_)
+ {
+ if (handler.previewKeyDown(data, e))
+ return true;
+ }
+ return false;
}
+
- private boolean onTextInput(String text)
+ private boolean onTextInput(JavaScriptObject data, String text)
{
if (text == null)
return false;
@@ -61,11 +96,15 @@ private boolean onTextInput(String text)
if (text.equals("\u001B"))
return true;
- for (int i = 0; i < text.length(); i++)
- if (completionManager_.previewKeyPress(text.charAt(i)))
- return true;
+ for (Handler handler : handlers_)
+ {
+ for (int i = 0; i < text.length(); i++)
+ if (handler.previewKeyPress(data, text.charAt(i)))
+ return true;
+ }
+
return false;
}
-
- private CompletionManager completionManager_;
+
+ private ArrayList<Handler> handlers_ = new ArrayList<Handler>();
}
@@ -0,0 +1,74 @@
+/*
+ * AceVimCommandHandler.java
+ *
+ * Copyright (C) 2009-12 by RStudio, Inc.
+ *
+ * This program is licensed to you under the terms of version 3 of the
+ * GNU Affero General Public License. This program is distributed WITHOUT
+ * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
+ * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
+ * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
+ *
+ */
+
+package org.rstudio.studio.client.workbench.views.source.editors.text;
+
+import org.rstudio.core.client.command.KeyboardShortcut;
+import org.rstudio.studio.client.RStudioGinjector;
+
+import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.dom.client.NativeEvent;
+
+public class AceVimCommandHandler implements AceKeyboardPreviewer.Handler
+{
+
+ @Override
+ public boolean previewKeyDown(JavaScriptObject data, NativeEvent event)
+ {
+ if ((event.getKeyCode() == 191 /* '/' key */) &&
+ (KeyboardShortcut.getModifierValue(event) == KeyboardShortcut.NONE) &&
+ isNormalMode(data))
+ {
+ executeFind();
+ return true;
+ }
+
+ return false;
+ }
+
+ @Override
+ public boolean previewKeyPress(JavaScriptObject data, char charCode)
+ {
+ if (charCode == '/' && isNormalMode(data))
+ {
+ executeFind();
+ return true;
+ }
+
+ return false;
+ }
+
+ private boolean isNormalMode(JavaScriptObject data)
+ {
+ VimHandlerData vimData = data.cast();
+ return "start".equals(vimData.getState());
+ }
+
+ private void executeFind()
+ {
+ RStudioGinjector.INSTANCE.getCommands().findReplace().execute();
+ }
+
+ private static final class VimHandlerData extends JavaScriptObject
+ {
+ protected VimHandlerData()
+ {
+ }
+
+ public native final String getState() /*-{
+ return this.state;
+ }-*/;
+
+ }
+
+}
@@ -103,6 +103,7 @@
void setPrintMarginColumn(int column);
void setShowInvisibles(boolean show);
void setShowIndentGuides(boolean show);
+ void setUseVimMode(boolean use);
JsArray<AceFold> getFolds();
void addFold(Range range);
@@ -2608,6 +2608,11 @@ public void execute(Boolean arg) {
public void execute(Boolean arg) {
docDisplay.setShowIndentGuides(arg);
}}));
+ releaseOnDismiss.add(prefs.useVimMode().bind(
+ new CommandWithArg<Boolean>() {
+ public void execute(Boolean arg) {
+ docDisplay.setUseVimMode(arg);
+ }}));
}
public static void syncFontSize(
@@ -76,6 +76,11 @@ public native final void blur() /*-{
public native final void setKeyboardHandler(KeyboardHandler keyboardHandler) /*-{
this.setKeyboardHandler(keyboardHandler);
}-*/;
+
+ public native final void addKeyboardHandler(KeyboardHandler keyboardHandler) /*-{
+ this.keyBinding.addKeyboardHandler(keyboardHandler);
+ }-*/;
+
public native final void onChange(Command command) /*-{
this.getSession().on("change",
@@ -17,4 +17,9 @@
public class KeyboardHandler extends JavaScriptObject
{
protected KeyboardHandler() {}
+
+ public static native KeyboardHandler vim() /*-{
+ var vim = $wnd.require('ace/keyboard/vim').handler;
+ return vim;
+ }-*/;
}
Oops, something went wrong.

0 comments on commit 31385f9

Please sign in to comment.