@@ -34,6 +34,7 @@ import {
3434import {
3535 HEADER_OFFSET_EXTENDED_DATA ,
3636 HEADER_OFFSET_NODES ,
37+ HEADER_OFFSET_SOURCE_FILE_ID ,
3738 HEADER_OFFSET_STRING_TABLE ,
3839 HEADER_OFFSET_STRING_TABLE_OFFSETS ,
3940 HEADER_OFFSET_STRUCTURED_DATA ,
@@ -106,6 +107,7 @@ export class RemoteSourceFile extends RemoteNode implements SourceFileInfo {
106107 private _cachedSupplementalSourceFileNames : readonly RootedFilePath [ ] | undefined ;
107108 private _cachedDiagnosticDirectives : readonly MappedDiagnosticDirective [ ] | undefined ;
108109 private _diagnosticDirectivesRead = false ;
110+ private _handleFileKey : string | undefined ;
109111
110112 constructor (
111113 data : Uint8Array ,
@@ -147,6 +149,22 @@ export class RemoteSourceFile extends RemoteNode implements SourceFileInfo {
147149 return readSourceFileNodeId ( this . view ) ;
148150 }
149151
152+ /**
153+ * The file component of node handles sent to the server: `#` followed by the
154+ * server's identity for this exact source file.
155+ * @internal
156+ */
157+ get handleFileKey ( ) : string {
158+ if ( this . _handleFileKey === undefined ) {
159+ const nodeId = this . view . getBigUint64 ( HEADER_OFFSET_SOURCE_FILE_ID , true ) ;
160+ if ( nodeId === 0n ) {
161+ throw new Error ( "Node has no server source-file identity" ) ;
162+ }
163+ this . _handleFileKey = `#${ nodeId } ` ;
164+ }
165+ return this . _handleFileKey ;
166+ }
167+
150168 readFileReferences ( structuredDataOffset : number ) : readonly FileReference [ ] {
151169 if ( structuredDataOffset === NO_STRUCTURED_DATA ) {
152170 return [ ] ;
@@ -477,12 +495,13 @@ export function findDescendant(root: Node, pos: number, end: number, kind: Synta
477495export interface ParsedNodeHandle {
478496 index : number ;
479497 kind : SyntaxKind ;
498+ sourceFileId : string ;
480499 path : PathKey ;
481500}
482501
483502/**
484503 * Parse a compiler-produced node handle into its components.
485- * Handle format: "index.kind.path" where path may contain dots.
504+ * Handle format: "index.kind.#sourceFileId. path" where path may contain dots.
486505 */
487506export function parseNodeHandleFromCompiler ( handle : string ) : ParsedNodeHandle {
488507 const firstDot = handle . indexOf ( "." ) ;
@@ -496,7 +515,13 @@ export function parseNodeHandleFromCompiler(handle: string): ParsedNodeHandle {
496515
497516 const indexText = handle . slice ( 0 , firstDot ) ;
498517 const kindText = handle . slice ( firstDot + 1 , secondDot ) ;
499- const path = handle . slice ( secondDot + 1 ) ;
518+ const thirdDot = handle . indexOf ( "." , secondDot + 1 ) ;
519+ if ( thirdDot === - 1 ) {
520+ throw new Error ( `Invalid node handle: ${ handle } ` ) ;
521+ }
522+ const fileKey = handle . slice ( secondDot + 1 , thirdDot ) ;
523+ const sourceFileId = fileKey . slice ( 1 ) ;
524+ const path = handle . slice ( thirdDot + 1 ) ;
500525 const index = Number ( indexText ) ;
501526 const kind = Number ( kindText ) ;
502527 const key = tryPathKeyFromCanonical ( path ) ;
@@ -507,13 +532,16 @@ export function parseNodeHandleFromCompiler(handle: string): ParsedNodeHandle {
507532 ! Number . isSafeInteger ( kind ) ||
508533 kind < 0 ||
509534 String ( kind ) !== kindText ||
535+ ! / ^ # [ 1 - 9 ] [ 0 - 9 ] * $ / . test ( fileKey ) ||
536+ BigInt ( sourceFileId ) > 0xFFFFFFFFFFFFFFFFn ||
510537 key === undefined
511538 ) {
512539 throw new Error ( `Invalid node handle: ${ handle } ` ) ;
513540 }
514541 return {
515542 index,
516543 kind : kind as SyntaxKind ,
544+ sourceFileId,
517545 path : key ,
518546 } ;
519547}
@@ -536,5 +564,24 @@ export function getNodeId(node: Node): string {
536564 if ( ! ( node instanceof RemoteNode ) ) {
537565 throw new Error ( "getNodeId requires a RemoteNode" ) ;
538566 }
539- return node . id ;
567+ const sourceFile = node [ "_sourceFile" ] ;
568+ if ( sourceFile instanceof RemoteSourceFile ) {
569+ return `${ node [ "index" ] } .${ node . kind } .${ sourceFile . handleFileKey } .${ sourceFile . path } ` ;
570+ }
571+ throw new Error ( "Node has no server source-file identity" ) ;
572+ }
573+
574+ /**
575+ * Get the compact handle identifying a node in its exact server source-file AST.
576+ * @internal
577+ */
578+ export function getNodeRequestHandle ( node : Node ) : string {
579+ if ( ! ( node instanceof RemoteNode ) ) {
580+ throw new Error ( "getNodeRequestHandle requires a RemoteNode" ) ;
581+ }
582+ const sourceFile = node [ "_sourceFile" ] ;
583+ if ( sourceFile instanceof RemoteSourceFile ) {
584+ return `${ node [ "index" ] } .${ sourceFile . handleFileKey } ` ;
585+ }
586+ throw new Error ( "Node has no server source-file identity" ) ;
540587}
0 commit comments