Skip to content

Commit

Permalink
Merge pull request #3118 from Hannah-Sten/documentation-provider
Browse files Browse the repository at this point in the history
Refactor command execution in documentation provider
  • Loading branch information
PHPirates authored Jul 1, 2023
2 parents 1ba6f12 + e5be6ec commit 46c7514
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 53 deletions.
16 changes: 15 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@

### Fixed

## [0.7.31-alpha.2] - 2023-06-19

### Added
* Add text parameters of commands to grammar checked text
* Improved parsing of optional parameters
* Internal improvements (#3092, #3102)

### Fixed
* Improve code of the documentation provider
* Fix double highlighting of inline and display math, by @jojo2357
* Fix false positive grammar error when a sentence ends with a closing brace
* Fix false positive grammar error when a newline follows a command between sentences

## [0.7.31-alpha.1] - 2023-06-18

### Added
Expand Down Expand Up @@ -150,8 +163,9 @@ Thanks to @jojo2357 and @MisterDeenis for contributing to this release!
* Fix some intention previews. ([#2796](https://github.com/Hannah-Sten/TeXiFy-IDEA/issues/2796))
* Other small bug fixes and improvements. ([#2776](https://github.com/Hannah-Sten/TeXiFy-IDEA/issues/2776), [#2774](https://github.com/Hannah-Sten/TeXiFy-IDEA/issues/2774), [#2765](https://github.com/Hannah-Sten/TeXiFy-IDEA/issues/2765)-[#2773](https://github.com/Hannah-Sten/TeXiFy-IDEA/issues/2773))

[Unreleased]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.7.31-alpha.1...HEAD
[Unreleased]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.7.31-alpha.2...HEAD
[0.7.31-alpha.1]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.7.30...v0.7.31-alpha.1
[0.7.31-alpha.2]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.7.31-alpha.1...v0.7.31-alpha.2
[0.7.30]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.7.29...v0.7.30
[0.7.29]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.7.28...v0.7.29
[0.7.28]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.7.27...v0.7.28
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
pluginVersion = 0.7.31-alpha.1
pluginVersion = 0.7.31-alpha.2

# Info about build ranges: https://www.jetbrains.org/intellij/sdk/docs/basics/getting_started/build_number_ranges.html
# Note that an xyz branch corresponds to version 20xy.z and a since build of xyz.*
Expand Down
5 changes: 5 additions & 0 deletions src/nl/hannahsten/texifyidea/TeXception.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ open class TeXception : RuntimeException {
* Represents a signal that a request to one of the remote libraries failed.
*/
data class RemoteLibraryRequestFailure(val libraryName: String, val response: HttpResponse)

/**
* A system command/program that failed to run successfully.
*/
data class CommandFailure(val output: String, val exitCode: Int)
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package nl.hannahsten.texifyidea.documentation

import arrow.core.Either
import arrow.core.raise.either
import com.intellij.lang.documentation.DocumentationProvider
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiManager
import nl.hannahsten.texifyidea.CommandFailure
import nl.hannahsten.texifyidea.lang.Dependend
import nl.hannahsten.texifyidea.lang.Described
import nl.hannahsten.texifyidea.lang.Environment
Expand All @@ -11,10 +14,10 @@ import nl.hannahsten.texifyidea.lang.commands.LatexCommand
import nl.hannahsten.texifyidea.psi.*
import nl.hannahsten.texifyidea.settings.sdk.TexliveSdk
import nl.hannahsten.texifyidea.util.SystemEnvironment
import nl.hannahsten.texifyidea.util.containsAny
import nl.hannahsten.texifyidea.util.magic.CommandMagic
import nl.hannahsten.texifyidea.util.parser.*
import java.io.IOException
import java.io.InputStream
import nl.hannahsten.texifyidea.util.runCommandWithExitCode

/**
* @author Sten Wessel
Expand Down Expand Up @@ -46,17 +49,21 @@ class LatexDocumentationProvider : DocumentationProvider {
}

override fun getUrlFor(element: PsiElement?, originalElement: PsiElement?): List<String>? {
return getUrlForElement(element).getOrNull()
}

private fun getUrlForElement(element: PsiElement?): Either<CommandFailure, List<String>?> = either {
if (element !is LatexCommands) {
return null
return@either null
}

val command = LatexCommand.lookup(element)

if (command.isNullOrEmpty()) return null
if (command.isNullOrEmpty()) return@either null

// Special case for package inclusion commands
if (isPackageInclusionCommand(element)) {
val pkg = element.getRequiredParameters().getOrNull(0) ?: return null
val pkg = element.getRequiredParameters().getOrNull(0) ?: return@either null
return runTexdoc(LatexPackage(pkg))
}

Expand Down Expand Up @@ -105,23 +112,20 @@ class LatexDocumentationProvider : DocumentationProvider {

// Link to package docs
originalElement ?: return null
val urls = if (lookup is Dependend && !isPackageInclusionCommand(element)) runTexdoc((lookup as? Dependend)?.dependency) else getUrlFor(element, originalElement)

if (docString?.isNotBlank() == true && !urls.isNullOrEmpty()) {
docString += "<br/>"
val urlsMaybe = if (lookup is Dependend && !isPackageInclusionCommand(element)) runTexdoc((lookup as? Dependend)?.dependency) else getUrlForElement(
element
)
val urlsText = urlsMaybe.fold(
{ it.output },
{ urls -> urls?.joinToString(separator = "<br>") { "<a href=\"file:///$it\">$it</a>" } }
)

// Add a line break if necessary
if (docString?.isNotBlank() == true && urlsText?.isNotBlank() == true) {
docString += "<br>"
}

if (urls != null) {
for (url in urls) {
// Propagate the warning
docString += if (url.contains("install the texdoc package")) {
url
}
else {
"<a href=\"file:///$url\">$url</a><br/>"
}
}
}
docString += urlsText

if (element.previousSiblingIgnoreWhitespace() == null) {
lookup = null
Expand Down Expand Up @@ -162,53 +166,50 @@ class LatexDocumentationProvider : DocumentationProvider {
psiElement: PsiElement?
): PsiElement? = null

private fun runTexdoc(pkg: LatexPackage?): List<String> {
if (pkg == null) return emptyList()
/**
* Find list of documentation urls.
*/
private fun runTexdoc(pkg: LatexPackage?): Either<CommandFailure, List<String>> = either {
if (pkg == null) return@either emptyList()

// base/lt... files are documented in source2e.pdf
val name = if (pkg.fileName.isBlank() || (pkg.name.isBlank() && pkg.fileName.startsWith("lt"))) "source2e" else pkg.fileName

val stream: InputStream
try {
val command = if (TexliveSdk.isAvailable) {
// -M to avoid texdoc asking to choose from the list
val command = if (TexliveSdk.isAvailable) {
"texdoc -l -M $name"
}
else {
if (SystemEnvironment.isAvailable("texdoc")) {
// texdoc on MiKTeX is just a shortcut for mthelp which doesn't need the -M option
"texdoc -l $name"
}
else {
// In some cases, texdoc may not be available but mthelp is
"mthelp -l $name"
}
}
stream = Runtime.getRuntime().exec(command).inputStream
listOf("texdoc", "-l", "-M", name)
}
catch (e: IOException) {
return if (e.message?.contains("Cannot run program \"texdoc\"") == true) {
listOf("<br><i>Tip: install the texdoc package to get links to package documentation here</i>")
else {
if (SystemEnvironment.isAvailable("texdoc")) {
// texdoc on MiKTeX is just a shortcut for mthelp which doesn't need the -M option
listOf("texdoc", "-l", name)
}
else {
emptyList()
// In some cases, texdoc may not be available but mthelp is
listOf("mthelp", "-l", name)
}
}
val (output, exitCode) = runCommandWithExitCode(*command.toTypedArray(), returnExceptionMessage = true)
if (exitCode != 0 || output?.isNotBlank() != true) {
raise(CommandFailure(output ?: "", exitCode))
}

val lines = stream.bufferedReader().use { it.readLines() }
// Assume that if there are no path delimiters in the output, the output is some sort of error message (could be in any language)
val validLines = output.split("\n").filter { it.containsAny(setOf("\\", "/")) }

return if (lines.getOrNull(0)?.endsWith("could not be found.") == true) {
emptyList()
if (validLines.isEmpty()) {
raise(CommandFailure(output, exitCode))
}
else {

validLines.toSet().mapNotNull {
// Do some guesswork about the format
if (TexliveSdk.isAvailable) {
lines.mapNotNull {
// Line consists of: name version path optional file description
it.split("\t").getOrNull(2)
}
// Line consists of: name version path optional file description
it.split("\t").getOrNull(2)
}
else {
lines
// mthelp seems to just output the paths itself
it
}
}
}
Expand Down

0 comments on commit 46c7514

Please sign in to comment.