Skip to content

Commit

Permalink
Allow configuring targeted file extensions (#35, #38)
Browse files Browse the repository at this point in the history
  • Loading branch information
InSyncWithFoo committed May 28, 2024
1 parent f7203a3 commit d5c69db
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package com.insyncwithfoo.pyrightls.configuration

import com.insyncwithfoo.pyrightls.configuration.application.LogLevel
import com.insyncwithfoo.pyrightls.configuration.project.DelimitedFileExtensionList
import com.insyncwithfoo.pyrightls.configuration.project.FileExtension
import com.insyncwithfoo.pyrightls.configuration.project.WorkspaceFolders
import com.insyncwithfoo.pyrightls.configuration.project.split
import org.jetbrains.annotations.SystemDependent
import com.insyncwithfoo.pyrightls.configuration.application.Configurations as ApplicationConfigurations
import com.insyncwithfoo.pyrightls.configuration.project.Configurations as ProjectConfigurations
Expand All @@ -26,7 +29,8 @@ internal infix fun ApplicationConfigurations.mergeWith(other: ProjectConfigurati

projectExecutable = other.projectExecutable,
autoSuggestExecutable = other.autoSuggestExecutable,
workspaceFolders = other.workspaceFolders
workspaceFolders = other.workspaceFolders,
targetedFileExtensions = other.targetedFileExtensions
)


Expand All @@ -48,11 +52,17 @@ internal data class AllConfigurations(

val projectExecutable: @SystemDependent String?,
val autoSuggestExecutable: Boolean,
val workspaceFolders: WorkspaceFolders
val workspaceFolders: WorkspaceFolders,
val targetedFileExtensions: DelimitedFileExtensionList?
) {

val executable: @SystemDependent String?
get() = when {
alwaysUseGlobal -> globalExecutable
else -> projectExecutable ?: globalExecutable
}

val targetedFileExtensionList: List<FileExtension>
get() = targetedFileExtensions.orEmpty().split()

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ internal fun <T : JComponent> Cell<T>.ensureComment() = this.apply {
}


internal fun Row.secondColumnPathInput() = textFieldWithBrowseButton().apply {
internal fun <T : JComponent> Cell<T>.asSecondColumnInput() = this.apply {
ensureComment()

gap(RightGap.SMALL)
Expand All @@ -39,6 +39,9 @@ internal fun Row.secondColumnPathInput() = textFieldWithBrowseButton().apply {
}


internal fun Row.secondColumnPathInput() = textFieldWithBrowseButton().asSecondColumnInput()


internal fun <T : TextFieldWithBrowseButton> Cell<T>.bindText(property: KMutableProperty0<String?>) =
bindText({ property.get().orEmpty() }, property::set)

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.insyncwithfoo.pyrightls.configuration.project

import com.insyncwithfoo.pyrightls.configuration.Hint
import com.insyncwithfoo.pyrightls.configuration.asSecondColumnInput
import com.insyncwithfoo.pyrightls.configuration.bindText
import com.insyncwithfoo.pyrightls.configuration.displayPathHint
import com.insyncwithfoo.pyrightls.configuration.executablePathResolvingHint
Expand All @@ -14,10 +15,12 @@ import com.intellij.openapi.ui.ComboBox
import com.intellij.openapi.ui.TextFieldWithBrowseButton
import com.intellij.ui.SimpleListCellRenderer
import com.intellij.ui.components.JBCheckBox
import com.intellij.ui.components.fields.ExpandableTextField
import com.intellij.ui.dsl.builder.Cell
import com.intellij.ui.dsl.builder.Row
import com.intellij.ui.dsl.builder.bindItem
import com.intellij.ui.dsl.builder.bindSelected
import com.intellij.ui.dsl.builder.bindText
import com.intellij.ui.dsl.builder.panel
import com.intellij.ui.dsl.builder.toNullableProperty

Expand All @@ -43,6 +46,14 @@ private fun Row.makeWorkspaceFoldersInput(block: Cell<ComboBox<WorkspaceFolders>
}


private fun Row.makeTargetedFileExtensionsInput(block: Cell<ExpandableTextField>.() -> Unit) = run {
val parser = DelimitedFileExtensionList::split
val joiner = List<FileExtension>::join

expandableTextField(parser, joiner).asSecondColumnInput().apply(block)
}


internal fun Configurable.configurationPanel(state: Configurations) = panel {
// FIXME: The onInput() callbacks are too deeply nested.

Expand All @@ -66,6 +77,14 @@ internal fun Configurable.configurationPanel(state: Configurations) = panel {

@Suppress("DialogTitleCapitalization")
group(message("configurations.group.languageServer")) {
row(message("configurations.targetedFileExtensions.label")) {
makeTargetedFileExtensionsInput {
bindText(
{ state.targetedFileExtensions.orEmpty().deduplicate() },
{ state.targetedFileExtensions = it.deduplicate() }
)
}
}
row(message("configurations.workspaceFolders.label")) {
makeWorkspaceFoldersInput { bindItem(state::workspaceFolders.toNullableProperty()) }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,34 @@ import com.insyncwithfoo.pyrightls.message
import com.intellij.openapi.components.BaseState


private const val FILE_EXTENSIONS_DELIMITER = "|"


internal typealias FileExtension = String
internal typealias DelimitedFileExtensionList = String


private fun FileExtension.normalize() = this.trim().lowercase()


private fun List<FileExtension>.toSetOfNormalized(): Set<FileExtension> =
this.mapNotNullTo(mutableSetOf()) { extension ->
extension.normalize().takeIf { it.isNotEmpty() }
}


internal fun DelimitedFileExtensionList.split(): MutableList<FileExtension> =
this.split(FILE_EXTENSIONS_DELIMITER).toSetOfNormalized().toMutableList()


internal fun List<FileExtension>.join() =
this.toSetOfNormalized().joinToString(FILE_EXTENSIONS_DELIMITER)


internal fun DelimitedFileExtensionList.deduplicate() =
this.split().join()


internal enum class WorkspaceFolders(val label: String) {
PROJECT_BASE(message("configurations.workspaceFolders.projectBase")),
SOURCE_ROOTS(message("configurations.workspaceFolders.sourceRoots"));
Expand All @@ -14,4 +42,5 @@ internal class Configurations : BaseState() {
var projectExecutable by string(null)
var autoSuggestExecutable by property(true)
var workspaceFolders by enum(WorkspaceFolders.PROJECT_BASE)
var targetedFileExtensions by string(listOf("py").join())
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ internal class PyrightLSDescriptor(project: Project, private val executable: Pat
LOGGER.info(configurations.toString())
}

override fun isSupportedFile(file: VirtualFile) = file.isSupported
override fun isSupportedFile(file: VirtualFile) =
file.extension in configurations.targetedFileExtensionList

override fun createCommandLine() =
GeneralCommandLine(executable.toString(), "--stdio").apply {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.insyncwithfoo.pyrightls.server

import com.insyncwithfoo.pyrightls.PyrightLSInspection
import com.insyncwithfoo.pyrightls.configuration.ConfigurationService
import com.insyncwithfoo.pyrightls.pyrightLSConfigurations
import com.intellij.codeInspection.ex.InspectionToolRegistrar
import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
Expand Down Expand Up @@ -32,10 +33,6 @@ private val Project.pyrightLSExecutable: Path?
}


internal val VirtualFile.isSupported: Boolean
get() = extension == "py"


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

Expand All @@ -44,7 +41,9 @@ internal class PyrightLSSupportProvider : LspServerSupportProvider {
file: VirtualFile,
serverStarter: LspServerSupportProvider.LspServerStarter
) {
if (file.isSupported && project.isPyrightLSEnabled) {
val fileIsSupported = file.extension in project.pyrightLSConfigurations.targetedFileExtensionList

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

Expand Down
2 changes: 2 additions & 0 deletions src/main/resources/messages/pyrightls.properties
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ configurations.workspaceFolders.label = Workspace folders:
configurations.workspaceFolders.projectBase = Project base directories
configurations.workspaceFolders.sourceRoots = Source roots

configurations.targetedFileExtensions.label = Targeted file extensions:

configurations.hint.fileFound = File found
configurations.hint.fileNotExecutable = File is not executable
configurations.hint.fileNotFound = No file found at given path
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,14 @@ class ConfigurationFieldsTest : TestCase() {
fun `test defaults - project`() {
val configurations = ProjectConfigurations()

assertEquals(3, projectFields().size)
assertEquals(4, projectFields().size)

configurations.run {
assertEquals(null, projectExecutable)
assertEquals(true, autoSuggestExecutable)

assertEquals(WorkspaceFolders.PROJECT_BASE, workspaceFolders)
assertEquals("py", targetedFileExtensions)
}
}

Expand Down

0 comments on commit d5c69db

Please sign in to comment.