@@ -249,6 +249,22 @@ function parseFourslashStatement(statement: ts.Statement): Cmd[] | undefined {
249249 return parseVerifyNavTree ( callExpression . arguments ) ;
250250 case "navigationBar" :
251251 return [ ] ; // Deprecated.
252+ case "numberOfErrorsInCurrentFile" :
253+ return parseNumberOfErrorsInCurrentFile ( callExpression . arguments ) ;
254+ case "noErrors" :
255+ return [ { kind : "verifyNoErrors" } ] ;
256+ case "errorExistsAtRange" :
257+ return parseErrorExistsAtRange ( callExpression . arguments ) ;
258+ case "currentLineContentIs" :
259+ return parseCurrentLineContentIs ( callExpression . arguments ) ;
260+ case "currentFileContentIs" :
261+ return parseCurrentFileContentIs ( callExpression . arguments ) ;
262+ case "errorExistsBetweenMarkers" :
263+ return parseErrorExistsBetweenMarkers ( callExpression . arguments ) ;
264+ case "errorExistsAfterMarker" :
265+ return parseErrorExistsAfterMarker ( callExpression . arguments ) ;
266+ case "errorExistsBeforeMarker" :
267+ return parseErrorExistsBeforeMarker ( callExpression . arguments ) ;
252268 }
253269 }
254270 // `goTo....`
@@ -313,6 +329,32 @@ function parseEditStatement(funcName: string, args: readonly ts.Expression[]): E
313329 goStatement : `f.Backspace(t, 1)` ,
314330 } ;
315331 }
332+ case "deleteAtCaret" : {
333+ const arg = args [ 0 ] ;
334+ if ( arg ) {
335+ let arg0 ;
336+ if ( arg0 = getNumericLiteral ( arg ) ) {
337+ return {
338+ kind : "edit" ,
339+ goStatement : `f.DeleteAtCaret(t, ${ arg0 . text } )` ,
340+ } ;
341+ }
342+ // Handle 'string'.length expressions
343+ const lengthValue = getStringLengthExpression ( arg ) ;
344+ if ( lengthValue !== undefined ) {
345+ return {
346+ kind : "edit" ,
347+ goStatement : `f.DeleteAtCaret(t, ${ lengthValue } )` ,
348+ } ;
349+ }
350+ console . error ( `Expected numeric literal argument in edit.deleteAtCaret, got ${ arg . getText ( ) } ` ) ;
351+ return undefined ;
352+ }
353+ return {
354+ kind : "edit" ,
355+ goStatement : `f.DeleteAtCaret(t, 1)` ,
356+ } ;
357+ }
316358 default :
317359 console . error ( `Unrecognized edit function: ${ funcName } ` ) ;
318360 return undefined ;
@@ -1446,6 +1488,130 @@ function parseExpectedDiagnostic(expr: ts.Expression): string | undefined {
14461488 return `&lsproto.Diagnostic{\n${ diagnosticProps . join ( "\n" ) } \n}` ;
14471489}
14481490
1491+ function parseNumberOfErrorsInCurrentFile ( args : readonly ts . Expression [ ] ) : [ VerifyNumberOfErrorsInCurrentFileCmd ] | undefined {
1492+ let arg0 ;
1493+ if ( args . length !== 1 || ! ( arg0 = getNumericLiteral ( args [ 0 ] ) ) ) {
1494+ console . error ( `Expected a single numeric literal argument in verify.numberOfErrorsInCurrentFile, got ${ args . map ( arg => arg . getText ( ) ) . join ( ", " ) } ` ) ;
1495+ return undefined ;
1496+ }
1497+ return [ {
1498+ kind : "verifyNumberOfErrorsInCurrentFile" ,
1499+ expectedCount : parseInt ( arg0 . text , 10 ) ,
1500+ } ] ;
1501+ }
1502+
1503+ function parseErrorExistsAtRange ( args : readonly ts . Expression [ ] ) : [ VerifyErrorExistsAtRangeCmd ] | undefined {
1504+ if ( args . length < 2 || args . length > 3 ) {
1505+ console . error ( `Expected 2 or 3 arguments in verify.errorExistsAtRange, got ${ args . length } ` ) ;
1506+ return undefined ;
1507+ }
1508+
1509+ // First arg is a range
1510+ const rangeArg = parseBaselineMarkerOrRangeArg ( args [ 0 ] ) ;
1511+ if ( ! rangeArg ) {
1512+ console . error ( `Expected range argument in verify.errorExistsAtRange, got ${ args [ 0 ] . getText ( ) } ` ) ;
1513+ return undefined ;
1514+ }
1515+
1516+ // Second arg is error code
1517+ let codeArg ;
1518+ if ( ! ( codeArg = getNumericLiteral ( args [ 1 ] ) ) ) {
1519+ console . error ( `Expected numeric literal for code in verify.errorExistsAtRange, got ${ args [ 1 ] . getText ( ) } ` ) ;
1520+ return undefined ;
1521+ }
1522+
1523+ // Third arg is optional message
1524+ let message = "" ;
1525+ if ( args [ 2 ] ) {
1526+ const messageArg = getStringLiteralLike ( args [ 2 ] ) ;
1527+ if ( ! messageArg ) {
1528+ console . error ( `Expected string literal for message in verify.errorExistsAtRange, got ${ args [ 2 ] . getText ( ) } ` ) ;
1529+ return undefined ;
1530+ }
1531+ message = messageArg . text ;
1532+ }
1533+
1534+ return [ {
1535+ kind : "verifyErrorExistsAtRange" ,
1536+ range : rangeArg ,
1537+ code : parseInt ( codeArg . text , 10 ) ,
1538+ message : message ,
1539+ } ] ;
1540+ }
1541+
1542+ function parseCurrentLineContentIs ( args : readonly ts . Expression [ ] ) : [ VerifyCurrentLineContentIsCmd ] | undefined {
1543+ let arg0 ;
1544+ if ( args . length !== 1 || ! ( arg0 = getStringLiteralLike ( args [ 0 ] ) ) ) {
1545+ console . error ( `Expected a single string literal argument in verify.currentLineContentIs, got ${ args . map ( arg => arg . getText ( ) ) . join ( ", " ) } ` ) ;
1546+ return undefined ;
1547+ }
1548+ return [ {
1549+ kind : "verifyCurrentLineContentIs" ,
1550+ text : arg0 . text ,
1551+ } ] ;
1552+ }
1553+
1554+ function parseCurrentFileContentIs ( args : readonly ts . Expression [ ] ) : [ VerifyCurrentFileContentIsCmd ] | undefined {
1555+ let arg0 ;
1556+ if ( args . length !== 1 || ! ( arg0 = getStringLiteralLike ( args [ 0 ] ) ) ) {
1557+ console . error ( `Expected a single string literal argument in verify.currentFileContentIs, got ${ args . map ( arg => arg . getText ( ) ) . join ( ", " ) } ` ) ;
1558+ return undefined ;
1559+ }
1560+ return [ {
1561+ kind : "verifyCurrentFileContentIs" ,
1562+ text : arg0 . text ,
1563+ } ] ;
1564+ }
1565+
1566+ function parseErrorExistsBetweenMarkers ( args : readonly ts . Expression [ ] ) : [ VerifyErrorExistsBetweenMarkersCmd ] | undefined {
1567+ if ( args . length !== 2 ) {
1568+ console . error ( `Expected 2 arguments in verify.errorExistsBetweenMarkers, got ${ args . length } ` ) ;
1569+ return undefined ;
1570+ }
1571+ let startMarker , endMarker ;
1572+ if ( ! ( startMarker = getStringLiteralLike ( args [ 0 ] ) ) || ! ( endMarker = getStringLiteralLike ( args [ 1 ] ) ) ) {
1573+ console . error ( `Expected string literal arguments in verify.errorExistsBetweenMarkers, got ${ args . map ( arg => arg . getText ( ) ) . join ( ", " ) } ` ) ;
1574+ return undefined ;
1575+ }
1576+ return [ {
1577+ kind : "verifyErrorExistsBetweenMarkers" ,
1578+ startMarker : startMarker . text ,
1579+ endMarker : endMarker . text ,
1580+ } ] ;
1581+ }
1582+
1583+ function parseErrorExistsAfterMarker ( args : readonly ts . Expression [ ] ) : [ VerifyErrorExistsAfterMarkerCmd ] | undefined {
1584+ let markerName = "" ;
1585+ if ( args . length > 0 ) {
1586+ const arg0 = getStringLiteralLike ( args [ 0 ] ) ;
1587+ if ( ! arg0 ) {
1588+ console . error ( `Expected string literal argument in verify.errorExistsAfterMarker, got ${ args [ 0 ] . getText ( ) } ` ) ;
1589+ return undefined ;
1590+ }
1591+ markerName = arg0 . text ;
1592+ }
1593+ return [ {
1594+ kind : "verifyErrorExistsAfterMarker" ,
1595+ markerName : markerName ,
1596+ } ] ;
1597+ }
1598+
1599+ function parseErrorExistsBeforeMarker ( args : readonly ts . Expression [ ] ) : [ VerifyErrorExistsBeforeMarkerCmd ] | undefined {
1600+ let markerName = "" ;
1601+ if ( args . length > 0 ) {
1602+ const arg0 = getStringLiteralLike ( args [ 0 ] ) ;
1603+ if ( ! arg0 ) {
1604+ console . error ( `Expected string literal argument in verify.errorExistsBeforeMarker, got ${ args [ 0 ] . getText ( ) } ` ) ;
1605+ return undefined ;
1606+ }
1607+ markerName = arg0 . text ;
1608+ }
1609+ return [ {
1610+ kind : "verifyErrorExistsBeforeMarker" ,
1611+ markerName : markerName ,
1612+ } ] ;
1613+ }
1614+
14491615function stringToTristate ( s : string ) : string {
14501616 switch ( s ) {
14511617 case "true" :
@@ -2689,6 +2855,48 @@ interface VerifyNavTreeCmd {
26892855 kind : "verifyNavigationTree" ;
26902856}
26912857
2858+ interface VerifyNumberOfErrorsInCurrentFileCmd {
2859+ kind : "verifyNumberOfErrorsInCurrentFile" ;
2860+ expectedCount : number ;
2861+ }
2862+
2863+ interface VerifyNoErrorsCmd {
2864+ kind : "verifyNoErrors" ;
2865+ }
2866+
2867+ interface VerifyErrorExistsAtRangeCmd {
2868+ kind : "verifyErrorExistsAtRange" ;
2869+ range : string ;
2870+ code : number ;
2871+ message : string ;
2872+ }
2873+
2874+ interface VerifyCurrentLineContentIsCmd {
2875+ kind : "verifyCurrentLineContentIs" ;
2876+ text : string ;
2877+ }
2878+
2879+ interface VerifyCurrentFileContentIsCmd {
2880+ kind : "verifyCurrentFileContentIs" ;
2881+ text : string ;
2882+ }
2883+
2884+ interface VerifyErrorExistsBetweenMarkersCmd {
2885+ kind : "verifyErrorExistsBetweenMarkers" ;
2886+ startMarker : string ;
2887+ endMarker : string ;
2888+ }
2889+
2890+ interface VerifyErrorExistsAfterMarkerCmd {
2891+ kind : "verifyErrorExistsAfterMarker" ;
2892+ markerName : string ;
2893+ }
2894+
2895+ interface VerifyErrorExistsBeforeMarkerCmd {
2896+ kind : "verifyErrorExistsBeforeMarker" ;
2897+ markerName : string ;
2898+ }
2899+
26922900type Cmd =
26932901 | VerifyCompletionsCmd
26942902 | VerifyApplyCodeActionFromCompletionCmd
@@ -2715,7 +2923,15 @@ type Cmd =
27152923 | VerifyImportFixModuleSpecifiersCmd
27162924 | VerifyDiagnosticsCmd
27172925 | VerifyBaselineDiagnosticsCmd
2718- | VerifyOutliningSpansCmd ;
2926+ | VerifyOutliningSpansCmd
2927+ | VerifyNumberOfErrorsInCurrentFileCmd
2928+ | VerifyNoErrorsCmd
2929+ | VerifyErrorExistsAtRangeCmd
2930+ | VerifyCurrentLineContentIsCmd
2931+ | VerifyCurrentFileContentIsCmd
2932+ | VerifyErrorExistsBetweenMarkersCmd
2933+ | VerifyErrorExistsAfterMarkerCmd
2934+ | VerifyErrorExistsBeforeMarkerCmd ;
27192935
27202936function generateVerifyOutliningSpans ( { foldingRangeKind } : VerifyOutliningSpansCmd ) : string {
27212937 if ( foldingRangeKind ) {
@@ -3029,6 +3245,22 @@ function generateCmd(cmd: Cmd): string {
30293245 return generateVerifyOutliningSpans ( cmd ) ;
30303246 case "verifyNavigationTree" :
30313247 return `f.VerifyBaselineDocumentSymbol(t)` ;
3248+ case "verifyNumberOfErrorsInCurrentFile" :
3249+ return `f.VerifyNumberOfErrorsInCurrentFile(t, ${ cmd . expectedCount } )` ;
3250+ case "verifyNoErrors" :
3251+ return `f.VerifyNoErrors(t)` ;
3252+ case "verifyErrorExistsAtRange" :
3253+ return `f.VerifyErrorExistsAtRange(t, ${ cmd . range } , ${ cmd . code } , ${ getGoStringLiteral ( cmd . message ) } )` ;
3254+ case "verifyCurrentLineContentIs" :
3255+ return `f.VerifyCurrentLineContentIs(t, ${ getGoStringLiteral ( cmd . text ) } )` ;
3256+ case "verifyCurrentFileContentIs" :
3257+ return `f.VerifyCurrentFileContentIs(t, ${ getGoStringLiteral ( cmd . text ) } )` ;
3258+ case "verifyErrorExistsBetweenMarkers" :
3259+ return `f.VerifyErrorExistsBetweenMarkers(t, ${ getGoStringLiteral ( cmd . startMarker ) } , ${ getGoStringLiteral ( cmd . endMarker ) } )` ;
3260+ case "verifyErrorExistsAfterMarker" :
3261+ return `f.VerifyErrorExistsAfterMarker(t, ${ getGoStringLiteral ( cmd . markerName ) } )` ;
3262+ case "verifyErrorExistsBeforeMarker" :
3263+ return `f.VerifyErrorExistsBeforeMarker(t, ${ getGoStringLiteral ( cmd . markerName ) } )` ;
30323264 default :
30333265 let neverCommand : never = cmd ;
30343266 throw new Error ( `Unknown command kind: ${ neverCommand as Cmd [ "kind" ] } ` ) ;
@@ -3047,16 +3279,17 @@ function generateGoTest(failingTests: Set<string>, test: GoTest, isServer: boole
30473279 const commands = test . commands . map ( cmd => generateCmd ( cmd ) ) . join ( "\n" ) ;
30483280 const imports = [ `"github.com/microsoft/typescript-go/internal/fourslash"` ] ;
30493281 // Only include these imports if the commands use them to avoid unused import errors.
3050- if ( commands . includes ( "core." ) ) {
3282+ // Use regex with word boundary to avoid false positives like "underscore." matching "core."
3283+ if ( / \b c o r e \. / . test ( commands ) ) {
30513284 imports . unshift ( `"github.com/microsoft/typescript-go/internal/core"` ) ;
30523285 }
3053- if ( commands . includes ( "ls." ) ) {
3286+ if ( / \b l s \. / . test ( commands ) ) {
30543287 imports . push ( `"github.com/microsoft/typescript-go/internal/ls"` ) ;
30553288 }
3056- if ( commands . includes ( "lsutil." ) ) {
3289+ if ( / \b l s u t i l \. / . test ( commands ) ) {
30573290 imports . push ( `"github.com/microsoft/typescript-go/internal/ls/lsutil"` ) ;
30583291 }
3059- if ( commands . includes ( "lsproto." ) ) {
3292+ if ( / \b l s p r o t o \. / . test ( commands ) ) {
30603293 imports . push ( `"github.com/microsoft/typescript-go/internal/lsp/lsproto"` ) ;
30613294 }
30623295 if ( usesFourslashUtil ( commands ) ) {
@@ -3129,6 +3362,17 @@ function getArrayLiteralExpression(node: ts.Node): ts.ArrayLiteralExpression | u
31293362 return getNodeOfKind ( node , ts . isArrayLiteralExpression ) ;
31303363}
31313364
3365+ // Parses expressions like 'string'.length or "string".length and returns the length value
3366+ function getStringLengthExpression ( node : ts . Node ) : number | undefined {
3367+ if ( ts . isPropertyAccessExpression ( node ) && node . name . text === "length" ) {
3368+ const stringLiteral = getStringLiteralLike ( node . expression ) ;
3369+ if ( stringLiteral ) {
3370+ return stringLiteral . text . length ;
3371+ }
3372+ }
3373+ return undefined ;
3374+ }
3375+
31323376function getInitializer ( name : ts . Identifier ) : ts . Expression | undefined {
31333377 const file = name . getSourceFile ( ) ;
31343378 const varStmts = file . statements . filter ( ts . isVariableStatement ) ;
0 commit comments