@@ -415,22 +415,33 @@ namespace ts.projectSystem {
415415 checkArray ( "Open files" , arrayFrom ( projectService . openFiles . keys ( ) , path => projectService . getScriptInfoForPath ( path as Path ) ! . fileName ) , expectedFiles . map ( file => file . path ) ) ;
416416 }
417417
418- function textSpanFromSubstring ( str : string , substring : string ) : TextSpan {
418+ function protocolLocationFromSubstring ( str : string , substring : string ) {
419419 const start = str . indexOf ( substring ) ;
420420 Debug . assert ( start !== - 1 ) ;
421- return createTextSpan ( start , substring . length ) ;
421+ return protocolToLocation ( str ) ( start ) ;
422+ }
423+ function protocolToLocation ( text : string ) : ( pos : number ) => protocol . Location {
424+ const lineStarts = computeLineStarts ( text ) ;
425+ return pos => {
426+ const x = computeLineAndCharacterOfPosition ( lineStarts , pos ) ;
427+ return { line : x . line + 1 , offset : x . character + 1 } ;
428+ } ;
422429 }
423-
424430 function protocolTextSpanFromSubstring ( str : string , substring : string ) : protocol . TextSpan {
431+ const span = textSpanFromSubstring ( str , substring ) ;
432+ const toLocation = protocolToLocation ( str ) ;
433+ return { start : toLocation ( span . start ) , end : toLocation ( span . start + span . length ) } ;
434+ }
435+ function textSpanFromSubstring ( str : string , substring : string ) : TextSpan {
425436 const start = str . indexOf ( substring ) ;
426437 Debug . assert ( start !== - 1 ) ;
427- const lineStarts = computeLineStarts ( str ) ;
428- const toLocation = ( pos : number ) => lineAndCharacterToLocation ( computeLineAndCharacterOfPosition ( lineStarts , pos ) ) ;
429- return { start : toLocation ( start ) , end : toLocation ( start + substring . length ) } ;
438+ return createTextSpan ( start , substring . length ) ;
430439 }
431-
432- function lineAndCharacterToLocation ( lc : LineAndCharacter ) : protocol . Location {
433- return { line : lc . line + 1 , offset : lc . character + 1 } ;
440+ function protocolFileLocationFromSubstring ( file : File , substring : string ) : protocol . FileLocationRequestArgs {
441+ return { file : file . path , ...protocolLocationFromSubstring ( file . content , substring ) } ;
442+ }
443+ function protocolFileSpanFromSubstring ( file : File , substring : string ) : protocol . FileSpan {
444+ return { file : file . path , ...protocolTextSpanFromSubstring ( file . content , substring ) } ;
434445 }
435446
436447 /**
@@ -485,13 +496,19 @@ namespace ts.projectSystem {
485496 } ;
486497 }
487498
488- export function openFilesForSession ( files : ReadonlyArray < File > , session : server . Session ) {
499+ export function openFilesForSession ( files : ReadonlyArray < File > , session : server . Session ) : void {
489500 for ( const file of files ) {
490501 const request = makeSessionRequest < protocol . OpenRequestArgs > ( CommandNames . Open , { file : file . path } ) ;
491502 session . executeCommand ( request ) ;
492503 }
493504 }
494505
506+ export function closeFilesForSession ( files : ReadonlyArray < File > , session : server . Session ) : void {
507+ for ( const file of files ) {
508+ session . executeCommand ( makeSessionRequest < protocol . FileRequestArgs > ( CommandNames . Close , { file : file . path } ) ) ;
509+ }
510+ }
511+
495512 interface ErrorInformation {
496513 diagnosticMessage : DiagnosticMessage ;
497514 errorTextArguments ?: string [ ] ;
@@ -8830,4 +8847,108 @@ export const x = 10;`
88308847 assert . equal ( moduleInfo . cacheSourceFile . sourceFile . text , updatedModuleContent ) ;
88318848 } ) ;
88328849 } ) ;
8850+
8851+ function makeSampleProjects ( ) {
8852+ const aTs : File = {
8853+ path : "/a/a.ts" ,
8854+ content : "export function fnA() {}" ,
8855+ } ;
8856+ const aTsconfig : File = {
8857+ path : "/a/tsconfig.json" ,
8858+ content : `{
8859+ "compilerOptions": {
8860+ "outDir": "bin",
8861+ "declaration": true,
8862+ "declarationMap": true,
8863+ "composite": true,
8864+ }
8865+ }` ,
8866+ } ;
8867+
8868+ const bTs : File = {
8869+ path : "/b/b.ts" ,
8870+ content : 'import { fnA } from "../a/a";\nexport function fnB() { fnA(); }' ,
8871+ } ;
8872+ const bTsconfig : File = {
8873+ path : "/b/tsconfig.json" ,
8874+ content : `{
8875+ "compilerOptions": {
8876+ "outDir": "bin",
8877+ },
8878+ "references": [
8879+ { "path": "../a" }
8880+ ]
8881+ }` ,
8882+ } ;
8883+
8884+ const host = createServerHost ( [ aTs , aTsconfig , bTs , bTsconfig ] ) ;
8885+ const session = createSession ( host ) ;
8886+
8887+ writeDeclarationFiles ( aTs , host , session , [
8888+ { name : "/a/bin/a.d.ts.map" , text : '{"version":3,"file":"a.d.ts","sourceRoot":"","sources":["../a.ts"],"names":[],"mappings":"AAAA,wBAAgB,GAAG,SAAK"}' } ,
8889+ // Need to mangle the sourceMappingURL part or it breaks the build
8890+ { name : "/a/bin/a.d.ts" , text : `export declare function fnA(): void;\n//# source${ "" } MappingURL=a.d.ts.map` } ,
8891+ ] ) ;
8892+
8893+ return { session, aTs, bTs } ;
8894+ }
8895+
8896+ describe ( "tsserverProjectSystem project references" , ( ) => {
8897+ it ( "goToDefinition" , ( ) => {
8898+ const { session, aTs, bTs } = makeSampleProjects ( ) ;
8899+
8900+ openFilesForSession ( [ bTs ] , session ) ;
8901+
8902+ const definitionRequest = makeSessionRequest < protocol . FileLocationRequestArgs > ( CommandNames . Definition , protocolFileLocationFromSubstring ( bTs , "fnA()" ) ) ;
8903+ const definitionResponse = session . executeCommand ( definitionRequest ) . response as protocol . DefinitionResponse [ "body" ] ;
8904+
8905+ assert . deepEqual ( definitionResponse , [ protocolFileSpanFromSubstring ( aTs , "fnA" ) ] ) ;
8906+ } ) ;
8907+
8908+ it ( "navigateTo" , ( ) => {
8909+ const { session, bTs } = makeSampleProjects ( ) ;
8910+
8911+ openFilesForSession ( [ bTs ] , session ) ;
8912+
8913+ const navtoRequest = makeSessionRequest < protocol . NavtoRequestArgs > ( CommandNames . Navto , { file : bTs . path , searchValue : "fn" } ) ;
8914+ const navtoResponse = session . executeCommand ( navtoRequest ) . response as protocol . NavtoResponse [ "body" ] ;
8915+
8916+ assert . deepEqual ( navtoResponse , [
8917+ // TODO: First result should be from a.ts, not a.d.ts
8918+ {
8919+ file : "/a/bin/a.d.ts" ,
8920+ start : { line : 1 , offset : 1 } ,
8921+ end : { line : 1 , offset : 37 } ,
8922+ name : "fnA" ,
8923+ matchKind : "prefix" ,
8924+ kind : ScriptElementKind . functionElement ,
8925+ kindModifiers : "export,declare" ,
8926+ } ,
8927+ {
8928+ ...protocolFileSpanFromSubstring ( bTs , "export function fnB() { fnA(); }" ) ,
8929+ name : "fnB" ,
8930+ matchKind : "prefix" ,
8931+ kind : ScriptElementKind . functionElement ,
8932+ kindModifiers : "export" ,
8933+ }
8934+ ] ) ;
8935+ } ) ;
8936+ } ) ;
8937+
8938+ function writeDeclarationFiles ( file : File , host : TestServerHost , session : TestSession , expectedFiles : ReadonlyArray < { readonly name : string , readonly text : string } > ) : void {
8939+ openFilesForSession ( [ file ] , session ) ;
8940+ const project = Debug . assertDefined ( session . getProjectService ( ) . getDefaultProjectForFile ( file . path as server . NormalizedPath , /*ensureProject*/ false ) ) ;
8941+ const program = project . getCurrentProgram ( ) ;
8942+ const output = getFileEmitOutput ( program , Debug . assertDefined ( program . getSourceFile ( file . path ) ) , /*emitOnlyDtsFiles*/ true ) ;
8943+ closeFilesForSession ( [ file ] , session ) ;
8944+
8945+ Debug . assert ( ! output . emitSkipped ) ;
8946+ assert . deepEqual ( output . outputFiles , expectedFiles . map ( e => ( { ...e , writeByteOrderMark : false } ) ) ) ;
8947+
8948+ for ( const { name, text } of output . outputFiles ) {
8949+ const directory : Folder = { path : getDirectoryPath ( name ) } ;
8950+ host . ensureFileOrFolder ( directory ) ;
8951+ host . writeFile ( name , text ) ;
8952+ }
8953+ }
88338954}
0 commit comments