<commit_before>{Editor, $, Range}  = require "atom"
_ = require "underscore-plus"
path = require "path"
minimatch = require "minimatch"
SimpleSelectListView = require "./simple-select-list-view"
fuzzaldrin = require "fuzzaldrin"
Perf = require "./perf"
Utils = require "./utils"

module.exports =
class AutocompleteView extends SimpleSelectListView
  currentBuffer: null
  wordList: null
  wordRegex: /\b\w*[a-zA-Z_]\w*\b/g
  originalCursorPosition: null
  debug: false
  lastConfirmedWord: null

  ###
   * Makes sure we're listening to editor and buffer events, sets
   * the current buffer
   * @param  {EditorView} @editorView
   * @private
  ###
  initialize: (@editorView) ->
    super

    {@editor} = @editorView
    return if @currentFileBlacklisted()

    @addClass "autocomplete-plus"

    @handleEvents()
    @setCurrentBuffer @editor.getBuffer()

  ###
   * Checks whether the current file is blacklisted
   * @return {Boolean}
   * @private
  ###
  currentFileBlacklisted: ->
    blacklist = (atom.config.get("autocomplete-plus.fileBlacklist") or "")
      .split ","
      .map (s) -> s.trim()

    fileName = path.basename @editor.getBuffer().getPath()
    for blacklistGlob in blacklist
      if minimatch fileName, blacklistGlob
        return true

    return false

  ###
   * Handles editor events
   * @private
  ###
  handleEvents: ->
    # Make sure we don't scroll in the editor view when scrolling
    # in the list
    @list.on "mousewheel", (event) -> event.stopPropagation()

    # Is this the event for switching tabs? Dunno...
    @editor.on "title-changed-subscription-removed", @cancel

    # Close the overlay when the cursor moved without
    # changing any text
    @editor.on "cursor-moved", @cursorMoved

  ###
   * Finds autocompletions in the current syntax scope (e.g. css values)
   * @return {Array}
   * @private
  ###
  getCompletionsForCursorScope: ->
    cursorScope = @editor.scopesForBufferPosition @editor.getCursorBufferPosition()
    completions = atom.syntax.propertiesForScope cursorScope, "editor.completions"
    completions = completions.map (properties) -> _.valueForKeyPath properties, "editor.completions"
    return Utils.unique _.flatten(completions)

  ###
   * Generates the word list from the editor buffer(s)
   * @private
  ###
  buildWordList: ->
    # Abuse the Hash as a Set
    wordList = []

    # Do we want autocompletions from all open buffers?
    if atom.config.get "autocomplete-plus.includeCompletionsFromAllBuffers"
      buffers = atom.project.getBuffers()
    else
      buffers = [@currentBuffer]

    # Check how long the word list building took
    p = new Perf "Building word list", {@debug}
    p.start()

    # Collect words from all buffers using the regular expression
    matches = []
    matches.push(buffer.getText().match(@wordRegex)) for buffer in buffers

    # Flatten the matches, make it an unique array
    wordList = _.flatten matches
    wordList = Utils.unique wordList
    @wordList = wordList

    p.stop()

  ###
   * Gets called when the user successfully confirms a suggestion
   * @private
  ###
  confirmed: (match) ->
    @editor.getSelection().clear()
    @cancel()

    return unless match

    @lastConfirmedWord = match.word
    @replaceTextWithMatch match
    position = @editor.getCursorBufferPosition()
    @editor.setCursorBufferPosition [position.row, position.column]

  ###
   * Focuses the editor again
   * @private
  ###
  cancel: =>
    super
    @editorView.focus()

  ###
   * Finds suggestions for the current prefix, sets the list items,
   * positions the overlay and shows it
   * @private
  ###
  runAutocompletion: =>
    selection = @editor.getSelection()
    prefix = @prefixOfSelection selection

    # Stop completion if the word was already confirmed
    return if prefix is @lastConfirmedWord

    # No prefix? Don't autocomplete!
    return unless prefix.length

    suggestions = @findMatchesForWord prefix

    # No suggestions? Don't autocomplete!
    return unless suggestions.length

    # Now we're ready - display the suggestions
    @setItems suggestions
    @editorView.appendToLinesView this
    @setPosition()

    @setActive()

  ###
   * Gets called when the content has been modified
   * @private
  ###
  contentsModified: =>
    if @active
      @detach()
      @list.empty()
      @editorView.focus()

    delay = parseInt(atom.config.get "autocomplete-plus.completionDelay")
    if @delayTimeout
      clearTimeout @delayTimeout

    @delayTimeout = setTimeout @runAutocompletion, delay

  ###
   * Gets called when the cursor has moved. Cancels the autocompletion if
   * the text has not been changed and the autocompletion is
   * @param  {[type]} data [description]
   * @return {[type]}      [description]
  ###
  cursorMoved: (data) =>
    @cancel() unless data.textChanged

  ###
   * Gets called when the user saves the document. Rebuilds the word
   * list and cancels the autocompletion
   * @private<commit_msg>Stop flickering when re-triggering autocompletion<commit_after>{Editor, $, Range}  = require "atom"
