• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "navigate_to.h"
17 #include "quick_info.h"
18 #include "internal_api.h"
19 #include "lsp/include/get_adjusted_location.h"
20 #include "public/public.h"
21 
22 namespace ark::es2panda::lsp {
23 
PatternMatcher(const std::string & pattern,bool isCaseSensitive)24 PatternMatcher::PatternMatcher(const std::string &pattern, bool isCaseSensitive)
25     : pattern_(pattern), isCaseSensitive_(isCaseSensitive)
26 {
27     try {
28         regexPattern_ = isCaseSensitive ? std::regex(pattern) : std::regex(pattern, std::regex_constants::icase);
29     } catch (const std::regex_error &) {
30         regexPattern_ = std::nullopt;
31     }
32 }
33 
IsPatternValid() const34 bool PatternMatcher::IsPatternValid() const
35 {
36     return regexPattern_.has_value();
37 }
38 
MatchesExact(const std::string & candidate) const39 bool PatternMatcher::MatchesExact(const std::string &candidate) const
40 {
41     return regexPattern_ && std::regex_match(candidate, *regexPattern_);
42 }
43 
MatchesPrefix(const std::string & candidate) const44 bool PatternMatcher::MatchesPrefix(const std::string &candidate) const
45 {
46     if (candidate.size() < pattern_.size()) {
47         return false;
48     }
49 
50     for (size_t i = 0; i < pattern_.size(); ++i) {
51         char a = pattern_[i];
52         char b = candidate[i];
53         if (isCaseSensitive_) {
54             if (a != b) {
55                 return false;
56             }
57         } else {
58             if (std::tolower(a) != std::tolower(b)) {
59                 return false;
60             }
61         }
62     }
63 
64     return true;
65 }
66 
MatchesSubstring(const std::string & candidate) const67 bool PatternMatcher::MatchesSubstring(const std::string &candidate) const
68 {
69     return regexPattern_ && std::regex_search(candidate, *regexPattern_);
70 }
71 
DetermineMatchKind(const std::string & candidate,const PatternMatcher & matcher)72 MatchKind DetermineMatchKind(const std::string &candidate, const PatternMatcher &matcher)
73 {
74     if (matcher.MatchesExact(candidate)) {
75         return MatchKind::EXACT;
76     }
77     if (matcher.MatchesPrefix(candidate)) {
78         return MatchKind::PREFIX;
79     }
80     if (matcher.MatchesSubstring(candidate)) {
81         return MatchKind::SUBSTRING;
82     }
83     return MatchKind::NONE;
84 }
85 
86 // improve get declarations correct and better
GetItemsFromNamedDeclaration(es2panda_Context * context,const SourceFile & file,const PatternMatcher & matcher)87 std::vector<NavigateToItem> GetItemsFromNamedDeclaration(es2panda_Context *context, const SourceFile &file,
88                                                          const PatternMatcher &matcher)
89 {
90     std::vector<NavigateToItem> items;
91     auto filePath = std::string {file.filePath};
92     auto fileContent = std::string {file.source};
93     auto ctx = reinterpret_cast<public_lib::Context *>(context);
94     auto ast = reinterpret_cast<ir::AstNode *>(ctx->parserProgram->Ast());
95 
96     auto children = GetChildren(ast, ctx->allocator);
97 
98     for (const auto child : children) {
99         if (child->IsIdentifier()) {
100             auto name = child->AsIdentifier()->Name();
101             auto matchKind = DetermineMatchKind(std::string(name), matcher);
102             if (matchKind != MatchKind::NONE) {
103                 auto nodeType = child->Type();
104                 auto containerId = GetContainerNode(child);
105                 auto containerName = GetIdentifierName(containerId);
106                 auto containerType = containerId->Type();
107                 items.push_back({std::string(name), ToString(nodeType), matchKind, true, filePath, containerName,
108                                  ToString(containerType)});
109             }
110         }
111     }
112 
113     return items;
114 }
115 
116 // Helper: tries to emit a single item, returns false if limit reached
TryEmitItem(const NavigateToItem & item,size_t & remaining,std::set<std::pair<std::string,std::string>> & seenPairs,std::vector<NavigateToItem> & results)117 static bool TryEmitItem(const NavigateToItem &item, size_t &remaining,
118                         std::set<std::pair<std::string, std::string>> &seenPairs, std::vector<NavigateToItem> &results)
119 {
120     if (remaining == 0) {
121         return false;
122     }
123     auto key = std::make_pair(item.name, item.containerName);
124     if (!seenPairs.insert(key).second) {
125         return true;  // duplicate, but still under limit
126     }
127     results.emplace_back(item);
128     --remaining;
129     return remaining > 0;
130 }
131 
GetNavigateToItems(es2panda_Context * context,const std::vector<SourceFile> & srcFiles,size_t maxResultCount,const std::string & searchValue,bool isCaseSensitive)132 std::vector<NavigateToItem> GetNavigateToItems(es2panda_Context *context, const std::vector<SourceFile> &srcFiles,
133                                                size_t maxResultCount, const std::string &searchValue,
134                                                bool isCaseSensitive)
135 {
136     static std::unordered_map<std::string, size_t> totalEmitted;
137     size_t &emittedSoFar = totalEmitted[searchValue];
138 
139     std::vector<NavigateToItem> results;
140     std::set<std::pair<std::string, std::string>> seenPairs;
141     PatternMatcher matcher(searchValue, isCaseSensitive);
142 
143     if (!matcher.IsPatternValid() || emittedSoFar >= maxResultCount) {
144         return results;
145     }
146 
147     size_t remaining = maxResultCount - emittedSoFar;
148 
149     for (const auto &file : srcFiles) {
150         auto items = GetItemsFromNamedDeclaration(context, file, matcher);
151         for (const auto &item : items) {
152             if (!TryEmitItem(item, remaining, seenPairs, results)) {
153                 emittedSoFar = maxResultCount;
154                 return results;
155             }
156         }
157     }
158 
159     emittedSoFar = maxResultCount - remaining;
160     return results;
161 }
162 
163 }  // namespace ark::es2panda::lsp