Skip to content

Commit

Permalink
upstream LSP: basic diagnostic functionality (#176)
Browse files Browse the repository at this point in the history
Co-authored-by: Jerel Miller <[email protected]>
  • Loading branch information
phryneas and jerelmiller authored Aug 26, 2024
1 parent b4687eb commit cbc1c63
Show file tree
Hide file tree
Showing 10 changed files with 353 additions and 64 deletions.
5 changes: 5 additions & 0 deletions .changeset/grumpy-olives-argue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"vscode-apollo": patch
---

Fixed a bug where annotations might have mapped to the wrong position on the first line of an embedded document.
10 changes: 10 additions & 0 deletions src/language-server/project/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
Connection,
ServerRequestHandler,
TextDocumentChangeEvent,
StarRequestHandler,
StarNotificationHandler,
} from "vscode-languageserver/node";
import { TextDocument } from "vscode-languageserver-textdocument";

Expand Down Expand Up @@ -131,6 +133,14 @@ export abstract class GraphQLProject {
abstract onCodeLens?: ConnectionHandler["onCodeLens"];
abstract onCodeAction?: ConnectionHandler["onCodeAction"];

abstract onUnhandledRequest?: StarRequestHandler;
abstract onUnhandledNotification?: (
connection: Connection,
...rest: Parameters<StarNotificationHandler>
) => ReturnType<StarNotificationHandler>;

abstract dispose?(): void;

abstract provideSymbol?(
query: string,
token: CancellationToken,
Expand Down
3 changes: 3 additions & 0 deletions src/language-server/project/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -343,4 +343,7 @@ export abstract class GraphQLInternalProject
}
}
};
onUnhandledRequest: undefined;
onUnhandledNotification: undefined;
dispose: undefined;
}
86 changes: 76 additions & 10 deletions src/language-server/project/rover/DocumentSynchronization.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,30 @@ import {
DidOpenTextDocumentNotification,
DidCloseTextDocumentNotification,
TextDocumentPositionParams,
Diagnostic,
NotificationHandler,
PublishDiagnosticsParams,
} from "vscode-languageserver-protocol";
import { TextDocument } from "vscode-languageserver-textdocument";
import { DocumentUri, GraphQLProject } from "../base";
import { generateKeyBetween } from "fractional-indexing";
import { Source } from "graphql";
import { findContainedSourceAndPosition } from "../../utilities/source";
import {
findContainedSourceAndPosition,
rangeInContainingDocument,
} from "../../utilities/source";

export interface FilePart {
fractionalIndex: string;
source: Source;
diagnostics: Diagnostic[];
}

