Skip to content

Commit

Permalink
Merge pull request #166 from Coding/fs-module-refactor
Browse files Browse the repository at this point in the history
don't open duplicate tab of same file
  • Loading branch information
hackape committed Aug 16, 2017
2 parents b24052c + 2664375 commit 951f612
Show file tree
Hide file tree
Showing 11 changed files with 167 additions and 132 deletions.
25 changes: 16 additions & 9 deletions app/commands/commandBindings/file.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,15 +55,22 @@ function openFile ({ path, editor={} }) {
return data
})
.then((data) => {
TabStore.createTab({
title: path.split('/').pop(),
icon: 'fa fa-file-text-o',
editor: {
...editor,
// revision: data.hashedVersion,
filePath: path,
}
})
const activeTabGroup = TabStore.getState().activeTabGroup
const existingTabs = TabStore.findTab(
tab => tab.file && tab.file.path === path && tab.tabGroup === activeTabGroup
)
if (existingTabs.length) {
const existingTab = existingTabs[0]
existingTab.activate()
} else {
TabStore.createTab({
icon: 'fa fa-file-text-o',
editor: {
...editor,
filePath: path,
}
})
}
})
}

Expand Down
6 changes: 2 additions & 4 deletions app/commands/keymaps.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ if (isMac) {
'cmd+alt+3': 'editor:split_pane_vertical_3',
'cmd+alt+shift+3': 'editor:split_pane_horizontal_3',
'cmd+alt+4': 'editor:split_pane_vertical_4',
'cmd+comma': 'global:show_settings',
'cmd+,': 'global:show_settings',
'alt+b': 'global:show_branches',
}
modifierKeysMap = {
ctrl: '⌃',
alt: '⌥',
cmd: '⌘',
comma: ',',
shift: '⇧',
}
} else {
Expand All @@ -46,14 +45,13 @@ if (isMac) {
'ctrl+alt+3': 'editor:split_pane_vertical_3',
'ctrl+alt+shift+3': 'editor:split_pane_horizontal_3',
'ctrl+alt+4': 'editor:split_pane_vertical_4',
'alt+comma': 'global:show_settings',
'alt+,': 'global:show_settings',
'alt+b': 'global:show_branches',
}
modifierKeysMap = {
ctrl: 'Ctrl',
alt: 'Alt',
cmd: 'Cmd',
comma: ',',
shift: 'Shift',
}
}
Expand Down
10 changes: 5 additions & 5 deletions app/commands/lib/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@ export function keyEventToKeyCombination (e, combinator) {
return keycodes.keyCodeToKey[e.keyCode]
}

