Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -117,6 +119,13 @@
*/
private final Map<String, Map<ModuleType, DocumentContext>> documentsByMDORef
= new ConcurrentHashMap<>();
/**
* Каталог библиотечных сущностей OneScript: {@code nameKey → (каноничное имя, тип модуля)}.
* Наполняется извне (индексатором OneScript-библиотек) и служит резолву имени класса/модуля
* библиотеки в каноничное имя — аналогично тому, как {@link #findCommonModule(String)} резолвит
* общий модуль из метаданных конфигурации. Регистронезависимый (ключ — {@code nameKey}).
*/
private final Map<String, OScriptLibrarySymbol> oscriptLibrariesByNameKey = new ConcurrentHashMap<>();
private final Map<URI, ReadWriteLock> documentLocks = new ConcurrentHashMap<>();

private final Map<DocumentContext, State> states = new ConcurrentHashMap<>();
Expand Down Expand Up @@ -331,6 +340,7 @@
states.clear();
documentsByMDORef.clear();
mdoRefs.clear();
oscriptLibrariesByNameKey.clear();
documentLocks.clear();
commonModuleCache.invalidateAll();
configurationMetadata.clear();
Expand Down Expand Up @@ -470,6 +480,88 @@
return commonModuleCache.get(name, key -> getConfiguration().findCommonModule(key));
}

/**
* Зарегистрировать библиотечную сущность OneScript: занести её в каталог имён (для канонизации
* в {@link #findLibraryClass}/{@link #findLibraryModule}) и проиндексировать документ в
* {@code documentsByMDORef} под каноничным именем (чтобы он резолвился через
* {@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(qualifiedName, moduleType));
documentsByMDORef
.computeIfAbsent(qualifiedName, k -> Collections.synchronizedMap(new EnumMap<>(ModuleType.class)))
.put(moduleType, documentContext);
}

/**
* Снять регистрацию библиотечной сущности OneScript: из каталога имён и из
* {@code documentsByMDORef}. Вызывается индексатором при удалении файла/пере-индексации.
*
* @param qualifiedName каноничное имя сущности
* @param moduleType тип модуля сущности
*/
public void removeOScriptLibrary(String qualifiedName, ModuleType moduleType) {
oscriptLibrariesByNameKey.remove(oscriptNameKey(qualifiedName));
var group = documentsByMDORef.get(qualifiedName);
if (group != null) {
group.remove(moduleType);
if (group.isEmpty()) {
documentsByMDORef.remove(qualifiedName);
}
}
}

/**
* Найти каноничное имя зарегистрированного library-класса OneScript по имени из исходного кода.
*
* @param name имя из кода (произвольный регистр)
* @return каноничное имя класса либо {@code empty}
*/
public Optional<String> findLibraryClass(String name) {
return findOScriptLibrarySymbol(name, ModuleType.OScriptClass);
}

/**
* Найти каноничное имя зарегистрированного library-модуля OneScript по имени из исходного кода.
*
* @param name имя из кода (произвольный регистр)
* @return каноничное имя модуля либо {@code empty}
*/
public Optional<String> findLibraryModule(String name) {
return findOScriptLibrarySymbol(name, ModuleType.OScriptModule);
}

private Optional<String> findOScriptLibrarySymbol(String name, ModuleType moduleType) {
if (name == null || name.isBlank()) {

Check warning on line 539 in src/main/java/com/github/_1c_syntax/bsl/languageserver/context/ServerContext.java

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Remove this expression which always evaluates to "false"

See more on https://sonarcloud.io/project/issues?id=1c-syntax_bsl-language-server&issues=AZ8UrCPurJXjAhCQ85EP&open=AZ8UrCPurJXjAhCQ85EP&pullRequest=4233
return Optional.empty();
}
return Optional.ofNullable(oscriptLibrariesByNameKey.get(oscriptNameKey(name)))
.filter(symbol -> symbol.moduleType() == moduleType)
.map(OScriptLibrarySymbol::qualifiedName);
}

/**
* Нормализация имени 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 qualifiedName каноничное имя
* @param moduleType тип модуля (класс/модуль)
*/
public record OScriptLibrarySymbol(String qualifiedName, ModuleType moduleType) {
}

private DocumentContext createDocumentContext(URI uri) {
var documentContext = documentContextProvider.getObject(uri, this);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
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;
Expand Down Expand Up @@ -85,7 +84,6 @@ public class ReferenceIndexFiller {

private final ReferenceIndex index;
private final LanguageServerConfiguration configuration;
private final OScriptLibraryIndex oScriptLibraryIndex;

@EventListener
public void handleEvent(DocumentContextContentChangedEvent event) {
Expand Down Expand Up @@ -267,35 +265,34 @@ 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 mdoRef = libClass.get();
var range = Ranges.create(typeName.IDENTIFIER());

var ctor = libraryClassConstructor(libUri.get());
var ctor = libraryClassConstructor(mdoRef);
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<ConstructorSymbol> libraryClassConstructor(URI libUri) {
return Optional.ofNullable(documentContext.getServerContext().getDocument(libUri))
private Optional<ConstructorSymbol> libraryClassConstructor(String mdoRef) {
return documentContext.getServerContext().getDocument(mdoRef, ModuleType.OScriptClass)
.map(DocumentContext::getSymbolTree)
.flatMap(SymbolTree::getConstructor);
}
Expand All @@ -312,41 +309,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();

// Ссылка на сам 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;
}

/**
* Добавляет ссылку на модуль по позиции идентификатора, только если идентификатор является
* именем общего модуля. Для вызовов вида Справочники.Имя.Метод() ссылка не добавляется,
Expand Down Expand Up @@ -585,8 +568,8 @@ 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())
.orElse(null);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ public void handleWorkspaceAdded(WorkspaceAddedEvent event) {
*/
public List<Path> reindex(ServerContext serverContext) {
oScriptModuleTypeResolver.clear();
// снимаем прежние регистрации lib-сущностей в ServerContext (каталог имён + documentsByMDORef)
entriesByName.values().forEach(entry ->
serverContext.removeOScriptLibrary(entry.qualifiedName(), moduleTypeOf(entry.kind())));
entriesByUri.clear();
entriesByName.clear();

Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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 фолбэк).
Expand All @@ -424,6 +431,10 @@ void registerEntry(String rawQualifiedName, Path osFile, EntryKind kind, ServerC
try {
var dc = serverContext.addDocument(uri);
serverContext.rebuildDocument(dc);
// Регистрируем сущность в каталоге имён ServerContext и индексируем документ в
// documentsByMDORef под каноничным именем — чтобы ReferenceIndexFiller резолвил
// ссылки на эту lib-сущность через ServerContext.getDocument(имя, moduleType).
serverContext.registerOScriptLibrary(qualifiedName, moduleType, dc);
// Явный вызов: гарантирует регистрацию USER-типа в актуальном
// workspace-scope (event-listener тоже сработает, но он не
// обязан выполняться в том же scope/потоке, что и reindex).
Expand All @@ -438,6 +449,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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* This file is a part of BSL Language Server.
*
* Copyright (c) 2018-2026
* Alexey Sosnoviy <labotamy@gmail.com>, Nikita Fedkin <nixel2007@gmail.com> 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}.
* <p>
* Резолвят ссылки, консультируясь с системой типов (платформенные/конфигурационные члены,
* конструкторы, ключевые слова). Сам порт и 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;
Loading
Loading