'use strict' ; import cp = require ( 'child_process' ) ; import vscode = require ( 'vscode' ) ; import { toolExecutionEnvironment } from './goEnv' ; import { promptForMissingTool , promptForUpdatingTool } from './gopInstallTools' ; import { getBinPath , getFileArchive , getGoConfig , killProcess , makeMemoizedByteOffsetConverter } from './util' ; export interface GoOutlineRange { start : number ; end : number ; } export interface GoOutlineDeclaration { label : string ; type : string ; receiverType ? : string ; icon ? : string ; start : number ; end : number ; children ? : GoOutlineDeclaration [ ] ; signature ? : GoOutlineRange ; comment ? : GoOutlineRange ; } export enum GoOutlineImportsOptions { Include , Exclude , Only } export interface GoOutlineOptions { /**
     * Path of the file for which outline is needed
     */ fileName : string ; /**
     * Option to decide if the output includes, excludes or only includes imports
     * If the option is to only include imports, then the file will be parsed only till imports are collected
     */ importsOption : GoOutlineImportsOptions ; /**
     * Document to be parsed. If not provided, saved contents of the given fileName is used
     */ document ? : vscode . TextDocument ; } export async function documentSymbols ( options , token ) { const decls = await runGoOutline ( options , token ) ; return convertToCodeSymbols ( options . document , decls , options . importsOption !== GoOutlineImportsOptions . Exclude , makeMemoizedByteOffsetConverter ( Buffer . from ( options . document . getText ( ) ) ) ) ; } export function runGoOutline ( options , token ) { return new Promise < GoOutlineDeclaration [ ] > ( ( resolve , reject ) => { const gooutline = getBinPath ( 'go-outline' ) ; const gooutlineFlags = [ '-f' , options . fileName ] ; if ( options . importsOption === GoOutlineImportsOptions . Only ) { gooutlineFlags . push ( '-imports-only' ) ; } if ( options . document ) { gooutlineFlags . push ( '-modified' ) ; } let p ; if ( token ) { token . onCancellationRequested ( ( ) => killProcess ( p ) ) ; } p = cp . execFile ( gooutline , gooutlineFlags , { env : toolExecutionEnvironment ( ) } , ( err , stdout , stderr ) => { try { if ( err && ( < any > err ) . code === 'ENOENT' ) { promptForMissingTool ( 'go-outline' ) ; } if ( stderr && stderr . startsWith ( 'flag provided but not defined: ' ) ) { promptForUpdatingTool ( 'go-outline' ) ; if ( stderr . startsWith ( 'flag provided but not defined: -imports-only' ) ) { options . importsOption = GoOutlineImportsOptions . Include ; } if ( stderr . startsWith ( 'flag provided but not defined: -modified' ) ) { options . document = null ; } p = null ; return runGoOutline ( options , token ) . then ( ( results ) => { return resolve ( results ) ; } ) ; } if ( err ) { return resolve ( null ) ; } const result = stdout . toString ( ) ; const decls = < GoOutlineDeclaration [ ] > JSON . parse ( result ) ; return resolve ( decls ) ; } catch ( e ) { reject ( e ) ; } } ) ; if ( options . document && p . pid ) { p . stdin . end ( getFileArchive ( options . document ) ) ; } } ) ; } const goKindToCodeKind = { package : vscode . SymbolKind . Package , import : vscode . SymbolKind . Namespace , variable : vscode . SymbolKind . Variable , constant : vscode . SymbolKind . Constant , type : vscode . SymbolKind . TypeParameter , function : vscode . SymbolKind . Function , struct : vscode . SymbolKind . Struct , interface : vscode . SymbolKind . Interface } ; function convertToCodeSymbols ( document , decls , includeImports , byteOffsetToDocumentOffset ) { const symbols = [ ] ; ( decls || [ ] ) . forEach ( ( decl ) => { if ( ! includeImports && decl . type === 'import' ) { return ; } if ( decl . label === '_' && decl . type === 'variable' ) { return ; } const label = decl . receiverType ? `(${ decl . receiverType }).${ decl . label }` : decl . label ; const start = byteOffsetToDocumentOffset ( decl . start - 1 ) ; const end = byteOffsetToDocumentOffset ( decl . end - 1 ) ; const startPosition = document . positionAt ( start ) ; const endPosition = document . positionAt ( end ) ; const symbolRange = new vscode . Range ( startPosition , endPosition ) ; const selectionRange = startPosition . line === endPosition . line ? symbolRange : new vscode . Range ( startPosition , document . lineAt ( startPosition . line ) . range . end ) ; if ( decl . type === 'type' ) { const line = document . lineAt ( document . positionAt ( start ) ) ; const regexStruct = new RegExp ( `^\\s*type\\s+${ decl . label }\\s+struct\\b` ) ; const regexInterface = new RegExp ( `^\\s*type\\s+${ decl . label }\\s+interface\\b` ) ; decl . type = regexStruct . test ( line . text ) ? 'struct' : regexInterface . test ( line . text ) ? 'interface' : 'type' ; } const symbolInfo = new vscode . DocumentSymbol ( label , decl . type , goKindToCodeKind [ decl . type ] , symbolRange , selectionRange ) ; symbols . push ( symbolInfo ) ; if ( decl . children ) { symbolInfo . children = convertToCodeSymbols ( document , decl . children , includeImports , byteOffsetToDocumentOffset ) ; } } ) ; return symbols ; }