Skip to content

Commit

Permalink
feat: You can now ctrl+click on event name of methods like addListene…
Browse files Browse the repository at this point in the history
…r, on for jumping into event-related listeners or triggers like `emit`
  • Loading branch information
zardoy committed Oct 20, 2024
1 parent 16743dd commit 0d3848e
Show file tree
Hide file tree
Showing 2 changed files with 66 additions and 0 deletions.
4 changes: 4 additions & 0 deletions typescript/src/definitions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { join } from 'path-browserify'
import { GetConfig } from './types'
import { findChildContainingExactPosition } from './utils'
import { eventDefinitions } from './eventsReferences'

export default (proxy: ts.LanguageService, languageService: ts.LanguageService, languageServiceHost: ts.LanguageServiceHost, c: GetConfig) => {
proxy.getDefinitionAndBoundSpan = (fileName, position, ...props) => {
Expand All @@ -26,6 +27,9 @@ export default (proxy: ts.LanguageService, languageService: ts.LanguageService,
const noDefs = !prior?.definitions || prior.definitions.length === 0
const tryFileResolve = noDefs || ['?', '#'].some(x => prior.definitions?.[0]?.fileName?.includes(x))

const eventDefs = eventDefinitions(languageService, fileName, position)
if (eventDefs) return eventDefs

// Definition fallbacks
if (noDefs || tryFileResolve) {
const node = getNode()
Expand Down
62 changes: 62 additions & 0 deletions typescript/src/eventsReferences.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import ts from 'typescript'
import { findChildContainingExactPosition, matchParents } from './utils'

export const eventDefinitions = (languageService: ts.LanguageService, fileName: string, position: number): ts.DefinitionInfoAndBoundSpan | undefined => {
const program = languageService.getProgram()!
const sourceFile = program.getSourceFile(fileName)!
const node = findChildContainingExactPosition(sourceFile, position)
if (!node || !ts.isStringLiteral(node)) return
const eventName = node.text
const expr = matchParents(node, ['StringLiteral', 'CallExpression'])
if (!expr) return
if (!ts.isPropertyAccessExpression(expr.expression)) return
const parentAccessEndPos = expr.expression.expression.getEnd()
const method = expr.expression.name.text
let lookForMethods: string[] | undefined
const onMethods = ['on', 'once', 'off', 'addEventListener', 'removeEventListener', 'addListener', 'removeListener']
const triggerMethods = ['trigger', 'emit', 'dispatchEvent']
if (onMethods.includes(method)) {
lookForMethods = triggerMethods
}
if (triggerMethods.includes(method)) {
lookForMethods = onMethods
}
if (!lookForMethods) return
const references = languageService.findReferences(fileName, parentAccessEndPos) ?? []
const defs: ts.DefinitionInfo[] = references
.flatMap(({ references }) => references)
.map(({ fileName, textSpan }): ts.DefinitionInfo | undefined => {
const sourceFile = program.getSourceFile(fileName)!
const node = findChildContainingExactPosition(sourceFile, textSpan.start)
if (!node) return
if (!ts.isPropertyAccessExpression(node.parent)) return
let upNode = node as ts.PropertyAccessExpression
while (ts.isPropertyAccessExpression(upNode.parent)) {
upNode = upNode.parent
}
if (!ts.isCallExpression(upNode.parent)) return
if (!ts.isPropertyAccessExpression(upNode.parent.expression)) return
const method = upNode.parent.expression.name.text
if (!lookForMethods!.includes(method)) return
const arg = upNode.parent.arguments[0]
if (!arg || !ts.isStringLiteral(arg)) return
const lastArgEnd = upNode.parent.arguments.at(-1)!.end
const span = ts.createTextSpanFromBounds(arg.pos, lastArgEnd)
if (arg.text !== eventName) return

return {
kind: ts.ScriptElementKind.memberVariableElement,
name: method,
containerKind: ts.ScriptElementKind.variableElement,
containerName: method,
textSpan: span,
fileName,
}
})
.filter(a => a !== undefined)
.map(a => a!)
return {
textSpan: ts.createTextSpanFromBounds(node.pos, node.end),
definitions: defs,
}
}

0 comments on commit 0d3848e

Please sign in to comment.