110 lines
3.8 KiB
Go
110 lines
3.8 KiB
Go
package ls
|
|
|
|
import (
|
|
"context"
|
|
"strings"
|
|
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnosticwriter"
|
|
"efprojects.com/kitten-ipc/kitcom/internal/tsgo/lsp/lsproto"
|
|
)
|
|
|
|
func (l *LanguageService) ProvideDiagnostics(ctx context.Context, uri lsproto.DocumentUri) (lsproto.DocumentDiagnosticResponse, error) {
|
|
program, file := l.getProgramAndFile(uri)
|
|
|
|
diagnostics := make([][]*ast.Diagnostic, 0, 4)
|
|
diagnostics = append(diagnostics, program.GetSyntacticDiagnostics(ctx, file))
|
|
diagnostics = append(diagnostics, program.GetSemanticDiagnostics(ctx, file))
|
|
// !!! user preference for suggestion diagnostics; keep only unnecessary/deprecated?
|
|
// See: https://github.com/microsoft/vscode/blob/3dbc74129aaae102e5cb485b958fa5360e8d3e7a/extensions/typescript-language-features/src/languageFeatures/diagnostics.ts#L114
|
|
diagnostics = append(diagnostics, program.GetSuggestionDiagnostics(ctx, file))
|
|
if program.Options().GetEmitDeclarations() {
|
|
diagnostics = append(diagnostics, program.GetDeclarationDiagnostics(ctx, file))
|
|
}
|
|
|
|
return lsproto.RelatedFullDocumentDiagnosticReportOrUnchangedDocumentDiagnosticReport{
|
|
FullDocumentDiagnosticReport: &lsproto.RelatedFullDocumentDiagnosticReport{
|
|
Items: toLSPDiagnostics(l.converters, diagnostics...),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func toLSPDiagnostics(converters *Converters, diagnostics ...[]*ast.Diagnostic) []*lsproto.Diagnostic {
|
|
size := 0
|
|
for _, diagSlice := range diagnostics {
|
|
size += len(diagSlice)
|
|
}
|
|
lspDiagnostics := make([]*lsproto.Diagnostic, 0, size)
|
|
for _, diagSlice := range diagnostics {
|
|
for _, diag := range diagSlice {
|
|
lspDiagnostics = append(lspDiagnostics, toLSPDiagnostic(converters, diag))
|
|
}
|
|
}
|
|
return lspDiagnostics
|
|
}
|
|
|
|
func toLSPDiagnostic(converters *Converters, diagnostic *ast.Diagnostic) *lsproto.Diagnostic {
|
|
var severity lsproto.DiagnosticSeverity
|
|
switch diagnostic.Category() {
|
|
case diagnostics.CategorySuggestion:
|
|
severity = lsproto.DiagnosticSeverityHint
|
|
case diagnostics.CategoryMessage:
|
|
severity = lsproto.DiagnosticSeverityInformation
|
|
case diagnostics.CategoryWarning:
|
|
severity = lsproto.DiagnosticSeverityWarning
|
|
default:
|
|
severity = lsproto.DiagnosticSeverityError
|
|
}
|
|
|
|
relatedInformation := make([]*lsproto.DiagnosticRelatedInformation, 0, len(diagnostic.RelatedInformation()))
|
|
for _, related := range diagnostic.RelatedInformation() {
|
|
relatedInformation = append(relatedInformation, &lsproto.DiagnosticRelatedInformation{
|
|
Location: lsproto.Location{
|
|
Uri: FileNameToDocumentURI(related.File().FileName()),
|
|
Range: converters.ToLSPRange(related.File(), related.Loc()),
|
|
},
|
|
Message: related.Message(),
|
|
})
|
|
}
|
|
|
|
var tags []lsproto.DiagnosticTag
|
|
if diagnostic.ReportsUnnecessary() || diagnostic.ReportsDeprecated() {
|
|
tags = make([]lsproto.DiagnosticTag, 0, 2)
|
|
if diagnostic.ReportsUnnecessary() {
|
|
tags = append(tags, lsproto.DiagnosticTagUnnecessary)
|
|
}
|
|
if diagnostic.ReportsDeprecated() {
|
|
tags = append(tags, lsproto.DiagnosticTagDeprecated)
|
|
}
|
|
}
|
|
|
|
return &lsproto.Diagnostic{
|
|
Range: converters.ToLSPRange(diagnostic.File(), diagnostic.Loc()),
|
|
Code: &lsproto.IntegerOrString{
|
|
Integer: ptrTo(diagnostic.Code()),
|
|
},
|
|
Severity: &severity,
|
|
Message: messageChainToString(diagnostic),
|
|
Source: ptrTo("ts"),
|
|
RelatedInformation: ptrToSliceIfNonEmpty(relatedInformation),
|
|
Tags: ptrToSliceIfNonEmpty(tags),
|
|
}
|
|
}
|
|
|
|
func messageChainToString(diagnostic *ast.Diagnostic) string {
|
|
if len(diagnostic.MessageChain()) == 0 {
|
|
return diagnostic.Message()
|
|
}
|
|
var b strings.Builder
|
|
diagnosticwriter.WriteFlattenedDiagnosticMessage(&b, diagnostic, "\n")
|
|
return b.String()
|
|
}
|
|
|
|
func ptrToSliceIfNonEmpty[T any](s []T) *[]T {
|
|
if len(s) == 0 {
|
|
return nil
|
|
}
|
|
return &s
|
|
}
|