All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.psi2ir.PsiSourceManager.kt Maven / Gradle / Ivy

/*
 * Copyright 2010-2016 JetBrains s.r.o.
 *
 * 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 org.jetbrains.kotlin.psi2ir

import com.intellij.psi.PsiElement
import com.intellij.psi.PsiFile
import com.intellij.psi.util.PsiTreeUtil
import org.jetbrains.kotlin.ir.IrElement
import org.jetbrains.kotlin.ir.SourceManager
import org.jetbrains.kotlin.ir.SourceRangeInfo
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.declarations.IrFile
import org.jetbrains.kotlin.ir.util.fileOrNull
import org.jetbrains.kotlin.psi.KtFile
import java.util.*
import kotlin.reflect.KClass

class PsiSourceManager : SourceManager {
    class PsiFileEntry(psiFile: PsiFile) : SourceManager.FileEntry {
        private val psiFileName = psiFile.virtualFile?.path ?: psiFile.name

        override val maxOffset: Int
        private val lineStartOffsets: IntArray
        private val fileViewProvider = psiFile.viewProvider

        init {
            val document = fileViewProvider.document ?: throw AssertionError("No document for $psiFile")
            maxOffset = document.textLength
            lineStartOffsets = IntArray(document.lineCount) { document.getLineStartOffset(it) }
        }

        override fun getLineNumber(offset: Int): Int {
            if (offset < 0) return -1
            val index = lineStartOffsets.binarySearch(offset)
            return if (index >= 0) index else -index - 2
        }

        override fun getColumnNumber(offset: Int): Int {
            if (offset < 0) return -1
            val lineNumber = getLineNumber(offset)
            return offset - lineStartOffsets[lineNumber]
        }

        override fun getSourceRangeInfo(beginOffset: Int, endOffset: Int): SourceRangeInfo =
            SourceRangeInfo(
                filePath = getRecognizableName(),
                startOffset = beginOffset,
                startLineNumber = getLineNumber(beginOffset),
                startColumnNumber = getColumnNumber(beginOffset),
                endOffset = endOffset,
                endLineNumber = getLineNumber(endOffset),
                endColumnNumber = getColumnNumber(endOffset)
            )

        private fun getRecognizableName(): String = psiFileName

        override val name: String get() = getRecognizableName()

        override fun toString(): String = getRecognizableName()

        fun findPsiElement(irElement: IrElement): PsiElement? {
            var psiElement = fileViewProvider.findElementAt(irElement.startOffset)
            while (psiElement != null) {
                if (irElement.endOffset == psiElement.textRange?.endOffset) break
                psiElement = psiElement.parent
            }
            return psiElement
        }

        fun  findPsiElement(irElement: IrElement, psiElementClass: KClass): E? =
            findPsiElement(irElement)?.let {
                PsiTreeUtil.getParentOfType(it, psiElementClass.java, false)
            }
    }

    private val fileEntriesByKtFile = HashMap()
    private val fileEntriesByIrFile = HashMap()
    private val ktFileByFileEntry = HashMap()

    private fun createFileEntry(ktFile: KtFile): PsiFileEntry {
        if (ktFile in fileEntriesByKtFile) error("PsiFileEntry is already created for $ktFile")
        val newEntry = PsiFileEntry(ktFile)
        fileEntriesByKtFile[ktFile] = newEntry
        ktFileByFileEntry[newEntry] = ktFile
        return newEntry
    }

    fun putFileEntry(irFile: IrFile, fileEntry: PsiFileEntry) {
        fileEntriesByIrFile[irFile] = fileEntry
    }

    fun getOrCreateFileEntry(ktFile: KtFile): PsiFileEntry =
        fileEntriesByKtFile.getOrElse(ktFile) { createFileEntry(ktFile) }

    fun getKtFile(fileEntry: PsiFileEntry): KtFile? =
        ktFileByFileEntry[fileEntry]

    fun getKtFile(irFile: IrFile): KtFile? =
        (irFile.fileEntry as? PsiFileEntry)?.let { ktFileByFileEntry[it] }

    override fun getFileEntry(irFile: IrFile): SourceManager.FileEntry? =
        fileEntriesByIrFile[irFile] ?: irFile.fileEntry

    fun  findPsiElement(irElement: IrElement, irFile: IrFile, psiElementClass: KClass): E? {
        val psiFileEntry = fileEntriesByIrFile[irFile] ?: return null
        return psiFileEntry.findPsiElement(irElement, psiElementClass)
    }

    fun findPsiElement(irElement: IrElement, irFile: IrFile): PsiElement? {
        val psiFileEntry = fileEntriesByIrFile[irFile] ?: return null
        return psiFileEntry.findPsiElement(irElement)
    }

    fun  findPsiElement(irElement: IrElement, irDeclaration: IrDeclaration, psiElementClass: KClass): E? {
        val irFile = irDeclaration.fileOrNull ?: return null
        return findPsiElement(irElement, irFile, psiElementClass)
    }

    fun findPsiElement(irElement: IrElement, irDeclaration: IrDeclaration): PsiElement? {
        val irFile = irDeclaration.fileOrNull ?: return null
        return findPsiElement(irElement, irFile)
    }

    fun  findPsiElement(irDeclaration: IrDeclaration, psiElementClass: KClass): E? =
        findPsiElement(irDeclaration, irDeclaration, psiElementClass)

    fun findPsiElement(irDeclaration: IrDeclaration): PsiElement? =
        findPsiElement(irDeclaration, irDeclaration)
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy