Skip to content

Instantly share code, notes, and snippets.

@Roxbili
Last active January 16, 2024 00:45
Show Gist options
  • Save Roxbili/27ef0eef0a21b5795a1704b766f0a7e0 to your computer and use it in GitHub Desktop.
Save Roxbili/27ef0eef0a21b5795a1704b766f0a7e0 to your computer and use it in GitHub Desktop.
Vim-mode in overleaf
// ==UserScript==
// @name Overleaf Editor VIM Keybindings settings
// @description Vim-mode in overleaf
// @namespace http://tampermonkey.net/
// @version 0.1.1
// @match https://www.overleaf.com/project/*
// @match https://cn.overleaf.com/project/*
// @grant none
// Source 1: https://groups.google.com/d/msg/ace-discuss/gwXMzUM17I4/9B_acHBSCQAJ
// Source 2: https://www.overleaf.com/learn/how-to/How_can_I_define_custom_Vim_macros_in_a_vimrc_file_on_Overleaf%3F
// ==/UserScript==
// 下面这段话为了在该js中使用jquery
/* globals jQuery, $, waitForKeyElements */
(function() {
'use strict';
// poll until editor is loaded
setTimeout(function(){
var card;
var editor = window._debug_editors[window._debug_editors.length -1]
$(document).keyup(function(event){
switch(event.keyCode) {
case 27:
esc()
case 96:
esc()
}
});
function esc(){
card = $('.writefull-suggest-card');
if(card.css("display")=='flex'){
card.css("display",'none')
// Restore the focus back to ACE editor
// Ref: https://groups.google.com/g/ace-discuss/c/1_AXADucTEA/m/cc2KZQMtDgAJ?utm_medium=email&utm_source=footer
editor.focus() // side-effect - a side box is drawn
}
};
var DA = {48:-1,49:0,50:1,51:2};
$(document).keypress(function(e){
if(e.which>47 && e.which < 52){
card = $('.writefull-suggest-card');
if(card.css("display")=='flex'){
console.log(DA[e.which])
card.find('li.replacement').eq(DA[e.which])[0].click();
// Ref: https://groups.google.com/g/ace-discuss/c/1_AXADucTEA/m/cc2KZQMtDgAJ?utm_medium=email&utm_source=footer
editor.focus() // side-effect - a side box is drawn
}
}
});
},2000);
// End of chunk
// The following chunk is the main Vim emulator piece, defining a bunch of functions + shortcuts + remapping of keys.
const retry = setInterval(() => {
if (window._debug_editors === undefined) return
clearInterval(retry)
// get current editor instance
// get current editor instance
var editor = window._debug_editors[window._debug_editors.length -1]
// Option for a vertical split. Source: https://stackoverflow.com/a/24417327/3925312
// editor.setOption("showPrintMargin", true)
// relative line number
editor.setOption("relativeLineNumbers", true)
// session wrap at 80: thanks to help from Harutyun Amirjanyan,
// Source: https://groups.google.com/g/ace-discuss/c/gjLwB6Bmj0c/m/cdkIvkXuAgAJ
//editor.setOption("wrap", 80)
// Enforcing soft-wrap while allowing for auto-completion (not killing it).
ace.config.on("session", function(session) {
// session.setOption("wrap", 80) // This may break auto-completion.
session.setWrapLimitRange(95, 95) //Caveat: no more wrap to the pane of size smaler than 80 col.
})
// Unbind Ctrl+L ==> for Overleaf, this is to go to certain line/location. Not necessary with Vim bindings
editor.commands.bindKeys({"ctrl-l":null})
// vim keyboard plugin
var vimKeyboard = window.ace.require("ace/keyboard/vim")
////////////////////////////////////////////////////////////
// 2021-10-20 - https://groups.google.com/g/ace-discuss/c/2hwMMzBbYH8/m/Q0l9vJAqBQAJ?utm_medium=email&utm_source=footer
// g0, g$, and g^ as a bonus
vimKeyboard.handler.defaultKeymap.push({
keys: "L", type: "motion", motion: "moveToLineEnd"
}, {
keys: "H", type: "motion", motion: "moveToLineStart"
})
vimKeyboard.Vim.defineMotion("moveToLineEnd", function(cm) {
cm.ace.selection.moveCursorLineEnd()
cm.ace.selection.clearSelection()
return cm.getCursor()
})
vimKeyboard.Vim.defineMotion("moveToLineStart", function(cm) {
cm.ace.selection.moveCursorLineStart()
cm.ace.selection.clearSelection()
return cm.getCursor()
})
// j k within long lines
// vimKeyboard.Vim.map("j", "gj", "normal")
// vimKeyboard.Vim.map("k", "gk", "normal")
// Override $ and ^
vimKeyboard.Vim.map("H", "^", "normal")
vimKeyboard.Vim.map("L", "$", "normal")
vimKeyboard.Vim.map("H", "^", "visual")
vimKeyboard.Vim.map("L", "$", "visual")
// add custom keybindings - insert mode applies on insert
vimKeyboard.Vim.map("jj", "<Esc>", "insert")
// Equilivantly, from https://groups.google.com/d/msg/ace-discuss/gwXMzUM17I4/9B_acHBSCQAJ
// window.ace.require("ace/keyboard/vim").Vim.map('jj', '<Esc>', 'insert')
// vimKeyboard.Vim.map("jk", "<Esc>", "insert")
////////////////////////////////////////////////////////////
// Remapping one key to another is doable, in Normal mode
// `nmap h j` can be defined as the following:
// Though, why should this mapping be here anyways? It is just meant for demo purpose.
// vimKeyboard.Vim.map("h", "j", "normal")
// Define local commands ==> matching elements along with a identifier that matches.
// Maintenance note: if one hotkey/cmd does not work, check here for the matching HTML element.
editor.commands.addCommands(
[
{ // Note, this is not working as the element will change to "Split screen"
// At least, we need an "or-condition" for the querySelector.
name: "Projects",
exec: function() {
document.querySelector('a[class*="toolbar-header-back-projects"]').click()
}
},
{ // Note, this is not working as the element will change to "Split screen"
// At least, we need an "or-condition" for the querySelector.
name: "FullScreen",
exec: function() {
document.querySelector('a[tooltip*="Full screen"]').click()
}
},
{
name: "ToggleHistory",
exec: function() {
document.querySelector('i[class="fa fa-fw fa-history"]').click()
}
},
{
name: "ToggleLog",
exec: function() {
document.querySelector('a[tooltip*="Logs and output files"]').click()
}
},
{
name: "jumpToPdf",
exec: function() {
document.querySelector(".synctex-control-goto-pdf").click()
}
},
{
name: "VimtexTocToggle",
exec: function() {
document.querySelector('a[tooltip*="the file-tree"]').click()
}
}, {
name: "CloseComment",
exec: function() {
document.querySelector('a[class*="review-panel-toggler"]').click()
}
}, {
name: "TogglePDF",
exec: function() {
document.querySelector('a[tooltip*="the PDF"]').click()
}
}, {
name: "OmegaPress",
exec: function() {
// For the Toggle Symbol Palette, press it twice, to remove the gray-box should the Overleaf project is page is restored.
document.querySelector('button[tooltip*="Toggle Symbol Palette"]').click()
document.querySelector('button[tooltip*="Toggle Symbol Palette"]').click()
}
}, {
name: "FindTypoNext",
exec: function() {
document.querySelector('mwc-icon-button[id="writefull-next"]').click()
}
}, {
name: "FindTypoPrevious",
exec: function() {
document.querySelector('mwc-icon-button[id="writefull-previous"]').click()
}
}, {
name: "TestFocus",
exec: function() {
editor.focus()
}
}])
// add keybindings for jump to pdf
vimKeyboard.Vim.mapCommand("\\v", "action", "aceCommand",
{ name: "VimtexTocToggle" }, { context: "normal" });
vimKeyboard.Vim.mapCommand("\\lv", "action", "aceCommand",
{ name: "jumpToPdf" }, { context: "normal" });
vimKeyboard.Vim.mapCommand("\\o", "action", "aceCommand",
{ name: "TogglePDF" }, { context: "normal" });
// add keybindgs for jump to typos
// Note, [s and ]s has unknown complications. Just avoid using it.
vimKeyboard.Vim.mapCommand("\]", "action", "aceCommand",
{ name: "FindTypoNext" }, { context: "normal" });
//vimKeyboard.Vim.mapCommand("N", "action", "aceCommand",
//{ name: "FindTypoNext" }, { context: "normal" });
vimKeyboard.Vim.mapCommand("\[", "action", "aceCommand",
{ name: "FindTypoPrevious" }, { context: "normal" });
//vimKeyboard.Vim.mapCommand("P", "action", "aceCommand",
//{ name: "FindTypoPrevious" }, { context: "normal" });
// ESC to regain focus
vimKeyboard.Vim.mapCommand("<ESC>", "action", "aceCommand",
{ name: "TestFocus" }, { context: "normal" });
// bind to ;lv
vimKeyboard.Vim.unmap(";")
vimKeyboard.Vim.unmap(",")
vimKeyboard.Vim.unmap("\[\[")
vimKeyboard.Vim.unmap("\]\]")
vimKeyboard.Vim.map(";lv", "\\lv", "normal")
vimKeyboard.Vim.map(",v", "\\v", "normal")
// Use ,o to activate two hotkeys: hide file-menu and hide PDF preview
vimKeyboard.Vim.map(",o", "\\v\\o", "normal")
//vimKeyboard.Vim.mapCommand(",o", "action", "aceCommand",
// { name: "FullScreen" }, { context: "normal" });
//Use cmd statment to change theme: :light vs :dark
//Ref: https://github.com/Seldaek/php-console/issues/36
vimKeyboard.Vim.defineEx("light", "", ()=>editor.setTheme("ace/theme/dreamweaver"))
vimKeyboard.Vim.defineEx("dark", "", ()=>editor.setTheme("ace/theme/gruvbox"))
//vimKeyboard.Vim.defineEx("full", "", ()=>editor.commands.exec("FullScreen"))
vimKeyboard.Vim.defineEx("home", "", ()=>editor.commands.exec("Projects"))
vimKeyboard.Vim.defineEx("back", "", ()=>editor.commands.exec("Projects"))
vimKeyboard.Vim.defineEx("history", "", ()=>editor.commands.exec("ToggleHistory"))
vimKeyboard.Vim.defineEx("log", "", ()=>editor.commands.exec("ToggleLog"))
vimKeyboard.Vim.defineEx("ShowPDF", "", ()=>editor.commands.exec("TogglePDF"))
vimKeyboard.Vim.defineEx("ClosePDF", "", ()=>editor.commands.exec("TogglePDF"))
vimKeyboard.Vim.defineEx("OpenPDF", "", ()=>editor.commands.exec("TogglePDF"))
vimKeyboard.Vim.defineEx("PDF", "", ()=>editor.commands.exec("TogglePDF"))
vimKeyboard.Vim.defineEx("pdf", "", ()=>editor.commands.exec("TogglePDF"))
// Close comment: CC, cc, CloseComment
vimKeyboard.Vim.defineEx("CloseComment", "", ()=>editor.commands.exec("CloseComment"))
vimKeyboard.Vim.defineEx("CC", "", ()=>editor.commands.exec("CloseComment"))
vimKeyboard.Vim.defineEx("cc", "", ()=>editor.commands.exec("CloseComment"))
// Toggl the omega icon, for symbol palette
vimKeyboard.Vim.defineEx("o", "", ()=>editor.commands.exec("OmegaPress"))
// Test editor-focus
vimKeyboard.Vim.defineEx("focus", "", ()=>editor.commands.exec("TestFocus"))
// Test typo-find
vimKeyboard.Vim.defineEx("tnext", "", ()=>editor.commands.exec("FindTypoNext"))
// set the modified keyboard handler for editor
editor.setKeyboardHandler(vimKeyboard.handler)
console.log("Custom key bindings applied")
}, 100)
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment