Skip to content

Commit

Permalink
Also pass project interpreter path to executable
Browse files Browse the repository at this point in the history
  • Loading branch information
InSyncWithFoo committed Feb 29, 2024
1 parent 4efd384 commit 83587af
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 109 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@

* Allow using relative path for executables.

### Changed

* The interpreter of the current project is now recognized correctly.


## [0.1.0-mvp.1] - 2024-02-26

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package com.insyncwithfoo.pyright

import com.insyncwithfoo.pyright.configuration.PyrightAllConfigurations
import com.intellij.lang.annotation.AnnotationBuilder
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.Document
import com.intellij.openapi.util.TextRange
import com.intellij.xml.util.XmlStringUtil


private fun Document.getOffset(endpoint: PyrightDiagnosticTextRangeEndpoint) =
getLineStartOffset(endpoint.line) + endpoint.character


private fun Document.getStartEndRange(range: PyrightDiagnosticTextRange): TextRange {
val start = getOffset(range.start)
val end = getOffset(range.end)

return TextRange(start, end)
}


private fun AnnotationHolder.makeBuilderForDiagnostic(diagnostic: PyrightDiagnostic): AnnotationBuilder {
val (_, severity, message, rule) = diagnostic

val tooltip = message.toPreformattedTooltip()
val highlightSeverity = severity.toHighlightSeverity()

val suffix = if (rule != null) " (${rule})" else ""
val suffixedmessage = "$message$suffix"

return newAnnotation(highlightSeverity, suffixedmessage).tooltip(tooltip)
}


private fun PyrightDiagnosticSeverity.toHighlightSeverity() = when (this) {
PyrightDiagnosticSeverity.ERROR -> HighlightSeverity.WARNING
PyrightDiagnosticSeverity.WARNING -> HighlightSeverity.WEAK_WARNING
PyrightDiagnosticSeverity.INFORMATION -> HighlightSeverity.WEAK_WARNING
}


private fun String.toPreformattedTooltip(): String {
val escapedLines = this.split("\n").map {
XmlStringUtil.escapeString(it, true)
}

return escapedLines.joinToString("<br>")
}


private fun AnnotationHolder.applyDiagnostic(diagnostic: PyrightDiagnostic, document: Document) {
val builder = makeBuilderForDiagnostic(diagnostic)
val range = document.getStartEndRange(diagnostic.range)

builder.annotateRange(range)
}


private fun AnnotationBuilder.annotateRange(range: TextRange) {
this.needsUpdateOnTyping().range(range).create()
}


