blob: f85e8ed76ae9b934b90de23ad9bb3b0be9e1d2cb [file] [log] [blame]
Sam McCall3f0243f2018-07-03 08:09:29 +00001//===--- FileDistance.h - File proximity scoring -----------------*- C++-*-===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This library measures the distance between file paths.
11// It's used for ranking symbols, e.g. in code completion.
12// |foo/bar.h -> foo/bar.h| = 0.
13// |foo/bar.h -> foo/baz.h| < |foo/bar.h -> baz.h|.
14// This is an edit-distance, where edits go up or down the directory tree.
15// It's not symmetrical, the costs of going up and down may not match.
16//
17// Dealing with multiple sources:
18// In practice we care about the distance from a source file, but files near
19// its main-header and #included files are considered "close".
20// So we start with a set of (anchor, cost) pairs, and call the distance to a
21// path the minimum of `cost + |source -> path|`.
22//
23// We allow each source to limit the number of up-traversals paths may start
24// with. Up-traversals may reach things that are not "semantically near".
25//
26// Symbol URI schemes:
27// Symbol locations may be represented by URIs rather than file paths directly.
28// In this case we want to perform distance computations in URI space rather
29// than in file-space, without performing redundant conversions.
30// Therefore we have a lookup structure that accepts URIs, so that intermediate
31// calculations for the same scheme can be reused.
32//
33// Caveats:
34// Assuming up and down traversals each have uniform costs is simplistic.
35// Often there are "semantic roots" whose children are almost unrelated.
36// (e.g. /usr/include/, or / in an umbrella repository). We ignore this.
37//
38//===----------------------------------------------------------------------===//
39
Kirill Bobyrev19a94612018-09-06 12:54:43 +000040#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_FILEDISTANCE_H
41#define LLVM_CLANG_TOOLS_EXTRA_CLANGD_FILEDISTANCE_H
42
Sam McCall3f0243f2018-07-03 08:09:29 +000043#include "URI.h"
44#include "llvm/ADT/DenseMap.h"
45#include "llvm/ADT/DenseMapInfo.h"
46#include "llvm/ADT/SmallString.h"
47#include "llvm/ADT/StringRef.h"
48#include "llvm/Support/Allocator.h"
49#include "llvm/Support/Path.h"
50#include "llvm/Support/StringSaver.h"
51
52namespace clang {
53namespace clangd {
54
55struct FileDistanceOptions {
56 unsigned UpCost = 2; // |foo/bar.h -> foo|
57 unsigned DownCost = 1; // |foo -> foo/bar.h|
58 unsigned IncludeCost = 2; // |foo.cc -> included_header.h|
59};
60
61struct SourceParams {
62 // Base cost for paths starting at this source.
63 unsigned Cost = 0;
64 // Limits the number of upwards traversals allowed from this source.
65 unsigned MaxUpTraversals = std::numeric_limits<unsigned>::max();
66};
67
68// Supports lookups to find the minimum distance to a file from any source.
69// This object should be reused, it memoizes intermediate computations.
70class FileDistance {
71public:
Kirill Bobyrevc1bb7b92018-08-31 08:19:50 +000072 static constexpr unsigned Unreachable = std::numeric_limits<unsigned>::max();
Sam McCall3f0243f2018-07-03 08:09:29 +000073
74 FileDistance(llvm::StringMap<SourceParams> Sources,
75 const FileDistanceOptions &Opts = {});
76
77 // Computes the minimum distance from any source to the file path.
78 unsigned distance(llvm::StringRef Path);
79
80private:
81 // Costs computed so far. Always contains sources and their ancestors.
82 // We store hash codes only. Collisions are rare and consequences aren't dire.
83 llvm::DenseMap<llvm::hash_code, unsigned> Cache;
84 FileDistanceOptions Opts;
85};
86
87// Supports lookups like FileDistance, but the lookup keys are URIs.
88// We convert each of the sources to the scheme of the URI and do a FileDistance
89// comparison on the bodies.
90class URIDistance {
91public:
92 URIDistance(llvm::StringMap<SourceParams> Sources,
93 const FileDistanceOptions &Opts = {})
94 : Sources(Sources), Opts(Opts) {}
95
96 // Computes the minimum distance from any source to the URI.
97 // Only sources that can be mapped into the URI's scheme are considered.
98 unsigned distance(llvm::StringRef URI);
99
100private:
101 // Returns the FileDistance for a URI scheme, creating it if needed.
102 FileDistance &forScheme(llvm::StringRef Scheme);
103
104 // We cache the results using the original strings so we can skip URI parsing.
105 llvm::DenseMap<llvm::hash_code, unsigned> Cache;
106 llvm::StringMap<SourceParams> Sources;
107 llvm::StringMap<std::unique_ptr<FileDistance>> ByScheme;
108 FileDistanceOptions Opts;
109};
110
111} // namespace clangd
112} // namespace clang
Kirill Bobyrev19a94612018-09-06 12:54:43 +0000113
114#endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_FILEDISTANCE_H