⏪ revert(vscode): 还原悬停即显示链接,恢复按住 ⌘ / Ctrl 点击跳到定义
This commit is contained in:
1 parent
ea1873015b
commit
f0cd3942cc
9 files changed
+95
-237
No files matched your search
@@ -2,6 +2,13 @@ import { useEffect, useState } from 'react'
|
||||
import { DefinitionLinks } from './definitionLinks'
|
||||
import { onMessage, postMessage } from './vscode'
|
||||
|
||||
/** 跳到定义用的修饰键,按平台习惯:macOS 是 ⌘,其余是 Ctrl。 */
|
||||
const IS_MAC = /Mac|iPhone|iPad/.test(navigator.userAgent)
|
||||
|
||||
function modifierHeld(event: MouseEvent | KeyboardEvent): boolean {
|
||||
return IS_MAC ? event.metaKey : event.ctrlKey
|
||||
}
|
||||
|
||||
/** 事件落在的那个带源码下标的名字(SegmentText 渲染的 data-offset)。 */
|
||||
function nameAt(target: EventTarget | null): HTMLElement | null {
|
||||
return target instanceof Element ? target.closest<HTMLElement>('[data-offset]') : null
|
||||
@@ -11,32 +18,29 @@ function offsetOf(el: HTMLElement | null): number | undefined {
|
||||
return el?.dataset.offset === undefined ? undefined : Number(el.dataset.offset)
|
||||
}
|
||||
|
||||
/** 眼下选中的那段;没选中文字是 null */
|
||||
function selectedRange(): Range | null {
|
||||
const selection = document.getSelection()
|
||||
return selection && !selection.isCollapsed && selection.rangeCount > 0 ? selection.getRangeAt(0).cloneRange() : null
|
||||
}
|
||||
|
||||
function sameRange(a: Range, b: Range): boolean {
|
||||
return a.compareBoundaryPoints(Range.START_TO_START, b) === 0 && a.compareBoundaryPoints(Range.END_TO_END, b) === 0
|
||||
}
|
||||
|
||||
/** 指针下的名字;查得到定义时把链接样式(data-link)挂到它上面。 */
|
||||
/** 指针下的名字、修饰键按没按;把链接样式(data-link)挂到指着的那个名字上。 */
|
||||
class Pointer {
|
||||
private hovered: HTMLElement | null = null
|
||||
private held = false
|
||||
private linked: HTMLElement | null = null
|
||||
|
||||
constructor(private readonly links: DefinitionLinks) {}
|
||||
|
||||
move(target: EventTarget | null): void {
|
||||
move(target: EventTarget | null, held: boolean): void {
|
||||
this.hovered = nameAt(target)
|
||||
this.held = held
|
||||
this.sync()
|
||||
}
|
||||
|
||||
/** 把指着的名字告诉 links,再按它的回答挂上或摘掉链接样式 */
|
||||
hold(held: boolean): void {
|
||||
this.held = held
|
||||
this.sync()
|
||||
}
|
||||
|
||||
/** 把按住修饰键时指着的名字告诉 links,再按它的回答挂上或摘掉链接样式 */
|
||||
sync(): void {
|
||||
const offset = offsetOf(this.hovered)
|
||||
this.links.point(offset)
|
||||
this.links.point(this.held ? offset : undefined)
|
||||
const link = offset !== undefined && offset === this.links.linked ? this.hovered : null
|
||||
if (link === this.linked)
|
||||
return
|
||||
@@ -47,7 +51,7 @@ class Pointer {
|
||||
}
|
||||
|
||||
/**
|
||||
* 鼠标停在查得到定义的名字上时,名字显示成链接(不用按修饰键),单击跳到定义;拖选文字、双击选词不跳。
|
||||
* 和 VS Code 编辑器里一样:按住 ⌘ / Ctrl、鼠标停在查得到定义的名字上时,名字显示成链接,点一下跳到定义。
|
||||
* version 是当前显示的文档版本,名字的下标属于它。
|
||||
*/
|
||||
export function useDefinitionLinks(version: number | undefined): void {
|
||||
@@ -60,23 +64,16 @@ export function useDefinitionLinks(version: number | undefined): void {
|
||||
}, [links, pointer, version])
|
||||
|
||||
useEffect(() => {
|
||||
// mouseover 只在指针下换了元素时来,滚动后指针下换了名字也来
|
||||
const onOver = (event: MouseEvent): void => pointer.move(event.target)
|
||||
const onMove = (event: MouseEvent): void => pointer.move(event.target, modifierHeld(event))
|
||||
const onOut = (event: MouseEvent): void => {
|
||||
if (event.relatedTarget === null)
|
||||
pointer.move(null)
|
||||
pointer.move(null, false)
|
||||
}
|
||||
// 按下时就在的选区:按在它里面时浏览器要等点击之后才清掉,不能当成这一下拖出来的
|
||||
let pressedOn: Range | null = null
|
||||
const onDown = (event: MouseEvent): void => {
|
||||
pressedOn = selectedRange()
|
||||
links.press(event.detail)
|
||||
}
|
||||
// 点链接归跳转:拦下这次点击,不让它再触发块上的编辑切换、折叠
|
||||
const onKey = (event: KeyboardEvent): void => pointer.hold(modifierHeld(event))
|
||||
const onBlur = (): void => pointer.hold(false)
|
||||
// ⌘ / Ctrl + 点击名字归跳转:拦下这次点击,不让它再触发块上的编辑切换、折叠
|
||||
const onClick = (event: MouseEvent): void => {
|
||||
const range = selectedRange()
|
||||
const selected = range !== null && !(pressedOn !== null && sameRange(pressedOn, range))
|
||||
if (!links.click(offsetOf(nameAt(event.target)), event.detail, selected))
|
||||
if (!links.click(modifierHeld(event) ? offsetOf(nameAt(event.target)) : undefined))
|
||||
return
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
@@ -87,19 +84,21 @@ export function useDefinitionLinks(version: number | undefined): void {
|
||||
links.answer(msg.id, msg.found)
|
||||
pointer.sync()
|
||||
})
|
||||
document.addEventListener('mouseover', onOver)
|
||||
document.addEventListener('mousemove', onMove)
|
||||
document.addEventListener('mouseout', onOut)
|
||||
// 捕获阶段:先于块里的按钮、折叠箭头拿到点击
|
||||
document.addEventListener('mousedown', onDown, true)
|
||||
document.addEventListener('click', onClick, true)
|
||||
window.addEventListener('keydown', onKey)
|
||||
window.addEventListener('keyup', onKey)
|
||||
window.addEventListener('blur', onBlur)
|
||||
return () => {
|
||||
off()
|
||||
document.removeEventListener('mouseover', onOver)
|
||||
document.removeEventListener('mousemove', onMove)
|
||||
document.removeEventListener('mouseout', onOut)
|
||||
document.removeEventListener('mousedown', onDown, true)
|
||||
document.removeEventListener('click', onClick, true)
|
||||
pointer.move(null)
|
||||
links.dispose()
|
||||
window.removeEventListener('keydown', onKey)
|
||||
window.removeEventListener('keyup', onKey)
|
||||
window.removeEventListener('blur', onBlur)
|
||||
}
|
||||
}, [links, pointer])
|
||||
}
|
||||
Reference in new issue
Block a user