Skip to content

Commit

Permalink
🔀 Merge pull request #78 from evan-liu/75-simplify-left-and-right-mod…
Browse files Browse the repository at this point in the history
…ifiers

✨ Add side modifier aliases
  • Loading branch information
evan-liu authored May 12, 2023
2 parents 9b6ac99 + 0836cc1 commit bc0362e
Show file tree
Hide file tree
Showing 16 changed files with 252 additions and 52 deletions.
8 changes: 8 additions & 0 deletions src/config/double-tap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ describe('mapDoubleTap()', () => {
key_code: '1',
modifiers: { optional: ['any'] },
})
expect(mapDoubleTap(1, '‹⌘', '›⌥').to(2).build()[1].from).toEqual({
key_code: '1',
modifiers: { mandatory: ['left_command'], optional: ['right_option'] },
})
expect(mapDoubleTap(1, '?>⌘').to(2).build()[1].from).toEqual({
key_code: '1',
modifiers: { optional: ['right_command'] },
})

expect(
[
Expand Down
16 changes: 16 additions & 0 deletions src/config/from.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,22 @@ test('map()', () => {
})

expect(() => map(1, '?⌘⌥x' as any)).toThrow()

expect(map(1, '?‹⌘⌥').to(2).build()[0].from).toEqual({
key_code: '1',
modifiers: { optional: ['left_command', 'left_option'] },
})

expect(map(1, '?r⇧').to(2).build()[0].from).toEqual({
key_code: '1',
modifiers: { optional: ['right_shift'] },
})

expect(map('r⌘').build()[0].from).toEqual({
key_code: 'right_command',
})

expect(() => map('‹⌘⌥' as any).build()).toThrow()
})

