<commit_before>/*
 * Copyright (C) 2016 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.sqldelight.intellij.lang

import com.intellij.lang.Language
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.FileViewProvider
import com.intellij.psi.FileViewProviderFactory
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiManager
import com.intellij.psi.SingleRootFileViewProvider
import com.intellij.util.containers.MultiMap
import com.squareup.sqldelight.SqliteCompiler
import com.squareup.sqldelight.Status
import com.squareup.sqldelight.Status.ValidationStatus.Invalid
import com.squareup.sqldelight.model.relativePath
import com.squareup.sqldelight.types.SymbolTable
import com.squareup.sqldelight.validation.SqlDelightValidator
import com.squareup.sqldelight.validation.SqlDelightValidator.Companion.ALL_FILE_DEPENDENCY
import java.io.File

class SqlDelightFileViewProviderFactory : FileViewProviderFactory {
  override fun createFileViewProvider(virtualFile: VirtualFile, language: Language,
      psiManager: PsiManager, eventSystemEnabled: Boolean): FileViewProvider {
    return SqlDelightFileViewProvider(virtualFile, language, psiManager, eventSystemEnabled)
  }
}

internal class SqlDelightFileViewProvider(virtualFile: VirtualFile, language: Language,
    val psiManager: PsiManager, eventSystemEnabled: Boolean) :
    SingleRootFileViewProvider(psiManager, virtualFile, eventSystemEnabled, language) {

  val documentManager = PsiDocumentManager.getInstance(psiManager.project)
  val file: SqliteFile by lazy {
    getPsiInner(SqliteLanguage.INSTANCE) as SqliteFile
  }

  override fun contentsSynchronized() {
    super.contentsSynchronized()
    documentManager.performWhenAllCommitted { generateJavaInterface(true) }
  }

  internal fun generateJavaInterface(fromEdit: Boolean = false) {
    file.parseThen({ parsed ->
      symbolTable += SymbolTable(parsed, virtualFile)
      synchronized(dependencies) {
        dependencies.entrySet().forEach {
          it.value.remove(this)
        }
        file.status = sqldelightValidator.validate(parsed, symbolTable)
        (file.status as Status.ValidationStatus).dependencies.forEach {
          if (it != virtualFile) {
            dependencies.putValue(it, this)
          }
        }
      }

      triggerDependencyRefresh(virtualFile, fromEdit)

      if (file.status is Invalid) return@parseThen

      file.status = sqliteCompiler.write(
          parsed,
          virtualFile.nameWithoutExtension,
          virtualFile.getPlatformSpecificPath().relativePath(parsed),
          ModuleUtil.findModuleForPsiElement(file)!!.moduleFile!!.parent.getPlatformSpecificPath() + File.separatorChar
      )

      if (file.status is Status.Success) {
        val generatedFile = localFileSystem.findFileByIoFile((file.status as Status.Success).generatedFile)
        generatedFile!!.refresh(true, false)
        if (generatedFile != file.generatedFile?.virtualFile) {
          file.generatedFile?.delete()
        }
        file.generatedFile = psiManager.findFile(generatedFile)
      }
    }, onError = {
      removeFile(virtualFile, fromEdit)
    })
  }

  companion object {
    private val localFileSystem = LocalFileSystem.getInstance()
    private val sqliteCompiler = SqliteCompiler()<commit_msg>Its not guaranteed this will be non-null apparently. These logs go nowhere in production but at least when we eventually add proper logging Ill change this<commit_after>/*
 * Copyright (C) 2016 Square, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.squareup.sqldelight.intellij.lang

import com.intellij.lang.Language
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.psi.FileViewProvider
import com.intellij.psi.FileViewProviderFactory
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiManager
import com.intellij.psi.SingleRootFileViewProvider
import com.intellij.util.containers.MultiMap
import com.squareup.sqldelight.SqliteCompiler
import com.squareup.sqldelight.Status
import com.squareup.sqldelight.Status.ValidationStatus.Invalid
import com.squareup.sqldelight.model.relativePath
import com.squareup.sqldelight.types.SymbolTable
import com.squareup.sqldelight.validation.SqlDelightValidator
import com.squareup.sqldelight.validation.SqlDelightValidator.Companion.ALL_FILE_DEPENDENCY
import java.io.File

class SqlDelightFileViewProviderFactory : FileViewProviderFactory {
  override fun createFileViewProvider(virtualFile: VirtualFile, language: Language,
      psiManager: PsiManager, eventSystemEnabled: Boolean): FileViewProvider {
    return SqlDelightFileViewProvider(virtualFile, language, psiManager, eventSystemEnabled)
  }
}

internal class SqlDelightFileViewProvider(virtualFile: VirtualFile, language: Language,
    val psiManager: PsiManager, eventSystemEnabled: Boolean) :
    SingleRootFileViewProvider(psiManager, virtualFile, eventSystemEnabled, language) {

  val documentManager = PsiDocumentManager.getInstance(psiManager.project)
  val file: SqliteFile by lazy {
    getPsiInner(SqliteLanguage.INSTANCE) as SqliteFile
  }

  override fun contentsSynchronized() {
    super.contentsSynchronized()
    documentManager.performWhenAllCommitted { generateJavaInterface(true) }
  }

  internal fun generateJavaInterface(fromEdit: Boolean = false) {
    file.parseThen({ parsed ->
      symbolTable += SymbolTable(parsed, virtualFile)
      synchronized(dependencies) {
        dependencies.entrySet().forEach {
          it.value.remove(this)
        }
        file.status = sqldelightValidator.validate(parsed, symbolTable)
        (file.status as Status.ValidationStatus).dependencies.forEach {
          if (it != virtualFile) {
            dependencies.putValue(it, this)
          }
        }
      }

      triggerDependencyRefresh(virtualFile, fromEdit)

      if (file.status is Invalid) return@parseThen

      file.status = sqliteCompiler.write(
          parsed,
          virtualFile.nameWithoutExtension,
          virtualFile.getPlatformSpecificPath().relativePath(parsed),
          ModuleUtil.findModuleForPsiElement(file)!!.moduleFile!!.parent.getPlatformSpecificPath() + File.separatorChar
      )

      if (file.status is Status.Success) {
        val generatedFile = localFileSystem.findFileByIoFile((file.status as Status.Success).generatedFile)
        if (generatedFile == null) {
          Logger.getInstance(SqlDelightFileViewProvider::class.java)
              .error("Failed to find the generated file for ${file.virtualFile.path}, " +
                  "it currently is ${file.generatedFile?.virtualFile?.path}")
          return@parseThen
        }
        generatedFile.refresh(true, false)
        if (generatedFile != file.generatedFile?.virtualFile) {
          file.generatedFile?.delete()
        }
        file.generatedFile = psiManager.findFile(generatedFile)
      }
    }, onError = {
      removeFile(virtualFile, fromEdit)
    })
  }

  companion object {
    private val localFileSystem = LocalFileSystem.getInstance()
    private val sqliteCompiler = SqliteCompiler()
{'fraction_non_alphanumeric': 0.08169350029815146, 'fraction_numerical': 0.0019081693500298152, 'mean_word_length': 4.082424242424242, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3757246', 'n_tokens_mistral': 2284, 'n_tokens_neox': 2254, 'n_words': 525}