@@ -383,6 +383,7 @@ export async function registerCommands(enabled: boolean): Promise<void> {
383383 commandDisposables . forEach ( d => d . dispose ( ) ) ;
384384 commandDisposables . length = 0 ;
385385 commandDisposables . push ( vscode . commands . registerCommand ( 'C_Cpp.SwitchHeaderSource' , enabled ? onSwitchHeaderSource : onDisabledCommand ) ) ;
386+ commandDisposables . push ( vscode . commands . registerCommand ( 'C_Cpp.SelectTranslationUnit' , enabled ? onSelectTranslationUnit : onDisabledCommand ) ) ;
386387 commandDisposables . push ( vscode . commands . registerCommand ( 'C_Cpp.ResetDatabase' , enabled ? onResetDatabase : onDisabledCommand ) ) ;
387388 commandDisposables . push ( vscode . commands . registerCommand ( 'C_Cpp.SelectIntelliSenseConfiguration' , enabled ? selectIntelliSenseConfiguration : onDisabledCommand ) ) ;
388389 commandDisposables . push ( vscode . commands . registerCommand ( 'C_Cpp.InstallCompiler' , enabled ? installCompiler : onDisabledCommand ) ) ;
@@ -468,6 +469,107 @@ async function onRestartIntelliSenseForFile() {
468469 return clients . ActiveClient . restartIntelliSenseForFile ( activeEditor . document ) ;
469470}
470471
472+ function getEditorPath ( fileName : string ) : string {
473+ let bestRealRoot : string | undefined ;
474+ let bestRootPath : string | undefined ;
475+ clients . forEach ( client => {
476+ if ( ! client . RootRealPath ) {
477+ return ;
478+ }
479+ const relativePath : string = path . relative ( client . RootRealPath , fileName ) ;
480+ const isUnderRoot : boolean = relativePath === "" ||
481+ ( relativePath !== ".." && ! relativePath . startsWith ( `..${ path . sep } ` ) && ! path . isAbsolute ( relativePath ) ) ;
482+ if ( isUnderRoot && ( ! bestRealRoot || client . RootRealPath . length > bestRealRoot . length ) ) {
483+ bestRealRoot = client . RootRealPath ;
484+ bestRootPath = client . RootPath ;
485+ }
486+ } ) ;
487+ return bestRealRoot && bestRootPath ? path . join ( bestRootPath , path . relative ( bestRealRoot , fileName ) ) : fileName ;
488+ }
489+
490+ interface TranslationUnitQuickPickItem extends vscode . QuickPickItem {
491+ translationUnit : string ;
492+ }
493+
494+ async function onSelectTranslationUnit ( ) : Promise < void > {
495+ const activeEditor : vscode . TextEditor | undefined = vscode . window . activeTextEditor ;
496+ if ( ! activeEditor || ! util . isCpp ( activeEditor . document ) ) {
497+ return ;
498+ }
499+
500+ const client : Client = clients . ActiveClient ;
501+ const result = await ( async ( ) => {
502+ const tokenSource : vscode . CancellationTokenSource = new vscode . CancellationTokenSource ( ) ;
503+ try {
504+ const candidatesPromise = client . getTranslationUnitSourceCandidates ( activeEditor . document . uri , tokenSource . token ) ;
505+ const showProgress : boolean = await new Promise < boolean > ( ( resolve , reject ) => {
506+ const timer : NodeJS . Timeout = global . setTimeout ( ( ) => resolve ( true ) , 2000 ) ;
507+ void candidatesPromise . then ( ( ) => {
508+ clearTimeout ( timer ) ;
509+ resolve ( false ) ;
510+ } , ( e ) => {
511+ clearTimeout ( timer ) ;
512+ reject ( e ) ;
513+ } ) ;
514+ } ) ;
515+
516+ if ( ! showProgress ) {
517+ return await candidatesPromise ;
518+ }
519+
520+ return await vscode . window . withProgress ( {
521+ location : vscode . ProgressLocation . Notification ,
522+ title : localize ( 'find.translation.units' , 'Finding Translation Units...' ) ,
523+ cancellable : true
524+ } , async ( _progress , token ) => {
525+ const cancellationListener : vscode . Disposable = token . onCancellationRequested ( ( ) => tokenSource . cancel ( ) ) ;
526+ try {
527+ return await candidatesPromise ;
528+ } finally {
529+ cancellationListener . dispose ( ) ;
530+ }
531+ } ) ;
532+ } catch ( e ) {
533+ if ( e instanceof vscode . CancellationError ) {
534+ return undefined ;
535+ }
536+ throw e ;
537+ } finally {
538+ tokenSource . dispose ( ) ;
539+ }
540+ } ) ( ) ;
541+ if ( result === undefined ) {
542+ return ;
543+ }
544+ if ( result . candidates . length === 0 ) {
545+ void vscode . window . showInformationMessage ( localize ( 'no.translation.units.found' , 'No translation units were found for the active file.' ) ) ;
546+ return ;
547+ }
548+ const currentTranslationUnit : string = getEditorPath ( result . currentTranslationUnit ) ;
549+ const items : TranslationUnitQuickPickItem [ ] = result . candidates . map ( candidate => {
550+ const editorPath : string = getEditorPath ( candidate ) ;
551+ const relativePath : string = vscode . workspace . asRelativePath ( vscode . Uri . file ( editorPath ) ) ;
552+ const directory : string = path . dirname ( relativePath ) ;
553+ const isCurrent : boolean = editorPath === currentTranslationUnit ;
554+ return {
555+ label : `${ isCurrent ? "$(check) " : "" } ${ path . basename ( relativePath ) } ` ,
556+ description : isCurrent ? localize ( 'current.translation.unit' , 'Current translation unit' ) :
557+ directory === "." ? undefined : directory ,
558+ detail : editorPath ,
559+ translationUnit : editorPath
560+ } ;
561+ } ) ;
562+ const selection : TranslationUnitQuickPickItem | undefined = await vscode . window . showQuickPick ( items , {
563+ title : localize ( 'select.translation.unit' , 'Select a Translation Unit' ) ,
564+ placeHolder : localize ( 'select.translation.unit.placeholder' , 'Select a source file to use as the translation unit' ) ,
565+ matchOnDescription : true ,
566+ matchOnDetail : true
567+ } ) ;
568+ if ( selection !== undefined ) {
569+ await client . selectTranslationUnit ( activeEditor . document . uri , selection . translationUnit ) ;
570+ }
571+ }
572+
471573async function onSwitchHeaderSource ( ) : Promise < void > {
472574 const activeEditor : vscode . TextEditor | undefined = vscode . window . activeTextEditor ;
473575 if ( ! activeEditor || ! util . isCpp ( activeEditor . document ) ) {
@@ -487,16 +589,7 @@ async function onSwitchHeaderSource(): Promise<void> {
487589 if ( ! targetFileName ) {
488590 return ;
489591 }
490- // If the targetFileName has a path that is a symlink target of a workspace folder,
491- // then replace the RootRealPath with the RootPath (the symlink path).
492- let targetFileNameReplaced : boolean = false ;
493- clients . forEach ( client => {
494- if ( ! targetFileNameReplaced && client . RootRealPath && client . RootPath !== client . RootRealPath
495- && targetFileName . startsWith ( client . RootRealPath ) ) {
496- targetFileName = client . RootPath + targetFileName . substring ( client . RootRealPath . length ) ;
497- targetFileNameReplaced = true ;
498- }
499- } ) ;
592+ targetFileName = getEditorPath ( targetFileName ) ;
500593 const document : vscode . TextDocument = await vscode . workspace . openTextDocument ( targetFileName ) ;
501594 await vscode . window . showTextDocument ( document ) . then ( undefined , logAndReturn . undefined ) ;
502595 } catch ( e ) {
0 commit comments