Skip to content

Make a distinction between commands that can or cannot accept files with any extension #3599

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jul 12, 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
24 changes: 21 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,23 @@

### Fixed

## [0.9.7-alpha.3] - 2024-07-11

### Added

* Improve Evince forward/inverse search support, by Tim Klocke
* Support conversion of arguments in \def -> \newcommand quickfix, by @slideclimb
* Add a simple editor for postfix templates, by @slideclimb
* Add support for \ProvidesExpl(Class|File), by @Sirraide
* Support TeX Live docker image
* Formatter support for plain TeX \if-statements
* Index files from the TEXINPUTS variable, for autocompletion

### Fixed

* Fix Evince synchronization after creating a new run configuration, by Tim Klocke
* Fix unresolved file reference for \input commands

## [0.9.7-alpha.2] - 2024-06-09

### Added
Expand Down Expand Up @@ -376,13 +393,14 @@ 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.9.7-alpha.2...HEAD
[0.9.7-alpha.1]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.6...v0.9.7-alpha.1
[Unreleased]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.7-alpha.3...HEAD
[0.9.7-alpha.3]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.7-alpha.2...v0.9.7-alpha.3
[0.9.7-alpha.2]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.7-alpha.1...v0.9.7-alpha.2
[0.9.7-alpha.1]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.6...v0.9.7-alpha.1
[0.9.6]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.5...v0.9.6
[0.9.5]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.4...v0.9.5
[0.9.4]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.3...v0.9.4
[0.9.3]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.3...v0.9.2
[0.9.3]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.2...v0.9.3
[0.9.2]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.1...v0.9.2
[0.9.1]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.9.0...v0.9.1
[0.9.0]: https://github.com/Hannah-Sten/TeXiFy-IDEA/compare/v0.7.33...v0.9.0
Expand Down
1 change: 0 additions & 1 deletion Writerside/topics/Contributing-to-the-source-code.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ Here we add `\newcommandx` to the `regularStrictCommandDefinitions`, which is th

And here You go: it's done (at least for this simple example)

