Структура каталогов Autofix на основе пакета в scala

У меня есть файл src/main/scala/foo.scala который должен быть внутри упаковки bar. В идеале файл должен быть внутриsrc/main/scala/bar/foo.scala.

// src/main/scala/foo.scala


package bar

// ... 

Как я могу автоматически исправить эту проблему во всем моем проекте, чтобы структура папок соответствовала структуре пакета?

Есть ли какой-либо плагин SBT и т. Д., Который может помочь мне решить эту проблему?

2 ответа

Насколько мне известно, таких инструментов нет, хотя AFAIR IntelliJ может предупреждать о несовпадении пакета и каталога.

Лучшее, что я могу подумать, если это настраиваемое правило scalafix (https://scalacenter.github.io/scalafix/) - scalafix/scalameta будет использоваться для проверки фактического пакета файла, перевода его в ожидаемый каталог и, если они отличаются, переместить файл.

Я предлагаю scalafix / scalameta, потому что есть такие угловые случаи, как:

  • вам разрешено писать свои пакеты, например:

    package a
    package b
    package c
    

    и это почти какpackage a.b.c за исключением того, что он автоматически импортирует все из a а также b

  • вы можете иметь package object в вашем файле, а затем, если у вас есть

package a.b

package object c

этот файл должен быть в a/b/c каталог

поэтому я бы предпочел проверить, не попадает ли файл под какой-либо из существующих инструментов.

Если вы уверены, что таких случаев у вас нет (я бы не стал без проверки), вы можете:

  • сопоставьте первую строку с регулярным выражением (^package (.*))
  • перевести a.b.c в a/b/c (matched.split('.').map(_.trim).mkString(File.separator))
  • сравнить сгенерированное местоположение с фактическим местоположением (я предлагаю разрешить абсолютное местоположение файлов)
  • при необходимости переместить файл

Если есть возможность иметь более сложный случай, я мог бы заменить первый шаг запросом утилит scalafix / scalameta.

Вот плагин sbt, обеспечивающий packageStructureToDirectoryStructure задача, которая читает операторы пакета из исходных файлов, создает соответствующие каталоги, а затем перемещает в них файлы

import sbt._
import sbt.Keys._
import better.files._

object PackagesToDirectories extends AutoPlugin {
  object autoImport {
    val packageStructureToDirectoryStructure = taskKey[Unit]("Make directory structure match package structure")
  }

  import autoImport._

  override def trigger = allRequirements

  override lazy val projectSettings = Seq(
    packageStructureToDirectoryStructure := {
      val log = streams.value.log
      log.info(s"Refactoring directory structure to match package structure...")
      val sourceFiles = (Compile / sources).value
      val sourceBase = (Compile / scalaSource).value

      def packageStructure(lines: Traversable[String]): String = {
        val packageObjectRegex = """package object\s(.+)\s\{""".r
        val packageNestingRegex = """package\s(.+)\s\{""".r
        val packageRegex = """package\s(.+)""".r
        lines
          .collect {
            case packageObjectRegex(name) => name
            case packageNestingRegex(name) => name
            case packageRegex(name) => name
          }
          .flatMap(_.split('.'))
          .mkString("/")
      }

      sourceFiles.foreach { sourceFile =>
        val packagePath = packageStructure(sourceFile.toScala.lines)
        val destination = file"$sourceBase/$packagePath"
        destination.createDirectoryIfNotExists(createParents = true)
        val result = sourceFile.toScala.moveToDirectory(destination)
        log.info(s"$sourceFile moved to $result")
      }
    }
  )

}

ВНИМАНИЕ! Обязательно сделайте резервную копию проекта перед его запуском.

Другие вопросы по тегам