export function handleFilePartUpdates(
parsed: Source[],
previousParts: FilePart[],
): FilePart[] {
const newParts = [];
parsed: ReadonlyArray<Source>,
previousParts: ReadonlyArray<FilePart>,
): ReadonlyArray<FilePart> {
const newParts: FilePart[] = [];
let newIdx = 0;
let oldIdx = 0;
let offsetCorrection = 0;
Expand All @@ -37,7 +44,7 @@ export function handleFilePartUpdates(
newOffset === oldPart.source.locationOffset.line + offsetCorrection)
) {
// replacement of chunk
newParts.push({ source, fractionalIndex: oldPart.fractionalIndex });
newParts.push({ ...oldPart, source });
offsetCorrection =
source.locationOffset.line - oldPart.source.locationOffset.line;
newIdx++;
Expand All @@ -53,7 +60,7 @@ export function handleFilePartUpdates(
: newParts[newParts.length - 1].fractionalIndex,
oldPart ? oldPart.fractionalIndex : null,
);
newParts.push({ source, fractionalIndex });
newParts.push({ source, fractionalIndex, diagnostics: [] });
newIdx++;
offsetCorrection += source.body.split("\n").length - 1;
} else {
Expand All @@ -67,14 +74,20 @@ export function handleFilePartUpdates(
function getUri(part: FilePart) {
return part.source.name + "/" + part.fractionalIndex + ".graphql";
}
function splitUri(fullUri: DocumentUri) {
const result = /^(.*)\/(\w+)\.graphql/.exec(fullUri);
if (!result) return null;
const [, uri, fractionalIndex] = result;
return { uri, fractionalIndex };
}

export class DocumentSynchronization {
private pendingDocumentChanges = new Map<DocumentUri, TextDocument>();
private knownFiles = new Map<
DocumentUri,
{
full: TextDocument;
parts: FilePart[];
parts: ReadonlyArray<FilePart>;
}
>();

Expand All @@ -83,6 +96,7 @@ export class DocumentSynchronization {
type: ProtocolNotificationType<P, RO>,
params?: P,
) => Promise<void>,
private sendDiagnostics: NotificationHandler<PublishDiagnosticsParams>,
) {}

private documentSynchronizationScheduled = false;
Expand All @@ -109,10 +123,12 @@ export class DocumentSynchronization {
}
};

private async sendDocumentChanges(document: TextDocument) {
private async sendDocumentChanges(
document: TextDocument,
previousParts = this.knownFiles.get(document.uri)?.parts || [],
) {
this.pendingDocumentChanges.delete(document.uri);

const previousParts = this.knownFiles.get(document.uri)?.parts || [];
const previousObj = Object.fromEntries(
previousParts.map((p) => [p.fractionalIndex, p]),
);
Expand Down Expand Up @@ -161,6 +177,12 @@ export class DocumentSynchronization {
}
}

async resendAllDocuments() {
for (const file of this.knownFiles.values()) {
await this.sendDocumentChanges(file.full, []);
}
}

onDidOpenTextDocument: NonNullable<GraphQLProject["onDidOpen"]> = async (
params,
) => {
Expand Down Expand Up @@ -229,4 +251,48 @@ export class DocumentSynchronization {
position: match.position,
});
}

handlePartDiagnostics(params: PublishDiagnosticsParams) {
const uriDetails = splitUri(params.uri);
if (!uriDetails) {
return;
}
const found = this.knownFiles.get(uriDetails.uri);
if (!found) {
return;
}
const part = found.parts.find(
(p) => p.fractionalIndex === uriDetails.fractionalIndex,
);
if (!part) {
return;
}
part.diagnostics = params.diagnostics;

const fullDocumentParams: PublishDiagnosticsParams = {
uri: found.full.uri,
version: found.full.version,
diagnostics: found.parts.flatMap((p) =>
p.diagnostics.map((diagnostic) => ({
...diagnostic,
range: rangeInContainingDocument(p.source, diagnostic.range),
})),
),
};

this.sendDiagnostics(fullDocumentParams);
}

get openDocuments() {
return [...this.knownFiles.values()].map((f) => f.full);
}

clearAllDiagnostics() {
for (const file of this.knownFiles.values()) {
for (const part of file.parts) {
part.diagnostics = [];
}
this.sendDiagnostics({ uri: file.full.uri, diagnostics: [] });
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,15 @@ describe("handleFilePartUpdates", () => {
expect(initialUpdates).toEqual([
{
fractionalIndex: "a0",
diagnostics: [],
source: new Source(query1, "uri", {
column: 5,
line: 3,
}),
},
{
fractionalIndex: "a1",
diagnostics: [],
source: new Source(query2, "uri", {
column: 5,
line: 11,
Expand All @@ -169,13 +171,15 @@ describe("handleFilePartUpdates", () => {
).toEqual([
{
fractionalIndex: "a0",
diagnostics: [],
source: new Source(query1, "uri", {
column: 5,
line: 5,
}),
},
{
fractionalIndex: "a1",
diagnostics: [],
source: new Source(query2, "uri", {
column: 5,
line: 13,
Expand All @@ -195,20 +199,23 @@ describe("handleFilePartUpdates", () => {
).toEqual([
{
fractionalIndex: "a0",
diagnostics: [],
source: new Source(query1, "uri", {
column: 5,
line: 5,
}),
},
{
fractionalIndex: "a0V",
diagnostics: [],
source: new Source(query3, "uri", {
column: 5,
line: 12,
}),
},
{
fractionalIndex: "a1",
diagnostics: [],
source: new Source(query2, "uri", {
column: 5,
line: 19,
Expand All @@ -228,20 +235,23 @@ describe("handleFilePartUpdates", () => {
).toEqual([
{
fractionalIndex: "a0",
diagnostics: [],
source: new Source(query1, "uri", {
column: 5,
line: 5,
}),
},
{
fractionalIndex: "a1",
diagnostics: [],
source: new Source(query2, "uri", {
column: 5,
line: 12,
}),
},
{
fractionalIndex: "a2",
diagnostics: [],
source: new Source(query3, "uri", {
column: 5,
line: 19,
Expand All @@ -261,6 +271,7 @@ describe("handleFilePartUpdates", () => {
).toEqual([
{
fractionalIndex: "a1",
diagnostics: [],
source: new Source(query2, "uri", {
column: 5,
line: 5,
Expand All @@ -280,6 +291,7 @@ describe("handleFilePartUpdates", () => {
).toEqual([
{
fractionalIndex: "a0",
diagnostics: [],
source: new Source(query1, "uri", {
column: 5,
line: 3,
Expand Down
Loading

0 comments on commit cbc1c63

Please sign in to comment.