⏪ revert(vscode): 还原悬停即显示链接,恢复按住 ⌘ / Ctrl 点击跳到定义
This commit is contained in:
1 parent
ea1873015b
commit
f0cd3942cc
9 files changed
+95
-237
No files matched your search
@@ -242,7 +242,7 @@ body.vscode-high-contrast-light {
|
||||
border-top: 1px solid color-mix(in srgb, currentColor 30%, transparent);
|
||||
}
|
||||
|
||||
/* 鼠标停在查得到定义的名字上:和编辑器里的链接一样 */
|
||||
/* 按住 ⌘ / Ctrl 停在查得到定义的名字上:和编辑器里的链接一样 */
|
||||
.cr-code [data-link] {
|
||||
color: var(--vscode-editorLink-activeForeground);
|
||||
text-decoration: underline;
|
||||
|
||||
@@ -1,115 +1,76 @@
|
||||
import type { WebviewToExtension } from './types'
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { DefinitionLinks, DOUBLE_CLICK_WAIT_MS, HOVER_DELAY_MS } from './definitionLinks'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { DefinitionLinks } from './definitionLinks'
|
||||
|
||||
interface Probe { id: number, version: number, offset: number }
|
||||
|
||||
function setup(version: number | undefined): { links: DefinitionLinks, probes: () => Probe[], jumps: () => WebviewToExtension[] } {
|
||||
function setup(version: number | undefined): { links: DefinitionLinks, sent: WebviewToExtension[], probes: () => { id: number, version: number, offset: number }[] } {
|
||||
const sent: WebviewToExtension[] = []
|
||||
const links = new DefinitionLinks(msg => sent.push(msg))
|
||||
links.setVersion(version)
|
||||
const probes = (): Probe[] => sent.flatMap(msg => msg.type === 'probeDefinition' ? [{ id: msg.id, version: msg.version, offset: msg.offset }] : [])
|
||||
const jumps = (): WebviewToExtension[] => sent.filter(msg => msg.type === 'goToDefinition')
|
||||
return { links, probes, jumps }
|
||||
const probes = (): { id: number, version: number, offset: number }[] => sent.flatMap(msg => msg.type === 'probeDefinition' ? [{ id: msg.id, version: msg.version, offset: msg.offset }] : [])
|
||||
return { links, sent, probes }
|
||||
}
|
||||
|
||||
/** 停在 offset 处的名字上直到问出去,扩展端答 found */
|
||||
function hoverAnswered(links: DefinitionLinks, probes: () => Probe[], offset: number, found: boolean): void {
|
||||
links.point(offset)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
links.answer(probes().at(-1)!.id, found)
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
describe('definitionLinks 悬停', () => {
|
||||
it('不用按修饰键:鼠标在名字上停稳就问,查得到就链上', () => {
|
||||
const { links, probes } = setup(1)
|
||||
links.point(54)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS - 1)
|
||||
expect(probes()).toEqual([])
|
||||
|
||||
vi.advanceTimersByTime(1)
|
||||
const [probe] = probes()
|
||||
expect(probe).toEqual({ id: probe.id, version: 1, offset: 54 })
|
||||
expect(links.linked).toBeUndefined()
|
||||
|
||||
links.answer(probe.id, true)
|
||||
expect(links.linked).toBe(54)
|
||||
})
|
||||
|
||||
it('划过的名字不问:停稳之前换了名字或移开都不问,只问最后停下的那个', () => {
|
||||
const { links, probes } = setup(1)
|
||||
links.point(10)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS / 2)
|
||||
links.point(20)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS / 2)
|
||||
links.point(undefined)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
expect(probes()).toEqual([])
|
||||
|
||||
links.point(30)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
expect(probes().map(probe => probe.offset)).toEqual([30])
|
||||
})
|
||||
|
||||
describe('definitionLinks', () => {
|
||||
it('问到「没有」后移开再指回来会重新问,语言扩展启动好了就能链上', () => {
|
||||
const { links, probes } = setup(1)
|
||||
hoverAnswered(links, probes, 54, false)
|
||||
links.point(54)
|
||||
links.answer(probes()[0].id, false)
|
||||
expect(links.linked).toBeUndefined()
|
||||
|
||||
links.point(undefined)
|
||||
hoverAnswered(links, probes, 54, true)
|
||||
links.point(54)
|
||||
const [first, second] = probes()
|
||||
expect(second).toEqual({ id: second.id, version: 1, offset: 54 })
|
||||
expect(second.id).not.toBe(first.id)
|
||||
|
||||
links.answer(second.id, true)
|
||||
expect(links.linked).toBe(54)
|
||||
})
|
||||
|
||||
it('修饰键 + 点击名字总交给扩展端去跳,问到过「没有」也一样', () => {
|
||||
const { links, sent, probes } = setup(1)
|
||||
links.point(54)
|
||||
links.answer(probes()[0].id, false)
|
||||
|
||||
expect(links.click(54)).toBe(true)
|
||||
expect(sent.at(-1)).toEqual({ type: 'goToDefinition', version: 1, offset: 54 })
|
||||
})
|
||||
|
||||
it('还指着同一个名字不重问', () => {
|
||||
const { links, probes } = setup(1)
|
||||
links.point(54)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
links.point(54)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
expect(probes()).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('迟到的旧回答丢掉,只认最近一次询问的回答', () => {
|
||||
const { links, probes } = setup(1)
|
||||
for (const offset of [54, 79, 54]) {
|
||||
links.point(offset)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
}
|
||||
links.point(54)
|
||||
links.point(79)
|
||||
links.point(54)
|
||||
const [stale, , latest] = probes()
|
||||
links.answer(latest.id, true)
|
||||
links.answer(stale.id, false)
|
||||
expect(links.linked).toBe(54)
|
||||
})
|
||||
|
||||
it('移开就不链接,之后到的回答也不再挂上', () => {
|
||||
it('松开修饰键或移开就不链接,之后到的回答也不再挂上', () => {
|
||||
const { links, probes } = setup(1)
|
||||
hoverAnswered(links, probes, 54, true)
|
||||
links.point(54)
|
||||
const [probe] = probes()
|
||||
links.answer(probe.id, true)
|
||||
expect(links.linked).toBe(54)
|
||||
|
||||
links.point(undefined)
|
||||
expect(links.linked).toBeUndefined()
|
||||
links.answer(probes()[0].id, true)
|
||||
links.answer(probe.id, true)
|
||||
expect(links.linked).toBeUndefined()
|
||||
})
|
||||
|
||||
it('换了文档版本:正指着的名字按新版本重问,旧版本的回答丢掉', () => {
|
||||
const { links, probes } = setup(1)
|
||||
links.point(54)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
links.setVersion(2)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
const [old, renewed] = probes()
|
||||
expect(renewed).toEqual({ id: renewed.id, version: 2, offset: 54 })
|
||||
|
||||
@@ -119,86 +80,15 @@ describe('definitionLinks 悬停', () => {
|
||||
expect(links.linked).toBe(54)
|
||||
})
|
||||
|
||||
it('还没收到文档时不问', () => {
|
||||
const { links, probes } = setup(undefined)
|
||||
it('还没收到文档时不问,点击也不拦', () => {
|
||||
const { links, sent } = setup(undefined)
|
||||
links.point(54)
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
expect(probes()).toEqual([])
|
||||
expect(links.click(54)).toBe(false)
|
||||
expect(sent).toEqual([])
|
||||
})
|
||||
|
||||
it('不再听鼠标后,等着发的询问不发', () => {
|
||||
const { links, probes } = setup(1)
|
||||
links.point(54)
|
||||
links.dispose()
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
expect(probes()).toEqual([])
|
||||
})
|
||||
})
|
||||
|
||||
describe('definitionLinks 点击', () => {
|
||||
it('单击链接:拦下这次点击,等过双击间隔再跳', () => {
|
||||
const { links, probes, jumps } = setup(1)
|
||||
hoverAnswered(links, probes, 54, true)
|
||||
|
||||
expect(links.click(54, 1, false)).toBe(true)
|
||||
vi.advanceTimersByTime(DOUBLE_CLICK_WAIT_MS - 1)
|
||||
expect(jumps()).toEqual([])
|
||||
vi.advanceTimersByTime(1)
|
||||
expect(jumps()).toEqual([{ type: 'goToDefinition', version: 1, offset: 54 }])
|
||||
})
|
||||
|
||||
it('双击不跳:第二下按下时取消还没发出的跳转,第二下的点击也拦下', () => {
|
||||
const { links, probes, jumps } = setup(1)
|
||||
hoverAnswered(links, probes, 54, true)
|
||||
|
||||
expect(links.click(54, 1, false)).toBe(true)
|
||||
links.press(2)
|
||||
expect(links.click(54, 2, true)).toBe(true)
|
||||
vi.advanceTimersByTime(DOUBLE_CLICK_WAIT_MS)
|
||||
expect(jumps()).toEqual([])
|
||||
})
|
||||
|
||||
it('单击后又单独按下一次(不算双击)不取消跳转', () => {
|
||||
const { links, probes, jumps } = setup(1)
|
||||
hoverAnswered(links, probes, 54, true)
|
||||
|
||||
links.click(54, 1, false)
|
||||
links.press(1)
|
||||
vi.advanceTimersByTime(DOUBLE_CLICK_WAIT_MS)
|
||||
expect(jumps()).toHaveLength(1)
|
||||
})
|
||||
|
||||
it('拖选文字停在链接上松手不跳', () => {
|
||||
const { links, probes, jumps } = setup(1)
|
||||
hoverAnswered(links, probes, 54, true)
|
||||
|
||||
expect(links.click(54, 1, true)).toBe(true)
|
||||
vi.advanceTimersByTime(DOUBLE_CLICK_WAIT_MS)
|
||||
expect(jumps()).toEqual([])
|
||||
})
|
||||
|
||||
it('没链上的名字点了不拦也不跳:还没问完、问到「没有」、点的不是指着的那个', () => {
|
||||
const { links, probes, jumps } = setup(1)
|
||||
links.point(54)
|
||||
expect(links.click(54, 1, false)).toBe(false)
|
||||
|
||||
vi.advanceTimersByTime(HOVER_DELAY_MS)
|
||||
links.answer(probes()[0].id, false)
|
||||
expect(links.click(54, 1, false)).toBe(false)
|
||||
|
||||
hoverAnswered(links, probes, 79, true)
|
||||
expect(links.click(54, 1, false)).toBe(false)
|
||||
expect(links.click(undefined, 1, false)).toBe(false)
|
||||
vi.advanceTimersByTime(DOUBLE_CLICK_WAIT_MS)
|
||||
expect(jumps()).toEqual([])
|
||||
})
|
||||
|
||||
it('不再听鼠标后,等着发的跳转不发', () => {
|
||||
const { links, probes, jumps } = setup(1)
|
||||
hoverAnswered(links, probes, 54, true)
|
||||
links.click(54, 1, false)
|
||||
links.dispose()
|
||||
vi.advanceTimersByTime(DOUBLE_CLICK_WAIT_MS)
|
||||
expect(jumps()).toEqual([])
|
||||
it('没指着名字的点击不拦', () => {
|
||||
const { links } = setup(1)
|
||||
expect(links.click(undefined)).toBe(false)
|
||||
})
|
||||
})
|
||||
@@ -1,25 +1,17 @@
|
||||
import type { WebviewToExtension } from './types'
|
||||
|
||||
/** 鼠标在名字上停这么久才问定义:划过预览时路过的名字不问,免得一路打扰语言扩展 */
|
||||
export const HOVER_DELAY_MS = 100
|
||||
|
||||
/** 单击链接后等这么久才跳:双击的第二下在这之内按下就不跳,留给双击选词 */
|
||||
export const DOUBLE_CLICK_WAIT_MS = 250
|
||||
|
||||
/**
|
||||
* 鼠标指着名字时问不问定义、链接显示在哪、点击跳不跳。和编辑器一样,每回停到一个名字上都现问:
|
||||
* 按住 ⌘ / Ctrl 指着名字时问不问定义、链接显示在哪:和编辑器一样,每回指到一个名字上都现问。
|
||||
* 语言扩展(Pylance、tsserver)刚启动那几秒会答「没有」,所以回答不留到下次指过来时用。
|
||||
* 只管下标,不碰 DOM;指着的名字下标、点击拖没拖出选区由调用方给。
|
||||
* 只管下标,不碰 DOM;按住修饰键时指着的名字下标由调用方给,不按就给 undefined。
|
||||
*/
|
||||
export class DefinitionLinks {
|
||||
private version: number | undefined
|
||||
/** 鼠标指着的名字的下标 */
|
||||
/** 按住修饰键时指着的名字的下标 */
|
||||
private target: number | undefined
|
||||
/** 为 target 发出(或等着发)的最近一次询问;它的回答之外的迟到回答都丢掉 */
|
||||
/** 为 target 发出的最近一次询问;它的回答之外的迟到回答都丢掉 */
|
||||
private request = 0
|
||||
private found = false
|
||||
private probeTimer: ReturnType<typeof setTimeout> | undefined
|
||||
private jumpTimer: ReturnType<typeof setTimeout> | undefined
|
||||
|
||||
constructor(private readonly post: (msg: WebviewToExtension) => void) {}
|
||||
|
||||
@@ -36,7 +28,7 @@ export class DefinitionLinks {
|
||||
this.ask(this.target)
|
||||
}
|
||||
|
||||
/** 指着的名字变了;还指着同一个名字就不重问 */
|
||||
/** 指着的名字(或修饰键)变了;还指着同一个名字就不重问 */
|
||||
point(offset: number | undefined): void {
|
||||
if (offset !== this.target)
|
||||
this.ask(offset)
|
||||
@@ -47,41 +39,19 @@ export class DefinitionLinks {
|
||||
this.found = found
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击落在 offset 处的名字上。count 是连击数(同 MouseEvent.detail),selected 表示这一下拖出了选区。
|
||||
* 返回 true 表示点的是链接,这次点击归跳转,不再给块上的按钮;只有没拖出选区的单击才跳,
|
||||
* 而且等 DOUBLE_CLICK_WAIT_MS 再发——双击的第二下在这之内按下(press)就不跳。
|
||||
*/
|
||||
click(offset: number | undefined, count: number, selected: boolean): boolean {
|
||||
const { version } = this
|
||||
if (offset === undefined || offset !== this.linked || version === undefined)
|
||||
/** 修饰键 + 点击名字:交给扩展端去跳,返回 true 表示这次点击归跳转,不再给块上的按钮 */
|
||||
click(offset: number | undefined): boolean {
|
||||
if (offset === undefined || this.version === undefined)
|
||||
return false
|
||||
if (count === 1 && !selected) {
|
||||
clearTimeout(this.jumpTimer)
|
||||
this.jumpTimer = setTimeout(() => this.post({ type: 'goToDefinition', version, offset }), DOUBLE_CLICK_WAIT_MS)
|
||||
}
|
||||
this.post({ type: 'goToDefinition', version: this.version, offset })
|
||||
return true
|
||||
}
|
||||
|
||||
/** 按下鼠标,count 同 MouseEvent.detail:是双击的第二下就取消还没发出的跳转 */
|
||||
press(count: number): void {
|
||||
if (count > 1)
|
||||
clearTimeout(this.jumpTimer)
|
||||
}
|
||||
|
||||
/** 不再听鼠标:等着发的询问、跳转都不发了 */
|
||||
dispose(): void {
|
||||
this.point(undefined)
|
||||
clearTimeout(this.jumpTimer)
|
||||
}
|
||||
|
||||
private ask(offset: number | undefined): void {
|
||||
clearTimeout(this.probeTimer)
|
||||
this.target = offset
|
||||
this.found = false
|
||||
const id = ++this.request
|
||||
const { version } = this
|
||||
if (offset !== undefined && version !== undefined)
|
||||
this.probeTimer = setTimeout(() => this.post({ type: 'probeDefinition', id, version, offset }), HOVER_DELAY_MS)
|
||||
this.request++
|
||||
if (offset !== undefined && this.version !== undefined)
|
||||
this.post({ type: 'probeDefinition', id: this.request, version: this.version, offset })
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ export type TokenRole
|
||||
| 'hint' // 属性表里的说明词:未标注 无 N 个 默认导出 …
|
||||
| 'variable' | 'function' | 'type' | 'string' | 'number' | 'literal' | 'comment' | 'plain'
|
||||
|
||||
/** offset:照抄自源码里的名字、字符串时它在源码里的起始下标,悬停、点击时拿它查定义;译词、提示词没有 */
|
||||
/** offset:照抄自源码里的名字、字符串时它在源码里的起始下标,⌘ / Ctrl 悬停、点击时拿它查定义;译词、提示词没有 */
|
||||
export interface Segment { text: string, role: TokenRole, offset?: number }
|
||||
|
||||
/** 一段 Markdown 说明文字:注释、JSDoc、docstring 去掉注释符号、引号和共同缩进后的原文,按 Markdown 渲染成注释色的段落、列表、代码块 */
|
||||
@@ -123,9 +123,9 @@ export type ExtensionToWebview
|
||||
/**
|
||||
* edit:把 startLine..endLine 这几行整行替换成 text(扩展端给每行补回原来第一行的缩进)
|
||||
* visibleLine:视口顶部那块的起始行,切回文本编辑器时扩展端把这行放到顶部
|
||||
* probeDefinition:鼠标停在带下标的名字上,问 offset 处有没有定义;id 标出是哪一次问,回答带回来;
|
||||
* probeDefinition:按住 ⌘ / Ctrl 指到带下标的名字上,问 offset 处有没有定义;id 标出是哪一次问,回答带回来;
|
||||
* version 是这份下标所属的文档版本
|
||||
* goToDefinition:单击链接,跳到 offset 处名字的定义
|
||||
* goToDefinition:⌘ / Ctrl + 点击,跳到 offset 处名字的定义
|
||||
* setFilter:改块筛选(整份替换);不在本地先改,等扩展端发回 filter 再按它显示
|
||||
*/
|
||||
export type WebviewToExtension
|
||||
|
||||
@@ -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