[//]: # (todo: separate topic?)
## Building from source

We assume that git, IntelliJ, java and LaTeX are installed. If not, follow the normal [installation instructions](Installation-guide.md) first.
Expand Down
26 changes: 13 additions & 13 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ plugins {
id("org.jlleitschuh.gradle.ktlint") version "12.1.1"

// Vulnerability scanning
id("org.owasp.dependencycheck") version "9.2.0"
id("org.owasp.dependencycheck") version "10.0.2"

id("org.jetbrains.changelog") version "2.2.0"
id("org.jetbrains.changelog") version "2.2.1"

id("org.jetbrains.grammarkit") version "2022.3.2.2"
}
Expand Down Expand Up @@ -99,24 +99,24 @@ dependencies {
// Unzipping tar.xz/tar.bz2 files on Windows containing dtx files
implementation("org.codehaus.plexus:plexus-component-api:1.0-alpha-33")
implementation("org.codehaus.plexus:plexus-container-default:2.1.1")
implementation("org.codehaus.plexus:plexus-archiver:4.9.2")
implementation("org.codehaus.plexus:plexus-archiver:4.10.0")

// Parsing json
implementation("com.beust:klaxon:5.6")

// Parsing xml
implementation("com.fasterxml.jackson.core:jackson-core:2.17.1")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.1")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.1")
implementation("com.fasterxml.jackson.core:jackson-core:2.17.2")
implementation("com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.17.2")
implementation("com.fasterxml.jackson.module:jackson-module-kotlin:2.17.2")

// Http requests
implementation("io.ktor:ktor-client-core:2.3.12")
implementation("io.ktor:ktor-client-cio:2.3.11")
implementation("io.ktor:ktor-client-cio:2.3.12")
implementation("io.ktor:ktor-client-auth:2.3.12")
implementation("io.ktor:ktor-client-content-negotiation:2.3.11")
implementation("io.ktor:ktor-server-core:2.3.11")
implementation("io.ktor:ktor-server-jetty:2.3.11")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.11")
implementation("io.ktor:ktor-client-content-negotiation:2.3.12")
implementation("io.ktor:ktor-server-core:2.3.12")
implementation("io.ktor:ktor-server-jetty:2.3.12")
implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.12")

// Comparing versions
implementation("org.apache.maven:maven-artifact:4.0.0-beta-3")
Expand All @@ -139,11 +139,11 @@ dependencies {
testRuntimeOnly("org.junit.vintage:junit-vintage-engine:5.10.3")

// Use junit 5 for test cases
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.2")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.10.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.10.2")

// Enable use of the JUnitPlatform Runner within the IDE
testImplementation("org.junit.platform:junit-platform-runner:1.10.2")
testImplementation("org.junit.platform:junit-platform-runner:1.10.3")

testImplementation("io.mockk:mockk:1.13.11")

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.9.7-alpha.2
pluginVersion = 0.9.7-alpha.3

# 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
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ abstract class LatexPathProviderBase : CompletionProvider<CompletionParameters>(
private var resultSet: CompletionResultSet? = null
private var validExtensions: List<String>? = null
private var absolutePathSupport = true
private var supportsAnyExtension = true

companion object {

Expand All @@ -50,6 +51,7 @@ abstract class LatexPathProviderBase : CompletionProvider<CompletionParameters>(
if (parentCommand is RequiredFileArgument) {
validExtensions = parentCommand.supportedExtensions
absolutePathSupport = parentCommand.isAbsolutePathSupported
supportsAnyExtension = parentCommand.supportsAnyExtension
}

var finalCompleteText = expandCommandsOnce(autocompleteText, project = parameters.originalFile.project, file = parameters.originalFile) ?: autocompleteText
Expand Down Expand Up @@ -171,6 +173,7 @@ abstract class LatexPathProviderBase : CompletionProvider<CompletionParameters>(
* add file to autocompletion dialog
*/
private fun addFileCompletion(baseDir: String, foundFile: VirtualFile) {
// Some commands like \input accept any file extension (supportsExtension), but showing only .tex files is probably a better user experience.
if (validExtensions != null) {
if (validExtensions!!.contains(foundFile.extension).not()) return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ import nl.hannahsten.texifyidea.util.files.commandsInFile
import nl.hannahsten.texifyidea.util.files.document
import nl.hannahsten.texifyidea.util.files.findFile
import nl.hannahsten.texifyidea.util.files.findRootFile
import nl.hannahsten.texifyidea.util.replaceString
import nl.hannahsten.texifyidea.util.parser.requiredParameter
import nl.hannahsten.texifyidea.util.replaceString
import java.util.*

/**
Expand All @@ -38,7 +38,7 @@ open class LatexNestedIncludesInspection : TexifyInspectionBase() {
val root = file.findRootFile()

val isInclude = LatexIncludesIndex.Util.getItemsInFileSet(file).any {
it.name == "\\include" && it.requiredParameter(0)?.let { f -> root.findFile(f) } == file
it.name == "\\include" && it.requiredParameter(0)?.let { f -> root.findFile(f, supportsAnyExtension = true) } == file
}

if (!isInclude) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ enum class LatexGenericRegularCommand(
INCLUDEFROM("includefrom", RequiredFolderArgument("absolute path"), RequiredFileArgument("filename", false, false, "tex"), dependency = LatexPackage.IMPORT),
INPUT("input", RequiredFileArgument("sourcefile", true, false, "tex")),
INPUTFROM("inputfrom", RequiredFolderArgument("absolute path"), RequiredFileArgument("filename", false, false, "tex"), dependency = LatexPackage.IMPORT),
INCLUDEGRAPHICS("includegraphics", "key-val-list".asOptional(), RequiredPicturePathArgument("imagefile", isAbsolutePathSupported = true, commaSeparatesArguments = false, FileMagic.graphicFileExtensions), dependency = GRAPHICX),
INCLUDEGRAPHICS("includegraphics", "key-val-list".asOptional(), RequiredPicturePathArgument("imagefile", isAbsolutePathSupported = true, commaSeparatesArguments = false, FileMagic.graphicFileExtensions, supportsAnyExtension = false), dependency = GRAPHICX),
INCLUDEONLY("includeonly", RequiredFileArgument("sourcefile", false, true, "tex")),
INDEXNAME("indexname", "name".asRequired()),
INDEXSPACE("indexspace"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import java.util.regex.Pattern
* @param name
* The name of the required argument.
* @param commaSeparatesArguments True if arguments are separated by commas, for example \command{arg1,arg2}. If false, "arg1,arg2" will be seen as one argument.
* @param supportsAnyExtension True if the command accepts any file extension if provided explicitly.
* @param extensions
* All supported extensions, of which the first extension is the default extension.
* @author Hannah Schellekens
*/
open class RequiredFileArgument(name: String?, open val isAbsolutePathSupported: Boolean = true, open val commaSeparatesArguments: Boolean, vararg extensions: String) : RequiredArgument(name!!, Type.FILE), FileNameMatcher, FileExtensionMatcher {
open class RequiredFileArgument(name: String?, open val isAbsolutePathSupported: Boolean = true, open val commaSeparatesArguments: Boolean, vararg extensions: String, open val supportsAnyExtension: Boolean = true) : RequiredArgument(name!!, Type.FILE), FileNameMatcher, FileExtensionMatcher {

lateinit var supportedExtensions: List<String>
lateinit var defaultExtension: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ package nl.hannahsten.texifyidea.lang.commands
* @author Lukas Heiligenbrunner
*/
// We have to explicitly override isAbsolutePathSupported to avoid a NoSuchMethodError
class RequiredPicturePathArgument(name: String, override val isAbsolutePathSupported: Boolean = true, override val commaSeparatesArguments: Boolean = true, extension: List<String>) : RequiredFileArgument(name, isAbsolutePathSupported, commaSeparatesArguments, *extension.toTypedArray())
class RequiredPicturePathArgument(name: String, override val isAbsolutePathSupported: Boolean = true, override val commaSeparatesArguments: Boolean = true, extension: List<String>, override val supportsAnyExtension: Boolean = true) : RequiredFileArgument(name, isAbsolutePathSupported, commaSeparatesArguments, *extension.toTypedArray(), supportsAnyExtension = supportsAnyExtension)
11 changes: 6 additions & 5 deletions src/nl/hannahsten/texifyidea/reference/InputFileReference.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import nl.hannahsten.texifyidea.util.magic.CommandMagic
/**
* Reference to a file, based on the command and the range of the filename within the command text.
*
* @param defaultExtension Default extension of the command in which this reference is.
* @param defaultExtension Default extension of the command in which this reference is, in case the argument does not have an extension.
*/
class InputFileReference(
element: LatexCommands,
val range: TextRange,
val extensions: List<String>,
val defaultExtension: String
val defaultExtension: String,
val supportsAnyExtension: Boolean,
) : PsiReferenceBase<LatexCommands>(element) {

init {
Expand Down Expand Up @@ -138,15 +139,15 @@ class InputFileReference(
@Suppress("KotlinConstantConditions")
if (targetFile == null) {
for (rootDirectory in rootDirectories) {
targetFile = rootDirectory.findFile(filePath = processedKey, extensions = extensions)
targetFile = rootDirectory.findFile(filePath = processedKey, extensions, supportsAnyExtension)
if (targetFile != null) break
}
}

// Try content roots
if (targetFile == null && LatexSdkUtil.isMiktexAvailable) {
for (moduleRoot in ProjectRootManager.getInstance(element.project).contentSourceRoots) {
targetFile = moduleRoot.findFile(processedKey, extensions)
targetFile = moduleRoot.findFile(processedKey, extensions, supportsAnyExtension)
if (targetFile != null) break
}
}
Expand All @@ -161,7 +162,7 @@ class InputFileReference(
for (searchPath in searchPaths) {
val path = if (!searchPath.endsWith("/")) "$searchPath/" else searchPath
for (rootDirectory in rootDirectories) {
targetFile = rootDirectory.findFile(path + processedKey, extensions)
targetFile = rootDirectory.findFile(path + processedKey, extensions, supportsAnyExtension)
if (targetFile != null) break
}
if (targetFile != null) break
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class BibtexOutputListener(
val logMessage = extractMessage(windowList) ?: return

if (!messageList.contains(logMessage)) {
val file = mainFile?.parent?.findFile(logMessage.fileName ?: mainFile.name)
val file = mainFile?.parent?.findFile(logMessage.fileName ?: mainFile.name, supportsAnyExtension = true)
val messageWithFile = BibtexLogMessage(logMessage.message, logMessage.fileName, logMessage.line, logMessage.type, file)
messageList.add(messageWithFile)
addBibMessageToTree(messageWithFile)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -466,7 +466,7 @@ class LatexRunConfiguration(
.flatMap { command -> command.getRequiredParameters() }
.forEach { filename ->
// Find all the files of this chapter, then check if any of the bibliography commands appears in a file in this chapter
val chapterMainFile = psiFile!!.findFile(filename)
val chapterMainFile = psiFile!!.findFile(filename, supportsAnyExtension = true)
?: return@forEach

val chapterFiles = chapterMainFile.referencedFileSet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class LatexOutputListener(
}

private fun findProjectFileRelativeToMain(fileName: String?): VirtualFile? =
mainFile?.parent?.findFile(fileName ?: mainFile.name, listOf("tex"))
mainFile?.parent?.findFile(fileName ?: mainFile.name, listOf("tex"), supportsAnyExtension = true)

/**
* Reset the tree view and the message list when starting a new run. (latexmk)
Expand Down
29 changes: 10 additions & 19 deletions src/nl/hannahsten/texifyidea/util/files/PsiFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import nl.hannahsten.texifyidea.file.BibtexFileType
import nl.hannahsten.texifyidea.file.ClassFileType
import nl.hannahsten.texifyidea.file.LatexFileType
import nl.hannahsten.texifyidea.file.StyleFileType
import nl.hannahsten.texifyidea.index.LatexCommandsIndex
import nl.hannahsten.texifyidea.index.LatexDefinitionIndex
import nl.hannahsten.texifyidea.index.LatexEnvironmentsIndex
import nl.hannahsten.texifyidea.index.LatexIncludesIndex
Expand Down Expand Up @@ -70,7 +69,7 @@ fun PsiFile.isClassFile() = virtualFile?.extension == "cls"
* Looks up the argument that is in the documentclass command, and if the file is found in the project return it.
* Note this explicitly does not find files elsewhere on the system.
*/
fun PsiFile.documentClassFileInProject() = findFile("${documentClass()}.cls")
fun PsiFile.documentClassFileInProject() = findFile("${documentClass()}.cls", supportsAnyExtension = true)

/**
* If the file has a \documentclass command, return the class name, null otherwise.
Expand Down Expand Up @@ -132,20 +131,16 @@ private fun PsiFile.referencedFiles(files: MutableCollection<PsiFile>, rootFile:
/**
* Looks up a file relative to this file.
*
* @param path
* The path relative to this file.
* @param path The path relative to this file.
* @param extensions Search for extensions in this order
* @param supportsAnyExtension If true, the found file is accepted even if the extension is not in the provided non-empty list.
* @return The found file, or `null` when the file could not be found.
*/
fun PsiFile.findFile(path: String, extensions: List<String>? = null): PsiFile? {
fun PsiFile.findFile(path: String, extensions: List<String>? = null, supportsAnyExtension: Boolean): PsiFile? {
if (project.isDisposed) return null
val directory = containingDirectory?.virtualFile

val file = directory?.findFile(
path,
extensions
?: FileMagic.includeExtensions
)
?: return scanRoots(path, extensions)
val file = directory?.findFile(path, extensions ?: FileMagic.includeExtensions, supportsAnyExtension = supportsAnyExtension) ?: return scanRoots(path, extensions)
val psiFile = PsiManager.getInstance(project).findFile(file)
if (psiFile == null || LatexFileType != psiFile.fileType &&
StyleFileType != psiFile.fileType &&
Expand All @@ -170,10 +165,10 @@ fun PsiFile.findIncludedFile(command: LatexCommands): Set<PsiFile> {
return arguments.filter { it.isNotEmpty() }.mapNotNull {
val extension = FileMagic.automaticExtensions[command.name]
if (extension != null) {
findFile(it, listOf(extension))
findFile(it, listOf(extension), supportsAnyExtension = true)
}
else {
findFile(it)
findFile(it, supportsAnyExtension = true)
}
}.toSet()
}
Expand All @@ -188,11 +183,7 @@ fun PsiFile.findIncludedFile(command: LatexCommands): Set<PsiFile> {
fun PsiFile.scanRoots(path: String, extensions: List<String>? = null): PsiFile? {
val rootManager = ProjectRootManager.getInstance(project)
rootManager.contentSourceRoots.forEach { root ->
val file = root.findFile(
path,
extensions
?: FileMagic.includeExtensions
)
val file = root.findFile(path, extensions ?: FileMagic.includeExtensions, supportsAnyExtension = true)
if (file != null) {
return file.psiFile(project)
}
Expand All @@ -210,7 +201,7 @@ fun PsiFile.document(): Document? = PsiDocumentManager.getInstance(project).getD
* @param commandName
* The name of the command including a backslash, or `null` for all commands.
*
* @see [LatexCommandsIndex.Util.getItems]
* see LatexCommandsIndex.Util.getItems
*/
@JvmOverloads
fun PsiFile.commandsInFile(commandName: String? = null): Collection<LatexCommands> {
Expand Down
2 changes: 1 addition & 1 deletion src/nl/hannahsten/texifyidea/util/files/RootFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ fun PsiFile.findRootFilesWithoutCache(fileset: Set<PsiFile>): Set<PsiFile> {

if (magicComment.contains(DefaultMagicKeys.ROOT)) {
val path = magicComment.value(DefaultMagicKeys.ROOT) ?: ""
this.findFile(path)?.let { roots.add(it) }
this.findFile(path, supportsAnyExtension = true)?.let { roots.add(it) }
}

if (this.isRoot()) {
Expand Down
Loading
Loading