1 #pragma once
2 
3 #include "lsp_diagnostic.h"
4 #include "utils.h"
5 
6 #include <clang-c/Index.h>
7 #include <optional.h>
8 
9 #include <mutex>
10 #include <string>
11 
12 struct WorkingFile {
13   int version = 0;
14   AbsolutePath filename;
15 
16   std::string buffer_content;
17   // Note: This assumes 0-based lines (1-based lines are normally assumed).
18   std::vector<std::string> index_lines;
19   // Note: This assumes 0-based lines (1-based lines are normally assumed).
20   std::vector<std::string> buffer_lines;
21   // Mappings between index line number and buffer line number.
22   // Empty indicates either buffer or index has been changed and re-computation
23   // is required.
24   // For index_to_buffer[i] == j, if j >= 0, we are confident that index line
25   // i maps to buffer line j; if j == -1, FindMatchingLine will use the nearest
26   // confident lines to resolve its line number.
27   std::vector<int> index_to_buffer;
28   std::vector<int> buffer_to_index;
29   // A set of diagnostics that have been reported for this file.
30   // NOTE: _ is appended because it must be accessed under the WorkingFiles
31   // lock!
32   std::vector<lsDiagnostic> diagnostics_;
33 
34   WorkingFile(const AbsolutePath& filename, const std::string& buffer_content);
35 
36   // This should be called when the indexed content has changed.
37   void SetIndexContent(const std::string& index_content);
38   // This should be called whenever |buffer_content| has changed.
39   void OnBufferContentUpdated();
40 
41   // Finds the buffer line number which maps to index line number |line|.
42   // Also resolves |column| if not NULL.
43   // When resolving a range, use is_end = false for begin() and is_end =
44   // true for end() to get a better alignment of |column|.
45   optional<int> GetBufferPosFromIndexPos(int line, int* column, bool is_end);
46   // Finds the index line number which maps to buffer line number |line|.
47   // Also resolves |column| if not NULL.
48   optional<int> GetIndexPosFromBufferPos(int line, int* column, bool is_end);
49 
50   // TODO: Move FindClosestCallNameInBuffer and FindStableCompletionSource into
51   // lex_utils.h/cc
52 
53   // Finds the closest 'callable' name prior to position. This is used for
54   // signature help to filter code completion results.
55   //
56   // |completion_position| will be point to a good code completion location to
57   // for fetching signatures.
58   std::string FindClosestCallNameInBuffer(
59       lsPosition position,
60       int* active_parameter,
61       lsPosition* completion_position = nullptr) const;
62 
63   // Returns a relatively stable completion position (it jumps back until there
64   // is a non-alphanumeric character).
65   //
66   // The out param |is_global_completion| is set to true if this looks like a
67   // global completion.
68   // The out param |existing_completion| is set to any existing completion
69   // content the user has entered.
70   // The out param |replace_end_position| is set to the end of the existing
71   // identifier, including characters after the original position.
72   lsPosition FindStableCompletionSource(lsPosition position,
73                                         bool* is_global_completion,
74                                         std::string* existing_completion,
75                                         lsPosition* replace_end_position) const;
76 
77  private:
78   // Compute index_to_buffer and buffer_to_index.
79   void ComputeLineMapping();
80 };
81 
82 struct WorkingFiles {
83   struct Snapshot {
84     struct File {
85       std::string filename;
86       std::string content;
87     };
88 
89     std::vector<CXUnsavedFile> AsUnsavedFiles() const;
90     std::vector<File> files;
91   };
92 
93   //
94   // :: IMPORTANT :: All methods in this class are guarded by a single lock.
95   //
96 
97   // Find the file with the given filename.
98   WorkingFile* GetFileByFilename(const AbsolutePath& filename);
99   WorkingFile* GetFileByFilenameNoLock(const AbsolutePath& filename);
100 
101   // Run |action| under the lock.
102   void DoAction(const std::function<void()>& action);
103   // Run |action| on the file identified by |filename|. This executes under the
104   // lock.
105   void DoActionOnFile(const AbsolutePath& filename,
106                       const std::function<void(WorkingFile* file)>& action);
107 
108   WorkingFile* OnOpen(const lsTextDocumentItem& open);
109   void OnChange(const lsTextDocumentDidChangeParams& change);
110   void OnClose(const lsTextDocumentIdentifier& close);
111 
112   // If |filter_paths| is non-empty, only files which contain any of the given
113   // strings. For example, {"foo", "bar"} means that every result has either the
114   // string "foo" or "bar" contained within it.
115   Snapshot AsSnapshot(const std::vector<std::string>& filter_paths);
116 
117   // Use unique_ptrs so we can handout WorkingFile ptrs and not have them
118   // invalidated if we resize files.
119   std::vector<std::unique_ptr<WorkingFile>> files;
120   std::mutex files_mutex;  // Protects |files|.
121 };
122