import * as vscode from 'vscode' ; import { FuncMap } from './helm.funcmap' ; import * as logger from './logger' ; import * as YAML from 'yamljs' ; import * as exec from './helm.exec' ; import * as path from 'path' ; import * as _ from 'lodash' ; import { existsSync } from 'fs' ; export class HelmTemplateCompletionProvider implements vscode . CompletionItemProvider { private valuesMatcher = new RegExp ( '\\s+\\.Values\\.([a-zA-Z0-9\\._-]+)?$' ) ; private funcmap = new FuncMap ( ) ; private valuesCache : any ; private valuesWatcher : vscode . FileSystemWatcher ; public constructor ( ) { this . initializeValues ( { warnIfNoCharts : false } ) ; } public initializeValues ( options ) { const ed = vscode . window . activeTextEditor ; if ( ! ed ) { return ; } exec . pickChartForFile ( ed . document . fileName , options , ( f ) => { const valsYaml = path . join ( f , "values.yaml" ) ; if ( ! existsSync ( valsYaml ) ) { return ; } this . refreshValues ( valsYaml ) ; this . valuesWatcher = vscode . workspace . createFileSystemWatcher ( new vscode . RelativePattern ( f , "values.yaml" ) ) ; this . valuesWatcher . onDidChange ( ( uri ) => this . refreshValues ( uri . fsPath ) ) ; } ) ; } private refreshValues ( valsYaml ) { try { this . valuesCache = YAML . load ( valsYaml ) ; } catch ( err ) { logger . helm . log ( err . message ) ; return ; } } public provideCompletionItems ( doc , pos ) { const line = doc . lineAt ( pos . line ) . text ; const wordPos = doc . getWordRangeAtPosition ( pos ) ; let lineUntil = line . substr ( 0 , pos . character ) ; if ( wordPos ) { lineUntil = line . substr ( 0 , wordPos . start . character ) ; } if ( lineUntil . endsWith ( "." ) ) { return this . dotCompletionItems ( lineUntil ) ; } return new vscode . CompletionList ( ( new FuncMap ) . all ( ) ) ; } dotCompletionItems ( lineUntil ) { if ( lineUntil . endsWith ( " ." ) ) { return this . funcmap . helmVals ( ) ; } else if ( lineUntil . endsWith ( ".Release." ) ) { return this . funcmap . releaseVals ( ) ; } else if ( lineUntil . endsWith ( ".Chart." ) ) { return this . funcmap . chartVals ( ) ; } else if ( lineUntil . endsWith ( ".Files." ) ) { return this . funcmap . filesVals ( ) ; } else if ( lineUntil . endsWith ( ".Capabilities." ) ) { return this . funcmap . capabilitiesVals ( ) ; } else if ( lineUntil . endsWith ( ".Values." ) ) { if ( ! _ . isPlainObject ( this . valuesCache ) ) { return [ ] ; } const keys = _ . keys ( this . valuesCache ) ; const res = keys . map ( ( key ) => this . funcmap . v ( key , ".Values." + key , "In values.yaml: " + this . valuesCache [ key ] ) ) ; return res ; } else { let reExecResult = null ; try { reExecResult = this . valuesMatcher . exec ( lineUntil ) ; } catch ( err ) { logger . helm . log ( err . message ) ; return [ ] ; } if ( ! reExecResult || reExecResult . length === 0 ) { return [ ] ; } if ( reExecResult [ 1 ] . length === 0 ) { return [ ] ; } const valuesMatches = reExecResult ; const parts = reExecResult [ 1 ] . split ( "." ) ; let cache = this . valuesCache ; for ( const cur of parts ) { if ( cur . length === 0 ) { break ; } if ( ! cache [ cur ] ) { return [ ] ; } cache = cache [ cur ] ; } if ( ! cache || typeof cache === "string" || cache . constructor . name === 'Array' ) { return [ ] ; } const k = _ . keys ( cache ) . map ( ( item ) => this . v ( item , valuesMatches [ 0 ] + item , "In values.yaml: " + cache [ item ] ) ) ; return k ; } } v ( name , use , doc ) { const i = new vscode . CompletionItem ( name , vscode . CompletionItemKind . Constant ) ; i . detail = use ; i . documentation = doc ; return i ; } }