internal/ls/codeactions.go (93 lines of code) (raw):

package ls import ( "context" "slices" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/checker" "github.com/microsoft/typescript-go/internal/compiler" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/lsp/lsproto" ) // CodeFixProvider represents a provider for a specific type of code fix type CodeFixProvider struct { ErrorCodes []int32 GetCodeActions func(ctx context.Context, fixContext *CodeFixContext) []CodeAction FixIds []string GetAllCodeActions func(ctx context.Context, fixContext *CodeFixContext) *CombinedCodeActions } // CodeFixContext contains the context needed to generate code fixes type CodeFixContext struct { SourceFile *ast.SourceFile Span core.TextRange ErrorCode int32 Program *compiler.Program Checker *checker.Checker LS *LanguageService Diagnostic *lsproto.Diagnostic Params *lsproto.CodeActionParams } // CodeAction represents a single code action fix type CodeAction struct { Description string Changes []*lsproto.TextEdit } // CombinedCodeActions represents combined code actions for fix-all scenarios type CombinedCodeActions struct { Description string Changes []*lsproto.TextEdit } // codeFixProviders is the list of all registered code fix providers var codeFixProviders = []*CodeFixProvider{ ImportFixProvider, // Add more code fix providers here as they are implemented } // ProvideCodeActions returns code actions for the given range and context func (l *LanguageService) ProvideCodeActions(ctx context.Context, params *lsproto.CodeActionParams) (lsproto.CodeActionResponse, error) { program, file := l.getProgramAndFile(params.TextDocument.Uri) // Get the type checker ch, done := program.GetTypeCheckerForFile(ctx, file) if done != nil { defer done() } var actions []lsproto.CommandOrCodeAction // Process diagnostics in the context to generate quick fixes if params.Context != nil && params.Context.Diagnostics != nil { for _, diag := range params.Context.Diagnostics { if diag.Code == nil || diag.Code.Integer == nil { continue } errorCode := *diag.Code.Integer // Check all code fix providers for _, provider := range codeFixProviders { if !containsErrorCode(provider.ErrorCodes, errorCode) { continue } // Create context for the provider position := l.converters.LineAndCharacterToPosition(file, diag.Range.Start) endPosition := l.converters.LineAndCharacterToPosition(file, diag.Range.End) fixContext := &CodeFixContext{ SourceFile: file, Span: core.NewTextRange(int(position), int(endPosition)), ErrorCode: errorCode, Program: program, Checker: ch, LS: l, Diagnostic: diag, Params: params, } // Get code actions from the provider providerActions := provider.GetCodeActions(ctx, fixContext) for _, action := range providerActions { actions = append(actions, convertToLSPCodeAction(&action, diag, params.TextDocument.Uri)) } } } } return lsproto.CommandOrCodeActionArrayOrNull{CommandOrCodeActionArray: &actions}, nil } // containsErrorCode checks if the error code is in the list func containsErrorCode(codes []int32, code int32) bool { return slices.Contains(codes, code) } // convertToLSPCodeAction converts an internal CodeAction to an LSP CodeAction func convertToLSPCodeAction(action *CodeAction, diag *lsproto.Diagnostic, uri lsproto.DocumentUri) lsproto.CommandOrCodeAction { kind := lsproto.CodeActionKindQuickFix changes := map[lsproto.DocumentUri][]*lsproto.TextEdit{ uri: action.Changes, } diagnostics := []*lsproto.Diagnostic{diag} return lsproto.CommandOrCodeAction{ CodeAction: &lsproto.CodeAction{ Title: action.Description, Kind: &kind, Edit: &lsproto.WorkspaceEdit{Changes: &changes}, Diagnostics: &diagnostics, }, } }