<commit_before>{Range} = require('atom')
XRegExp = require('xregexp').XRegExp
path = require 'path'
child_process = require 'child_process'

module.exports = Helpers =
  # Validates that the results passed to Linter Base by a Provider contain the
  #   information needed to display an issue.
  validateResults: (results) ->
    if (not results) or results.constructor.name isnt 'Array'
      throw new Error "Got invalid response from Linter, Type: #{typeof results}"
    for result in results
      unless result.type
        throw new Error "Missing type field on Linter Response, Got: #{Object.keys(result)}"
      result.range = Range.fromObject result.range if result.range?
      result.class = result.type.toLowerCase().replace(' ', '-')
      Helpers.validateResults(result.trace) if result.trace
    results

  # Validates that a provider is capable of providing the Base Linter info
  #   needed to be used at lint time.
  validateLinter: (linter) ->
    unless linter.grammarScopes instanceof Array
      throw new Error("grammarScopes is not an Array. Got: #{linter.grammarScopes})")
    unless linter.lint?
      throw new Error("Missing linter.lint")
    if typeof linter.lint isnt 'function'
      throw new Error("linter.lint isn't a function")
    return true

  # Everything past this point relates to CLI helpers as loosly demoed out in:
  #   https://gist.github.com/steelbrain/43d9c38208bf9f2964ab

  exec: (command, options = {stream: 'stdout'}) ->
    throw new Error "Nothing to execute." if not arguments.length
    return new Promise (resolve, reject) ->
      process = child_process.exec(command, options)
      data = []
      if options.stream == 'stderr'
        process.stderr.on 'data', (d) -> data.push(d.toString())
      else
        process.stdout.on 'data', (d) -> data.push(d.toString())
      process.on 'close', ->
        resolve(data)

  # This should only be used if the linter is only working with files in their
  #   base directory. Else wise they should use `Helpers#exec`.
  execFilePath: (command, filePath, options = {}) ->
    throw new Error "Nothing to execute." if not arguments.length
    throw new Error "No File Path to work with." if not filePath
    return new Promise (resolve, reject) ->
      options.cwd = path.dirname(filePath) if not options.cwd
      command = "#{command} #{filePath}"
      resolve(Helpers.exec(command, options))

  # Due to what we are attempting to do, the only viable solution right now is
  #   XRegExp.
  #
  # Follows the following format taken from 0.x.y API.
  #
  # file: the file where the issue Exists
  # type: the type of issue occuring here
  # message: the message to show in the linter views (required)
  # line: the line number on which to mark error (required if not lineStart)
  # lineStart: the line number to start the error mark (optional)
  # lineEnd: the line number on end the error mark (optional)
  # col: the column on which to mark, will utilize syntax scope to higlight the
  #      closest matching syntax element based on your code syntax (optional)
  # colStart: column to on which to start a higlight (optional)
  # colEnd: column to end highlight (optional)
  # We place priority on `lineStart` and `lineEnd` over `line.`
  # We place priority on `colStart` and `colEnd` over `col.`
  parse: (data, regex, options = {baseReduction: 1}) ->
    new Promise (resolve, reject) ->
      toReturn = []
      regex = XRegExp(regex)
      for line in data
        match = XRegExp.exec(line, regex)
        if match
          if match.lineStart
            lineStart = match.lineStart - options.baseReduction
          else if match.line
            lineStart = match.line - options.baseReduction
          else
            lineStart = 0
          if match.colStart
            colStart = match.colStart - options.baseReduction
          else if match.col
            colStart = match.col - options.baseReduction
          else
            colStart = 0
          if match.lineEnd
            lineEnd = match.lineEnd - options.baseReduction
          else if match.line
            lineEnd = match.line - options.baseReduction
          else
            lineEnd = 0
          if match.colEnd
            colEnd = match.colEnd - options.baseReduction
          else if match.col
            colEnd = match.col - options.baseReduction
          else
            colEnd = 0
          toReturn.push(
            type: match.type,
            text: match.message,
            filePath: match.file
            range: [[lineStart, colStart], [lineEnd, colEnd]]
          )
      resolve(toReturn)<commit_msg>Implement stdin, and did some cleanup.<commit_after>{Range} = require('atom')
XRegExp = require('xregexp').XRegExp
path = require 'path'
child_process = require 'child_process'

