✨ feat(vscode): 代码阅读的名字高亮更显眼(当前那处描框、别处画细线),⌘↓ / ⌘↑ 在各处之间跳,跳到折叠处自动展开,筛选栏写第几处
This commit is contained in:
1 parent
c5c5d2b517
commit
49f253b80f
18 files changed
+731
-53
No files matched your search
@@ -0,0 +1,85 @@
|
||||
import type { KeyCombo } from './messages'
|
||||
|
||||
/** 在高亮着的名字的各处出现之间跳:两条命令、快捷键的 when 子句用的上下文键。 */
|
||||
|
||||
export const NEXT_OCCURRENCE = 'superpowers.codeReader.nextOccurrence'
|
||||
export const PREVIOUS_OCCURRENCE = 'superpowers.codeReader.previousOccurrence'
|
||||
/** 活动的预览里能用键盘跳(有高亮、页面有焦点、焦点不在输入框里)。 */
|
||||
export const OCCURRENCES_CONTEXT = 'superpowers.codeReader.hasOccurrences'
|
||||
|
||||
/** VS Code 快捷键写法里的键名 → KeyboardEvent.key;单个字母、数字照写。 */
|
||||
const KEY_NAMES: Record<string, string> = {
|
||||
up: 'ArrowUp',
|
||||
down: 'ArrowDown',
|
||||
left: 'ArrowLeft',
|
||||
right: 'ArrowRight',
|
||||
home: 'Home',
|
||||
end: 'End',
|
||||
pageup: 'PageUp',
|
||||
pagedown: 'PageDown',
|
||||
}
|
||||
|
||||
/** `cmd+down` 这种一下按完的组合 → KeyCombo;`ctrl+k ctrl+x` 这种分两下按的、认不得的键名没有。 */
|
||||
export function parseKey(text: string): KeyCombo | undefined {
|
||||
const parts = text.trim().toLowerCase().split('+')
|
||||
if (parts.some(part => part === '' || /\s/.test(part)))
|
||||
return undefined
|
||||
const name = parts.pop() ?? ''
|
||||
const key = KEY_NAMES[name] ?? (/^[a-z0-9]$/.test(name) ? name : undefined)
|
||||
const modifiers = new Set(parts)
|
||||
if (!key || [...modifiers].some(modifier => !['ctrl', 'shift', 'alt', 'cmd', 'meta', 'win'].includes(modifier)))
|
||||
return undefined
|
||||
return {
|
||||
key,
|
||||
ctrl: modifiers.has('ctrl'),
|
||||
shift: modifiers.has('shift'),
|
||||
alt: modifiers.has('alt'),
|
||||
meta: modifiers.has('cmd') || modifiers.has('meta') || modifiers.has('win'),
|
||||
}
|
||||
}
|
||||
|
||||
interface Keybinding { command?: unknown, key?: unknown, mac?: unknown, linux?: unknown, win?: unknown }
|
||||
|
||||
/**
|
||||
* package.json 给这两条命令默认绑的键,各平台的都算(扩展可能跑在远程机器上,看不出界面在哪个系统)。
|
||||
* 用户在「键盘快捷方式」里改绑的读不到:VS Code 没有给扩展查快捷键的接口。
|
||||
*/
|
||||
export function navigationKeys(manifest: unknown): KeyCombo[] {
|
||||
const bindings = (manifest as { contributes?: { keybindings?: Keybinding[] } } | undefined)?.contributes?.keybindings ?? []
|
||||
const combos = bindings
|
||||
.filter(binding => binding.command === NEXT_OCCURRENCE || binding.command === PREVIOUS_OCCURRENCE)
|
||||
.flatMap(binding => [binding.key, binding.mac, binding.linux, binding.win])
|
||||
.filter((text): text is string => typeof text === 'string')
|
||||
.map(parseKey)
|
||||
.filter((combo): combo is KeyCombo => combo !== undefined)
|
||||
return combos.filter((combo, i) => combos.findIndex(other => JSON.stringify(other) === JSON.stringify(combo)) === i)
|
||||
}
|
||||
|
||||
/**
|
||||
* 上下文键跟着各预览报来的状态走:有一个预览能跳就是真(同一时刻只有一个页面有焦点),变了才设。
|
||||
* 预览关掉、页面被销毁(标签页切走)时当它不能跳。
|
||||
*/
|
||||
export class NavigationContext<Owner> {
|
||||
private readonly available = new Set<Owner>()
|
||||
private value = false
|
||||
|
||||
constructor(private readonly set: (value: boolean) => void) {}
|
||||
|
||||
update(owner: Owner, available: boolean): void {
|
||||
if (available)
|
||||
this.available.add(owner)
|
||||
else
|
||||
this.available.delete(owner)
|
||||
const value = this.available.size > 0
|
||||
if (value === this.value)
|
||||
return
|
||||
this.value = value
|
||||
this.set(value)
|
||||
}
|
||||
|
||||
/** 命令发给哪个预览:报了能跳的那个;从命令面板运行时页面没焦点,就发给活动的那个。 */
|
||||
target(owners: Iterable<Owner>, active: (owner: Owner) => boolean): Owner | undefined {
|
||||
const list = [...owners]
|
||||
return list.find(owner => this.available.has(owner)) ?? list.find(active)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user