export function normalizeKeys (keys, combinator, delimiter) {
export function normalizeKeys (keys, combinator='+', delimiter=' ') {
// validate keys spec, if valid, also unify order of modifiers as in MODIFIERS_LIST
const keyCombos = keys.toLowerCase().replace(/\s/g, '').split(delimiter)
return keyCombos.map((keyCombo) => {
return keys.toLowerCase().split(delimiter).map((keyCombo) => {
const keyEventObj = {}
keyCombo.split(combinator).forEach((key) => {
// 'meta' is also aliased as 'cmd' or 'super'
if (key === 'cmd' || key === 'super') key = 'meta'
if (key === 'cmd' || key === 'command' || key === 'super') key = 'meta'
if (MODIFIERS_LIST.indexOf(key) > -1) {
keyEventObj[`${key}Key`] = true
} else {
Expand All @@ -28,5 +27,6 @@ export function normalizeKeys (keys, combinator, delimiter) {
throw Error(`Keymapper: Unrecognized key combination \`${keyCombo}\``)
}
return keyEventToKeyCombination(keyEventObj, combinator)
}).join(',')
})
.join(delimiter)
}
2 changes: 1 addition & 1 deletion app/commands/lib/keycodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const keyCodeToKey = {
90: 'z',
186: ';',
187: '=',
188: 'comma',
188: ',',
189: '-',
190: '.',
191: '/',
Expand Down
117 changes: 9 additions & 108 deletions app/commands/lib/keymapper.js
Original file line number Diff line number Diff line change
@@ -1,118 +1,19 @@
// 不可用的快捷键:
// http://stackoverflow.com/questions/7295508/javascript-capture-browser-shortcuts-ctrlt-n-w
import { normalizeKeys, keyEventToKeyCombination } from './helpers'
import Mousetrap from 'mousetrap'
import { normalizeKeys } from './helpers'

/*
定义两个术语:
Key Combination: 键位组合,指的是一个快捷键组合,e.g. `ctrl+c`
Key Composition: 复合快捷键,指的是连续按几个快捷键组合,e.g. `ctrl+K,ctrl+F`
*/
Mousetrap.prototype.stopCallback = function () { return false }

class Keymapper {
static defaults = {
dispatchCommand: console.log || (f => f),
combinator: '+',
delimiter: ',',
wait: 500,
};

constructor (opts) {
const options = { ...Keymapper.defaults, ...opts }
const { dispatchCommand, combinator, delimiter, wait } = options
constructor ({ dispatchCommand }) {
this.dispatchCommand = dispatchCommand
this.combinator = combinator
this.delimiter = delimiter
this.wait = wait

this.keymaps = {}
this.keysRegistered = {}
this.lastTimeoutId = false
this.buffer = {
keys: [],
getDelimiter: () => this.delimiter,
reset () {
this.keys.length = 0
},
push (keyCombination) {
this.keys.push(keyCombination)
},
read () {
return this.keys.join(this.getDelimiter())
}
}

window.addEventListener('keydown', this.handleKeyEvent)
Object.defineProperty(Keymapper, '$$singleton', { value: this })
}


handleKeyEvent = (e) => {
const isComboPending = Boolean(this.lastTimeoutId)
// ignore if only modifier is pressed
if (['Meta', 'OS', 'Control', 'Shift', 'Alt'].indexOf(e.key) > -1) return
const keyCombination = keyEventToKeyCombination(e, this.combinator)
const keyCombinationState = this.keysRegistered[keyCombination]
if (keyCombinationState === undefined && !isComboPending) return

this.buffer.push(keyCombination)

if (this.lastTimeoutId) {
clearTimeout(this.lastTimeoutId)
this.lastTimeoutId = false
}

if (keyCombinationState == 1) {
// hotkey should stop propagate
e.preventDefault(); e.stopPropagation()
this.consumeBuffer()
}

if (keyCombinationState > 1 || isComboPending) {
e.preventDefault(); e.stopPropagation()
this.lastTimeoutId = setTimeout(() => this.consumeBuffer(), this.wait)
}
}

consumeBuffer () {
this.lastTimeoutId = false
const keys = this.buffer.read()
if (keys) this.dispatchCommand(this.keymaps[keys])
this.buffer.reset()
}

map (keys, command) {
if (typeof keys !== 'string' || typeof command !== 'string') return
const normalizedKeys = normalizeKeys(keys, this.combinator, this.delimiter)
this.registerKeys(normalizedKeys)
this.registerKeymaps(normalizedKeys, command)
}

registerKeys (keys) {
const keyComps = keys.split(this.delimiter)
this.keysRegistered[keyComps[0]] = keyComps.length
}

unregisterKeys (keys) {
const keyComps = keys.split(this.delimiter)
delete this.keysRegistered[keyComps[0]]
}

registerKeymaps (keys, command) {
this.keymaps[keys] = command
}

loadKeymaps (keymaps) {
if (typeof keymaps === 'string') {
try { keymaps = JSON.parse(keymaps) } catch (e) { throw Error('Keymapper: Invalid keymaps description. Fail to parse JSON to object.') }
}
if (!keymaps || typeof keymaps !== 'object') return

Object.keys(keymaps).forEach((keys) => {
if (typeof keymaps[keys] === 'string') {
this.map(keys, keymaps[keys])
} else {
throw Error('Keymapper: Invalid keymaps description.')
}
Object.entries(keymaps).map(([keycombo, commandType]) => {
Mousetrap.bind(normalizeKeys(keycombo), (e) => {
this.dispatchCommand(commandType)
e.preventDefault(); e.stopPropagation()
})
})
}
}
Expand Down
120 changes: 120 additions & 0 deletions app/commands/lib/keymapper2.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
// 不可用的快捷键:
// http://stackoverflow.com/questions/7295508/javascript-capture-browser-shortcuts-ctrlt-n-w
import { normalizeKeys, keyEventToKeyCombination } from './helpers'

/*
定义两个术语:
Key Combination: 键位组合,指的是一个快捷键组合,e.g. `ctrl+c`
Key Composition: 复合快捷键,指的是连续按几个快捷键组合,e.g. `ctrl+K,ctrl+F`
*/

class Keymapper {
static defaults = {
dispatchCommand: console.log || (f => f),
combinator: '+',
delimiter: ',',
wait: 500,
};

constructor (opts) {
const options = { ...Keymapper.defaults, ...opts }
const { dispatchCommand, combinator, delimiter, wait } = options
this.dispatchCommand = dispatchCommand
this.combinator = combinator
this.delimiter = delimiter
this.wait = wait

this.keymaps = {}
this.keysRegistered = {}
this.lastTimeoutId = false
this.buffer = {
keys: [],
getDelimiter: () => this.delimiter,
reset () {
this.keys.length = 0
},
push (keyCombination) {
this.keys.push(keyCombination)
},
read () {
return this.keys.join(this.getDelimiter())
}
}

window.addEventListener('keydown', this.handleKeyEvent)
Object.defineProperty(Keymapper, '$$singleton', { value: this })
}


handleKeyEvent = (e) => {
const isComboPending = Boolean(this.lastTimeoutId)
// ignore if only modifier is pressed
if (['Meta', 'OS', 'Control', 'Shift', 'Alt'].indexOf(e.key) > -1) return
const keyCombination = keyEventToKeyCombination(e, this.combinator)
const keyCombinationState = this.keysRegistered[keyCombination]
if (keyCombinationState === undefined && !isComboPending) return

this.buffer.push(keyCombination)

if (this.lastTimeoutId) {
clearTimeout(this.lastTimeoutId)
this.lastTimeoutId = false
}

if (keyCombinationState == 1) {
// hotkey should stop propagate
e.preventDefault(); e.stopPropagation()
this.consumeBuffer()
}

if (keyCombinationState > 1 || isComboPending) {
e.preventDefault(); e.stopPropagation()
this.lastTimeoutId = setTimeout(() => this.consumeBuffer(), this.wait)
}
}

consumeBuffer () {
this.lastTimeoutId = false
const keys = this.buffer.read()
if (keys) this.dispatchCommand(this.keymaps[keys])
this.buffer.reset()
}

map (keys, command) {
if (typeof keys !== 'string' || typeof command !== 'string') return
const normalizedKeys = normalizeKeys(keys, this.combinator, this.delimiter)
this.registerKeys(normalizedKeys)
this.registerKeymaps(normalizedKeys, command)
}

registerKeys (keys) {
const keyComps = keys.split(this.delimiter)
this.keysRegistered[keyComps[0]] = keyComps.length
}

unregisterKeys (keys) {
const keyComps = keys.split(this.delimiter)
delete this.keysRegistered[keyComps[0]]
}

registerKeymaps (keys, command) {
this.keymaps[keys] = command
}

loadKeymaps (keymaps) {
if (typeof keymaps === 'string') {
try { keymaps = JSON.parse(keymaps) } catch (e) { throw Error('Keymapper: Invalid keymaps description. Fail to parse JSON to object.') }
}
if (!keymaps || typeof keymaps !== 'object') return

Object.keys(keymaps).forEach((keys) => {
if (typeof keymaps[keys] === 'string') {
this.map(keys, keymaps[keys])
} else {
throw Error('Keymapper: Invalid keymaps description.')
}
})
}
}

export default Keymapper
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ export default {
// })
if (!editor.file) return
editor.file.isSynced = false
FileStore.updateFile({
id: editor.file.id,
content: cm.getValue(),
})
debounced(() => {
FileStore.updateFile({
id: editor.file.id,
content: cm.getValue(),
})
dispatchCommand('file:save')
})
},
Expand Down
1 change: 0 additions & 1 deletion app/components/Tab/state.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { autorun, extendObservable, observable, computed, action } from 'mobx'
import { TabStateScope } from 'commons/Tab'
import PaneState from 'components/Pane/state'
import EditorState, { Editor } from 'components/Editor/state'
import FileState, { FileNode } from 'commons/File/state'

const { Tab: BaseTab, TabGroup: BaseTabGroup, state } = TabStateScope()

Expand Down
5 changes: 5 additions & 0 deletions app/components/Tab/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ class TabStore {
getTab (key) { return state.tabs.get(key) }
getTabGroup (key) { return state.tabGroups.get(key) }

findTab (predicate) {
const state = this.getState()
return state.tabs.values().filter(predicate)
}

isValidTab (instance) {
return (instance instanceof Tab && state.tabs.has(instance.id))
}
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@
"mobx": "^3.1.8",
"mobx-react": "^4.1.5",
"moment": "^2.18.1",
"mousetrap": "^1.6.1",
"octicons": "4.4.0",
"prop-types": "^15.5.10",
"qs": "^6.4.0",
Expand Down

0 comments on commit 951f612

Please sign in to comment.