From b72e5a669086253912d5c5db948e1bcf699e080f Mon Sep 17 00:00:00 2001 From: Leandro Libarona Date: Fri, 7 Jun 2024 16:57:53 -0300 Subject: [PATCH 1/5] support tooling for external fragments when possible --- .../amfintegration/AmfResolvedUnit.scala | 2 +- .../ALSConfigurationState.scala | 40 ++++---- .../amfconfiguration/ProfileMatcher.scala | 3 + .../ProjectConfigurationState.scala | 7 +- .../DeclarationCreatorTests.scala | 7 +- .../objectintree/ObjectInTreeBaseTest.scala | 7 +- .../typescript/als-node-client.d.ts | 1 + .../node-package/typescript/als-server.d.ts | 1 + .../ClientAlsInitializeParams.scala | 4 +- .../convert/LspConvertersClientToShared.scala | 3 +- .../als/server/lsp4j/LspConversions.scala | 3 +- .../lsp4j/extension/AlsInitializeParams.java | 9 +- .../AlsInitializeParamsTypeAdapter.java | 6 ++ .../als/server/LanguageServerImpl.scala | 1 + .../als/server/modules/CleanAmfProcess.scala | 2 +- .../configuration/ConfigurationManager.scala | 9 ++ .../configuration/ConfigurationProvider.scala | 1 + .../serialization/SerializationManager.scala | 2 +- .../workspace/MainFileTreeBuilder.scala | 31 ++++-- .../workspace/WorkspaceContentManager.scala | 37 +++++--- .../workspace/WorkspaceParserRepository.scala | 4 +- .../configuration/AlsInitializeParams.scala | 9 +- .../als/server/workspace/WorkspaceList.scala | 6 +- .../workspace/external-fragment-1/api.raml | 3 + .../workspace/external-fragment-1/schema.json | 3 + .../workspace/external-fragment-2/api.raml | 3 + .../workspace/external-fragment-2/schema.json | 4 + .../workspace/external-fragment-3/api.yaml | 6 ++ .../workspace/external-fragment-3/schema.json | 1 + .../ServerWorkspaceSuggestionsTest.scala | 95 +++++++++++++++++++ .../WorkspaceServerSuggestionTest.scala | 56 +++++++++++ ...aultProjectConfigurationProviderTest.scala | 5 +- .../WorkspaceParserRepositoryTest.scala | 7 +- .../language/outline/test/OutlineTest.scala | 2 +- .../als/suggestions/client/Suggestions.scala | 13 ++- .../aml/pathnavigation/PathNavigation.scala | 2 +- .../webapi/raml/RamlAbstractDefinition.scala | 2 +- .../test/core/aml/BasicCoreTestsAML.scala | 2 +- .../oas30/Oas30ComponentSuggestionTest.scala | 4 +- 39 files changed, 329 insertions(+), 74 deletions(-) create mode 100644 als-server/shared/src/test/resources/workspace/external-fragment-1/api.raml create mode 100644 als-server/shared/src/test/resources/workspace/external-fragment-1/schema.json create mode 100644 als-server/shared/src/test/resources/workspace/external-fragment-2/api.raml create mode 100644 als-server/shared/src/test/resources/workspace/external-fragment-2/schema.json create mode 100644 als-server/shared/src/test/resources/workspace/external-fragment-3/api.yaml create mode 100644 als-server/shared/src/test/resources/workspace/external-fragment-3/schema.json create mode 100644 als-server/shared/src/test/scala/org/mulesoft/als/server/modules/completion/ServerWorkspaceSuggestionsTest.scala create mode 100644 als-server/shared/src/test/scala/org/mulesoft/als/server/modules/completion/workspace/WorkspaceServerSuggestionTest.scala diff --git a/als-common/shared/src/main/scala/org/mulesoft/amfintegration/AmfResolvedUnit.scala b/als-common/shared/src/main/scala/org/mulesoft/amfintegration/AmfResolvedUnit.scala index 467771b3f9..e4f4273cce 100644 --- a/als-common/shared/src/main/scala/org/mulesoft/amfintegration/AmfResolvedUnit.scala +++ b/als-common/shared/src/main/scala/org/mulesoft/amfintegration/AmfResolvedUnit.scala @@ -16,7 +16,7 @@ import scala.concurrent.Future trait AmfResolvedUnit extends UnitWithNextReference { override protected type T = AmfResolvedUnit val alsConfigurationState: ALSConfigurationState - val configuration: AMLSpecificConfiguration = AMLSpecificConfiguration(alsConfigurationState.getAmfConfig) + val configuration: AMLSpecificConfiguration = AMLSpecificConfiguration(alsConfigurationState.getAmfConfig(false)) protected def resolvedUnitFn(): Future[AMFResult] diff --git a/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ALSConfigurationState.scala b/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ALSConfigurationState.scala index b3925a3d83..26bb7b8227 100644 --- a/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ALSConfigurationState.scala +++ b/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ALSConfigurationState.scala @@ -37,12 +37,12 @@ case class ALSConfigurationState( editorResourceLoader: Option[ResourceLoader] ) extends PlatformSecrets { - lazy val amfParseContext: AmfParseContext = AmfParseContext(getAmfConfig, this) + lazy val amfParseContext: AmfParseContext = AmfParseContext(getAmfConfig(false), this) def configForUnit(unit: BaseUnit): AMLSpecificConfiguration = configForSpec(unit.sourceSpec.getOrElse(Spec.AML)) - private val rootConfiguration: AMFConfiguration = projectState.rootProjectConfiguration + private def rootConfiguration(asMain: Boolean): AMFConfiguration = projectState.rootProjectConfiguration(asMain) def configForDialect(d: Dialect): AMLSpecificConfiguration = ProfileMatcher.spec(d) match { @@ -52,14 +52,16 @@ case class ALSConfigurationState( .contains( "file://vocabularies/dialects/metadialect.yaml" ) => // TODO change when Dialect name and version be spec - AMLSpecificConfiguration(rootConfiguration) + AMLSpecificConfiguration(rootConfiguration(false)) case Some(spec) => configForSpec(spec) case _ => AMLSpecificConfiguration(predefinedWithDialects) } - def configForSpec(spec: Spec): AMLSpecificConfiguration = + def configForSpec(spec: Spec): AMLSpecificConfiguration = // todo: check if asMain = true has any inconvenience AMLSpecificConfiguration( - getAmlConfig(apiConfigurationForSpec(spec).map(projectState.customSetUp).getOrElse(predefinedWithDialects)) + getAmlConfig( + apiConfigurationForSpec(spec).map(projectState.customSetUp(_, asMain = true)).getOrElse(predefinedWithDialects) + ) ) private def apiConfigurationForSpec(spec: Spec): Option[AMFConfiguration] = @@ -76,15 +78,15 @@ case class ALSConfigurationState( case _ => None } - def getAmfConfig(url: String): AMFConfiguration = { + def getAmfConfig(url: String, asMain: Boolean): AMFConfiguration = { val base = if (url.endsWith("graphql")) - projectState.getProjectConfig - else getAmfConfig.withPlugins(editorState.alsParsingPlugins) - getAmfConfig(base) + projectState.getProjectConfig(asMain) + else getAmfConfig(asMain).withPlugins(editorState.alsParsingPlugins) + getAmfConfig(base, asMain) } - def getAmfConfig: AMFConfiguration = getAmfConfig(rootConfiguration) + def getAmfConfig(asMain: Boolean): AMFConfiguration = getAmfConfig(rootConfiguration(asMain), asMain) def getAmfConfig(spec: Spec): AMFConfiguration = { val base = spec match { @@ -93,7 +95,7 @@ case class ALSConfigurationState( case _ => APIConfiguration.fromSpec(spec) } - getAmfConfig(base) + getAmfConfig(base, asMain = true) // todo: check if asMain = true has any inconvenience } def allDialects: Seq[Dialect] = dialects ++ BaseAlsDialectProvider.allBaseDialects @@ -118,14 +120,14 @@ case class ALSConfigurationState( dialects.foldLeft(configuration)((c, dialect) => c.withDialect(dialect)) } - def getAmfConfig(base: AMFConfiguration): AMFConfiguration = - projectState.customSetUp(getAmlConfig(base).asInstanceOf[AMFConfiguration]) + def getAmfConfig(base: AMFConfiguration, asMain: Boolean): AMFConfiguration = + projectState.customSetUp(getAmlConfig(base).asInstanceOf[AMFConfiguration], asMain) def findSemanticByName(name: String): Option[(SemanticExtension, Dialect)] = configForSpec(Spec.AML).config.configurationState().findSemanticByName(name) - def parse(url: String, maxFileSize: Option[Int] = None): Future[AmfParseResult] = - parse(getAmfConfig(url), url, maxFileSize) + def parse(url: String, asMain: Boolean = false, maxFileSize: Option[Int] = None): Future[AmfParseResult] = + parse(getAmfConfig(url, asMain), url, maxFileSize) private def parse(amfConfiguration: AMFConfiguration, uri: String, maxFileSize: Option[Int]): Future[AmfParseResult] = wrapLoadersIfNeeded(amfConfiguration, maxFileSize) @@ -189,12 +191,12 @@ case class ALSConfigurationState( } def findSemanticFor(uri: String): Seq[(SemanticExtension, Dialect)] = - getAmfConfig(uri) + getAmfConfig(uri, asMain = true) // todo: check if asMain = true has any inconvenience .configurationState() .findSemanticByTarget(uri) def findSemanticForName(name: String): Option[(SemanticExtension, Dialect)] = - getAmfConfig + getAmfConfig(false) .configurationState() .findSemanticByName(name) @@ -254,12 +256,12 @@ case class ALSConfigurationState( predefinedWithDialects.configurationState().getDialects().find(_.nameAndVersion() == nameAndVersion) def fetchContent(uri: String): Future[Content] = try { - platform.fetchContent(uri, getAmfConfig) + platform.fetchContent(uri, getAmfConfig(false)) } catch { case e: Exception => Future.failed(e) } def buildJsonSchema(shape: AnyShape): String = - JsonSchemaShapeRenderer.buildJsonSchema(shape, getAmfConfig) + JsonSchemaShapeRenderer.buildJsonSchema(shape, getAmfConfig(false)) } diff --git a/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ProfileMatcher.scala b/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ProfileMatcher.scala index 7504e7d270..25b0df2242 100644 --- a/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ProfileMatcher.scala +++ b/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ProfileMatcher.scala @@ -57,6 +57,9 @@ object ProfileMatcher { DialectWithVendor(MetaDialect(), Spec.AML) ) + def dialect(spec: Spec): Option[Dialect] = + webApiDialects.find(_.spec == spec).map(_.dialect) + def spec(dialect: Dialect): Option[Spec] = webApiDialects.find(_.dialect == dialect).map(_.spec) } diff --git a/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ProjectConfigurationState.scala b/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ProjectConfigurationState.scala index 0b310bd5a5..e86f1fde11 100644 --- a/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ProjectConfigurationState.scala +++ b/als-common/shared/src/main/scala/org/mulesoft/amfintegration/amfconfiguration/ProjectConfigurationState.scala @@ -16,7 +16,7 @@ import scala.concurrent.Future trait ProjectConfigurationState { - def customSetUp(amfConfiguration: AMFConfiguration): AMFConfiguration = amfConfiguration + def customSetUp(amfConfiguration: AMFConfiguration, asMain: Boolean): AMFConfiguration = amfConfiguration def cache: UnitCache def getCompanionForDialect(d: Dialect): Future[Option[Module]] = Future @@ -30,9 +30,8 @@ trait ProjectConfigurationState { ) .map(r => r.flatten.find(_._2).map(_._1)) - def getProjectConfig: AMFConfiguration = { + def getProjectConfig(asMain: Boolean): AMFConfiguration = GraphQLConfiguration.GraphQL() - } val extensions: Seq[Dialect] val profiles: Seq[ValidationProfile] @@ -40,7 +39,7 @@ trait ProjectConfigurationState { val results: Seq[AMFParseResult] val resourceLoaders: Seq[ResourceLoader] val projectErrors: Seq[AMFValidationResult] - val rootProjectConfiguration: AMFConfiguration = APIConfiguration.APIWithJsonSchema() + def rootProjectConfiguration(asMain: Boolean): AMFConfiguration = APIConfiguration.APIWithJsonSchema() } case class EmptyProjectConfigurationState(folder: String) extends ProjectConfigurationState() { diff --git a/als-common/shared/src/test/scala/org/mulesoft/als/common/objectintree/DeclarationCreatorTests.scala b/als-common/shared/src/test/scala/org/mulesoft/als/common/objectintree/DeclarationCreatorTests.scala index 6e752c0419..ecb1d36bf0 100644 --- a/als-common/shared/src/test/scala/org/mulesoft/als/common/objectintree/DeclarationCreatorTests.scala +++ b/als-common/shared/src/test/scala/org/mulesoft/als/common/objectintree/DeclarationCreatorTests.scala @@ -51,8 +51,11 @@ class DeclarationCreatorTests extends AsyncFlatSpec with DeclarationCreator { private def baseUnit(file: String): Future[BaseUnit] = for { - state <- EditorConfiguration().getState - result <- ALSConfigurationState(state, EmptyProjectConfigurationState, None).parse(uriTemplate(file)) + state <- EditorConfiguration().getState + result <- ALSConfigurationState(state, EmptyProjectConfigurationState, None).parse( + uriTemplate(file), + asMain = true + ) } yield { result.result.baseUnit } diff --git a/als-common/shared/src/test/scala/org/mulesoft/als/common/objectintree/ObjectInTreeBaseTest.scala b/als-common/shared/src/test/scala/org/mulesoft/als/common/objectintree/ObjectInTreeBaseTest.scala index 525b14f6da..81b1cd5eae 100644 --- a/als-common/shared/src/test/scala/org/mulesoft/als/common/objectintree/ObjectInTreeBaseTest.scala +++ b/als-common/shared/src/test/scala/org/mulesoft/als/common/objectintree/ObjectInTreeBaseTest.scala @@ -27,8 +27,11 @@ case class ObjectInTreeBaseTest(instanceFile: String, dialectFile: String) exten private val eventualResult: Future[AmfParseResult] = { for { - s <- global.getState - result <- ALSConfigurationState(s, EmptyProjectConfigurationState, None).parse(uriTemplate(instanceFile)) + s <- global.getState + result <- ALSConfigurationState(s, EmptyProjectConfigurationState, None).parse( + uriTemplate(instanceFile), + asMain = true + ) } yield result } private def fn(pos: AmfPosition, result: AmfParseResult, dialect: Dialect): ObjectInTree = diff --git a/als-node-client/node-package/typescript/als-node-client.d.ts b/als-node-client/node-package/typescript/als-node-client.d.ts index 76768c5ffa..a6127aa48a 100644 --- a/als-node-client/node-package/typescript/als-node-client.d.ts +++ b/als-node-client/node-package/typescript/als-node-client.d.ts @@ -6596,6 +6596,7 @@ declare module '@aml-org/als-node-client' { configuration?: AlsConfiguration hotReload?: boolean maxFileSize?: number + shouldRetryExternalFragments?: boolean } export type DidFocusParams = { diff --git a/als-server/js/node-package/typescript/als-server.d.ts b/als-server/js/node-package/typescript/als-server.d.ts index 18248bcb84..a7eb7e9079 100644 --- a/als-server/js/node-package/typescript/als-server.d.ts +++ b/als-server/js/node-package/typescript/als-server.d.ts @@ -6596,6 +6596,7 @@ declare module '@aml-org/als-server' { configuration?: AlsConfiguration hotReload?: boolean maxFileSize?: number + shouldRetryExternalFragments?: boolean } export type DidFocusParams = { diff --git a/als-server/js/src/main/scala/org/mulesoft/als/server/protocol/configuration/ClientAlsInitializeParams.scala b/als-server/js/src/main/scala/org/mulesoft/als/server/protocol/configuration/ClientAlsInitializeParams.scala index 91672b0133..56e30be4ba 100644 --- a/als-server/js/src/main/scala/org/mulesoft/als/server/protocol/configuration/ClientAlsInitializeParams.scala +++ b/als-server/js/src/main/scala/org/mulesoft/als/server/protocol/configuration/ClientAlsInitializeParams.scala @@ -22,6 +22,7 @@ trait ClientAlsInitializeParams extends js.Object { def workspaceFolders: js.Array[ClientWorkspaceFolder] = js.native // Nullable def hotReload: UndefOr[Boolean] = js.native def maxFileSize: UndefOr[Int] = js.native + def shouldRetryExternalFragments: UndefOr[Boolean] = js.native } object ClientAlsInitializeParams { @@ -39,7 +40,8 @@ object ClientAlsInitializeParams { rootPath = internal.rootPath.orUndefined, initializationOptions = internal.initializationOptions.collect { case js: js.Object => js }.orUndefined, hotReload = internal.hotReload.orUndefined, - maxFileSize = internal.maxFileSize.orUndefined + maxFileSize = internal.maxFileSize.orUndefined, + shouldRetryExternalFragments = internal.shouldRetryExternalFragments.orUndefined ) .asInstanceOf[ClientAlsInitializeParams] } diff --git a/als-server/js/src/main/scala/org/mulesoft/als/server/protocol/convert/LspConvertersClientToShared.scala b/als-server/js/src/main/scala/org/mulesoft/als/server/protocol/convert/LspConvertersClientToShared.scala index f3a146eb98..eda072604c 100644 --- a/als-server/js/src/main/scala/org/mulesoft/als/server/protocol/convert/LspConvertersClientToShared.scala +++ b/als-server/js/src/main/scala/org/mulesoft/als/server/protocol/convert/LspConvertersClientToShared.scala @@ -119,7 +119,8 @@ object LspConvertersClientToShared { initializationOptions = v.initializationOptions.toOption, configuration = v.configuration.toOption.map(_.toShared), hotReload = v.hotReload.toOption, - maxFileSize = v.maxFileSize.toOption + maxFileSize = v.maxFileSize.toOption, + shouldRetryExternalFragments = v.shouldRetryExternalFragments.toOption ) } diff --git a/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/LspConversions.scala b/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/LspConversions.scala index 1a177e562c..936cfdf565 100644 --- a/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/LspConversions.scala +++ b/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/LspConversions.scala @@ -108,7 +108,8 @@ object LspConversions { initializationOptions = Option(p.getInitializationOptions), configuration = Option(p.getConfiguration), hotReload = Option(p.getHotReload), - maxFileSize = Option(p.getMaxFileSize).map(_.toInt) + maxFileSize = Option(p.getMaxFileSize).map(_.toInt), + shouldRetryExternalFragments = Option(p.getShouldRetryExternalFragments) ) } getOrElse AlsInitializeParams.default diff --git a/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/extension/AlsInitializeParams.java b/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/extension/AlsInitializeParams.java index 1f3db34fd5..5f2b519eb2 100644 --- a/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/extension/AlsInitializeParams.java +++ b/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/extension/AlsInitializeParams.java @@ -8,6 +8,7 @@ public class AlsInitializeParams extends InitializeParams { private AlsClientCapabilities alsClientCapabilities; private AlsConfiguration configuration; private Boolean hotReload; + private Boolean shouldRetryExternalFragments; private Integer maxFileSize; @Override @@ -39,11 +40,17 @@ public void setConfiguration(AlsConfiguration configuration) { public Boolean getHotReload() { return hotReload; } - public void setHotReload(Boolean hotReload) { this.hotReload = hotReload; } + public Boolean getShouldRetryExternalFragments() { + return shouldRetryExternalFragments; + } + public void setShouldRetryExternalFragments(Boolean shouldRetryExternalFragments) { + this.shouldRetryExternalFragments = shouldRetryExternalFragments; + } + public Integer getMaxFileSize() { return maxFileSize; } diff --git a/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/extension/AlsInitializeParamsTypeAdapter.java b/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/extension/AlsInitializeParamsTypeAdapter.java index fbfd089e78..d963af1526 100644 --- a/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/extension/AlsInitializeParamsTypeAdapter.java +++ b/als-server/jvm/src/main/scala/org/mulesoft/als/server/lsp4j/extension/AlsInitializeParamsTypeAdapter.java @@ -95,6 +95,9 @@ public AlsInitializeParams read(final JsonReader in) throws IOException { case "hotReload": result.setHotReload(readHotReload(in)); break; + case "shouldRetryExternalFragments": + result.setShouldRetryExternalFragments(readShouldRetryExternalFragments(in)); + break; case "maxFileSize": result.setMaxFileSize(readMaxFileSize(in)); break; @@ -109,6 +112,9 @@ public AlsInitializeParams read(final JsonReader in) throws IOException { private Boolean readHotReload(JsonReader in) { return gson.fromJson(in, Boolean.class); } + private Boolean readShouldRetryExternalFragments(JsonReader in) { + return gson.fromJson(in, Boolean.class); + } private Integer readMaxFileSize(JsonReader in) { return gson.fromJson(in, Integer.class); diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/LanguageServerImpl.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/LanguageServerImpl.scala index 7eff065bc6..270a41b633 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/LanguageServerImpl.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/LanguageServerImpl.scala @@ -24,6 +24,7 @@ class LanguageServerImpl( override def initialize(params: AlsInitializeParams): Future[AlsInitializeResult] = { logParams(params) params.hotReload.foreach(configuration.setHotReloadDialects) + params.shouldRetryExternalFragments.foreach(configuration.setShouldRetryExternalFragments) configuration.setMaxFileSize(params.maxFileSize) params.configuration.foreach(c => { updateConfiguration( diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/CleanAmfProcess.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/CleanAmfProcess.scala index d711c31125..ee77a92358 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/CleanAmfProcess.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/CleanAmfProcess.scala @@ -17,7 +17,7 @@ trait CleanAmfProcess { alsConfigurationState: ALSConfigurationState ): Future[(AmfResultWrap, AMFResult, ALSConfigurationState)] = for { - pr <- AMLSpecificConfiguration(alsConfigurationState.getAmfConfig(refinedUri)) + pr <- AMLSpecificConfiguration(alsConfigurationState.getAmfConfig(refinedUri, asMain = true)) .parse(refinedUri) .map(new AmfResultWrap(_)) helper <- Future(alsConfigurationState.configForUnit(pr.result.baseUnit)) diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/configuration/ConfigurationManager.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/configuration/ConfigurationManager.scala index b6a7fcb896..75e6365903 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/configuration/ConfigurationManager.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/configuration/ConfigurationManager.scala @@ -35,6 +35,14 @@ class ConfigurationManager */ def setHotReloadDialects(p: Boolean): Unit = hotReloadDialects = p + private var shouldRetryExternalFragments: Boolean = false + + override def getShouldRetryExternalFragments: Boolean = shouldRetryExternalFragments + + /** Should only be called from initialization + */ + def setShouldRetryExternalFragments(p: Boolean): Unit = shouldRetryExternalFragments = p + private var maxFileSize: Option[Int] = None override def getMaxFileSize: Option[Int] = maxFileSize @@ -58,4 +66,5 @@ class ConfigurationManager } override def initialize(): Future[Unit] = { Future.successful() } + } diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/configuration/ConfigurationProvider.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/configuration/ConfigurationProvider.scala index e75520814d..65631d6632 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/configuration/ConfigurationProvider.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/configuration/ConfigurationProvider.scala @@ -6,4 +6,5 @@ trait ConfigurationProvider { def getConfiguration: AlsConfigurationReader def getHotReloadDialects: Boolean def getMaxFileSize: Option[Int] + def getShouldRetryExternalFragments: Boolean } diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/serialization/SerializationManager.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/serialization/SerializationManager.scala index 245765e056..7311363b2b 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/serialization/SerializationManager.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/serialization/SerializationManager.scala @@ -69,7 +69,7 @@ class SerializationManager[S]( for { state <- wcm.getConfigurationState(uri) (_, resolved, configurationState) <- parseAndResolve(refinedUri, state) - } yield serialize(resolved.baseUnit, configurationState.getAmfConfig, sourcemaps) + } yield serialize(resolved.baseUnit, configurationState.getAmfConfig(false), sourcemaps) case _ => Future.failed(new RuntimeException("no workspaceConfigurationManager set")) } diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/MainFileTreeBuilder.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/MainFileTreeBuilder.scala index 0285f02b01..ae17c33512 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/MainFileTreeBuilder.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/MainFileTreeBuilder.scala @@ -5,7 +5,8 @@ import amf.core.client.scala.AMFResult import amf.core.client.scala.model.document.BaseUnit import org.mulesoft.als.logger.Logger import org.mulesoft.amfintegration.AmfImplicits.BaseUnitImp -import org.mulesoft.amfintegration.amfconfiguration.{AmfParseContext, AmfParseResult} +import org.mulesoft.amfintegration.amfconfiguration.{AmfParseContext, AmfParseResult, ProfileMatcher} +import org.mulesoft.amfintegration.dialect.dialects.ExternalFragmentDialect import org.mulesoft.amfintegration.relationships.{AliasInfo, RelationshipLink} import org.mulesoft.amfintegration.visitors.AmfElementVisitors import org.mulesoft.lsp.feature.link.DocumentLink @@ -27,13 +28,30 @@ class ParsedMainFileTree( override def parsedUnits: Map[String, ParsedUnit] = units - .map(t => t._1 -> ParsedUnit(new AmfParseResult(t._2, definedBy, parseContext, t._1), inTree = true, definedBy)) + .map(t => + t._1 -> { + val definedBy = getDialectForBaseUnit(t._2.baseUnit) + ParsedUnit( + new AmfParseResult( + t._2, + definedBy, + parseContext, + t._1 + ), + inTree = true, + definedBy + ) + } + ) .toMap + private def getDialectForBaseUnit(baseUnit: BaseUnit): Dialect = + baseUnit.sourceSpec.flatMap(ProfileMatcher.dialect).getOrElse(ExternalFragmentDialect.dialect) + override def references: Map[String, Seq[DocumentLink]] = documentLinks - def index(): Unit = - (main.baseUnit +: main.baseUnit.flatRefs) + def index(refs: Seq[BaseUnit]): Unit = + (main.baseUnit +: refs) .map(AMFResult(_, main.results)) .foreach(r => units.put(r.baseUnit.identifier, r)) @@ -81,7 +99,8 @@ object ParsedMainFileTree { object MainFileTreeBuilder { def build( amfParseResult: AmfParseResult, - visitors: AmfElementVisitors + visitors: AmfElementVisitors, + refs: Seq[BaseUnit] ): Future[ParsedMainFileTree] = Future { handleVisit(visitors, amfParseResult.result.baseUnit, amfParseResult.context) val tree = ParsedMainFileTree( @@ -92,7 +111,7 @@ object MainFileTreeBuilder { amfParseResult.definedBy, amfParseResult.context ) - tree.index() + tree.index(refs) tree } diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceContentManager.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceContentManager.scala index e58135f84f..902d4e74c9 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceContentManager.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceContentManager.scala @@ -1,9 +1,10 @@ package org.mulesoft.als.server.modules.workspace import amf.aml.client.scala.model.document.{Dialect, DialectInstance} -import amf.core.client.scala.model.document.ExternalFragment +import amf.core.client.scala.model.document.{BaseUnit, ExternalFragment} import amf.core.internal.remote.Platform import amf.core.internal.unsafe.PlatformSecrets +import amf.shapes.client.scala.model.document.DataTypeFragment import org.mulesoft.als.common.URIImplicits._ import org.mulesoft.als.configuration.ProjectConfiguration import org.mulesoft.als.logger.Logger @@ -28,7 +29,8 @@ class WorkspaceContentManager private ( val projectConfigAdapter: ProjectConfigurationAdapter, hotReload: Boolean, maxFileSize: Option[Int], - switchWorkspace: String => Unit + switchWorkspace: String => Unit, + shouldRetryExternalFragments: Boolean ) extends UnitTaskManager[ParsedUnit, CompilableUnit, NotificationKind] with WorkspaceFolderManager with PlatformSecrets { @@ -175,14 +177,14 @@ class WorkspaceContentManager private ( } private def processIsolated(file: String, uuid: String): Future[Unit] = - parse(file, uuid) + parse(file, asMain = false, uuid) .flatMap { result => updateUnit(uuid, result, isDependency = false) } private def updateUnit(uuid: String, result: AmfParseResult, isDependency: Boolean): Future[Unit] = { repository.updateUnit(result) - projectConfigAdapter.getConfigurationState.map(state => { + projectConfigAdapter.getConfigurationState.map(_ => { Logger.debug(s"Sending new AST from $folderUri", "WorkspaceContentManager", "processIsolated") baseUnitSubscribers.foreach(s => try { @@ -324,9 +326,11 @@ class WorkspaceContentManager private ( s"${if (folderUri != "" && !mainFile.contains(folderUri)) trailSlash(folderUri) else folderUri}$mainFile", + asMain = true, uuid ) - _ <- repository.newTree(u).flatMap(t => projectConfigAdapter.newTree(t)) + refs <- getTweakedRefs(u.result.baseUnit) + _ <- repository.newTree(u, refs).flatMap(t => projectConfigAdapter.newTree(t)) } yield { stagingArea.enqueue(snapshot.files.filterNot(_._2 == CHANGE_CONFIG).filter(t => !isInMainTree(t._1))) baseUnitSubscribers.foreach(s => { @@ -340,24 +344,32 @@ class WorkspaceContentManager private ( } } - private def parse(uri: String, uuid: String): Future[AmfParseResult] = { + private def getTweakedRefs(baseUnit: BaseUnit): Future[Seq[BaseUnit]] = + Future.sequence(baseUnit.flatRefs.map { + case bu @ (_: ExternalFragment | _: DataTypeFragment) if shouldRetryExternalFragments => + innerParse(bu.identifier, asMain = false) + .map(r => r.result.baseUnit) + case bu => Future.successful(bu) + }) + + private def parse(uri: String, asMain: Boolean, uuid: String): Future[AmfParseResult] = { Logger.timeProcess( "AMF Parse", MessageTypes.BEGIN_PARSE, MessageTypes.END_PARSE, "WorkspaceContentManager : parse", uri, - innerParse(uri), + innerParse(uri, asMain), uuid ) } - private def innerParse(uri: String)(): Future[AmfParseResult] = { + private def innerParse(uri: String, asMain: Boolean)(): Future[AmfParseResult] = { val decodedUri = uri.toAmfDecodedUri Logger.debug(s"Sent uri: $decodedUri", "WorkspaceContentManager", "innerParse") (for { state <- projectConfigAdapter.getConfigurationState - r <- state.parse(decodedUri, maxFileSize) + r <- state.parse(decodedUri, asMain, maxFileSize) newConfig <- projectConfigAdapter.getProjectConfiguration.map(config => { new ProjectConfiguration( config.folder, @@ -403,6 +415,7 @@ class WorkspaceContentManager private ( override protected def disableTasks(): Future[Unit] = Future { baseUnitSubscribers.map(d => repository.getAllFilesUris.map(_.toAmfUri).foreach(d.onRemoveFile)) } + } object WorkspaceContentManager { @@ -413,7 +426,8 @@ object WorkspaceContentManager { projectConfigAdapter: ProjectConfigurationAdapter, switchWorkspace: String => Unit, hotReload: Boolean = false, - maxFileSize: Option[Int] = None + maxFileSize: Option[Int] = None, + shouldRetryExternalFragments: Boolean = false ): Future[WorkspaceContentManager] = { val repository = new WorkspaceParserRepository() val wcm = new WorkspaceContentManager( @@ -424,7 +438,8 @@ object WorkspaceContentManager { projectConfigAdapter.withRepository(repository), hotReload, maxFileSize, - switchWorkspace + switchWorkspace, + shouldRetryExternalFragments ) wcm.init() Future.successful(wcm) diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceParserRepository.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceParserRepository.scala index bfafe75af1..7cf038c937 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceParserRepository.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceParserRepository.scala @@ -52,10 +52,10 @@ class WorkspaceParserRepository() extends Repository[ParsedUnit] { emptyFileTree } - def newTree(result: AmfParseResult): Future[MainFileTree] = synchronized { + def newTree(result: AmfParseResult, refs: Seq[BaseUnit]): Future[MainFileTree] = synchronized { cleanTree() MainFileTreeBuilder - .build(result, visitors(result.result.baseUnit)) + .build(result, visitors(result.result.baseUnit), refs) .map { nt => tree = nt nt.parsedUnits.keys.foreach { removeUnit } diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/protocol/configuration/AlsInitializeParams.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/protocol/configuration/AlsInitializeParams.scala index 5f5140222b..72530cbcf5 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/protocol/configuration/AlsInitializeParams.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/protocol/configuration/AlsInitializeParams.scala @@ -52,7 +52,8 @@ class AlsInitializeParams private ( val initializationOptions: Option[Any] = None, val configuration: Option[AlsConfiguration] = None, val hotReload: Option[Boolean] = None, - val maxFileSize: Option[Int] = None + val maxFileSize: Option[Int] = None, + val shouldRetryExternalFragments: Option[Boolean] = None ) object AlsInitializeParams { @@ -68,7 +69,8 @@ object AlsInitializeParams { initializationOptions: Option[Any] = None, configuration: Option[AlsConfiguration] = None, hotReload: Option[Boolean] = None, - maxFileSize: Option[Int] = None + maxFileSize: Option[Int] = None, + shouldRetryExternalFragments: Option[Boolean] = None ): AlsInitializeParams = new AlsInitializeParams( capabilities.getOrElse(AlsClientCapabilities()), @@ -81,7 +83,8 @@ object AlsInitializeParams { initializationOptions, configuration, hotReload, - maxFileSize + maxFileSize, + shouldRetryExternalFragments ) def default: AlsInitializeParams = apply(None, Some(TraceKind.Off)) diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/workspace/WorkspaceList.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/workspace/WorkspaceList.scala index 40b425cafa..b4456ada99 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/workspace/WorkspaceList.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/workspace/WorkspaceList.scala @@ -46,7 +46,8 @@ class WorkspaceList( buildConfigurationAdapter("", IgnoreProjectConfigurationAdapter), switchWorkspace, configurationProvider.getHotReloadDialects, - configurationProvider.getMaxFileSize + configurationProvider.getMaxFileSize, + configurationProvider.getShouldRetryExternalFragments ) } @@ -97,7 +98,8 @@ class WorkspaceList( buildConfigurationAdapter(uri, projectConfigurationProvider), switchWorkspace, configurationProvider.getHotReloadDialects, - configurationProvider.getMaxFileSize + configurationProvider.getMaxFileSize, + configurationProvider.getShouldRetryExternalFragments ) _ <- Future.sequence(applicableFiles.map(wcm.stage(_, OPEN_FILE))) } yield { diff --git a/als-server/shared/src/test/resources/workspace/external-fragment-1/api.raml b/als-server/shared/src/test/resources/workspace/external-fragment-1/api.raml new file mode 100644 index 0000000000..dc8d17e57a --- /dev/null +++ b/als-server/shared/src/test/resources/workspace/external-fragment-1/api.raml @@ -0,0 +1,3 @@ +#%RAML 1.0 +types: + t: !include schema.json diff --git a/als-server/shared/src/test/resources/workspace/external-fragment-1/schema.json b/als-server/shared/src/test/resources/workspace/external-fragment-1/schema.json new file mode 100644 index 0000000000..2622b92b46 --- /dev/null +++ b/als-server/shared/src/test/resources/workspace/external-fragment-1/schema.json @@ -0,0 +1,3 @@ +{ + "$schema": "http://json-schema.org/draft/2019*" +} diff --git a/als-server/shared/src/test/resources/workspace/external-fragment-2/api.raml b/als-server/shared/src/test/resources/workspace/external-fragment-2/api.raml new file mode 100644 index 0000000000..dc8d17e57a --- /dev/null +++ b/als-server/shared/src/test/resources/workspace/external-fragment-2/api.raml @@ -0,0 +1,3 @@ +#%RAML 1.0 +types: + t: !include schema.json diff --git a/als-server/shared/src/test/resources/workspace/external-fragment-2/schema.json b/als-server/shared/src/test/resources/workspace/external-fragment-2/schema.json new file mode 100644 index 0000000000..c26f5dbea6 --- /dev/null +++ b/als-server/shared/src/test/resources/workspace/external-fragment-2/schema.json @@ -0,0 +1,4 @@ +{ + "$schema": "http://json-schema.org/draft/2019-09/schema#", + "type": "*" +} diff --git a/als-server/shared/src/test/resources/workspace/external-fragment-3/api.yaml b/als-server/shared/src/test/resources/workspace/external-fragment-3/api.yaml new file mode 100644 index 0000000000..26d0c88f1d --- /dev/null +++ b/als-server/shared/src/test/resources/workspace/external-fragment-3/api.yaml @@ -0,0 +1,6 @@ +openapi: "3.0.0" +components: + schemas: + json: + $ref: schema.json +paths: {} diff --git a/als-server/shared/src/test/resources/workspace/external-fragment-3/schema.json b/als-server/shared/src/test/resources/workspace/external-fragment-3/schema.json new file mode 100644 index 0000000000..72e8ffc0db --- /dev/null +++ b/als-server/shared/src/test/resources/workspace/external-fragment-3/schema.json @@ -0,0 +1 @@ +* diff --git a/als-server/shared/src/test/scala/org/mulesoft/als/server/modules/completion/ServerWorkspaceSuggestionsTest.scala b/als-server/shared/src/test/scala/org/mulesoft/als/server/modules/completion/ServerWorkspaceSuggestionsTest.scala new file mode 100644 index 0000000000..36d2d50f9f --- /dev/null +++ b/als-server/shared/src/test/scala/org/mulesoft/als/server/modules/completion/ServerWorkspaceSuggestionsTest.scala @@ -0,0 +1,95 @@ +package org.mulesoft.als.server.modules.completion + +import amf.core.client.scala.AMFGraphConfiguration +import org.mulesoft.als.common.{MarkerFinderTest, MarkerInfo} +import org.mulesoft.als.convert.LspRangeConverter +import org.mulesoft.als.server.client.scala.LanguageServerBuilder +import org.mulesoft.als.server.feature.configuration.UpdateConfigurationParams +import org.mulesoft.als.server.modules.WorkspaceManagerFactoryBuilder +import org.mulesoft.als.server.protocol.LanguageServer +import org.mulesoft.als.server.protocol.configuration.AlsInitializeParams +import org.mulesoft.als.server.workspace.ChangesWorkspaceConfiguration +import org.mulesoft.als.server.workspace.command.Commands +import org.mulesoft.als.server.{LanguageServerBaseTest, MockDiagnosticClientNotifier} +import org.mulesoft.lsp.configuration.TraceKind +import org.mulesoft.lsp.feature.common.TextDocumentIdentifier +import org.mulesoft.lsp.feature.completion.{CompletionItem, CompletionParams, CompletionRequestType} +import org.mulesoft.lsp.textsync.KnownDependencyScopes +import org.mulesoft.lsp.workspace.ExecuteCommandParams +import org.scalatest.{Assertion, EitherValues} + +import scala.concurrent.Future + +abstract class ServerWorkspaceSuggestionsTest + extends LanguageServerBaseTest + with EitherValues + with MarkerFinderTest + with ChangesWorkspaceConfiguration { + + def buildServer(): LanguageServer = { + val factory = + new WorkspaceManagerFactoryBuilder(new MockDiagnosticClientNotifier).buildWorkspaceManagerFactory() + new LanguageServerBuilder( + factory.documentManager, + factory.workspaceManager, + factory.configurationManager, + factory.resolutionTaskManager + ) + .addRequestModule(factory.completionManager) + .build() + } + + def runTest(path: String, mainFile: String, folder: String, expectedSuggestions: Set[String]): Future[Assertion] = + withServer[Assertion]( + buildServer(), + AlsInitializeParams( + None, + Some(TraceKind.Off), + rootUri = Some(filePath(folder)), + shouldRetryExternalFragments = Some(true) + ) + ) { server => + val resolved = filePath(platform.encodeURI(path)) + for { + _ <- changeWorkspaceConfiguration(server)(changeConfigArgs(Some(mainFile), filePath(folder))) + content <- this.platform.fetchContent(resolved, AMFGraphConfiguration.predefined()) + suggestions <- { + val fileContentsStr = content.stream.toString + val markerInfo = this.findMarker(fileContentsStr, "*") + getServerCompletions(resolved, server, markerInfo) + } + } yield { + val resultSet = suggestions + .map(item => item.textEdit.map(_.left.get.newText).orElse(item.insertText).value) + .toSet + val diff1 = resultSet.diff(expectedSuggestions) + val diff2 = expectedSuggestions.diff(resultSet) + + if (diff1.isEmpty && diff2.isEmpty) succeed + else + fail( + s"Difference for $path: got [${resultSet.mkString(", ")}] while expecting [${expectedSuggestions.mkString(", ")}]" + ) + } + } + def getServerCompletions( + filePath: String, + server: LanguageServer, + markerInfo: MarkerInfo + ): Future[Seq[CompletionItem]] = { + + openFile(server)(filePath, markerInfo.content) + .flatMap { _ => + val completionHandler = server.resolveHandler(CompletionRequestType).value + + completionHandler( + CompletionParams(TextDocumentIdentifier(filePath), LspRangeConverter.toLspPosition(markerInfo.position)) + ) + .flatMap(completions => { + closeFile(server)(filePath) + .map(_ => completions.left.value) + }) + } + } + +} diff --git a/als-server/shared/src/test/scala/org/mulesoft/als/server/modules/completion/workspace/WorkspaceServerSuggestionTest.scala b/als-server/shared/src/test/scala/org/mulesoft/als/server/modules/completion/workspace/WorkspaceServerSuggestionTest.scala new file mode 100644 index 0000000000..11c6ea35b8 --- /dev/null +++ b/als-server/shared/src/test/scala/org/mulesoft/als/server/modules/completion/workspace/WorkspaceServerSuggestionTest.scala @@ -0,0 +1,56 @@ +package org.mulesoft.als.server.modules.completion.workspace + +import org.mulesoft.als.server.modules.completion.ServerWorkspaceSuggestionsTest + +import scala.concurrent.ExecutionContext + +class WorkspaceServerSuggestionTest extends ServerWorkspaceSuggestionsTest { + + override implicit def executionContext: ExecutionContext = scala.concurrent.ExecutionContext.Implicits.global + override def rootPath: String = "workspace" + + test("test tooling in no-spec external fragments") { + runTest( + "external-fragment-1/schema.json", + "api.raml", + "external-fragment-1", + Set( + "\"$schema\": \"http://json-schema.org/draft/2019-09/schema#\"" + ) + ) + } + + test("test tooling in json schema external fragments") { + runTest( + "external-fragment-2/schema.json", + "api.raml", + "external-fragment-2", + Set( + "\"null\"", + "\"object\"", + "\"boolean\"", + "\"array\"", + "\"string\"", + "\"integer\"", + "\"number\"" + ) + ) + } + + test("test tooling in empty json") { + runTest( + "external-fragment-3/schema.json", + "api.yaml", + "external-fragment-3", + Set( + "{\n \"swagger\": \"2.0\"\n}", + "{\n \"asyncapi\": \"2.0.0\"\n}", + "{\n \"$schema\": \"http://json-schema.org/draft-07/schema#\"\n}", + "{\n \"openapi\": \"3.0.0\"\n}", + "{\n \"$schema\": \"http://json-schema.org/draft-04/schema#\"\n}", + "{\n \"$schema\": \"http://json-schema.org/draft/2019-09/schema#\"\n}", + "{\n \"asyncapi\": \"2.6.0\"\n}" + ) + ) + } +} diff --git a/als-server/shared/src/test/scala/org/mulesoft/als/server/workspace/DefaultProjectConfigurationProviderTest.scala b/als-server/shared/src/test/scala/org/mulesoft/als/server/workspace/DefaultProjectConfigurationProviderTest.scala index 5724594bf8..4e5ae36de4 100644 --- a/als-server/shared/src/test/scala/org/mulesoft/als/server/workspace/DefaultProjectConfigurationProviderTest.scala +++ b/als-server/shared/src/test/scala/org/mulesoft/als/server/workspace/DefaultProjectConfigurationProviderTest.scala @@ -156,10 +156,11 @@ class DefaultProjectConfigurationProviderTest extends LanguageServerBaseTest { ) p1 <- provider.getProjectInfo(ws1).getOrElse(Future(EmptyProjectConfigurationState)) editorConfig <- EditorConfiguration().getState - parseResult <- ALSConfigurationState(editorConfig, p1, None).parse(api) + parseResult <- ALSConfigurationState(editorConfig, p1, None).parse(api, asMain = true) tree <- MainFileTreeBuilder.build( parseResult, - new AmfElementVisitors(Seq.empty) + new AmfElementVisitors(Seq.empty), + parseResult.result.baseUnit.flatRefs ) _ <- provider.afterNewTree(ws1, tree) c2 <- provider.newProjectConfiguration( diff --git a/als-server/shared/src/test/scala/org/mulesoft/als/server/workspace/WorkspaceParserRepositoryTest.scala b/als-server/shared/src/test/scala/org/mulesoft/als/server/workspace/WorkspaceParserRepositoryTest.scala index f99fb71ba2..ea90c9d8cc 100644 --- a/als-server/shared/src/test/scala/org/mulesoft/als/server/workspace/WorkspaceParserRepositoryTest.scala +++ b/als-server/shared/src/test/scala/org/mulesoft/als/server/workspace/WorkspaceParserRepositoryTest.scala @@ -4,6 +4,7 @@ import amf.core.client.scala.AMFResult import amf.core.client.scala.model.document.BaseUnit import amf.core.internal.unsafe.PlatformSecrets import org.mulesoft.als.server.modules.workspace.{ParsedUnit, WorkspaceParserRepository} +import org.mulesoft.amfintegration.AmfImplicits.BaseUnitImp import org.mulesoft.amfintegration.amfconfiguration.{ ALSConfigurationState, AmfParseResult, @@ -138,7 +139,7 @@ class WorkspaceParserRepositoryTest extends AsyncFunSuite with Matchers with Pla } r <- Future .sequence(files.map(f => { - aLSConfigurationState.parse(f.uri).map(bu => repository.updateUnit(bu)) + aLSConfigurationState.parse(f.uri, asMain = true).map(bu => repository.updateUnit(bu)) })) .map(_ => repository) } yield r @@ -155,8 +156,8 @@ class WorkspaceParserRepositoryTest extends AsyncFunSuite with Matchers with Pla globalConfiguration <- configWithRL(files) repository <- Future { new WorkspaceParserRepository() } _ <- globalConfiguration - .parse(mainFile.uri) - .flatMap(bu => repository.newTree(bu)) + .parse(mainFile.uri, asMain = true) + .flatMap(bu => repository.newTree(bu, bu.result.baseUnit.flatRefs)) } yield repository } diff --git a/als-structure/shared/src/test/scala/org/mulesoft/language/outline/test/OutlineTest.scala b/als-structure/shared/src/test/scala/org/mulesoft/language/outline/test/OutlineTest.scala index 02334a9fed..fe5a138ac5 100644 --- a/als-structure/shared/src/test/scala/org/mulesoft/language/outline/test/OutlineTest.scala +++ b/als-structure/shared/src/test/scala/org/mulesoft/language/outline/test/OutlineTest.scala @@ -70,7 +70,7 @@ trait OutlineTest[T] extends AsyncFunSuite with FileAssertionTest with PlatformS // val fileContentsStr = content.stream.toString // configuration.withResourceLoader(loader(url, fileContentsStr)) - configuration.parse(url).map(cu => (cu.result.baseUnit, cu.definedBy)) + configuration.parse(url, asMain = true).map(cu => (cu.result.baseUnit, cu.definedBy)) }) .map { case (amfUnit, d) => diff --git a/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/client/Suggestions.scala b/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/client/Suggestions.scala index a24e9bb1cc..ab5245f8a4 100644 --- a/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/client/Suggestions.scala +++ b/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/client/Suggestions.scala @@ -98,11 +98,14 @@ class Suggestions( } } - private def isHeader(position: Int, originalContent: String): Boolean = - !originalContent - .substring(0, position) - .replaceAll("^\\{?\\s+", "") - .contains('\n') + private def isHeader(position: Int, originalContent: String): Boolean = { + if (originalContent.length < position) false + else + !originalContent + .substring(0, position) + .replaceAll("^\\{?\\s+", "") + .contains('\n') + } private def buildCompletionProviderAST( bu: BaseUnit, diff --git a/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/pathnavigation/PathNavigation.scala b/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/pathnavigation/PathNavigation.scala index 4014aa99d6..7d9874c0eb 100644 --- a/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/pathnavigation/PathNavigation.scala +++ b/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/pathnavigation/PathNavigation.scala @@ -69,7 +69,7 @@ private[pathnavigation] case class PathNavigation( .parse( c.stream, mime.get, - ParserContext(config = ParseConfig(alsConfiguration.getAmfConfig, DefaultErrorHandler())) + ParserContext(config = ParseConfig(alsConfiguration.getAmfConfig(false), DefaultErrorHandler())) ) match { case SyamlParsedDocument(document, _) => Some(document.node) case _ => None diff --git a/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/webapi/raml/RamlAbstractDefinition.scala b/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/webapi/raml/RamlAbstractDefinition.scala index d17740c072..38560f4d85 100644 --- a/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/webapi/raml/RamlAbstractDefinition.scala +++ b/als-suggestions/shared/src/main/scala/org/mulesoft/als/suggestions/plugins/aml/webapi/raml/RamlAbstractDefinition.scala @@ -66,7 +66,7 @@ object RamlAbstractDefinition extends AMLCompletionPlugin { private def elementInfo(params: AmlCompletionRequest): Option[ElementInfo] = findAbstractDeclaration(params).flatMap( AbstractDeclarationInformation - .extractInformation(_, params.baseUnit, params.alsConfigurationState.getAmfConfig) + .extractInformation(_, params.baseUnit, params.alsConfigurationState.getAmfConfig(false)) ) } diff --git a/als-suggestions/shared/src/test/scala/org/mulesoft/als/suggestions/test/core/aml/BasicCoreTestsAML.scala b/als-suggestions/shared/src/test/scala/org/mulesoft/als/suggestions/test/core/aml/BasicCoreTestsAML.scala index bb1905871a..a55a59fad5 100644 --- a/als-suggestions/shared/src/test/scala/org/mulesoft/als/suggestions/test/core/aml/BasicCoreTestsAML.scala +++ b/als-suggestions/shared/src/test/scala/org/mulesoft/als/suggestions/test/core/aml/BasicCoreTestsAML.scala @@ -55,7 +55,7 @@ class BasicCoreTestsAML extends CoreTest with DummyPlugins { for { editorConfigState <- editorConfiguration.getState alsConfiguration <- Future(ALSConfigurationState(editorConfigState, EmptyProjectConfigurationState, None)) - dialect <- alsConfiguration.getAmfConfig.baseUnitClient().parseDialect(p) + dialect <- alsConfiguration.getAmfConfig(false).baseUnitClient().parseDialect(p) result <- { val url = filePath("visit01.yaml") for { diff --git a/als-suggestions/shared/src/test/scala/org/mulesoft/als/suggestions/test/oas30/Oas30ComponentSuggestionTest.scala b/als-suggestions/shared/src/test/scala/org/mulesoft/als/suggestions/test/oas30/Oas30ComponentSuggestionTest.scala index d573f13f83..f3603d9600 100644 --- a/als-suggestions/shared/src/test/scala/org/mulesoft/als/suggestions/test/oas30/Oas30ComponentSuggestionTest.scala +++ b/als-suggestions/shared/src/test/scala/org/mulesoft/als/suggestions/test/oas30/Oas30ComponentSuggestionTest.scala @@ -93,7 +93,7 @@ class Oas30ComponentSuggestionTest extends AsyncFunSuite with BaseSuggestionsFor resourceLoader: ResourceLoader ): ALSConfigurationState = new ALSConfigurationState(configurationState.editorState, configurationState.projectState, Some(resourceLoader)) { - override def getAmfConfig: AMFConfiguration = - getAmfConfig(OASConfiguration.OAS30Component()) + override def getAmfConfig(asMain: Boolean = false): AMFConfiguration = + getAmfConfig(OASConfiguration.OAS30Component(), asMain) } } From 864cbda22cec5fe3258d0f404f6fe89a2f4cdb6f Mon Sep 17 00:00:00 2001 From: Leandro Libarona Date: Fri, 14 Jun 2024 16:55:20 -0300 Subject: [PATCH 2/5] add telemetry for reparsing external fragments --- .../feature/telemetry/TelemetryProvider.scala | 2 ++ .../workspace/WorkspaceContentManager.scala | 21 +++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/als-lsp/shared/src/main/scala/org/mulesoft/lsp/feature/telemetry/TelemetryProvider.scala b/als-lsp/shared/src/main/scala/org/mulesoft/lsp/feature/telemetry/TelemetryProvider.scala index ee41fb232b..95fd3234b9 100644 --- a/als-lsp/shared/src/main/scala/org/mulesoft/lsp/feature/telemetry/TelemetryProvider.scala +++ b/als-lsp/shared/src/main/scala/org/mulesoft/lsp/feature/telemetry/TelemetryProvider.scala @@ -10,6 +10,8 @@ object MessageTypes extends Enumeration { type MessageTypes = String val BEGIN_PARSE = "BEGIN_PARSE" val END_PARSE = "END_PARSE" + val BEGIN_PARSE_EXTERNAL = "BEGIN_PARSE_EXTERNAL" + val END_PARSE_EXTERNAL = "END_PARSE_EXTERNAL" val BEGIN_PARSE_PATCHED = "BEGIN_PARSE_PATCHED" val END_PARSE_PATCHED = "END_PARSE_PATCHED" val BEGIN_REPORT = "BEGIN_REPORT" diff --git a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceContentManager.scala b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceContentManager.scala index 902d4e74c9..8d874c5889 100644 --- a/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceContentManager.scala +++ b/als-server/shared/src/main/scala/org/mulesoft/als/server/modules/workspace/WorkspaceContentManager.scala @@ -345,12 +345,21 @@ class WorkspaceContentManager private ( } private def getTweakedRefs(baseUnit: BaseUnit): Future[Seq[BaseUnit]] = - Future.sequence(baseUnit.flatRefs.map { - case bu @ (_: ExternalFragment | _: DataTypeFragment) if shouldRetryExternalFragments => - innerParse(bu.identifier, asMain = false) - .map(r => r.result.baseUnit) - case bu => Future.successful(bu) - }) + Logger.timeProcess( + "AMF Parse - External Fragments", + MessageTypes.BEGIN_PARSE_EXTERNAL, + MessageTypes.END_PARSE_EXTERNAL, + "WorkspaceContentManager : getTweakedRefs", + folderUri, + () => + Future.sequence(baseUnit.flatRefs.map { + case bu @ (_: ExternalFragment | _: DataTypeFragment) if shouldRetryExternalFragments => + innerParse(bu.identifier, asMain = false) + .map(r => r.result.baseUnit) + case bu => Future.successful(bu) + }), + UUID.randomUUID().toString + ) private def parse(uri: String, asMain: Boolean, uuid: String): Future[AmfParseResult] = { Logger.timeProcess( From a59e368cc7ecebcfa53041e3d0e2fec9a87e8725 Mon Sep 17 00:00:00 2001 From: emilianoascona Date: Thu, 4 Jul 2024 16:33:42 -0300 Subject: [PATCH 3/5] Bump dependencies --- dependencies.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies.properties b/dependencies.properties index baf7c45609..009c2a85e1 100644 --- a/dependencies.properties +++ b/dependencies.properties @@ -1,5 +1,5 @@ version=6.3.0-SNAPSHOT -amf=5.5.2-0 +amf=5.5.3 amf.custom-validator.js=1.7.2 -amf.custom-validator-scalajs=0.5.12 +amf.custom-validator-scalajs=0.5.13 amf-antlr-parsers=0.7.25 From ad39dfccba92a07fa1812e4f6d70b67ce47ca9a7 Mon Sep 17 00:00:00 2001 From: emilianoascona Date: Thu, 4 Jul 2024 17:11:47 -0300 Subject: [PATCH 4/5] bump to RC version --- dependencies.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.properties b/dependencies.properties index 009c2a85e1..894f80b8d3 100644 --- a/dependencies.properties +++ b/dependencies.properties @@ -1,4 +1,4 @@ -version=6.3.0-SNAPSHOT +version=6.2.2-RC amf=5.5.3 amf.custom-validator.js=1.7.2 amf.custom-validator-scalajs=0.5.13 From e94cf07b9668747946f2351784ddf47476a7933a Mon Sep 17 00:00:00 2001 From: emilianoascona Date: Thu, 4 Jul 2024 17:25:34 -0300 Subject: [PATCH 5/5] Bump to stable version --- dependencies.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dependencies.properties b/dependencies.properties index 894f80b8d3..a6d85244a7 100644 --- a/dependencies.properties +++ b/dependencies.properties @@ -1,4 +1,4 @@ -version=6.2.2-RC +version=6.2.2 amf=5.5.3 amf.custom-validator.js=1.7.2 amf.custom-validator-scalajs=0.5.13