_ = require "underscore-plus"
path = require "path"
minimatch = require "minimatch"
SimpleSelectListView = require "./simple-select-list-view"
fuzzaldrin = require "fuzzaldrin"
Perf = require "./perf"
Utils = require "./utils"

module.exports =
class AutocompleteView extends SimpleSelectListView
  currentBuffer: null
  wordList: null
  wordRegex: /\b\w*[a-zA-Z_]\w*\b/g
  originalCursorPosition: null
  debug: false
  lastConfirmedWord: null

  ###
   * Makes sure we're listening to editor and buffer events, sets
   * the current buffer
   * @param  {EditorView} @editorView
   * @private
  ###
  initialize: (@editorView) ->
    super

    {@editor} = @editorView
    return if @currentFileBlacklisted()

    @addClass "autocomplete-plus"

    @handleEvents()
    @setCurrentBuffer @editor.getBuffer()

  ###
   * Checks whether the current file is blacklisted
   * @return {Boolean}
   * @private
  ###
  currentFileBlacklisted: ->
    blacklist = (atom.config.get("autocomplete-plus.fileBlacklist") or "")
      .split ","
      .map (s) -> s.trim()

    fileName = path.basename @editor.getBuffer().getPath()
    for blacklistGlob in blacklist
      if minimatch fileName, blacklistGlob
        return true

    return false

  ###
   * Handles editor events
   * @private
  ###
  handleEvents: ->
    # Make sure we don't scroll in the editor view when scrolling
    # in the list
    @list.on "mousewheel", (event) -> event.stopPropagation()

    # Is this the event for switching tabs? Dunno...
    @editor.on "title-changed-subscription-removed", @cancel

    # Close the overlay when the cursor moved without
    # changing any text
    @editor.on "cursor-moved", @cursorMoved

  ###
   * Finds autocompletions in the current syntax scope (e.g. css values)
   * @return {Array}
   * @private
  ###
  getCompletionsForCursorScope: ->
    cursorScope = @editor.scopesForBufferPosition @editor.getCursorBufferPosition()
    completions = atom.syntax.propertiesForScope cursorScope, "editor.completions"
    completions = completions.map (properties) -> _.valueForKeyPath properties, "editor.completions"
    return Utils.unique _.flatten(completions)

  ###
   * Generates the word list from the editor buffer(s)
   * @private
  ###
  buildWordList: ->
    # Abuse the Hash as a Set
    wordList = []

    # Do we want autocompletions from all open buffers?
    if atom.config.get "autocomplete-plus.includeCompletionsFromAllBuffers"
      buffers = atom.project.getBuffers()
    else
      buffers = [@currentBuffer]

    # Check how long the word list building took
    p = new Perf "Building word list", {@debug}
    p.start()

    # Collect words from all buffers using the regular expression
    matches = []
    matches.push(buffer.getText().match(@wordRegex)) for buffer in buffers

    # Flatten the matches, make it an unique array
    wordList = _.flatten matches
    wordList = Utils.unique wordList
    @wordList = wordList

    p.stop()

  ###
   * Gets called when the user successfully confirms a suggestion
   * @private
  ###
  confirmed: (match) ->
    @editor.getSelection().clear()
    @cancel()

    return unless match

    @lastConfirmedWord = match.word
    @replaceTextWithMatch match
    position = @editor.getCursorBufferPosition()
    @editor.setCursorBufferPosition [position.row, position.column]

  ###
   * Focuses the editor again
   * @private
  ###
  cancel: =>
    super
    @editorView.focus()

  ###
   * Finds suggestions for the current prefix, sets the list items,
   * positions the overlay and shows it
   * @private
  ###
  runAutocompletion: =>
    if @active
      @detach()
      @list.empty()
      @editorView.focus()

    selection = @editor.getSelection()
    prefix = @prefixOfSelection selection

    # Stop completion if the word was already confirmed
    return if prefix is @lastConfirmedWord

    # No prefix? Don't autocomplete!
    return unless prefix.length

    suggestions = @findMatchesForWord prefix

    # No suggestions? Don't autocomplete!
    return unless suggestions.length

    # Now we're ready - display the suggestions
    @setItems suggestions
    @editorView.appendToLinesView this
    @setPosition()

    @setActive()

  ###
   * Gets called when the content has been modified
   * @private
  ###
  contentsModified: =>
    delay = parseInt(atom.config.get "autocomplete-plus.completionDelay")
    if @delayTimeout
      clearTimeout @delayTimeout

    @delayTimeout = setTimeout @runAutocompletion, delay

  ###
   * Gets called when the cursor has moved. Cancels the autocompletion if
   * the text has not been changed and the autocompletion is
   * @param  {[type]} data [description]
   * @return {[type]}      [description]
  ###
  cursorMoved: (data) =>
    @cancel() unless data.textChanged

  ###
   * Gets called when the user saves the document. Rebuilds the word
   * list and cancels the autocompletion
   * @private
{'fraction_non_alphanumeric': 0.10232046409281856, 'fraction_numerical': 0.0, 'mean_word_length': 3.2189873417721517, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 29, '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': '1334439', 'n_tokens_mistral': 2961, 'n_tokens_neox': 2848, 'n_words': 879}