test('mapConsumerKey()', () => {
Expand Down
8 changes: 6 additions & 2 deletions src/config/from.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FromKeyCode } from '../karabiner/key-code'
import { getKeyWithAlias, KeyAlias, NumberKeyValue } from '../utils/key-alias'
import { FromModifierParam } from './modifier'
import { FromModifierParam, SideModifierAlias } from './modifier'
import { BasicManipulatorBuilder } from './manipulator'
import { FromEvent } from '../karabiner/karabiner-config'
import { FromConsumerKeyCode } from '../karabiner/consumer-key-code'
Expand All @@ -11,7 +11,11 @@ import {
} from '../utils/from-modifier-overload'
import { FromOptionalModifierParam } from '../utils/optional-modifiers'

export type FromKeyParam = FromKeyCode | KeyAlias | NumberKeyValue
export type FromKeyParam =
| FromKeyCode
| KeyAlias
| NumberKeyValue
| SideModifierAlias

/** Start a manipulator with a FromEvent */
export function map(from: FromEvent): BasicManipulatorBuilder
Expand Down
6 changes: 5 additions & 1 deletion src/config/layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { BasicRuleBuilder } from './rule'
import { toArray } from '../utils/to-array'
import { BuildContext } from '../utils/build-context'
import { BasicManipulatorBuilder } from './manipulator'
import { SideModifierAlias } from './modifier'

export type LayerKeyCode = Exclude<
FromKeyCode,
Expand All @@ -25,7 +26,10 @@ export type LayerKeyCode = Exclude<
export type LayerKeyParam =
| Exclude<
FromKeyParam,
FromOnlyKeyCode | StickyModifierKeyCode | ModifierKeyAlias
| FromOnlyKeyCode
| StickyModifierKeyCode
| ModifierKeyAlias
| SideModifierAlias
>
| '⇪'

Expand Down
15 changes: 15 additions & 0 deletions src/config/modifier.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,19 @@ test('parseFromModifierParams()', () => {
expect(parseFromModifierParams({ left: 'win' as any })).toEqual({
mandatory: ['win'],
})

expect(parseFromModifierParams({ l: '⌘', r: '⌥⌃' })).toEqual({
mandatory: ['left_command', 'right_option', 'right_control'],
})

expect(parseFromModifierParams('l⌘', ['‹⌘', '›⌥⌃'])).toEqual({
mandatory: ['left_command'],
optional: ['left_command', 'right_option', 'right_control'],
})

expect(parseFromModifierParams('l' as any)).toEqual({})

expect(parseFromModifierParams(['l⌘', '⌥', 'l', ''] as any)).toEqual({
mandatory: ['left_command', 'option'],
})
})
111 changes: 91 additions & 20 deletions src/config/modifier.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,76 @@ import { ModifierKeyAlias, modifierKeyAliases } from '../utils/key-alias'
import {
MultiModifierAlias,
multiModifierAliases,
NamedMultiModifierAlias,
} from '../utils/multi-modifier'

export type ModifierParam =
| Modifier
| ModifierKeyAlias
| Array<Modifier | ModifierKeyAlias>
| MultiModifierAlias
| { left?: ModifierParam; right?: ModifierParam }
| { l?: ModifierParam; r?: ModifierParam }
| SideMultiModifierAlias
| SideMultiModifierAlias[]

export function parseModifierParam(
src?: ModifierParam,
): Modifier[] | undefined {
if (!src) return undefined

if (typeof src !== 'string') return src.map(getModifierWithAlias)
if (typeof src === 'string') {
if (isSideMultiModifierAlias(src)) {
return parseSideMultiModifierAlias(src)
}
if (src in modifierKeyAliases)
return [modifierKeyAliases[src as ModifierKeyAlias]]

if (src in modifierKeyAliases)
return [modifierKeyAliases[src as ModifierKeyAlias]]
if (src in multiModifierAliases) {
return multiModifierAliases[src as MultiModifierAlias]
}
return [src as Modifier]
}

if (Array.isArray(src)) {
if (isSideMultiModifierAliases(src)) {
return parseSideMultiModifierAliases(src)
}
return src.map(getModifierWithAlias)
}

if (src in multiModifierAliases) {
return multiModifierAliases[src as MultiModifierAlias]
let left: Modifier[] | undefined = undefined
if ('left' in src) {
left = parseSideModifier('left', src.left)
} else if ('l' in src) {
left = parseSideModifier('left', src.l)
}
let right: Modifier[] | undefined = undefined
if ('right' in src) {
right = parseSideModifier('right', src.right)
} else if ('r' in src) {
right = parseSideModifier('right', src.r)
}
if (!left?.length && !right?.length) return undefined

return [src as Modifier]
return [...(left || []), ...(right || [])]
}

export type FromModifierParam =
| ModifierParam
| { left?: ModifierParam; right?: ModifierParam }
| 'any'
export type LeftModifierFlag = 'left' | 'l' | '<' | '‹'
export type RightModifierFlag = 'right' | 'r' | '>' | '›'
export type SideModifierFlag = LeftModifierFlag | RightModifierFlag
export type SideModifierAlias = `${SideModifierFlag}${Exclude<
ModifierKeyAlias,
'⇪'
>}`
export type SideMultiModifierAlias = `${SideModifierFlag}${
| Exclude<ModifierKeyAlias, '⇪'>
| Exclude<MultiModifierAlias, NamedMultiModifierAlias>}`

export type FromModifierParam = ModifierParam | 'any'

const leftModifierRegExp = /^(left|l|<|‹)([⌘⌥⌃⇧]*)$/
const rightModifierRegExp = /^(right|r|>|›)([⌘⌥⌃⇧]*)$/

export function parseFromModifierParams(
mandatoryParam?: FromModifierParam | '' | null,
Expand All @@ -49,16 +90,7 @@ function parseFromModifiers(
): Modifier[] | ['any'] | undefined {
if (!param) return undefined
if (param === 'any') return ['any']

if (typeof param === 'string' || Array.isArray(param)) {
return parseModifierParam(param)
}

const left = parseSideModifier('left', param.left)
const right = parseSideModifier('right', param.right)
if (!left?.length && !right?.length) return undefined

return [...(left || []), ...(right || [])]
return parseModifierParam(param)
}

const sidedModifiers = new Set<string>([
Expand All @@ -68,6 +100,45 @@ const sidedModifiers = new Set<string>([
'shift',
] /* c8 ignore next */ satisfies Modifier[])

export function isSideMultiModifierAlias(
src: string,
): src is SideMultiModifierAlias {
return leftModifierRegExp.test(src) || rightModifierRegExp.test(src)
}

export function parseSideMultiModifierAlias(
src: SideMultiModifierAlias,
): Modifier[] | undefined {
const leftMatched = src.match(leftModifierRegExp)
if (leftMatched) {
return parseSideModifier('left', leftMatched[2] as ModifierParam)
}
const rightMatched = src.match(rightModifierRegExp)
if (rightMatched) {
return parseSideModifier('right', rightMatched[2] as ModifierParam)
}
}

function isSideMultiModifierAliases(
src: string[],
): src is SideMultiModifierAlias[] {
return src.some(isSideMultiModifierAlias)
}

function parseSideMultiModifierAliases(
src: SideMultiModifierAlias[],
): Modifier[] | undefined {
return src.reduce(
(r, v) => [
...r,
...(isSideMultiModifierAlias(v)
? parseSideMultiModifierAlias(v) || []
: parseModifierParam(v) || []),
],
[] as Modifier[],
)
}

function parseSideModifier(
side: 'left' | 'right',
src?: ModifierParam,
Expand Down
13 changes: 13 additions & 0 deletions src/config/mouse-motion-to-scroll.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,17 @@ test('mouseMotionToScroll()', () => {
speed_multiplier: 2,
},
})

expect(
mouseMotionToScroll().modifiers('›⌘⌥', '⌃').build()[0].from?.modifiers,
).toEqual({
mandatory: ['right_command', 'right_option'],
optional: ['control'],
})

expect(
mouseMotionToScroll().modifiers('?›⌥').build()[0].from?.modifiers,
).toEqual({
optional: ['right_option'],
})
})
21 changes: 17 additions & 4 deletions src/config/mouse-motion-to-scroll.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,14 @@ import {
MouseMotionToScrollManipulator,
MouseMotionToScrollOptions,
} from '../karabiner/karabiner-config'
import { FromModifierParam, parseFromModifierParams } from './modifier'
import { FromModifierParam } from './modifier'
import { buildCondition, ConditionBuilder } from './condition'
import { BuildContext } from '../utils/build-context'
import {
FromModifierOverloadParam,
parseFromModifierOverload,
} from '../utils/from-modifier-overload'
import { FromOptionalModifierParam } from '../utils/optional-modifiers'

export function mouseMotionToScroll() {
return new MouseMotionToScrollManipulatorBuilder()
Expand All @@ -20,12 +25,20 @@ export class MouseMotionToScrollManipulatorBuilder
type: 'mouse_motion_to_scroll',
}

modifiers(
mandatoryModifiers?: FromModifierParam,
public modifiers(
mandatoryModifiers?: FromModifierOverloadParam,
optionalModifiers?: FromModifierParam,
): this
public modifiers(modifiers: FromOptionalModifierParam): this
public modifiers(
mandatoryModifiers?: FromModifierOverloadParam,
optionalModifiers?: FromModifierParam,
): this {
this.manipulator.from = {
modifiers: parseFromModifierParams(mandatoryModifiers, optionalModifiers),
modifiers: parseFromModifierOverload(
mandatoryModifiers,
optionalModifiers,
),
}
return this
}
Expand Down
11 changes: 9 additions & 2 deletions src/config/simlayer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,10 @@ test('simlayer().enableLayer()', () => {
test('simlayer().modifiers()', () => {
expect(
simlayer('a', 'b')
.modifiers('⌘')
.modifiers('›⌘')
.manipulators([map(1).to(2)])
.build().manipulators[1].from?.modifiers,
).toEqual({ mandatory: ['command'] })
).toEqual({ mandatory: ['right_command'] })

expect(
simlayer('a', 'b')
Expand All @@ -125,6 +125,13 @@ test('simlayer().modifiers()', () => {
.build().manipulators[1].from?.modifiers,
).toEqual({ optional: ['caps_lock'] })

expect(
simlayer('a', 'b')
.modifiers('?⇪')
.manipulators([map(1).to(2)])
.build().manipulators[1].from?.modifiers,
).toEqual({ optional: ['caps_lock'] })

expect(
simlayer('a', 'b')
.modifiers(null)
Expand Down
15 changes: 15 additions & 0 deletions src/config/to.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,21 @@ test('toKey()', () => {
expect(toKey('/')).toEqual({ key_code: 'slash' })
// NumberKeyValue
expect(toKey(1)).toEqual({ key_code: '1' })

expect(toKey(1).modifiers).toBeUndefined()
expect(toKey(1, '⌘⌥').modifiers).toEqual(['command', 'option'])
expect(toKey(1, { left: '⌘', right: '⌥' }).modifiers).toEqual([
'left_command',
'right_option',
])
expect(toKey(1, ['‹⌘', '›⌥']).modifiers).toEqual([
'left_command',
'right_option',
])
expect(toKey(1, '›⌘⇧').modifiers).toEqual(['right_command', 'right_shift'])

expect(toKey('r')).toEqual({ key_code: 'r' })
expect(toKey('r⌘')).toEqual({ key_code: 'right_command' })
})

test('setVar()', () => {
Expand Down
12 changes: 10 additions & 2 deletions src/config/to.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { ModifierParam, parseModifierParam } from './modifier'
import {
ModifierParam,
parseModifierParam,
SideModifierAlias,
} from './modifier'
import {
ToEvent,
ToEventOptions,
Expand All @@ -12,7 +16,11 @@ import { StickyModifierKeyCode, ToKeyCode } from '../karabiner/key-code'
import { ToConsumerKeyCode } from '../karabiner/consumer-key-code'
import { PointingButton } from '../karabiner/pointing-button'

export type ToKeyParam = ToKeyCode | KeyAlias | NumberKeyValue
export type ToKeyParam =
| ToKeyCode
| KeyAlias
| NumberKeyValue
| SideModifierAlias

/** Create ToEvent with key_code */
export function toKey(
Expand Down
10 changes: 9 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,15 @@ export type * from './karabiner/key-code'
export type * from './karabiner/karabiner-config'

// Types
export type { ModifierParam, FromModifierParam } from './config/modifier'
export type {
LeftModifierFlag,
RightModifierFlag,
SideModifierFlag,
SideModifierAlias,
SideMultiModifierAlias,
ModifierParam,
FromModifierParam,
} from './config/modifier'
export type { ModificationParameters } from './config/complex-modifications'
export type { MultiModifierAlias } from './utils/multi-modifier'
export type {
Expand Down
Loading

0 comments on commit bc0362e

Please sign in to comment.