-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
GH-4 Working on the workspace configurations usage
Signed-off-by: Uladzislau <[email protected]>
- Loading branch information
Showing
12 changed files
with
774 additions
and
71 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright (c) 2024 IBA Group. | ||
* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* IBA Group | ||
* Zowe Community | ||
*/ | ||
|
||
package org.zowe.cobol | ||
|
||
/** | ||
* Various config section options that could be found across configuration files | ||
* @property section the section string representation | ||
*/ | ||
enum class Sections(val section: String) { | ||
DIALECTS_SECTION("cobol-lsp.dialects"), | ||
DIALECT_REGISTRY("cobol-lsp.dialect.registry"), | ||
DIALECT_LIBS("cobol-lsp.dialect.libs"), | ||
CPY_SECTION("cobol-lsp.cpy-manager"), | ||
CPY_LOCAL_PATH("cobol-lsp.cpy-manager.paths-local"), | ||
CPY_EXTENSIONS("cobol-lsp.cpy-manager.copybook-extensions"), | ||
CPY_FILE_ENCODING("cobol-lsp.cpy-manager.copybook-file-encoding"), | ||
SQL_BACKEND("cobol-lsp.target-sql-backend"), | ||
COMPILER_OPTIONS("cobol-lsp.compiler.options"), | ||
LOGGIN_LEVEL_ROOT("cobol-lsp.logging.level.root"), | ||
LOCALE("cobol-lsp.locale"), | ||
COBOL_PROGRAM_LAYOUT("cobol-lsp.cobol.program.layout"), | ||
SUBROUTINE_LOCAL_PATH("cobol-lsp.subroutine-manager.paths-local"), | ||
CICS_TRANSLATOR("cobol-lsp.cics.translator"), | ||
UNRECOGNIZED("unrecognized-section"); | ||
|
||
companion object { | ||
operator fun invoke(section: String): Sections { | ||
return entries.find { it.section == section } ?: UNRECOGNIZED | ||
} | ||
} | ||
|
||
override fun toString(): String { | ||
return section | ||
} | ||
} |
70 changes: 70 additions & 0 deletions
70
src/main/kotlin/org/zowe/cobol/VSCodeSettingsAdapterService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
/* | ||
* Copyright (c) 2024 IBA Group. | ||
* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* IBA Group | ||
* Zowe Community | ||
*/ | ||
|
||
package org.zowe.cobol | ||
|
||
import com.google.gson.JsonParser | ||
import com.intellij.openapi.components.Service | ||
import com.intellij.openapi.components.service | ||
import java.io.File | ||
import java.io.FileNotFoundException | ||
import java.io.FileReader | ||
import java.nio.file.Path | ||
|
||
/** VS Code settings adapter service. Needed to recognize configs in .vscode folder */ | ||
@Service | ||
class VSCodeSettingsAdapterService { | ||
|
||
companion object { | ||
fun getService(): VSCodeSettingsAdapterService = service() | ||
} | ||
|
||
/** | ||
* Read the .vscode/settings.json file section | ||
* @param workspaceFolder the workspace folder path to search for the file in | ||
* @param readBlock the function to handle the read section | ||
* @return the result of the read section handling or error if file is not found | ||
*/ | ||
private fun <T : Any> readConfigFileSection(workspaceFolder: Path, readBlock: (FileReader) -> T?): T? { | ||
return try { | ||
val settingsPath = workspaceFolder.resolve(".vscode").resolve("settings.json") | ||
val settingsFile = File(settingsPath.toUri()) | ||
FileReader(settingsFile).use(readBlock) | ||
} catch (e: FileNotFoundException) { | ||
// TODO: logger | ||
// println("settings.json file not found") | ||
null | ||
} | ||
} | ||
|
||
/** | ||
* Read the .vscode/settings.json file section and expect to return a list of strings as a result | ||
* @param workspaceFolder the workspace folder path to search for the file in | ||
* @param section the section to read | ||
* @return the list of strings, read from the section, or empty list on failure or if there are no items | ||
*/ | ||
fun getListOfStringsConfiguration(workspaceFolder: Path, section: Sections): List<String> { | ||
return readConfigFileSection(workspaceFolder) { settingsJsonReader -> | ||
val settingsJsonElement = JsonParser.parseReader(settingsJsonReader) | ||
if (settingsJsonElement.isJsonObject) { | ||
val settingsJsonObject = settingsJsonElement.asJsonObject | ||
val settingsDialectJsonArray = settingsJsonObject.get(section.toString())?.asJsonArray | ||
settingsDialectJsonArray?.map { it.asString } ?: listOf() | ||
} else { | ||
listOf() | ||
} | ||
} ?: listOf() | ||
} | ||
|
||
} |
223 changes: 223 additions & 0 deletions
223
src/main/kotlin/org/zowe/cobol/lsp/CobolConfigsRecognitionService.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,223 @@ | ||
/* | ||
* Copyright (c) 2024 IBA Group. | ||
* | ||
* This program and the accompanying materials are made available under the terms of the | ||
* Eclipse Public License v2.0 which accompanies this distribution, and is available at | ||
* https://www.eclipse.org/legal/epl-v20.html | ||
* | ||
* SPDX-License-Identifier: EPL-2.0 | ||
* | ||
* Contributors: | ||
* IBA Group | ||
* Zowe Community | ||
*/ | ||
|
||
package org.zowe.cobol.lsp | ||
|
||
import com.google.gson.* | ||
import com.intellij.openapi.components.Service | ||
import com.intellij.openapi.components.service | ||
import org.eclipse.lsp4j.ConfigurationItem | ||
import org.zowe.cobol.findFilesInWorkspace | ||
import java.io.File | ||
import java.lang.reflect.Type | ||
import java.net.URI | ||
import java.nio.file.* | ||
|
||
private const val PROCESSOR_GROUP_FOLDER = ".cobolplugin" | ||
private const val PROCESSOR_GROUP_PGM = "pgm_conf.json" | ||
private const val PROCESSOR_GROUP_PROC = "proc_grps.json" | ||
private val COBOL_EXTENSTIONS = listOf(".CBL", ".COB", ".COBOL") | ||
|
||
/** Service to handle COBOL-related configs */ | ||
@Service | ||
class CobolConfigsRecognitionService { | ||
|
||
companion object { | ||
fun getService(): CobolConfigsRecognitionService = service() | ||
} | ||
|
||
/** | ||
* Preprocessor description item class | ||
* @property name the name of the preprocessor | ||
* @property libs the optional preprocessor libs | ||
*/ | ||
private data class PreprocessorItem(val name: String, val libs: List<String> = emptyList()) | ||
|
||
/** | ||
* Processor group class to represent processor groups | ||
* @property name the name of the processor group | ||
* @property libs the optional libs to be used | ||
* @property preprocessor the optional preprocessors list to be used with the processor group | ||
*/ | ||
private data class ProcessorGroup( | ||
val name: String, | ||
val libs: List<String> = emptyList(), | ||
val preprocessor: List<PreprocessorItem> = emptyList() | ||
) | ||
|
||
/** | ||
* Processor groups container class | ||
* @property pgroups the processor groups list | ||
*/ | ||
private data class PGroupContainer(val pgroups: List<ProcessorGroup>) | ||
|
||
/** Class to provide a deserialization mechanism for processor groups */ | ||
private class ProcessorGroupDeserializer : JsonDeserializer<ProcessorGroup> { | ||
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ProcessorGroup { | ||
val jsonObject = json.asJsonObject | ||
|
||
val name = jsonObject.get("name").asString | ||
val libs = jsonObject.getAsJsonArray("libs")?.map { it.asString } ?: emptyList() | ||
|
||
val preprocessor = when { | ||
jsonObject.has("preprocessor") -> { | ||
val preprocessorElement = jsonObject.get("preprocessor") | ||
if (preprocessorElement.isJsonArray) { | ||
preprocessorElement.asJsonArray.map { parsePreprocessorItem(it) } | ||
} else { | ||
listOf(parsePreprocessorItem(preprocessorElement)) | ||
} | ||
} | ||
else -> emptyList() | ||
} | ||
|
||
return ProcessorGroup(name, libs, preprocessor) | ||
} | ||
|
||
/** | ||
* Parse preprocessor item from JSON. It is either a string or an object | ||
* @param element the [JsonElement] to parse preprocessor item from | ||
* @return parsed and formed [PreprocessorItem] instance | ||
*/ | ||
private fun parsePreprocessorItem(element: JsonElement): PreprocessorItem { | ||
return if (element.isJsonPrimitive && element.asJsonPrimitive.isString) { | ||
PreprocessorItem(name = element.asString) | ||
} else { | ||
val jsonObject = element.asJsonObject | ||
val name = jsonObject.get("name").asString | ||
val libs = jsonObject.getAsJsonArray("libs")?.map { it.asString } ?: emptyList() | ||
PreprocessorItem(name = name, libs = libs) | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Program config item class to represent a single COBOL program configuration | ||
* @param program a name or a wildcard of a file to be considered as the main program (open code) | ||
* @param pgroup name of a processor group as defined in proc_grps.json | ||
*/ | ||
private data class ProgramItem(val program: String, val pgroup: String) | ||
|
||
/** | ||
* Programs config class to represent program configuration options for the COBOL programs | ||
* @param pgms a list of main programs | ||
*/ | ||
private data class ProgramContainer(val pgms: List<ProgramItem>) | ||
|
||
// TODO: doc | ||
// TODO: recheck functionality correctness | ||
private fun matchProcessorGroup(pgmCfg: ProgramContainer, documentPathStr: String, workspaceFolder: Path): String? { | ||
val documentPath = Paths.get(documentPathStr) | ||
val relativeDocPath = workspaceFolder.relativize(documentPath) | ||
|
||
val candidates = mutableListOf<String>() | ||
|
||
for ((program, pgroup) in pgmCfg.pgms) { | ||
val programPath = try { | ||
Paths.get(program) | ||
} catch (e: InvalidPathException) { | ||
null | ||
} | ||
// exact match | ||
if (programPath != null && programPath.isAbsolute) { | ||
if ( | ||
programPath == documentPath | ||
|| programPath.toString().uppercase() == documentPath.toString().uppercase() | ||
) { | ||
return pgroup | ||
} | ||
} else { | ||
if (relativeDocPath == programPath) { | ||
candidates.add(pgroup) | ||
} | ||
} | ||
|
||
val relativeDocPathIgnoreCase = Paths.get(relativeDocPath.toString().uppercase()) | ||
val matcher = FileSystems.getDefault().getPathMatcher("glob:${program.uppercase()}") | ||
if (matcher.matches(relativeDocPathIgnoreCase)) { | ||
candidates.add(pgroup) | ||
} | ||
} | ||
|
||
return candidates.getOrNull(0) | ||
} | ||
|
||
// TODO: doc | ||
private fun loadProcessorsConfig(workspaceFolder: Path, documentUri: URI): ProcessorGroup? { | ||
val documentPath = Paths.get(documentUri).toString() | ||
val cfgPath = workspaceFolder.resolve(PROCESSOR_GROUP_FOLDER) | ||
val procCfgPath = cfgPath.resolve(PROCESSOR_GROUP_PROC) | ||
val pgmCfgPath = cfgPath.resolve(PROCESSOR_GROUP_PGM) | ||
if (!Files.exists(pgmCfgPath) || !Files.exists(procCfgPath)) { | ||
return null | ||
} | ||
val procCfgFile = File(procCfgPath.toUri()).readText() | ||
val procCfgGson = GsonBuilder() | ||
.registerTypeAdapter(ProcessorGroup::class.java, ProcessorGroupDeserializer()) | ||
.create() | ||
val procCfg = procCfgGson.fromJson(procCfgFile, PGroupContainer::class.java) | ||
|
||
val pgmCfgFile = File(pgmCfgPath.toUri()).readText() | ||
val pgmCfgGson = Gson() | ||
val pgmCfg = pgmCfgGson.fromJson(pgmCfgFile, ProgramContainer::class.java) | ||
|
||
val pgroup = matchProcessorGroup(pgmCfg, documentPath, workspaceFolder) | ||
|
||
val result = procCfg.pgroups.find { p -> pgroup == p.name } | ||
return result | ||
} | ||
|
||
// TODO: doc | ||
fun loadProcessorGroupDialectConfig( | ||
workspaceFolder: Path, | ||
item: ConfigurationItem, | ||
configObject: List<String> | ||
): List<String> { | ||
return try { | ||
// "SQL" is not a real dialect, we will use it only to set up sql backend for now | ||
loadProcessorsConfig(workspaceFolder, URI(item.scopeUri)) | ||
?.preprocessor | ||
?.map { (name, _) -> name } | ||
?.filter { name -> name != "SQL" } | ||
?.ifEmpty { configObject } | ||
?: configObject | ||
} catch (e: Exception) { | ||
println(e) | ||
configObject | ||
} | ||
} | ||
|
||
// TODO: doc | ||
fun loadProcessorGroupCopybookPathsConfig( | ||
workspaceFolder: Path, | ||
item: ConfigurationItem, | ||
configObject: List<String> | ||
): List<String> { | ||
val copybookPaths = loadProcessorsConfig(workspaceFolder, URI(item.scopeUri)) | ||
?.libs | ||
?.ifEmpty { configObject } | ||
?: configObject | ||
return findFilesInWorkspace(copybookPaths, workspaceFolder) | ||
} | ||
|
||
// TODO: doc | ||
// TODO: implement | ||
// fun resolveSubroutineURI(project: Project, name: String): String { | ||
// val folders = VSCodeSettingsAdapterService | ||
// .getService(project) | ||
// .getListOfStringsConfiguration(Sections.SUBROUTINE_LOCAL_PATH) | ||
// return searchCopybookInWorkspace(project, name, folders, COBOL_EXTENSTIONS) | ||
// } | ||
|
||
} |
Oops, something went wrong.