module.exports = Helpers =
  # Validates that the results passed to Linter Base by a Provider contain the
  #   information needed to display an issue.
  validateResults: (results) ->
    if (not results) or results.constructor.name isnt 'Array'
      throw new Error "Got invalid response from Linter, Type: #{typeof results}"
    for result in results
      unless result.type
        throw new Error "Missing type field on Linter Response, Got: #{Object.keys(result)}"
      result.range = Range.fromObject result.range if result.range?
      result.class = result.type.toLowerCase().replace(' ', '-')
      Helpers.validateResults(result.trace) if result.trace
    results

  # Validates that a provider is capable of providing the Base Linter info
  #   needed to be used at lint time.
  validateLinter: (linter) ->
    unless linter.grammarScopes instanceof Array
      throw new Error("grammarScopes is not an Array. Got: #{linter.grammarScopes})")
    unless linter.lint?
      throw new Error("Missing linter.lint")
    if typeof linter.lint isnt 'function'
      throw new Error("linter.lint isn't a function")
    return true

  # Everything past this point relates to CLI helpers as loosly demoed out in:
  #   https://gist.github.com/steelbrain/43d9c38208bf9f2964ab

  exec: (command, options = {stream: 'stdout'}) ->
    throw new Error "Nothing to execute." if not arguments.length
    return new Promise (resolve, reject) ->
      process = child_process.exec(command, options)
      options.stream = 'stdout' if not options.stream
      data = []
      if options.stream == 'stderr'
        process.stderr.on 'data', (d) -> data.push(d.toString())
      else
        process.stdout.on 'data', (d) -> data.push(d.toString())
      process.stdin.write(options.stdin.toString()) if options.stdin
      process.on 'close', ->
        resolve(data)

  # This should only be used if the linter is only working with files in their
  #   base directory. Else wise they should use `Helpers#exec`.
  execFilePath: (command, filePath, options = {}) ->
    throw new Error "Nothing to execute." if not arguments.length
    throw new Error "No File Path to work with." if not filePath
    return new Promise (resolve, reject) ->
      options.cwd = path.dirname(filePath) if not options.cwd
      command = "#{command} #{filePath}"
      resolve(Helpers.exec(command, options))

  # Due to what we are attempting to do, the only viable solution right now is
  #   XRegExp.
  #
  # Follows the following format taken from 0.x.y API.
  #
  # file: the file where the issue Exists
  # type: the type of issue occuring here
  # message: the message to show in the linter views (required)
  # line: the line number on which to mark error (required if not lineStart)
  # lineStart: the line number to start the error mark (optional)
  # lineEnd: the line number on end the error mark (optional)
  # col: the column on which to mark, will utilize syntax scope to higlight the
  #      closest matching syntax element based on your code syntax (optional)
  # colStart: column to on which to start a higlight (optional)
  # colEnd: column to end highlight (optional)
  # We place priority on `lineStart` and `lineEnd` over `line.`
  # We place priority on `colStart` and `colEnd` over `col.`
  parse: (data, regex, options = {baseReduction: 1}) ->
    new Promise (resolve, reject) ->
      toReturn = []
      regex = XRegExp(regex)
      for line in data
        if regex.test line
          match = XRegExp.exec(line, regex)
          options.baseReduction = 1 if not options.baseReduction
          lineStart = 0
          lineStart = match.line - options.baseReduction if match.line
          lineStart = match.lineStart - options.baseReduction if match.lineStart
          colStart = 0
          colStart = match.col - options.baseReduction if match.col
          colStart = match.colStart - options.baseReduction if match.colStart
          lineEnd = 0
          lineEnd = match.line - options.baseReduction if match.line
          lineEnd = match.lineEnd - options.baseReduction if match.lineEnd
          colEnd = 0
          colEnd = match.col - options.baseReduction if match.col
          colEnd = match.colEnd - options.baseReduction if match.colEnd
          filePath = match.file
          filePath = options.filePath if options.filePath
          toReturn.push(
            type: match.type,
            text: match.message,
            filePath: filePath,
            range: [[lineStart, colStart], [lineEnd, colEnd]]
          )
      resolve(toReturn)
{'fraction_non_alphanumeric': 0.08924754168448054, 'fraction_numerical': 0.004168448054724241, 'mean_word_length': 2.694038689301224, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 25, 'https://': 2, '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': '3782049', 'n_tokens_mistral': 2631, 'n_tokens_neox': 2591, 'n_words': 970}