Skip to content

Commit e1d532e

Browse files
committed
重构TextRange, 格式检查支持缩进
1 parent 7bee434 commit e1d532e

File tree

24 files changed

+404
-307
lines changed

24 files changed

+404
-307
lines changed

CodeFormat/src/LuaFormat.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,8 +175,8 @@ void LuaFormat::DiagnosticInspection(std::string_view message, TextRange range,
175175
std::string_view path) {
176176
auto startLine = file->GetLine(range.StartOffset);
177177
auto startChar = file->GetColumn(range.StartOffset);
178-
auto endLine = file->GetLine(range.EndOffset);
179-
auto endChar = file->GetColumn(range.EndOffset);
178+
auto endLine = file->GetLine(range.GetEndOffset());
179+
auto endChar = file->GetColumn(range.GetEndOffset());
180180
std::cerr << util::format("\t{}({}:{} to {}:{}): {}", path, startLine + 1, startChar, endLine + 1, endChar,
181181
message) << std::endl;
182182
}

CodeFormatLib/src/LuaCodeFormat.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,8 +257,8 @@ std::vector<LuaDiagnosticInfo> LuaCodeFormat::MakeDiagnosticInfo(const std::vect
257257
result.Data = diagnostic.Data;
258258
result.Start.Line = file->GetLine(diagnostic.Range.StartOffset);
259259
result.Start.Col = file->GetColumn(diagnostic.Range.StartOffset);
260-
result.End.Line = file->GetLine(diagnostic.Range.EndOffset);
261-
result.End.Col = file->GetColumn(diagnostic.Range.EndOffset);
260+
result.End.Line = file->GetLine(diagnostic.Range.GetEndOffset());
261+
result.End.Col = file->GetColumn(diagnostic.Range.GetEndOffset());
262262
}
263263

264264
return results;

