✨ feat(vscode): 代码阅读预览单击名字高亮它在文件里的各处出现
This commit is contained in:
1 parent
5872cf7958
commit
9c0f05362c
12 files changed
+558
-9
No files matched your search
@@ -19,6 +19,16 @@ export interface Reveal {
|
||||
definition?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 点中的名字在这份文档里的一处出现:源码下标 [start, end)。kind 同 VS Code 的 DocumentHighlightKind:
|
||||
* write 是写入(定义、赋值),read 是读取,text 是语言扩展按文字认出来的。
|
||||
*/
|
||||
export interface Occurrence {
|
||||
start: number
|
||||
end: number
|
||||
kind: 'text' | 'read' | 'write'
|
||||
}
|
||||
|
||||
export interface DocumentMessage {
|
||||
type: 'document'
|
||||
/** TextDocument.version:webview 靠它判断是不是新一份文档 */
|
||||
@@ -37,12 +47,14 @@ export interface DocumentMessage {
|
||||
/**
|
||||
* reveal:已经开着的预览滚到某处(同 DocumentMessage.reveal)
|
||||
* definitionProbed:id 那次 probeDefinition 的回答,found 表示 VS Code 查得到定义
|
||||
* occurrencesFound:id 那次 findOccurrences 的回答,语言扩展没答(或没装)就是空的
|
||||
* filter:整个工作区共用的块筛选;ready 后先于第一份文档发一次,之后任何一个预览改了都发给所有预览
|
||||
*/
|
||||
export type ExtensionToWebview
|
||||
= | DocumentMessage
|
||||
| ({ type: 'reveal' } & Reveal)
|
||||
| { type: 'definitionProbed', id: number, found: boolean }
|
||||
| { type: 'occurrencesFound', id: number, occurrences: Occurrence[] }
|
||||
| { type: 'filter', excluded: Excluded }
|
||||
|
||||
/**
|
||||
@@ -51,6 +63,7 @@ export type ExtensionToWebview
|
||||
* probeDefinition:按住 ⌘ / Ctrl 指到带下标的名字上,问源码 offset 处有没有定义;id 标出是哪一次问,回答带回去;
|
||||
* version 是这份下标所属的文档版本
|
||||
* goToDefinition:⌘ / Ctrl + 点击,跳到 offset 处名字的定义
|
||||
* findOccurrences:单击带下标的名字,问它在这份文档里出现在哪几处;id、version 同 probeDefinition
|
||||
* setFilter:改块筛选(整份替换),扩展端记下后发 filter 给所有预览
|
||||
*/
|
||||
export type WebviewToExtension
|
||||
@@ -61,3 +74,4 @@ export type WebviewToExtension
|
||||
| { type: 'visibleLine', line: number }
|
||||
| { type: 'probeDefinition', id: number, version: number, offset: number }
|
||||
| { type: 'goToDefinition', version: number, offset: number }
|
||||
| { type: 'findOccurrences', id: number, version: number, offset: number }
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { DocumentHighlight, TextDocument } from 'vscode'
|
||||
import type { Occurrence } from './messages'
|
||||
import { commands, DocumentHighlightKind } from 'vscode'
|
||||
import { logger } from '../../logging/logger'
|
||||
|
||||
const KINDS: Record<DocumentHighlightKind, Occurrence['kind']> = {
|
||||
[DocumentHighlightKind.Text]: 'text',
|
||||
[DocumentHighlightKind.Read]: 'read',
|
||||
[DocumentHighlightKind.Write]: 'write',
|
||||
}
|
||||
|
||||
/**
|
||||
* document 里 offset 处的名字出现在哪几处:交给 VS Code 注册的高亮提供者(Pylance、TypeScript 语言服务……),
|
||||
* 和普通编辑器里单击名字高亮的是同一套,按语义认,同名的别的东西不算。
|
||||
*/
|
||||
export async function occurrencesOf(document: TextDocument, offset: number): Promise<Occurrence[]> {
|
||||
const position = document.positionAt(offset)
|
||||
let highlights: DocumentHighlight[]
|
||||
try {
|
||||
highlights = await commands.executeCommand<DocumentHighlight[] | undefined>('vscode.executeDocumentHighlights', document.uri, position) ?? []
|
||||
}
|
||||
catch (err) {
|
||||
logger.add({ level: 'warn', source: 'codeReader', message: `查名字的出现处失败 ${document.uri.toString()}:${position.line + 1}:${err instanceof Error ? err.message : String(err)}` })
|
||||
return []
|
||||
}
|
||||
return highlights.map(({ range, kind }) => ({
|
||||
start: document.offsetAt(range.start),
|
||||
end: document.offsetAt(range.end),
|
||||
kind: KINDS[kind ?? DocumentHighlightKind.Text],
|
||||
}))
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import { blockReplacement } from '../editText'
|
||||
import { definitionAction } from './definitionAction'
|
||||
import { findDefinitions, hasDefinition, pickDefinition } from './definitions'
|
||||
import { documentMessage } from './documentMessage'
|
||||
import { occurrencesOf } from './occurrences'
|
||||
|
||||
export const PREVIEW_VIEW_TYPE = 'superpowers.codeReader.preview'
|
||||
|
||||
@@ -197,9 +198,22 @@ class Preview {
|
||||
return
|
||||
case 'goToDefinition':
|
||||
await this.goToDefinition(msg.version, msg.offset)
|
||||
return
|
||||
case 'findOccurrences':
|
||||
await this.findOccurrences(msg.id, msg.version, msg.offset)
|
||||
}
|
||||
}
|
||||
|
||||
/** 回答 webview 第 id 次询问:offset 处的名字在这份文档里出现在哪几处。下标属于别的文档版本时不答,webview 换了文档版本会清掉高亮 */
|
||||
private async findOccurrences(id: number, version: number, offset: number): Promise<void> {
|
||||
const { document } = this
|
||||
if (version !== document.version)
|
||||
return
|
||||
const occurrences = await occurrencesOf(document, offset)
|
||||
if (version === document.version && this.ready)
|
||||
this.post({ type: 'occurrencesFound', id, occurrences })
|
||||
}
|
||||
|
||||
/** 回答 webview 第 id 次询问:offset 处的名字有没有定义。下标属于别的文档版本时不答,webview 收到新文档后会重新问 */
|
||||
private async probeDefinition(id: number, version: number, offset: number): Promise<void> {
|
||||
const { document } = this
|
||||
|
||||
Reference in new issue
Block a user