@@ -2396,3 +2396,199 @@ func (f *FourslashTest) applyEditsToString(content string, edits []*lsproto.Text
23962396
23972397 return result
23982398}
2399+
2400+ // FormatDocument applies formatting to the entire document and updates the file content.
2401+ // This method modifies the file in place, similar to how TypeScript's fourslash works.
2402+ func (f * FourslashTest ) FormatDocument (t * testing.T ) {
2403+ options := & lsproto.FormattingOptions {
2404+ TabSize : 4 ,
2405+ InsertSpaces : true ,
2406+ }
2407+
2408+ params := & lsproto.DocumentFormattingParams {
2409+ TextDocument : lsproto.TextDocumentIdentifier {
2410+ Uri : lsconv .FileNameToDocumentURI (f .activeFilename ),
2411+ },
2412+ Options : options ,
2413+ }
2414+
2415+ resMsg , result , resultOk := sendRequest (t , f , lsproto .TextDocumentFormattingInfo , params )
2416+ if resMsg == nil {
2417+ t .Fatal ("Nil response received for document formatting request" )
2418+ }
2419+ if ! resultOk {
2420+ resp := resMsg .AsResponse ()
2421+ if resp .Result == nil {
2422+ // No formatting needed
2423+ return
2424+ }
2425+ t .Fatalf ("Unexpected response type for document formatting request: %T" , resp .Result )
2426+ }
2427+
2428+ if result .TextEdits != nil && len (* result .TextEdits ) > 0 {
2429+ f .applyTextEdits (t , * result .TextEdits )
2430+ }
2431+ }
2432+
2433+ // FormatSelection applies formatting to the selection between two markers and updates the file content.
2434+ func (f * FourslashTest ) FormatSelection (t * testing.T , startMarkerName string , endMarkerName string ) {
2435+ startMarker , ok := f .testData .MarkerPositions [startMarkerName ]
2436+ if ! ok {
2437+ t .Fatalf ("Start marker '%s' not found" , startMarkerName )
2438+ }
2439+ endMarker , ok := f .testData .MarkerPositions [endMarkerName ]
2440+ if ! ok {
2441+ t .Fatalf ("End marker '%s' not found" , endMarkerName )
2442+ }
2443+ if startMarker .FileName () != endMarker .FileName () {
2444+ t .Fatalf ("Markers '%s' and '%s' are in different files" , startMarkerName , endMarkerName )
2445+ }
2446+
2447+ f .ensureActiveFile (t , startMarker .FileName ())
2448+
2449+ formatRange := lsproto.Range {
2450+ Start : startMarker .LSPosition ,
2451+ End : endMarker .LSPosition ,
2452+ }
2453+
2454+ options := & lsproto.FormattingOptions {
2455+ TabSize : 4 ,
2456+ InsertSpaces : true ,
2457+ }
2458+
2459+ params := & lsproto.DocumentRangeFormattingParams {
2460+ TextDocument : lsproto.TextDocumentIdentifier {
2461+ Uri : lsconv .FileNameToDocumentURI (f .activeFilename ),
2462+ },
2463+ Range : formatRange ,
2464+ Options : options ,
2465+ }
2466+
2467+ resMsg , result , resultOk := sendRequest (t , f , lsproto .TextDocumentRangeFormattingInfo , params )
2468+ if resMsg == nil {
2469+ t .Fatal ("Nil response received for range formatting request" )
2470+ }
2471+ if ! resultOk {
2472+ resp := resMsg .AsResponse ()
2473+ if resp .Result == nil {
2474+ // No formatting needed
2475+ return
2476+ }
2477+ t .Fatalf ("Unexpected response type for range formatting request: %T" , resp .Result )
2478+ }
2479+
2480+ if result .TextEdits != nil && len (* result .TextEdits ) > 0 {
2481+ f .applyTextEdits (t , * result .TextEdits )
2482+ }
2483+ }
2484+
2485+ // FormatOnType applies on-type formatting at the specified marker position after typing the given character.
2486+ func (f * FourslashTest ) FormatOnType (t * testing.T , markerName string , character string ) {
2487+ f .GoToMarker (t , markerName )
2488+
2489+ options := & lsproto.FormattingOptions {
2490+ TabSize : 4 ,
2491+ InsertSpaces : true ,
2492+ }
2493+
2494+ params := & lsproto.DocumentOnTypeFormattingParams {
2495+ TextDocument : lsproto.TextDocumentIdentifier {
2496+ Uri : lsconv .FileNameToDocumentURI (f .activeFilename ),
2497+ },
2498+ Position : f .currentCaretPosition ,
2499+ Ch : character ,
2500+ Options : options ,
2501+ }
2502+
2503+ resMsg , result , resultOk := sendRequest (t , f , lsproto .TextDocumentOnTypeFormattingInfo , params )
2504+ if resMsg == nil {
2505+ t .Fatal ("Nil response received for on-type formatting request" )
2506+ }
2507+ if ! resultOk {
2508+ resp := resMsg .AsResponse ()
2509+ if resp .Result == nil {
2510+ // No formatting needed
2511+ return
2512+ }
2513+ t .Fatalf ("Unexpected response type for on-type formatting request: %T" , resp .Result )
2514+ }
2515+
2516+ if result .TextEdits != nil && len (* result .TextEdits ) > 0 {
2517+ f .applyTextEdits (t , * result .TextEdits )
2518+ }
2519+ }
2520+
2521+ // CurrentLineContentIs verifies that the current line (where the caret is positioned) has the expected content.
2522+ func (f * FourslashTest ) CurrentLineContentIs (t * testing.T , expectedText string ) {
2523+ script := f .getScriptInfo (f .activeFilename )
2524+ lineIndex := int (f .currentCaretPosition .Line )
2525+
2526+ if lineIndex >= len (script .lineMap .LineStarts ) {
2527+ t .Fatalf ("Current line index %d is out of bounds (file has %d lines)" , lineIndex , len (script .lineMap .LineStarts ))
2528+ }
2529+
2530+ lineStart := int (script .lineMap .LineStarts [lineIndex ])
2531+ var lineEnd int
2532+ if lineIndex + 1 < len (script .lineMap .LineStarts ) {
2533+ lineEnd = int (script .lineMap .LineStarts [lineIndex + 1 ])
2534+ // Remove trailing newline characters
2535+ for lineEnd > lineStart && (script .content [lineEnd - 1 ] == '\n' || script .content [lineEnd - 1 ] == '\r' ) {
2536+ lineEnd --
2537+ }
2538+ } else {
2539+ lineEnd = len (script .content )
2540+ }
2541+
2542+ actualText := script .content [lineStart :lineEnd ]
2543+ if actualText != expectedText {
2544+ t .Fatalf ("Expected current line to be:\n %q\n but got:\n %q" , expectedText , actualText )
2545+ }
2546+ }
2547+
2548+ // CurrentFileContentIs verifies that the active file has the expected content.
2549+ func (f * FourslashTest ) CurrentFileContentIs (t * testing.T , expectedContent string ) {
2550+ script := f .getScriptInfo (f .activeFilename )
2551+ actualContent := script .content
2552+
2553+ if actualContent != expectedContent {
2554+ t .Fatalf ("Expected file content to be:\n %s\n \n but got:\n %s" , expectedContent , actualContent )
2555+ }
2556+ }
2557+
2558+ // FormatDocumentChangesNothing verifies that formatting the document produces no changes.
2559+ func (f * FourslashTest ) FormatDocumentChangesNothing (t * testing.T ) {
2560+ script := f .getScriptInfo (f .activeFilename )
2561+ originalContent := script .content
2562+
2563+ options := & lsproto.FormattingOptions {
2564+ TabSize : 4 ,
2565+ InsertSpaces : true ,
2566+ }
2567+
2568+ params := & lsproto.DocumentFormattingParams {
2569+ TextDocument : lsproto.TextDocumentIdentifier {
2570+ Uri : lsconv .FileNameToDocumentURI (f .activeFilename ),
2571+ },
2572+ Options : options ,
2573+ }
2574+
2575+ resMsg , result , resultOk := sendRequest (t , f , lsproto .TextDocumentFormattingInfo , params )
2576+ if resMsg == nil {
2577+ t .Fatal ("Nil response received for document formatting request" )
2578+ }
2579+ if ! resultOk {
2580+ resp := resMsg .AsResponse ()
2581+ if resp .Result == nil {
2582+ // No edits means no changes, which is what we expect
2583+ return
2584+ }
2585+ t .Fatalf ("Unexpected response type for document formatting request: %T" , resp .Result )
2586+ }
2587+
2588+ if result .TextEdits != nil && len (* result .TextEdits ) > 0 {
2589+ formattedContent := f .applyEditsToString (originalContent , * result .TextEdits )
2590+ if formattedContent != originalContent {
2591+ t .Fatalf ("Expected formatting to produce no changes, but content changed:\n Original:\n %s\n \n Formatted:\n %s" , originalContent , formattedContent )
2592+ }
2593+ }
2594+ }
0 commit comments