diff --git a/packages/graphql-language-service-server/src/__tests__/GraphQLLanguageService.test.ts b/packages/graphql-language-service-server/src/__tests__/GraphQLLanguageService.test.ts index 62e14a12a82..3a30108227a 100644 --- a/packages/graphql-language-service-server/src/__tests__/GraphQLLanguageService.test.ts +++ b/packages/graphql-language-service-server/src/__tests__/GraphQLLanguageService.test.ts @@ -12,7 +12,7 @@ import { join } from 'node:path'; import { GraphQLConfig } from 'graphql-config'; import { GraphQLLanguageService } from '../GraphQLLanguageService'; import { SymbolKind } from 'vscode-languageserver-protocol'; -import { Position } from 'graphql-language-service'; +import { getRange, Position } from 'graphql-language-service'; import { NoopLogger } from '../Logger'; import { GraphQLEnumType } from 'graphql'; @@ -207,6 +207,7 @@ describe('GraphQLLanguageService', () => { ); expect(definitionQueryResult?.definitions.length).toEqual(1); }); + it('runs definition service on fragment spread', async () => { const definitionQueryResult = await languageService.getDefinition( 'fragment TestFragment on Human { name }\nquery { ...TestFragment }', @@ -236,6 +237,24 @@ describe('GraphQLLanguageService', () => { expect(definitionQueryResult?.definitions.length).toEqual(1); }); + it('can get range on first line', async () => { + const range = await getRange( + { line: 0, column: 15 }, + `query myHero($id: ID) { + hero(id: $id) { + name + id + } + } + `, + ); + + expect(range).toMatchObject({ + end: { character: 23, line: -1 }, + start: { character: 22, line: -1 }, + }); + }); + it('runs hover service as expected', async () => { const hoverInformation = await languageService.getHoverInformation( 'type Query { hero(episode: String): String }', diff --git a/packages/graphql-language-service/src/interface/getDiagnostics.ts b/packages/graphql-language-service/src/interface/getDiagnostics.ts index d41c3ff80fc..e9ce9bc41eb 100644 --- a/packages/graphql-language-service/src/interface/getDiagnostics.ts +++ b/packages/graphql-language-service/src/interface/getDiagnostics.ts @@ -181,7 +181,12 @@ export function getRange(location: SourceLocation, queryText: string): IRange { let stream = null; - for (let i = 0; i < location.line; i++) { + // LSP may count from 1, but monaco counts from 0 + for ( + let i = 0; + location.line === 0 ? i <= location.line : i < location.line; + i++ + ) { stream = new CharacterStream(lines[i]); while (!stream.eol()) { const style = parser.token(stream, state); diff --git a/packages/graphql-language-service/src/parser/CharacterStream.ts b/packages/graphql-language-service/src/parser/CharacterStream.ts index 0d6120e5bdb..20ab103496e 100644 --- a/packages/graphql-language-service/src/parser/CharacterStream.ts +++ b/packages/graphql-language-service/src/parser/CharacterStream.ts @@ -46,7 +46,12 @@ export default class CharacterStream implements CharacterStreamInterface { return isMatched; } - public eol = (): boolean => this._sourceText.length === this._pos; + public eol = (): boolean => { + if (!this._sourceText) { + return true; + } + return this._sourceText.length === this._pos; + }; public sol = (): boolean => this._pos === 0;