Skip to content
This repository has been archived by the owner on Dec 22, 2024. It is now read-only.

Support WSL project/executable #56

Merged
merged 6 commits into from
Jun 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/main/kotlin/com/insyncwithfoo/pyrightls/Path.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.insyncwithfoo.pyrightls

import java.nio.file.InvalidPathException
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.nameWithoutExtension


Expand All @@ -20,6 +21,12 @@ internal fun String.toPathOrNull() =
}


internal fun String.toPathIfItExists(base: Path? = Path.of("")) =
this.toPathOrNull()
?.let { (base?.resolve(it) ?: it).normalize() }
?.takeIf { it.exists() }


internal val Path.isEmpty: Boolean
get() = this.toString() == ""

Expand Down
17 changes: 17 additions & 0 deletions src/main/kotlin/com/insyncwithfoo/pyrightls/Project.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ package com.insyncwithfoo.pyrightls

import com.insyncwithfoo.pyrightls.configuration.AllConfigurations
import com.insyncwithfoo.pyrightls.configuration.ConfigurationService
import com.intellij.execution.wsl.WSLDistribution
import com.intellij.execution.wsl.target.WslTargetEnvironmentConfiguration
import com.intellij.openapi.project.Project
import com.intellij.openapi.projectRoots.Sdk
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.profile.codeInspection.InspectionProjectProfileManager
import com.jetbrains.python.target.PyTargetAwareAdditionalData
import java.nio.file.Path
import kotlin.io.path.listDirectoryEntries

Expand All @@ -14,6 +17,16 @@ private val Project.sdk: Sdk?
get() = ProjectRootManager.getInstance(this).projectSdk


internal val Project.wslDistribution: WSLDistribution?
get() {
val sdk = this.sdk ?: return null
val additionalData = sdk.sdkAdditionalData as? PyTargetAwareAdditionalData ?: return null
val configuration = additionalData.targetEnvironmentConfiguration as? WslTargetEnvironmentConfiguration

return configuration?.distribution
}


internal val Project.path: Path?
get() = basePath?.let { Path.of(it) }

Expand All @@ -30,6 +43,10 @@ internal val Project.pyrightLSConfigurations: AllConfigurations
get() = ConfigurationService.getInstance(this).state


internal val Project.pyrightLSExecutable: Path?
get() = pyrightLSConfigurations.executable?.toPathIfItExists(base = this.path)


