<commit_before>import * as ts from 'typescript'
import _ = require('lodash')
import { CompletionItemBuilder } from '../completionItemBuilder'
import { BaseExpressionTemplate } from './baseTemplates'
import { getConfigValue } from '../utils'

export class VarTemplate extends BaseExpressionTemplate {
  private static MethodCallRegex = /^(get|read|create|retrieve|select|modify|update|use|find)(?<name>[A-Z].+?)(By.*)?$/

  constructor(private keyword: 'var' | 'let' | 'const') {
    super(keyword)
  }

  buildCompletionItem(node: ts.Node, indentSize?: number) {
    const inferVarNameEnabled = getConfigValue<boolean>('inferVariableName')
    const suggestedVarName = inferVarNameEnabled ? this.inferVarName(node) : undefined;

    return CompletionItemBuilder
      .create(this.keyword, node, indentSize)
      .replace(`${this.keyword} \${1:${suggestedVarName ?? 'name'}} = {{expr}}$0`, true)
      .build()
  }

  override canUse(node: ts.Node) {
    return (super.canUse(node) || this.isNewExpression(node) || this.isObjectLiteral(node) || this.isStringLiteral(node))
      && !this.inReturnStatement(node)
      && !this.inFunctionArgument(node)
      && !this.inVariableDeclaration(node)
      && !this.inAssignmentStatement(node)
  }

  private inferVarName(node: ts.Node) {
    const buildVarName = (name: string) => name && _.lowerFirst(name)

    if (ts.isNewExpression(node)) {
      return buildVarName(this.inferNewExpressionVar(node))
    } else if (ts.isCallExpression(node)) {
      const methodName = this.getMethodName(node)
      const matches = VarTemplate.MethodCallRegex.exec(methodName)

      return buildVarName(matches?.groups?.name)
    }
  }

  private getMethodName(node: ts.CallExpression) {
    if (ts.isIdentifier(node.expression)) {
      return node.expression.text
    } else if (ts.isPropertyAccessExpression(node.expression)) {
      return node.expression.name.text
    }
  }

  private inferNewExpressionVar(node: ts.NewExpression) {
    if (ts.isIdentifier(node.expression)) {
      return node.expression.text
    } else if (ts.isPropertyAccessExpression(node.expression)) {
      return node.expression.name.text
    }
  }
}<commit_msg>enable proposal of multiple variants<commit_after>import * as ts from 'typescript'
import _ = require('lodash')
import { CompletionItemBuilder } from '../completionItemBuilder'
import { BaseExpressionTemplate } from './baseTemplates'
import { getConfigValue } from '../utils'

export class VarTemplate extends BaseExpressionTemplate {
  private static MethodCallRegex = /^(get|read|create|retrieve|select|modify|update|use|find)(?<name>[A-Z].+?)?$/
  private static CleanNameRegex = /((By|With|From).*$)|(Sync$)/

  constructor(private keyword: 'var' | 'let' | 'const') {
    super(keyword)
  }

  buildCompletionItem(node: ts.Node, indentSize?: number) {
    const inferVarNameEnabled = getConfigValue<boolean>('inferVariableName')
    const suggestedVarName = inferVarNameEnabled ? this.inferVarName(node) : undefined
    const nameSnippet = suggestedVarName?.length > 1 ? `\${1|${suggestedVarName.join(',')}|}` : `\${1:${suggestedVarName?.[0] ?? 'name'}}`

    return CompletionItemBuilder
      .create(this.keyword, node, indentSize)
      .replace(`${this.keyword} ${nameSnippet} = {{expr}}$0`, true)
      .build()
  }

  override canUse(node: ts.Node) {
    return (super.canUse(node) || this.isNewExpression(node) || this.isObjectLiteral(node) || this.isStringLiteral(node))
      && !this.inReturnStatement(node)
      && !this.inFunctionArgument(node)
      && !this.inVariableDeclaration(node)
      && !this.inAssignmentStatement(node)
  }

  private inferVarName(node: ts.Node) {
    const buildVarName = (name: string) => name && _.lowerFirst(name)

    if (ts.isNewExpression(node)) {
      return [buildVarName(this.inferNewExpressionVar(node))]
    } else if (ts.isCallExpression(node)) {
      const methodName = this.getMethodName(node)
      const name = VarTemplate.MethodCallRegex.exec(methodName)?.groups?.name
      const cleanerVariant = name?.replace(VarTemplate.CleanNameRegex, '')
      if (!name) {
        return
      }

      return [...cleanerVariant && cleanerVariant !== name ? [cleanerVariant] : [], name].map(buildVarName)
    }
  }

  private getMethodName(node: ts.CallExpression) {
    if (ts.isIdentifier(node.expression)) {
      return node.expression.text
    } else if (ts.isPropertyAccessExpression(node.expression)) {
      return node.expression.name.text
    }
  }

  private inferNewExpressionVar(node: ts.NewExpression) {
    if (ts.isIdentifier(node.expression)) {
      return node.expression.text
    } else if (ts.isPropertyAccessExpression(node.expression)) {
      return node.expression.name.text
    }
  }
}
{'fraction_non_alphanumeric': 0.1365222746869226, 'fraction_numerical': 0.0014370765756518168, 'mean_word_length': 3.6711409395973154, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 10, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3841560', 'n_tokens_mistral': 1490, 'n_tokens_neox': 1422, 'n_words': 258}