Skip to content

Commit

Permalink
Merge pull request #11 from rintoj/feat/reference-enhanced
Browse files Browse the repository at this point in the history
Feat/reference enhanced
  • Loading branch information
rintoj authored Jul 6, 2024
2 parents 84aacca + 60fb63d commit 279678d
Show file tree
Hide file tree
Showing 12 changed files with 225 additions and 86 deletions.
24 changes: 8 additions & 16 deletions src/auto-complete/hook-auto-complete.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import { toNonNullArray } from 'tsds-tools'
import ts from 'typescript'
import { GQLAssistConfig } from '../config'
import { Position } from '../diff'
import { isFieldNode, makeQueryParsable } from '../gql'
import { isFieldNode } from '../gql'
import { getGQLNodeRange } from '../gql/get-gql-node-location-range'
import { isPositionWithInRange } from '../position'
import { getGQLContent, getGraphQLQueryVariable, getTSNodeLocationRange } from '../ts'
import { parseGraphQLDocumentFromTS } from '../ts'

export const DEFAULT_SIPPET = '{\n ${1}\n}'

Expand Down Expand Up @@ -60,21 +60,13 @@ export function autoCompleteHook(
schema: gql.GraphQLSchema,
config: GQLAssistConfig,
): FieldInformation[] {
const variable = getGraphQLQueryVariable(sourceFile)
if (!variable) return []

const range = getTSNodeLocationRange(variable, sourceFile)
if (!isPositionWithInRange(position, range)) return []

const query = getGQLContent(variable)
if (!query || query?.trim() === '') return topLevelInfo(schema)
if (isEmptyQuery(query)) return topLevelInfo(schema)

const offset = new Position(range.start.line, 0)

try {
const fixed = makeQueryParsable(query)
const document = gql.parse(fixed)
const { variable, document, source, offset } = parseGraphQLDocumentFromTS(sourceFile, {
position,
})
if (!variable) return []
if (!source || isEmptyQuery(source) || !document) return topLevelInfo(schema)

let schemaType: gql.GraphQLObjectType | null | undefined
let existingFields: string[] = []
const typeInfo = new gql.TypeInfo(schema)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Position, Range } from '../diff'
import { trimSpaces } from '../util/trim-spaces'
import { provideDefinitionFromSchema } from './definition-provider-from-schema'
import { provideDefinitionForSchema } from './definition-provider-for-schema'

const schema = `
type User {
Expand Down Expand Up @@ -60,7 +60,7 @@ function getAt(schema: string, range: Range | null) {

describe('provideDefinitionFromSchema', () => {
test('should provide return address type', async () => {
const range = provideDefinitionFromSchema(schema, new Position(4, 16))
const range = provideDefinitionForSchema(schema, new Position(4, 16))
const output = getAt(schema, range)
expect(output).toEqual(
trimSpaces(`
Expand All @@ -73,7 +73,7 @@ describe('provideDefinitionFromSchema', () => {
})

test('should provide user status', async () => {
const range = provideDefinitionFromSchema(schema, new Position(5, 16))
const range = provideDefinitionForSchema(schema, new Position(5, 16))
const output = getAt(schema, range)
expect(output).toEqual(
trimSpaces(`
Expand All @@ -85,7 +85,7 @@ describe('provideDefinitionFromSchema', () => {
})

test('should provide tweet type', async () => {
const range = provideDefinitionFromSchema(schema, new Position(36, 22))
const range = provideDefinitionForSchema(schema, new Position(36, 22))
const output = getAt(schema, range)
expect(output).toEqual(
trimSpaces(`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ function isInRange(node: gql.ASTNode, position: Position, offset?: Position) {
return isPositionWithInRange(position, nodeRange, true)
}

export function provideDefinitionFromSchema(source: string, position: Position) {
export function provideDefinitionForSchema(source: string, position: Position) {
try {
const fixed = makeQueryParsable(source)
const document = gql.parse(fixed)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function isInRange(node: gql.ASTNode, position: Position, offset?: Position) {
return isPositionWithInRange(position, nodeRange, true)
}

export function provideDefinitionFromSource(
export function provideDefinitionForSource(
sourceFile: ts.SourceFile,
position: Position,
schema: gql.GraphQLSchema,
Expand Down
8 changes: 4 additions & 4 deletions src/definition-provider/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export * from './definition-provider-from-schema'
export * from './definition-provider-from-source'
export * from './reference-provider-from-schema'
export * from './symbol-provider-from-schema'
export * from './definition-provider-for-schema'
export * from './definition-provider-for-source'
export * from './reference-provider-for-schema'
export * from './symbol-provider-for-schema'
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Position, Range } from '../diff'
import { trimSpaces } from '../util/trim-spaces'
import { provideReferenceFromSchema } from './reference-provider-from-schema'
import { provideReferenceForSchema } from './reference-provider-for-schema'

const schema = `
type User {
Expand Down Expand Up @@ -64,8 +64,8 @@ function getAt(schema: string, range: Range | null) {

describe('provideDefinitionFromSchema', () => {
test('should return all lines that has User in it', async () => {
const ranges = provideReferenceFromSchema(schema, new Position(1, 5))
const output = ranges?.map(range => getAt(schema, range)).join('\n\n')
const positions = await provideReferenceForSchema(schema, 'schema.ts', new Position(1, 5), '')
const output = positions?.map(position => getAt(schema, position.range)).join('\n\n')
expect(trimSpaces(output)).toEqual(
trimSpaces(`
mentions: [User!]
Expand Down
132 changes: 132 additions & 0 deletions src/definition-provider/reference-provider-for-schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
import { globStream } from 'fast-glob'
import * as gql from 'graphql'
import { Location, Position } from '../diff'
import { getGQLNodeRange, getGQLNodeRangeWithoutDescription, makeQueryParsable } from '../gql'
import { isPositionWithInRange } from '../position/is-position-within-range'
import { parseGraphQLDocumentFromTS, readTSFile } from '../ts'

interface SelectedField {
parent: string
name: string
}

function isInRange(node: gql.ASTNode, position: Position, offset?: Position) {
const nodeRange = getGQLNodeRange(node, offset)
return isPositionWithInRange(position, nodeRange, true)
}

function referencesByType(
document: gql.DocumentNode,
sourcePath: string,
type: string | undefined,
) {
if (!type) return []
const locations: Location[] = []
gql.visit(document, {
NamedType(node) {
if (node.name.value !== type) return
locations.push(new Location(sourcePath, getGQLNodeRangeWithoutDescription(node)))
},
})
return locations
}

function processFile(file: string, field: SelectedField, schema: gql.GraphQLSchema) {
const locations: Location[] = []
try {
const sourceFile = readTSFile(file)
const { document, offset } = parseGraphQLDocumentFromTS(sourceFile)
if (!document) return locations
const typeInfo = new gql.TypeInfo(schema)
gql.visit(
document,
gql.visitWithTypeInfo(typeInfo, {
Field(node) {
if (node.name.value !== field.name) return
const parent = typeInfo.getParentType()
if (!parent || parent.name !== field.parent) return
locations.push(new Location(file, getGQLNodeRange(node.name, offset)))
},
}),
)
} catch (e) {
console.log(`Failed to process file ${file}`, e.message)
}
return locations
}

async function referencesByField(
schema: gql.GraphQLSchema,
field: SelectedField | undefined,
pattern: string,
) {
if (!field || !pattern) return []
const stream = globStream(pattern)
let locations: Location[] = []
for await (const file of stream) {
locations = locations.concat(processFile(file as string, field, schema))
}
return locations
}

export async function provideReferenceForSchema(
source: string,
sourcePath: string,
position: Position,
pattern: string,
) {
try {
const fixed = makeQueryParsable(source)
const document = gql.parse(fixed)
let type: string | undefined
let selectedField: SelectedField | undefined
const processNode = (node: gql.NameNode) => {
if (!isInRange(node, position)) return
type = node.value
return gql.BREAK
}
const processField = (node: gql.TypeDefinitionNode) => {
switch (node.kind) {
case gql.Kind.OBJECT_TYPE_DEFINITION:
case gql.Kind.INPUT_OBJECT_TYPE_DEFINITION:
case gql.Kind.INTERFACE_TYPE_DEFINITION:
for (const field of node.fields ?? []) {
if (isInRange(field.name, position)) {
selectedField = { parent: node.name.value, name: field.name.value }
return gql.BREAK
}
}
}
}
gql.visit(document, {
Name(node) {
return processNode(node)
},
EnumTypeDefinition(node) {
for (const value of node.values ?? []) {
if (isInRange(value.name, position)) {
selectedField = { parent: node.name.value, name: value.name.value }
return
}
}
},
ObjectTypeDefinition(node) {
return processField(node)
},
InterfaceTypeDefinition(node) {
return processField(node)
},
InputObjectTypeDefinition(node) {
return processField(node)
},
})
if (type) {
return referencesByType(document, sourcePath, type)
}
const schema = gql.buildSchema(source)
return await referencesByField(schema, selectedField, pattern)
} catch (e) {
console.error(e)
return []
}
}
56 changes: 0 additions & 56 deletions src/definition-provider/reference-provider-from-schema.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ function parseChildren(node: gql.TypeDefinitionNode) {
return symbols
}

export function provideSymbolsFromSchema(source: string) {
export function provideSymbolsForSchema(source: string) {
try {
const document = gql.parse(source)
const symbols: SymbolInformation[] = []
Expand Down
12 changes: 12 additions & 0 deletions src/diff/token.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class Range {
constructor(
public start: Position,
public end: Position,
public path?: string,
) {}

setStart(start: Position) {
Expand All @@ -39,6 +40,17 @@ export class Range {
}
}

export class Location {
constructor(
public path: string,
public range: Range,
) {}

clone() {
return new Location(this.path, this.range.clone())
}
}

export class Token {
constructor(
public index: number,
Expand Down
1 change: 1 addition & 0 deletions src/ts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export * from './is-nullable'
export * from './is-nullable-from-decorator'
export * from './is-private'
export * from './organize-imports'
export * from './parse-graphql-document-from-ts'
export * from './parse-ts'
export * from './prettify'
export * from './print-ts'
Expand Down
Loading

0 comments on commit 279678d

Please sign in to comment.