Skip to content

Commit 30bc80e

Browse files
committed
暂时采用一种性能略耗的方案
1 parent ce0faf2 commit 30bc80e

File tree

10 files changed

+455
-292
lines changed

10 files changed

+455
-292
lines changed

EmmyLua-LS/src/main/kotlin/com/tang/vscode/LuaTextDocumentService.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -354,7 +354,7 @@ class LuaTextDocumentService(private val workspace: LuaWorkspaceService) : TextD
354354
val position = arr[1].toInt()
355355
file.psi?.findElementAt(position)?.let { psi ->
356356
PsiTreeUtil.getParentOfType(psi, LuaClassMember::class.java)?.let { member ->
357-
val doc = documentProvider.generateDoc(member, member)
357+
val doc = documentProvider.generateDoc(member, false)
358358
val content = MarkupContent()
359359
content.kind = "markdown"
360360
content.value = doc
@@ -378,7 +378,7 @@ class LuaTextDocumentService(private val workspace: LuaWorkspaceService) : TextD
378378
val element = TargetElementUtil.findTarget(file.psi, pos)
379379
if (element != null) {
380380
val ref = element.reference?.resolve() ?: element
381-
val doc = documentProvider.generateDoc(ref, element)
381+
val doc = documentProvider.generateDoc(ref, true)
382382
if (doc != null)
383383
hover = Hover(listOf(Either.forLeft(doc)))
384384
}

EmmyLua-LS/src/main/kotlin/com/tang/vscode/diagnostics/DiagnosticsOptions.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,6 @@ object DiagnosticsOptions {
2424
var undeclaredVariable = InspectionsLevel.None
2525

2626
var assignValidation = InspectionsLevel.None
27+
28+
var deprecated = InspectionsLevel.Warning
2729
}

EmmyLua-LS/src/main/kotlin/com/tang/vscode/diagnostics/DiagnosticsService.kt

Lines changed: 7 additions & 274 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import com.tang.intellij.lua.search.SearchContext
1010
import com.tang.intellij.lua.ty.*
1111
import com.tang.lsp.ILuaFile
1212
import com.tang.lsp.toRange
13+
import com.tang.vscode.diagnostics.inspections.*
1314
import org.eclipse.lsp4j.Diagnostic
1415
import org.eclipse.lsp4j.DiagnosticSeverity
1516
import org.eclipse.lsp4j.DiagnosticTag
@@ -37,292 +38,24 @@ object DiagnosticsService {
3738
}
3839
}
3940
is LuaIndexExpr -> {
40-
// indexDeprecatedInspections(it, file, diagnostics)
41-
fieldValidationInspections(it, file, diagnostics)
41+
DeprecatedInspection.indexDeprecatedInspections(it, file, diagnostics)
42+
FieldValidInspection.fieldValidationInspections(it, file, diagnostics)
4243
}
4344
is LuaCallExpr -> {
44-
// callDeprecatedInspections(it, file, diagnostics)
45-
callExprInspections(it, file, diagnostics)
45+
FunctionInspection.callExprInspections(it, file, diagnostics)
4646
}
4747
is LuaAssignStat -> {
48-
assignInspections(it, file, diagnostics)
48+
AssignInspection.assignInspections(it, file, diagnostics)
4949
}
5050
is LuaNameExpr -> {
51-
undeclaredVariableInspections(it, file, diagnostics)
51+
DeprecatedInspection.nameExprDeprecatedInspections(it, file, diagnostics)
52+
UndeclaredVariableInspection.undeclaredVariableInspections(it, file, diagnostics)
5253
}
5354
}
54-
5555
true
5656
}
5757
}
5858

