| 209 | } |
| 210 | |
| 211 | void MessageHandler::textDocument_switchSourceHeader(TextDocumentIdentifier ¶m, ReplyOnce &reply) { |
| 212 | QueryFile *file; |
| 213 | WorkingFile *wf; |
| 214 | std::tie(file, wf) = findOrFail(param.uri.getPath(), reply); |
| 215 | if (!wf) |
| 216 | return reply(JsonNull{}); |
| 217 | int file_id = file->id; |
| 218 | |
| 219 | DocumentUri result; |
| 220 | const std::string &path = wf->filename; |
| 221 | bool is_hdr = lookupExtension(path).second; |
| 222 | |
| 223 | // Vote for each interesting symbol's definitions (for header) or declarations (for non-header). |
| 224 | // Select the file with the most votes. |
| 225 | // Ignore Type symbols to skip class forward declarations and namespaces. |
| 226 | std::unordered_map<int, int> file_id2cnt; |
| 227 | for (auto [sym, refcnt] : file->symbol2refcnt) { |
| 228 | if (refcnt <= 0 || !sym.extent.valid() || sym.kind == Kind::Type) |
| 229 | continue; |
| 230 | |
| 231 | if (is_hdr) { |
| 232 | withEntity(db, sym, [&](const auto &entity) { |
| 233 | for (auto &def : entity.def) |
| 234 | if (def.spell && def.file_id != file_id) |
| 235 | ++file_id2cnt[def.file_id]; |
| 236 | }); |
| 237 | } else { |
| 238 | for (DeclRef dr : getNonDefDeclarations(db, sym)) |
| 239 | if (dr.file_id != file_id) |
| 240 | ++file_id2cnt[dr.file_id]; |
| 241 | } |
| 242 | } |
| 243 | if (file_id2cnt.size()) { |
| 244 | auto best = file_id2cnt.begin(); |
| 245 | for (auto it = file_id2cnt.begin(); it != file_id2cnt.end(); ++it) |
| 246 | if (it->second > best->second || (it->second == best->second && it->first < best->first)) |
| 247 | best = it; |
| 248 | if (auto &def = db->files[best->first].def) |
| 249 | return reply(DocumentUri::fromPath(def->path)); |
| 250 | } |
| 251 | |
| 252 | if (is_hdr) { |
| 253 | // Check if `path` is in a #include entry. |
| 254 | for (QueryFile &file1 : db->files) { |
| 255 | auto &def = file1.def; |
| 256 | if (!def || lookupExtension(def->path).second) |
| 257 | continue; |
| 258 | for (IndexInclude &include : def->includes) |
| 259 | if (path == include.resolved_path) |
| 260 | return reply(DocumentUri::fromPath(def->path)); |
| 261 | } |
| 262 | return reply(JsonNull{}); |
| 263 | } |
| 264 | |
| 265 | // Otherwise, find the #include with the same stem. |
| 266 | StringRef stem = sys::path::stem(path); |
| 267 | for (IndexInclude &include : file->def->includes) |
| 268 | if (sys::path::stem(include.resolved_path) == stem) |
nothing calls this directly
no test coverage detected