CodeFormatServer/src/Service/DiagnosticService.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ DiagnosticService::Diagnostic(std::size_t fileId,
3434
auto &diag = diagnostics.emplace_back();
3535
diag.message = result.Message;
3636
auto startLC = lineIndex->GetLineCol(result.Range.StartOffset);
37-
auto endLC = lineIndex->GetLineCol(result.Range.EndOffset);
37+
auto endLC = lineIndex->GetLineCol(result.Range.GetEndOffset());
3838
diag.range = lsp::Range(
3939
lsp::Position(startLC.Line, startLC.Col),
4040
lsp::Position(endLC.Line, endLC.Col + 1)

CodeService/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ target_sources(CodeService
4747
${CodeService_SOURCE_DIR}/src/Diagnostic/Spell/CodeSpellChecker.cpp
4848
${CodeService_SOURCE_DIR}/src/Diagnostic/Spell/IdentifyParser.cpp
4949
${CodeService_SOURCE_DIR}/src/Diagnostic/Spell/TextParser.cpp
50+
# diagnostic/codestyle
51+
${CodeService_SOURCE_DIR}/src/Diagnostic/CodeStyle/CodeStyleChecker.cpp
5052
)
5153

5254
if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC")

CodeService/src/Config/FunctionOption.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ void FunctionOption::Parse(std::string_view option) {
2424
return;
2525
}
2626
_key = std::string(
27-
virtualFile->Slice(token.Range.StartOffset, token.Range.EndOffset));
27+
virtualFile->Slice(token.Range.StartOffset, token.Range.GetEndOffset()));
2828
state = ParseState::Key;
2929
break;
3030
}
@@ -37,7 +37,7 @@ void FunctionOption::Parse(std::string_view option) {
3737
}
3838
case ParseState::ExpectParam: {
3939
if (token.TokenType == TK_NAME || token.TokenType == TK_NUMBER || token.TokenType == TK_STRING) {
40-
_params.emplace_back(virtualFile->Slice(token.Range.StartOffset, token.Range.EndOffset));
40+
_params.emplace_back(virtualFile->Slice(token.Range.StartOffset, token.Range.GetEndOffset()));
4141
state = ParseState::ExpectCommaOrFinish;
4242
} else if (token.TokenType == ')') {
4343
return;
Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
#include "CodeService/Diagnostic/CodeStyle/CodeStyleChecker.h"
2+
#include "CodeService/Diagnostic/DiagnosticBuilder.h"
3+
#include "CodeService/Config/LanguageTranslator.h"
4+
#include "Util/format.h"
5+
#include "LuaParser/Lexer/LuaTokenTypeDetail.h"
6+
7+
CodeStyleChecker::CodeStyleChecker() {
8+
9+
}
10+
11+
void CodeStyleChecker::Analyze(DiagnosticBuilder &d, const LuaSyntaxTree &t) {
12+
BasicStyleCheck(d, t);
13+
EndWithNewLine(d, t);
14+
}
15+
16+
void CodeStyleChecker::BasicStyleCheck(DiagnosticBuilder &d, const LuaSyntaxTree &t) {
17+
auto &state = d.GetState();
18+
state.Analyze(t);
19+
20+
auto root = t.GetRootNode();
21+
std::vector<LuaSyntaxNode> startNodes = {root};
22+
23+
state.DfsForeach(startNodes, t, [this, &d](LuaSyntaxNode &syntaxNode,
24+
const LuaSyntaxTree &t,
25+
FormatResolve &resolve) {
26+
BasicResolve(syntaxNode, t, resolve, d);
27+
});
28+
}
29+
30+
void CodeStyleChecker::EndWithNewLine(DiagnosticBuilder &d, const LuaSyntaxTree &t) {
31+
auto &state = d.GetState();
32+
auto &file = t.GetFile();
33+
auto text = file.GetSource();
34+
if (state.GetStyle().insert_final_newline) {
35+
if (!text.empty()) {
36+
if (text.back() != '\n' && text.back() != '\r') {
37+
d.PushDiagnostic(DiagnosticType::EndWithNewLine,
38+
TextRange(text.size(), 0),
39+
LText("must end with new line")
40+
);
41+
}
42+
}
43+
} else {
44+
if (!text.empty()) {
45+
if (text.back() == '\n' || text.back() == '\r') {
46+
d.PushDiagnostic(DiagnosticType::EndWithNewLine,
47+
TextRange(text.size(), 0),
48+
LText("can not end with new line")
49+
);
50+
}
51+
}
52+
}
53+
}
54+
55+
void CodeStyleChecker::BasicResolve(LuaSyntaxNode syntaxNode, const LuaSyntaxTree &t, FormatResolve &resolve,
56+
DiagnosticBuilder &d) {
57+
if (syntaxNode.IsToken(t)) {
58+
auto textRange = syntaxNode.GetTextRange(t);
59+
auto prevToken = syntaxNode.GetPrevToken(t);
60+
if (prevToken.GetEndLine(t) != syntaxNode.GetStartLine(t) || prevToken.IsNull(t)) {
61+
ProcessIndentDiagnostic(syntaxNode, t, d);
62+
}
63+
64+
switch (resolve.GetPrevSpaceStrategy()) {
65+
case PrevSpaceStrategy::AlignPos: {
66+
d.ClearDiagnostic(syntaxNode.GetPrevToken(t).GetIndex());
67+
break;
68+
}
69+
case PrevSpaceStrategy::AlignRelativeIndent: {
70+
d.ClearDiagnostic(syntaxNode.GetPrevToken(t).GetIndex());
71+
break;
72+
}
73+
default: {
74+
break;
75+
}
76+
}
77+
78+
switch (resolve.GetTokenStrategy()) {
79+
case TokenStrategy::StringSingleQuote: {
80+
if (syntaxNode.GetTokenKind(t) == TK_STRING) {
81+
auto text = syntaxNode.GetText(t);
82+
if (text.size() >= 2
83+
&& text.front() == '\"') {
84+
d.PushDiagnostic(DiagnosticType::StringQuote, textRange,
85+
LText("\" should be \' ")
86+
);
87+
break;
88+
}
89+
}
90+
break;
91+
}
92+
case TokenStrategy::StringDoubleQuote: {
93+
if (syntaxNode.GetTokenKind(t) == TK_STRING) {
94+
auto text = syntaxNode.GetText(t);
95+
if (text.size() >= 2
96+
&& text.front() == '\'') {
97+
d.PushDiagnostic(DiagnosticType::StringQuote, textRange,
98+
LText("\' should be \" ")
99+
);
100+
break;
101+
}
102+
}
103+
104+
break;
105+
}
106+
default: {
107+
break;
108+
}
109+
}
110+
111+
switch (resolve.GetNextSpaceStrategy()) {
112+
case NextSpaceStrategy::Space: {
113+
auto nextToken = syntaxNode.GetNextToken(t);
114+
if (nextToken.IsToken(t)
115+
&& nextToken.GetStartLine(t) == syntaxNode.GetEndLine(t)) {
116+
ProcessSpaceDiagnostic(syntaxNode, nextToken, resolve.GetNextSpace(), t, d);
117+
}
118+
break;
119+
}
120+
case NextSpaceStrategy::LineBreak: {
121+
break;
122+
}
123+
default: {
124+
break;
125+
}
126+
}
127+
}
128+
}
129+
130+
void CodeStyleChecker::ProcessSpaceDiagnostic(LuaSyntaxNode &node, LuaSyntaxNode &next, size_t shouldSpace,
131+
const LuaSyntaxTree &t, DiagnosticBuilder &d) {
132+
auto leftOffset = node.GetTextRange(t).GetEndOffset();
133+
auto rightOffset = next.GetTextRange(t).StartOffset;
134+
if (rightOffset < leftOffset + 1) {
135+
return;
136+
}
137+
138+
std::size_t diff = rightOffset - leftOffset - 1;
139+
if (diff == shouldSpace) {
140+
return;
141+
}
142+
auto additional = GetAdditionalNote(node, next, t);
143+
switch (shouldSpace) {
144+
case 0: {
145+
d.PushDiagnostic(DiagnosticType::Space,
146+
node.GetIndex(),
147+
TextRange(leftOffset + 1, diff),
148+
util::format(LText("unnecessary whitespace {}"), additional)
149+
);
150+
break;
151+
}
152+
case 1: {
153+
if (diff == 0) {
154+
d.PushDiagnostic(DiagnosticType::Space,
155+
node.GetIndex(),
156+
TextRange(leftOffset, 2),
157+
util::format(LText("missing whitespace {}"), additional)
158+
);
159+
} else {
160+
d.PushDiagnostic(DiagnosticType::Space,
161+
node.GetIndex(),
162+
TextRange(leftOffset + 1, diff),
163+
util::format(LText("multiple spaces {}"), additional)
164+
);
165+
}
166+
break;
167+
}
168+
default: {
169+
if (diff < shouldSpace) {
170+
d.PushDiagnostic(DiagnosticType::Space, node.GetIndex(),
171+
TextRange(leftOffset, diff + 1),
172+
util::format(LText("expected {} whitespace, found {} {}"),
173+
shouldSpace, diff, additional)
174+
);
175+
} else {
176+
d.PushDiagnostic(DiagnosticType::Space, node.GetIndex(),
177+
TextRange(leftOffset + 1, diff),
178+
util::format(LText("expected {} whitespace, found {} {}"),
179+
shouldSpace, diff, additional)
180+
);
181+
}
182+
}
183+
}
184+
}
185+
186+
std::string CodeStyleChecker::GetAdditionalNote(LuaSyntaxNode &left, LuaSyntaxNode &right, const LuaSyntaxTree &t) {
187+
switch (left.GetTokenKind(t)) {
188+
case TK_STRING:
189+
case TK_LONG_STRING:
190+
case TK_LONG_COMMENT:
191+
case TK_SHORT_COMMENT:
192+
case TK_NAME: {
193+
break;
194+
}
195+
default: {
196+
return util::format("after token '{}'", left.GetText(t));
197+
}
198+
}
199+
200+
switch (right.GetTokenKind(t)) {
201+
case TK_STRING:
202+
case TK_LONG_STRING:
203+
case TK_LONG_COMMENT:
204+
case TK_SHORT_COMMENT:
205+
case TK_NAME: {
206+
return "";
207+
}
208+
default: {
209+
return util::format("before token '{}'", right.GetText(t));
210+
}
211+
}
212+
}
213+
214+
void CodeStyleChecker::ProcessIndentDiagnostic(LuaSyntaxNode &node, const LuaSyntaxTree &t, DiagnosticBuilder &d) {
215+
auto textRange = node.GetTextRange(t);
216+
auto &state = d.GetState();
217+
auto &file = t.GetFile();
218+
219+
auto indent = state.GetCurrentIndent();
220+
auto currentIndentRange = file.GetIndentRange(textRange.StartOffset);
221+
auto currentIndentString = file.Slice(currentIndentRange);
222+
223+
struct CheckIndent {
224+
std::size_t Tab = 0;
225+
std::size_t Space = 0;
226+
} checkIndent;
227+
228+
enum class ParseState {
229+
Tab,
230+
Space,
231+
} parseState = ParseState::Tab;
232+
233+
for (auto c: currentIndentString) {
234+
switch (parseState) {
235+
case ParseState::Tab: {
236+
if (c == '\t') {
237+
checkIndent.Tab++;
238+
} else {
239+
checkIndent.Space++;
240+
parseState = ParseState::Space;
241+
}
242+
break;
243+
}
244+
case ParseState::Space: {
245+
checkIndent.Space++;
246+
}
247+
}
248+
249+
}
250+
251+
if (indent.TabSize != checkIndent.Tab || indent.SpaceSize != checkIndent.Space) {
252+
d.PushDiagnostic(DiagnosticType::Indent,
253+
currentIndentRange,
254+
util::format("{}, found {} whitespace, {} tab",
255+
GetIndentNote(indent, state.GetStyle().indent_style),
256+
checkIndent.Space, checkIndent.Tab
257+
));
258+
}
259+
}
260+
261+
std::string CodeStyleChecker::GetIndentNote(IndentState indent, IndentStyle style) {
262+
if (style == IndentStyle::Tab) {
263+
return util::format("expected {} tab indent", indent.TabSize);
264+
} else {
265+
return util::format("expected {} whitespace indent", indent.SpaceSize);
266+
}
267+
}

0 commit comments

Comments
 (0)