Skip to content

Commit 200460f

Browse files
committed
Extract code to parse and publish diagnostic from compileContents
1 parent 45c6119 commit 200460f

1 file changed

Lines changed: 176 additions & 110 deletions

File tree

server/src/incrementalCompilation.ts

Lines changed: 176 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,174 @@ rule mij
487487
callArgs.push(entry.file.incrementalFilePath);
488488
return callArgs;
489489
}
490+
491+
/**
492+
* Remaps code action file paths from the incremental temp file to the actual source file.
493+
*/
494+
function remapCodeActionsToSourceFile(
495+
codeActions: Record<string, fileCodeActions[]>,
496+
sourceFilePath: NormalizedPath,
497+
): fileCodeActions[] {
498+
const actions = Object.values(codeActions)[0] ?? [];
499+
500+
// Code actions will point to the locally saved incremental file, so we must remap
501+
// them so the editor understand it's supposed to apply them to the unsaved doc,
502+
// not the saved "dummy" incremental file.
503+
actions.forEach((ca) => {
504+
if (ca.codeAction.edit != null && ca.codeAction.edit.changes != null) {
505+
const change = Object.values(ca.codeAction.edit.changes)[0];
506+
507+
ca.codeAction.edit.changes = {
508+
[utils.pathToURI(sourceFilePath)]: change,
509+
};
510+
}
511+
});
512+
513+
return actions;
514+
}
515+
516+
/**
517+
* Filters diagnostics to remove unwanted parser errors from incremental compilation.
518+
*/
519+
function filterIncrementalDiagnostics(
520+
diagnostics: p.Diagnostic[],
521+
sourceFileName: string,
522+
): { filtered: p.Diagnostic[]; hasIgnoredMessages: boolean } {
523+
let hasIgnoredMessages = false;
524+
525+
const filtered = diagnostics
526+
.map((d) => ({
527+
...d,
528+
message: removeAnsiCodes(d.message),
529+
}))
530+
// Filter out a few unwanted parser errors since we run the parser in ignore mode
531+
.filter((d) => {
532+
if (
533+
!d.message.startsWith("Uninterpreted extension 'rescript.") &&
534+
(!d.message.includes(`/${INCREMENTAL_FOLDER_NAME}/${sourceFileName}`) ||
535+
// The `Multiple definition of the <kind> name <name>` type error's
536+
// message includes the filepath with LOC of the duplicate definition
537+
d.message.startsWith("Multiple definition of the") ||
538+
// The signature mismatch, with mismatch and ill typed applicative functor
539+
// type errors all include the filepath with LOC
540+
d.message.startsWith("Signature mismatch") ||
541+
d.message.startsWith("In this `with' constraint") ||
542+
d.message.startsWith("This `with' constraint on"))
543+
) {
544+
hasIgnoredMessages = true;
545+
return true;
546+
}
547+
return false;
548+
});
549+
550+
return { filtered, hasIgnoredMessages };
551+
}
552+
553+
/**
554+
* Logs an error when incremental compilation produces unexpected output.
555+
*/
556+
function logIncrementalCompilationError(
557+
entry: IncrementallyCompiledFileInfo,
558+
stderr: string,
559+
callArgs: string[] | null,
560+
send: (msg: p.Message) => void,
561+
): void {
562+
hasReportedFeatureFailedError.add(entry.project.rootPath);
563+
const logfile = path.resolve(
564+
entry.project.incrementalFolderPath,
565+
"error.log",
566+
);
567+
568+
try {
569+
fs.writeFileSync(
570+
logfile,
571+
`== BSC ARGS ==\n${callArgs?.join(" ")}\n\n== OUTPUT ==\n${stderr}`,
572+
);
573+
574+
const params: p.ShowMessageParams = {
575+
type: p.MessageType.Warning,
576+
message: `[Incremental typechecking] Something might have gone wrong with incremental type checking. Check out the [error log](file://${logfile}) and report this issue please.`,
577+
};
578+
579+
const message: p.NotificationMessage = {
580+
jsonrpc: c.jsonrpcVersion,
581+
method: "window/showMessage",
582+
params: params,
583+
};
584+
585+
send(message);
586+
} catch (e) {
587+
console.error(e);
588+
}
589+
}
590+
591+
/**
592+
* Processes compilation results and publishes diagnostics to the LSP client.
593+
*/
594+
function processAndPublishDiagnostics(
595+
entry: IncrementallyCompiledFileInfo,
596+
result: Record<string, p.Diagnostic[]>,
597+
codeActions: Record<string, fileCodeActions[]>,
598+
stderr: string,
599+
callArgs: string[] | null,
600+
send: (msg: p.Message) => void,
601+
): void {
602+
// Remap code actions to source file
603+
const actions = remapCodeActionsToSourceFile(
604+
codeActions,
605+
entry.file.sourceFilePath,
606+
);
607+
entry.codeActions = actions;
608+
609+
// Filter diagnostics
610+
const rawDiagnostics = Object.values(result)[0] ?? [];
611+
const { filtered: res, hasIgnoredMessages } = filterIncrementalDiagnostics(
612+
rawDiagnostics,
613+
entry.file.sourceFileName,
614+
);
615+
616+
// Log error if compilation produced unexpected output
617+
if (
618+
res.length === 0 &&
619+
stderr !== "" &&
620+
!hasIgnoredMessages &&
621+
!hasReportedFeatureFailedError.has(entry.project.rootPath)
622+
) {
623+
logIncrementalCompilationError(entry, stderr, callArgs, send);
624+
}
625+
626+
const fileUri = utils.pathToURI(entry.file.sourceFilePath);
627+
628+
// Get compiler diagnostics from main build (if any) and combine with incremental diagnostics
629+
const compilerDiagnosticsForFile =
630+
getCurrentCompilerDiagnosticsForFile(fileUri);
631+
const allDiagnostics = [...res, ...compilerDiagnosticsForFile];
632+
633+
// Update filesWithDiagnostics to track this file
634+
// entry.project.rootPath is guaranteed to match a key in projectsFiles
635+
// (see triggerIncrementalCompilationOfFile where the entry is created)
636+
const projectFile = projectsFiles.get(entry.project.rootPath);
637+
638+
if (projectFile != null) {
639+
if (allDiagnostics.length > 0) {
640+
projectFile.filesWithDiagnostics.add(fileUri);
641+
} else {
642+
// Only remove if there are no diagnostics at all
643+
projectFile.filesWithDiagnostics.delete(fileUri);
644+
}
645+
}
646+
647+
const notification: p.NotificationMessage = {
648+
jsonrpc: c.jsonrpcVersion,
649+
method: "textDocument/publishDiagnostics",
650+
params: {
651+
uri: fileUri,
652+
diagnostics: allDiagnostics,
653+
},
654+
};
655+
send(notification);
656+
}
657+
490658
async function compileContents(
491659
entry: IncrementallyCompiledFileInfo,
492660
fileContent: string,
@@ -564,116 +732,14 @@ async function compileContents(
564732
return;
565733
}
566734

567-
const actions = Object.values(codeActions)[0] ?? [];
568-
569-
// Code actions will point to the locally saved incremental file, so we must remap
570-
// them so the editor understand it's supposed to apply them to the unsaved doc,
571-
// not the saved "dummy" incremental file.
572-
actions.forEach((ca) => {
573-
if (
574-
ca.codeAction.edit != null &&
575-
ca.codeAction.edit.changes != null
576-
) {
577-
const change = Object.values(ca.codeAction.edit.changes)[0];
578-
579-
ca.codeAction.edit.changes = {
580-
[utils.pathToURI(entry.file.sourceFilePath)]: change,
581-
};
582-
}
583-
});
584-
585-
entry.codeActions = actions;
586-
587-
const res = (Object.values(result)[0] ?? [])
588-
.map((d) => ({
589-
...d,
590-
message: removeAnsiCodes(d.message),
591-
}))
592-
// Filter out a few unwanted parser errors since we run the parser in ignore mode
593-
.filter((d) => {
594-
if (
595-
!d.message.startsWith("Uninterpreted extension 'rescript.") &&
596-
(!d.message.includes(
597-
`/${INCREMENTAL_FOLDER_NAME}/${entry.file.sourceFileName}`,
598-
) ||
599-
// The `Multiple definition of the <kind> name <name>` type error's
600-
// message includes the filepath with LOC of the duplicate definition
601-
d.message.startsWith("Multiple definition of the") ||
602-
// The signature mismatch, with mismatch and ill typed applicative functor
603-
// type errors all include the filepath with LOC
604-
d.message.startsWith("Signature mismatch") ||
605-
d.message.startsWith("In this `with' constraint") ||
606-
d.message.startsWith("This `with' constraint on"))
607-
) {
608-
hasIgnoredErrorMessages = true;
609-
return true;
610-
}
611-
return false;
612-
});
613-
614-
if (
615-
res.length === 0 &&
616-
stderr !== "" &&
617-
!hasIgnoredErrorMessages &&
618-
!hasReportedFeatureFailedError.has(entry.project.rootPath)
619-
) {
620-
try {
621-
hasReportedFeatureFailedError.add(entry.project.rootPath);
622-
const logfile = path.resolve(
623-
entry.project.incrementalFolderPath,
624-
"error.log",
625-
);
626-
fs.writeFileSync(
627-
logfile,
628-
`== BSC ARGS ==\n${callArgs?.join(
629-
" ",
630-
)}\n\n== OUTPUT ==\n${stderr}`,
631-
);
632-
let params: p.ShowMessageParams = {
633-
type: p.MessageType.Warning,
634-
message: `[Incremental typechecking] Something might have gone wrong with incremental type checking. Check out the [error log](file://${logfile}) and report this issue please.`,
635-
};
636-
let message: p.NotificationMessage = {
637-
jsonrpc: c.jsonrpcVersion,
638-
method: "window/showMessage",
639-
params: params,
640-
};
641-
send(message);
642-
} catch (e) {
643-
console.error(e);
644-
}
645-
}
646-
647-
const fileUri = utils.pathToURI(entry.file.sourceFilePath);
648-
649-
// Get compiler diagnostics from main build (if any) and combine with incremental diagnostics
650-
const compilerDiagnosticsForFile =
651-
getCurrentCompilerDiagnosticsForFile(fileUri);
652-
const allDiagnostics = [...res, ...compilerDiagnosticsForFile];
653-
654-
// Update filesWithDiagnostics to track this file
655-
// entry.project.rootPath is guaranteed to match a key in projectsFiles
656-
// (see triggerIncrementalCompilationOfFile where the entry is created)
657-
const projectFile = projectsFiles.get(entry.project.rootPath);
658-
659-
if (projectFile != null) {
660-
if (allDiagnostics.length > 0) {
661-
projectFile.filesWithDiagnostics.add(fileUri);
662-
} else {
663-
// Only remove if there are no diagnostics at all
664-
projectFile.filesWithDiagnostics.delete(fileUri);
665-
}
666-
}
667-
668-
const notification: p.NotificationMessage = {
669-
jsonrpc: c.jsonrpcVersion,
670-
method: "textDocument/publishDiagnostics",
671-
params: {
672-
uri: fileUri,
673-
diagnostics: allDiagnostics,
674-
},
675-
};
676-
send(notification);
735+
processAndPublishDiagnostics(
736+
entry,
737+
result,
738+
codeActions,
739+
stderr,
740+
callArgs,
741+
send,
742+
);
677743
}
678744
onCompilationFinished?.();
679745
},

0 commit comments

Comments
 (0)