@@ -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+
490658async 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