<commit_before>crypto = require 'crypto'
ipc = require 'ipc'
keytar = require 'keytar'
os = require 'os'
path = require 'path'
remote = require 'remote'
shell = require 'shell'
dialog = remote.require 'dialog'
app = remote.require 'app'

_ = require 'underscore-plus'
telepath = require 'telepath'
{Model} = telepath
fs = require 'fs-plus'

{$} = require './space-pen-extensions'
SiteShim = require './site-shim'
WindowEventHandler = require './window-event-handler'

# Public: Atom global for dealing with packages, themes, menus, and the window.
#
# An instance of this class is always available as the `atom` global.
#
# ## Useful properties available:
#
#  * `atom.config`      - A {Config} instance
#  * `atom.contextMenu` - A {ContextMenuManager} instance
#  * `atom.keymap`      - A {Keymap} instance
#  * `atom.menu`        - A {MenuManager} instance
#  * `atom.workspaceView`    - A {WorkspaceView} instance
#  * `atom.packages`    - A {PackageManager} instance
#  * `atom.pasteboard`  - A {Pasteboard} instance
#  * `atom.project`     - A {Project} instance
#  * `atom.syntax`      - A {Syntax} instance
#  * `atom.themes`      - A {ThemeManager} instance
module.exports =
class Atom extends Model
  # Public: Load or create the Atom environment in the given mode
  #
  # - mode: Pass 'editor' or 'spec' depending on the kind of environment you
  #         want to build.
  #
  # Returns an Atom instance, fully initialized
  @loadOrCreate: (mode) ->
    telepath.devMode = not @isReleasedVersion()

    if documentState = @loadDocumentState(mode)
      environment = @deserialize(documentState)

    environment ? @createAsRoot({mode})

  # Private: Loads and returns the serialized state corresponding to this window
  # if it exists; otherwise returns undefined.
  @loadDocumentState: (mode) ->
    statePath = @getStatePath(mode)

    if fs.existsSync(statePath)
      try
        documentStateString = fs.readFileSync(statePath, 'utf8')
      catch error
        console.warn "Error reading window state: #{statePath}", error.stack, error
    else
      documentStateString = @getLoadSettings().windowState

    try
      JSON.parse(documentStateString) if documentStateString?
    catch error
      console.warn "Error parsing window state: #{statePath} #{error.stack}", error

  # Private: Returns the path where the state for the current window will be
  # located if it exists.
  @getStatePath: (mode) ->
    switch mode
      when 'spec'
        filename = 'spec'
      when 'editor'
        {initialPath} = @getLoadSettings()
        if initialPath
          sha1 = crypto.createHash('sha1').update(initialPath).digest('hex')
          filename = "editor-#{sha1}"

    if filename
      path.join(@getStorageDirPath(), filename)
    else
      null

  # Private: Get the directory path to Atom's configuration area.
  #
  # Returns the absolute path to ~/.atom
  @getConfigDirPath: ->
    @configDirPath ?= fs.absolute('~/.atom')

  # Private: Get the path to Atom's storage directory.
  #
  # Returns the absolute path to ~/.atom/storage
  @getStorageDirPath: ->
    @storageDirPath ?= path.join(@getConfigDirPath(), 'storage')

  # Private: Returns the load settings hash associated with the current window.
  @getLoadSettings: ->
    _.deepClone(@loadSettings ?= _.deepClone(@getCurrentWindow().loadSettings))

  # Private:
  @getCurrentWindow: ->
    remote.getCurrentWindow()

  # Private: Get the version of the Atom application.
  @getVersion: ->
    @version ?= app.getVersion()

  # Private: Determine whether the current version is an official release.
  @isReleasedVersion: ->
    not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix

  @properties
    mode: null
    project: null

  workspaceViewParentSelector: 'body'

  # Private: Called by telepath. I'd like this to be merged with initialize eventually.
  created: ->
    DeserializerManager = require './deserializer-manager'
    @deserializers = new DeserializerManager(this)

  # Public: Sets up the basic services that should be available in all modes
  # (both spec and application). Call after this instance has been assigned to
  # the `atom` global.
  initialize: ->
    window.onerror = =>
      @openDevTools()
      @emit 'error', arguments

    @unsubscribe()
    @setBodyPlatformClass()

    @loadTime = null

    Config = require './config'
    Keymap = require './keymap'
    PackageManager = require './package-manager'
    Pasteboard = require './pasteboard'
    Syntax = require './syntax'
    ThemeManager = require './theme-manager'
    ContextMenuManager = require './context-menu-manager'
    MenuManager = require './menu-manager'
    {devMode, resourcePath} = @getLoadSettings()
    configDirPath = @getConfigDirPath()

    @config = new Config({configDirPath, resourcePath})
    @keymap = new Keymap({configDirPath, resourcePath})
    @packages = new PackageManager({devMode, configDirPath, resourcePath})
    @themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
    @contextMenu = new ContextMenuManager(devMode)
    @menu = new MenuManager({resourcePath})
    @pasteboard = new Pasteboard()
    @syntax = @deserializers.deserialize(@state.get('syntax')) ? new Syntax()
    @site = new SiteShim(this)

    @subscribe @packages, 'activated', => @watchThemes()

    Project = require './project'
    TextBuffer = require './text-buffer'
    TokenizedBuffer = require './tokenized-buffer'
    DisplayBuffer = require './display-buffer'
    Editor = require './editor'
    @registerRepresentationClasses(Project, TextBuffer, TokenizedBuffer, DisplayBuffer, Editor)
    @createRepresentations()

    @windowEventHandler = new WindowEventHandler

  # Private:
  setBodyPlatformClass: ->
    document.body.classList.add("platform-#{process.platform}")

  # Public: Get the current window
  getCurrentWindow: ->
    @constructor.getCurrentWindow()

  # Public: Get the dimensions of this window.
  #
  # Returns an object with x, y, width, and height keys.
  getWindowDimensions: ->
    browserWindow = @getCurrentWindow()
    [x, y] = browserWindow.getPosition()
    [width, height] = browserWindow.getSize()
    {x, y, width, height}

  # Public: Set the dimensions of the window.
  #
  # The window will be centered if either the x or y coordinate is not set
  # in the dimensions parameter. If x or y are omitted the window will be
  # centered. If height or width are omitted only the position will be changed.
  #
  # * dimensions:
  #    + x: The new x coordinate.
  #    + y: The new y coordinate.
  #    + width: The new width.
  #    + height: The new height.
  setWindowDimensions: ({x, y, width, height}) ->
    browserWindow = @getCurrentWindow()
    if width? and height?
      browserWindow.setSize(width, height)
    if x? and y?
      browserWindow.setPosition(x, y)
    else
      browserWindow.center()

  # Private:
  restoreWindowDimensions: ->
    windowDimensions = @state.getObject('windowDimensions') ? {}
    {initialSize} = @getLoadSettings()
    windowDimensions.height ?= initialSize?.height ? global.screen.availHeight
    windowDimensions.width ?= initialSize?.width ? Math.min(global.screen.availWidth, 1024)
    @setWindowDimensions(windowDimensions)

  # Private:
  storeWindowDimensions: ->
    @state.set('windowDimensions', @getWindowDimensions())

  # Public: Get the load settings for the current window.
  #
  # Returns an object containing all the load setting key/value pairs.
  getLoadSettings: ->
    @constructor.getLoadSettings()

  # Private:
  deserializeProject: ->
    Project = require './project'
    @project ?= new Project(path: @getLoadSettings().initialPath)

  # Private:
  deserializeWorkspaceView: ->
    WorkspaceView = require './workspace-view'
    @workspaceView = @deserializers.deserialize(@state.get('workspaceView'))
    unless @workspaceView?
      @workspaceView = new WorkspaceView()
      @state.set('workspaceView', @workspaceView.getState())
    $(@workspaceViewParentSelector).append(@workspaceView)

  # Private:
  deserializePackageStates: ->
    @packages.packageStates = @state.getObject('packageStates') ? {}
    @state.remove('packageStates')

  # Private:
  deserializeEditorWindow: ->
    @deserializePackageStates()
    @deserializeProject()
    @deserializeWorkspaceView()

  # Private: Call this method when establishing a real application window.
  startEditorWindow: ->
    if process.platform is 'darwin'
      CommandInstaller = require './command-installer'
      CommandInstaller.installAtomCommand()
      CommandInstaller.installApmCommand()

    @restoreWindowDimensions()
    @config.load()
    @config.setDefaults('core', require('./workspace-view').configDefaults)
    @config.setDefaults('editor', require('./editor-view').configDefaults)
    @keymap.loadBundledKeymaps()
    @themes.loadBaseStylesheets()
    @packages.loadPackages()
    @createRepresentations()
    @deserializeEditorWindow()
    @packages.activate()
    @keymap.loadUserKeymap()
    @requireUserInitScript()
    @menu.update()

    $(window).on 'unload', =>
      $(document.body).css('visibility', 'hidden')
      @unloadEditorWindow()
      false

    @displayWindow()

  # Private:
  unloadEditorWindow: ->
    return if not @project and not @workspaceView

    @state.set('syntax', @syntax.serialize())
    @state.set('workspaceView', @workspaceView.serialize())
    @packages.deactivatePackages()
    @state.set('packageStates', @packages.packageStates)
    @saveSync()
    @workspaceView.remove()
    @workspaceView = null
    @project.destroy()
    @windowEventHandler?.unsubscribe()
    @keymap.destroy()
    @windowState = null

  # Private:
  loadThemes: ->
    @themes.load()

  # Private:
  watchThemes: ->
    @themes.on 'reloaded', =>
      # Only reload stylesheets from non-theme packages
      for pack in @packages.getActivePackages() when pack.getType() isnt 'theme'
        pack.reloadStylesheets?()
      null

  # Public: Open a new Atom window using the given options.
  #
  # Calling this method without an options parameter will open a prompt to pick
  # a file/folder to open in the new window.
  #
  # * options
  #   * pathsToOpen: A string array of paths to open
  open: (options) ->
    ipc.sendChannel('open', options)

  # Public: Open a confirm dialog.
  #
  # ## Example:
  # ```coffeescript
  #   atom.confirm
  #      message: 'How you feeling?'
  #      detailedMessage: 'Be honest.'
  #      buttons:
  #        Good: -> window.alert('good to hear')
  #        Bad: ->  window.alert('bummer')
  # ```
  #
  # * options:
  #    + message: The string message to display.
  #    + detailedMessage: The string detailed message to display.
  #    + buttons: Either an array of strings or an object where the values
  #      are callbacks to invoke when clicked.
  #
  # Returns the chosen index if buttons was an array or the return of the
  # callback if buttons was an object.
  confirm: ({message, detailedMessage, buttons}={}) ->
    buttons ?= {}
    if _.isArray(buttons)
      buttonLabels = buttons
    else
      buttonLabels = Object.keys(buttons)

    chosen = dialog.showMessageBox @getCurrentWindow(),
      type: 'info'
      message: message
      detail: detailedMessage
      buttons: buttonLabels

    if _.isArray(buttons)
      chosen
    else
      callback = buttons[buttonLabels[chosen]]
      callback?()

  # Private:
  showSaveDialog: (callback) ->
    callback(showSaveDialogSync())

  # Private:
  showSaveDialogSync: (defaultPath) ->
    defaultPath ?= @project?.getPath()
    currentWindow = @getCurrentWindow()
    dialog.showSaveDialog currentWindow, {title: 'Save File', defaultPath}

  # Public: Open the dev tools for the current window.
  openDevTools: ->
    @getCurrentWindow().openDevTools()

  # Public: Toggle the visibility of the dev tools for the current window.
  toggleDevTools: ->
    @getCurrentWindow().toggleDevTools()

  # Public: Reload the current window.
  reload: ->
    @getCurrentWindow().restart()

  # Public: Focus the current window.
  focus: ->
    @getCurrentWindow().focus()
    $(window).focus()

  # Public: Show the current window.
  show: ->
    @getCurrentWindow().show()

  # Public: Hide the current window.
  hide: ->
    @getCurrentWindow().hide()

  # Private: Schedule the window to be shown and focused on the next tick.
  #
  # This is done in a next tick to prevent a white flicker from occurring
  # if called synchronously.
  displayWindow: ->
    setImmediate =>
      @show()
      @focus()
      @setFullScreen(true) if @workspaceView.getState().get('fullScreen')

  # Public: Close the current window.
  close: ->
    @getCurrentWindow().close()

  # Private:
  exit: (status) -> app.exit(status)

  # Public: Is the current window in development mode?
  inDevMode: ->
    @getLoadSettings().devMode

  # Public: Is the current window running specs?
  inSpecMode: ->
    @getLoadSettings().isSpec

  # Public: Toggle the full screen state of the current window.
  toggleFullScreen: ->
    @setFullScreen(!@isFullScreen())

  # Public: Set the full screen state of the current window.
  setFullScreen: (fullScreen=false) ->
    @getCurrentWindow().setFullScreen(fullScreen)

  # Public: Is the current window in full screen mode?
  isFullScreen: ->
    @getCurrentWindow().isFullScreen()

  # Public: Get the version of the Atom application.
  getVersion: ->
    @constructor.getVersion()

  # Public: Determine whether the current version is an official release.
  isReleasedVersion: ->
    @constructor.isReleasedVersion()

  getGitHubAuthTokenName: ->
    'Atom GitHub API Token'

  # Public: Set the the github token in the keychain
  setGitHubAuthToken: (token) ->
    keytar.replacePassword(@getGitHubAuthTokenName(), 'github', token)

  # Public: Get the github token from the keychain
  getGitHubAuthToken: ->
    keytar.getPassword(@getGitHubAuthTokenName(), 'github')

  # Public: Get the directory path to Atom's configuration area.
  #
  # Returns the absolute path to ~/.atom
  getConfigDirPath: ->
    @constructor.getConfigDirPath()

  # Private:
  saveSync: ->
    if statePath = @constructor.getStatePath(@mode)
      super(statePath)
    else
      @getCurrentWindow().loadSettings.windowState = JSON.stringify(@serializeForPersistence())


  # Public: Get the time taken to completely load the current window.
  #
  # This time include things like loading and activating packages, creating
  # DOM elements for the editor, and reading the config.
  #
  # Returns the number of milliseconds taken to load the window or null
  # if the window hasn't finished loading yet.
  getWindowLoadTime: ->
    @loadTime

  # Private:
  crashMainProcess: ->
    remote.process.crash()

  # Private:
  crashRenderProcess: ->
    process.crash()

  # Public: Visually and audibly trigger a beep.
  beep: ->
    shell.beep() if @config.get('core.audioBeep')
    @workspaceView.trigger 'beep'

  # Private:
  requireUserInitScript: ->
    if userInitScriptPath = fs.resolve(@getConfigDirPath(), 'user', ['js', 'coffee'])
      try
        require userInitScriptPath
      catch error
        console.error "Failed to load `#{userInitScriptPath}`", error.stack, error

  # Public: Require the module with the given globals.
  #
  # The globals will be set on the `window` object and removed after the
  # require completes.
  requireWithGlobals: (id, globals={}) ->
    existingGlobals = {}
    for key, value of globals
      existingGlobals[key] = window[key]
      window[key] = value

    require(id)

    for key, value of existingGlobals
      if value is undefined
        delete window[key]
      else
        window[key] = value