59-
private fun callDeprecatedInspections(o: LuaCallExpr, file: ILuaFile, diagnostics: MutableList<Diagnostic>) {
60-
val expr = o.expr
61-
if (expr is LuaNameExpr) {
62-
val resolve = expr.reference?.resolve()
63-
if (resolve is LuaFuncDef && resolve.isDeprecated) {
64-
val diagnostic = Diagnostic()
65-
diagnostic.message = "deprecated"
66-
diagnostic.severity = DiagnosticSeverity.Hint
67-
diagnostic.tags = listOf(DiagnosticTag.Deprecated)
68-
diagnostic.range = expr.textRange.toRange(file)
69-
diagnostics.add(diagnostic)
70-
}
71-
}
72-
}
73-
74-
private fun indexDeprecatedInspections(o: LuaIndexExpr, file: ILuaFile, diagnostics: MutableList<Diagnostic>) {
75-
val searchContext = SearchContext.get(o.project)
76-
val res = resolve(o, searchContext)
77-
if ((res is LuaClassMethodDef && res.isDeprecated)
78-
|| (res is LuaClassField && res.isDeprecated)
79-
) {
80-
o.id?.let { id ->
81-
val diagnostic = Diagnostic()
82-
diagnostic.message = "deprecated"
83-
diagnostic.severity = DiagnosticSeverity.Hint
84-
diagnostic.tags = listOf(DiagnosticTag.Deprecated)
85-
diagnostic.range = id.textRange.toRange(file)
86-
diagnostics.add(diagnostic)
87-
}
88-
}
89-
}
90-
91-
private fun fieldValidationInspections(o: LuaIndexExpr, file: ILuaFile, diagnostics: MutableList<Diagnostic>) {
92-
if (DiagnosticsOptions.fieldValidation != InspectionsLevel.None) {
93-
if (o.parent is LuaVarList) {
94-
return
95-
}
96-
val searchContext = SearchContext.get(o.project)
97-
val res = resolve(o, searchContext)
98-
val context = SearchContext.get(o.project)
99-
val prefixType = o.guessParentType(context)
100-
101-
if (prefixType !is TyUnknown && res == null) {
102-
o.id?.let { id ->
103-
val diagnostic = Diagnostic()
104-
diagnostic.message = "Undefined property '${id.text}'"
105-
diagnostic.severity = makeSeverity(DiagnosticsOptions.fieldValidation)
106-
diagnostic.range = id.textRange.toRange(file)
107-
diagnostics.add(diagnostic)
108-
}
109-
}
110-
}
111-
}
112-
113-
private fun assignInspections(o: LuaAssignStat, file: ILuaFile, diagnostics: MutableList<Diagnostic>) {
114-
if (DiagnosticsOptions.assignValidation != InspectionsLevel.None) {
115-
val assignees = o.varExprList.exprList
116-
val values = o.valueExprList?.exprList ?: listOf()
117-
val searchContext = SearchContext.get(o.project)
118-
119-
// Check right number of fields/assignments
120-
if (assignees.size > values.size) {
121-
for (i in values.size until assignees.size) {
122-
val diagnostic = Diagnostic()
123-
diagnostic.message = "Missing value assignment."
124-
diagnostic.severity = makeSeverity(DiagnosticsOptions.assignValidation)
125-
diagnostic.range = assignees[i].textRange.toRange(file)
126-
diagnostics.add(diagnostic)
127-
}
128-
} else if (assignees.size < values.size) {
129-
for (i in assignees.size until values.size) {
130-
val diagnostic = Diagnostic()
131-
diagnostic.message = "Nothing to assign to."
132-
diagnostic.severity = makeSeverity(DiagnosticsOptions.assignValidation)
133-
diagnostic.range = values[i].textRange.toRange(file)
134-
diagnostics.add(diagnostic)
135-
}
136-
} else {
137-
// Try to match types for each assignment
138-
for (i in 0 until assignees.size) {
139-
val field = assignees[i]
140-
val name = field.name ?: ""
141-
val value = values[i]
142-
val valueType = value.guessType(searchContext)
143-
144-
// Field access
145-
if (field is LuaIndexExpr) {
146-
// Get owner class
147-
val parent = field.guessParentType(searchContext)
148-
149-
if (parent is TyClass) {
150-
val fieldType = parent.findMemberType(name, searchContext) ?: Ty.NIL
151-
152-
if (!valueType.subTypeOf(fieldType, searchContext, false)) {
153-
val diagnostic = Diagnostic()
154-
diagnostic.message =
155-
"Type mismatch. Required: '%s' Found: '%s'".format(fieldType, valueType)
156-
diagnostic.severity = makeSeverity(DiagnosticsOptions.assignValidation)
157-
diagnostic.range = value.textRange.toRange(file)
158-
diagnostics.add(diagnostic)
159-
}
160-
}
161-
} else {
162-
// Local/global var assignments, only check type if there is no comment defining it
163-
if (o.comment == null) {
164-
val fieldType = field.guessType(searchContext)
165-
if (!valueType.subTypeOf(fieldType, searchContext, false)) {
166-
val diagnostic = Diagnostic()
167-
diagnostic.message =
168-
"Type mismatch. Required: '%s' Found: '%s'".format(fieldType, valueType)
169-
diagnostic.severity = makeSeverity(DiagnosticsOptions.assignValidation)
170-
diagnostic.range = value.textRange.toRange(file)
171-
diagnostics.add(diagnostic)
172-
}
173-
}
174-
}
175-
}
176-
}
177-
}
178-
}
179-
180-
181-
private fun callExprInspections(callExpr: LuaCallExpr, file: ILuaFile, diagnostics: MutableList<Diagnostic>) {
182-
if (DiagnosticsOptions.parameterValidation != InspectionsLevel.None) {
183-
var nCommas = 0
184-
val paramMap = mutableMapOf<Int, LuaTypeGuessable>()
185-
callExpr.args.firstChild?.let { firstChild ->
186-
var child: PsiElement? = firstChild
187-
while (child != null) {
188-
if (child.node.elementType == LuaTypes.COMMA) {
189-
nCommas++
190-
} else {
191-
if (child is LuaTypeGuessable) {
192-
paramMap[nCommas] = child
193-
}
194-
}
195-
196-
child = child.nextSibling
197-
}
198-
}
199-
val context = SearchContext.get(callExpr.project)
200-
callExpr.guessParentType(context).let { parentType ->
201-
parentType.each { ty ->
202-
if (ty is ITyFunction) {
203-
val sig = ty.findPerfectSignature(nCommas + 1)
204-
205-
var index = 0;
20659

207-
var skipFirstParam = false
20860

209-
if (sig.colonCall && callExpr.isMethodDotCall) {
210-
index++;
211-
} else if (!sig.colonCall && callExpr.isMethodColonCall) {
212-
skipFirstParam = true
213-
}
214-
215-
sig.params.forEach { pi ->
216-
if (skipFirstParam) {
217-
skipFirstParam = false
218-
return@forEach
219-
}
220-
221-
val param = paramMap[index]
222-
if (param != null) {
223-
val paramType = param.guessType(context)
224-
if (!paramTypeCheck(pi, param, context)) {
225-
val diagnostic = Diagnostic()
226-
diagnostic.message =
227-
"Type mismatch '${paramType.displayName}' not match type '${pi.ty.displayName}'"
228-
diagnostic.severity = makeSeverity(DiagnosticsOptions.parameterValidation)
229-
diagnostic.range = param.textRange.toRange(file)
230-
diagnostics.add(diagnostic)
231-
}
232-
} else if (!pi.nullable) {
233-
val diagnostic = Diagnostic()
234-
diagnostic.message =
235-
"Too few arguments to function call"
236-
diagnostic.severity = makeSeverity(DiagnosticsOptions.parameterValidation)
237-
val endOffset = callExpr.textRange.endOffset
238-
diagnostic.range = TextRange(endOffset, endOffset).toRange(file)
239-
diagnostics.add(diagnostic)
240-
return@each
241-
}
242-
++index;
243-
}
244-
//可变参数暂时不做验证
245-
}
246-
}
247-
}
248-
}
249-
}
250-
251-
private fun undeclaredVariableInspections(o: LuaNameExpr, file: ILuaFile, diagnostics: MutableList<Diagnostic>) {
252-
if (DiagnosticsOptions.undeclaredVariable != InspectionsLevel.None) {
253-
val res = resolve(o, SearchContext.get(o.project))
254-
255-
if (res == null) {
256-
val diagnostic = Diagnostic()
257-
diagnostic.message = "Undeclared variable '%s'.".format(o.text)
258-
diagnostic.severity = makeSeverity(DiagnosticsOptions.undeclaredVariable)
259-
diagnostic.range = o.textRange.toRange(file)
260-
diagnostics.add(diagnostic)
261-
262-
}
263-
}
264-
}
265-
266-
private fun makeSeverity(level: InspectionsLevel): DiagnosticSeverity {
267-
return when (level) {
268-
InspectionsLevel.None -> DiagnosticSeverity.Information
269-
InspectionsLevel.Warning -> DiagnosticSeverity.Warning
270-
InspectionsLevel.Error -> DiagnosticSeverity.Error
271-
}
272-
}
273-
274-
private fun paramTypeCheck(param: LuaParamInfo, variable: LuaTypeGuessable, context: SearchContext): Boolean {
275-
val variableType = variable.guessType(context)
276-
val defineType = param.ty
277-
278-
if (DiagnosticsOptions.defineTypeCanReceiveNilType && variableType.kind == TyKind.Nil) {
279-
return true
280-
}
281-
282-
if (!param.nullable && variableType.kind == TyKind.Nil) {
283-
return false
284-
}
285-
286-
// 由于没有接口 interface
287-
// 那么将匿名表传递给具有特定类型的定义类型也都被认为是合理的
288-
// 暂时不做field检查
289-
if (variable is LuaTableExpr &&
290-
(defineType.kind == TyKind.Class || defineType.kind == TyKind.Array || defineType.kind == TyKind.Tuple)
291-
) {
292-
return true
293-
}
294-
295-
// 类似于回调函数的写法,不写传参是非常普遍的,所以只需要认为定义类型是个函数就通过
296-
if (variable is LuaClosureExpr && defineType.kind == TyKind.Function) {
297-
return true
298-
}
299-
300-
return typeCheck(defineType, variableType, context)
301-
}
302-
303-
private fun typeCheck(defineType: ITy, variableType: ITy, context: SearchContext): Boolean {
304-
if (DiagnosticsOptions.anyTypeCanAssignToAnyDefineType && variableType is TyUnknown) {
305-
return true
306-
}
307-
308-
if (DiagnosticsOptions.defineAnyTypeCanBeAssignedByAnyVariable && defineType is TyUnknown) {
309-
return true
310-
}
311-
312-
if (defineType is TyUnion) {
313-
var isUnionCheckPass = false
314-
defineType.each {
315-
if (typeCheck(it, variableType, context)) {
316-
isUnionCheckPass = true
317-
return@each
318-
}
319-
}
320-
321-
if (isUnionCheckPass) {
322-
return true
323-
}
324-
}
325-
326-
return variableType.subTypeOf(defineType, context, true)
327-
}
32861
}

0 commit comments

Comments
 (0)