From 4e872ae78c82ec8b5d6e518eef2505eb127cb25f Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 29 Oct 2025 18:29:50 -0700 Subject: [PATCH 1/2] Respect client capabilities for diagnostics --- internal/fourslash/fourslash.go | 11 ++++++++ internal/ls/diagnostics.go | 47 +++++++++++++++++++++------------ internal/lsp/server.go | 6 ++++- 3 files changed, 46 insertions(+), 18 deletions(-) diff --git a/internal/fourslash/fourslash.go b/internal/fourslash/fourslash.go index 7dcb546e2f..93bade8044 100644 --- a/internal/fourslash/fourslash.go +++ b/internal/fourslash/fourslash.go @@ -261,6 +261,17 @@ func getCapabilitiesWithDefaults(capabilities *lsproto.ClientCapabilities) *lspr if capabilitiesWithDefaults.TextDocument.Completion == nil { capabilitiesWithDefaults.TextDocument.Completion = defaultCompletionCapabilities } + if capabilitiesWithDefaults.TextDocument.Diagnostic == nil { + capabilitiesWithDefaults.TextDocument.Diagnostic = &lsproto.DiagnosticClientCapabilities{ + RelatedInformation: ptrTrue, + TagSupport: &lsproto.ClientDiagnosticsTagOptions{ + ValueSet: []lsproto.DiagnosticTag{ + lsproto.DiagnosticTagUnnecessary, + lsproto.DiagnosticTagDeprecated, + }, + }, + } + } if capabilitiesWithDefaults.Workspace == nil { capabilitiesWithDefaults.Workspace = &lsproto.WorkspaceClientCapabilities{} } diff --git a/internal/ls/diagnostics.go b/internal/ls/diagnostics.go index 684e3bcc0b..dfec4f35dd 100644 --- a/internal/ls/diagnostics.go +++ b/internal/ls/diagnostics.go @@ -2,6 +2,7 @@ package ls import ( "context" + "slices" "strings" "github.com/microsoft/typescript-go/internal/ast" @@ -11,7 +12,7 @@ import ( "github.com/microsoft/typescript-go/internal/lsp/lsproto" ) -func (l *LanguageService) ProvideDiagnostics(ctx context.Context, uri lsproto.DocumentUri) (lsproto.DocumentDiagnosticResponse, error) { +func (l *LanguageService) ProvideDiagnostics(ctx context.Context, uri lsproto.DocumentUri, clientOptions *lsproto.DiagnosticClientCapabilities) (lsproto.DocumentDiagnosticResponse, error) { program, file := l.getProgramAndFile(uri) diagnostics := make([][]*ast.Diagnostic, 0, 4) @@ -26,12 +27,12 @@ func (l *LanguageService) ProvideDiagnostics(ctx context.Context, uri lsproto.Do return lsproto.RelatedFullDocumentDiagnosticReportOrUnchangedDocumentDiagnosticReport{ FullDocumentDiagnosticReport: &lsproto.RelatedFullDocumentDiagnosticReport{ - Items: toLSPDiagnostics(l.converters, diagnostics...), + Items: toLSPDiagnostics(l.converters, clientOptions, diagnostics...), }, }, nil } -func toLSPDiagnostics(converters *lsconv.Converters, diagnostics ...[]*ast.Diagnostic) []*lsproto.Diagnostic { +func toLSPDiagnostics(converters *lsconv.Converters, clientOptions *lsproto.DiagnosticClientCapabilities, diagnostics ...[]*ast.Diagnostic) []*lsproto.Diagnostic { size := 0 for _, diagSlice := range diagnostics { size += len(diagSlice) @@ -39,13 +40,13 @@ func toLSPDiagnostics(converters *lsconv.Converters, diagnostics ...[]*ast.Diagn lspDiagnostics := make([]*lsproto.Diagnostic, 0, size) for _, diagSlice := range diagnostics { for _, diag := range diagSlice { - lspDiagnostics = append(lspDiagnostics, toLSPDiagnostic(converters, diag)) + lspDiagnostics = append(lspDiagnostics, toLSPDiagnostic(converters, clientOptions, diag)) } } return lspDiagnostics } -func toLSPDiagnostic(converters *lsconv.Converters, diagnostic *ast.Diagnostic) *lsproto.Diagnostic { +func toLSPDiagnostic(converters *lsconv.Converters, clientOptions *lsproto.DiagnosticClientCapabilities, diagnostic *ast.Diagnostic) *lsproto.Diagnostic { var severity lsproto.DiagnosticSeverity switch diagnostic.Category() { case diagnostics.CategorySuggestion: @@ -58,24 +59,27 @@ func toLSPDiagnostic(converters *lsconv.Converters, diagnostic *ast.Diagnostic) 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: lsconv.FileNameToDocumentURI(related.File().FileName()), - Range: converters.ToLSPRange(related.File(), related.Loc()), - }, - Message: related.Message(), - }) + var relatedInformation []*lsproto.DiagnosticRelatedInformation + if clientOptions != nil && ptrIsTrue(clientOptions.RelatedInformation) { + relatedInformation = make([]*lsproto.DiagnosticRelatedInformation, 0, len(diagnostic.RelatedInformation())) + for _, related := range diagnostic.RelatedInformation() { + relatedInformation = append(relatedInformation, &lsproto.DiagnosticRelatedInformation{ + Location: lsproto.Location{ + Uri: lsconv.FileNameToDocumentURI(related.File().FileName()), + Range: converters.ToLSPRange(related.File(), related.Loc()), + }, + Message: related.Message(), + }) + } } var tags []lsproto.DiagnosticTag - if diagnostic.ReportsUnnecessary() || diagnostic.ReportsDeprecated() { + if clientOptions != nil && clientOptions.TagSupport != nil && (diagnostic.ReportsUnnecessary() || diagnostic.ReportsDeprecated()) { tags = make([]lsproto.DiagnosticTag, 0, 2) - if diagnostic.ReportsUnnecessary() { + if diagnostic.ReportsUnnecessary() && slices.Contains(clientOptions.TagSupport.ValueSet, lsproto.DiagnosticTagUnnecessary) { tags = append(tags, lsproto.DiagnosticTagUnnecessary) } - if diagnostic.ReportsDeprecated() { + if diagnostic.ReportsDeprecated() && slices.Contains(clientOptions.TagSupport.ValueSet, lsproto.DiagnosticTagDeprecated) { tags = append(tags, lsproto.DiagnosticTagDeprecated) } } @@ -108,3 +112,12 @@ func ptrToSliceIfNonEmpty[T any](s []T) *[]T { } return &s } + +func sliceContains(slice []lsproto.DiagnosticTag, value lsproto.DiagnosticTag) bool { + for _, v := range slice { + if v == value { + return true + } + } + return false +} diff --git a/internal/lsp/server.go b/internal/lsp/server.go index d5a2aca5a3..c51a40547f 100644 --- a/internal/lsp/server.go +++ b/internal/lsp/server.go @@ -800,7 +800,11 @@ func (s *Server) handleSetTrace(ctx context.Context, params *lsproto.SetTracePar } func (s *Server) handleDocumentDiagnostic(ctx context.Context, ls *ls.LanguageService, params *lsproto.DocumentDiagnosticParams) (lsproto.DocumentDiagnosticResponse, error) { - return ls.ProvideDiagnostics(ctx, params.TextDocument.Uri) + var diagnosticClientCapabilities *lsproto.DiagnosticClientCapabilities + if s.initializeParams != nil && s.initializeParams.Capabilities != nil && s.initializeParams.Capabilities.TextDocument != nil { + diagnosticClientCapabilities = s.initializeParams.Capabilities.TextDocument.Diagnostic + } + return ls.ProvideDiagnostics(ctx, params.TextDocument.Uri, diagnosticClientCapabilities) } func (s *Server) handleHover(ctx context.Context, ls *ls.LanguageService, params *lsproto.HoverParams) (lsproto.HoverResponse, error) { From 1d804fb36004f65122792e07181165b4ce053bed Mon Sep 17 00:00:00 2001 From: Jake Bailey <5341706+jakebailey@users.noreply.github.com> Date: Wed, 29 Oct 2025 18:32:39 -0700 Subject: [PATCH 2/2] To method --- internal/ls/diagnostics.go | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/internal/ls/diagnostics.go b/internal/ls/diagnostics.go index dfec4f35dd..83c3f1d61d 100644 --- a/internal/ls/diagnostics.go +++ b/internal/ls/diagnostics.go @@ -27,12 +27,12 @@ func (l *LanguageService) ProvideDiagnostics(ctx context.Context, uri lsproto.Do return lsproto.RelatedFullDocumentDiagnosticReportOrUnchangedDocumentDiagnosticReport{ FullDocumentDiagnosticReport: &lsproto.RelatedFullDocumentDiagnosticReport{ - Items: toLSPDiagnostics(l.converters, clientOptions, diagnostics...), + Items: l.toLSPDiagnostics(clientOptions, diagnostics...), }, }, nil } -func toLSPDiagnostics(converters *lsconv.Converters, clientOptions *lsproto.DiagnosticClientCapabilities, diagnostics ...[]*ast.Diagnostic) []*lsproto.Diagnostic { +func (l *LanguageService) toLSPDiagnostics(clientOptions *lsproto.DiagnosticClientCapabilities, diagnostics ...[]*ast.Diagnostic) []*lsproto.Diagnostic { size := 0 for _, diagSlice := range diagnostics { size += len(diagSlice) @@ -40,13 +40,13 @@ func toLSPDiagnostics(converters *lsconv.Converters, clientOptions *lsproto.Diag lspDiagnostics := make([]*lsproto.Diagnostic, 0, size) for _, diagSlice := range diagnostics { for _, diag := range diagSlice { - lspDiagnostics = append(lspDiagnostics, toLSPDiagnostic(converters, clientOptions, diag)) + lspDiagnostics = append(lspDiagnostics, l.toLSPDiagnostic(clientOptions, diag)) } } return lspDiagnostics } -func toLSPDiagnostic(converters *lsconv.Converters, clientOptions *lsproto.DiagnosticClientCapabilities, diagnostic *ast.Diagnostic) *lsproto.Diagnostic { +func (l *LanguageService) toLSPDiagnostic(clientOptions *lsproto.DiagnosticClientCapabilities, diagnostic *ast.Diagnostic) *lsproto.Diagnostic { var severity lsproto.DiagnosticSeverity switch diagnostic.Category() { case diagnostics.CategorySuggestion: @@ -66,7 +66,7 @@ func toLSPDiagnostic(converters *lsconv.Converters, clientOptions *lsproto.Diagn relatedInformation = append(relatedInformation, &lsproto.DiagnosticRelatedInformation{ Location: lsproto.Location{ Uri: lsconv.FileNameToDocumentURI(related.File().FileName()), - Range: converters.ToLSPRange(related.File(), related.Loc()), + Range: l.converters.ToLSPRange(related.File(), related.Loc()), }, Message: related.Message(), }) @@ -85,7 +85,7 @@ func toLSPDiagnostic(converters *lsconv.Converters, clientOptions *lsproto.Diagn } return &lsproto.Diagnostic{ - Range: converters.ToLSPRange(diagnostic.File(), diagnostic.Loc()), + Range: l.converters.ToLSPRange(diagnostic.File(), diagnostic.Loc()), Code: &lsproto.IntegerOrString{ Integer: ptrTo(diagnostic.Code()), }, @@ -112,12 +112,3 @@ func ptrToSliceIfNonEmpty[T any](s []T) *[]T { } return &s } - -func sliceContains(slice []lsproto.DiagnosticTag, value lsproto.DiagnosticTag) bool { - for _, v := range slice { - if v == value { - return true - } - } - return false -}