package scala.meta.internal.semanticdb

import java.nio.file.Files
import java.nio.file.StandardOpenOption
import scala.meta.internal.{semanticdb => s}
import scala.meta.io.AbsolutePath

package object scalac {

  implicit class XtensionSchemaTextDocument(sdocument: s.TextDocument) {
    private def write(targetroot: AbsolutePath, append: Boolean): Unit = {
      val openOption =
        if (append) StandardOpenOption.APPEND
        else StandardOpenOption.TRUNCATE_EXISTING
      val out = SemanticdbPaths.toSemanticdb(sdocument, targetroot)
      if (!Files.exists(out.toNIO.getParent)) {
        Files.createDirectories(out.toNIO.getParent)
      }
      val bytes = s.TextDocuments(sdocument :: Nil).toByteArray
      Files.write(out.toNIO, bytes, StandardOpenOption.CREATE, openOption)
    }
    def save(targetroot: AbsolutePath): Unit =
      write(targetroot, append = false)
    def append(targetroot: AbsolutePath): Unit =
      write(targetroot, append = true)
  }

}

package scala.meta.internal.semanticdb

import java.nio.file.Files
import java.nio.file.StandardOpenOption
import scala.meta.internal.{semanticdb => s}
import scala.meta.io.AbsolutePath

package object scalac {

  implicit class XtensionSchemaTextDocument(sdocument: s.TextDocument) {
    private def write(targetroot: AbsolutePath, append: Boolean): Unit = {
      val openOption =
        if (append) StandardOpenOption.APPEND
        else StandardOpenOption.TRUNCATE_EXISTING
      val out = SemanticdbPaths.toSemanticdb(sdocument, targetroot)
      if (!Files.exists(out.toNIO.getParent)) {
        Files.createDirectories(out.toNIO.getParent)
      }
      val fos = Files.newOutputStream(out.toNIO, StandardOpenOption.CREATE, openOption)
      try {
        s.TextDocuments(sdocument :: Nil).writeTo(fos)
      } finally {
        fos.close()
      }
    }
    def save(targetroot: AbsolutePath): Unit =
      write(targetroot, append = false)
    def append(targetroot: AbsolutePath): Unit =
      write(targetroot, append = true)
  }

}

--- 
+++ 
@@ -16,12 +16,8 @@
       if (!Files.exists(out.toNIO.getParent)) {
         Files.createDirectories(out.toNIO.getParent)
       }
-      val fos = Files.newOutputStream(out.toNIO, StandardOpenOption.CREATE, openOption)
-      try {
-        s.TextDocuments(sdocument :: Nil).writeTo(fos)
-      } finally {
-        fos.close()
-      }
+      val bytes = s.TextDocuments(sdocument :: Nil).toByteArray
+      Files.write(out.toNIO, bytes, StandardOpenOption.CREATE, openOption)
     }
     def save(targetroot: AbsolutePath): Unit =
       write(targetroot, append = false)
Use `java.nio.file.Files.write` to write SemanticDB files

Related to https://github.com/scalameta/scalameta/issues/2561

Previously, the SemanticDB compiler plugin streamed bytes into the file
increasing the risk that the plugin would be interrupted mid-way. Now,
the plugin writes the payload into a byte array and then writes that
byte array to disk.

There's still a small risk that `Files.write` gets interrupted. We could
minimize that risk even further by writing to a temporary file first and
then moving that file. We can consider using that approach if the
problem persists, this change along should already significantly
minimize the risk of half-complete SemanticDB files.