internal class PyrightAnnotationApplier(
private val document: Document,
private val output: PyrightOutput,
private val configurations: PyrightAllConfigurations,
private val holder: AnnotationHolder
) {
fun apply() {
output.generalDiagnostics.forEach {
holder.applyDiagnostic(it, document)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import com.intellij.xml.util.XmlStringUtil
import com.jetbrains.python.PythonLanguage
import com.jetbrains.python.psi.PyFile
import com.jetbrains.python.psi.impl.PyFileImpl
import java.io.File


private val PsiFile.isInjected: Boolean
Expand Down Expand Up @@ -48,65 +47,11 @@ private val PsiFile.isApplicable: Boolean
}


private fun Document.getOffset(endpoint: PyrightDiagnosticTextRangeEndpoint) =
getLineStartOffset(endpoint.line) + endpoint.character


private fun Document.getStartEndRange(range: PyrightDiagnosticTextRange): TextRange {
val start = getOffset(range.start)
val end = getOffset(range.end)

return TextRange(start, end)
}


private fun FileDocumentManager.saveAllUnsavedDocumentsAsIs() {
unsavedDocuments.forEach { saveDocumentAsIs(it) }
}


private fun PyrightDiagnosticSeverity.toHighlightSeverity() = when (this) {
PyrightDiagnosticSeverity.ERROR -> HighlightSeverity.WARNING
PyrightDiagnosticSeverity.WARNING -> HighlightSeverity.WEAK_WARNING
PyrightDiagnosticSeverity.INFORMATION -> HighlightSeverity.WEAK_WARNING
}


private fun String.toPreformattedTooltip(): String {
val escapedLines = this.split("\n").map {
XmlStringUtil.escapeString(it, true)
}

return escapedLines.joinToString("<br>")
}


private fun AnnotationHolder.makeBuilderForDiagnostic(diagnostic: PyrightDiagnostic): AnnotationBuilder {
val (_, severity, message, rule) = diagnostic

val tooltip = message.toPreformattedTooltip()
val highlightSeverity = severity.toHighlightSeverity()

val suffix = if (rule != null) " (${rule})" else ""
val suffixedmessage = "$message$suffix"

return newAnnotation(highlightSeverity, suffixedmessage).tooltip(tooltip)
}


private fun AnnotationHolder.applyDiagnostic(diagnostic: PyrightDiagnostic, document: Document) {
val builder = makeBuilderForDiagnostic(diagnostic)
val range = document.getStartEndRange(diagnostic.range)

builder.annotateRange(range)
}


private fun AnnotationBuilder.annotateRange(range: TextRange) {
this.needsUpdateOnTyping().range(range).create()
}


private fun Project.isPyrightEnabled(file: PsiFile): Boolean {
val profileManager = InspectionProjectProfileManager.getInstance(this)
val profile = profileManager.currentProfile
Expand All @@ -120,12 +65,6 @@ private val Project.pyrightConfigurations: PyrightAllConfigurations
get() = PyrightConfigurationService.getInstance(this).configurations


private fun String.toFileIfItExists(projectPath: String? = null) =
File(this)
.let { if (it.isAbsolute) it else File(projectPath, this).canonicalFile }
.takeIf { it.exists() }


class PyrightExternalAnnotator :
ExternalAnnotator<PyrightExternalAnnotator.Info, PyrightExternalAnnotator.Result>() {

Expand Down Expand Up @@ -162,33 +101,31 @@ class PyrightExternalAnnotator :

override fun doAnnotate(collectedInfo: Info?): Result? {
val (configurations, file) = collectedInfo ?: return null
val projectPath = file.project.basePath ?: return null

val executable = configurations.executable?.toFileIfItExists(projectPath) ?: return null
val target = file.virtualFile.path.toFileIfItExists() ?: return null
val configurationFile = configurations.configurationFile

val output = PyrightRunner(executable, target, configurationFile, projectPath).run()
val command = Command.create(configurations, file) ?: return null
val output = PyrightRunner(command).run() ?: return null

return Result(output)
return Result(configurations, output)
}

override fun apply(file: PsiFile, annotationResult: Result?, holder: AnnotationHolder) {
val (configurations, output) = annotationResult ?: return

val project = file.project
val documentManager = PsiDocumentManager.getInstance(project)
val document = documentManager.getDocument(file) ?: return
val output = annotationResult?.output ?: return

output.generalDiagnostics.forEach {
holder.applyDiagnostic(it, document)
}
PyrightAnnotationApplier(document, output, configurations, holder).apply()
}

data class Info(
val configurations: PyrightAllConfigurations,
val file: PsiFile
)

data class Result(val output: PyrightOutput?)
data class Result(
val configurations: PyrightAllConfigurations,
val output: PyrightOutput
)

}
86 changes: 50 additions & 36 deletions src/main/kotlin/com/insyncwithfoo/pyright/PyrightRunner.kt
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
package com.insyncwithfoo.pyright

import com.insyncwithfoo.pyright.configuration.PyrightAllConfigurations
import com.intellij.execution.RunCanceledByUserException
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.CapturingProcessHandler
import com.intellij.execution.process.ProcessOutput
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.roots.ProjectRootManager
import com.intellij.openapi.ui.Messages
import com.intellij.psi.PsiFile
import com.jetbrains.python.packaging.IndicatedProcessOutputListener
import kotlinx.serialization.json.Json
import kotlinx.serialization.serializer
import org.jetbrains.annotations.SystemDependent
import java.io.File


private fun String.toFileIfItExists(projectPath: String? = null) =
File(this)
.let { if (it.isAbsolute) it else File(projectPath, this).canonicalFile }
.takeIf { it.exists() }


private sealed class PyrightException(message: String) : Exception(message)


Expand Down Expand Up @@ -53,13 +62,28 @@ internal data class Command(
target.toString()
)

private val handler: CapturingProcessHandler
get() = CapturingProcessHandler(getGeneralCommandLine())

private fun getGeneralCommandLine(): GeneralCommandLine =
GeneralCommandLine(fragments)
.withWorkDirectory(projectPath)
.withCharset(Charsets.UTF_8)

private val handler: CapturingProcessHandler
get() = CapturingProcessHandler(getGeneralCommandLine())
private fun runWithIndicator(): ProcessOutput {
val indicator = ProgressManager.getInstance().progressIndicator

return with(handler) {
when {
indicator != null -> {
addProcessListener(IndicatedProcessOutputListener(indicator))
runProcessWithProgressIndicator(indicator)
}

else -> runProcess()
}
}
}

fun run(): String {
val processOutput = runWithIndicator()
Expand All @@ -78,18 +102,29 @@ internal data class Command(
}
}

private fun runWithIndicator(): ProcessOutput {
val indicator = ProgressManager.getInstance().progressIndicator

return with(handler) {
when {
indicator != null -> {
addProcessListener(IndicatedProcessOutputListener(indicator))
runProcessWithProgressIndicator(indicator)
}

else -> runProcess()
}
companion object {
fun create(
configurations: PyrightAllConfigurations,
file: PsiFile
): Command? {
val project = file.project

val projectPath = project.basePath ?: return null

val executable = configurations.executable?.toFileIfItExists(projectPath) ?: return null
val target = file.virtualFile.path.toFileIfItExists() ?: return null
val configurationFile = configurations.configurationFile

val projectSdk = ProjectRootManager.getInstance(project).projectSdk
val pythonExecutable = projectSdk?.homePath ?: return null

val extraArguments = listOf(
"--outputjson",
"--project", configurationFile ?: projectPath,
"--pythonpath", pythonExecutable
)

return Command(executable, target, projectPath, extraArguments)
}
}

Expand All @@ -115,14 +150,7 @@ private class PyrightErrorReporter(private val logger: Logger) {
}


class PyrightRunner(
executable: File,
target: File,
configurationFile: String?,
projectPath: String
) {

private val command = makeCommand(executable, target, configurationFile, projectPath)
internal class PyrightRunner(private val command: Command) {

fun run(): PyrightOutput? {
LOGGER.info("Running: $command")
Expand Down Expand Up @@ -156,20 +184,6 @@ class PyrightRunner(
companion object {
private val LOGGER = Logger.getInstance(PyrightRunner::class.java)
private val ERROR_REPORTER = PyrightErrorReporter(LOGGER)

private fun makeCommand(
executable: File,
target: File,
configurationFile: String?,
projectPath: String
): Command {
val extraArguments = listOf(
"--outputjson",
"--project", configurationFile ?: projectPath
)

return Command(executable, target, projectPath, extraArguments)
}
}

}

0 comments on commit 83587af

Please sign in to comment.