diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/MdoRefBuilder.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/MdoRefBuilder.java index 08a25d7ce9e..3c3223e3e00 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/MdoRefBuilder.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/MdoRefBuilder.java @@ -73,6 +73,10 @@ public static String getMdoRef(DocumentContext documentContext) { // осторожно! не менять на вызов documentContext.getMdoRef, а то зациклится var mdoRef = documentContext.getMdObject() .map(MD::getMdoRef) + // .os-файл library-сущности OneScript не имеет объекта метаданных: его mdoRef — каноничное + // имя библиотеки (зарегистрированное индексатором до добавления документа), чтобы он + // резолвился в documentsByMDORef единообразно с BSL-объектами. + .or(() -> documentContext.getServerContext().findOScriptLibraryName(documentContext.getUri())) .orElseGet(() -> documentContext.getUri().toString()); return stringInterner.intern(mdoRef); } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java index 3eae30b5d40..1a53450a6e4 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java @@ -48,10 +48,12 @@ import java.io.File; import java.net.URI; import java.nio.file.Path; +import java.text.Normalizer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; +import java.util.Locale; import java.util.Map; import java.util.Optional; import java.util.Set; @@ -117,6 +119,23 @@ public class ServerContext { */ private final Map> documentsByMDORef = new ConcurrentHashMap<>(); + /** + * Каталог библиотечных сущностей OneScript: {@code nameKey → (тип модуля, документ)}. + * Наполняется извне (индексатором OneScript-библиотек) и служит резолву имени класса/модуля + * библиотеки в документ .os-файла — аналогично тому, как {@link #findCommonModule(String)} + * резолвит общий модуль из метаданных конфигурации. Регистронезависимый (ключ — {@code nameKey}); + * под несколькими именами (алиасами) может резолвиться один и тот же документ. + */ + private final Map oscriptLibrariesByNameKey = new ConcurrentHashMap<>(); + /** + * {@code URI .os-файла → каноничное имя} его library-сущности. Наполняется индексатором + * OneScript-библиотек до добавления документа в контекст, чтобы {@code mdoRef} + * такого документа вычислялся как имя библиотеки (см. {@link MdoRefBuilder}), а не как URI: + * тогда документ индексируется в {@link #documentsByMDORef} под своим именем и резолвится + * единообразно с BSL-объектами. Первое зарегистрированное имя выигрывает (у одного файла + * может быть несколько ролей — см. {@code OScriptModuleTypeResolver}). + */ + private final Map oscriptLibraryNamesByUri = new ConcurrentHashMap<>(); private final Map documentLocks = new ConcurrentHashMap<>(); private final Map states = new ConcurrentHashMap<>(); @@ -331,6 +350,8 @@ public void clear() { states.clear(); documentsByMDORef.clear(); mdoRefs.clear(); + oscriptLibrariesByNameKey.clear(); + oscriptLibraryNamesByUri.clear(); documentLocks.clear(); commonModuleCache.invalidateAll(); configurationMetadata.clear(); @@ -470,6 +491,120 @@ public Optional findCommonModule(String name) { return commonModuleCache.get(name, key -> getConfiguration().findCommonModule(key)); } + /** + * Привязать {@code URI .os-файла} к каноничному имени его library-сущности до + * добавления документа в контекст. Нужно, чтобы {@code mdoRef} документа вычислился как имя + * библиотеки (см. {@link MdoRefBuilder}), а не как URI. Первое имя выигрывает: у одного файла + * может быть несколько ролей, но идентичность документа (mdoRef) должна быть стабильной. + * + * @param uri URI .os-файла (абсолютный, нормализованный) + * @param qualifiedName каноничное имя сущности + */ + public void registerOScriptLibraryName(URI uri, String qualifiedName) { + oscriptLibraryNamesByUri.putIfAbsent(uri, qualifiedName); + } + + /** + * Каноничное имя library-сущности OneScript, привязанное к URI через + * {@link #registerOScriptLibraryName}. Используется {@link MdoRefBuilder} при вычислении + * {@code mdoRef} .os-документов, у которых нет объекта метаданных. + * + * @param uri URI .os-файла + * @return каноничное имя либо {@code empty} + */ + public Optional findOScriptLibraryName(URI uri) { + return Optional.ofNullable(oscriptLibraryNamesByUri.get(uri)); + } + + /** + * Зарегистрировать библиотечную сущность OneScript: занести её в каталог имён (для резолва + * в {@link #findLibraryClass}/{@link #findLibraryModule}) и проиндексировать документ в + * {@code documentsByMDORef} под его {@code mdoRef} (= каноничным именем, см. + * {@link #registerOScriptLibraryName}), чтобы он резолвился через + * {@link #getDocument(String, ModuleType)}. Вызывается индексатором OneScript-библиотек. + * + * @param qualifiedName каноничное имя сущности (алиас в каталоге имён) + * @param moduleType {@link ModuleType#OScriptClass} или {@link ModuleType#OScriptModule} + * @param documentContext документ .os-файла + */ + public void registerOScriptLibrary(String qualifiedName, ModuleType moduleType, DocumentContext documentContext) { + oscriptLibrariesByNameKey.put(oscriptNameKey(qualifiedName), new OScriptLibrarySymbol(moduleType, documentContext)); + documentsByMDORef + .computeIfAbsent(documentContext.getMdoRef(), k -> Collections.synchronizedMap(new EnumMap<>(ModuleType.class))) + .put(moduleType, documentContext); + } + + /** + * Снять регистрацию библиотечной сущности OneScript: из каталога имён, из + * {@code documentsByMDORef} и из привязки {@code URI → имя}. Вызывается индексатором при + * удалении файла/пере-индексации. + * + * @param qualifiedName каноничное имя сущности + * @param moduleType тип модуля сущности + */ + public void removeOScriptLibrary(String qualifiedName, ModuleType moduleType) { + var symbol = oscriptLibrariesByNameKey.remove(oscriptNameKey(qualifiedName)); + if (symbol == null) { + return; + } + var documentContext = symbol.documentContext(); + var group = documentsByMDORef.get(documentContext.getMdoRef()); + if (group != null) { + group.remove(moduleType); + if (group.isEmpty()) { + documentsByMDORef.remove(documentContext.getMdoRef()); + } + } + oscriptLibraryNamesByUri.remove(documentContext.getUri()); + } + + /** + * Найти документ зарегистрированного library-класса OneScript по имени из исходного кода. + * + * @param name имя из кода (произвольный регистр) + * @return документ .os-файла класса либо {@code empty} + */ + public Optional findLibraryClass(String name) { + return findOScriptLibrarySymbol(name, ModuleType.OScriptClass); + } + + /** + * Найти документ зарегистрированного library-модуля OneScript по имени из исходного кода. + * + * @param name имя из кода (произвольный регистр) + * @return документ .os-файла модуля либо {@code empty} + */ + public Optional findLibraryModule(String name) { + return findOScriptLibrarySymbol(name, ModuleType.OScriptModule); + } + + private Optional findOScriptLibrarySymbol(String name, ModuleType moduleType) { + if (name == null || name.isBlank()) { + return Optional.empty(); + } + return Optional.ofNullable(oscriptLibrariesByNameKey.get(oscriptNameKey(name))) + .filter(symbol -> symbol.moduleType() == moduleType) + .map(OScriptLibrarySymbol::documentContext); + } + + /** + * Нормализация имени OneScript-сущности для регистронезависимого сравнения. + * Должна совпадать с {@code OScriptLibraryIndex.nameKey}: NFC + lower-case (Locale.ROOT) — + * имена в .os-файлах хранятся в NFD, а в коде набираются в NFC. + */ + private static String oscriptNameKey(String name) { + return Normalizer.normalize(name, Normalizer.Form.NFC).toLowerCase(Locale.ROOT); + } + + /** + * Запись каталога библиотечных сущностей OneScript. + * + * @param moduleType тип модуля (класс/модуль) + * @param documentContext документ .os-файла сущности + */ + public record OScriptLibrarySymbol(ModuleType moduleType, DocumentContext documentContext) { + } + private DocumentContext createDocumentContext(URI uri) { var documentContext = documentContextProvider.getObject(uri, this); diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/KeywordSymbol.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/KeywordSymbol.java index ab9ad2e28c0..981d2a04427 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/KeywordSymbol.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/context/symbol/KeywordSymbol.java @@ -28,7 +28,7 @@ /** * Synthetic-символ BSL-keyword'а ({@code Если}, {@code Истина}, {@code Цикл}…). *

- * Создаётся on-the-fly в {@link com.github._1c_syntax.bsl.languageserver.references.KeywordReferenceFinder} + * Создаётся on-the-fly в {@link com.github._1c_syntax.bsl.languageserver.types.references.KeywordReferenceFinder} * при попадании курсора на keyword-токен — keyword'ы не являются * source-defined-символами и не лежат в symbol-tree модуля, но участвуют * в общем reference/hover-flow на правах обычного {@link Symbol}. diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/KeywordSymbolMarkupContentBuilder.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/KeywordSymbolMarkupContentBuilder.java index c4d676f2a5d..6336a36cb67 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/KeywordSymbolMarkupContentBuilder.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/hover/KeywordSymbolMarkupContentBuilder.java @@ -35,7 +35,7 @@ * ({@code Если}, {@code Истина}, {@code Цикл}…). *

* {@link KeywordSymbol} приходит уже с локализованным описанием - * (выбранным {@link com.github._1c_syntax.bsl.languageserver.references.KeywordReferenceFinder} + * (выбранным {@link com.github._1c_syntax.bsl.languageserver.types.references.KeywordReferenceFinder} * по текущей локали LS и AST-контексту). Билдер только оборачивает * keyword и описание в markdown-формат: *

diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/HoverProvider.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/HoverProvider.java
index 4c6e4360677..77bb84810b8 100644
--- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/HoverProvider.java
+++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/providers/HoverProvider.java
@@ -42,7 +42,7 @@
  * подбору ссылки, живёт в реализациях {@link com.github._1c_syntax.bsl.languageserver.references.ReferenceFinder}
  * (в том числе synthetic-символы для аннотаций и keyword'ов —
  * {@link com.github._1c_syntax.bsl.languageserver.references.AnnotationReferenceFinder},
- * {@link com.github._1c_syntax.bsl.languageserver.references.KeywordReferenceFinder}),
+ * {@link com.github._1c_syntax.bsl.languageserver.types.references.KeywordReferenceFinder}),
  * всё, что относится к формированию текста подсказки — в соответствующем
  * {@code MarkupContentBuilder}.
  *
diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndexFiller.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndexFiller.java
index b5fdaa22211..2f9e2b9e3c5 100644
--- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndexFiller.java
+++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/ReferenceIndexFiller.java
@@ -25,12 +25,9 @@
 import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
 import com.github._1c_syntax.bsl.languageserver.context.events.DocumentContextContentChangedEvent;
 import com.github._1c_syntax.bsl.languageserver.context.events.ServerContextDocumentRemovedEvent;
-import com.github._1c_syntax.bsl.languageserver.context.symbol.ConstructorSymbol;
 import com.github._1c_syntax.bsl.languageserver.context.symbol.MethodSymbol;
 import com.github._1c_syntax.bsl.languageserver.context.symbol.SourceDefinedSymbol;
-import com.github._1c_syntax.bsl.languageserver.context.symbol.SymbolTree;
 import com.github._1c_syntax.bsl.languageserver.context.symbol.VariableSymbol;
-import com.github._1c_syntax.bsl.languageserver.types.oscript.OScriptLibraryIndex;
 import com.github._1c_syntax.bsl.languageserver.context.MdoRefBuilder;
 import com.github._1c_syntax.bsl.languageserver.utils.Methods;
 import com.github._1c_syntax.bsl.languageserver.utils.ModuleReference;
@@ -85,7 +82,6 @@ public class ReferenceIndexFiller {
 
   private final ReferenceIndex index;
   private final LanguageServerConfiguration configuration;
-  private final OScriptLibraryIndex oScriptLibraryIndex;
 
   @EventListener
   public void handleEvent(DocumentContextContentChangedEvent event) {
@@ -267,39 +263,33 @@ private void tryRegisterLibraryClassReference(BSLParser.NewExpressionContext ctx
         return;
       }
       var name = typeName.IDENTIFIER().getText();
-      var libUri = oScriptLibraryIndex.findClassUri(name);
-      if (libUri.isEmpty()) {
+      var libClass = documentContext.getServerContext().findLibraryClass(name);
+      if (libClass.isEmpty()) {
         return;
       }
-      var libMdoRef = libUri.get().toString();
-      var moduleType = actualLibraryModuleType(libUri.get(), ModuleType.OScriptClass);
+      var libDocument = libClass.get();
+      var mdoRef = libDocument.getMdoRef();
       var range = Ranges.create(typeName.IDENTIFIER());
 
-      var ctor = libraryClassConstructor(libUri.get());
+      var ctor = libDocument.getSymbolTree().getConstructor();
       if (ctor.isPresent()) {
         index.addMethodCall(
           documentContext.getUri(),
-          libMdoRef,
-          moduleType,
+          mdoRef,
+          ModuleType.OScriptClass,
           ctor.get().getName(),
           range
         );
       } else {
         index.addModuleReference(
           documentContext.getUri(),
-          libMdoRef,
-          moduleType,
+          mdoRef,
+          ModuleType.OScriptClass,
           range
         );
       }
     }
 
-    private Optional libraryClassConstructor(URI libUri) {
-      return Optional.ofNullable(documentContext.getServerContext().getDocument(libUri))
-        .map(DocumentContext::getSymbolTree)
-        .flatMap(SymbolTree::getConstructor);
-    }
-
     /**
      * Если идентификатор соответствует имени зарегистрированного OneScript
      * library-модуля, регистрирует:
@@ -312,41 +302,27 @@ private void tryRegisterLibraryModuleCall(@Nullable TerminalNode identifier, Opt
       if (identifier == null) {
         return;
       }
-      var libUri = oScriptLibraryIndex.findModuleUri(identifier.getText());
-      if (libUri.isEmpty()) {
+      var libModule = documentContext.getServerContext().findLibraryModule(identifier.getText());
+      if (libModule.isEmpty()) {
         return;
       }
-      var libMdoRef = libUri.get().toString();
-      var moduleType = actualLibraryModuleType(libUri.get(), ModuleType.OScriptModule);
+      var mdoRef = libModule.get().getMdoRef();
 
       // Ссылка на сам identifier модуля — нужна для go-to-definition без точки.
       index.addModuleReference(
         documentContext.getUri(),
-        libMdoRef,
-        moduleType,
+        mdoRef,
+        ModuleType.OScriptModule,
         Ranges.create(identifier)
       );
 
       if (methodName.isPresent()) {
         var methodNameToken = methodName.get();
-        addMethodCall(libMdoRef, moduleType, Strings.trimQuotes(methodNameToken.getText()),
+        addMethodCall(mdoRef, ModuleType.OScriptModule, Strings.trimQuotes(methodNameToken.getText()),
           Ranges.create(methodNameToken));
       }
     }
 
-    /**
-     * Возвращает фактический {@link ModuleType} документа библиотечного .os-файла.
-     * Один .os может быть зарегистрирован одновременно и как класс, и как модуль
-     * (см. {@link OScriptLibraryIndex}); чтобы ссылка корректно резолвилась через
-     * {@code ServerContext.getDocument(mdoRef, moduleType)}, используем тип
-     * фактически загруженного {@link DocumentContext}, а не «теоретический»
-     * тип из роли регистрации.
-     */
-    private ModuleType actualLibraryModuleType(java.net.URI libUri, ModuleType fallback) {
-      var dc = documentContext.getServerContext().getDocument(libUri);
-      return dc != null ? dc.getModuleType() : fallback;
-    }
-
     /**
      * Добавляет ссылку на модуль по позиции идентификатора, только если идентификатор является
      * именем общего модуля. Для вызовов вида Справочники.Имя.Метод() ссылка не добавляется,
@@ -585,8 +561,9 @@ public ParserRuleContext visitAssignment(BSLParser.AssignmentContext ctx) {
       if (typeName == null || typeName.IDENTIFIER() == null) {
         return null;
       }
-      return oScriptLibraryIndex.findClassUri(typeName.IDENTIFIER().getText())
-        .map(java.net.URI::toString)
+      return documentContext.getServerContext()
+        .findLibraryClass(typeName.IDENTIFIER().getText())
+        .map(DocumentContext::getMdoRef)
         .orElse(null);
     }
 
diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptLibraryIndex.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptLibraryIndex.java
index 97be1bf29fa..800ce82cc0c 100644
--- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptLibraryIndex.java
+++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/oscript/OScriptLibraryIndex.java
@@ -138,6 +138,9 @@ public void handleWorkspaceAdded(WorkspaceAddedEvent event) {
    */
   public List reindex(ServerContext serverContext) {
     oScriptModuleTypeResolver.clear();
+    // снимаем прежние регистрации lib-сущностей в ServerContext (каталог имён + documentsByMDORef)
+    entriesByName.values().forEach(entry ->
+      serverContext.removeOScriptLibrary(entry.qualifiedName(), moduleTypeOf(entry.kind())));
     entriesByUri.clear();
     entriesByName.clear();
 
@@ -172,8 +175,12 @@ public void handleDocumentRemoved(ServerContextDocumentRemovedEvent event) {
       return;
     }
     oScriptModuleTypeResolver.unregister(uri);
+    var serverContext = serverContextProvider.getServerContext(uri).orElse(null);
     for (var entry : entries) {
       entriesByName.remove(nameKey(entry.qualifiedName()));
+      if (serverContext != null) {
+        serverContext.removeOScriptLibrary(entry.qualifiedName(), moduleTypeOf(entry.kind()));
+      }
     }
     oScriptModuleMembersProvider.unregister(uri);
   }
@@ -404,7 +411,7 @@ void registerEntry(String rawQualifiedName, Path osFile, EntryKind kind, ServerC
     // OScriptModuleMembersProvider → TypeRegistry, и в completion-метки.
     var qualifiedName = Normalizer.normalize(rawQualifiedName, Normalizer.Form.NFC);
     var uri = Absolute.uri(osFile.toUri());
-    var moduleType = kind == EntryKind.CLASS ? ModuleType.OScriptClass : ModuleType.OScriptModule;
+    var moduleType = moduleTypeOf(kind);
     // Сначала сообщаем резолверу тип модуля — это нужно, чтобы при первом
     // событии DocumentContextContentChangedEvent документ уже знал свой
     // ModuleType (через DocumentContext.computeModuleType фолбэк).
@@ -418,12 +425,21 @@ void registerEntry(String rawQualifiedName, Path osFile, EntryKind kind, ServerC
     entriesByUri.computeIfAbsent(uri, k -> new java.util.concurrent.CopyOnWriteArrayList<>()).add(entry);
     entriesByName.put(nameKey(qualifiedName), entry);
 
+    // Привязываем URI → каноничное имя ДО addDocument: тогда mdoRef создаваемого документа
+    // вычислится как имя библиотеки (а не URI), и он проиндексируется в documentsByMDORef под
+    // своим именем — единообразно с BSL-объектами.
+    serverContext.registerOScriptLibraryName(uri, qualifiedName);
+
     // Добавляем .os-файл в ServerContext как обычный документ. SymbolTreeComputer,
     // ReferenceIndexFiller, OScriptModuleMembersProvider и прочие подхватят его
     // через события.
     try {
       var dc = serverContext.addDocument(uri);
       serverContext.rebuildDocument(dc);
+      // Регистрируем сущность в каталоге имён ServerContext и индексируем документ в
+      // documentsByMDORef под его mdoRef (= каноничным именем) — чтобы ReferenceIndexFiller
+      // резолвил ссылки на эту lib-сущность через ServerContext.getDocument(имя, moduleType).
+      serverContext.registerOScriptLibrary(qualifiedName, moduleType, dc);
       // Явный вызов: гарантирует регистрацию USER-типа в актуальном
       // workspace-scope (event-listener тоже сработает, но он не
       // обязан выполняться в том же scope/потоке, что и reindex).
@@ -438,6 +454,10 @@ void registerEntry(String rawQualifiedName, Path osFile, EntryKind kind, ServerC
     }
   }
 
+  private static ModuleType moduleTypeOf(EntryKind kind) {
+    return kind == EntryKind.CLASS ? ModuleType.OScriptClass : ModuleType.OScriptModule;
+  }
+
   @Nullable
   private static String libOriginOf(@Nullable Path libRoot) {
     if (libRoot == null) {
diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/KeywordReferenceFinder.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/KeywordReferenceFinder.java
similarity index 97%
rename from src/main/java/com/github/_1c_syntax/bsl/languageserver/references/KeywordReferenceFinder.java
rename to src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/KeywordReferenceFinder.java
index cac6f071d82..671dc607e80 100644
--- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/KeywordReferenceFinder.java
+++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/KeywordReferenceFinder.java
@@ -19,11 +19,13 @@
  * You should have received a copy of the GNU Lesser General Public
  * License along with BSL Language Server.
  */
-package com.github._1c_syntax.bsl.languageserver.references;
+package com.github._1c_syntax.bsl.languageserver.types.references;
 
 import com.github._1c_syntax.bsl.languageserver.configuration.LanguageServerConfiguration;
 import com.github._1c_syntax.bsl.languageserver.context.ServerContextProvider;
 import com.github._1c_syntax.bsl.languageserver.context.symbol.KeywordSymbol;
+import com.github._1c_syntax.bsl.languageserver.references.AnnotationReferenceFinder;
+import com.github._1c_syntax.bsl.languageserver.references.ReferenceFinder;
 import com.github._1c_syntax.bsl.languageserver.references.model.Reference;
 import com.github._1c_syntax.bsl.languageserver.types.registry.GlobalScopeProvider;
 import com.github._1c_syntax.bsl.languageserver.utils.Ranges;
diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/NewExpressionReferenceFinder.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/NewExpressionReferenceFinder.java
similarity index 97%
rename from src/main/java/com/github/_1c_syntax/bsl/languageserver/references/NewExpressionReferenceFinder.java
rename to src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/NewExpressionReferenceFinder.java
index 09c40842fc3..0599c5a6158 100644
--- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/NewExpressionReferenceFinder.java
+++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/NewExpressionReferenceFinder.java
@@ -19,10 +19,11 @@
  * You should have received a copy of the GNU Lesser General Public
  * License along with BSL Language Server.
  */
-package com.github._1c_syntax.bsl.languageserver.references;
+package com.github._1c_syntax.bsl.languageserver.types.references;
 
 import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
 import com.github._1c_syntax.bsl.languageserver.context.ServerContextProvider;
+import com.github._1c_syntax.bsl.languageserver.references.ReferenceFinder;
 import com.github._1c_syntax.bsl.languageserver.references.model.OccurrenceType;
 import com.github._1c_syntax.bsl.languageserver.references.model.Reference;
 import com.github._1c_syntax.bsl.languageserver.types.TypeService;
diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/PlatformMemberReferenceFinder.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/PlatformMemberReferenceFinder.java
similarity index 95%
rename from src/main/java/com/github/_1c_syntax/bsl/languageserver/references/PlatformMemberReferenceFinder.java
rename to src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/PlatformMemberReferenceFinder.java
index 130d6772603..0a24899e2fc 100644
--- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/references/PlatformMemberReferenceFinder.java
+++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/PlatformMemberReferenceFinder.java
@@ -19,9 +19,10 @@
  * You should have received a copy of the GNU Lesser General Public
  * License along with BSL Language Server.
  */
-package com.github._1c_syntax.bsl.languageserver.references;
+package com.github._1c_syntax.bsl.languageserver.types.references;
 
 import com.github._1c_syntax.bsl.languageserver.context.ServerContextProvider;
+import com.github._1c_syntax.bsl.languageserver.references.ReferenceFinder;
 import com.github._1c_syntax.bsl.languageserver.references.model.OccurrenceType;
 import com.github._1c_syntax.bsl.languageserver.references.model.Reference;
 import com.github._1c_syntax.bsl.languageserver.types.TypeService;
diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/package-info.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/package-info.java
new file mode 100644
index 00000000000..6cad5aa5fca
--- /dev/null
+++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/references/package-info.java
@@ -0,0 +1,34 @@
+/*
+ * This file is a part of BSL Language Server.
+ *
+ * Copyright (c) 2018-2026
+ * Alexey Sosnoviy , Nikita Fedkin  and contributors
+ *
+ * SPDX-License-Identifier: LGPL-3.0-or-later
+ *
+ * BSL Language Server is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3.0 of the License, or (at your option) any later version.
+ *
+ * BSL Language Server is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with BSL Language Server.
+ */
+
+/**
+ * Type-aware реализации порта {@link com.github._1c_syntax.bsl.languageserver.references.ReferenceFinder}.
+ * 

+ * Резолвят ссылки, консультируясь с системой типов (платформенные/конфигурационные члены, + * конструкторы, ключевые слова). Сам порт и type-agnostic finder'ы живут в пакете + * {@code references}; эти адаптеры вынесены сюда, к своим зависимостям из {@code types}, + * чтобы пакет {@code references} не зависел от {@code types}. + */ +@NullMarked +package com.github._1c_syntax.bsl.languageserver.types.references; + +import org.jspecify.annotations.NullMarked; diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/architecture/ArchitectureTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/architecture/ArchitectureTest.java index cfce50266ab..9c38c3df02b 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/architecture/ArchitectureTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/architecture/ArchitectureTest.java @@ -201,10 +201,11 @@ class ArchitectureTest { // зависимости): в байткоде в него никто не входит (ссылки на него — только в Javadoc @link), // поэтому он не нарушает полноту. websocket — лист без внутренних зависимостей. // - // Известные циклы оставлены ОСОЗНАННО (правило их допускает, но они помечены как долг): - // - References↔Types: взаимные ссылки индексов. Разрывается позже. - // Появление НОВЫХ циклов среди уже ацикличных пакетов ловит отдельное правило ниже - // (acyclic_domains_stay_free_of_cycles). + // Цикл References↔Types разорван: type-aware finder'ы переехали в types.references, а + // ReferenceIndexFiller резолвит library-сущности OneScript по mdoRef через ServerContext + // (каталог наполняет types), поэтому references больше не зависит от types — осталось только + // types→references. Появление НОВЫХ циклов среди уже ацикличных пакетов ловит отдельное правило + // ниже (acyclic_domains_stay_free_of_cycles). // // Замечание про inline-константы: codeactions и diagnostics ссылаются на DiagnosticProvider.SOURCE // (public static final String) — javac встраивает значение, ребра в байткоде нет, поэтому Providers @@ -316,13 +317,14 @@ class ArchitectureTest { "Cfg", "Configuration", "Context", "DiagnosticsMetadata", "Formatting", "Infrastructure", "Recognizer", "References", "Types", "Utils") - // Домены-фундамент. References↔Types — известный цикл (см. комментарий выше). + // Домены-фундамент. references — нижний слой разрешения имён, от types НЕ зависит; + // types выше и зависит от references (вывод типов опирается на разрешение имён). .whereLayer("Configuration").mayOnlyAccessLayers( "DiagnosticsMetadata", "Events", "Infrastructure", "Utils") .whereLayer("Context").mayOnlyAccessLayers( "Client", "Configuration", "DiagnosticsMetadata", "Infrastructure", "Utils") .whereLayer("References").mayOnlyAccessLayers( - "Configuration", "Context", "Infrastructure", "Types", "Utils") + "Configuration", "Context", "Infrastructure", "Utils") .whereLayer("Types").mayOnlyAccessLayers( "Configuration", "Context", "Events", "Infrastructure", "References", "Utils") .whereLayer("Cfg").mayOnlyAccessLayers("Utils")