1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
|
#pragma once
#include "Core/Str.h"
#include "Core/Io/Url.h"
#include "Core/Io/Text.h"
#include "Core/EnginePtr.h"
#include "Compiler/Thread.h"
#include "Compiler/Reader.h"
#include "Compiler/FileReader.h"
#include "Compiler/Syntax/InfoNode.h"
#include "Range.h"
#include "WorkQueue.h"
namespace storm {
namespace server {
STORM_PKG(core.lang.server);
class File;
/**
* Store information about a single info-node while doing re-parsing.
*/
class Node {
STORM_VALUE;
public:
STORM_CTOR Node(syntax::InfoInternal *node, Range range);
syntax::InfoInternal *node;
Range range;
void STORM_FN toS(StrBuf *to) const;
};
/**
* Represents a part of an open file in the language server. Corresponds to one of the parts
* in the chain of 'FileReader's.
*/
class Part : public ObjectOn<Compiler> {
STORM_CLASS;
public:
STORM_CTOR Part(File *owner, Nat offset, FileReader *part, MAYBE(FileReader *) next);
// Replaces the content in this part with the content dictated by 'reader'. Attempts to
// re-parse the entire content, but keeps the old syntax tree in case parsing fails.
// Returns 'false' if no replacement was needed.
Bool STORM_FN replace(FileReader *part, MAYBE(FileReader *) next, Bool force);
// Get the offset of this part.
inline Nat STORM_FN offset() const { return start; }
inline void STORM_FN offset(Nat offset) { start = offset; }
// Adjust the offset of this part, modifying the content to match.
void STORM_FN adjustOffset(Nat offset, Str *src);
// Get the full range of this part.
Range STORM_FN full() const;
// Perform an edit to this part. Returns the amount of syntax information invalidated.
Range STORM_FN replace(Range range, Str *replace);
// Get syntax colorings in the specified range. These are ordered and
// non-overlapping. The resulting colorings all overlap the specified range, but are not
// clipped to be strictly inside the range.
Array<ColoredRange> *colors(Range r);
// Get the indentation of the character at offset 'n'.
syntax::TextIndent indent(Nat pos);
// Output our internal representation for debugging.
void STORM_FN debugOutput(TextOutput *to, Bool tree) const;
// Compute the total size of the syntax tree.
Nat STORM_FN dbg_size() const;
// Output all our text.
void STORM_FN text(StrBuf *to) const;
/**
* Knobs to tweak for re-parsing performance and correctness.
*/
enum {
// How many extra characters are we aiming at re-parsing?
reparseExtra = 200,
// How many characters extra at each side of an edit are we aiming for?
reparseEdge = 20,
// What is the maximum number of characters to re-parse at once?
reparseMax = 10000,
// Invalidate this part's boundaries if corrections were made this far from the edges.
invalidateLength = 10,
};
private:
// Parser used for this part. Contains the proper packages etc.
syntax::InfoParser *parser;
// Parsed content of this part.
syntax::InfoNode *content;
// Path of this file. Only used for error reporting.
Url *path;
// Offset of the first character in this part.
Nat start;
// Owning file.
File *owner;
// Traverse the syntax tree and extract colors in 'range'.
void colors(Array<ColoredRange> *out, const Range &range, Nat offset, syntax::InfoNode *node);
// Re-parse a node in the syntax tree. Returns 'null' on complete failure.
syntax::InfoNode *parse(syntax::InfoNode *node, syntax::Rule *root);
syntax::InfoNode *parse(syntax::InfoNode *node, syntax::Rule *root, MAYBE(syntax::InfoErrors *) result);
// Re-parse a range in the syntax tree. Returns the range that was re-parsed and needs
// to be transmitted to the client again.
Range parse(const Range &range);
// Traverse the parse tree and find a path to the leafmost node we might want to
// re-parse. Returns an empty range on complete failure for some reason.
MAYBE(Array<Node> *) findParsePath(const Range &update, Nat offset, syntax::InfoNode *node);
// Traverse the path and re-parse a node that looks promising. Returns the invalidated range.
Range parsePath(Array<Node> *path, Range changed);
// Compare InfoErrors:s and see which is better/worse.
static bool bad(syntax::InfoErrors which, syntax::InfoNode *node);
static syntax::InfoErrors combine(syntax::InfoErrors a, syntax::InfoErrors b);
// Remove 'range' in the current node (if applicable). The structure of the syntax tree
// is kept intact. Returns 'false' if content was modified so that a regex in a leaf node
// no longer matches the contents of that node (or if a modified leaf node is missing a regex).
Bool remove(const Range &range);
// Helpers to 'remove'
Bool remove(const Range &range, Nat offset, syntax::InfoNode *node, Bool seenError);
Bool removeLeaf(const Range &range, Nat offset, syntax::InfoLeaf *src, Bool seenError);
Bool removeInternal(const Range &range, Nat offset, syntax::InfoInternal *node, Bool seenError);
// State struct used in 'insert'.
struct InsertState;
// Insert 'v' at 'point'. Does not alter the structure of the tree. Returns 'false' if a
// node was modified so that its regex no longer matches its content.
Bool insert(Nat point, Str *v);
// Helpers to 'insert'.
void insert(InsertState &state, Nat offset, syntax::InfoNode *node, Bool seenError);
void insertLeaf(InsertState &state, Nat offset, syntax::InfoLeaf *node, Bool seenError);
void insertInternal(InsertState &state, Nat offset, syntax::InfoInternal *node, Bool seenError);
// Replace node 'oldNode' with 'newNode' somewhere in the tree.
void replace(syntax::InfoNode *oldNode, syntax::InfoNode *newNode);
};
/**
* Represents an open file in the language server.
*/
class File : public ObjectOn<Compiler> {
STORM_CLASS;
public:
STORM_CTOR File(Nat id, Url *path, Str *content, WorkQueue *q);
// Get the range representing the entire file.
Range STORM_FN full() const;
// Perform an edit to this file. Returns how much of the syntax information was invalidated.
Range STORM_FN replace(Range range, Str *replace);
// Get syntax colorings in the specified range. These are ordered and
// non-overlapping. The resulting colorings all overlap the specified range, but are not
// clipped to be strictly inside the range.
// TODO: Generate the required SExpr directly instead of going through an array.
Array<ColoredRange> *STORM_FN colors(Range r);
// Get the indentation of the character at offset 'n'.
syntax::TextIndent STORM_FN indent(Nat pos);
// Find the first parse error in the file and throw the error.
void STORM_FN findError();
// Our id.
Nat id;
// ID of the last edit operation.
Nat editId;
// Position of last edit operation (only a hint).
Nat editPos;
// Output our internal representation for debugging.
void STORM_FN debugOutput(TextOutput *to, Bool tree) const;
// Post a message for invalidating a part (or before/after a part).
void postInvalidate(Part *part, Int offset, Bool force);
// Border.
enum Border {
prevBorder,
nextBorder,
};
// Post a message for invalidating the border between 'part' and the next/previous part.
void postInvalidate(Part *part, Border border, Bool force);
// Get the file's Url.
Url *url() const { return path; }
// Get the content as a string.
Str *contentStr();
private:
// Path to the underlying file (so we can properly locate packages etc., we never actually read the file).
Url *path;
// Remember the package this file is located inside.
Package *package;
// All parts.
Array<Part *> *parts;
// Work queue.
WorkQueue *work;
// Empty string.
Str *emptyStr;
// Update 'part', return the modified range. This generally causes all following parts
// to be updated eventually as well.
Range updatePart(Nat part, Bool force);
friend class InvalidatePart;
};
/**
* Work items posted when we need to invalidate one or more parts.
*/
class InvalidatePart : public WorkItem {
STORM_CLASS;
public:
STORM_CTOR InvalidatePart(File *file, Nat part, Bool force);
Nat part;
Bool force;
virtual Range STORM_FN run(WorkQueue *q);
virtual Bool STORM_FN merge(WorkItem *o);
};
}
}
|