internal fun Project.isPyrightLSInspectionEnabled(): Boolean {
val inspectionManager = InspectionProjectProfileManager.getInstance(this)
val profile = inspectionManager.currentProfile
Expand Down
14 changes: 13 additions & 1 deletion src/main/kotlin/com/insyncwithfoo/pyrightls/server/Listener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,30 @@ package com.insyncwithfoo.pyrightls.server

import com.insyncwithfoo.pyrightls.pyrightLSConfigurations
import com.insyncwithfoo.pyrightls.sdkPath
import com.insyncwithfoo.pyrightls.wslDistribution
import com.intellij.openapi.project.Project
import com.intellij.platform.lsp.api.LspServerListener
import com.intellij.platform.lsp.api.LspServerManager
import org.eclipse.lsp4j.DidChangeConfigurationParams
import org.eclipse.lsp4j.InitializeResult


private val Project.osDependentInterpreterPath: String?
get() {
val interpreterPath = sdkPath?.toString()

return when (wslDistribution) {
null -> interpreterPath
else -> interpreterPath?.replace("\\", "/")
}
}


private fun Project.createPyrightLSSettingsObject() = Settings().apply {
val configurations = pyrightLSConfigurations

python.apply {
pythonPath = sdkPath?.toString()
pythonPath = osDependentInterpreterPath

analysis.apply {
logLevel = configurations.logLevel.label
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,37 @@ import com.insyncwithfoo.pyrightls.message
import com.insyncwithfoo.pyrightls.path
import com.insyncwithfoo.pyrightls.pyrightLSConfigurations
import com.insyncwithfoo.pyrightls.server.diagnostics.DiagnosticsSupport
import com.insyncwithfoo.pyrightls.wslDistribution
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.wsl.WSLCommandLineOptions
import com.intellij.execution.wsl.WSLDistribution
import com.intellij.execution.wsl.WslPath
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.BaseProjectDirectories.Companion.getBaseDirectories
import com.intellij.openapi.project.Project
import com.intellij.openapi.project.modules
import com.intellij.openapi.roots.ModuleRootManager
import com.intellij.openapi.util.io.OSAgnosticPathUtil
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.lsp.api.LspServerDescriptor
import java.net.URI
import java.nio.file.Path


private fun makeFileUri(path: String): String {
val (scheme, host, fragment) = Triple("file", "", null)
return URI(scheme, host, path, fragment).toASCIIString()
}


private val Path.isUncPath: Boolean
get() = WslPath.parseWindowsUncPath(this.toString()) != null


private val URI.pathIsAbsoluteDos: Boolean
get() = OSAgnosticPathUtil.isAbsoluteDosPath(Path.of(this).toString())


private fun Project.getModuleSourceRoots(): Collection<VirtualFile> =
modules.flatMap { module ->
ModuleRootManager.getInstance(module).sourceRoots.asIterable()
Expand Down Expand Up @@ -53,11 +73,51 @@ internal class PyrightLSDescriptor(project: Project, private val executable: Pat
override fun isSupportedFile(file: VirtualFile) =
file.extension in configurations.targetedFileExtensionList

override fun createCommandLine() =
GeneralCommandLine(executable.toString(), "--stdio").apply {
withWorkDirectory(project.path?.toString())
withCharset(Charsets.UTF_8)
override fun getFileUri(file: VirtualFile): String {
val wslDistribution = project.wslDistribution

return when {
wslDistribution == null -> super.getFileUri(file)
else -> makeFileUri(wslDistribution.getWslPath(Path.of(file.path))!!)
}
}

override fun findFileByUri(fileUri: String) =
findFileByUri(URI.create(fileUri))

private fun findFileByUri(fileUri: URI): VirtualFile? {
val wslDistribution = project.wslDistribution

val virtualFileUri = when {
wslDistribution == null || fileUri.pathIsAbsoluteDos -> fileUri
else -> Path.of(wslDistribution.getWindowsPath(fileUri.path)).toUri()
}

return super.findFileByUri(virtualFileUri.toString())
}

override fun createCommandLine() = GeneralCommandLine().apply {
val wslDistribution = project.wslDistribution
val projectPath = project.path
val exePath = wslDistribution.getPureLinuxOrWindowsPath(executable)

withExePath(exePath)
addParameter("--stdio")

withCharset(Charsets.UTF_8)
withParentEnvironmentType(GeneralCommandLine.ParentEnvironmentType.CONSOLE)

if (projectPath != null) {
withWorkDirectory(projectPath.toString())
}

wslDistribution?.patchCommandLine(this, project, WSLCommandLineOptions())
}

private fun WSLDistribution?.getPureLinuxOrWindowsPath(path: Path) = when {
this != null && path.isUncPath -> this.getWslPath(path)!!
else -> path.toString()
}

companion object {
val PRESENTABLE_NAME = message("languageServer.representableName")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,16 @@
package com.insyncwithfoo.pyrightls.server

import com.insyncwithfoo.pyrightls.PyrightLSInspection
import com.insyncwithfoo.pyrightls.configuration.ConfigurationService
import com.insyncwithfoo.pyrightls.pyrightLSConfigurations
import com.insyncwithfoo.pyrightls.pyrightLSExecutable
import com.intellij.codeInspection.ex.InspectionToolRegistrar
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.platform.lsp.api.LspServerSupportProvider
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
import java.nio.file.Path
import kotlin.io.path.exists


private val Project.isPyrightLSEnabled: Boolean
private val Project.pyrightLSInspectionIsEnabled: Boolean
get() {
val inspectionProfileManager = ProjectInspectionProfileManager.getInstance(this)
val toolWrapper = InspectionToolRegistrar.getInstance().createTools()
Expand All @@ -22,17 +20,6 @@ private val Project.isPyrightLSEnabled: Boolean
}


private val Project.pyrightLSExecutable: Path?
get() {
val projectPath = Path.of(basePath ?: "")

val configurationService = ConfigurationService.getInstance(this)
val configurations = configurationService.state

return projectPath.resolve(configurations.executable ?: return null).normalize()
}


@Suppress("UnstableApiUsage")
internal class PyrightLSSupportProvider : LspServerSupportProvider {

Expand All @@ -43,8 +30,8 @@ internal class PyrightLSSupportProvider : LspServerSupportProvider {
) {
val fileIsSupported = file.extension in project.pyrightLSConfigurations.targetedFileExtensionList

if (fileIsSupported && project.isPyrightLSEnabled) {
val executable = project.pyrightLSExecutable?.takeIf { it.exists() } ?: return
if (fileIsSupported && project.pyrightLSInspectionIsEnabled) {
val executable = project.pyrightLSExecutable ?: return
val descriptor = PyrightLSDescriptor(project, executable)

serverStarter.ensureServerStarted(descriptor)
Expand Down
Loading