<commit_before>/* @flow */

import { Emitter, CompositeDisposable } from 'atom'
import type { TextEditor, Disposable } from 'atom'
import { linter as validateLinter, messages as validateMessages, messagesLegacy as validateMessagesLegacy } from './validate'
import { normalizeMessages, normalizeMessagesLegacy, isPathIgnored, shouldTriggerLinter, $version, $activated, $requestLatest, $requestLastReceived } from './helpers'
import type { Linter } from './types'

export default class LinterRegistry {
  emitter: Emitter;
  linters: Set<Linter>;
  lintOnFly: boolean;
  ignoreVCS: boolean;
  ignoreGlob: string;
  lintPreviewTabs: boolean;
  subscriptions: CompositeDisposable;

  constructor() {
    this.emitter = new Emitter()
    this.linters = new Set()
    this.subscriptions = new CompositeDisposable()

    this.subscriptions.add(atom.config.observe('linter.lintOnFly', lintOnFly => {
      this.lintOnFly = lintOnFly
    }))
    this.subscriptions.add(atom.config.observe('core.excludeVcsIgnoredPaths', ignoreVCS => {
      this.ignoreVCS = ignoreVCS
    }))
    this.subscriptions.add(atom.config.observe('linter.ignoreGlob', ignoreGlob => {
      this.ignoreGlob = ignoreGlob
    }))
    this.subscriptions.add(atom.config.observe('linter.lintPreviewTabs', lintPreviewTabs => {
      this.lintPreviewTabs = lintPreviewTabs
    }))
    this.subscriptions.add(this.emitter)
  }
  hasLinter(linter: Linter): boolean {
    return this.linters.has(linter)
  }
  addLinter(linter: Linter, legacy: boolean = false) {
    if (!validateLinter(linter)) {
      return
    }
    linter[$activated] = true
    if (typeof linter[$requestLatest] === 'undefined') {
      linter[$requestLatest] = 0
    }
    if (typeof linter[$requestLastReceived] === 'undefined') {
      linter[$requestLastReceived] = 0
    }
    linter[$version] = legacy ? 1 : 2
    this.linters.add(linter)
  }
  deleteLinter(linter: Linter) {
    if (!this.linters.has(linter)) {
      return
    }
    linter[$activated] = false
    this.linters.delete(linter)
  }
  async lint({ onChange, editor } : { onChange: boolean, editor: TextEditor }): Promise<boolean> {
    /* eslint-disable no-multi-spaces */
    const filePath = editor.getPath()

    if (
      (onChange && !this.lintOnFly)                                             || // Lint-on-fly mismatch
      !filePath                                                                 || // Not saved anywhere yet
      editor !== atom.workspace.getActiveTextEditor()                           || // Not active
      isPathIgnored(editor.getPath(), this.ignoreGlob, this.ignoreVCS)          || // Ignored by VCS or Glob
      (!this.lintPreviewTabs && editor.hasTerminatedPendingState === false)        // Ignore Preview tabs
    ) {
      return false
    }

    const scopes = editor.scopeDescriptorForBufferPosition(editor.getCursorBufferPosition()).getScopesArray()
    scopes.push('*')

    const promises = []
    for (const linter of this.linters) {
      if (!shouldTriggerLinter(linter, onChange, scopes)) {
        continue
      }
      const number = ++linter[$requestLatest]
      this.emitter.emit('did-begin-linting', { number, linter, filePath: linter.scope === 'file' ? filePath : null })
      promises.push(new Promise(function(resolve) {
        resolve(linter.lint(editor))
      }).then(messages => {
        this.emitter.emit('did-finish-linting', { number, linter, filePath: linter.scope === 'file' ? filePath : null })
        if (linter[$requestLastReceived] >= number || !linter[$activated]) {
          return
        }
        const buffer = linter.scope === 'project' ? null : editor.getBuffer()
        if (Array.isArray(messages)) {
          linter[$requestLastReceived] = number
          if (!buffer || buffer.isAlive()) {
            let validity = true
            if (atom.inDevMode()) {
              validity = linter[$version] === 2 ? validateMessages(linter.name, messages) : validateMessagesLegacy(linter.name, messages)
            }
            if (validity) {
              if (linter[$version] === 2) {
                normalizeMessages(linter.name, messages)
              } else {
                normalizeMessagesLegacy(linter.name, messages)
              }
            }
          }
        }
        this.emitter.emit('did-update-messages', { messages: messages || [], linter, buffer })
      }, function(error) {
        atom.notifications.addError(`[Linter] Error running ${linter.name}`, {<commit_msg>:bug: Fix bug in linter status notifier of when a linter throws or returns null and the tab is closed<commit_after>/* @flow */

import { Emitter, CompositeDisposable } from 'atom'
import type { TextEditor, Disposable } from 'atom'
import { linter as validateLinter, messages as validateMessages, messagesLegacy as validateMessagesLegacy } from './validate'
import { normalizeMessages, normalizeMessagesLegacy, isPathIgnored, shouldTriggerLinter, $version, $activated, $requestLatest, $requestLastReceived } from './helpers'
import type { Linter } from './types'

export default class LinterRegistry {
  emitter: Emitter;
  linters: Set<Linter>;
  lintOnFly: boolean;
  ignoreVCS: boolean;
  ignoreGlob: string;
  lintPreviewTabs: boolean;
  subscriptions: CompositeDisposable;

  constructor() {
    this.emitter = new Emitter()
    this.linters = new Set()
    this.subscriptions = new CompositeDisposable()

    this.subscriptions.add(atom.config.observe('linter.lintOnFly', lintOnFly => {
      this.lintOnFly = lintOnFly
    }))
    this.subscriptions.add(atom.config.observe('core.excludeVcsIgnoredPaths', ignoreVCS => {
      this.ignoreVCS = ignoreVCS
    }))
    this.subscriptions.add(atom.config.observe('linter.ignoreGlob', ignoreGlob => {
      this.ignoreGlob = ignoreGlob
    }))
    this.subscriptions.add(atom.config.observe('linter.lintPreviewTabs', lintPreviewTabs => {
      this.lintPreviewTabs = lintPreviewTabs
    }))
    this.subscriptions.add(this.emitter)
  }
  hasLinter(linter: Linter): boolean {
    return this.linters.has(linter)
  }
  addLinter(linter: Linter, legacy: boolean = false) {
    if (!validateLinter(linter)) {
      return
    }
    linter[$activated] = true
    if (typeof linter[$requestLatest] === 'undefined') {
      linter[$requestLatest] = 0
    }
    if (typeof linter[$requestLastReceived] === 'undefined') {
      linter[$requestLastReceived] = 0
    }
    linter[$version] = legacy ? 1 : 2
    this.linters.add(linter)
  }
  deleteLinter(linter: Linter) {
    if (!this.linters.has(linter)) {
      return
    }
    linter[$activated] = false
    this.linters.delete(linter)
  }
  async lint({ onChange, editor } : { onChange: boolean, editor: TextEditor }): Promise<boolean> {
    /* eslint-disable no-multi-spaces */
    const filePath = editor.getPath()

    if (
      (onChange && !this.lintOnFly)                                             || // Lint-on-fly mismatch
      !filePath                                                                 || // Not saved anywhere yet
      editor !== atom.workspace.getActiveTextEditor()                           || // Not active
      isPathIgnored(editor.getPath(), this.ignoreGlob, this.ignoreVCS)          || // Ignored by VCS or Glob
      (!this.lintPreviewTabs && editor.hasTerminatedPendingState === false)        // Ignore Preview tabs
    ) {
      return false
    }

    const scopes = editor.scopeDescriptorForBufferPosition(editor.getCursorBufferPosition()).getScopesArray()
    scopes.push('*')

    const promises = []
    for (const linter of this.linters) {
      if (!shouldTriggerLinter(linter, onChange, scopes)) {
        continue
      }
      const number = ++linter[$requestLatest]
      const statusBuffer = linter.scope === 'file' ? null : editor.getBuffer()
      const statusFilePath = linter.scope === 'file' ? filePath : null

      this.emitter.emit('did-begin-linting', { number, linter, filePath: statusFilePath })
      promises.push(new Promise(function(resolve) {
        resolve(linter.lint(editor))
      }).then(messages => {
        this.emitter.emit('did-finish-linting', { number, linter, filePath: statusFilePath })
        if (linter[$requestLastReceived] >= number || !linter[$activated]) {
          return
        }
        linter[$requestLastReceived] = number

        let validity = Array.isArray(messages) && (statusBuffer === null || statusBuffer.isAlive())
        if (validity && atom.inDevMode()) {
          validity = linter[$version] === 2 ? validateMessages(linter.name, messages) : validateMessagesLegacy(linter.name, messages)
        }
        if (validity) {
          if (linter[$version] === 2) {
            normalizeMessages(linter.name, messages)
          } else {
            normalizeMessagesLegacy(linter.name, messages)
          }
        }
        this.emitter.emit('did-update-messages', { messages: messages || [], linter, buffer: statusBuffer })
      }, error => {
        this.emitter.emit('did-finish-linting', { number, linter, filePath: statusFilePath })
        atom.notifications.addError(`[Linter] Error running ${linter.name}`, {
{'fraction_non_alphanumeric': 0.1258649093904448, 'fraction_numerical': 0.0013179571663920922, 'mean_word_length': 2.78156146179402, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 20, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2845319', 'n_tokens_mistral': 2723, 'n_tokens_neox': 2637, 'n_words': 542}