File size: 1,902 Bytes
064bfd6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | /**
* Vim Motion Functions
*
* Pure functions for resolving vim motions to cursor positions.
*/
import type { Cursor } from '../utils/Cursor.js'
/**
* Resolve a motion to a target cursor position.
* Does not modify anything - pure calculation.
*/
export function resolveMotion(
key: string,
cursor: Cursor,
count: number,
): Cursor {
let result = cursor
for (let i = 0; i < count; i++) {
const next = applySingleMotion(key, result)
if (next.equals(result)) break
result = next
}
return result
}
/**
* Apply a single motion step.
*/
function applySingleMotion(key: string, cursor: Cursor): Cursor {
switch (key) {
case 'h':
return cursor.left()
case 'l':
return cursor.right()
case 'j':
return cursor.downLogicalLine()
case 'k':
return cursor.upLogicalLine()
case 'gj':
return cursor.down()
case 'gk':
return cursor.up()
case 'w':
return cursor.nextVimWord()
case 'b':
return cursor.prevVimWord()
case 'e':
return cursor.endOfVimWord()
case 'W':
return cursor.nextWORD()
case 'B':
return cursor.prevWORD()
case 'E':
return cursor.endOfWORD()
case '0':
return cursor.startOfLogicalLine()
case '^':
return cursor.firstNonBlankInLogicalLine()
case '$':
return cursor.endOfLogicalLine()
case 'G':
return cursor.startOfLastLine()
default:
return cursor
}
}
/**
* Check if a motion is inclusive (includes character at destination).
*/
export function isInclusiveMotion(key: string): boolean {
return 'eE$'.includes(key)
}
/**
* Check if a motion is linewise (operates on full lines when used with operators).
* Note: gj/gk are characterwise exclusive per `:help gj`, not linewise.
*/
export function isLinewiseMotion(key: string): boolean {
return 'jkG'.includes(key) || key === 'gg'
}
|