载入中...
搜索中...
未找到
LanguageText.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <cctype>
5#include <utility>
6#include <vector>
7
8namespace eve::dev::lsp {
9namespace {
10
11enum class ScanKind { Code, LineComment, BlockComment, String };
12
13struct ScanState {
14 ScanKind kind = ScanKind::Code;
15 char quote = 0;
16 bool verbatim = false;
17};
18
19void advance(ScanState& state, std::string_view source, size_t& i, size_t end) {
20 if (i >= end) return;
21 const char value = source[i];
22 const char next = i + 1 < end ? source[i + 1] : 0;
23 if (state.kind == ScanKind::LineComment) {
24 if (value == '\n') state.kind = ScanKind::Code;
25 ++i;
26 return;
27 }
28 if (state.kind == ScanKind::BlockComment) {
29 if (value == '*' && next == '/') {
30 state.kind = ScanKind::Code;
31 i += 2;
32 return;
33 }
34 ++i;
35 return;
36 }
37 if (state.kind == ScanKind::String) {
38 if (!state.verbatim && value == '\\' && i + 1 < end) {
39 i += 2;
40 return;
41 }
42 if (state.verbatim && value == state.quote && next == state.quote) {
43 i += 2;
44 return;
45 }
46 if (value == state.quote) {
47 state.kind = ScanKind::Code;
48 ++i;
49 return;
50 }
51 ++i;
52 return;
53 }
54 if (value == '/' && next == '/') {
55 state.kind = ScanKind::LineComment;
56 i += 2;
57 return;
58 }
59 if (value == '/' && next == '*') {
60 state.kind = ScanKind::BlockComment;
61 i += 2;
62 return;
63 }
64 const bool verbatim = value == '@' && (next == '"' || next == '\'');
65 if (value == '"' || value == '\'' || verbatim) {
66 state.kind = ScanKind::String;
67 state.quote = verbatim ? next : value;
68 state.verbatim = verbatim;
69 i += verbatim ? 2 : 1;
70 return;
71 }
72 ++i;
73}
74
75std::string indentText(unsigned depth, const FormatOptions& options) {
76 if (depth == 0) return {};
77 if (options.insertSpaces) return std::string(static_cast<size_t>(depth) * options.tabSize, ' ');
78 return std::string(depth, '\t');
79}
80
81std::string rtrim(std::string_view line) {
82 size_t end = line.size();
83 while (end > 0 && (line[end - 1] == ' ' || line[end - 1] == '\t' || line[end - 1] == '\r')) --end;
84 return std::string(line.substr(0, end));
85}
86
87unsigned leadingCloses(std::string_view line, ScanState state) {
88 unsigned closes = 0;
89 size_t i = 0;
90 while (i < line.size() && (line[i] == ' ' || line[i] == '\t')) ++i;
91 while (i < line.size()) {
92 const size_t before = i;
93 const auto kind = state.kind;
94 advance(state, line, i, line.size());
95 if (kind != ScanKind::Code) continue;
96 if (before >= line.size()) break;
97 const char value = line[before];
98 if (value == '}' || value == ']') ++closes;
99 else if (value == '{' || value == '[')
100 break;
101 else if (!std::isspace(static_cast<unsigned char>(value)))
102 break;
103 if (i == before) ++i;
104 }
105 return closes;
106}
107
108int braceDelta(std::string_view line, ScanState& state) {
109 int delta = 0;
110 size_t i = 0;
111 while (i < line.size()) {
112 const size_t before = i;
113 const auto kind = state.kind;
114 advance(state, line, i, line.size());
115 if (kind != ScanKind::Code) continue;
116 if (before >= line.size()) break;
117 const char value = line[before];
118 if (value == '{' || value == '[') ++delta;
119 else if (value == '}' || value == ']')
120 --delta;
121 if (i == before) ++i;
122 }
123 return delta;
124}
125
126Position positionAtOffset(std::string_view source, size_t offset) {
128 for (size_t i = 0; i < offset && i < source.size(); ++i) {
129 if (source[i] == '\n') {
130 ++position.line;
131 position.character = 0;
132 } else {
133 ++position.character;
134 }
135 }
136 return position;
137}
138
139} // namespace
140
141size_t offsetAt(std::string_view source, Position position) noexcept {
142 size_t offset = 0;
143 for (size_t line = 0; line < position.line && offset < source.size(); ++line) {
144 const size_t newline = source.find('\n', offset);
145 if (newline == std::string_view::npos) return source.size();
146 offset = newline + 1;
147 }
148 return std::min(offset + position.character, source.size());
149}
150
151Range documentRange(std::string_view source) noexcept {
152 return {{0, 0}, positionAtOffset(source, source.size())};
153}
154
155size_t lastLineIndex(std::string_view source) noexcept {
156 size_t line = 0;
157 for (const char value : source)
158 if (value == '\n') ++line;
159 return line;
160}
161
162Range coveringLines(std::string_view source, size_t startLine, size_t endLineInclusive) noexcept {
163 const size_t last = lastLineIndex(source);
164 if (startLine > last) startLine = last;
165 if (endLineInclusive > last) endLineInclusive = last;
166 if (endLineInclusive < startLine) endLineInclusive = startLine;
167 Range range;
168 range.start = {startLine, 0};
169 if (endLineInclusive < last)
170 range.end = {endLineInclusive + 1, 0};
171 else
172 range.end = positionAtOffset(source, source.size());
173 return range;
174}
175
176std::string sliceLines(std::string_view source, size_t startLine, size_t endLineInclusive) {
177 const Range range = coveringLines(source, startLine, endLineInclusive);
178 const size_t begin = offsetAt(source, range.start);
179 const size_t end = offsetAt(source, range.end);
180 if (begin > end) return {};
181 return std::string(source.substr(begin, end - begin));
182}
183
184bool applyIncremental(std::string& source, const Range* range, std::string_view text) {
185 if (range == nullptr) {
186 source = std::string(text);
187 return true;
188 }
189 const size_t begin = offsetAt(source, range->start);
190 const size_t end = offsetAt(source, range->end);
191 if (begin > end || end > source.size()) return false;
192 source.replace(begin, end - begin, text);
193 return true;
194}
195
196std::string formatEveScript(std::string_view source, const FormatOptions& options) {
197 std::string result;
198 result.reserve(source.size());
199 ScanState state;
200 int indent = 0;
201 size_t lineBegin = 0;
202 const unsigned tab = options.tabSize == 0 ? 4 : options.tabSize;
203 FormatOptions normalized = options;
204 normalized.tabSize = tab;
205 for (size_t i = 0; i <= source.size(); ++i) {
206 if (i < source.size() && source[i] != '\n') continue;
207 const std::string_view raw = source.substr(lineBegin, i - lineBegin);
208 const std::string trimmed = rtrim(raw);
209 size_t content = 0;
210 while (content < trimmed.size() && (trimmed[content] == ' ' || trimmed[content] == '\t')) ++content;
211 const bool blank = content == trimmed.size();
212 if (blank) {
213 if (state.kind == ScanKind::BlockComment) result += trimmed;
214 } else if (state.kind == ScanKind::BlockComment) {
215 result += trimmed;
216 } else {
217 const unsigned closes = leadingCloses(trimmed, state);
218 const int display = indent - static_cast<int>(closes);
219 result += indentText(display < 0 ? 0 : static_cast<unsigned>(display), normalized);
220 result += trimmed.substr(content);
221 }
222 indent += braceDelta(trimmed, state);
223 if (indent < 0) indent = 0;
224 if (i < source.size()) result += '\n';
225 lineBegin = i + 1;
226 }
227 return result;
228}
229
230std::vector<FoldingRange> foldingRanges(std::string_view source) {
231 struct Open {
232 size_t line = 0;
233 char kind = '{';
234 ScanKind scan = ScanKind::Code;
235 };
236 std::vector<Open> stack;
237 std::vector<FoldingRange> result;
238 ScanState state;
239 size_t line = 0;
240 for (size_t i = 0; i < source.size();) {
241 const size_t before = i;
242 const auto kind = state.kind;
243 const char value = source[i];
244 advance(state, source, i, source.size());
245 if (value == '\n') {
246 ++line;
247 continue;
248 }
249 if (kind == ScanKind::Code && state.kind == ScanKind::BlockComment) {
250 stack.push_back({line, '*', ScanKind::BlockComment});
251 continue;
252 }
253 if (kind == ScanKind::BlockComment && state.kind == ScanKind::Code) {
254 if (!stack.empty() && stack.back().kind == '*') {
255 const size_t start = stack.back().line;
256 stack.pop_back();
257 if (line > start) result.push_back({start, line, "comment"});
258 }
259 continue;
260 }
261 if (kind != ScanKind::Code) continue;
262 if (value == '{' || value == '[') stack.push_back({line, value, ScanKind::Code});
263 else if ((value == '}' || value == ']') && !stack.empty() && stack.back().kind != '*') {
264 const size_t start = stack.back().line;
265 stack.pop_back();
266 if (line > start) result.push_back({start, line, "region"});
267 }
268 if (i == before) ++i;
269 }
270 return result;
271}
272
273} // namespace eve::dev::lsp
double value
Duration start
std::string text
TokenKind kind
size_t offset
Range range
bool verbatim
char quote
std::array< float, 3 > position
float begin
const SquirrelValueOptions & options
const UnitySourceAsset & source
std::uint32_t depth
size_t offsetAt(std::string_view source, Position position) noexcept
Converts an LSP position into a UTF-8 byte offset (same contract as the rest of the server).
size_t lastLineIndex(std::string_view source) noexcept
Last 0-based line index (empty source is line 0).
std::string sliceLines(std::string_view source, size_t startLine, size_t endLineInclusive)
Source bytes for coveringLines of the same line span.
bool applyIncremental(std::string &source, const Range *range, std::string_view text)
Applies one LSP content change.
std::string formatEveScript(std::string_view source, const FormatOptions &options)
Indents EveScript by braces/brackets and trims trailing whitespace.
Range coveringLines(std::string_view source, size_t startLine, size_t endLineInclusive) noexcept
Half-open LSP range covering startLine through endLine inclusive. Ends at the start of the following ...
Range documentRange(std::string_view source) noexcept
Full-document range covering every character in source.
std::vector< FoldingRange > foldingRanges(std::string_view source)
Brace, bracket, and block-comment folds that span more than one line.
constexpr HexDirection next(HexDirection d) noexcept
The next direction clockwise (NW wraps to NE).
Definition HexMetrics.h:76
enum EVENGINE_API_FOUNDATION String
OT_STRING.
Definition Runtime.h:119
Editor formatting preferences from LSP FormattingOptions.
Position public API.
Range public API.