diff --git a/CHANGELOG.md b/CHANGELOG.md index ffcba928a..36f4e9af4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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 diff --git a/gradle.properties b/gradle.properties index 49e2d7028..ce5d3da7b 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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.* diff --git a/src/nl/hannahsten/texifyidea/TeXception.kt b/src/nl/hannahsten/texifyidea/TeXception.kt index 577f4f57f..546904e41 100644 --- a/src/nl/hannahsten/texifyidea/TeXception.kt +++ b/src/nl/hannahsten/texifyidea/TeXception.kt @@ -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) diff --git a/src/nl/hannahsten/texifyidea/documentation/LatexDocumentationProvider.kt b/src/nl/hannahsten/texifyidea/documentation/LatexDocumentationProvider.kt index 4d9f339cc..6936e2371 100644 --- a/src/nl/hannahsten/texifyidea/documentation/LatexDocumentationProvider.kt +++ b/src/nl/hannahsten/texifyidea/documentation/LatexDocumentationProvider.kt @@ -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 @@ -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 @@ -46,17 +49,21 @@ class LatexDocumentationProvider : DocumentationProvider { } override fun getUrlFor(element: PsiElement?, originalElement: PsiElement?): List? { + return getUrlForElement(element).getOrNull() + } + + private fun getUrlForElement(element: PsiElement?): Either?> = 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)) } @@ -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 += "
" + 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 = "
") { "$it" } } + ) + + // Add a line break if necessary + if (docString?.isNotBlank() == true && urlsText?.isNotBlank() == true) { + docString += "
" } - if (urls != null) { - for (url in urls) { - // Propagate the warning - docString += if (url.contains("install the texdoc package")) { - url - } - else { - "$url
" - } - } - } + docString += urlsText if (element.previousSiblingIgnoreWhitespace() == null) { lookup = null @@ -162,53 +166,50 @@ class LatexDocumentationProvider : DocumentationProvider { psiElement: PsiElement? ): PsiElement? = null - private fun runTexdoc(pkg: LatexPackage?): List { - if (pkg == null) return emptyList() + /** + * Find list of documentation urls. + */ + private fun runTexdoc(pkg: LatexPackage?): Either> = 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("
Tip: install the texdoc package to get links to package documentation here") + 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 } } }