<commit_msg>Rename error event to uncaught-error<commit_after>crypto = require 'crypto'
ipc = require 'ipc'
keytar = require 'keytar'
os = require 'os'
path = require 'path'
remote = require 'remote'
shell = require 'shell'
dialog = remote.require 'dialog'
app = remote.require 'app'

_ = require 'underscore-plus'
telepath = require 'telepath'
{Model} = telepath
fs = require 'fs-plus'

{$} = require './space-pen-extensions'
SiteShim = require './site-shim'
WindowEventHandler = require './window-event-handler'

# Public: Atom global for dealing with packages, themes, menus, and the window.
#
# An instance of this class is always available as the `atom` global.
#
# ## Useful properties available:
#
#  * `atom.config`      - A {Config} instance
#  * `atom.contextMenu` - A {ContextMenuManager} instance
#  * `atom.keymap`      - A {Keymap} instance
#  * `atom.menu`        - A {MenuManager} instance
#  * `atom.workspaceView`    - A {WorkspaceView} instance
#  * `atom.packages`    - A {PackageManager} instance
#  * `atom.pasteboard`  - A {Pasteboard} instance
#  * `atom.project`     - A {Project} instance
#  * `atom.syntax`      - A {Syntax} instance
#  * `atom.themes`      - A {ThemeManager} instance
module.exports =
class Atom extends Model
  # Public: Load or create the Atom environment in the given mode
  #
  # - mode: Pass 'editor' or 'spec' depending on the kind of environment you
  #         want to build.
  #
  # Returns an Atom instance, fully initialized
  @loadOrCreate: (mode) ->
    telepath.devMode = not @isReleasedVersion()

    if documentState = @loadDocumentState(mode)
      environment = @deserialize(documentState)

    environment ? @createAsRoot({mode})

  # Private: Loads and returns the serialized state corresponding to this window
  # if it exists; otherwise returns undefined.
  @loadDocumentState: (mode) ->
    statePath = @getStatePath(mode)

    if fs.existsSync(statePath)
      try
        documentStateString = fs.readFileSync(statePath, 'utf8')
      catch error
        console.warn "Error reading window state: #{statePath}", error.stack, error
    else
      documentStateString = @getLoadSettings().windowState

    try
      JSON.parse(documentStateString) if documentStateString?
    catch error
      console.warn "Error parsing window state: #{statePath} #{error.stack}", error

  # Private: Returns the path where the state for the current window will be
  # located if it exists.
  @getStatePath: (mode) ->
    switch mode
      when 'spec'
        filename = 'spec'
      when 'editor'
        {initialPath} = @getLoadSettings()
        if initialPath
          sha1 = crypto.createHash('sha1').update(initialPath).digest('hex')
          filename = "editor-#{sha1}"

    if filename
      path.join(@getStorageDirPath(), filename)
    else
      null

  # Private: Get the directory path to Atom's configuration area.
  #
  # Returns the absolute path to ~/.atom
  @getConfigDirPath: ->
    @configDirPath ?= fs.absolute('~/.atom')

  # Private: Get the path to Atom's storage directory.
  #
  # Returns the absolute path to ~/.atom/storage
  @getStorageDirPath: ->
    @storageDirPath ?= path.join(@getConfigDirPath(), 'storage')

  # Private: Returns the load settings hash associated with the current window.
  @getLoadSettings: ->
    _.deepClone(@loadSettings ?= _.deepClone(@getCurrentWindow().loadSettings))

  # Private:
  @getCurrentWindow: ->
    remote.getCurrentWindow()

  # Private: Get the version of the Atom application.
  @getVersion: ->
    @version ?= app.getVersion()

  # Private: Determine whether the current version is an official release.
  @isReleasedVersion: ->
    not /\w{7}/.test(@getVersion()) # Check if the release is a 7-character SHA prefix

  @properties
    mode: null
    project: null

  workspaceViewParentSelector: 'body'

  # Private: Called by telepath. I'd like this to be merged with initialize eventually.
  created: ->
    DeserializerManager = require './deserializer-manager'
    @deserializers = new DeserializerManager(this)

  # Public: Sets up the basic services that should be available in all modes
  # (both spec and application). Call after this instance has been assigned to
  # the `atom` global.
  initialize: ->
    window.onerror = =>
      @openDevTools()
      @emit 'uncaught-error', arguments

    @unsubscribe()
    @setBodyPlatformClass()

    @loadTime = null

    Config = require './config'
    Keymap = require './keymap'
    PackageManager = require './package-manager'
    Pasteboard = require './pasteboard'
    Syntax = require './syntax'
    ThemeManager = require './theme-manager'
    ContextMenuManager = require './context-menu-manager'
    MenuManager = require './menu-manager'
    {devMode, resourcePath} = @getLoadSettings()
    configDirPath = @getConfigDirPath()

    @config = new Config({configDirPath, resourcePath})
    @keymap = new Keymap({configDirPath, resourcePath})
    @packages = new PackageManager({devMode, configDirPath, resourcePath})
    @themes = new ThemeManager({packageManager: @packages, configDirPath, resourcePath})
    @contextMenu = new ContextMenuManager(devMode)
    @menu = new MenuManager({resourcePath})
    @pasteboard = new Pasteboard()
    @syntax = @deserializers.deserialize(@state.get('syntax')) ? new Syntax()
    @site = new SiteShim(this)

    @subscribe @packages, 'activated', => @watchThemes()

    Project = require './project'
    TextBuffer = require './text-buffer'
    TokenizedBuffer = require './tokenized-buffer'
    DisplayBuffer = require './display-buffer'
    Editor = require './editor'
    @registerRepresentationClasses(Project, TextBuffer, TokenizedBuffer, DisplayBuffer, Editor)
    @createRepresentations()

    @windowEventHandler = new WindowEventHandler

  # Private:
  setBodyPlatformClass: ->
    document.body.classList.add("platform-#{process.platform}")

  # Public: Get the current window
  getCurrentWindow: ->
    @constructor.getCurrentWindow()

  # Public: Get the dimensions of this window.
  #
  # Returns an object with x, y, width, and height keys.
  getWindowDimensions: ->
    browserWindow = @getCurrentWindow()
    [x, y] = browserWindow.getPosition()
    [width, height] = browserWindow.getSize()
    {x, y, width, height}

  # Public: Set the dimensions of the window.
  #
  # The window will be centered if either the x or y coordinate is not set
  # in the dimensions parameter. If x or y are omitted the window will be
  # centered. If height or width are omitted only the position will be changed.
  #
  # * dimensions:
  #    + x: The new x coordinate.
  #    + y: The new y coordinate.
  #    + width: The new width.
  #    + height: The new height.
  setWindowDimensions: ({x, y, width, height}) ->
    browserWindow = @getCurrentWindow()
    if width? and height?
      browserWindow.setSize(width, height)
    if x? and y?
      browserWindow.setPosition(x, y)
    else
      browserWindow.center()

  # Private:
  restoreWindowDimensions: ->
    windowDimensions = @state.getObject('windowDimensions') ? {}
    {initialSize} = @getLoadSettings()
    windowDimensions.height ?= initialSize?.height ? global.screen.availHeight
    windowDimensions.width ?= initialSize?.width ? Math.min(global.screen.availWidth, 1024)
    @setWindowDimensions(windowDimensions)

  # Private:
  storeWindowDimensions: ->
    @state.set('windowDimensions', @getWindowDimensions())

  # Public: Get the load settings for the current window.
  #
  # Returns an object containing all the load setting key/value pairs.
  getLoadSettings: ->
    @constructor.getLoadSettings()

  # Private:
  deserializeProject: ->
    Project = require './project'
    @project ?= new Project(path: @getLoadSettings().initialPath)

  # Private:
  deserializeWorkspaceView: ->
    WorkspaceView = require './workspace-view'
    @workspaceView = @deserializers.deserialize(@state.get('workspaceView'))
    unless @workspaceView?
      @workspaceView = new WorkspaceView()
      @state.set('workspaceView', @workspaceView.getState())
    $(@workspaceViewParentSelector).append(@workspaceView)

  # Private:
  deserializePackageStates: ->
    @packages.packageStates = @state.getObject('packageStates') ? {}
    @state.remove('packageStates')

  # Private:
  deserializeEditorWindow: ->
    @deserializePackageStates()
    @deserializeProject()
    @deserializeWorkspaceView()

  # Private: Call this method when establishing a real application window.
  startEditorWindow: ->
    if process.platform is 'darwin'
      CommandInstaller = require './command-installer'
      CommandInstaller.installAtomCommand()
      CommandInstaller.installApmCommand()

    @restoreWindowDimensions()
    @config.load()
    @config.setDefaults('core', require('./workspace-view').configDefaults)
    @config.setDefaults('editor', require('./editor-view').configDefaults)
    @keymap.loadBundledKeymaps()
    @themes.loadBaseStylesheets()
    @packages.loadPackages()
    @createRepresentations()
    @deserializeEditorWindow()
    @packages.activate()
    @keymap.loadUserKeymap()
    @requireUserInitScript()
    @menu.update()

    $(window).on 'unload', =>
      $(document.body).css('visibility', 'hidden')
      @unloadEditorWindow()
      false

    @displayWindow()

  # Private:
  unloadEditorWindow: ->
    return if not @project and not @workspaceView

    @state.set('syntax', @syntax.serialize())
    @state.set('workspaceView', @workspaceView.serialize())
    @packages.deactivatePackages()
    @state.set('packageStates', @packages.packageStates)
    @saveSync()
    @workspaceView.remove()
    @workspaceView = null
    @project.destroy()
    @windowEventHandler?.unsubscribe()
    @keymap.destroy()
    @windowState = null

  # Private:
  loadThemes: ->
    @themes.load()

  # Private:
  watchThemes: ->
    @themes.on 'reloaded', =>
      # Only reload stylesheets from non-theme packages
      for pack in @packages.getActivePackages() when pack.getType() isnt 'theme'
        pack.reloadStylesheets?()
      null

  # Public: Open a new Atom window using the given options.
  #
  # Calling this method without an options parameter will open a prompt to pick
  # a file/folder to open in the new window.
  #
  # * options
  #   * pathsToOpen: A string array of paths to open
  open: (options) ->
    ipc.sendChannel('open', options)

  # Public: Open a confirm dialog.
  #
  # ## Example:
  # ```coffeescript
  #   atom.confirm
  #      message: 'How you feeling?'
  #      detailedMessage: 'Be honest.'
  #      buttons:
  #        Good: -> window.alert('good to hear')
  #        Bad: ->  window.alert('bummer')
  # ```
  #
  # * options:
  #    + message: The string message to display.
  #    + detailedMessage: The string detailed message to display.
  #    + buttons: Either an array of strings or an object where the values
  #      are callbacks to invoke when clicked.
  #
  # Returns the chosen index if buttons was an array or the return of the
  # callback if buttons was an object.
  confirm: ({message, detailedMessage, buttons}={}) ->
    buttons ?= {}
    if _.isArray(buttons)
      buttonLabels = buttons
    else
      buttonLabels = Object.keys(buttons)

    chosen = dialog.showMessageBox @getCurrentWindow(),
      type: 'info'
      message: message
      detail: detailedMessage
      buttons: buttonLabels

    if _.isArray(buttons)
      chosen
    else
      callback = buttons[buttonLabels[chosen]]
      callback?()

  # Private:
  showSaveDialog: (callback) ->
    callback(showSaveDialogSync())

  # Private:
  showSaveDialogSync: (defaultPath) ->
    defaultPath ?= @project?.getPath()
    currentWindow = @getCurrentWindow()
    dialog.showSaveDialog currentWindow, {title: 'Save File', defaultPath}

  # Public: Open the dev tools for the current window.
  openDevTools: ->
    @getCurrentWindow().openDevTools()

  # Public: Toggle the visibility of the dev tools for the current window.
  toggleDevTools: ->
    @getCurrentWindow().toggleDevTools()

  # Public: Reload the current window.
  reload: ->
    @getCurrentWindow().restart()

  # Public: Focus the current window.
  focus: ->
    @getCurrentWindow().focus()
    $(window).focus()

  # Public: Show the current window.
  show: ->
    @getCurrentWindow().show()

  # Public: Hide the current window.
  hide: ->
    @getCurrentWindow().hide()

  # Private: Schedule the window to be shown and focused on the next tick.
  #
  # This is done in a next tick to prevent a white flicker from occurring
  # if called synchronously.
  displayWindow: ->
    setImmediate =>
      @show()
      @focus()
      @setFullScreen(true) if @workspaceView.getState().get('fullScreen')

  # Public: Close the current window.
  close: ->
    @getCurrentWindow().close()

  # Private:
  exit: (status) -> app.exit(status)

  # Public: Is the current window in development mode?
  inDevMode: ->
    @getLoadSettings().devMode

  # Public: Is the current window running specs?
  inSpecMode: ->
    @getLoadSettings().isSpec

  # Public: Toggle the full screen state of the current window.
  toggleFullScreen: ->
    @setFullScreen(!@isFullScreen())

  # Public: Set the full screen state of the current window.
  setFullScreen: (fullScreen=false) ->
    @getCurrentWindow().setFullScreen(fullScreen)

  # Public: Is the current window in full screen mode?
  isFullScreen: ->
    @getCurrentWindow().isFullScreen()

  # Public: Get the version of the Atom application.
  getVersion: ->
    @constructor.getVersion()

  # Public: Determine whether the current version is an official release.
  isReleasedVersion: ->
    @constructor.isReleasedVersion()

  getGitHubAuthTokenName: ->
    'Atom GitHub API Token'

  # Public: Set the the github token in the keychain
  setGitHubAuthToken: (token) ->
    keytar.replacePassword(@getGitHubAuthTokenName(), 'github', token)

  # Public: Get the github token from the keychain
  getGitHubAuthToken: ->
    keytar.getPassword(@getGitHubAuthTokenName(), 'github')

  # Public: Get the directory path to Atom's configuration area.
  #
  # Returns the absolute path to ~/.atom
  getConfigDirPath: ->
    @constructor.getConfigDirPath()

  # Private:
  saveSync: ->
    if statePath = @constructor.getStatePath(@mode)
      super(statePath)
    else
      @getCurrentWindow().loadSettings.windowState = JSON.stringify(@serializeForPersistence())


  # Public: Get the time taken to completely load the current window.
  #
  # This time include things like loading and activating packages, creating
  # DOM elements for the editor, and reading the config.
  #
  # Returns the number of milliseconds taken to load the window or null
  # if the window hasn't finished loading yet.
  getWindowLoadTime: ->
    @loadTime

  # Private:
  crashMainProcess: ->
    remote.process.crash()

  # Private:
  crashRenderProcess: ->
    process.crash()

  # Public: Visually and audibly trigger a beep.
  beep: ->
    shell.beep() if @config.get('core.audioBeep')
    @workspaceView.trigger 'beep'

  # Private:
  requireUserInitScript: ->
    if userInitScriptPath = fs.resolve(@getConfigDirPath(), 'user', ['js', 'coffee'])
      try
        require userInitScriptPath
      catch error
        console.error "Failed to load `#{userInitScriptPath}`", error.stack, error

  # Public: Require the module with the given globals.
  #
  # The globals will be set on the `window` object and removed after the
  # require completes.
  requireWithGlobals: (id, globals={}) ->
    existingGlobals = {}
    for key, value of globals
      existingGlobals[key] = window[key]
      window[key] = value

    require(id)

    for key, value of existingGlobals
      if value is undefined
        delete window[key]
      else
        window[key] = value

{'fraction_non_alphanumeric': 0.11234811374769388, 'fraction_numerical': 0.0006361727845282779, 'mean_word_length': 3.3890827865419517, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 131, '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': '6134307', 'n_tokens_mistral': 9180, 'n_tokens_neox': 9007, 'n_words': 2668}