diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index f1fa0a4..fd2dc22 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -17,7 +17,7 @@ - + diff --git a/src/main/scala/io/smartdatalake/completion/SDLBCompletionEngineImpl.scala b/src/main/scala/io/smartdatalake/completion/SDLBCompletionEngineImpl.scala index ec2abed..c1664f7 100644 --- a/src/main/scala/io/smartdatalake/completion/SDLBCompletionEngineImpl.scala +++ b/src/main/scala/io/smartdatalake/completion/SDLBCompletionEngineImpl.scala @@ -15,54 +15,60 @@ class SDLBCompletionEngineImpl(private val schemaReader: SchemaReader, private v override def generateCompletionItems(context: SDLBContext): List[CompletionItem] = val itemSuggestionsFromSchema = schemaReader.retrieveAttributeOrTemplateCollection(context) match case AttributeCollection(attributes) => generateAttributeSuggestions(attributes, context.getParentContext) - case TemplateCollection(templates, templateType) => generateTemplateSuggestions(templates, templateType) + case TemplateCollection(templates, templateType) => generateTemplateSuggestions(templates, templateType, context.parentPath.size) - val itemSuggestionsFromConfig = contextAdvisor.generateSuggestions(context).map(createCompletionItem) + val itemSuggestionsFromConfigContextSuggestions = contextAdvisor.generateSuggestions(context) + val longestItemLength = itemSuggestionsFromConfigContextSuggestions.maxByOption(_.value.length).map(_.value.length).getOrElse(0) + val itemSuggestionsFromConfig = itemSuggestionsFromConfigContextSuggestions.map(createCompletionItem(_, longestItemLength)) val allItems = itemSuggestionsFromConfig ++ itemSuggestionsFromSchema - if allItems.isEmpty then typeList else allItems //TODO wrong + if allItems.isEmpty then typeList else allItems //TODO too aggressive. Sometimes suggesting nothing is better private def generateAttributeSuggestions(attributes: Iterable[SchemaItem], parentContext: Option[ConfigValue]): List[CompletionItem] = val items = parentContext match case Some(config: ConfigObject) => attributes.filter(item => Option(config.get(item.name)).isEmpty) case _ => attributes - items.map(createCompletionItem).toList + val longestItemLength = items.maxByOption(_.name.length).map(_.name.length).getOrElse(0) + items.map(createCompletionItem(_, longestItemLength)).toList - private[completion] def generateTemplateSuggestions(templates: Iterable[(String, Iterable[SchemaItem])], templateType: TemplateType): List[CompletionItem] = + private[completion] def generateTemplateSuggestions(templates: Iterable[(String, Iterable[SchemaItem])], templateType: TemplateType, depth: Int): List[CompletionItem] = + val indentDepth = depth + (if templateType == TemplateType.ATTRIBUTES then 0 else 1) templates.map { case (actionType, attributes) => val completionItem = new CompletionItem() completionItem.setLabel(actionType.toLowerCase) completionItem.setDetail(" template") val keyName = if templateType == TemplateType.OBJECT then s"${actionType.toLowerCase}_PLACEHOLDER" else "" val startObject = if templateType != TemplateType.ATTRIBUTES then "{" else "" - val endObject = if templateType != TemplateType.ATTRIBUTES then "}" else "" - completionItem.setInsertText( //TODO handle indentation + val endObject = if templateType != TemplateType.ATTRIBUTES then (" " * (indentDepth-1)) + "}" else "" + completionItem.setInsertText( s"""$keyName $startObject |${ def generatePlaceHolderValue(att: SchemaItem) = { if att.name == "type" then actionType else att.itemType.defaultValue } - attributes.map(att => "\t\t" + att.name + " = " + generatePlaceHolderValue(att)).mkString("\n")}\n\t$endObject - |""".stripMargin.replace("\r\n", "\n")) //TODO remove blank lines? + attributes.map(att => (" " * indentDepth) + att.name + " = " + generatePlaceHolderValue(att)).mkString("\n")}\n$endObject + |""".stripMargin.replace("\r\n", "\n").trim) completionItem.setKind(CompletionItemKind.Snippet) completionItem }.toList - private def createCompletionItem(item: SchemaItem): CompletionItem = + private def createCompletionItem(item: SchemaItem, longestItemLength: Int): CompletionItem = val completionItem = new CompletionItem() completionItem.setLabel(item.name) - completionItem.setDetail(f" ${if item.required then "required" else ""}%s ${item.itemType.name}%-10s") //TODO check how to justify properly + val trailingSpaces = " " * (2 + longestItemLength - item.name.length) + completionItem.setDetail(trailingSpaces + s"${if item.required then "required" else ""} ${item.itemType.name}") completionItem.setInsertText(item.name + (if item.itemType == ItemType.OBJECT then " " else " = ") + item.itemType.defaultValue) completionItem.setKind(CompletionItemKind.Snippet) completionItem - private def createCompletionItem(item: ContextSuggestion): CompletionItem = + private def createCompletionItem(item: ContextSuggestion, longestItemLength: Int): CompletionItem = val completionItem = new CompletionItem() completionItem.setLabel(item.value) - completionItem.setDetail(s" ${item.label}") + val trailingSpaces = " " * (2 + longestItemLength - item.value.length) + completionItem.setDetail(trailingSpaces + s"${item.label}") completionItem.setInsertText(item.value) completionItem.setKind(CompletionItemKind.Snippet) completionItem - private val typeItem = createCompletionItem(SchemaItem("type", ItemType.STRING, " type of object", true)) + private val typeItem = createCompletionItem(SchemaItem("type", ItemType.STRING, " type of object", true), 4) private val typeList = List(typeItem) } diff --git a/src/main/scala/io/smartdatalake/context/ContextAdvisorImpl.scala b/src/main/scala/io/smartdatalake/context/ContextAdvisorImpl.scala index 545486a..b3a5fc3 100644 --- a/src/main/scala/io/smartdatalake/context/ContextAdvisorImpl.scala +++ b/src/main/scala/io/smartdatalake/context/ContextAdvisorImpl.scala @@ -19,7 +19,7 @@ class ContextAdvisorImpl extends ContextAdvisor: private def retrieveDataObjectIds(context: SDLBContext): List[ContextSuggestion] = Option(context.textContext.rootConfig.root().get("dataObjects")) match case Some(asConfigObject: ConfigObject) => asConfigObject.unwrapped().toScala.map { (k, v) => v match - case jMap: JMap[String, Object] => ContextSuggestion(k, Option(jMap.get("type")).map(_.toString).getOrElse("")) + case jMap: JMap[?, ?] => ContextSuggestion(k, Option(jMap.get("type")).map(_.toString).getOrElse("")) case _ => ContextSuggestion(k, "") }.toList case _ => List.empty[ContextSuggestion] diff --git a/src/main/scala/io/smartdatalake/schema/SchemaContext.scala b/src/main/scala/io/smartdatalake/schema/SchemaContext.scala index e88b687..3c7ae6d 100644 --- a/src/main/scala/io/smartdatalake/schema/SchemaContext.scala +++ b/src/main/scala/io/smartdatalake/schema/SchemaContext.scala @@ -68,7 +68,10 @@ private[schema] case class SchemaContext(private val globalSchema: Value, localS localSchema.obj.get(DESCRIPTION) .map(_.str) .orElse(localSchema.obj.get(ADDITIONAL_PROPERTIES) - .flatMap(_.obj.get(DESCRIPTION).map(_.str))) + .flatMap{ + case Obj(value) => value.get(DESCRIPTION).map(_.str) + case _ => None + }) .getOrElse("") private def generateTemplates(oneOf: Value): Iterable[(String, Iterable[SchemaItem])] = oneOf match diff --git a/src/main/scala/io/smartdatalake/schema/SchemaReaderImpl.scala b/src/main/scala/io/smartdatalake/schema/SchemaReaderImpl.scala index 0a4265e..65d20d4 100644 --- a/src/main/scala/io/smartdatalake/schema/SchemaReaderImpl.scala +++ b/src/main/scala/io/smartdatalake/schema/SchemaReaderImpl.scala @@ -26,8 +26,12 @@ class SchemaReaderImpl(val schemaPath: String) extends SchemaReader { case Some(schemaContext) => schemaContext.generateSchemaSuggestions override def retrieveDescription(context: SDLBContext): String = if isWordMeaningless(context.word) then "" else retrieveSchemaContext(context, withWordInPath = true) match - case None => "" - case Some(schemaContext) => schemaContext.getDescription + case None => + logger.debug("No schema could be retrieved") + "" + case Some(schemaContext) => + logger.debug("Schema retrieved: {}", schemaContext.toString.take(300)) + schemaContext.getDescription /** * Not a crucial method but useful to speedup query process and might avoid some unwanted crash @@ -44,7 +48,7 @@ class SchemaReaderImpl(val schemaPath: String) extends SchemaReader { logger.debug("path = {}", path) path.foldLeft((oInitialSchemaContext, rootConfigValue)){(scCv, elementPath) => val (newConfigValue, oTypeObject) = moveInConfigAndRetrieveType(scCv._2, elementPath) - if (newConfigValue == null) {logger.error("Error, newConfig is null with pathElement={} and fullPath={}", elementPath, path)} + if (newConfigValue == null) {logger.warn("Error, newConfig is null with pathElement={} and fullPath={}", elementPath, path)} val newSchemaContext = oTypeObject match case Some(objectType) => val tryUpdateByName = scCv._1.flatMap(_.updateByName(elementPath)) diff --git a/src/test/scala/io/smartdatalake/schema/SchemaContextSpec.scala b/src/test/scala/io/smartdatalake/schema/SchemaContextSpec.scala index bbfd550..8e56ecd 100644 --- a/src/test/scala/io/smartdatalake/schema/SchemaContextSpec.scala +++ b/src/test/scala/io/smartdatalake/schema/SchemaContextSpec.scala @@ -288,5 +288,10 @@ class SchemaContextSpec extends UnitSpec { dataFrameIncrementalModeContext.getDescription.take(21) shouldBe "Compares max entry in" } + it should "remain quiet if executed on the global schema" in { + val globalContext = initialSchemaContext + globalContext.getDescription shouldBe "" + } + }