已合并
Fix LSP import definition navigation #11174
已合并
wdc创建于 12 天前
4 个文件变更+226-6
Mets2panda/lsp/include/internal_api.h+5-0
@@ -24,6 +24,10 @@
24#include "public/public.h"24#include "public/public.h"
25#include "util/eheap.h"25#include "util/eheap.h"
26 26 
27+namespace ark::es2panda::util {
28+class ImportInfo;
29+} // namespace ark::es2panda::util
30+ 
27namespace ark::es2panda::lsp {31namespace ark::es2panda::lsp {
28 32 
29class Initializer {33class Initializer {
@@ -169,6 +173,7 @@ varbinder::Decl *FindDeclInFunctionScope(varbinder::Scope *scope, const util::St
169varbinder::Decl *FindDeclInGlobalScope(varbinder::Scope *scope, const util::StringView &name);173varbinder::Decl *FindDeclInGlobalScope(varbinder::Scope *scope, const util::StringView &name);
170varbinder::Decl *FindDeclInScopeWithFallback(varbinder::Scope *scope, const util::StringView &name);174varbinder::Decl *FindDeclInScopeWithFallback(varbinder::Scope *scope, const util::StringView &name);
171std::string GetImportFilePath(es2panda_Context *context, size_t pos);175std::string GetImportFilePath(es2panda_Context *context, size_t pos);
176+std::string GetImportFilePath(const util::ImportInfo &importInfo);
172void MakeDiagnosticReferences(es2panda_Context *context, const util::DiagnosticStorage &diagnostics,177void MakeDiagnosticReferences(es2panda_Context *context, const util::DiagnosticStorage &diagnostics,
173 DiagnosticReferences &result);178 DiagnosticReferences &result);
174std::string GetTokenTypes(ir::ModifierFlags flags);179std::string GetTokenTypes(ir::ModifierFlags flags);
Mets2panda/lsp/src/api.cpp+31-0
@@ -26,6 +26,7 @@
26#include "get_safe_delete_info.h"26#include "get_safe_delete_info.h"
27#include "internal_api.h"27#include "internal_api.h"
28#include "ir/astNode.h"28#include "ir/astNode.h"
29+#include "ir/ets/etsReExportDeclaration.h"
29#include "find_safe_delete_location.h"30#include "find_safe_delete_location.h"
30#include "references.h"31#include "references.h"
31#include "public/es2panda_lib.h"32#include "public/es2panda_lib.h"
@@ -56,6 +57,30 @@ using ark::es2panda::lsp::details::GetCompletionEntryDetailsImpl;
56extern "C" {57extern "C" {
57namespace ark::es2panda::lsp {58namespace ark::es2panda::lsp {
58 59 
60+static std::string GetDefinitionFilePathFromImportNamespaceAlias(const ir::AstNode *declNode)
61+{
62+ if (declNode == nullptr || !declNode->IsImportNamespaceSpecifier()) {
63+ return {};
64+ }
65+ 
66+ const auto *parent = declNode->Parent();
67+ if (parent == nullptr) {
68+ return {};
69+ }
70+ 
71+ const ir::ETSImportDeclaration *importDecl = nullptr;
72+ if (parent->IsETSImportDeclaration()) {
73+ importDecl = parent->AsETSImportDeclaration();
74+ } else if (parent->IsETSReExportDeclaration()) {
75+ importDecl = parent->AsETSReExportDeclaration()->GetETSImportDeclarations();
liu-jialiang
liu-jialiangliu-jialiang8 天前

export * as components from "./source"; Can we jump to ./source when click ./source?

likedislike
wdc
wdc
8 天前 评论:
76+ }
77+ if (importDecl == nullptr) {
78+ return {};
79+ }
80+ 
81+ return GetImportFilePath(importDecl->ImportInfo());
82+}
83+ 
59DefinitionInfo GetDefinitionAtPosition(es2panda_Context *context, size_t position)84DefinitionInfo GetDefinitionAtPosition(es2panda_Context *context, size_t position)
60{85{
61 auto ctx = reinterpret_cast<public_lib::Context *>(context);86 auto ctx = reinterpret_cast<public_lib::Context *>(context);
@@ -74,6 +99,12 @@ DefinitionInfo GetDefinitionAtPosition(es2panda_Context *context, size_t positio
74 if (declInfo.first == nullptr) {99 if (declInfo.first == nullptr) {
75 return result;100 return result;
76 }101 }
102+ 
103+ auto importNamespaceAliasFilePath = GetDefinitionFilePathFromImportNamespaceAlias(declInfo.first);
104+ if (!importNamespaceAliasFilePath.empty()) {
105+ return {importNamespaceAliasFilePath, 0, 0};
106+ }
107+ 
77 auto node = declInfo.first;108 auto node = declInfo.first;
78 auto targetNode = declInfo.first->FindChild([&declInfo](ir::AstNode *childNode) {109 auto targetNode = declInfo.first->FindChild([&declInfo](ir::AstNode *childNode) {
79 return childNode->IsIdentifier() && childNode->AsIdentifier()->Name() == declInfo.second;110 return childNode->IsIdentifier() && childNode->AsIdentifier()->Name() == declInfo.second;
Mets2panda/lsp/src/internal_api.cpp+9-6
@@ -1452,16 +1452,19 @@ std::string GetImportFilePath(es2panda_Context *context, size_t pos)
1452 }1452 }
1453 auto parent = node->Parent();1453 auto parent = node->Parent();
1454 if (parent != nullptr && parent->IsETSImportDeclaration() && parent->AsETSImportDeclaration()->Source() == node) {1454 if (parent != nullptr && parent->IsETSImportDeclaration() && parent->AsETSImportDeclaration()->Source() == node) {
1455- const auto &importInfo = parent->AsETSImportDeclaration()->ImportInfo();1455+ res = GetImportFilePath(parent->AsETSImportDeclaration()->ImportInfo());
1456- if (node->IsStringLiteral() && node->AsStringLiteral()->Str().StartsWith("dynamic@")) {
1457- res = std::string(importInfo.TextSource());
1458- } else {
1459- res = std::string(importInfo.ResolvedSource());
1460- }
1461 }1456 }
1462 return res;1457 return res;
1463}1458}
1464 1459 
1460+std::string GetImportFilePath(const util::ImportInfo &importInfo)
1461+{
1462+ if (importInfo.HasSpecifiedDeclPath() && !importInfo.ReferencesABC()) {
1463+ return std::string(importInfo.DeclPath());
1464+ }
1465+ return std::string(importInfo.ResolvedSource());
1466+}
1467+ 
1465std::vector<ir::AstNode *> RemoveRefDuplicates(const std::vector<ir::AstNode *> &nodes)1468std::vector<ir::AstNode *> RemoveRefDuplicates(const std::vector<ir::AstNode *> &nodes)
1466{1469{
1467 auto hashFunc = [](const ir::AstNode *node) {1470 auto hashFunc = [](const ir::AstNode *node) {
Mets2panda/test/unit/lsp/get_definition_at_position_test.cpp+181-0
@@ -14,13 +14,38 @@
14 */14 */
15 15 
16#include <gtest/gtest.h>16#include <gtest/gtest.h>
17+#include <array>
17#include <cstddef>18#include <cstddef>
19+#include <memory>
18#include <string>20#include <string>
21+#include <utility>
19#include "lsp_api_test.h"22#include "lsp_api_test.h"
20#include "lsp/include/internal_api.h"23#include "lsp/include/internal_api.h"
21 24 
22using ark::es2panda::lsp::Initializer;25using ark::es2panda::lsp::Initializer;
23 26 
27+namespace {
28+ 
29+constexpr size_t DYNAMIC_DEPENDENCY_INDEX = 0U;
30+constexpr size_t STATIC_IMPLEMENTATION_INDEX = 1U;
31+constexpr size_t STATIC_DECLARATION_INDEX = 2U;
32+constexpr size_t DEPENDENCY_USE_INDEX = 3U;
33+constexpr size_t DEPENDENCY_CONFIG_INDEX = 4U;
34+constexpr size_t DEPENDENCY_FILE_COUNT = 5U;
35+ 
36+std::string MakeDependencyConfig(const std::filesystem::path &tempDir, const std::vector<std::string> &files)
37+{
38+ const auto dynamicDependencyPath = (tempDir / files[DYNAMIC_DEPENDENCY_INDEX]).string();
39+ const auto staticImplementationPath = (tempDir / files[STATIC_IMPLEMENTATION_INDEX]).string();
40+ const auto staticDeclarationPath = (tempDir / files[STATIC_DECLARATION_INDEX]).string();
41+ return R"({"compilerOptions": {"baseUrl": ")" + tempDir.string() +
42+ R"(", "dependencies": {"js": {"language": "js", "path": ")" + dynamicDependencyPath +
43+ R"(", "ohmUrl": "js"}, "staticLib": {"language": "ets", "path": ")" + staticDeclarationPath +
44+ R"(", "sourceFilePath": ")" + staticImplementationPath + R"("}}}})";
45+}
46+ 
47+} // namespace
48+ 
24class LspGetDefTests : public LSPAPITests {};49class LspGetDefTests : public LSPAPITests {};
25 50 
26TEST_F(LspGetDefTests, GetDefinitionAtPosition1)51TEST_F(LspGetDefTests, GetDefinitionAtPosition1)
@@ -496,6 +521,162 @@ TEST_F(LspGetDefTests, DisableLoweringTest4)
496 ASSERT_EQ(result.length, expectedLength);521 ASSERT_EQ(result.length, expectedLength);
497}522}
498 523 
524+TEST_F(LspGetDefTests, GetDefinitionAtPosition_ImportNamespaceAliasDeclaration)
525+{
526+ std::vector<std::string> files = {"getDefinitionAtPositionNamespaceAliasDeclarationSource.ets",
527+ "getDefinitionAtPositionNamespaceAliasDeclarationUse.ets"};
528+ std::vector<std::string> texts = {
529+ R"(export class A {})", R"(import * as aaaa from './getDefinitionAtPositionNamespaceAliasDeclarationSource';)"};
530+ auto filePaths = CreateTempFile(files, texts);
531+ size_t const expectedFileCount = 2;
532+ ASSERT_EQ(filePaths.size(), expectedFileCount);
533+ 
534+ const auto offset = texts[1].find("aaaa");
535+ ASSERT_NE(offset, std::string::npos);
536+ 
537+ LSPAPI const *lspApi = GetImpl();
538+ Initializer initializer = Initializer();
539+ auto ctx = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
540+ auto result = lspApi->getDefinitionAtPosition(ctx, offset);
541+ initializer.DestroyContext(ctx);
542+ 
543+ ASSERT_EQ(result.fileName, filePaths[0]);
544+ ASSERT_EQ(result.start, 0U);
545+ ASSERT_EQ(result.length, 0U);
546+}
547+ 
548+TEST_F(LspGetDefTests, GetDefinitionAtPosition_ImportNamespaceModulePath)
549+{
550+ std::vector<std::string> files = {"getDefinitionAtPositionNamespacePathSource.ets",
551+ "getDefinitionAtPositionNamespacePathUse.ets"};
552+ const std::string importPath = "./getDefinitionAtPositionNamespacePathSource";
553+ std::vector<std::string> texts = {R"(export class A {})",
554+ R"(import * as aaaa from './getDefinitionAtPositionNamespacePathSource';)"};
555+ auto filePaths = CreateTempFile(files, texts);
556+ size_t const expectedFileCount = 2;
557+ ASSERT_EQ(filePaths.size(), expectedFileCount);
558+ 
559+ const auto offset = texts[1].find(importPath);
560+ ASSERT_NE(offset, std::string::npos);
561+ 
562+ LSPAPI const *lspApi = GetImpl();
563+ Initializer initializer = Initializer();
564+ auto ctx = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
565+ auto result = lspApi->getDefinitionAtPosition(ctx, offset);
566+ initializer.DestroyContext(ctx);
567+ 
568+ ASSERT_EQ(result.fileName, filePaths[0]);
569+ ASSERT_EQ(result.start, 0U);
570+ ASSERT_EQ(result.length, 0U);
571+}
572+ 
573+TEST_F(LspGetDefTests, GetDefinitionAtPosition_ArktsconfigDependency)
574+{
575+ std::vector<std::string> files = {
576+ "getDefinitionAtPositionDynamicDependency.d.ets", "getDefinitionAtPositionStaticDependency.ets",
577+ "getDefinitionAtPositionStaticDependency.d.ets", "getDefinitionAtPositionDependencyUse.ets",
578+ "getDefinitionAtPositionDependencyArktsconfig.json"};
579+ const auto tempDir = std::filesystem::path(testing::TempDir()).append(GetExecutableName());
580+ const auto configText = MakeDependencyConfig(tempDir, files);
581+ std::vector<std::string> texts = {R"(export declare class DynamicA {})", R"(export class StaticA {})",
582+ R"(export declare class StaticA {})",
583+ R"('use static'
584+ 
585+import * as jsModule from 'js';
586+import * as staticModule from 'staticLib';)",
587+ configText};
588+ auto filePaths = CreateTempFile(files, texts);
589+ ASSERT_EQ(filePaths.size(), DEPENDENCY_FILE_COUNT);
590+ 
591+ const auto configOption = "--arktsconfig=" + filePaths[DEPENDENCY_CONFIG_INDEX];
592+ std::array<const char *, 2U> args = {filePaths[DEPENDENCY_USE_INDEX].c_str(), configOption.c_str()};
593+ auto *compilerImpl = es2panda_GetImpl(ES2PANDA_LIB_VERSION);
594+ ASSERT_NE(compilerImpl, nullptr);
595+ 
596+ auto configDeleter = [compilerImpl](es2panda_Config *config) { compilerImpl->DestroyConfigWithoutLog(config); };
597+ std::unique_ptr<es2panda_Config, decltype(configDeleter)> config(
598+ compilerImpl->CreateConfig(args.size(), args.data()), configDeleter);
599+ ASSERT_NE(config, nullptr);
600+ 
601+ auto contextDeleter = [compilerImpl](es2panda_Context *context) { compilerImpl->DestroyContext(context); };
602+ std::unique_ptr<es2panda_Context, decltype(contextDeleter)> context(
603+ compilerImpl->CreateContextFromFile(config.get(), filePaths[DEPENDENCY_USE_INDEX].c_str()), contextDeleter);
604+ ASSERT_NE(context, nullptr);
605+ compilerImpl->ProceedToState(context.get(), ES2PANDA_STATE_BOUND);
606+ 
607+ const auto dynamicAliasOffset = texts[DEPENDENCY_USE_INDEX].find("jsModule");
608+ const auto dynamicSourceOffset = texts[DEPENDENCY_USE_INDEX].rfind("js");
609+ const auto staticAliasOffset = texts[DEPENDENCY_USE_INDEX].find("staticModule");
610+ const auto staticSourceOffset = texts[DEPENDENCY_USE_INDEX].rfind("staticLib");
611+ ASSERT_NE(dynamicAliasOffset, std::string::npos);
612+ ASSERT_NE(dynamicSourceOffset, std::string::npos);
613+ ASSERT_NE(staticAliasOffset, std::string::npos);
614+ ASSERT_NE(staticSourceOffset, std::string::npos);
615+ 
616+ LSPAPI const *lspApi = GetImpl();
617+ const std::array<std::pair<size_t, std::string>, 4U> expectedDefinitions = {
618+ {{dynamicAliasOffset, filePaths[DYNAMIC_DEPENDENCY_INDEX]},
619+ {dynamicSourceOffset, filePaths[DYNAMIC_DEPENDENCY_INDEX]},
620+ {staticAliasOffset, filePaths[STATIC_DECLARATION_INDEX]},
621+ {staticSourceOffset, filePaths[STATIC_DECLARATION_INDEX]}}};
622+ for (const auto &[offset, expectedFile] : expectedDefinitions) {
623+ const auto result = lspApi->getDefinitionAtPosition(context.get(), offset);
624+ EXPECT_EQ(result.fileName, expectedFile);
625+ EXPECT_EQ(result.start, 0U);
626+ EXPECT_EQ(result.length, 0U);
627+ }
628+}
629+ 
630+TEST_F(LspGetDefTests, GetDefinitionAtPosition_ImportNamespaceAliasUsage)
631+{
632+ std::vector<std::string> files = {"getDefinitionAtPositionNamespaceAliasUsageSource.ets",
633+ "getDefinitionAtPositionNamespaceAliasUsageUse.ets"};
634+ std::vector<std::string> texts = {R"(export class A {})",
635+ R"(import * as aaaa from './getDefinitionAtPositionNamespaceAliasUsageSource';
636+let instance = new aaaa.A();)"};
637+ auto filePaths = CreateTempFile(files, texts);
638+ size_t const expectedFileCount = 2;
639+ ASSERT_EQ(filePaths.size(), expectedFileCount);
640+ 
641+ const auto offset = texts[1].rfind("aaaa");
642+ ASSERT_NE(offset, std::string::npos);
643+ 
644+ LSPAPI const *lspApi = GetImpl();
645+ Initializer initializer = Initializer();
646+ auto ctx = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
647+ auto result = lspApi->getDefinitionAtPosition(ctx, offset);
648+ initializer.DestroyContext(ctx);
649+ 
650+ ASSERT_EQ(result.fileName, filePaths[0]);
651+ ASSERT_EQ(result.start, 0U);
652+ ASSERT_EQ(result.length, 0U);
653+}
654+ 
655+TEST_F(LspGetDefTests, GetDefinitionAtPosition_ReExportNamespaceAliasInDeclaration)
656+{
657+ std::vector<std::string> files = {"getDefinitionAtPositionReExportNamespaceAliasSource.ets",
658+ "getDefinitionAtPositionReExportNamespaceAliasUse.ets"};
659+ std::vector<std::string> texts = {
660+ R"(export class A {})",
661+ R"(export * as components from './getDefinitionAtPositionReExportNamespaceAliasSource';)"};
662+ auto filePaths = CreateTempFile(files, texts);
663+ size_t const expectedFileCount = 2;
664+ ASSERT_EQ(filePaths.size(), expectedFileCount);
665+ 
666+ const auto offset = texts[1].find("components");
667+ ASSERT_NE(offset, std::string::npos);
668+ 
669+ LSPAPI const *lspApi = GetImpl();
670+ Initializer initializer = Initializer();
671+ auto ctx = initializer.CreateContext(filePaths[1].c_str(), ES2PANDA_STATE_CHECKED);
672+ auto result = lspApi->getDefinitionAtPosition(ctx, offset);
673+ initializer.DestroyContext(ctx);
674+ 
675+ ASSERT_EQ(result.fileName, filePaths[0]);
676+ ASSERT_EQ(result.start, 0U);
677+ ASSERT_EQ(result.length, 0U);
678+}
679+ 
499TEST_F(LspGetDefTests, negativeTest1)680TEST_F(LspGetDefTests, negativeTest1)
500{681{
501 std::vector<std::string> files = {"SetJumpTarget.ets"};682 std::vector<std::string> files = {"SetJumpTarget.ets"};