src/main/scala/com/github/gtache/lsp/utils/FileUtils.scala
/**
* Copyright 2017-2018 Guillaume Tâche
*
* 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.github.gtache.lsp.utils
import java.io.File
import java.net.{URI, URL}
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.editor.{Document, Editor}
import com.intellij.openapi.fileEditor.{FileDocumentManager, FileEditorManager, TextEditor}
import com.intellij.openapi.fileTypes.FileType
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.{LocalFileSystem, VirtualFile}
import com.intellij.psi.PsiFile
import org.eclipse.lsp4j.TextDocumentIdentifier
/**
* Various file / uri related methods
*/
object FileUtils {
val os: OS.Value = if (System.getProperty("os.name").toLowerCase.contains("win")) OS.WINDOWS else OS.UNIX
val COLON_ENCODED: String = "%3A"
val SPACE_ENCODED: String = "%20"
val URI_FILE_BEGIN = "file:"
val URI_VALID_FILE_BEGIN: String = "file:///"
val URI_PATH_SEP: Char = '/'
private val LOG: Logger = Logger.getInstance(this.getClass)
def extFromPsiFile(psiFile: PsiFile): String = {
psiFile.getVirtualFile.getExtension
}
def editorFromPsiFile(psiFile: PsiFile): Editor = {
editorFromVirtualFile(psiFile.getVirtualFile, psiFile.getProject)
}
def editorFromUri(uri: String, project: Project): Editor = {
editorFromVirtualFile(virtualFileFromURI(uri), project)
}
def editorFromVirtualFile(file: VirtualFile, project: Project): Editor = {
FileEditorManager.getInstance(project).getAllEditors(file).collectFirst { case t: TextEditor => t.getEditor }.orNull
}
def virtualFileFromURI(uri: String): VirtualFile = {
LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(sanitizeURI(uri))))
}
/**
* Returns a file type given an editor
*
* @param editor The editor
* @return The FileType
*/
def fileTypeFromEditor(editor: Editor): FileType = {
FileDocumentManager.getInstance().getFile(editor.getDocument).getFileType
}
/**
* Transforms an editor (Document) identifier to an LSP identifier
*
* @param editor The editor
* @return The TextDocumentIdentifier
*/
def editorToLSPIdentifier(editor: Editor): TextDocumentIdentifier = {
new TextDocumentIdentifier(editorToURIString(editor))
}
/**
* Returns the URI string corresponding to an Editor (Document)
*
* @param editor The Editor
* @return The URI
*/
def editorToURIString(editor: Editor): String = {
sanitizeURI(VFSToURI(FileDocumentManager.getInstance().getFile(editor.getDocument)))
}
/**
* Transforms an URI string into a VFS file
*
* @param uri The uri
* @return The virtual file
*/
def URIToVFS(uri: String): VirtualFile = {
val res = LocalFileSystem.getInstance().findFileByIoFile(new File(new URI(sanitizeURI(uri))))
res
}
/**
* Returns the project base dir uri given an editor
*
* @param editor The editor
* @return The project whose the editor belongs
*/
def editorToProjectFolderUri(editor: Editor): String = {
pathToUri(editorToProjectFolderPath(editor))
}
def editorToProjectFolderPath(editor: Editor): String = {
new File(editor.getProject.getBasePath).getAbsolutePath
}
/**
* Transforms a path into an URI string
*
* @param path The path
* @return The uri
*/
def pathToUri(path: String): String = {
sanitizeURI(new File(path.replace(" ", SPACE_ENCODED)).toURI.toString)
}
def documentToUri(document: Document): String = {
sanitizeURI(VFSToURI(FileDocumentManager.getInstance().getFile(document)))
}
/**
* Returns the URI string corresponding to a VirtualFileSystem file
*
* @param file The file
* @return the URI
*/
def VFSToURI(file: VirtualFile): String = {
try {
val uri = sanitizeURI(new URL(file.getUrl.replace(" ", SPACE_ENCODED)).toURI.toString)
uri
} catch {
case e: Exception =>
LOG.warn(e)
null
}
}
/**
* Fixes common problems in uri, mainly related to Windows
*
* @param uri The uri to sanitize
* @return The sanitized uri
*/
def sanitizeURI(uri: String): String = {
val reconstructed: StringBuilder = StringBuilder.newBuilder
var uriCp = new String(uri).replace(" ", SPACE_ENCODED) //Don't trust servers
if (!uri.startsWith(URI_FILE_BEGIN)) {
LOG.warn("Malformed uri : " + uri)
uri //Probably not an uri
} else {
uriCp = uriCp.drop(URI_FILE_BEGIN.length).dropWhile(c => c == URI_PATH_SEP)
reconstructed.append(URI_VALID_FILE_BEGIN)
if (os == OS.UNIX) {
reconstructed.append(uriCp).toString()
} else {
reconstructed.append(uriCp.takeWhile(c => c != URI_PATH_SEP))
val driveLetter = reconstructed.charAt(URI_VALID_FILE_BEGIN.length)
if (driveLetter.isLower) {
reconstructed.setCharAt(URI_VALID_FILE_BEGIN.length, driveLetter.toUpper)
}
if (reconstructed.endsWith(COLON_ENCODED)) {
reconstructed.dropRight(3)
}
if (!reconstructed.endsWith(":")) {
reconstructed.append(":")
}
reconstructed.append(uriCp.dropWhile(c => c != URI_PATH_SEP)).toString()
}
}
}
/**
* Object representing the OS type (Windows or Unix)
*/
object OS extends Enumeration {
type OS = Value
val WINDOWS, UNIX = Value
}
}