diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/BaseScope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/BaseScope.java new file mode 100644 index 00000000000..264270220fe --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/BaseScope.java @@ -0,0 +1,35 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; + +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +public class BaseScope implements IScope { + @Override + public Set getCapabilities() { + return Collections.emptySet(); + } + + @Override + public Stream getMethods() { + return Stream.empty(); + } + + @Override + public Stream getProperties() { + return Stream.empty(); + } + + @Override + public Optional getMethod(String name) { + return Optional.empty(); + } + + @Override + public Optional getProperty(String name) { + return Optional.empty(); + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/Capability.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/Capability.java new file mode 100644 index 00000000000..ece66a1c14d --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/Capability.java @@ -0,0 +1,7 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +public enum Capability { + CLIENT, + SERVER, + PUBLIC +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ConfigurationScope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ConfigurationScope.java new file mode 100644 index 00000000000..41bc6cd3faf --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ConfigurationScope.java @@ -0,0 +1,62 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.ServerContext; +import com.github._1c_syntax.bsl.types.MDOType; +import com.github._1c_syntax.mdclasses.mdo.MDCommonModule; + +import java.util.Collection; +import java.util.Set; + +public class ConfigurationScope extends SimpleScope { + + private static final Collection rootProperties = Set.of( + MDOType.EXCHANGE_PLAN, + MDOType.CONSTANT, + MDOType.CATALOG, + MDOType.DOCUMENT, + MDOType.DOCUMENT_JOURNAL, + MDOType.ENUM, + MDOType.REPORT, + MDOType.DATA_PROCESSOR, + MDOType.CHART_OF_CHARACTERISTIC_TYPES, + MDOType.CHART_OF_ACCOUNTS, + MDOType.CHART_OF_CALCULATION_TYPES, + MDOType.INFORMATION_REGISTER, + MDOType.ACCUMULATION_REGISTER, + MDOType.ACCOUNTING_REGISTER, + MDOType.CALCULATION_REGISTER, + MDOType.BUSINESS_PROCESS, + MDOType.TASK, + MDOType.EXTERNAL_DATA_SOURCE); + + private final ServerContext serverContext; + + public ConfigurationScope(ServerContext serverContext) { + this.serverContext = serverContext; + + for (var module : serverContext.getConfiguration().getCommonModules().values()) { + if (module.isGlobal()) { + appendGlobalCommonModule(module); + } else { + appendCommonModule(module); + } + } + + for (var type : rootProperties) { + var property = IScopeOwner.create(type); + properties.put(type.getGroupName(), property); + properties.put(type.getGroupNameRu(), property); + } + + } + + private void appendCommonModule(MDCommonModule module) { + properties.put(module.getName(), IScopeOwner.create(module)); + } + + private void appendGlobalCommonModule(MDCommonModule module) { + for (var method : Utils.getMethods(module, serverContext)) { + methods.put(method.getName(), method); + } + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/IScope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/IScope.java new file mode 100644 index 00000000000..15312745bae --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/IScope.java @@ -0,0 +1,21 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; + +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +public interface IScope { + IScope EMPTY = new BaseScope(); + + Set getCapabilities(); + + Stream getMethods(); + + Stream getProperties(); + + Optional getMethod(String name); + + Optional getProperty(String name); +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/IScopeOwner.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/IScopeOwner.java new file mode 100644 index 00000000000..ce0da171985 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/IScopeOwner.java @@ -0,0 +1,17 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import lombok.Getter; + +public class IScopeOwner { + + public static IScopeOwner create(Object owner) { + return new IScopeOwner(owner); + } + + private IScopeOwner(Object owner) { + this.owner = owner; + } + + @Getter + private final Object owner; +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeProvider.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeProvider.java new file mode 100644 index 00000000000..b70e394970c --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeProvider.java @@ -0,0 +1,40 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; +import com.github._1c_syntax.utils.Lazy; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +@Component +@RequiredArgsConstructor +public class ScopeProvider { + + private final ScopeResolver resolver; + private final Lazy configurationScope = new Lazy<>(this::createConfigurationScope); + + public IScope getGlobalScope() { + return UnionContext.builder() + .innerScope(getConfigurationScope()) + .innerScope(resolver.getPlatformScope()) + .build(); + } + + public IScope getConfigurationScope() { + return configurationScope.getOrCompute(); + } + + public IScope getScope(DocumentContext document) { + return UnionContext.builder() + .innerScope(resolver.createFilteredScope(getGlobalScope(), resolver.getCapabilities(document))) + .innerScope(resolver.createScope(document)) + .build(); + } + + public IScope getScope(IScopeOwner owner) { + return resolver.createScope(owner); + } + + private ConfigurationScope createConfigurationScope(){ + return resolver.createConfigurationScope(); + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeResolver.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeResolver.java new file mode 100644 index 00000000000..3aca21e2824 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeResolver.java @@ -0,0 +1,147 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.DocumentContext; +import com.github._1c_syntax.bsl.languageserver.context.ServerContext; +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; +import com.github._1c_syntax.bsl.mdo.MDObject; +import com.github._1c_syntax.bsl.mdo.ModuleOwner; +import com.github._1c_syntax.bsl.types.MDOType; +import com.github._1c_syntax.mdclasses.mdo.MDCommonModule; +import com.github._1c_syntax.mdclasses.mdo.MDOHasChildren; +import com.github._1c_syntax.mdclasses.mdo.attributes.AbstractMDOAttribute; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; + +import java.net.URI; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Stream; + +@Component +@RequiredArgsConstructor +public class ScopeResolver { + + private final ServerContext serverContext; + + public IScope getPlatformScope() { + return IScope.EMPTY; + } + + public IScope createScope(IScopeOwner owner) { + if (owner.getOwner() instanceof MDOType) { + return new MDOTypeScope((MDOType) owner.getOwner(), serverContext); + } else if (owner.getOwner() instanceof MDObject) { + return createScope((MDObject) owner.getOwner()); + } + return IScope.EMPTY; + } + + public IScope createScope(DocumentContext document) { + return IScope.EMPTY; + } + + IScope createFilteredScope(IScope scope, Set capabilities) { + return scope; + } + + ConfigurationScope createConfigurationScope() { + return new ConfigurationScope(serverContext); + } + + IScope createScope(MDObject object) { + var builder = UnionContext.builder(); + + if (object instanceof MDCommonModule) { + builder.innerScope(new CommonModuleScope((MDCommonModule) object, serverContext)); + } else if (object instanceof ModuleOwner) { + var uri = Utils.getManagerModuleURI((ModuleOwner) object, serverContext); + if (uri != null) { + builder.innerScope(new ModuleScope(uri, serverContext)); + } + } +// if (object instanceof MDOHasChildren) { +// builder.innerScope(new MDOHasChildrenScope((MDOHasChildren) object)); +// } + + return builder.build(); + } + + Set getCapabilities(DocumentContext document) { + return Collections.emptySet(); + } + + @RequiredArgsConstructor + static class MDOTypeScope extends BaseScope { + private final MDOType type; + private final ServerContext serverContext; + + @Override + public Stream getProperties() { + return serverContext.getConfiguration().getOrderedTopMDObjects().get(type).stream() + .map(IScopeOwner::create); + } + + @Override + public Optional getProperty(String name) { + return serverContext.getConfiguration().getOrderedTopMDObjects().get(type).stream() + .filter(abstractMDObjectBase -> abstractMDObjectBase.getName().equalsIgnoreCase(name)) + .map(IScopeOwner::create) + .findFirst(); + } + } + + @RequiredArgsConstructor + static class CommonModuleScope extends BaseScope { + + private final MDCommonModule module; + private final ServerContext serverContext; + + @Override + public Stream getMethods() { + return Utils.getMethods(module, serverContext).stream(); + } + + @Override + public Optional getMethod(String name) { + return Utils.getMethod(module, serverContext, name); + } + } + + @RequiredArgsConstructor + static class ModuleScope extends BaseScope { + private final URI moduleUri; + private final ServerContext serverContext; + + @Override + public Stream getMethods() { + return Utils.getMethods(moduleUri, serverContext).stream(); + } + + @Override + public Optional getMethod(String name) { + return Utils.getMethod(moduleUri, serverContext, name); + } + } + + @RequiredArgsConstructor + static class MDOHasChildrenScope extends BaseScope { + private final MDOHasChildren object; + + @Override + public Stream getProperties() { + return object.getChildren().stream() + .filter(AbstractMDOAttribute.class::isInstance) + .map(IScopeOwner::create); + } + + @Override + public Optional getProperty(String name) { + return object.getChildren().stream() + .filter(AbstractMDOAttribute.class::isInstance) + .filter(it -> it.getName().equalsIgnoreCase(name)) + .map(IScopeOwner::create) + .findFirst(); + } + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/SimpleScope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/SimpleScope.java new file mode 100644 index 00000000000..62d875cea3f --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/SimpleScope.java @@ -0,0 +1,32 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; +import org.apache.commons.collections4.map.CaseInsensitiveMap; + +import java.util.Optional; +import java.util.stream.Stream; + +public class SimpleScope extends BaseScope { + protected final CaseInsensitiveMap methods = new CaseInsensitiveMap<>(); + protected final CaseInsensitiveMap properties = new CaseInsensitiveMap<>(); + + @Override + public Stream getMethods() { + return methods.values().stream(); + } + + @Override + public Stream getProperties() { + return properties.values().stream(); + } + + @Override + public Optional getMethod(String name) { + return Optional.of(methods.get(name)); + } + + @Override + public Optional getProperty(String name) { + return Optional.ofNullable(properties.get(name)); + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ThisObjectResolver.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ThisObjectResolver.java new file mode 100644 index 00000000000..9e85dec761e --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/ThisObjectResolver.java @@ -0,0 +1,5 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +public class ThisObjectResolver { + +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/UnionContext.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/UnionContext.java new file mode 100644 index 00000000000..0dc23e028eb --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/UnionContext.java @@ -0,0 +1,46 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; +import lombok.Builder; +import lombok.Singular; + +import java.util.Collection; +import java.util.Optional; +import java.util.stream.Stream; + +@Builder +public class UnionContext extends BaseScope { + + @Singular + private Collection innerScopes; + + @Override + public Stream getMethods() { + return innerScopes.stream() + .flatMap(IScope::getMethods); + } + + @Override + public Stream getProperties() { + return innerScopes.stream() + .flatMap(IScope::getProperties); + } + + @Override + public Optional getMethod(String name) { + return innerScopes.stream() + .map(it -> it.getMethod(name)) + .filter(Optional::isPresent) + .findFirst() + .orElseGet(Optional::empty); + } + + @Override + public Optional getProperty(String name) { + return innerScopes.stream() + .map(it -> it.getProperty(name)) + .filter(Optional::isPresent) + .findFirst() + .orElseGet(Optional::empty); + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/Utils.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/Utils.java new file mode 100644 index 00000000000..ec065383e84 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/scope/Utils.java @@ -0,0 +1,45 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.ServerContext; +import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol; +import com.github._1c_syntax.bsl.mdo.ModuleOwner; +import com.github._1c_syntax.bsl.types.ModuleType; +import com.github._1c_syntax.mdclasses.mdo.MDCommonModule; + +import java.net.URI; +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; + +public class Utils { + + public static Collection getMethods(MDCommonModule module, ServerContext serverContext) { + return getMethods(module.getModules().get(0).getUri(), serverContext); + } + + public static Collection getMethods(URI moduleUri, ServerContext serverContext) { + var document = serverContext.getDocument(moduleUri); + if (document == null) { + return Collections.emptyList(); + } + + return document.getSymbolTree().getMethods(); + } + + public static Optional getMethod(MDCommonModule module, ServerContext serverContext, String name) { + return getMethod(module.getModules().get(0).getUri(), serverContext, name); + } + + public static Optional getMethod(URI moduleUri, ServerContext serverContext, String name) { + var document = serverContext.getDocument(moduleUri); + if (document == null) { + return Optional.empty(); + } + return document.getSymbolTree().getMethodSymbol(name); + } + + public static URI getManagerModuleURI(ModuleOwner object, ServerContext serverContext) { + var modules = serverContext.getConfiguration().getModulesByMDORef(object.getMdoReference().getMdoRef()); + return modules.get(ModuleType.ManagerModule); + } +} diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeProviderTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeProviderTest.java new file mode 100644 index 00000000000..6826e50779a --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/scope/ScopeProviderTest.java @@ -0,0 +1,53 @@ +package com.github._1c_syntax.bsl.languageserver.scope; + +import com.github._1c_syntax.bsl.languageserver.context.ServerContext; +import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterClass; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import javax.annotation.PostConstruct; +import java.nio.file.Paths; + +import static com.github._1c_syntax.bsl.languageserver.util.TestUtils.PATH_TO_METADATA; +import static org.junit.jupiter.api.Assertions.*; + +@SpringBootTest +@CleanupContextBeforeClassAndAfterClass +class ScopeProviderTest { + @Autowired + ScopeProvider provider; + + @Autowired + private ServerContext serverContext; + + private static final String PATH_TO_FILE = "./src/test/resources/providers/references.bsl"; + + @PostConstruct + void prepareServerContext() { + serverContext.setConfigurationRoot(Paths.get(PATH_TO_METADATA)); + serverContext.populateContext(); + } + + @Test + void getConfigurationScope() { + + var configurationScope = provider.getConfigurationScope(); + configurationScope.getMethod("ГлобальнаяСервернаяПроцедура").get(); + + var property = configurationScope.getProperty("ПервыйОбщийМодуль").get(); + provider.getScope(property).getMethod("НеУстаревшаяПроцедура").get(); + provider.getScope(property).getMethod("УстаревшаяФункция").get(); + + property = configurationScope.getProperty("Документы").get(); + provider.getScope(property).getProperty("Документ1").get(); + + property = configurationScope.getProperty("Catalogs").get(); + property = provider.getScope(property).getProperty("Справочник1").get(); + + var catalogScope = provider.getScope(property); + catalogScope.getMethod("ТестЭкспортная").get(); + catalogScope.getProperty("Реквизит1").get(); + } + +} \ No newline at end of file