@@ -22,9 +22,9 @@ import { sendClientSideSyncTelemetryEvent } from "../extension";
2222interface WSFolderIndex {
2323 /** The `FileSystemWatcher` for this workspace folder */
2424 watcher : vscode . FileSystemWatcher ;
25- /** Map of document names (i.e., server-side names) to VSCode URIs */
25+ /** Map of document names (i.e., server-side names) to VS Code URIs */
2626 documents : Map < string , vscode . Uri [ ] > ;
27- /** Map of VSCode URIs to document names */
27+ /** Map of VS Code URIs to document names */
2828 uris : Map < string , string > ;
2929}
3030
@@ -426,3 +426,56 @@ export function allDocumentsInWorkspace(wsFolder: vscode.WorkspaceFolder): strin
426426export function getDocumentForUri ( uri : vscode . Uri ) : string {
427427 return wsFolderIndex . get ( vscode . workspace . getWorkspaceFolder ( uri ) ?. uri . toString ( ) ) ?. uris . get ( uri . toString ( ) ) ;
428428}
429+
430+ /**
431+ * Use the known mappings between files and document names to infer
432+ * a name for a document contained in file `uri`. For example,
433+ * `uri` with path `/wsFolder/src/User/Test.cls` may return
434+ * `User.Test.cls`. Returns `undefined` if an inference couldn't
435+ * be made. Only attempts inferencing for classes or routines.
436+ * Does not attempt to read `uri`. This is useful for
437+ * generating stub content for a file that was just created.
438+ */
439+ export function inferDocName ( uri : vscode . Uri ) : string | undefined {
440+ const exts = [ ".cls" , ".mac" , ".int" , ".inc" ] ;
441+ const fileExt = uri . path . slice ( - 4 ) . toLowerCase ( ) ;
442+ if ( ! exts . includes ( fileExt ) ) return ;
443+ const wsFolder = vscode . workspace . getWorkspaceFolder ( uri ) ;
444+ if ( ! wsFolder ) return ;
445+ const index = wsFolderIndex . get ( wsFolder . uri . toString ( ) ) ;
446+ if ( ! index ) return ;
447+ // Convert the URI into an array of path segments
448+ const uriParts = uri . path . split ( "/" ) ;
449+ uriParts . pop ( ) ;
450+ // Stop looping once we reach the workspace folder root
451+ const loopEnd = wsFolder . uri . path . split ( "/" ) . length - ( wsFolder . uri . path . endsWith ( "/" ) ? 1 : 0 ) ;
452+ // Look for known documents in the same directory tree as the target URI.
453+ // Once we find a match, look at the relationship between the URI and name
454+ // and apply that same relationship to the target URI. Start at the containing
455+ // directory of the target and then work up the tree until we have a match.
456+ let result : string ;
457+ for ( let i = uriParts . length ; i >= loopEnd ; i -- ) {
458+ const uriDir = `${ uriParts . slice ( 0 , i ) . join ( "/" ) } /` ;
459+ for ( const [ docUriStr , docName ] of index . uris ) {
460+ const docUri = vscode . Uri . parse ( docUriStr ) ;
461+ if ( exts . includes ( docName . slice ( - 4 ) ) && docUri . path . startsWith ( uriDir ) ) {
462+ // This class or routine is in the same directory tree as the target
463+ // so attempt to determine how its name relates to its URI
464+ const docNamePath = `/${ docName . slice ( 0 , - 4 ) . replaceAll ( "." , "/" ) } ${ docName . slice ( - 4 ) } ` ;
465+ // Make sure the file extension is lowercased in the path before matching
466+ const startOfDocName = ( docUri . path . slice ( 0 , - 3 ) + docUri . path . slice ( - 3 ) . toLowerCase ( ) ) . lastIndexOf (
467+ docNamePath
468+ ) ;
469+ if ( startOfDocName > - 1 ) {
470+ // We've identified the leading path segments that don't contribute to the document name,
471+ // so remove them from the target URI before generating the document name. Need the + 1 to
472+ // remove the leading slash which was part of the match string.
473+ result = `${ uri . path . slice ( startOfDocName + 1 , - 4 ) . replaceAll ( "/" , "." ) } ${ fileExt } ` ;
474+ break ;
475+ }
476+ }
477+ }
478+ if ( result ) break ;
479+ }
480+ return result ;
481+ }
0 commit comments