alm
Version:
The best IDE for TypeScript
589 lines (546 loc) • 17.7 kB
text/typescript
/**
* Defines:
* commands / command registry / code editor commands
*/
// Keyboard shortcut origins:
// c9: cloud9 IDE
// ca: CodeAnywhere
// atom: github atom
// sublime: sublime text
// code: VScode
//---------------------------------------------
//
// This file also sets up Monaco keybindings
import * as Mousetrap from "mousetrap";
require("mousetrap/plugins/global-bind/mousetrap-global-bind");
import * as events from "../../common/events";
import * as utils from "../../common/utils";
import * as types from "../../common/types";
export enum CommandContext {
Global,
Editor,
TreeView,
}
interface UICommandConfig {
keyboardShortcut?: string;
description: string;
context: CommandContext;
// only valid for editor commands
// we use this to trigger the command on the editor
editorCommandName?: string;
/** allow the browser default to still happen */
allowDefault?: boolean;
}
/**
* The command registry composed of commands that are keyboard only
*/
export let commandRegistry: UICommand[] = [];
/**
* A command is just an event emitter with some useful properties relevant to the front end command registry
* such commands cannot have a payload
*/
export class UICommand extends events.TypedEvent<{}>{
constructor(public config: UICommandConfig) {
super();
commandRegistry.push(this);
}
}
/**
* BAS
*/
// export const bas = new UICommand({
// description: "BAS: I map this to whatever command I am currently testing",
// context: CommandContext.Global,
// });
/**
* General purpose UI escape
*/
export const esc = new UICommand({
keyboardShortcut: 'esc', // atom
description: "Close any open dialogs and focus back to any open tab",
context: CommandContext.Global,
});
/**
* Active list
*/
export const gotoNext = new UICommand({
keyboardShortcut: 'mod+f8', // atom
description: "Main Panel : Goto next error in project",
context: CommandContext.Global,
});
export const gotoPrevious = new UICommand({
keyboardShortcut: 'mod+shift+f8', // atom
description: "Main Panel : Goto previous error in project",
context: CommandContext.Global,
});
/**
* Tabs
*/
export const nextTab = new UICommand({
keyboardShortcut: 'alt+k',
description: "Tabs: Focus on the Next Tab",
context: CommandContext.Global,
});
export const prevTab = new UICommand({
keyboardShortcut: 'alt+j',
description: "Tabs: Focus on the Previous Tab",
context: CommandContext.Global,
});
export const closeTab = new UICommand({
keyboardShortcut: 'alt+w', // c9
description: "Tabs: Close current tab",
context: CommandContext.Global,
});
export const undoCloseTab = new UICommand({
keyboardShortcut: 'shift+alt+w', // Couldn't find IDEs that do this. c9/ca have this bound to close all tabs
description: "Tabs: Undo close tab",
context: CommandContext.Global,
});
export const saveTab = new UICommand({
keyboardShortcut: 'mod+s', // c9
description: "Tabs: Save current tab",
context: CommandContext.Global,
});
export const closeOtherTabs = new UICommand({
description: "Tabs: Close other tabs",
context: CommandContext.Global,
});
export const closeAllTabs = new UICommand({
description: "Tabs: Close all tabs",
context: CommandContext.Global,
});
export const jumpToTab = new UICommand({
keyboardShortcut: 'mod+shift+enter',
description: "Tabs: Jump to tab",
context: CommandContext.Global,
});
export const duplicateTab = new UICommand({
description: "Tabs: Duplicate",
context: CommandContext.Global,
});
export const duplicateWindow = new UICommand({
description: "Window: Duplicate in a new browser window",
context: CommandContext.Global,
});
/**
* Build / output js
*/
export const sync = new UICommand({
keyboardShortcut: 'shift+f6',
description: "TypeScript: Sync",
context: CommandContext.Global,
});
export const build = new UICommand({
keyboardShortcut: 'f6',
description: "TypeScript: Build",
context: CommandContext.Global,
});
export const toggleOutputJS = new UICommand({
keyboardShortcut: 'mod+shift+m', // atom
description: "TypeScript: Toggle output js file",
context: CommandContext.Global,
});
export const enableLiveDemo = new UICommand({
description: "TypeScript: Demo file",
context: CommandContext.Global,
});
export const disableLiveDemo = new UICommand({
description: "TypeScript: Demo stop",
context: CommandContext.Global,
});
export const enableLiveDemoReact = new UICommand({
description: "TypeScript: Demo react file",
context: CommandContext.Global,
});
export const disableLiveDemoReact = new UICommand({
description: "TypeScript: Demo react stop",
context: CommandContext.Global,
});
/**
* Tab indexing
* // c9, chrome, atom
*/
export const gotoTab1 = new UICommand({
keyboardShortcut: 'mod+1',
description: "Tabs: Goto Tab 1",
context: CommandContext.Global,
});
export const gotoTab2 = new UICommand({
keyboardShortcut: 'mod+2',
description: "Tabs: Goto Tab 2",
context: CommandContext.Global,
});
export const gotoTab3 = new UICommand({
keyboardShortcut: 'mod+3',
description: "Tabs: Goto Tab 3",
context: CommandContext.Global,
});
export const gotoTab4 = new UICommand({
keyboardShortcut: 'mod+4',
description: "Tabs: Goto Tab 4",
context: CommandContext.Global,
});
export const gotoTab5 = new UICommand({
keyboardShortcut: 'mod+5',
description: "Tabs: Goto Tab 5",
context: CommandContext.Global,
});
export const gotoTab6 = new UICommand({
keyboardShortcut: 'mod+6',
description: "Tabs: Goto Tab 6",
context: CommandContext.Global,
});
export const gotoTab7 = new UICommand({
keyboardShortcut: 'mod+7',
description: "Tabs: Goto Tab 7",
context: CommandContext.Global,
});
export const gotoTab8 = new UICommand({
keyboardShortcut: 'mod+8',
description: "Tabs: Goto Tab 8",
context: CommandContext.Global,
});
export const gotoTab9 = new UICommand({
keyboardShortcut: 'mod+9',
description: "Tabs: Goto Tab 9",
context: CommandContext.Global,
});
/**
* OmniSearch
*/
export const omniFindFile = new UICommand({
keyboardShortcut: 'mod+o', // atom,sublime
description: "Find a file in the working directory",
context: CommandContext.Global,
});
export const omniFindCommand = new UICommand({
keyboardShortcut: 'mod+shift+p', // atom,sublime
description: "Find a command",
context: CommandContext.Global,
});
export const omniSelectProject = new UICommand({
keyboardShortcut: 'alt+shift+p', // atom:projectmanager package
description: "Find and set active project",
context: CommandContext.Global,
});
export const omniProjectSymbols = new UICommand({
keyboardShortcut: 'mod+shift+h',
description: "Find Symbols (Hieroglyphs) in active project",
context: CommandContext.Global,
});
export const omniProjectSourcefile = new UICommand({
keyboardShortcut: 'mod+p', //
description: "Find Source File in active project",
context: CommandContext.Global,
});
/**
* FAR find and replace
*/
export const findAndReplace = new UICommand({
keyboardShortcut: 'mod+f', // atom,sublime,c9
description: "Show find and replace dialog",
context: CommandContext.Global,
});
export const findAndReplaceMulti = new UICommand({
keyboardShortcut: 'mod+shift+f', // atom,sublime,c9
description: "Show find and replace in files",
context: CommandContext.Global,
});
export const findNext = new UICommand({
keyboardShortcut: 'f3', // atom,sublime
description: "Find the next search result",
context: CommandContext.Global,
});
export const findPrevious = new UICommand({
keyboardShortcut: 'shift+f3', // atom,sublime
description: "Find the previous search result",
context: CommandContext.Global,
});
export const replaceNext = new events.TypedEvent<{ newText: string }>();
export const replacePrevious = new events.TypedEvent<{ newText: string }>();
export const replaceAll = new events.TypedEvent<{ newText: string }>();
/**
* Error panel
*/
export let toggleMessagePanel = new UICommand({
keyboardShortcut: 'mod+;', //
description: "Toggle Message Panel",
context: CommandContext.Global,
});
export let cycleMessagesPanel = new UICommand({
keyboardShortcut: 'mod+shift+;', //
description: "Cycle Message Panel",
context: CommandContext.Global,
});
/**
* Documentation features
*/
export let toggleDoctor = new UICommand({
keyboardShortcut: "mod+'", //
description: "Editor: Toggle Doctor",
context: CommandContext.Global,
});
export const toggleDocumentationBrowser = new UICommand({
keyboardShortcut: 'mod+shift+\'', // Same as doctor with Shift
description: "Documentation Browser: Open",
context: CommandContext.Global,
});
export const doOpenUmlDiagram = new UICommand({
description: "UML Class diagram",
context: CommandContext.Global,
});
export const toggleSemanticView = new UICommand({
description: "Toggle Semantic View",
context: CommandContext.Global,
});
export const launchTsFlow = new UICommand({
description: "Launch TypeScript flow based programming",
context: CommandContext.Global,
});
export const doOpenTestResultsView = new UICommand({
description: "Test Results View",
context: CommandContext.Global,
});
/**
* Cursor history
*/
export let previousCursorLocation = new UICommand({
keyboardShortcut: "mod+u", // CM undo cursor
description: "Cursor: Previous Cursor Location",
context: CommandContext.Global,
});
export let nextCursorLocation = new UICommand({
keyboardShortcut: "mod+shift+u", // CM redo cursor
description: "Cursor: Next Cursor Location",
context: CommandContext.Global,
});
/**
* Clipboard Ring
*/
export const copy = new UICommand({
keyboardShortcut: 'mod+c', // atom
description: "Copy",
context: CommandContext.Global,
allowDefault: true
});
export const cut = new UICommand({
keyboardShortcut: 'mod+x', // atom
description: "Cut",
context: CommandContext.Global,
allowDefault: true
});
export const pasteFromRing = new UICommand({
keyboardShortcut: 'mod+shift+v', // VS
description: "PasteFromRing",
context: CommandContext.Global,
allowDefault: false
});
/**
* Tree view
*/
export let treeViewToggle = new UICommand({
keyboardShortcut: 'mod+\\', // atom
description: "Tree View: Toggle",
context: CommandContext.Global,
});
export let treeViewRevealActiveFile = new UICommand({
keyboardShortcut: 'mod+shift+\\', // atom
description: "Tree View: Reveal Active File",
context: CommandContext.Global,
});
export let treeViewFocus = new UICommand({
keyboardShortcut: 'mod+0', // atom, code
description: "Tree View: Focus",
context: CommandContext.Global,
});
export let treeAddFile = new UICommand({
keyboardShortcut: 'a', // atom
description: "Tree View: Add File",
context: CommandContext.TreeView,
});
export let treeAddFolder = new UICommand({
keyboardShortcut: 'shift+a', // atom
description: "Tree View: Add Folder",
context: CommandContext.TreeView,
});
export let treeDuplicateFile = new UICommand({
keyboardShortcut: 'd', // atom
description: "Tree View: Duplicate File|Folder",
context: CommandContext.TreeView,
});
export let treeMoveFile = new UICommand({
keyboardShortcut: 'm', // atom
description: "Tree View: Move File|Folder",
context: CommandContext.TreeView,
});
/** Rename is same as `move` but people want to search for it */
export let treeRenameFile = new UICommand({
keyboardShortcut: 'r',
description: "Tree View: Rename File|Folder",
context: CommandContext.TreeView,
});
export let treeDeleteFile = new UICommand({
keyboardShortcut: 'del', // atom
description: "Tree View: Delete File|Folder",
context: CommandContext.TreeView,
});
export let treeOpenInExplorerFinder = new UICommand({
keyboardShortcut: 'o', // natural
description: "Tree View: Open folder in explorer / finder",
context: CommandContext.TreeView,
});
export let treeOpenInCmdTerminal = new UICommand({
keyboardShortcut: 'shift+o', // natural
description: "Tree View: Open folder in cmd / terminal",
context: CommandContext.TreeView,
});
/**
* General purpose file opening
* These are handled in appTabsContainer at the moment
*/
export const doOpenFile = new events.TypedEvent<{ filePath: string, position?: EditorPosition }>();
export const doOpenOrFocusFile = new events.TypedEvent<{ filePath: string, position?: EditorPosition }>();
export const openFileFromDisk = new UICommand({
keyboardShortcut: 'mod+shift+o',
description: 'Open a file present on server disk',
context: CommandContext.Global,
});
/** needed by cursor history */
export const doOpenOrFocusTab = new events.TypedEvent<{ tabId: string, tabUrl: string, position: EditorPosition }>();
/** needed by file tree */
export const closeFilesDirs = new events.TypedEvent<{ files: string[], dirs: string[] }>();
/** Needed by file tree, activates the tab but doesn't change focus away from tree view */
export const doOpenOrActivateFileTab = new events.TypedEvent<{ filePath: string }>();
/** Needed to toggle output js file. We toggle and also do not steal focus */
export const doToggleFileTab = new events.TypedEvent<{ filePath: string }>();
/** Needed to ensure that a demo view is open */
export const ensureLiveDemoTab = new events.TypedEvent<{ filePath: string }>();
export const closeDemoTab = new events.TypedEvent<{}>();
export const ensureLiveDemoReactTab = new events.TypedEvent<{ filePath: string }>();
export const closeDemoReactTab = new events.TypedEvent<{}>();
/**
* Other tab types
*/
export const doOpenDependencyView = new UICommand({
description: 'Open Dependency View',
context: CommandContext.Global,
});
export const doOpenASTView = new UICommand({
description: 'Open AST View',
context: CommandContext.Global,
});
export const doOpenASTFullView = new UICommand({
description: 'Open AST-Full View',
context: CommandContext.Global,
});
/**
* Common configuration file creations
*/
export const createEditorconfig = new UICommand({
description: 'Create a .editorconfig',
context: CommandContext.Global,
});
/**
* Settings stuff
*/
export const openSettingsFile = new UICommand({
description: 'Open settings file',
context: CommandContext.Global
})
/**
* Git
*/
export const gitAddAllCommitAndPush = new UICommand({
description: 'Git: Add all, Commit and Push',
context: CommandContext.Global
})
export const gitFetchLatestAndRebase = new UICommand({
description: 'Git: Fetch + Pull latest, and rebase any local commits',
context: CommandContext.Global
})
/** Whenever status might be invalid */
export const gitStatusNeedsRefresh = new events.TypedEvent<{}>();
/**
* Registration
*/
export function register() {
commandRegistry.forEach(c => {
if (c.config.context == CommandContext.Global
&& c.config.keyboardShortcut) {
Mousetrap.bindGlobal(c.config.keyboardShortcut, function() {
c.emit({});
return !!c.config.allowDefault;
});
}
});
}
/**
*
* CODE MIRROR
*
*/
/**
* Straight out of codemirror.js
*/
export const ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
export const mac = ios || /Mac/.test(navigator.platform);
export const windows = /win/i.test(navigator.platform);
/** Nice display name for the mod by user platform */
export const modName = mac ? '⌘' : 'Ctrl';
const mod = mac ? 'Cmd' : 'Ctrl';
/** Load editor actions + keymaps */
import { getActions } from "./monacoActionLoader";
const actions = getActions();
// console.log(actions); // DEBUG
if (mac) {
// TODO: mon
// Prevent the browser from handling the CMD + SHIFT + [ (or ]) which monaco uses for fold / unfold
}
function addEditorMapToCommands(command: types.MonacoActionInformation) {
new UICommand({
keyboardShortcut: command.kbd,
description: `Editor: ${command.label}`,
context: CommandContext.Editor,
editorCommandName: command.id,
});
}
actions.forEach(addEditorMapToCommands)
/**
* This is a consolidation of the `file edited` and `file changed on disk`
*/
export const fileContentsChanged = new events.TypedEvent<{ filePath: string }>();
/**
* Setup toasts to hide on esc
* Note: this is done here instead of `ui` as some commands depend on `ui` and having depend on commands causes a circular dependency
*/
import * as toastr from "toastr";
esc.on(() => {
toastr.clear();
});
/* DEBUG
console.table(
commandRegistry
.filter(c=>c.config.context == CommandContext.Editor)
.map(c=>({cmd:c.config.description, shortcut:c.config.keyboardShortcut}))
);
/* DEBUG */
/**
* Mac (the chrome browser in mac) doesn't have *cmd + y* (common redo).
* Instead it opens the browser history by mistake.
* So we redirect it to redo for any open editor :)
*/
Mousetrap.bindGlobal('mod+y', function(event: any) {
// If the focus is on editor than monaco already handles it
// If we made it till here .... then ....
// Prevent default
return false;
});
/**
* Mac: Cmd + H at the wrong place hides the window.
*/
Mousetrap.bindGlobal('mod+h', function(event: any) {
// If the focus is on editor than monaco already handles it
// If we made it till here .... then ....
// Prevent default
return false;
});