Full Code of steelbrain/flow-ide for AI

master 3ec590624211 cached
36 files
176.4 KB
47.2k tokens
64 symbols
1 requests
Download .txt
Repository: steelbrain/flow-ide
Branch: master
Commit: 3ec590624211
Files: 36
Total size: 176.4 KB

Directory structure:
gitextract_6xu3tfy1/

├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .flowconfig
├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── decls/
│   ├── atom.js
│   ├── hyperclick.js
│   ├── jasmine-atom.js
│   ├── jasmine.js
│   ├── linter.js
│   └── outline.js
├── lib/
│   ├── coverage-view.js
│   ├── helpers.js
│   ├── index.js
│   ├── language-client.js
│   ├── linter/
│   │   ├── v1/
│   │   │   ├── index.js
│   │   │   ├── pretty.js
│   │   │   └── types.js
│   │   └── v2/
│   │       ├── index.js
│   │       └── types.js
│   ├── main.js
│   ├── outline/
│   │   ├── index.js
│   │   ├── parse.js
│   │   ├── text.js
│   │   └── types.js
│   └── types.js
├── package.json
├── spec/
│   ├── linter/
│   │   └── v2/
│   │       └── index-spec.js
│   └── outline/
│       ├── parse-sample-ast.js
│       └── parse-spec.js
├── styles/
│   └── flow-ide.less
└── vendor/
    └── .flowconfig

================================================
FILE CONTENTS
================================================

================================================
FILE: .babelrc
================================================
{
  "presets": [
    ["env", { "targets": { "node": "current" } }]
  ],
  "sourceMap": "inline",
  "plugins": [
    "transform-class-properties",
    "transform-object-rest-spread",
    "transform-flow-strip-types"
  ]
}


================================================
FILE: .editorconfig
================================================
root = true

[*]
indent_style = space
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false

================================================
FILE: .eslintignore
================================================
decls


================================================
FILE: .eslintrc.json
================================================
{
  "extends": "steelbrain",
  "plugins": [
    "flowtype"
  ],
  "rules": {
    "import/prefer-default-export": "off",
    "no-duplicate-imports": "off",
    "flowtype/define-flow-type": "error",
    "flowtype/use-flow-type": "error"
  },
  "globals": {
    "waitsForPromise": true
  }
}


================================================
FILE: .flowconfig
================================================
[ignore]

[include]

[libs]
decls

[options]
module.system=node
include_warnings=true
suppress_comment= \\(.\\|\n\\)*\\$FlowIgnore

[lints]
all=warn

# turned off because there are lot of types missing atm...
unclear-type=off
untyped-import=off


================================================
FILE: .gitignore
================================================
.idea
node_modules
*.log
.DS_Store


================================================
FILE: CHANGELOG.md
================================================
#### 1.13.0

- Use the official [language server protocol](https://microsoft.github.io/language-server-protocol/) to communicate with `flow`

#### 1.12.1

- Support for Tree-Sitter Flow grammar

#### 1.12.0

- Add config to stop flow server when flow-ide is stopped
- Fix autocomplete selector

#### 1.11.1

- Support for Tree-Sitter Flow grammar

#### 1.11.0

- Support new errors messages since [flow 0.66.0](https://github.com/facebook/flow/releases/tag/v0.66.0)
- Fix goto definition (via hyperclick) if the cursor is at the end of the word
- Fix outline for rest/spread operators

#### 1.10.0

- [Outline](https://github.com/facebook-atom/atom-ide-ui/blob/2767934/docs/outline-view.md) of classes, functions, types and variables (using [atom-ide-ui](https://github.com/facebook-atom/atom-ide-ui/))
- Minor bug fixes regarding markdown rendering of flow errors

#### 1.9.0

- Support [datatip](https://github.com/facebook-atom/atom-ide-ui/blob/508ecfd6aa8121ae2e423b2becbe22e34cf191fb/docs/datatips.md) from [atom-ide-ui](https://github.com/facebook-atom/atom-ide-ui/)
- Fix wrong coverage (#112)
- Fix minor markdown rendering issue

#### 1.8.1

- Hide coverage view instead of emptying to fix extra padding

#### 1.8.0

- Add linting in flow support
- Show a restart notification after `hyperclickPriority` is updated

#### 1.7.0

- Change search order of `flow` executable (_Executable Path_ setting > `node_modules/.bin/flow` > global `flow`)

#### 1.6.0

- Add "jump to definition" via `hyperclick`
- Show the complete flow error message as error description

#### 1.5.0

- Add option to show uncovered code
- Fix process exited with non-zero status code for autocomplete
- Fix invalid behavior of `onlyIfAppropriate` config

#### 1.4.2

- Limit concurrent spawned processes

#### 1.4.1

- Increase exec timeout to 60 seconds

#### 1.4.0

- Upgrade for linter v2 support

#### 1.3.0

- Terminate flow servers on deactivate
- Fix path.dirname deprecation by ignoring autocomplete requests on files not yet saved

#### 1.2.4

- Had to bump version because of some issues in deployment (network)

#### 1.2.2

- Handle coverage count zero (#52)

#### 1.2.1

- Fix flow type checking (#49)

#### 1.2

- Add Flow coverage view

#### 1.1.10
- Provide a default .flowconfig file if onlyIfAppropriate is enabled and a .flowconfig file is not found already

#### 1.1.9

- Fix autocompletion for properties (#33)
- Remove types from function params (#8)

#### 1.1.8

- APM was having hiccups back then so didn't publish properly

#### 1.1.7

- Fix a bug in last release

#### 1.1.6

- Workaround a Atom's builtin babel bug

#### 1.1.5

- Just another patch to catch more flow errors gracefully

#### 1.1.4

- Handle flow crashes/respawns gracefully

#### 1.1.3

- Show entire linter error message

#### 1.1.1-1.1.2

- Fix a bug introduced in 1.1.0 where autocomplete wouldn't work
- Fix a reference to undefined variable in case of error ( Fixes #12 )

#### 1.1.0

- Add support for locally installed flow bins
- Bump `atom-linter` to v5

#### 1.0.6

- Show a correct type for Objects in autocomplete
- Only run autocomplete at appropriate times ( Fixes #3 )

#### 1.0.5

- Fix a bug in retrying for server
- Fix a bug where deep nested objects would mess up autocomplete

#### 1.0.4

- Bump `atom-package-deps` version

#### 1.0.3

- Bump `atom-linter` dependency to include fix for projects that don't have a `.flowcofig`

#### 1.0.2

- Improve handling of fatal errors

#### 1.0.1

- Implement smart sorting and filtering of autocomplete suggestions
- Make linter messages more user friendly

#### 1.0.0

- Linting support added
- Autocomplete support added


================================================
FILE: LICENSE.md
================================================
Copyright (c) 2016 steelbrain

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


================================================
FILE: README.md
================================================
Flow-IDE
=======

Flow IDE is a lightweight package that provides IDE features for [FlowType][FlowType] for [Atom Editor][Atom]. It's pretty lightweight and robust.

#### Installation
```
apm install flow-ide
```

#### Setup
 1) You will need to install flow-bin into your project!
```
npm install --save-dev flow-bin
```
or
```
yarn add --dev flow-bin
```

 2) You will need ` // @flow ` at the top of all files you wish to lint


3) You will need a `.flowconfig` which can be initialized with `flow init` if you have flow installed, if not you can use [this flowconfig](https://github.com/steelbrain/flow-ide/blob/master/.flowconfig)

4) Window: Reload (Ctrl+Shift+F5) to apply changes


#### Features

 - Linting
 - Autocomplete
 - Jump to declaration (using [facebook-atom/hyperclick][hyperclick] or [facebook-atom/atom-ide-ui][atom-ide-ui])
 - Datatip on hover (using [facebook-atom/atom-ide-ui][atom-ide-ui])
 - Outline of classes, functions, types and variables (using [facebook-atom/atom-ide-ui][atom-ide-ui])

#### Differences to other packages

Differences to [facebook/nuclide][nuclide]
 - Nuclide is nice and all but it's mostly bloatware for lightweight flow programming

Differences to [AtomLinter/linter-flow][linter-flow]
 - It tries to manage flow servers by itself, I find it annoying

Differences to [nmn/autocomplete-flow][autocomplete-flow]
 - Never worked for me

Differences to [LukeHoban/ide-flow][ide-flow]
 - Outdated and buggy
 - No longer maintained

#### Screenshots

![Autocomplete](https://cloud.githubusercontent.com/assets/4278113/12857027/bb8e2c80-cc69-11e5-918d-4451d0679e66.png)


#### License

This project is licensed under the terms of MIT License. Check the LICENSE file for more info.

[FlowType]:http://flowtype.org/
[Atom]:https://atom.io/
[nuclide]:https://github.com/facebook/nuclide
[hyperclick]:https://github.com/facebook-atom/hyperclick
[atom-ide-ui]:https://github.com/facebook-atom/atom-ide-ui
[ide-flow]:https://github.com/lukehoban/atom-ide-flow
[linter-flow]:https://github.com/AtomLinter/linter-flow
[autocomplete-flow]:https://github.com/nmn/autocomplete-flow


================================================
FILE: decls/atom.js
================================================
/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the license found in the LICENSE file in
 * the root directory of this source tree.
 *
 * @flow
 */

/**
 * Private Classes
 */

type IDisposable = { dispose(): void }

// Octicons v4.4.0. List extracted from the atom-styleguide package.
type atom$Octicon = 'alert' | 'alignment-align' | 'alignment-aligned-to' | 'alignment-unalign' |
  'arrow-down' | 'arrow-left' | 'arrow-right' | 'arrow-small-down' | 'arrow-small-left' |
  'arrow-small-right' | 'arrow-small-up' | 'arrow-up' | 'beaker' | 'beer' | 'bell' | 'bold' |
  'book' | 'bookmark' | 'briefcase' | 'broadcast' | 'browser' | 'bug' | 'calendar' | 'check' |
  'checklist' | 'chevron-down' | 'chevron-left' | 'chevron-right' | 'chevron-up' | 'circle-slash' |
  'circuit-board' | 'clippy' | 'clock' | 'cloud-download' | 'cloud-upload' | 'code' | 'color-mode' |
  'comment' | 'comment-add' | 'comment-discussion' | 'credit-card' | 'dash' | 'dashboard' |
  'database' | 'desktop-download' | 'device-camera' | 'device-camera-video' | 'device-desktop' |
  'device-mobile' | 'diff' | 'diff-added' | 'diff-ignored' | 'diff-modified' | 'diff-removed' |
  'diff-renamed' | 'ellipses' | 'ellipsis' | 'eye' | 'eye-unwatch' | 'eye-watch' | 'file' |
  'file-add' | 'file-binary' | 'file-code' | 'file-directory' | 'file-directory-create' |
  'file-media' | 'file-pdf' | 'file-submodule' | 'file-symlink-directory' | 'file-symlink-file' |
  'file-text' | 'file-zip' | 'flame' | 'fold' | 'gear' | 'gift' | 'gist' | 'gist-fork' |
  'gist-new' | 'gist-private' | 'gist-secret' | 'git-branch' | 'git-branch-create' |
  'git-branch-delete' | 'git-commit' | 'git-compare' | 'git-fork-private' | 'git-merge' |
  'git-pull-request' | 'git-pull-request-abandoned' | 'globe' | 'grabber' | 'graph' | 'heart' |
  'history' | 'home' | 'horizontal-rule' | 'hourglass' | 'hubot' | 'inbox' | 'info' |
  'issue-closed' | 'issue-opened' | 'issue-reopened' | 'italic' | 'jersey' | 'jump-down' |
  'jump-left' | 'jump-right' | 'jump-up' | 'key' | 'keyboard' | 'law' | 'light-bulb' | 'link' |
  'link-external' | 'list-ordered' | 'list-unordered' | 'location' | 'lock' | 'log-in' | 'log-out' |
  'logo-gist' | 'logo-github' | 'mail' | 'mail-read' | 'mail-reply' | 'mark-github' | 'markdown' |
  'megaphone' | 'mention' | 'microscope' | 'milestone' | 'mirror' | 'mirror-private' |
  'mirror-public' | 'mortar-board' | 'move-down' | 'move-left' | 'move-right' | 'move-up' | 'mute' |
  'no-newline' | 'octoface' | 'organization' | 'package' | 'paintcan' | 'pencil' | 'person' |
  'person-add' | 'person-follow' | 'pin' | 'playback-fast-forward' | 'playback-pause' |
  'playback-play' | 'playback-rewind' | 'plug' | 'plus-small' | 'plus' | 'podium' |
  'primitive-dot' | 'primitive-square' | 'pulse' | 'puzzle' | 'question' | 'quote' | 'radio-tower' |
  'remove-close' | 'reply' | 'repo' | 'repo-clone' | 'repo-create' | 'repo-delete' |
  'repo-force-push' | 'repo-forked' | 'repo-pull' | 'repo-push' | 'repo-sync' | 'rocket' | 'rss' |
  'ruby' | 'screen-full' | 'screen-normal' | 'search' | 'search-save' | 'server' | 'settings' |
  'shield' | 'sign-in' | 'sign-out' | 'smiley' | 'split' | 'squirrel' | 'star' | 'star-add' |
  'star-delete' | 'steps' | 'stop' | 'sync' | 'tag' | 'tag-add' | 'tag-remove' | 'tasklist' |
  'telescope' | 'terminal' | 'text-size' | 'three-bars' | 'thumbsdown' | 'thumbsup' | 'tools' |
  'trashcan' | 'triangle-down' | 'triangle-left' | 'triangle-right' | 'triangle-up' | 'type-array'|
  'type-boolean'| 'type-class'| 'type-constant'| 'type-constructor'| 'type-enum'| 'type-field'|
  'type-file'| 'type-function'| 'type-interface'| 'type-method'| 'type-module'| 'type-namespace'|
  'type-number'| 'type-package'| 'type-property'| 'type-string'| 'type-variable' | 'unfold' |
  'unmute' | 'unverified' | 'verified' | 'versions' | 'watch' | 'x' | 'zap';

type atom$PaneLocation = 'left' | 'right' | 'bottom' | 'center';

declare type atom$Color  = {
  // Returns a String in the form '#abcdef'.
  toHexString(): string;
  // Returns a String in the form 'rgba(25, 50, 75, .9)'.
  toRGBAString(): string;
}

declare class atom$Model {
  destroy(): void,
  isDestroyed(): boolean,
}

declare class atom$Package {
  path: string,
  activateTime: number,
  mainModule: any,
  mainModulePath: string,
  metadata: Object,
  name: string,
  loadTime: number,
  getType(): 'atom' | 'textmate' | 'theme',
  hasActivationCommands(): boolean,
  hasActivationHooks(): boolean,
  initializeTime: number,
  getActivationHooks(): Array<string>,
  onDidDeactivate(cb: () => mixed): IDisposable,
  activateNow(): void,
  // Undocumented
  bundledPackage: boolean,
  getCanDeferMainModuleRequireStorageKey(): string,
  initializeIfNeeded(): void,
}

/**
 * Essential Classes
 */

declare type atom$CustomEvent  = CustomEvent & {
  originalEvent?: Event;
}

type atom$CommandCallback = (event: atom$CustomEvent) => mixed;

type atom$CommandDescriptor = {
  name: string,
  displayName: string,
  description?: string,
  hiddenInCommandPalette?: boolean,
  tags?: Array<string>,
};

type atom$CommandListener = atom$CommandCallback | {
  displayName?: string,
  description?: string,
  didDispatch: atom$CommandCallback,
};

declare class atom$CommandRegistry {
  // Methods
  add(
    target: string | HTMLElement,
    commandNameOrCommands: string | {[commandName: string]: atom$CommandListener},
    listener?: atom$CommandListener,
    throwOnInvalidSelector?: boolean,
  ): IDisposable,
  dispatch(target: HTMLElement, commandName: string, detail?: Object): void,
  onDidDispatch(callback: (event: atom$CustomEvent) => mixed): IDisposable,
  onWillDispatch(callback: (event: atom$CustomEvent) => mixed): IDisposable,
  findCommands(opts: {target: Node}): Array<atom$CommandDescriptor>,
}

declare class atom$CompositeDisposable {
  constructor(...disposables: Array<IDisposable>): void,
  dispose(): void,

  add(...disposables: Array<IDisposable>): void,
  remove(disposable: IDisposable): void,
  clear(): void,
}

type atom$ConfigParams = {
  saveCallback?: Object => void,
  mainSource?: string,
  projectHomeSchema?: atom$ConfigSchema,
};

type atom$ConfigType =
  'boolean' | 'string' | 'integer' | 'number' |
  'array' | 'object' | 'color' | 'any';

type atom$ConfigSchema = {
  default?: mixed,
  description?: string,
  enum?: Array<string | {value: string, description: string}>,
  maximum?: number,
  minimum?: number,
  properties?: Object,
  title?: string,
  type: Array<atom$ConfigType> | atom$ConfigType,
};

declare class atom$Config {
  defaultSettings: Object,
  settings: Object,

  // Config Subscription
  observe(
    keyPath: string,
    optionsOrCallback?:
      | {scope?: atom$ScopeDescriptorLike}
      | (value: mixed) => void,
    callback?: (value: mixed) => mixed,
  ): IDisposable,

  onDidChange(
    keyPathOrCallback:
      | string
      | (event: {oldValue: mixed, newValue: mixed}) => mixed,
    optionsOrCallback?:
      | {scope?: atom$ScopeDescriptorLike}
      | (event: {oldValue: mixed, newValue: mixed}) => mixed,
    callback?: (event: {oldValue: mixed, newValue: mixed}) => mixed
  ): IDisposable,

  // Managing Settings
  get(
    keyPath?: string,
    options?: {
      excludeSources?: Array<string>,
      sources?: Array<string>,
      scope?: atom$ScopeDescriptorLike,
    }
  ): mixed,

  set(
    keyPath: string,
    value: ?mixed,
    options?: {
      scopeSelector?: string,
      source?: string,
    },
  ): boolean,

  unset(
    keyPath: string,
    options?: {
      scopeSelector?: string,
      source?: string,
    }
  ): void,

  getUserConfigPath(): string,

  // Undocumented Methods
  constructor(params?: atom$ConfigParams): atom$Config,
  getRawValue(keyPath: ?string, options: {excludeSources?: string, sources?: string}): mixed,
  getSchema(keyPath: string): atom$ConfigSchema,
  save(): void,
  setRawValue(keyPath: string, value: mixed): void,
  setSchema(
    keyPath: string,
    schema: atom$ConfigSchema,
  ): void,
  removeAtKeyPath(keyPath: ?string, value: ?mixed): mixed,

  // Used by nuclide-config to set the initial settings from disk
  resetUserSettings(newSettings: Object, options?: {source?: string}): void,
}

declare class atom$Cursor {
  // Cursor Marker
  marker: atom$Marker;
  editor: atom$TextEditor;

  // Event Subscription
  onDidChangePosition(
    callback: (event: {
      oldBufferPosition: atom$Point,
      oldScreenPosition: atom$Point,
      newBufferPosition: atom$Point,
      newScreenPosition: atom$Point,
      textChanged: boolean,
      Cursor: atom$Cursor,
    }) => mixed,
  ): IDisposable,

  // Managing Cursor Position
  getBufferRow(): number,
  getBufferColumn(): number,
  getBufferPosition(): atom$Point,

  // Cursor Position Details
  // Moving the Cursor
  moveUp(rowCount: number, {moveToEndOfSelection?: boolean}): void,
  moveDown(rowCount: number, {moveToEndOfSelection?: boolean}): void,

  // Local Positions and Ranges
  getCurrentWordBufferRange(options?: {wordRegex: RegExp}): atom$Range,
  getCurrentWordPrefix(): string,

  // Visibility
  // Comparing to another cursor
  // Utilities
  wordRegExp(options?: {includeNonWordCharacters: boolean}): RegExp,
}

declare class atom$Decoration {
  destroy(): void,
  onDidChangeProperties(
    callback: (event: {oldProperties: Object, newProperties: Object}) => mixed
    ): IDisposable,
  onDidDestroy(callback: () => mixed): IDisposable,
  getMarker(): atom$Marker,
  getProperties(): Object,
  setProperties(properties: mixed): void,
}

declare class atom$DisplayMarkerLayer {
  destroy(): void,
  clear(): void,
  isDestroyed(): boolean,
  markBufferPosition(position: atom$PointLike, options?: MarkerOptions): atom$Marker,
  markBufferRange(range: atom$Range | atom$RangeLike, options?: MarkerOptions): atom$Marker,
  findMarkers(options: MarkerOptions): Array<atom$Marker>,
  getMarkers(): Array<atom$Marker>,
  onDidUpdate(callback: () => mixed): IDisposable
}

declare class atom$LayerDecoration {
  destroy(): void,
  isDestroyed(): boolean,
  getProperties(): Object,
  setProperties(properties: mixed): void,
  setPropertiesForMarker(marker: atom$Marker, properties: mixed): void,
}

declare class atom$Disposable {
  constructor(disposalAction?: (...args: Array<any>) => any): void,
  disposed: boolean,
  dispose(): void,
}

declare class atom$Emitter {
  static onEventHandlerException(error: any): IDisposable,
  dispose(): void,
  on(name: string, callback: (v: any) => mixed): IDisposable,
  once(name: string, callback: (v: any) => mixed): IDisposable,
  preempt(name: string, callback: (v: any) => void): IDisposable,
  // This is a flow hack to prevent emitting more than one value.
  // `EventEmitter` allows emitting any number of values - making this a land
  // mine, since we tend to think of `emit` as interchangeable.
  // This hack only works if the extra value is not `undefined`, so this isn't
  // full-proof, but it works for most cases.
  emit(name: string, value: any, ...no_extra_args_allowed: Array<void>): void,
}

declare class atom$Gutter {
  name: string,
  destroy(): void,
  decorateMarker(
    marker: atom$Marker,
    options?: {type?: string, 'class'?: string, item?: Object | HTMLElement},
  ): atom$Decoration,
  show(): void,
  hide(): void,
  onDidDestroy(callback: () => void): IDisposable,
}

declare type atom$MarkerChangeEvent = {
  oldHeadScreenPosition: atom$Point,
  newHeadScreenPosition: atom$Point,
  oldTailScreenPosition: atom$Point,
  newTailScreenPosition: atom$Point,

  oldHeadBufferPosition: atom$Point,
  newHeadBufferPosition: atom$Point,
  oldTailBufferPosition: atom$Point,
  newTailBufferPosition: atom$Point,

  isValid: boolean,
  textChanged: boolean,
}

declare class atom$Marker {
  destroy(): void,
  getBufferRange(): atom$Range,
  getStartBufferPosition(): atom$Point,
  onDidChange(callback: (event: atom$MarkerChangeEvent) => mixed): IDisposable,
  isValid(): boolean,
  isDestroyed(): boolean,
  onDidDestroy(callback: () => mixed): IDisposable,
  getScreenRange(): atom$Range,
  setBufferRange(
    range: atom$RangeLike,
    properties?: {reversed: boolean},
  ): void,
  id: number,
}

declare class atom$ServiceHub {
  provide<T>(keyPath: string, version: string, service: T): IDisposable,
  consume<T>(
    keyPath: string,
    versionRange: string,
    callback: (provider: T) => mixed
  ): IDisposable,
}

type atom$PackageMetadata = {
  name: string,
  version: string,
};

declare class atom$PackageManager {
  +initialPackagesActivated: boolean,

  // Event Subscription
  onDidLoadInitialPackages(callback: () => void): IDisposable,
  onDidActivateInitialPackages(callback: () => void): IDisposable,
  onDidActivatePackage(callback: (pkg: atom$Package) => mixed): IDisposable,
  onDidDeactivatePackage(callback: (pkg: atom$Package) => mixed): IDisposable,
  onDidLoadPackage(callback: (pkg: atom$Package) => mixed): IDisposable,
  onDidUnloadPackage(callback: (pkg: atom$Package) => mixed): IDisposable,
  onDidTriggerActivationHook(activationHook: string, callback: () => mixed): IDisposable,

  // Package system data
  getApmPath(): string,
  getPackageDirPaths(): Array<string>,

  // General package data
  resolvePackagePath(name: string): ?string,
  isBundledPackage(name: string): boolean,

  // Enabling and disabling packages
  enablePackage(name: string): ?atom$Package,
  disablePackage(name: string): ?atom$Package,
  isPackageDisabled(name: string): boolean,

  // Accessing active packages
  getActivePackage(name: string): ?atom$Package,
  getActivePackages(): Array<atom$Package>,
  isPackageActive(name: string): boolean,
  hasActivatedInitialPackages(): boolean,

  // Activating and deactivating packages
  activatePackage(name: string): Promise<atom$Package>,

  // Accessing loaded packages
  getLoadedPackage(name: string): ?atom$Package,
  getLoadedPackages(): Array<atom$Package>,
  isPackageLoaded(name: string): boolean,
  hasLoadedInitialPackages(): boolean,

  // Accessing available packages
  getAvailablePackageNames(): Array<string>,
  getAvailablePackageMetadata(): Array<atom$PackageMetadata>,

  // (Undocumented.)
  activate(): Promise<any>,
  deactivatePackages(): Promise<void>,
  deactivatePackage(name: string, suppressSerialization?: boolean): Promise<void>,
  emitter: atom$Emitter,
  loadedPackages: {[packageName: string]: atom$Package},
  loadPackage(name: string): void,
  loadPackages(): void,
  serializePackage(pkg: atom$Package): void,
  serviceHub: atom$ServiceHub,
  packageDirPaths: Array<string>,
  triggerActivationHook(hook: string): void,
  triggerDeferredActivationHooks(): void,
  unloadPackage(name: string): void,
  unloadPackages(): void,
}

declare class atom$StyleManager {
  // Event Subscription

  // Reading Style Elements
  getStyleElements(): Array<HTMLStyleElement>,

  // Paths
  getUserStyleSheetPath(): string,

  // (Undocumented.)
  addStyleSheet(
    source: string,
    params: {
      sourcePath?: string,
      context?: boolean,
      priority?: number,
      skipDeprecatedSelectorsTransformation?: boolean
    }
  ): IDisposable,
}

type atom$PaneSplitParams = {
  copyActiveItem?: boolean,
  items?: Array<Object>,
};

type atom$PaneSplitOrientation = 'horizontal' | 'vertical';
type atom$PaneSplitSide = 'before' | 'after';

// Undocumented class
declare class atom$applicationDelegate {
  focusWindow(): Promise<mixed>,
  open(params: {
    pathsToOpen: Array<string>,
    newWindow?: boolean,
    devMode?: boolean,
    safeMode?: boolean,
  }): void,

  // Used by nuclide-config to replicate atom.config saveCallback
  setUserSettings(config: atom$Config, configFilePath: string): Promise<mixed>;
}

type atom$PaneParams = {
  activeItem?: Object,
  applicationDelegate: atom$applicationDelegate,
  focused?: boolean,
  container: Object,
  config: atom$Config,
  notificationManager: atom$NotificationManager,
  deserializerManager: atom$DeserializerManager,
  items?: Array<Object>,
  itemStackIndices?: Array<number>,
  flexScale?: number,
};

declare class atom$Pane {
  // Items
  addItem(item: Object, options?: {index?: number, pending?: boolean}): Object,
  getItems(): Array<Object>,
  getActiveItem(): ?Object,
  itemAtIndex(index: number): ?Object,
  getActiveItemIndex(): number,
  activateItem(item: Object): ?Object,
  activateItemAtIndex(index: number): void,
  moveItemToPane(item: Object, pane: atom$Pane, index: number): void,
  destroyItem(item: Object, force?: boolean): Promise<boolean>,
  itemForURI(uri: string): Object,

  // Event subscriptions.
  onDidAddItem(cb: (event: {item: Object, index: number}) => void): IDisposable,
  onDidRemoveItem(cb: (event: {item: Object, index: number}) => void): IDisposable,
  onWillRemoveItem(cb: (event: {item: Object, index: number}) => void): IDisposable,
  onDidDestroy(cb: () => mixed): IDisposable,
  onDidChangeFlexScale(cb: (newFlexScale: number) => void): IDisposable,
  onWillDestroy(cb: () => void): IDisposable,
  observeActiveItem(cb: (item: ?Object) => void): IDisposable,

  // Lifecycle
  isActive(): boolean,
  activate(): void,
  destroy(): void,
  isDestroyed(): void,

  // Splitting
  splitLeft(params?: atom$PaneSplitParams): atom$Pane,
  splitRight(params?: atom$PaneSplitParams): atom$Pane,
  splitUp(params?: atom$PaneSplitParams): atom$Pane,
  splitDown(params?: atom$PaneSplitParams): atom$Pane,
  split(
    orientation: atom$PaneSplitOrientation,
    side: atom$PaneSplitSide,
    params?: atom$PaneSplitParams,
  ): atom$Pane,

  // Undocumented Methods
  constructor(params: atom$PaneParams): atom$Pane,
  getPendingItem(): atom$PaneItem,
  setPendingItem(item: atom$PaneItem): void,
  clearPendingItem(): void,
  getFlexScale(): number,
  getElement(): HTMLElement,
  getParent(): Object,
  removeItem(item: Object, moved: ?boolean): void,
  setActiveItem(item: Object): Object,
  setFlexScale(flexScale: number): number,
  getContainer(): atom$PaneContainer,

  element: HTMLElement,
}

declare interface atom$PaneItem {
  // These are all covariant, meaning that these props are read-only. Therefore we can assign an
  // object with more strict requirements to an variable of this type.
  +getTitle: () => string,
  +getLongTitle?: () => string,
  +getIconName?: () => string,
  +getURI?: () => ?string,
  +onDidChangeIcon?: (cb: (icon: string) => void) => IDisposable,
  +onDidChangeTitle?: (cb: (title: string) => void) => IDisposable,
  +onDidTerminatePendingState?: (() => mixed) => IDisposable;
  +serialize?: () => Object,
  +terminatePendingState?: () => void,
}

// Undocumented class
declare class atom$PaneAxis {
  getFlexScale(): number,
  setFlexScale(flexScale: number): number,
  getItems(): Array<Object>,
}

// Undocumented class
// Note that this is not the same object returned by `atom.workspace.getPaneContainers()`. (Those
// are typed here as AbstractPaneContainers and, in the current implementation, wrap these.)
declare class atom$PaneContainer {
  constructor({
    config: atom$Config,
    applicationDelegate: atom$applicationDelegate,
    notificationManager: atom$NotificationManager,
    deserializerManager: atom$DeserializerManager,
  }): atom$PaneContainer,
  destroy(): void,
  getActivePane(): atom$Pane,
  getActivePaneItem(): ?Object,
  getLocation(): atom$PaneLocation,
  getPanes(): Array<atom$Pane>,
  getPaneItems(): Array<atom$PaneItem>,
  observePanes(cb: (pane: atom$Pane) => void): IDisposable,
  onDidAddPane(cb: (event: {pane: atom$Pane}) => void): IDisposable,
  onDidDestroyPane(cb: (event: {pane: atom$Pane}) => void): IDisposable,
  onWillDestroyPane(cb: (event: {pane: atom$Pane}) => void): IDisposable,
  onDidAddPaneItem(cb: (item: atom$PaneItem) => void): IDisposable,
  onDidDestroyPaneItem(cb: (item: atom$Pane) => void): IDisposable,
  paneForItem(item: Object): ?atom$Pane,
  serialize(): Object,
}

declare class atom$Panel {
  // Construction and Destruction
  destroy(): void,

  // Event Subscription
  onDidChangeVisible(callback: (visible: boolean) => any): IDisposable,
  onDidDestroy(callback: (panel: atom$Panel) => any): IDisposable,

  // Panel Details
  getElement(): HTMLElement,
  getItem(): any,
  getPriority(): number,
  isVisible(): boolean,
  hide(): void,
  show(): void,
}

type atom$PointObject = {row: number, column: number};

type atom$PointLike = atom$Point
| [number, number]
| atom$PointObject;

declare class atom$Point {
  static fromObject(object: atom$PointLike, copy: ? boolean): atom$Point,
  constructor(row: number, column: number): void,
  row: number,
  column: number,
  copy(): atom$Point,
  negate(): atom$Point,

  // Comparison
  min(point1: atom$PointLike, point2: atom$PointLike): atom$Point,
  compare(other: atom$PointLike): -1 | 0 | 1,
  isEqual(otherRange: atom$PointLike): boolean,
  isLessThan(other: atom$PointLike): boolean,
  isLessThanOrEqual(other: atom$PointLike): boolean,
  isGreaterThan(other: atom$PointLike): boolean,
  isGreaterThanOrEqual(other: atom$PointLike): boolean,

  // Operations
  translate(other: atom$PointLike): atom$Point,

  // Conversion
  serialize(): [number, number],
  toArray(): [number, number],
}

type atom$RangeObject = {
  start: atom$PointObject,
  end: atom$PointObject,
};

type atom$RangeLike = atom$Range
  | atom$RangeObject // TODO: Flow doesn't really handle the real signature below...
  | [atom$PointLike, atom$PointLike]
  | {
    start: atom$PointLike,
    end: atom$PointLike,
  };

declare class atom$Range {
  static fromObject(
    object: atom$RangeLike,
    copy?: boolean,
  ): atom$Range,
  constructor(pointA: atom$PointLike, pointB: atom$PointLike): void,
  compare(other: atom$Range): number,
  start: atom$Point,
  end: atom$Point,
  isEmpty(): boolean,
  isEqual(otherRange: atom$RangeLike): boolean,
  intersectsWith(otherRange: atom$RangeLike, exclusive?: boolean): boolean,
  containsPoint(point: atom$PointLike, exclusive?: boolean): boolean,
  containsRange(other: atom$Range, exclusive?: boolean): boolean,
  union(other: atom$Range): atom$Range,
  serialize(): Array<Array<number>>,
  translate(startDelta: atom$PointLike, endDelta?: atom$PointLike): atom$Range,
  getRowCount(): number,
  getRows(): Array<number>,
}

type RawStatusBarTile = {
  item: HTMLElement,
  priority: number,
};

type atom$StatusBarTile = {
  getPriority(): number,
  getItem(): HTMLElement,
  destroy(): void,
};

declare class atom$ScopeDescriptor {
  constructor(object: {scopes: Array<string>}): void,
  getScopesArray(): Array<string>,
}

type atom$ScopeDescriptorLike = atom$ScopeDescriptor | Array<string>;

/**
 * This API is defined at https://github.com/atom/status-bar.
 */
declare class atom$StatusBar {
  addLeftTile(tile: RawStatusBarTile): atom$StatusBarTile,
  addRightTile(tile: RawStatusBarTile): atom$StatusBarTile,
  getLeftTiles(): Array<atom$StatusBarTile>,
  getRightTiles(): Array<atom$StatusBarTile>,
}

// https://github.com/atom/atom/blob/v1.9.0/src/text-editor-registry.coffee
declare class atom$TextEditorRegistry {
  add(editor: atom$TextEditor): IDisposable,
  remove(editor: atom$TextEditor): boolean,
  observe(callback: (editor: atom$TextEditor) => void): IDisposable,
  build: (params: atom$TextEditorParams) => atom$TextEditor,

  // Private
  editors: Set<atom$TextEditor>,
}

declare class atom$ThemeManager {
  // Event Subscription
  /**
   * As recent as Atom 1.0.10, the implementation of this method was:
   *
   * ```
   * onDidChangeActiveThemes: (callback) ->
   *   @emitter.on 'did-change-active-themes', callback
   *   @emitter.on 'did-reload-all', callback # TODO: Remove once deprecated pre-1.0 APIs are gone
   * ```
   *
   * Due to the nature of CoffeeScript, onDidChangeActiveThemes returns a Disposable even though it
   * is not documented as doing so. However, the Disposable that it does return removes the
   * subscription on the 'did-reload-all' event (which is supposed to be deprecated) rather than the
   * 'did-change-active-themes' one.
   */
  onDidChangeActiveThemes(callback: () => mixed): IDisposable,

  // Accessing Loaded Themes
  getLoadedThemeNames(): Array<string>,
  getLoadedThemes(): Array<mixed>, // TODO: Define undocumented ThemePackage class.

  // Accessing Active Themes
  getActiveThemeNames(): Array<string>,
  getActiveThemes(): Array<mixed>, // TODO: Define undocumented ThemePackage class.

  // Managing Enabled Themes
  getEnabledThemeNames(): Array<string>,

  // Private
  activateThemes(): Promise<any>,
  requireStylesheet(stylesheetPath: string): IDisposable,
}

type atom$TooltipsPlacementOption = 'top' | 'bottom' | 'left' | 'right' | 'auto';

type atom$TooltipsAddOptions = {
  title?: string,
  item?: HTMLElement,
  keyBindingCommand?: string,
  keyBindingTarget?: HTMLElement,
  animation?: boolean,
  container?: string | false,
  delay?: number | {show: number, hide: number},
  placement?: atom$TooltipsPlacementOption | () => atom$TooltipsPlacementOption,
  trigger?: string,
};

type atom$Tooltip = {
  show(): void;
  hide(): void;
  getTooltipElement(): HTMLElement,
};

declare class atom$TooltipManager {
  tooltips: Map<HTMLElement, Array<atom$Tooltip>>;
  add(
    target: HTMLElement,
    options: atom$TooltipsAddOptions,
  ): IDisposable,
  findTooltips(HTMLElement): Array<atom$Tooltip>,
}

type InsertTextOptions = {
  select: boolean,
  autoIndent: boolean,
  autoIndentNewline: boolean,
  autoDecreaseIndent: boolean,
  normalizeLineEndings: ?boolean,
  undo: string,
};

type DecorateMarkerParams = {
  type: 'line',
  class: string,
  onlyHead?: boolean,
  onlyEmpty?: boolean,
  onlyNonEmpty?: boolean,
} | {
  type: 'gutter',
  item?: HTMLElement,
  class?: string,
  onlyHead?: boolean,
  onlyEmpty?: boolean,
  onlyNonEmpty?: boolean,
  gutterName?: string,
} | {
  type: 'highlight',
  class?: string,
  gutterName?: string,
} | {
  type: 'overlay',
  item: Object,
  position?: 'head' | 'tail', // Defaults to 'head' when unspecified.
} | {
  type: 'block',
  item: HTMLElement,
  position?: 'before' | 'after', // Defaults to 'before' when unspecified.
} | {
  type: 'line-number',
  class?: string,
};

type ChangeCursorPositionEvent = {
  oldBufferPosition: atom$Point,
  oldScreenPosition: atom$Point,
  newBufferPosition: atom$Point,
  newScreenPosition: atom$Point,
  textChanged: boolean,
  cursor: atom$Cursor,
};

type MarkerOptions = {
  reversed?: boolean,
  tailed?: boolean,
  invalidate?: 'never' | 'surround' | 'overlap' | 'inside' | 'touch',
  exclusive?: boolean,
};

type atom$ChangeSelectionRangeEvent = {|
  oldBufferRange: atom$Range,
  oldScreenRange: atom$Range,
  newBufferRange: atom$Range,
  newScreenRange: atom$Range,
  selection: atom$Selection,
|};

declare class atom$TextEditor extends atom$Model {
  id: number,
  verticalScrollMargin: number,

  // Event Subscription
  onDidChange(callback: () => void): IDisposable,
  onDidChangePath(callback: (newPath: string) => mixed): IDisposable,
  onDidStopChanging(callback: () => mixed): IDisposable,
  onDidChangeCursorPosition(callback: (event: ChangeCursorPositionEvent) => mixed):
    IDisposable,
  onDidAddCursor(callback: (cursor: atom$Cursor) => mixed): IDisposable;
  onDidRemoveCursor(callback: (cursor: atom$Cursor) => mixed): IDisposable;
  onDidDestroy(callback: () => mixed): IDisposable,
  onDidSave(callback: (event: {path: string}) => mixed): IDisposable,
  getBuffer(): atom$TextBuffer,
  observeGrammar(callback: (grammar: atom$Grammar) => mixed): IDisposable,
  onWillInsertText(callback: (event: {cancel: () => void, text: string}) => void):
      IDisposable,
  // Note that the range property of the event is undocumented.
  onDidInsertText(callback: (event: {text: string, range: atom$Range}) => mixed): IDisposable,
  onDidChangeSoftWrapped(callback: (softWrapped: boolean) => mixed): IDisposable,
  onDidChangeSelectionRange(callback: (event: atom$ChangeSelectionRangeEvent) => mixed): IDisposable,
  observeSelections(callback: (selection: atom$Selection) => mixed): IDisposable,

  // File Details
  getTitle: () => string,
  getLongTitle(): string,
  /**
   * If you open Atom via Spotlight such that it opens with a tab named
   * "untitled" that does not correspond to a file on disk, this will return
   * null.
   */
  getPath(): ?string,
  getURI: () => ?string,
  insertNewline(): void,
  isModified: () => boolean,
  isEmpty(): boolean,
  getEncoding(): buffer$Encoding,
  setEncoding(encoding: string): void,
  getTabLength() : number,
  getSoftTabs(): boolean,
  getIconName(): string,
  onDidChangeIcon(cb: (icon: string) => void): IDisposable,
  onDidChangeTitle(cb: (title: string) => void): IDisposable,

  // File Operations
  save(): Promise<void>,
  // DO NOT USE: Doesn't work with remote text buffers!
  // saveAs(filePath: string): void,

  // Reading Text
  getText(): string,
  getTextInBufferRange(range: atom$RangeLike): string,
  getLineCount(): number,
  getScreenLineCount(): number,
  getLastScreenRow(): number,

  // Mutating Text
  setText(text: string, options?: InsertTextOptions): void,
  setTextInBufferRange(
    range: atom$RangeLike,
    text: string,
    options?: {
      normalizeLineEndings?: boolean,
      undo?: string,
    },
  ): atom$Range,
  insertText(text: string): Array<atom$Range> | false,
  mutateSelectedText(fn: (selection: atom$Selection, index: number) => void): void,
  delete: () => void,
  backspace: () => void,
  duplicateLines: () => void,

  // History
  createCheckpoint(): atom$TextBufferCheckpoint,
  revertToCheckpoint(checkpoint: atom$TextBufferCheckpoint): boolean,
  terminatePendingState(): void,
  transact(fn: () => mixed, _: void): void,
  transact(groupingInterval: number, fn: () => mixed): void,
  onDidTerminatePendingState(() => mixed): IDisposable;

  // TextEditor Coordinates
  screenPositionForBufferPosition(
    bufferPosition: atom$PointLike,
    options?: {
      wrapBeyondNewlines?: boolean,
      wrapAtSoftNewlines?: boolean,
      screenLine?: boolean,
    },
  ): atom$Point,
  bufferPositionForScreenPosition(
    screenPosition: atom$PointLike,
    options?: {
      wrapBeyondNewlines?: boolean,
      wrapAtSoftNewlines?: boolean,
      screenLine?: boolean,
    },
  ): atom$Point,
  getEofBufferPosition(): atom$Point,
  getVisibleRowRange(): [number, number],
  bufferRowForScreenRow(screenRow: number): number,
  screenRangeForBufferRange(bufferRange: atom$RangeLike): atom$Range,

  // Decorations
  decorateMarker(marker: atom$Marker, decorationParams: DecorateMarkerParams): atom$Decoration,
  decorateMarkerLayer(
    markerLayer: atom$DisplayMarkerLayer,
    decorationParams: DecorateMarkerParams,
  ): atom$LayerDecoration,
  decorationsForScreenRowRange(
    startScreenRow: number,
    endScreenRow: number,
  ): {[markerId: string]: Array<Object>},
  getDecorations(options?: {class?: string, type?: string}): Array<atom$Decoration>,

  // Markers
  addMarkerLayer(): atom$DisplayMarkerLayer,
  getDefaultMarkerLayer(): atom$DisplayMarkerLayer,
  markBufferPosition(position: atom$PointLike, options?: MarkerOptions): atom$Marker,
  markBufferRange(range: atom$RangeLike, options?: MarkerOptions): atom$Marker,
  markScreenRange(range: atom$RangeLike, options?: MarkerOptions): atom$Marker,
  markScreenPosition(position: atom$PointLike, options?: MarkerOptions): atom$Marker,
  findMarkers(options: MarkerOptions): Array<atom$Marker>,
  getMarkerCount(): number,

  // Cursors
  getCursors(): Array<atom$Cursor>,
  setCursorBufferPosition(
    position: atom$PointLike,
    options?: {
      autoscroll?: boolean,
      wrapBeyondNewlines?: boolean,
      wrapAtSoftNewlines?: boolean,
      screenLine?: boolean,
    }): void,
  getCursorBufferPosition(): atom$Point,
  getCursorBufferPositions(): Array<atom$Point>,
  getCursorScreenPosition(): atom$Point,
  getCursorScreenPositions(): Array<atom$Point>,
  getLastCursor(): atom$Cursor,
  addCursorAtBufferPosition(point: atom$PointLike): atom$Cursor,
  moveToBeginningOfLine(): void,
  moveToEndOfLine(): void,
  moveToBottom(): void,

  // Folds
  foldCurrentRow(): void,
  unfoldCurrentRow(): void,
  foldBufferRow(bufferRow: number): void,
  unfoldBufferRow(bufferRow: number): void,

  // Selections
  getSelectedText(): string,
  selectAll(): void,
  getSelectedBufferRange(): atom$Range,
  getSelectedBufferRanges(): Array<atom$Range>,
  getSelections(): Array<atom$Selection>,
  selectToBufferPosition(point: atom$Point): void,
  setSelectedBufferRange(
    bufferRange: atom$RangeLike,
    options?: {
      reversed?: boolean,
      preserveFolds?: boolean,
    },
  ): void,
  setSelectedBufferRanges(
    bufferRanges: Array<atom$Range>,
    options?: {
      reversed?: boolean,
      preserveFolds?: boolean,
    },
  ): void,
  selectWordsContainingCursors(): void,

  // Folds
  unfoldAll(): void,

  // Searching and Replacing
  scanInBufferRange(
    regex: RegExp,
    range: atom$Range,
    iterator: (foundMatch: {
      match: mixed,
      matchText: string,
      range: atom$Range,
      stop: () => mixed,
      replace: (replaceWith: string) => mixed,
    }) => mixed
  ): void,

  scan(
    regex: RegExp,
    iterator: (foundMatch: {
      match: mixed,
      matchText: string,
      range: atom$Range,
      stop: () => mixed,
      replace: (replaceWith: string) => mixed,
    }) => mixed
  ): void,

  // Tab Behavior
  // Soft Wrap Behavior
  // Indentation
  indentationForBufferRow(bufferRow: number): number,
  setTabLength(tabLength: number): void,
  setSoftTabs(softTabs: boolean): void,

  lineTextForBufferRow(bufferRow: number): string,
  lineTextForScreenRow(screenRow: number): string,

  // Grammars
  getGrammar(): atom$Grammar,
  setGrammar(grammar: ?atom$Grammar): void,

  // Clipboard Operations
  pasteText: (options?: Object) => void,
  copySelectedText: () => void,

  // Managing Syntax Scopes
  getRootScopeDescriptor(): atom$ScopeDescriptor,
  scopeDescriptorForBufferPosition(
    bufferPosition: atom$PointLike,
  ): atom$ScopeDescriptor,

  // Gutter
  addGutter(options: {
    name: string,
    priority?: number,
    visible?: boolean,
  }): atom$Gutter,
  observeGutters(callback: (gutter: atom$Gutter) => void): IDisposable,
  getGutters(): Array<atom$Gutter>,
  gutterWithName(name: string): ?atom$Gutter,

  // Scrolling the TextEditor
  scrollToBufferPosition(
    position: atom$Point | [?number, ?number],
    options?: {center?: boolean}
  ): void,
  scrollToScreenPosition(
    position: atom$Point | [?number, ?number],
    options?: {center?: boolean}
  ): void,
  scrollToScreenRange(screenRange: atom$Range, options?: {clip?: boolean}): void,
  scrollToCursorPosition(
    options?: {center?: boolean}
  ): void,
  scrollToBottom(): void,
  scrollToTop(): void,

  // TextEditor Rendering
  getPlaceholderText(): string,
  setPlaceholderText(placeholderText: string): void,

  // This is undocumented, but Nuclide uses it in the AtomTextEditor wrapper.
  setLineNumberGutterVisible(lineNumberGutterVisible: boolean): void,

  // Editor Options
  setSoftWrapped(softWrapped: boolean): void,

  isFoldedAtBufferRow(row: number): boolean,
  getLastBufferRow(): number,

  // Undocumented Methods
  getElement(): atom$TextEditorElement,
  getDefaultCharWidth(): number,
  getLineHeightInPixels(): number,
  moveToTop(): void,
  tokenForBufferPosition(position: atom$Point | [?number, ?number]): atom$Token,
  onDidConflict(callback: () => void): IDisposable,
  serialize(): Object,
  foldBufferRowRange(startRow: number, endRow: number): void,
  getNonWordCharacters(position?: atom$PointLike): string,
  scheduleComponentUpdate(): void,
}

/**
 * This is not part of the official Atom 1.0 API. Nevertheless, we need to reach into this object
 * via `atom$TextEditorElement` to do some things that we have no other way to do.
 */
declare class atom$TextEditorComponent {
  domNode: HTMLElement,
  scrollViewNode: HTMLElement,
  presenter: atom$TextEditorPresenter,
  refs: atom$TextEditorComponentRefs,
  linesComponent: atom$LinesComponent,
  // NOTE: This is typed as a property to allow overwriting.
  startCursorBlinking: () => void,
  stopCursorBlinking(): void,
  pixelPositionForScreenPosition(
    screenPosition: atom$Point,
    clip?: boolean,
  ): {top: number, left: number},
  screenPositionForMouseEvent(event: MouseEvent): atom$Point,
  pixelPositionForMouseEvent(
    event: MouseEvent,
    linesClientRect?: {top: number, left: number, bottom: number, right: number},
  ): {top: number, left: number, bottom: number, right: number},
  invalidateBlockDecorationDimensions(decoration: atom$Decoration): void,
  element: atom$TextEditorElement,
  didFocus(): void,
  setScrollTop(scrollTop: number): void,
  getScrollTop(): number,

  setScrollLeft(scrollLeft: number): void,
  getScrollLeft(): number,
  updateSync(useScheduler?: boolean): void,
}

/**
 * This is not part of the official Atom 1.0 API. Nevertheless, we need to reach into this object
 * via `atom$TextEditorComponent` to do some things that we have no other way to do.
 */
declare class atom$TextEditorPresenter {
  startBlinkingCursors: () => void,
  stopBlinkingCursors(visible: boolean): void,
  updateLineNumberGutterState(): void,
}

/**
 * This is not part of the official Atom 1.0 API. Nevertheless, we need it to access
 * the deepest dom element receiving DOM events.
 */
declare class atom$LinesComponent {
  domNode: HTMLElement,
  getDomNode(): HTMLElement,
}

/**
 * This is not part of the official Atom 1.0 API. Nevertheless, we need it to access
 * the deepest dom element receiving DOM events.
 */
declare class atom$TextEditorComponentRefs {
  lineTiles: HTMLElement,
}

/**
 * This is not part of the official Atom 1.0 API, but it really should be. This is the element that
 * is returned when you run `atom.views.getView(<TextEditor>)`.
 */
declare class atom$TextEditorElement extends HTMLElement {
  component: ?atom$TextEditorComponent,
  getModel(): atom$TextEditor,
  setModel(model: atom$TextEditor): void,
  pixelPositionForBufferPosition(
    bufferPosition: atom$PointLike,
  ): {top: number, left: number},
  pixelPositionForScreenPosition(screenPosition: atom$Point): {
    left: number,
    top: number,
  },

  setScrollTop(scrollTop: number): void,
  getScrollTop(): number,

  setScrollLeft(scrollLeft: number): void,
  getScrollLeft(): number,

  getScrollHeight(): number,
  getHeight(): number,

  onDidChangeScrollTop(callback: (scrollTop: number) => mixed): IDisposable,
  onDidChangeScrollLeft(callback: (scrollLeft: number) => mixed): IDisposable,

  // Called when the editor is attached to the DOM.
  onDidAttach(callback: () => mixed): IDisposable,
  // Called when the editor is detached from the DOM.
  onDidDetach(callback: () => mixed): IDisposable,

  measureDimensions(): void,

  // Undocumented Methods

  // Returns a promise that resolves after the next update.
  getNextUpdatePromise(): Promise<void>,

  // `undefined` means no explicit width. `null` sets a zero width (which is almost certainly a
  // mistake) so we don't allow it.
  setWidth(width: number | void): void,
}

declare class atom$ViewProvider {
  modelConstructor: Function,
}

declare class atom$ViewRegistry {
  // Methods
  addViewProvider(
    modelConstructor: any,
    createView?: (...args: Array<any>) => ?HTMLElement
  ): IDisposable,
  getView(textEditor: atom$TextEditor): atom$TextEditorElement,
  getView(notification: atom$Notification): HTMLElement,
  getView(gutter: atom$Gutter): HTMLElement,
  getView(panel: atom$Panel): HTMLElement,
  getView(workspace: atom$Workspace): HTMLElement,
  getView(object: Object): HTMLElement,
  providers: Array<atom$ViewProvider>,
}

type atom$WorkspaceAddPanelOptions = {
  item: Object,
  visible?: boolean,
  priority?: number,
  className?: string,
};

type atom$TextEditorParams = {
  buffer?: atom$TextBuffer,
  lineNumberGutterVisible?: boolean,
};

type DestroyPaneItemEvent = {
  item: atom$PaneItem,
  pane: atom$Pane,
  index: number,
};

type AddPaneItemEvent = {
  item: atom$PaneItem,
  pane: atom$Pane,
  index: number,
};

type OnDidOpenEvent = {
  uri: string,
  item: mixed,
  pane: atom$Pane,
  index: number,
};

type AddTextEditorEvent = {
  textEditor: atom$TextEditor,
  pane: atom$Pane,
  index: number,
};

type atom$WorkspaceOpenOptions = {
  activatePane?: ?boolean,
  activateItem?: ?boolean,
  initialLine?: ?number,
  initialColumn?: ?number,
  pending?: ?boolean,
  split?: ?string,
  searchAllPanes?: ?boolean,
  location?: atom$PaneLocation,
}

declare class atom$Workspace {
  // Event Subscription
  observePanes(cb: (pane: atom$Pane) => void): IDisposable,
  observeTextEditors(callback: (editor: atom$TextEditor) => mixed): IDisposable,
  observeActiveTextEditor(callback: (editor: ?atom$TextEditor) => mixed): IDisposable,
  onDidAddTextEditor(callback: (event: AddTextEditorEvent) => mixed): IDisposable,
  onDidChangeActivePaneItem(callback: (item: mixed) => mixed): IDisposable,
  onDidDestroyPaneItem(callback: (event: DestroyPaneItemEvent) => mixed): IDisposable,
  onDidAddPaneItem(callback: (event: AddPaneItemEvent) => mixed): IDisposable,
  observeActivePaneItem(callback: (item: ?mixed) => mixed): IDisposable,
  onDidStopChangingActivePaneItem(callback: (item: ?mixed) => mixed): IDisposable,
  observePaneItems(callback: (item: mixed) => mixed): IDisposable,
  onWillDestroyPaneItem(
    callback: (event: {item: mixed, pane: mixed, index: number}) => mixed
  ): IDisposable,
  onDidOpen(callback: (event: OnDidOpenEvent) => mixed): IDisposable,

  getElement(): HTMLElement,

  // Opening
  open(
    uri?: string,
    options?: atom$WorkspaceOpenOptions,
  ): Promise<atom$TextEditor>,
  openURIInPane(
    uri?: string,
    pane: atom$Pane,
    options?: {
      initialLine?: number,
      initialColumn?: number,
      activePane?: boolean,
      searchAllPanes?: boolean,
    }
  ): Promise<atom$TextEditor>,
  isTextEditor(item: ?mixed): boolean,
  /* Optional method because this was added post-1.0. */
  buildTextEditor: ((params: ?atom$TextEditorParams) => atom$TextEditor),
  /* Optional method because this was added in 1.9.0 */
  handleGrammarUsed?: (grammar: atom$Grammar) => void,
  reopenItem(): Promise<?atom$TextEditor>,
  addOpener(callback: (uri: string) => any): IDisposable,
  hide(uriOrItem: string | Object): void,
  toggle(uriOrItem: string | Object): void,

  // Pane Containers
  getPaneContainers(): Array<atom$AbstractPaneContainer>,
  paneContainerForItem(item: ?mixed): ?atom$AbstractPaneContainer,

  // Pane Items
  getPaneItems(): Array<Object>,
  getActivePaneItem(): ?Object,
  getActivePaneContainer(): atom$PaneContainer,
  getTextEditors(): Array<atom$TextEditor>,
  getActiveTextEditor(): ?atom$TextEditor,
  createItemForURI(uri: string, options: atom$WorkspaceOpenOptions): atom$PaneItem,

  // Panes
  getPanes(): Array<atom$Pane>,
  getActivePane(): atom$Pane,
  activateNextPane(): boolean,
  activatePreviousPane(): boolean,
  paneForURI(uri: string): atom$Pane,
  paneForItem(item: mixed): ?atom$Pane,
  paneContainers: {[location: atom$PaneLocation]: atom$AbstractPaneContainer},

  // Panels
  panelContainers: {[location: string]: atom$PanelContainer},
  getBottomPanels(): Array<atom$Panel>,
  addBottomPanel(options: atom$WorkspaceAddPanelOptions): atom$Panel,
  getLeftPanels(): Array<atom$Panel>,
  addLeftPanel(options: atom$WorkspaceAddPanelOptions): atom$Panel,
  getRightPanels(): Array<atom$Panel>,
  addRightPanel(options: atom$WorkspaceAddPanelOptions): atom$Panel,
  getTopPanels(): Array<atom$Panel>,
  addTopPanel(options: atom$WorkspaceAddPanelOptions): atom$Panel,
  getModalPanels(): Array<atom$Panel>,
  addModalPanel(options: atom$WorkspaceAddPanelOptions): atom$Panel,
  getHeaderPanels(): Array<atom$Panel>,
  addHeaderPanel(options: atom$WorkspaceAddPanelOptions): atom$Panel,

  getLeftDock(): atom$Dock,
  getRightDock(): atom$Dock,
  getBottomDock(): atom$Dock,
  getCenter(): atom$WorkspaceCenter,

  // Searching and Replacing
  scan(
    regex: RegExp,
    options: {
      paths?: Array<string>,
      onPathsSearched?: (numSearched: number) => mixed,
      leadingContextLineCount?: number,
      trailingContextLineCount?: number,
    },
    iterator: (
      ?{
        filePath: string,
        matches: Array<{
          leadingContextLines: Array<string>,
          lineText: string,
          lineTextOffset: number,
          range: atom$RangeLike,
          matchText: string,
          trailingContextLines: Array<string>,
        }>,
      },
      Error,
    ) => mixed,
  ): Promise<?string>,

  destroyActivePaneItemOrEmptyPane(): void,
  destroyActivePaneItem(): void,
}

declare class atom$AbstractPaneContainer {
  activate(): void,
  getLocation(): atom$PaneLocation,
  getElement(): HTMLElement,
  isVisible(): boolean,
  show(): void,
  hide(): void,
  getActivePane(): atom$Pane,
  getPanes(): Array<atom$Pane>,
  onDidAddPaneItem((item: {item: Object}) => void): IDisposable,
  observePanes(cb: (pane: atom$Pane) => void): IDisposable,
  state: {
    size: number,
  }
}

declare class atom$Dock extends atom$AbstractPaneContainer {
  // This is a woefully incomplete list, items can be added as needed from
  // https://github.com/atom/atom/blob/master/src/dock.js
  toggle(): void,
}

declare class atom$WorkspaceCenter extends atom$AbstractPaneContainer {
  activate(): void;

  // Pane Items
  getPaneItems(): Array<atom$PaneItem>,
  getActivePaneItem(): ?atom$PaneItem,
  getTextEditors(): Array<atom$TextEditor>,
  getActiveTextEditor(): ?atom$TextEditor,

  observeActivePaneItem(callback: atom$PaneItem => mixed): IDisposable;
  onDidChangeActivePaneItem(callback: (item: mixed) => mixed): IDisposable;
  onDidAddTextEditor(
    callback: (item: {
      textEditor: atom$TextEditor,
      pane: atom$Pane,
      index: number,
    }) => mixed,
  ): IDisposable;
  // This should be removed soon anyway, it's currently deprecated.
  paneContainer: Object;
}

/**
 * Extended Classes
 */

declare class atom$BufferedNodeProcess { }

declare class atom$BufferedProcess {
  // Event Subscription
  onWillThrowError(
    callback: (errorObject: {error: Object, handle: mixed}) => mixed
  ): IDisposable,
  // Helper Methods
  kill(): void,
}

declare class atom$Clipboard {
  // Methods
  write(text: string, metadata?: mixed): void,
  read(): string,
  readWithMetadata(): {
    metadata: ?mixed,
    text: string,
  },
}

declare class atom$ContextMenuManager {
  add(itemsBySelector: {[cssSelector: string]: Array<atom$ContextMenuItem>}): IDisposable,
  itemSets: Array<atom$ContextMenuItemSet>,

  // Undocumented methods
  showForEvent(event: Event): void,
  templateForEvent(event: Event): Array<Object>,
}

declare class atom$ContextMenuItemSet {
  items: Array<atom$ContextMenuItem>,
  selector: string,
}

type atom$ContextMenuItem = {
  command?: string,
  created?: (event: MouseEvent) => void,
  enabled?: boolean,
  label?: string,
  shouldDisplay?: (event: MouseEvent) => boolean,
  submenu?: Array<atom$ContextMenuItem>,
  type?: string,
  visible?: boolean,
};

type atom$Deserializer = {
  name: string,
  deserialize: (state: Object) => mixed,
};

declare class atom$DeserializerManager {
  add(...deserializers: Array<atom$Deserializer>): IDisposable,
  deserialize(state: Object, params?: Object): mixed,
}

// Apparently it can sometimes include a `code` property.
declare class atom$GetEntriesError extends Error {
  code?: string,
}

declare class atom$Directory {
  constructor(dirname?: string): atom$Directory,

  symlink: boolean,

  // Construction
  create(mode?: number): Promise<boolean>,

  // Event Subscription
  onDidChange(callback: () => mixed): IDisposable,

  // Directory Metadata
  isFile(): boolean,
  isDirectory(): boolean,
  exists():Promise<boolean>,

  // Managing Paths
  getPath(): string,
  getBaseName(): string,
  relativize(fullPath: string): string,

  // Event Subscription
  onDidRename(callback: () => void): IDisposable,
  onDidDelete(callback: () => void): IDisposable,

  // Traversing
  getParent(): atom$Directory,
  getFile(filename: string): atom$File,
  getSubdirectory(dirname: string): atom$Directory,
  getEntries(
    callback: (
      error: ?atom$GetEntriesError,
      entries: ?Array<atom$Directory | atom$File>,
    ) => mixed): void,
  contains(path: string): boolean,
}

// These are the methods called on a file by atom-text-buffer
interface atom$Fileish {
  existsSync(): boolean,
  setEncoding(encoding: string): void,
  getEncoding(): ?string,

  onDidRename(callback: () => void): IDisposable,
  onDidDelete(callback: () => void): IDisposable,
  onDidChange(callback: () => void): IDisposable,
  onWillThrowWatchError(callback: () => mixed): IDisposable,

  getPath(): string,
  getBaseName(): string,

  createReadStream(): stream$Readable,
  createWriteStream(): stream$Writable,
}

declare class atom$File /* implements atom$Fileish */ {
  constructor(filePath?: string, symlink?: boolean): atom$File,

  symlink: boolean,

  // Construction
  create(): Promise<boolean>,

  // File Metadata
  isFile(): boolean,
  isDirectory(): boolean,
  exists(): Promise<boolean>,
  existsSync(): boolean,
  setEncoding(encoding: string): void,
  getEncoding(): string,

  // Event Subscription
  onDidRename(callback: () => void): IDisposable,
  onDidDelete(callback: () => void): IDisposable,
  onDidChange(callback: () => void): IDisposable,
  onWillThrowWatchError(callback: () => mixed): IDisposable,

  // Managing Paths
  getPath(): string,
  getBaseName(): string,

  // Traversing
  getParent(): atom$Directory,

  // Reading and Writing
  read(flushCache?: boolean): Promise<string>,
  write(text: string): Promise<void>,
  writeSync(text: string): void,
  createReadStream(): stream$Readable,
  createWriteStream(): stream$Writable,
}

declare class atom$GitRepository extends atom$Repository {
  // Unofficial API.
  statuses: {[filePath: string]: number},
  // Return the `git-utils` async repo.
  getRepo(): atom$GitRepositoryInternal,
}

declare class atom$Grammar {
  name: string,
  scopeName: string,
  tokenizeLines(text: string): Array<Array<atom$GrammarToken>>,
  tokenizeLine(line: string, ruleStack: mixed, firstLine: boolean): {
    line: string,
    tags: Array<number>,
    // Dynamic property: invoking it will incur additional overhead
    tokens: Array<atom$GrammarToken>,
    ruleStack: mixed
  },
}

type atom$GrammarToken = {
  value: string,
  scopes: Array<string>,
};

declare class atom$GrammarRegistry {
  // Event Subscription
  onDidAddGrammar(callback: (grammar: atom$Grammar) => void): IDisposable,

  // Managing Grammars
  grammarForScopeName(scopeName: string): ?atom$Grammar,
  removeGrammarForScopeName(scopeName: string): ?atom$Grammar,
  loadGrammarSync(grammarPath: string): atom$Grammar,
  selectGrammar(filePath: string, fileContents: string): atom$Grammar,
  autoAssignLanguageMode(buffer: atom$TextBuffer): void,
  assignLanguageMode(buffer: atom$TextBuffer, languageId: string): void,

  // Extended
  getGrammarScore(grammar: atom$Grammar, filePath: string, contents?: string): number,
  forEachGrammar(callback: (grammar: atom$Grammar) => mixed): void,

  // Private API
  clear(): IDisposable,
}

declare class atom$HistoryManager {
  removeProject(paths: Array<string>): void,
  getProjects(): Array<atom$HistoryProject>,
}

declare class atom$HistoryProject {
  paths: Array<string>;
  lastOpened: Date;
}

// https://github.com/atom/atom-keymap/blob/18f00ac307de5770bb8f98958bd9a13ecffa9e68/src/key-binding.coffee
declare class atom$KeyBinding {
  cachedKeyups: ?Array<string>,
  command: string,
  index: number,
  keystrokeArray : Array<string>,
  keystrokeCount: number,
  keystrokes: string,
  priority: number,
  selector: string,
  source: string,
  specificity: number,

  matches(keystroke: string): boolean,
  compare(keybinding: atom$KeyBinding): number,
  getKeyups(): ?Array<string>,
  matchesKeystrokes(userKeystrokes: Array<string>): boolean | 'exact' | 'partial' | 'pendingKeyup',
}

declare class atom$KeymapManager {
  // Event Subscription
  onDidMatchBinding(callback: (event: {
    keystrokes: string,
    binding: atom$KeyBinding,
    keyboardEventTarget: HTMLElement,
  }) => mixed): IDisposable,

  onDidPartiallyMatchBinding(callback: (event: {
    keystrokes: string,
    partiallyMatchedBindings: atom$KeyBinding,
    keyboardEventTarget: HTMLElement,
  }) => mixed): IDisposable,

  onDidFailToMatchBinding(callback: (event: {
    keystrokes: string,
    partiallyMatchedBindings: atom$KeyBinding,
    keyboardEventTarget: HTMLElement,
  }) => mixed): IDisposable,

  onDidFailToReadFile(callback: (error: {
    message: string,
    stack: string,
  }) => mixed): IDisposable,

  // Adding and Removing Bindings
  add(source: string, bindings: Object): IDisposable,
  removeBindingsFromSource(source: string): void,

  // Accessing Bindings
  getKeyBindings(): Array<atom$KeyBinding>,
  findKeyBindings(params: {
    keystrokes?: string,
    command?: string,
    target?: HTMLElement,
  }): Array<atom$KeyBinding>,

  // Managing Keymap Files
  loadKeymap(path: string, options?: {watch: boolean}): void,
  watchKeymap(path: string): void,

  // Managing Keyboard Events
  handleKeyboardEvent(event: Event): void,
  keystrokeForKeyboardEvent(event: Event): string,
  getPartialMatchTimeout(): number,

  static buildKeydownEvent(
    key: string,
    options: {
      target: HTMLElement,
      alt?: boolean,
      cmd?: boolean,
      ctrl?: boolean,
      shift?: boolean,
    },
  ): Event,
}

declare class atom$MenuManager {
  add(items: Array<Object>): IDisposable,
  update(): void,

  // Private API
  template: Array<Object>,
}

type atom$ProjectSpecification = {
  originPath: string,
  paths?: Array<string>,
  config?: {[string]: mixed}
};

declare class atom$Project {
  // Event Subscription
  onDidChangePaths(callback: (projectPaths: Array<string>) => mixed): IDisposable,
  onDidAddBuffer(callback: (buffer: atom$TextBuffer) => mixed): IDisposable,
  onDidReplace((settings: atom$ProjectSpecification) => mixed): IDisposable,
  observeBuffers(callback: (buffer: atom$TextBuffer) => mixed): IDisposable,
  replace?: (newSettings: atom$ProjectSpecification) => void,
  // Accessing the git repository
  getRepositories(): Array<?atom$Repository>,
  repositoryForDirectory(directory: atom$Directory): Promise<?atom$Repository>,

  // Managing Paths
  getPaths(): Array<string>,
  addPath(projectPath: string, options?: {
    emitEvent?: boolean,
    exact?: boolean,
    mustExist?: boolean,
  }): void,
  setPaths(paths: Array<string>): void,
  removePath(projectPath: string): void,
  getDirectories(): Array<atom$Directory>,
  relativizePath(relativizePath?: string): Array<string>, // [projectPath: ?string, relativePath: string]
  relativize(filePath: string): string,
  contains(path: string): boolean,

  // Private API
  findBufferForPath(path: string): ?atom$TextBuffer,
  addBuffer(buffer: atom$TextBuffer): void,
  removeBuffer(buffer: atom$TextBuffer): void,
  getBuffers(): Array<atom$TextBuffer>,
}

type TextBufferScanIterator = (arg: {
  match: Array<string>,
  matchText: string,
  range: atom$Range,
  stop(): void,
  replace(replacement: string): void,
}) => void;

// This happens to be a number but it would be better if the type could be entirely opaque. All you
// need to know is that if something needs a checkpoint you should only pass it values received from
// TextBuffer::createCheckpoint
type atom$TextBufferCheckpoint = number;

// TextBuffer did-change/will-change
type atom$TextEditEvent = {
  oldRange: atom$Range,
  newRange: atom$Range,
  oldText: string,
  newText: string,
};

type atom$AggregatedTextEditEvent = {
  changes: Array<atom$TextEditEvent>,
};

declare class atom$LanguageMode {
  getLanguageId(): string,
}

declare class atom$TextBuffer {
  constructor(text?: string): atom$TextBuffer,
  constructor(params?: {
    filePath?: string,
    text?: string,
  }): atom$TextBuffer,

  file: ?atom$File,

  // Mixin
  static deserialize: (state: Object, params: Object) => mixed,
  static load: (file: string | atom$Fileish, params: Object) => Promise<atom$TextBuffer>,

  setFile(file: atom$Fileish): void,

  // Events
  onWillChange(callback: () => mixed): IDisposable,
  onDidChangeText(callback: (event: atom$AggregatedTextEditEvent) => mixed): IDisposable,
  onDidStopChanging(callback: () => mixed): IDisposable,
  onDidConflict(callback: () => mixed): IDisposable,
  onDidChangeModified(callback: () => mixed): IDisposable,
  onDidUpdateMarkers(callback: () => mixed): IDisposable,
  onDidCreateMarker(callback: () => mixed): IDisposable,
  onDidChangePath(callback: () => mixed): IDisposable,
  onDidChangeEncoding(callback: () => mixed): IDisposable,
  onWillSave(callback: () => mixed): IDisposable,
  onDidSave(callback: (event: {path: string}) => mixed): IDisposable,
  onDidDelete(callback: () => mixed): IDisposable,
  onWillReload(callback: () => mixed): IDisposable,
  onDidReload(callback: () => mixed): IDisposable,
  onDidDestroy(callback: () => mixed): IDisposable,
  onWillThrowWatchError(callback: () => mixed): IDisposable,

  // File Details
  // DO NOT USE (T21363106): Doesn't work with remote text buffers!
  // setPath(filePath: string): void,
  getPath(): ?string,
  setEncoding(encoding: string): void,
  getEncoding(): string,
  getUri(): string,
  getId(): string,
  getLanguageMode(): atom$LanguageMode,

  // Reading Text
  isEmpty(): boolean,
  getText(): string,
  getTextInRange(range: atom$RangeLike): string,
  getLineCount(): number,
  getLines(): Array<string>,
  getLastLine(): string,
  lineForRow(row: number): string,
  lineEndingForRow(row: number): string,
  lineLengthForRow(row: number): number,
  isRowBlank(row: number): boolean,
  previousNonBlankRow(startRow: number): ?number,
  nextNonBlankRow(startRow: number): ?number,

  // Mutating Text
  setText: (text: string) => atom$Range,
  setTextInRange(range: atom$RangeLike, text: string, options?: Object): atom$Range,
  setTextViaDiff(text: string): void,
  insert(
    position: atom$Point,
    text: string,
    options?: {
      normalizeLineEndings?: boolean,
      undo?: string,
    },
  ): atom$Range,
  append(text: string, options: ?{
    normalizeLineEndings?: boolean,
    undo?: string,
  }): atom$Range,
  delete(range: atom$Range): atom$Range,
  deleteRows(startRow: number, endRow: number): atom$Range,

  // History
  undo(): void,
  redo(): void,
  transact(fn: () => mixed, _: void): void,
  transact(groupingInterval: number, fn: () => mixed): void,
  clearUndoStack(): void,
  createCheckpoint(): atom$TextBufferCheckpoint,
  revertToCheckpoint(checkpoint: atom$TextBufferCheckpoint): boolean,
  groupChangesSinceCheckpoint(checkpoint: atom$TextBufferCheckpoint): boolean,
  // TODO describe the return type more precisely.
  getChangesSinceCheckpoint(checkpoint: atom$TextBufferCheckpoint): Array<mixed>,

  // Search And Replace
  scan(regex: RegExp, iterator: TextBufferScanIterator): void,
  scanInRange(regex: RegExp, range: atom$Range, iterator: TextBufferScanIterator): void,
  backwardsScanInRange(regex: RegExp, range: atom$Range, iterator: TextBufferScanIterator): void,

  // Buffer Range Details
  getLastRow(): number,
  getRange(): atom$Range,
  rangeForRow(row: number, includeNewLine?: boolean): atom$Range,
  getLength(): number,

  // Position/Index mapping
  characterIndexForPosition(position: atom$PointLike): number,
  positionForCharacterIndex(index: number): atom$Point,

  // Buffer Operations
  reload(): void,
  load(): Promise<void>,
  save(): Promise<void>,

  isInConflict(): boolean,
  isModified(): boolean,

  // Private APIs
  cachedDiskContents: ?string,
  emitter: atom$Emitter,
  refcount: number,
  loaded: boolean,
  wasModifiedBeforeRemove: boolean,
  finishLoading(): atom$TextBuffer,
  updateCachedDiskContents(flushCache?: boolean, callback?: () => mixed): Promise<void>,
  emitModifiedStatusChanged(changed: boolean): void,
  destroy(): void,
  isDestroyed(): boolean,
  applyChange: () => void,
  shouldDestroyOnFileDelete?: () => boolean,
}

declare class atom$Notification {
  // Event Subscription
  onDidDismiss(callback: () => mixed): IDisposable,
  onDidDisplay(callback: () => mixed): IDisposable,

  // Methods
  getType(): string,
  getMessage(): string,
  getOptions(): Object,
  dismiss(): void,
  isDismissed(): boolean,
}

type atom$NotificationButton = {
  text: string,
  className?: string,
  onDidClick?: () => mixed,
};

type atom$NotificationOptions = {
  detail?: string,
  dismissable?: boolean,
  description?: string,
  icon?: string,
  stack?: string,
  buttons?: Array<atom$NotificationButton>,
};

declare class atom$NotificationManager {
  // Events
  onDidAddNotification(callback: (notification: atom$Notification) => void): IDisposable,

  // Adding Notifications
  add(type: string, message: string, options?: atom$NotificationOptions): atom$Notification,
  addSuccess(message: string, options?: atom$NotificationOptions): atom$Notification,
  addInfo(message: string, options?: atom$NotificationOptions): atom$Notification,
  addWarning(message: string, options?: atom$NotificationOptions): atom$Notification,
  addError(message: string, options?: atom$NotificationOptions): atom$Notification,
  addFatalError(message: string, options?: atom$NotificationOptions): atom$Notification,
  addNotification(notification: atom$Notification): atom$Notification,

  // Getting Notifications
  getNotifications(): Array<atom$Notification>,
}

// The items in this declaration are available off of `require('atom')`.
// This list is not complete.
declare module 'atom' {
  declare var BufferedNodeProcess: typeof atom$BufferedNodeProcess;
  declare var BufferedProcess: typeof atom$BufferedProcess;
  declare var CompositeDisposable: typeof atom$CompositeDisposable;
  declare var Directory: typeof atom$Directory;
  declare var Disposable: typeof atom$Disposable;
  declare var Emitter: typeof atom$Emitter;
  declare var File: typeof atom$File;
  declare var GitRepository: typeof atom$GitRepository;
  declare var Notification: typeof atom$Notification;
  declare var Point: typeof atom$Point;
  declare var Range: typeof atom$Range;
  declare var TextBuffer: typeof atom$TextBuffer;
  declare var TextEditor: typeof atom$TextEditor;
}

// Make sure that common types can be referenced without the `atom$` prefix
// in type declarations.
declare var Cursor: typeof atom$Cursor;
declare var Panel: typeof atom$Panel;
declare var TextEditor: typeof atom$TextEditor;

type atom$UnhandledErrorEvent = {
  originalError: Object,
  message: string,
  url: string,
  line: number,
  column: number,
};

// The properties of this type match the properties of the `atom` global.
// This list is not complete.
type AtomGlobal = {
  // Properties
  appVersion: string,
  atomScriptMode: ?boolean, // Added by nuclide-atom-script.
  clipboard: atom$Clipboard,
  commands: atom$CommandRegistry,
  config: atom$Config,
  contextMenu: atom$ContextMenuManager,
  applicationDelegate: atom$applicationDelegate,
  deserializers: atom$DeserializerManager,
  grammars: atom$GrammarRegistry,
  history: atom$HistoryManager,
  keymaps: atom$KeymapManager,
  menu: atom$MenuManager,
  notifications: atom$NotificationManager,
  packages: atom$PackageManager,
  styles: atom$StyleManager,
  themes: atom$ThemeManager,
  textEditors: atom$TextEditorRegistry,
  tooltips: atom$TooltipManager,
  views: atom$ViewRegistry,
  workspace: atom$Workspace,
  project: atom$Project,
  devMode: boolean,

  // Event Subscription
  onWillThrowError(callback: (event: atom$UnhandledErrorEvent) => mixed): IDisposable,
  onDidThrowError(callback: (event: atom$UnhandledErrorEvent) => mixed): IDisposable,
  whenShellEnvironmentLoaded(callback: () => mixed): IDisposable,

  // Atom Details
  inDevMode(): boolean,
  inSafeMode(): boolean,
  inSpecMode(): boolean,
  getVersion(): string,
  isReleasedVersion(): boolean,
  getWindowLoadTime(): number,

  // This is an undocumented way to reach the Electron BrowserWindow.
  // Use `electron.remote.getCurrentWindow` instead.
  getCurrentWindow: void,

  // Messaging the User
  +confirm:
    & ((
      {
        message: string,
        detail?: string,
        buttons?: Array<string>,
      },
      (number) => void,
    ) => void)
    // The synchronous form. You really shouldn't use this.
    & (({
        message: string,
        detailedMessage?: string,
        buttons?: Array<string> | {[buttonName: string]: () => mixed},
      }) => ?number),

  open(params: {
    pathsToOpen?: Array<string>,
    newWindow?: boolean,
    devMode?: boolean,
    safeMode?: boolean,
  }): void,
  reload(): void,
  restartApplication(): void,

  // Undocumented Methods
  getConfigDirPath(): string,
  showSaveDialogSync(options: Object): string,
  loadState(): Promise<?Object>,
  getLoadSettings(): Object,
};

declare var atom: AtomGlobal;

type RepositoryDidChangeStatusCallback = (event: {path: string, pathStatus: number}) => mixed;
type RepositoryLineDiff = {
  oldStart: number,
  newStart: number,
  oldLines: number,
  newLines: number,
};

// Taken from the interface of [`GitRepository`][1], which is also implemented by
// `HgRepositoryClient`.
//
// [1]: https://github.com/atom/atom/blob/v1.7.3/src/git-repository.coffee
declare class atom$Repository {
  constructor(path: string, options?: {refreshOnWindowFocus?: boolean}): void,

  // Event Subscription
  onDidChangeStatus: (callback: RepositoryDidChangeStatusCallback) => IDisposable,
  onDidChangeStatuses: (callback: () => mixed) => IDisposable,

  // Repository Details
  getType: () => string,
  getPath: () => string,
  getWorkingDirectory: () => string,
  isProjectAtRoot: () => boolean,
  relativize: (aPath: string) => string,
  getOriginURL: (aPath: ?string) => ?string,

  // Reading Status
  isPathModified: (aPath: string) => boolean,
  isPathNew: (aPath: string) => boolean,
  isPathIgnored: (aPath: string) => boolean,
  getDirectoryStatus: (aPath: string) => number,
  getPathStatus: (aPath: string) => number,
  getCachedPathStatus: (aPath: string) => ?number,
  isStatusModified: (status: number) => boolean,
  isStatusNew: (status: number) => boolean,
  refreshStatus: () => Promise<void>,

  // Retrieving Diffs
  getDiffStats: (filePath: string) => {added: number, deleted: number},
  getLineDiffs: (aPath: string, text: string) => Array<RepositoryLineDiff>,

  // Checking Out
  checkoutHead: (aPath: string) => boolean,
  checkoutReference: (reference: string, create: boolean) => Promise<void>,

  // Event Subscription
  onDidDestroy(callback: () => mixed): IDisposable,
  isDestroyed(): boolean,
}

declare class atom$GitRepositoryInternal {
  // Reading Status
  isStatusModified: (status: number) => boolean,
  isStatusNew: (status: number) => boolean,
  isStatusIgnored: (status: number) => boolean,
  isStatusStaged: (status: number) => boolean,
  isStatusDeleted: (status: number) => boolean,
}

// One of text or snippet is required.
// TODO(hansonw): use a union + intersection type
type atom$AutocompleteSuggestion = {
  text?: string,
  snippet?: string,
  displayText?: string,
  replacementPrefix?: string,
  type?: ?string,
  leftLabel?: ?string,
  leftLabelHTML?: ?string,
  rightLabel?: ?string,
  rightLabelHTML?: ?string,
  className?: ?string,
  iconHTML?: ?string,
  description?: ?string,
  descriptionMarkdown?: ?string,
  descriptionMoreURL?: ?string,
};

type atom$AutocompleteRequest = {
  editor: TextEditor,
  bufferPosition: atom$Point,
  scopeDescriptor: atom$ScopeDescriptor,
  prefix: string,
  activatedManually?: boolean,
};

type atom$AutocompleteProvider = {
  +selector: string,
  +getSuggestions: (
    request: atom$AutocompleteRequest,
  ) => Promise<?Array<atom$AutocompleteSuggestion>> | ?Array<atom$AutocompleteSuggestion>,
  +getSuggestionDetailsOnSelect?: (
    suggestion: atom$AutocompleteSuggestion
  ) => Promise<?atom$AutocompleteSuggestion>,
  +disableForSelector?: string,
  +inclusionPriority?: number,
  +excludeLowerPriority?: boolean,
  +suggestionPriority?: number,
  +filterSuggestions?: boolean,
  +disposable?: () => void,
  +onDidInsertSuggestion?: (
    insertedSuggestion: atom$SuggestionInsertedRequest,
  ) => void,
};

type atom$SuggestionInsertedRequest = {
  +editor: atom$TextEditor,
  +triggerPosition: atom$Point,
  +suggestion: atom$AutocompleteSuggestion,
};

// https://github.com/atom/autocomplete-plus/blob/master/README.md#the-watcheditor-api
type atom$AutocompleteWatchEditor = (
  editor: atom$TextEditor,
  labels?: Array<string>,
) => IDisposable;

// Undocumented API.
declare class atom$Token {
  value: string,
  matchesScopeSelector(selector: string): boolean,
}

declare class atom$Selection {
  clear(): void,
  getText(): string,
  getBufferRange(): atom$Range,
  insertText(
    text: string,
    options?: {
      select?: boolean,
      autoIndent?: boolean,
      autoIndentNewLine?: boolean,
      autoDecreaseIdent?: boolean,
      normalizeLineEndings?: boolean,
      undo?: boolean,
    },
  ): string,
}

declare class atom$PanelContainer {
  dock: atom$Dock,
  element: HTMLElement,
  emitter: atom$Emitter,
  location: atom$PaneLocation,
  panels: Array<atom$Panel>,
  subscriptions: atom$CompositeDisposable,
  viewRegistry: atom$ViewRegistry,

  getPanels(): Array<atom$Panel>,
};


================================================
FILE: decls/hyperclick.js
================================================
/* @flow */

declare module 'atom-ide-ui/hyperclick' {
  import type { Point, Range, TextEditor } from 'atom'

  declare type HyperclickProvider = {
    // Use this to provide a suggestion for single-word matches.
    // Optionally set `wordRegExp` to adjust word-matching.
    getSuggestionForWord?: (
      textEditor: TextEditor,
      text: string,
      range: Range,
    ) => Promise<?HyperclickSuggestion>,

    wordRegExp?: RegExp,

    // Use this to provide a suggestion if it can have non-contiguous ranges.
    // A primary use-case for this is Objective-C methods.
    getSuggestion?: (
      textEditor: TextEditor,
      position: Point,
    ) => Promise<?HyperclickSuggestion>,

    // The higher this is, the more precedence the provider gets. Defaults to 0.
    priority?: number,

    // Must be unique. Used for analytics.
    providerName?: string,
  };

  declare type HyperclickSuggestion = {
    // The range(s) to underline to provide as a visual cue for clicking.
    range: ?Range | ?Array<Range>,

    // The function to call when the underlined text is clicked.
    callback: (() => mixed) | Array<{rightLabel?: string, title: string, callback: () => mixed}>,
  };
}


================================================
FILE: decls/jasmine-atom.js
================================================
/* @flow */

declare function waitsForPromise(
  optionsOrFunc: {timeout?: number, shouldReject?: boolean, label?: string} | () => Promise<mixed>,
  func?: () => Promise<mixed>
): void;


================================================
FILE: decls/jasmine.js
================================================
/* @flow */

declare var jasmine: Object;
declare function it(name: string, callback: (() => void)): void;
declare function fit(name: string, callback: (() => void)): void;
declare function spyOn(object: Object, property: string): Object;
declare function expect(value: any): Object;
declare function describe(name: string, callback: (() => void)): void;
declare function fdescribe(name: string, callback: (() => void)): void;
declare function beforeEach(callback: (() => void)): void;
declare function afterEach(callback: (() => void)): void;


================================================
FILE: decls/linter.js
================================================
/* @flow */

declare module 'linter' {
  import type { Point, Range } from 'atom'

  declare type LinterProvider = {
  }

  declare type Message = {
    location: {
      file: string,
      position: Range,
    },
    reference: ?{
      file: string,
      position?: Point,
    },
    url?: string,
    icon?: string,
    excerpt: string,
    severity: 'error' | 'warning' | 'info',
    solutions?: Array<{
      title?: string,
      position: Range,
      priority?: number,
      currentText?: string,
      replaceWith: string,
    } | {
      title?: string,
      position: Range,
      priority?: number,
      apply: (() => any),
    }>,
    description?: string | (() => Promise<string> | string),
    linterName?: string,
  }
}


================================================
FILE: decls/outline.js
================================================
/* @flow */

declare module 'atom-ide-ui/outline' {
  import type { Point, TextEditor } from 'atom'

  declare type TokenKind =
    | 'keyword'
    | 'class-name'
    | 'constructor'
    | 'method'
    | 'param'
    | 'string'
    | 'whitespace'
    | 'plain'
    | 'type';

  declare type TextToken = {
    kind: TokenKind,
    value: string,
  };

  declare type TokenizedText = Array<TextToken>;

  declare type OutlineTreeKind =
    | 'file'
    | 'module'
    | 'namespace'
    | 'package'
    | 'class'
    | 'method'
    | 'property'
    | 'field'
    | 'constructor'
    | 'enum'
    | 'interface'
    | 'function'
    | 'variable'
    | 'constant'
    | 'string'
    | 'number'
    | 'boolean'
    | 'array';

  declare type OutlineTree = {
    icon?: string, // from atom$Octicon (that type's not allowed over rpc so we use string)
    kind?: OutlineTreeKind, // kind you can pass to the UI for theming

    // Must be one or the other. If both are present, tokenizedText is preferred.
    plainText?: string,
    tokenizedText?: TokenizedText,

    // If user has atom-ide-outline-view.nameOnly then representativeName is used instead.
    representativeName?: string,

    startPosition: Point,
    endPosition?: Point,
    landingPosition?: Point,
    children: Array<OutlineTree>,
  };

  declare type Outline = {
    outlineTrees: Array<OutlineTree>,
  };

  declare type OutlineProvider = {
    name: string,
    // If there are multiple providers for a given grammar, the one with the highest priority will be
    // used.
    priority: number,
    grammarScopes: Array<string>,
    updateOnEdit?: boolean,
    getOutline(editor: TextEditor): Promise<?Outline>,
  };
}


================================================
FILE: lib/coverage-view.js
================================================
/* @flow */

import type { CoverageObject } from './types'

class CoverageView extends HTMLElement {
  tooltipDisposable: ?IDisposable = null;

  initialize(): void {
    this.classList.add('inline-block')

    this.addEventListener('click', () => {
      atom.config.set('flow-ide.showUncovered', !atom.config.get('flow-ide.showUncovered'))
    })
  }

  update(json: CoverageObject): void {
    const covered: number = json.expressions.covered_count
    const uncovered: number = json.expressions.uncovered_count
    const total: number = covered + uncovered
    const percent: number = total === 0 ? 100 : Math.round((covered / total) * 100)

    this.textContent = `Flow Coverage: ${percent}%`

    if (this.tooltipDisposable) {
      this.tooltipDisposable.dispose()
    }

    this.classList.remove('flow-ide-hide')
    this.tooltipDisposable = atom.tooltips.add(this, {
      title: `Covered ${percent}% (${covered} of ${total} expressions)<br>Click to toggle uncovered code`,
    })
  }

  reset() {
    this.classList.add('flow-ide-hide')
    this.textContent = ''
    if (this.tooltipDisposable) {
      this.tooltipDisposable.dispose()
    }
  }

  destroy(): void {
    if (this.tooltipDisposable) {
      this.tooltipDisposable.dispose()
    }
  }
}

export default document.registerElement('flow-ide-coverage', {
  prototype: CoverageView.prototype,
  extends: 'a',
})


================================================
FILE: lib/helpers.js
================================================
/* @flow */

import { findCached, findCachedAsync } from 'atom-linter'
import * as Path from 'path'

const executable = process.platform === 'win32' ? 'flow.cmd' : 'flow'

export const defaultFlowFile = Path.resolve(__dirname, '..', 'vendor', '.flowconfig')
export const defaultFlowBinLocation = Path.join('node_modules', '.bin', executable)
export const grammarScopes = [
  'source.js', 'source.jsx', 'source.js.jsx',
  'source.flow', 'flow-javascript',
]

export async function getExecutablePath(fileDirectory: string): Promise<string> {
  return (
    (atom.config.get('flow-ide.executablePath'): any) ||
    await findCachedAsync(fileDirectory, defaultFlowBinLocation) ||
    'flow'
  )
}

export function getExecutablePathSync(fileDirectory: string): string {
  return (
    (atom.config.get('flow-ide.executablePath'): any) ||
    findCached(fileDirectory, defaultFlowBinLocation) ||
    'flow'
  )
}

export async function getConfigFile(fileDirectory: string): Promise<?string> {
  return findCachedAsync(fileDirectory, '.flowconfig')
}


================================================
FILE: lib/index.js
================================================
/* @flow */

import { CompositeDisposable } from 'atom'
import type { TextEditor, Point, Range } from 'atom'
import { shouldTriggerAutocomplete } from 'atom-autocomplete'
import { exec } from 'atom-linter'
import type { HyperclickProvider, HyperclickSuggestion } from 'atom-ide-ui/hyperclick'
import type { OutlineProvider } from 'atom-ide-ui/outline'
import * as Path from 'path'
import type { Message } from 'linter'
import prettyPrintTypes from 'flow-language-server/lib/pkg/nuclide-flow-rpc/lib/prettyPrintTypes'
import score from 'sb-string_score'
import * as semver from 'semver'
import CoverageView from './coverage-view'
import * as LinterV1 from './linter/v1'
import * as LinterV2 from './linter/v2'
import * as Helpers from './helpers'
import { toOutline } from './outline'
import type { OutlineOptions } from './outline/types'
import type { CoverageObject, TypeAtPosObject } from './types'

export const INIT_MESSAGE = 'flow server'
export const RECHECKING_MESSAGE = 'flow is'

type Server = {
  version: ?Promise<string>,
}
const servers: Map<string, Server> = new Map()

export class Flow {
  subscriptions: CompositeDisposable
  executablePath: string
  onlyIfAppropriate: boolean
  hyperclickPriority: number
  showUncovered: boolean
  coverageView: any
  coverages: WeakMap<TextEditor, CoverageObject>
  statusBar: ?atom$StatusBarTile

  activate() {
    // eslint-disable-next-line global-require
    require('atom-package-deps').install('flow-ide', true)

    this.subscriptions = new CompositeDisposable()
    this.subscriptions.add(atom.config.observe('flow-ide.executablePath', (executablePath) => {
      this.executablePath = executablePath
    }))
    this.subscriptions.add(atom.config.observe('flow-ide.onlyIfAppropriate', (onlyIfAppropriate) => {
      this.onlyIfAppropriate = onlyIfAppropriate
    }))

    let restartNotification
    this.subscriptions.add(atom.config.observe('flow-ide.hyperclickPriority', (hyperclickPriority) => {
      if (this.hyperclickPriority != null) {
        if (hyperclickPriority !== this.hyperclickPriority && restartNotification === undefined) {
          restartNotification = atom.notifications.addSuccess('Restart atom to update flow-ide priority?', {
            dismissable: true,
            buttons: [{
              text: 'Restart',
              onDidClick: () => atom.restartApplication(),
            }],
          })
          restartNotification.onDidDismiss(() => { restartNotification = undefined })
        }
      }
      this.hyperclickPriority = hyperclickPriority
    }))
    this.subscriptions.add(atom.config.observe('flow-ide.showUncovered', (showUncovered) => {
      this.showUncovered = showUncovered
      // lint again so that the coverage actually updates
      const editor = atom.workspace.getActiveTextEditor()
      if (!editor) {
        return
      }
      const view = atom.views.getView(editor)
      if (view) {
        atom.commands.dispatch(view, 'linter:lint')
      }
    }))
    this.subscriptions.add(atom.workspace.onDidChangeActivePaneItem((item: any): void => {
      if (this.coverageView) {
        const coverage = this.coverages.get(item)
        if (coverage) {
          this.coverageView.update(coverage)
        } else {
          this.coverageView.reset()
        }
      }
    }))

    this.coverages = new WeakMap()
  }

  async getConfigFile(fileDirectory: string): Promise<?string> {
    const configFile = await Helpers.getConfigFile(fileDirectory)
    if (configFile != null) {
      const dir = Path.dirname(configFile)
      if (!servers.has(dir)) {
        servers.set(Path.dirname(configFile), { version: null })
      }
    }
    return configFile
  }

  deactivate() {
    this.subscriptions.dispose()
    if (atom.config.get('flow-ide.stopServer')) {
      this.stopServer()
    }
  }

  stopServer() {
    servers.forEach((server, rootDirectory) => {
      const executable = Helpers.getExecutablePathSync(rootDirectory)
      exec(executable, ['stop'], {
        cwd: rootDirectory,
        timeout: 60 * 1000,
        detached: true,
        ignoreExitCode: true,
      }).catch(() => null) // <-- ignore all errors
    })
  }

  async getVersion(configFile: string, executable: string): Promise<?string> {
    const dir = Path.dirname(configFile)
    const server = servers.get(dir)
    if (server && server.version != null) {
      return server.version
    }

    const p = exec(executable, ['version', '--json'], {
      timeout: 60 * 1000,
      uniqueKey: 'flow-ide-version',
      ignoreExitCode: true,
    }).then((result) => {
      if (result === null) {
        // reset the version because something went wrong...
        servers.set(dir, { ...server, version: null })
        return null
      }
      return JSON.parse(result).semver
    })
    .catch((error) => {
      this.handleError(error, configFile)
      return this.getVersion(configFile, executable)
    })

    servers.set(dir, { ...server, version: p })
    return p
  }

  provideLinter(): Object[] {
    return [this.provideStatusLinter(), this.provideCoverageLinter()]
  }

  provideStatusLinter(): Object {
    const linter = {
      name: 'Flow IDE',
      scope: 'project',
      grammarScopes: Helpers.grammarScopes,
      lintsOnChange: false,
      // eslint-disable-next-line arrow-parens
      lint: async (textEditor: TextEditor) => {
        const filePath = textEditor.getPath()
        if (filePath == null) {
          return []
        }

        const fileDirectory = Path.dirname(filePath)

        let configFile = await this.getConfigFile(fileDirectory)
        if (configFile == null) {
          if (this.onlyIfAppropriate) {
            return []
          }
          configFile = ((configFile: any): string)
        }

        const executable = await Helpers.getExecutablePath(fileDirectory)

        const version = await this.getVersion(configFile, executable)
        if (version == null) {
          return null
        }

        return semver.gte(version, '0.66.0')
          ? this.lintV2(textEditor, filePath, fileDirectory, configFile, executable)
          : this.lintV1(textEditor, filePath, fileDirectory, configFile, executable)
      },
    }
    return linter
  }
  async lintV1(textEditor: TextEditor, filePath: string, fileDirectory: string, configFile: string, executable: string) {
    let result
    try {
      result = await exec(executable, ['status', '--json'], {
        cwd: fileDirectory,
        timeout: 60 * 1000,
        uniqueKey: 'flow-ide-linter',
        ignoreExitCode: true,
      })
    } catch (error) {
      this.handleError(error, configFile)
      return this.lintV1(textEditor, filePath, fileDirectory, configFile, executable)
    }

    if (result === null) {
      return null
    }
    return LinterV1.toLinterMessages(result)
  }
  async lintV2(textEditor: TextEditor, filePath: string, fileDirectory: string, configFile: string, executable: string) {
    let result
    try {
      result = await exec(executable, ['status', '--json', '--json-version=2'], {
        cwd: fileDirectory,
        timeout: 60 * 1000,
        uniqueKey: 'flow-ide-linter',
        ignoreExitCode: true,
      })
    } catch (error) {
      this.handleError(error, configFile)
      return this.lintV2(textEditor, filePath, fileDirectory, configFile, executable)
    }

    if (result === null) {
      return null
    }
    return LinterV2.toLinterMessages(result, filePath)
  }

  provideCoverageLinter(): Object {
    const linter = {
      name: 'Flow IDE Coverage',
      scope: 'file',
      grammarScopes: Helpers.grammarScopes,
      lintsOnChange: false,
      lint: async (textEditor: TextEditor) => {
        const filePath = textEditor.getPath()
        if (filePath == null) {
          return []
        }

        const fileDirectory = Path.dirname(filePath)

        let configFile
        if (this.onlyIfAppropriate) {
          configFile = await this.getConfigFile(fileDirectory)
          if (configFile == null) {
            return []
          }
        }

        const executable = await Helpers.getExecutablePath(fileDirectory)

        let result: string
        try {
          result = await exec(executable, ['coverage', filePath, '--json'], {
            cwd: fileDirectory,
            timeout: 60 * 1000,
            uniqueKey: 'flow-ide-coverage',
            ignoreExitCode: true,
          })
          if (result === null) {
            return null
          }
        } catch (error) {
          this.handleError(error, configFile)
          return linter.lint(textEditor)
        }

        const coverage: CoverageObject = JSON.parse(result)
        this.coverages.set(textEditor, coverage)
        if (this.coverageView) {
          this.coverageView.update(coverage)
        }
        return this.showUncovered ? this.toCoverageLinterMessages(coverage) : []
      },
    }
    return linter
  }

  provideAutocomplete(): Object {
    const provider = {
      selector: Helpers.grammarScopes.map(x => (x.includes('.') ? `.${x}` : x)).join(', '),
      disableForSelector: '.comment',
      inclusionPriority: 100,
      // eslint-disable-next-line arrow-parens
      getSuggestions: async (params) => {
        const { editor, bufferPosition, activatedManually } = params
        let prefix = params.prefix
        const filePath = editor.getPath()
        if (!filePath) {
          return []
        }

        const fileDirectory = Path.dirname(filePath)
        const fileContents = this.injectPosition(editor.getText(), editor, bufferPosition)
        let flowOptions = ['autocomplete', '--json', filePath]

        let configFile = await this.getConfigFile(fileDirectory)
        if (configFile == null) {
          if (this.onlyIfAppropriate) {
            return []
          }
          configFile = ((configFile: any): string)
          flowOptions = ['autocomplete', '--root', Helpers.defaultFlowFile, '--json', filePath]
        }

        // NOTE: Fix for class properties autocompletion
        if (prefix === '.') {
          prefix = ''
        }

        if (!shouldTriggerAutocomplete({ activatedManually, bufferPosition, editor })) {
          return []
        }

        let result
        try {
          result = await exec(await Helpers.getExecutablePath(fileDirectory), flowOptions, {
            cwd: fileDirectory,
            stdin: fileContents,
            timeout: 60 * 1000,
            uniqueKey: 'flow-ide-autocomplete',
            ignoreExitCode: true,
          })
          if (result === null) {
            return []
          }
        } catch (error) {
          this.handleError(error, configFile)
          return provider.getSuggestions(params)
        }

        return this.toAutocompleteSuggestions(result, prefix)
      },
    }
    return provider
  }
  injectPosition(text: string, editor: Object, bufferPosition: Object) {
    const characterIndex = editor.getBuffer().characterIndexForPosition(bufferPosition)
    return text.slice(0, characterIndex) + 'AUTO332' + text.slice(characterIndex)
  }
  toAutocompleteSuggestions(contents: string, prefix: string) {
    if (contents.slice(0, 1) !== '{') {
      // Invalid server response
      return []
    }

    const parsed = JSON.parse(contents)
    const hasPrefix = prefix.trim().length
    const suggestions = parsed.result.map((suggestion) => {
      const isFunction = suggestion.func_details !== null && suggestion.func_details !== undefined
      let text = null
      let snippet = null
      let displayText = null
      let description = null

      if (isFunction) {
        const functionParams = suggestion.func_details.params
        displayText = `${suggestion.name}(${functionParams.map(value => value.name).join(', ')})`
        const snippetArgs = functionParams.map((value, i) => `\${${i + 1}:${value.name}}`).join(', ')
        snippet = `${suggestion.name}(${snippetArgs})$${functionParams.length + 1}`

        const params = functionParams.map(param => param.name + (param.type ? `: ${param.type}` : ''))
        const match = suggestion.type.match(/\(.*?\) => (.*)/)
        const returnType = match ? `=> ${match[1]}` : ''

        description = `(${params.join(', ')}) ${returnType}`
      } else {
        text = suggestion.name
      }

      return {
        text,
        type: isFunction ? 'function' : 'property',
        score: hasPrefix ? score(suggestion.name, prefix) : 1,
        snippet,
        leftLabel: isFunction ? 'function' : this.getType(suggestion),
        displayText,
        replacementPrefix: prefix,
        description,
      }
    })
    return suggestions.sort((a, b) => b.score - a.score).filter(item => item.score)
  }
  getType(value: { value: string, type: string }) {
    return value.type && value.type.substr(0, 1) === '{' ? 'Object' : value.type || 'any'
  }

  provideHyperclick(): HyperclickProvider {
    const provider = {
      priority: this.hyperclickPriority,
      grammarScopes: Helpers.grammarScopes,
      getSuggestionForWord: async (textEditor: TextEditor, text: string, range: Range): Promise<?HyperclickSuggestion> => {
        const filePath = textEditor.getPath()
        if (filePath == null) {
          return null
        }

        const fileDirectory = Path.dirname(filePath)
        const configFile = await this.getConfigFile(fileDirectory)
        if (configFile == null) {
          return null
        }

        const pos = this.adjustPosition(range.start, textEditor)
        const flowOptions = [
          'get-def',
          '--json',
          '--path=' + filePath,
          pos.row + 1,
          pos.column + 1,
        ]

        let result
        try {
          result = await exec(await Helpers.getExecutablePath(fileDirectory), flowOptions, {
            cwd: fileDirectory,
            stdin: textEditor.getText(),
            ignoreExitCode: true,
            timeout: 60 * 1000,
            uniqueKey: 'flow-ide-hyperclick',
          })
          if (result === null) {
            return null
          }
        } catch (error) {
          this.handleError(error, configFile)
          return provider.getSuggestionForWord(textEditor, text, range)
        }
        const jsonResult = JSON.parse(result)

        if (!jsonResult.path) {
          return null
        }

        return {
          range,
          callback() {
            atom.workspace.open(jsonResult.path, { searchAllPanes: true }).then((editor) => {
              editor.setCursorBufferPosition([jsonResult.line - 1, jsonResult.start - 1])
            })
          },
        }
      },
    }
    return provider
  }
  adjustPosition(pos: atom$Point, editor: TextEditor) {
    // Flow fails to determine the position if the cursor is at the end of a word
    // e.g. "path.dirname ()"
    //                   ↑ the cursor is here, between "dirname" and "("
    // In order to avoid this problem we have to check whether the char
    // at the given position is considered a part of an identifier.
    // If not step back 1 char as it might contain a valid identifier.
    const char = editor.getTextInBufferRange([
      pos,
      pos.translate([0, 1]),
    ])
    const nonWordChars = editor.getNonWordCharacters(pos)
    if (nonWordChars.indexOf(char) >= 0 || /\s/.test(char)) {
      return pos.translate([0, -1])
    }
    return pos
  }

  provideOutlines(): OutlineProvider {
    const outlineOptions: OutlineOptions = {
      showKeywords: {
        export: false,
        default: false,
        const: false,
        var: false,
        let: false,
        class: false,
        function: false,
        type: false,
      },
      showFunctionArgs: false,
    }
    this.subscriptions.add(atom.config.observe('flow-ide.outline', (outline) => {
      outlineOptions.showKeywords.export = outline.showExport
      outlineOptions.showFunctionArgs = outline.showFunctionArgs
    }))

    const provider = {
      name: 'flow-ide',
      priority: 1,
      grammarScopes: Helpers.grammarScopes,
      updateOnEdit: true,
      getOutline: async (editor: TextEditor) => {
        const filePath = editor.getPath()
        if (filePath == null) {
          return null
        }

        const fileDirectory = Path.dirname(filePath)
        const configFile = await this.getConfigFile(fileDirectory)
        if (configFile == null) {
          return null
        }

        const flowOptions = ['ast']

        let result
        try {
          result = await exec(await Helpers.getExecutablePath(fileDirectory), flowOptions, {
            cwd: fileDirectory,
            stdin: editor.getText(),
            timeout: 60 * 1000,
            uniqueKey: 'flow-ide-ast',
            ignoreExitCode: true,
          })
          if (result === null) {
            return { outlineTrees: [] }
          }
        } catch (error) {
          this.handleError(error, configFile)
          return provider.getOutline(editor)
        }

        return toOutline(result, outlineOptions)
      },
    }
    return provider
  }

  consumeDatatip(datatipService: any) {
    const provider = {
      providerName: 'flow-ide',
      priority: 1,
      grammarScopes: Helpers.grammarScopes,
      datatip: async (editor: TextEditor, point: Point): Promise<any> => {
        const filePath = editor.getPath()
        if (filePath == null) {
          return null
        }

        const fileDirectory = Path.dirname(filePath)
        const configFile = await this.getConfigFile(fileDirectory)
        if (configFile == null) {
          return null
        }

        const flowOptions = [
          'type-at-pos',
          '--json',
          '--path=' + filePath,
          point.row + 1,
          point.column + 1,
        ]

        let result
        try {
          result = await exec(await Helpers.getExecutablePath(fileDirectory), flowOptions, {
            cwd: fileDirectory,
            stdin: editor.getText(),
            timeout: 60 * 1000,
            uniqueKey: 'flow-ide-type-at-pos',
            ignoreExitCode: true,
          })
          if (result === null) {
            return null
          }
        } catch (error) {
          this.handleError(error, configFile)
          return provider.datatip(editor, point)
        }

        return this.toDatatip(editor, point, result)
      },
    }
    this.subscriptions.add(datatipService.addProvider(provider))
  }
  toDatatip(editor: TextEditor, point: Point, result: string) {
    const parsed: TypeAtPosObject = JSON.parse(result)

    const { type, loc } = parsed
    if (type === '(unknown)') {
      return null
    }

    return {
      range: LinterV1.locationToRange(loc),
      markedStrings: [{
        type: 'snippet',
        grammar: editor.getGrammar(),
        value: prettyPrintTypes(type),
      }],
    }
  }

  consumeStatusBar(statusBar: atom$StatusBar): void {
    this.coverageView = new CoverageView()
    this.coverageView.initialize()
    this.statusBar = statusBar.addLeftTile({ item: this.coverageView, priority: 10 })
    this.subscriptions.add({
      dispose: () => {
        if (this.statusBar) {
          this.statusBar.destroy()
        }
      },
    })
  }

  handleError(error: { message: string, code: string }, configFile: ?string) {
    if (error.message.indexOf(INIT_MESSAGE) !== -1 && typeof configFile === 'string') {
      servers.set(Path.dirname(configFile), { version: null })
    }
    if (error.message.indexOf(INIT_MESSAGE) !== -1 || error.message.indexOf(RECHECKING_MESSAGE) !== -1) {
      // continue...
    } else if (error.code === 'ENOENT') {
      throw new Error('Unable to find `flow` executable.')
    } else {
      throw error
    }
  }

  toCoverageLinterMessages(coverage: CoverageObject): Message[] {
    return coverage.expressions.uncovered_locs.map(loc => ({
      severity: 'info',
      location: LinterV1.toLinterLocation(loc),
      excerpt: 'Uncovered code',
      reference: null,
    }))
  }
}


================================================
FILE: lib/language-client.js
================================================
/* @flow */

import { TextEditor } from 'atom'
import { AutoLanguageClient } from '@lloiser/atom-languageclient'
import { findCachedAsync } from 'atom-linter'
import { spawn } from 'child_process'
import * as Path from 'path'
import * as Helpers from './helpers'

export class FlowLanguageClient extends AutoLanguageClient {
  getGrammarScopes() { return Helpers.grammarScopes }
  getLanguageName() { return 'Flowtype' }
  getServerName() { return 'flow' }

  async startServerProcess(projectPath: string) {
    return spawn(
      await Helpers.getExecutablePath(projectPath),
      ['lsp'],
      { cwd: projectPath },
    )
  }

  async determineProjectPath(textEditor: TextEditor) {
    const path = textEditor.getPath()
    if (path == null) {
      return null
    }

    const configFile: ?string = await findCachedAsync(path, '.flowconfig')
    if (configFile != null) {
      return Path.dirname(configFile)
    }
    return null
  }
}


================================================
FILE: lib/linter/v1/index.js
================================================
/* @flow */

import { Range } from 'atom'
import type { Message } from 'linter'

import { mainMessageOfError, prettyPrintError } from './pretty'
import type { StatusMessage, StatusObject } from './types'
import type { Location } from '../../types'

export function locationToRange({ start, end }: Location): Range {
  return new Range(
    [start.line - 1, start.column - 1],
    [end.line - 1, end.column],
  )
}
export function toLinterLocation(loc: Location) {
  return {
    file: loc.source,
    position: locationToRange(loc),
  }
}

function toLinterReference(messages: StatusMessage[]) {
  for (let i = 1, length = messages.length; i < length; i++) {
    const message = messages[i]
    if (message.loc) {
      return {
        file: message.loc.source,
        position: locationToRange(message.loc).start,
      }
    }
  }
  return null
}

export function toLinterMessages(contents: string): Message[] {
  const parsed: StatusObject = JSON.parse(contents)
  if (!Array.isArray(parsed.errors) || !parsed.errors.length) {
    return []
  }

  return parsed.errors.map((error) => {
    const mainMsg = mainMessageOfError(error)
    const { loc } = mainMsg
    if (!loc) {
      return null
    }

    let excerpt = error.message.map(msg => msg.descr).join(' ')
    if (error.operation && mainMsg === error.operation) {
      excerpt = error.operation.descr + ' ' + excerpt
    }

    return {
      severity: error.level === 'error' ? 'error' : 'warning',
      location: toLinterLocation(loc),
      excerpt,
      description: prettyPrintError(error),
      reference: toLinterReference(error.message),
    }
  }).filter(Boolean)
}


================================================
FILE: lib/linter/v1/pretty.js
================================================
/* @flow */

// Note: the following code is based on
// https://github.com/facebook/flow/blob/v0.47.0/tsrc/flowResult.js
// and adjusted to output nicely formatted markdown.

import { format } from 'util'
import type { StatusError, StatusExtra, StatusMessage } from './types'
import type { Location } from '../../types'

const regexMarkdownChars = /[*#()[\]<>_]/g
const escapeChars = { '<': '&lt;', '>': '&gt;' }

function fileUrl({ source, start }: Location): string {
  // build a link that can be opened by the linter ui.
  // e.g. atom://linter?file=<path>&row=<number>&column=<number>
  const params = ['file=' + encodeURIComponent(source)]
  if (start.line > 0) {
    params.push('row=' + (start.line - 1))
    if (start.column > 0) {
      params.push('column=' + (start.column - 1))
    }
  }
  return 'atom://linter?' + params.join('&')
}
function linterLink(loc: Location, text: string) {
  return '[' + text + '](' + fileUrl(loc) + ')'
}
function fileLink(loc: Location): string {
  return linterLink(loc, atom.project.relativize(loc.source))
}
function lineLink(loc: Location): string {
  return linterLink(loc, loc.start.line.toString())
}

export function mainMessageOfError(error: StatusError): StatusMessage {
  return error.operation || error.message[0]
}

export function mainLocOfError(error: StatusError): ?Location {
  return mainMessageOfError(error).loc
}

function getExtraMessages(extra: ?StatusExtra[]): StatusMessage[] {
  if (extra) {
    const messages = extra.reduce((acc, current) => {
      const childrenMessages = getExtraMessages(current.children)
      return acc.concat(current.message, childrenMessages)
    }, [])
    messages.forEach((message) => {
      const msg = message
      msg.indent = (msg.indent || 0) + 2
    })
    return messages
  }
  return []
}

function getTraceReasons(trace: ?StatusMessage[]): StatusMessage[] {
  if (trace != null && trace.length > 0) {
    return ([{ descr: 'Trace:', type: 'Blame' }].concat(trace): any)
  }
  return []
}

function mkComment(descr: string): StatusMessage {
  return ({ descr, type: 'Comment' }: any)
}

function getOpReason(op: ?StatusMessage): StatusMessage[] {
  if (op) {
    return [
      op,
      mkComment('Error:'),
    ]
  }
  return []
}

function getHeader(mainLoc: ?Location, kind: string, level: string): StatusMessage[] {
  let line = -1
  let filename = ''
  if (mainLoc) {
    const { source, start } = mainLoc
    line = start.line
    if (source) {
      filename = fileLink(mainLoc)
    }
  }
  if (!filename) {
    filename = format('%s:%d', '[No file]', line)
  }

  let prefix = ''
  if (kind === 'internal' && level === 'error') {
    prefix = 'Internal error (see logs):\n'
  } else if (mainLoc && mainLoc.type === 'LibFile') {
    if (kind === 'parse' && level === 'error') {
      prefix = 'Library parse error:\n'
    } else if (kind === 'infer') {
      prefix = 'Library type error:\n'
    }
  }

  return [mkComment(format('%s%s', prefix, filename))]
}

function prettyPrintMessage(mainFile: string, { context, descr, loc, indent }: StatusMessage): string {
  const indentation = ' '.repeat(indent || 0)
  if (loc) {
    const startCol = loc.start.column - 1
    let contextStr = indentation
    if (context !== null && typeof context === 'string') {
      // On Windows this might have \r
      let ctx = context.trimRight()
      // Replace tabs with spaces
      ctx = ctx.replace(/\t/g, ' ')
      // escape certain chars that serve a purpose in markdown
      ctx = ctx.replace(regexMarkdownChars, m => escapeChars[m] || '\\' + m)

      const { line } = loc.start
      const prefix = line < 100 ? ' '.repeat(3 - line.toString().length) : ''

      let padding = Array((prefix + line + ': ').length + 1).join(' ')
      if (ctx.length > startCol) {
        padding += ctx.substr(0, startCol).replace(/[^\t ]/g, ' ')
      }

      const underlineSize = line === loc.end.line ?
        Math.max(1, loc.end.column - startCol) :
        1
      const underline = '^'.repeat(underlineSize)

      contextStr = format(
        '%s%s%s: %s\n%s%s%s ',
        indentation,
        prefix,
        lineLink(loc),
        ctx,
        indentation,
        padding,
        underline,
      )
    }
    const seeAnotherFile = loc.source === mainFile ?
      '' :
      format(
        '. See%s: %s',
        loc.type === 'LibFile' ? ' lib' : '',
        fileLink(loc),
      )
    return format('%s%s%s', contextStr, descr, seeAnotherFile)
  }
  return indentation + descr
}

export function mergedMessagesOfError(error: StatusError): StatusMessage[] {
  const { level, kind, message, operation, trace, extra } = error
  const mainLoc = mainLocOfError(error)
  const messages = [].concat(
    getHeader(mainLoc, kind, level),
    getOpReason(operation),
    message,
    getExtraMessages(extra),
    getTraceReasons(trace),
  )
  // Merge comments into blames
  return messages.reduce((acc, msg) => {
    const { descr, loc, type } = msg
    if (loc || acc.length === 0 || type === 'Blame') {
      acc.push(msg)
    } else if (descr !== 'Error:') {
      const prev = acc[acc.length - 1]
      prev.descr = prev.descr === '' ? descr : format('%s. %s', prev.descr, descr)
    }
    return acc
  }, [])
}

export function prettyPrintError(error: StatusError): string {
  const mainLoc = mainLocOfError(error)
  const mainFile = (mainLoc && mainLoc.source) || '[No file]'
  const messages = mergedMessagesOfError(error)
  return '<div style="white-space: pre-wrap">' + messages.map(msg => prettyPrintMessage(mainFile, msg)).join('\n') + '</div>'
}


================================================
FILE: lib/linter/v1/types.js
================================================
/* @flow */

import type { Location } from '../../types'

export type StatusMessage = {
  context: ?string,
  descr: string,
  type: string,
  loc?: Location,
  path: string,
  line: number,
  endline: number,
  start: number,
  end: number,
  indent?: number,
}

export type StatusExtra = {
  message: StatusMessage[],
  children?: StatusExtra[],
}

export type StatusError = {
  kind: string,
  level: string,
  message: StatusMessage[],
  operation?: StatusMessage,
  extra?: StatusExtra[],
  trace?: StatusMessage[],
}

export type StatusObject = {
  flowVersion: string,
  errors: StatusError[],
  passed: boolean,
}


================================================
FILE: lib/linter/v2/index.js
================================================
/* @flow */

import { Range } from 'atom'
import type { Message } from 'linter'

import type { Location, MessageMarkup, InlineMarkup, ReferenceLocs, StatusResult } from './types'

function locationToRange({ start, end }: Location): Range {
  return new Range(
    [start.line - 1, start.column - 1],
    [end.line - 1, end.column],
  )
}

function locactionToLinterLocation(loc: Location) {
  return {
    file: loc.source,
    position: locationToRange(loc),
  }
}

function plainText(messages: InlineMarkup[], references: ReferenceLocs): string {
  return messages.map((message) => {
    switch (message.kind) {
      case 'Text':
        return message.text

      case 'Code':
        return message.text

      case 'Reference': {
        const ref = references[message.referenceId]
        if (!ref) {
          return ''
        }
        return plainText(message.message, references)
      }

      default:
        return ''
    }
  }).join('')
}

function fileUrl(loc: Location, path: string): string {
  // build a link that can be opened by the linter ui.
  // e.g. atom://linter?file=<path>&row=<number>&column=<number>
  let { source } = loc
  if (source === '-') {
    source = path
  }
  const params = ['file=' + encodeURIComponent(source)]
  const { start } = loc
  if (start.line > 0) {
    params.push('row=' + (start.line - 1))
    if (start.column > 0) {
      params.push('column=' + (start.column - 1))
    }
  }
  return 'atom://linter?' + params.join('&')
}

function markup(messages: MessageMarkup, references: ReferenceLocs, path: string): string {
  if (Array.isArray(messages)) {
    return messages.map((message) => {
      switch (message.kind) {
        case 'Text':
          return message.text

        case 'Code':
          return '`' + message.text + '`'

        case 'Reference': {
          const ref = references[message.referenceId]
          if (!ref) {
            return ''
          }
          return `[${markup(message.message, references, path)}](${fileUrl(ref, path)})`
        }

        default:
          return ''
      }
    }).join('')
  }

  if (messages.kind === 'UnorderedList') {
    // `messages.message` is already part of the excerpt
    return messages.items.map(msg => markup(msg, references, path))
      .map(msg => ` - ${msg}`).join('\n')
  }
  return ''
}

export function toLinterMessages(json: string, path: string): Message[] {
  const result: StatusResult = JSON.parse(json)

  if (result.passed && result.errors.length === 0) {
    return []
  }

  return result.errors.map((err) => {
    const location = locactionToLinterLocation(err.primaryLoc)
    const severity = err.level
    const { messageMarkup } = err
    if (Array.isArray(messageMarkup)) {
      return {
        location,
        excerpt: plainText(messageMarkup, err.referenceLocs),
        severity,
        reference: null,
      }
    }

    return {
      location,
      excerpt: plainText(messageMarkup.message, err.referenceLocs),
      severity,
      description: markup(messageMarkup, err.referenceLocs, path),
      reference: null,
    }
  })
}


================================================
FILE: lib/linter/v2/types.js
================================================
/* @flow */

export type Position = {
  line: number,
  column: number,
  offset: number
}

export type Location = {
  source: string,
  type: "SourceFile", // TODO: others?
  start: Position,
  end: Position,
  context: string
}

export type ReferenceId = string

export type ReferenceLocs = { [id: ReferenceId]: Location }

// CodeMarkup represents text that originates from the actual source code.
// Typically should be rendered in a fixed-width font (e.g. in markdown using `...`)
export type CodeMarkup = {
  kind: "Code",
  text: string
}
// TextMarkup just represents text, most likely used in combination with other markups
export type TextMarkup = {
  kind: "Text",
  text: string
}
// ReferenceMarkup can be used to reference to source code (e.g. via links)
export type ReferenceMarkup = {
  kind: "Reference",
  referenceId: ReferenceId,
  message: InlineMarkup[] // eslint-disable-line no-use-before-define
}

// UnorderedListMarkup represents a `message` and a list of bullet points (`items`)
export type UnorderedListMarkup = {
  kind: 'UnorderedList',
  message: InlineMarkup[], // eslint-disable-line no-use-before-define
  items: MessageMarkup[] // eslint-disable-line no-use-before-define
}

// InlineMarkup represents a single line of text
export type InlineMarkup = CodeMarkup | TextMarkup | ReferenceMarkup
// BlockMarkup represents a larger block of text.
// Typically this is suitable for linter `description`
export type BlockMarkup = UnorderedListMarkup

export type MessageMarkup =
  InlineMarkup[] |
  BlockMarkup

export type Error = {
  kind: "lint" | "infer",
  level: "warning" | "error",
  suppressions: [], // TODO: I probably have to turn on some suppressions to see this...
  classic: boolean,
  primaryLoc: Location,
  rootLoc: ?Location,
  messageMarkup: MessageMarkup,
  referenceLocs: ReferenceLocs
}

export type StatusResult = {
  flowVersion: string, // e.g 0.66.0
  jsonVersion: "2",
  errors: Error[],
  passed: true
};


================================================
FILE: lib/main.js
================================================
/* @flow */

const useLSP = atom.config.get('flow-ide.useLSP')

// notify to restart if the value changes
atom.config.onDidChange('flow-ide.useLSP', () => {
  const buttons = [{
    text: 'Restart',
    onDidClick () { return atom.restartApplication() },
  }]
  atom.notifications.addInfo('Changing this value requires a restart of atom.', { dismissable: true, buttons })
})

// possible improvement: check if flow really supports lsp

if (useLSP === true) {
  // eslint-disable-next-line global-require
  const { FlowLanguageClient } = require('./language-client')
  module.exports = new FlowLanguageClient()
} else {
  // eslint-disable-next-line global-require
  const { Flow } = require('./index')
  module.exports = new Flow()
}


================================================
FILE: lib/outline/index.js
================================================
/* @flow */

import type { Outline } from 'atom-ide-ui/outline'
import { astToOutline } from './parse'
import type { OutlineOptions } from './types'

export function toOutline(result: string, options: OutlineOptions): Outline {
  const parsed = JSON.parse(result)
  return astToOutline(options, parsed)
}


================================================
FILE: lib/outline/parse.js
================================================
/* @flow */

// NOTE: the following code is based on
// https://github.com/flowtype/flow-language-server/blob/fbd1bc3/src/pkg/nuclide-flow-rpc/lib/astToOutline.js
// and adjusted to toggle keywords and function arguments

import { Point } from 'atom'
import invariant from 'assert'
import type { Outline, OutlineTree, TokenizedText } from 'atom-ide-ui/outline'
import * as Text from './text'
import type { OutlineOptions, Extent } from './types'

function exportDeclaration(
  options: OutlineOptions,
  item: Object,
  extent: Extent,
  isDefault: boolean,
): ?OutlineTree {
  const tree = itemToTree(options, item.declaration) // eslint-disable-line
  if (tree == null) {
    return null
  }
  // Flow always has tokenizedText
  invariant(tree.tokenizedText != null)

  const tokenizedText = []
  if (options.showKeywords.export) {
    tokenizedText.push(Text.keyword('export'), Text.whitespace(' '))
  }
  if (options.showKeywords.default && isDefault) {
    tokenizedText.push(Text.keyword('default'), Text.whitespace(' '))
  }
  tokenizedText.push(...tree.tokenizedText)

  return {
    kind: tree.kind,
    tokenizedText,
    representativeName: tree.representativeName,
    children: tree.children,
    ...extent,
  }
}

function declarationsTokenizedText(options: OutlineOptions, declarations: Array<Object>): TokenizedText {
  return declarations.reduce((text, decl, i, decls) => {
    text.push(...declarationReducer(options, decl)) // eslint-disable-line
    if (i < decls.length - 1) {
      text.push(Text.plain(','))
      text.push(Text.whitespace(' '))
    }
    return text
  }, [])
}

function declarationReducer(
  options: OutlineOptions,
  decl: Object,
): TokenizedText {
  if (!decl) {
    // special case: array patterns can contain `null` elements
    // which means that this value is simply ignored.
    return []
  }

  switch (decl.type) {
    case 'Identifier':
      return [Text.param(decl.name)]
    case 'ObjectPattern':
      return [
        Text.plain('{'),
        ...declarationsTokenizedText(options, decl.properties),
        Text.plain('}'),
      ]
    case 'ArrayPattern':
      return [
        Text.plain('['),
        ...declarationsTokenizedText(options, decl.elements),
        Text.plain(']'),
      ]
    case 'AssignmentPattern':
      return declarationReducer(options, decl.left)
    case 'RestElement':
    case 'RestProperty':
      return [
        Text.plain('...'),
        ...declarationReducer(options, decl.argument),
      ]
    case 'Property':
      return declarationReducer(options, decl.value)
    default:
      throw new Error(`encountered unexpected argument type ${decl.type}`)
  }
}

function getExtent(item: Object): Extent {
  return {
    startPosition: new Point(
      // It definitely makes sense that the lines we get are 1-based and the columns are
      // 0-based... convert to 0-based all around.
      item.loc.start.line - 1,
      item.loc.start.column,
    ),
    endPosition: new Point(item.loc.end.line - 1, item.loc.end.column),
  }
}

function functionOutline(
  options: OutlineOptions,
  name: string,
  params: Array<Object>,
  extent: Extent,
): OutlineTree {
  const tokenizedText = []

  if (options.showKeywords.function || !name) {
    tokenizedText.push(Text.keyword('function'))
    if (name) {
      tokenizedText.push(Text.whitespace(' '))
    }
  }
  if (name) {
    tokenizedText.push(Text.method(name))
  }
  if (options.showFunctionArgs) {
    tokenizedText.push(
      Text.plain('('),
      ...declarationsTokenizedText(options, params),
      Text.plain(')'),
    )
  }

  return {
    kind: 'function',
    tokenizedText,
    representativeName: name,
    children: [],
    ...extent,
  }
}

function typeAliasOutline(options: OutlineOptions, typeAliasExpression: Object): OutlineTree {
  invariant(typeAliasExpression.type === 'TypeAlias' || typeAliasExpression.type === 'DeclareTypeAlias')
  const name = typeAliasExpression.id.name

  const tokenizedText = []
  if (options.showKeywords.type) {
    tokenizedText.push(Text.keyword('type'), Text.whitespace(' '))
  }
  tokenizedText.push(Text.type(name))

  return {
    kind: 'interface',
    tokenizedText,
    representativeName: name,
    children: [],
    ...getExtent(typeAliasExpression),
  }
}

function isModuleExports(left: Object): boolean {
  return (
    left.type === 'MemberExpression' &&
    left.object.type === 'Identifier' &&
    left.object.name === 'module' &&
    left.property.type === 'Identifier' &&
    left.property.name === 'exports'
  )
}

function moduleExportsPropertyOutline(options: OutlineOptions, property: Object): ?OutlineTree {
  invariant(property.type === 'Property')
  if (property.key.type !== 'Identifier') {
    return null
  }
  const propName = property.key.name

  if (property.shorthand) {
    // This happens when the shorthand `{ foo }` is used for `{ foo: foo }`
    return {
      kind: 'method',
      tokenizedText: [Text.string(propName)],
      representativeName: propName,
      children: [],
      ...getExtent(property),
    }
  }

  if (
    property.value.type === 'FunctionExpression' ||
    property.value.type === 'ArrowFunctionExpression'
  ) {
    const tokenizedText = [Text.method(propName)]
    if (options.showFunctionArgs) {
      tokenizedText.push(
        Text.plain('('),
        ...declarationsTokenizedText(options, property.value.params),
        Text.plain(')'),
      )
    }

    return {
      kind: 'method',
      tokenizedText,
      representativeName: propName,
      children: [],
      ...getExtent(property),
    }
  }

  return {
    kind: 'field',
    tokenizedText: [Text.string(propName), Text.plain(':')],
    representativeName: propName,
    children: [],
    ...getExtent(property),
  }
}

function moduleExportsOutline(options: OutlineOptions, assignmentStatement: Object): ?OutlineTree {
  invariant(assignmentStatement.type === 'AssignmentExpression')

  const left = assignmentStatement.left
  if (!isModuleExports(left)) {
    return null
  }

  const right = assignmentStatement.right
  if (right.type !== 'ObjectExpression') {
    return null
  }
  const properties: Array<Object> = right.properties
  return {
    kind: 'module',
    tokenizedText: [Text.plain('module.exports')],
    children: properties.map(prop => moduleExportsPropertyOutline(options, prop)).filter(Boolean),
    ...getExtent(assignmentStatement),
  }
}

// Return the function name as written as a string. Intended to stringify patterns like `describe`
// and `describe.only` even though `describe.only` is a MemberExpression rather than an Identifier.
function getFunctionName(callee: Object): ?string {
  switch (callee.type) {
    case 'Identifier':
      return callee.name
    case 'MemberExpression':
      if (
        callee.object.type !== 'Identifier' ||
        callee.property.type !== 'Identifier'
      ) {
        return null
      }
      return `${callee.object.name}.${callee.property.name}`
    default:
      return null
  }
}

function isDescribe(functionName: string): boolean {
  switch (functionName) {
    case 'describe':
    case 'fdescribe':
    case 'ddescribe':
    case 'xdescribe':
    case 'describe.only':
    case 'describe.skip':
    case 'test.cb':
    case 'test.serial':
    case 'test.todo':
    case 'test.failing':
    case 'test':
    case 'test.concurrent':
    case 'test.only':
    case 'test.skip':
    case 'suite':
    case 'suite.only':
    case 'suite.skip':
    case 'xtest':
    case 'xtest.concurrent':
    case 'xtest.only':
    case 'xtest.skip':
      return true
    default:
      return false
  }
}

function isIt(functionName: string): boolean {
  switch (functionName) {
    case 'it':
    case 'fit':
    case 'iit':
    case 'pit':
    case 'xit':
    case 'it.only':
    case 'it.skip':
      return true
    default:
      return false
  }
}

/** If the given AST Node is a string literal, return its literal value. Otherwise return null */
function getStringLiteralValue(literal: ?Object): ?string {
  if (literal == null) {
    return null
  }
  if (literal.type !== 'Literal') {
    return null
  }
  const value = literal.value
  if (typeof value !== 'string') {
    return null
  }
  return value
}

function getFunctionBody(fn: ?Object): ?Array<Object> {
  if (fn == null) {
    return null
  }
  if (
    fn.type !== 'ArrowFunctionExpression' &&
    fn.type !== 'FunctionExpression'
  ) {
    return null
  }
  return fn.body.body
}

function specOutline(
  options: OutlineOptions,
  expressionStatement: Object,
  describeOnly: boolean = false,
): ?OutlineTree {
  const expression = expressionStatement.expression
  if (expression.type !== 'CallExpression') {
    return null
  }
  const functionName = getFunctionName(expression.callee)
  if (functionName == null) {
    return null
  }
  if (!isDescribe(functionName)) {
    if (describeOnly || !isIt(functionName)) {
      return null
    }
  }
  const description = getStringLiteralValue(expression.arguments[0])
  const specBody = getFunctionBody(expression.arguments[1])
  if (description == null || specBody == null) {
    return null
  }
  let children
  if (isIt(functionName)) {
    children = []
  } else {
    children =
      specBody
        .filter(item => item.type === 'ExpressionStatement')
        .map(item => specOutline(options, item))
        .filter(Boolean)
  }
  return {
    kind: 'function',
    tokenizedText: [Text.method(functionName), Text.whitespace(' '), Text.string(description)],
    representativeName: description,
    children,
    ...getExtent(expressionStatement),
  }
}

function topLevelExpressionOutline(options: OutlineOptions, expressionStatement: Object): ?OutlineTree {
  switch (expressionStatement.expression.type) {
    case 'CallExpression':
      return specOutline(options, expressionStatement, /* describeOnly */ true)
    case 'AssignmentExpression':
      return moduleExportsOutline(options, expressionStatement.expression)
    default:
      return null
  }
}

function variableDeclaratorOutline(
  options: OutlineOptions,
  declarator: Object,
  kind: string,
  extent: Extent,
): ?OutlineTree {
  if (
    declarator.init != null &&
    (declarator.init.type === 'FunctionExpression' ||
      declarator.init.type === 'ArrowFunctionExpression')
  ) {
    return functionOutline(options, declarator.id.name, declarator.init.params, extent)
  }

  const { id } = declarator

  const tokenizedText = []
  if (options.showKeywords[kind]) {
    tokenizedText.push(
      Text.keyword(kind),
      Text.whitespace(' '),
    )
  }
  tokenizedText.push(...declarationsTokenizedText(options, [id]))

  const representativeName = id.type === 'Identifier' ? id.name : undefined
  return {
    kind: kind === 'const' ? 'constant' : 'variable',
    tokenizedText,
    representativeName,
    children: [],
    ...extent,
  }
}

function variableDeclarationOutline(options: OutlineOptions, declaration: Object): ?OutlineTree {
  // If there are multiple var declarations in one line, just take the first.
  return variableDeclaratorOutline(
    options,
    declaration.declarations[0],
    declaration.kind,
    getExtent(declaration),
  )
}

function itemToTree(options: OutlineOptions, item: Object): ?OutlineTree {
  if (item == null) {
    return null
  }
  const extent = getExtent(item)
  switch (item.type) {
    case 'FunctionDeclaration':
    case 'ArrowFunctionExpression':
      return functionOutline(
        options,
        item.id != null ? item.id.name : '',
        item.params,
        extent,
      )
    case 'ClassDeclaration':
    case 'ClassExpression': {
      let representativeName
      if (item.id != null) {
        representativeName = item.id.name
      }

      const tokenizedText = []
      if (options.showKeywords.class || !representativeName) {
        tokenizedText.push(Text.keyword('class'))
      }
      if (representativeName) {
        if (options.showKeywords.class) {
          tokenizedText.push(Text.whitespace(' '))
        }
        tokenizedText.push(Text.className(representativeName))
      }
      return {
        kind: 'class',
        tokenizedText,
        representativeName,
        children: itemsToTrees(options, item.body.body), // eslint-disable-line
        ...extent,
      }
    }
    case 'ClassProperty': {
      if (item.value && item.value.type === 'ArrowFunctionExpression') {
        const tokenizedText = [Text.method(item.key.name)]
        if (options.showFunctionArgs) {
          tokenizedText.push(
            Text.plain('('),
            ...declarationsTokenizedText(options, item.value.params),
            Text.plain(')'),
          )
        }
        return {
          kind: 'method',
          tokenizedText,
          representativeName: item.key.name,
          children: [],
          ...extent,
        }
      }
      return {
        kind: 'property',
        tokenizedText: [Text.param(item.key.name)],
        representativeName: item.key.name,
        children: [],
        ...extent,
      }
    }
    case 'MethodDefinition': {
      const tokenizedText = [Text.method(item.key.name)]
      if (options.showFunctionArgs) {
        tokenizedText.push(
          Text.plain('('),
          ...declarationsTokenizedText(options, item.value.params),
          Text.plain(')'),
        )
      }
      return {
        kind: 'method',
        tokenizedText,
        representativeName: item.key.name,
        children: [],
        ...extent,
      }
    }
    case 'ExportDeclaration':
    case 'ExportNamedDeclaration':
      return exportDeclaration(options, item, extent, Boolean(item.default))
    case 'ExportDefaultDeclaration':
      return exportDeclaration(options, item, extent, true)
    case 'ExpressionStatement':
      return topLevelExpressionOutline(options, item)
    case 'DeclareTypeAlias':
    case 'TypeAlias':
      return typeAliasOutline(options, item)
    case 'VariableDeclaration':
      return variableDeclarationOutline(options, item)
    default:
      return null
  }
}

function itemsToTrees(options: OutlineOptions, items: Array<Object>): Array<OutlineTree> {
  return items.map(item => itemToTree(options, item)).filter(Boolean)
}

export function astToOutline(options: OutlineOptions, ast: Object): Outline {
  return {
    outlineTrees: itemsToTrees(options, ast.body),
  }
}


================================================
FILE: lib/outline/text.js
================================================
/* @flow */

import type { TokenKind, TextToken } from 'atom-ide-ui/outline'

function buildToken(kind: TokenKind, value: string): TextToken {
  return { kind, value }
}

export function keyword(value: string): TextToken {
  return buildToken('keyword', value)
}

export function className(value: string): TextToken {
  return buildToken('class-name', value)
}

export function constructor(value: string): TextToken {
  return buildToken('constructor', value)
}

export function method(value: string): TextToken {
  return buildToken('method', value)
}

export function param(value: string): TextToken {
  return buildToken('param', value)
}

export function string(value: string): TextToken {
  return buildToken('string', value)
}

export function whitespace(value: string): TextToken {
  return buildToken('whitespace', value)
}

export function plain(value: string): TextToken {
  return buildToken('plain', value)
}

export function type(value: string): TextToken {
  return buildToken('type', value)
}


================================================
FILE: lib/outline/types.js
================================================
/* @flow */

import type { Point } from 'atom'

export type OutlineOptions = {
  showKeywords: {
    export: boolean,
    default: boolean,
    const: boolean,
    var: boolean,
    let: boolean,
    class: boolean,
    function: boolean,
    type: boolean,
  },
  showFunctionArgs: boolean
};

export type Extent = {|
  startPosition: Point,
  endPosition: Point,
|};


================================================
FILE: lib/types.js
================================================
/* @flow */

export type Position = {
  line: number,
  column: number,
  offset: number,
  end: number,
}

export type Location = {
  source: string,
  type: string,
  start: Position,
  end: Position,
}

export type CoverageObject = {
  expressions: {
    covered_count: number,
    uncovered_count: number,
    uncovered_locs: Location[],
  }
}

export type TypeAtPosObject = {
  type: string,
  reasons: Array<{
    desc: string,
    loc: Location,
    path: string,
    line: number,
    endline: number,
    start: number,
    end: number,
  }>,
  loc: Location,
  path: string,
  line: number,
  endline: number,
  start: number,
  end: number,
}


================================================
FILE: package.json
================================================
{
  "name": "flow-ide",
  "private": true,
  "version": "1.13.0",
  "description": "Flowtype support in Atom without any bloatware",
  "main": "lib/main.js",
  "scripts": {
    "test": "apm test",
    "lint": "eslint ."
  },
  "package-deps": [
    "linter",
    "hyperclick"
  ],
  "engines": {
    "atom": ">=1.4.0 <2.0.0"
  },
  "providedServices": {
    "autocomplete.provider": {
      "versions": {
        "2.0.0": "provideAutocomplete"
      }
    },
    "hyperclick.provider": {
      "versions": {
        "0.0.0": "provideHyperclick"
      }
    },
    "linter": {
      "versions": {
        "2.0.0": "provideLinter"
      }
    },
    "outline-view": {
      "versions": {
        "0.1.0": "provideOutlines"
      }
    },
    "code-actions": {
      "versions": {
        "0.1.0": "provideCodeActions"
      }
    },
    "code-format.range": {
      "versions": {
        "0.1.0": "provideCodeFormat"
      }
    },
    "code-highlight": {
      "versions": {
        "0.1.0": "provideCodeHighlight"
      }
    },
    "definitions": {
      "versions": {
        "0.1.0": "provideDefinitions"
      }
    },
    "find-references": {
      "versions": {
        "0.1.0": "provideFindReferences"
      }
    }
  },
  "consumedServices": {
    "datatip": {
      "versions": {
        "0.1.0": "consumeDatatip"
      }
    },
    "status-bar": {
      "versions": {
        "^1.0.0": "consumeStatusBar"
      }
    },
    "console": {
      "versions": {
        "0.1.0": "consumeConsole"
      }
    },
    "atom-ide-busy-signal": {
      "versions": {
        "0.1.0": "consumeBusySignal"
      }
    },
    "linter-indie": {
      "versions": {
        "2.0.0": "consumeLinterV2"
      }
    },
    "signature-help": {
      "versions": {
        "0.1.0": "consumeSignatureHelp"
      }
    }
  },
  "atomTranspilers": [
    {
      "glob": "{lib,spec}/**/*.js",
      "transpiler": "atom-babel6-transpiler",
      "options": {
        "cacheKeyFiles": [
          "package.json",
          ".babelrc"
        ]
      }
    }
  ],
  "repository": {
    "type": "git",
    "url": "https://github.com/steelbrain/flow-ide.git"
  },
  "keywords": [
    "flow",
    "flowtype",
    "ide",
    "javascript"
  ],
  "author": "steelbrain",
  "license": "MIT",
  "bugs": {
    "url": "https://github.com/steelbrain/flow-ide/issues"
  },
  "homepage": "https://github.com/steelbrain/flow-ide#readme",
  "dependencies": {
    "@lloiser/atom-languageclient": "^0.10.1",
    "atom-autocomplete": "^1.0.0",
    "atom-babel6-transpiler": "^1.2.0",
    "atom-linter": "^10.0.0",
    "atom-package-deps": "^4.4.1",
    "babel-plugin-transform-class-properties": "^6.24.1",
    "babel-plugin-transform-flow-strip-types": "^6.22.0",
    "babel-plugin-transform-object-rest-spread": "^6.26.0",
    "babel-preset-env": "^1.7.0",
    "flow-language-server": "^0.2.3",
    "sb-string_score": "^0.1.20",
    "semver": "^5.5.0"
  },
  "devDependencies": {
    "eslint-config-steelbrain": "^3.0.1",
    "eslint-plugin-flowtype": "^2.33.0",
    "flow-bin": "^0.91.0"
  },
  "configSchema": {
    "useLSP": {
      "title": "Use the Language Server Protocol to talk to flow, otherwise call flow",
      "description": "Changing this value requires a restart of atom.",
      "type": "boolean",
      "default": false,
      "order": 1
    },
    "executablePath": {
      "description": "Path to `flow` executable",
      "type": "string",
      "default": "",
      "order": 2
    },
    "onlyIfAppropriate": {
      "title": "Only activate when .flowconfig exists",
      "type": "boolean",
      "default": true,
      "order": 3
    },
    "stopServer": {
      "title": "Stop flow server when disabling this plugin or closing atom",
      "type": "boolean",
      "default": true,
      "order": 4
    },
    "showUncovered": {
      "title": "Show uncovered code in the editor",
      "type": "boolean",
      "default": false,
      "order": 5
    },
    "hyperclickPriority": {
      "description": "Priority to use for hyperclick provider (requires restart)",
      "type": "integer",
      "default": 0,
      "order": 6
    },
    "outline": {
      "title": "Outline",
      "type": "object",
      "order": 7,
      "properties": {
        "showExport": {
          "title": "Show 'export' keyword",
          "description": "Shows a leading 'export' keyword if the variable, function or type is exported",
          "type": "boolean",
          "default": true
        },
        "showFunctionArgs": {
          "title": "Show function arguments",
          "type": "boolean",
          "default": true
        }
      }
    }
  }
}


================================================
FILE: spec/linter/v2/index-spec.js
================================================
/* @flow */
/* eslint-env jasmine */

import { findCached, exec } from 'atom-linter'
import * as LinterV2 from '../../../lib/linter/v2'

function fileUrl(file, row, column) {
  return `atom://linter?file=${encodeURIComponent(file)}&row=${row}&column=${column}`
}

describe('linter/v2', function() {
  const flowBin = findCached(__dirname, 'node_modules/.bin/flow')

  describe('toLinterMessages', function() {
    it('only excerpt', function() {
      const code = `// @flow

      type Human = { name: string }

      function getAge({ age }): number {
        return age
      }

      const p1: Human = { name: 'John Doe' }
      getAge(p1)`

      /* flow status
        Cannot call getAge with p1 bound to the first parameter because property age is missing in Human [1].

              7| }
              8|
         [1]  9| const p1: Human = { name: 'John Doe' }
             10| getAge(p1)
             11|
      */

      let result = null
      waitsForPromise(async () => {
        result = await exec(flowBin, ['check-contents', '--json', '--json-version=2'], { stdin: code })

        const messages = LinterV2.toLinterMessages(result, __filename)
        expect(messages.length).toEqual(1)
        expect(messages[0].excerpt).toEqual('Cannot call getAge with p1 bound to the first parameter because property age is missing in Human.')
      })
    })

    it('with description', function() {
      const code = `// @flow

      type Human = { name: string }
      type Man = Human & { gender: 'male' }

      function getAge({ age }): number {
        return age
      }

      const p1: Man = { name: 'John Doe', gender: 'male' }
      getAge(p1)`

      /* flow status
        Cannot call getAge with p1 bound to the first parameter because:
         - Either property age is missing in Human [1].
         - Or property age is missing in object type [2].

         [1][2]  4| type Man = Human & { gender: 'male' }
                 5|
                 6| function getAge({ age }): number {
                 7|   return age
                 8| }
                 9|
                10| const p1: Man = { name: 'John Doe', gender: 'male' }
                11| getAge(p1)
                12|
      */

      let result = null
      waitsForPromise(async () => {
        result = await exec(flowBin, ['check-contents', '--json', '--json-version=2'], { stdin: code })

        const messages = LinterV2.toLinterMessages(result, __filename)
        expect(messages.length).toEqual(1)
        expect(messages[0].excerpt).toEqual('Cannot call getAge with p1 bound to the first parameter because:')
        expect(messages[0].description).toEqual(
          ` - Either property \`age\` is missing in [\`Human\`](${fileUrl(__filename, 3, 17)}).\n` +
          ` - Or property \`age\` is missing in [object type](${fileUrl(__filename, 3, 25)}).`,
        )
      })
    })
  })
})


================================================
FILE: spec/outline/parse-sample-ast.js
================================================
/* @flow */

const lc = (line, column) => ({ line, column })
const loc = (start, end, source = null) => ({ start, end, source })

/*
declare type LocalType = {
  foo: string,
  bar: string
}
*/
export const typeDecl = {
  type: 'Program',
  loc: loc(lc(3, 0), lc(6, 1)),
  range: [13, 70],
  body: [{
    type: 'DeclareTypeAlias',
    loc: loc(lc(3, 0), lc(6, 1)),
    range: [13, 70],
    id: {
      type: 'Identifier',
      loc: loc(lc(3, 13), lc(3, 22)),
      range: [26, 35],
      name: 'LocalType',
      optional: false,
    },
    right: {
      type: 'ObjectTypeAnnotation',
      loc: loc(lc(3, 25), lc(6, 1)),
      range: [38, 70],
      exact: false,
      properties: [{
        type: 'ObjectTypeProperty',
        loc: loc(lc(4, 2), lc(4, 13)),
        range: [42, 53],
        key: {
          type: 'Identifier',
          loc: loc(lc(4, 2), lc(4, 5)),
          range: [42, 45],
          name: 'foo',
          optional: false,
        },
        value: {
          type: 'StringTypeAnnotation',
          loc: loc(lc(4, 7), lc(4, 13)),
          range: [47, 53],
        },
        optional: false,
        static: false,
        kind: 'init',
      }, {
        type: 'ObjectTypeProperty',
        loc: loc(lc(5, 2), lc(5, 13)),
        range: [57, 68],
        key: {
          type: 'Identifier',
          loc: loc(lc(5, 2), lc(5, 5)),
          range: [57, 60],
          name: 'bar',
          optional: false,
        },
        value: {
          type: 'StringTypeAnnotation',
          loc: loc(lc(5, 7), lc(5, 13)),
          range: [62, 68],
        },
        optional: false,
        static: false,
        kind: 'init',
      }],
    },
  }],
}

/*
export type ExportedType = {
  foo: string
}
*/
export const exportedTypeDecl = {
  type: 'Program',
  loc: loc(lc(3, 0), lc(5, 1)),
  range: [13, 48],
  body: [{
    type: 'ExportNamedDeclaration',
    loc: loc(lc(3, 0), lc(5, 1)),
    range: [13, 48],
    declaration: {
      type: 'TypeAlias',
      loc: loc(lc(3, 7), lc(5, 1)),
      range: [20, 48],
      id: {
        type: 'Identifier',
        loc: loc(lc(3, 12), lc(3, 15)),
        range: [25, 28],
        name: 'ExportedType',
        optional: false,
      },
      right: {
        type: 'ObjectTypeAnnotation',
        loc: loc(lc(3, 18), lc(5, 1)),
        range: [31, 48],
        exact: false,
        properties: [{
          type: 'ObjectTypeProperty',
          loc: loc(lc(4, 2), lc(4, 13)),
          range: [35, 46],
          key: {
            type: 'Identifier',
            loc: loc(lc(4, 2), lc(4, 5)),
            range: [35, 38],
            name: 'foo',
            optional: false,
          },
          value: {
            type: 'StringTypeAnnotation',
            loc: loc(lc(4, 7), lc(4, 13)),
            range: [40, 46],
          },
          optional: false,
          static: false,
          kind: 'init',
        }],
        indexers: [],
        callProperties: [],
      },
    },
    specifiers: [],
    exportKind: 'type',
  }],
}

/*
function func(foo: string, bar: string) {
}
*/
export const func = {
  type: 'Program',
  loc: loc(lc(3, 0), lc(4, 1)),
  range: [13, 59],
  body: [{
    type: 'FunctionDeclaration',
    loc: loc(lc(3, 0), lc(4, 1)),
    range: [13, 59],
    id: {
      type: 'Identifier',
      loc: loc(lc(3, 9), lc(3, 16)),
      range: [22, 29],
      name: 'func',
      optional: false,
    },
    params: [{
      type: 'Identifier',
      loc: loc(lc(3, 17), lc(3, 28)),
      range: [30, 41],
      name: 'foo',
      typeAnnotation: {
        type: 'TypeAnnotation',
        loc: loc(lc(3, 20), lc(3, 28)),
        range: [33, 41],
        typeAnnotation: {
          type: 'StringTypeAnnotation',
          loc: loc(lc(3, 22), lc(3, 28)),
          range: [35, 41],
        },
      },
      optional: false,
    }, {
      type: 'Identifier',
      loc: loc(lc(3, 30), lc(3, 41)),
      range: [43, 54],
      name: 'bar',
      typeAnnotation: {
        type: 'TypeAnnotation',
        loc: loc(lc(3, 33), lc(3, 41)),
        range: [46, 54],
        typeAnnotation: {
          type: 'StringTypeAnnotation',
          loc: loc(lc(3, 35), lc(3, 41)),
          range: [48, 54],
        },
      },
      optional: false,
    }],
    body: {
      type: 'BlockStatement',
      loc: loc(lc(3, 43), lc(4, 1)),
      range: [56, 59],
      body: [],
    },
    async: false,
    generator: false,
    expression: false,
  }],
}

/*
export function exportedFunc(fooBar: FooBar) {
}
*/
export const exportedFunc = {
  type: 'Program',
  loc: loc(lc(3, 0), lc(4, 1)),
  range: [13, 62],
  body: [{
    type: 'ExportNamedDeclaration',
    loc: loc(lc(3, 0), lc(4, 1)),
    range: [13, 62],
    declaration: {
      type: 'FunctionDeclaration',
      loc: loc(lc(3, 7), lc(4, 1)),
      range: [20, 62],
      id: {
        type: 'Identifier',
        loc: loc(lc(3, 16), lc(3, 29)),
        range: [29, 42],
        name: 'exportedFunc',
        optional: false,
      },
      params: [{
        type: 'Identifier',
        loc: loc(lc(3, 30), lc(3, 44)),
        range: [43, 57],
        name: 'fooBar',
        typeAnnotation: {
          type: 'TypeAnnotation',
          loc: loc(lc(3, 36), lc(3, 44)),
          range: [49, 57],
          typeAnnotation: {
            type: 'GenericTypeAnnotation',
            loc: loc(lc(3, 38), lc(3, 44)),
            range: [51, 57],
            id: {
              type: 'Identifier',
              loc: loc(lc(3, 38), lc(3, 44)),
              range: [51, 57],
              name: 'FooBar',
              optional: false,
            },
          },
        },
        optional: false,
      }],
      body: {
        type: 'BlockStatement',
        loc: loc(lc(3, 46), lc(4, 1)),
        range: [59, 62],
        body: [],
      },
      async: false,
      generator: false,
      expression: false,
    },
    specifiers: [],
    exportKind: 'value',
  }],
}

/*
const constantValue = {}
let letValue = 1
var varValue = ''
*/
export const variables = {
  type: 'Program',
  loc: loc(lc(3, 0), lc(5, 17)),
  range: [13, 72],
  body: [{
    type: 'VariableDeclaration',
    loc: loc(lc(3, 0), lc(3, 24)),
    range: [13, 37],
    declarations: [{
      type: 'VariableDeclarator',
      loc: loc(lc(3, 6), lc(3, 24)),
      range: [19, 37],
      id: {
        type: 'Identifier',
        loc: loc(lc(3, 6), lc(3, 19)),
        range: [19, 32],
        name: 'constantValue',
        optional: false,
      },
      init: {
        type: 'ObjectExpression',
        loc: loc(lc(3, 22), lc(3, 24)),
        range: [35, 37],
        properties: [],
      },
    }],
    kind: 'const',
  }, {
    type: 'VariableDeclaration',
    loc: loc(lc(4, 0), lc(4, 16)),
    range: [38, 54],
    declarations: [{
      type: 'VariableDeclarator',
      loc: loc(lc(4, 4), lc(4, 16)),
      range: [42, 54],
      id: {
        type: 'Identifier',
        loc: loc(lc(4, 4), lc(4, 12)),
        range: [42, 50],
        name: 'letValue',
        optional: false,
      },
      init: {
        type: 'Literal',
        loc: loc(lc(4, 15), lc(4, 16)),
        range: [53, 54],
        value: 1,
        raw: '1',
      },
    }],
    kind: 'let',
  }, {
    type: 'VariableDeclaration',
    loc: loc(lc(5, 0), lc(5, 17)),
    range: [55, 72],
    declarations: [{
      type: 'VariableDeclarator',
      loc: loc(lc(5, 4), lc(5, 17)),
      range: [59, 72],
      id: {
        type: 'Identifier',
        loc: loc(lc(5, 4), lc(5, 12)),
        range: [59, 67],
        name: 'varValue',
        optional: false,
      },
      init: {
        type: 'Literal',
        loc: loc(lc(5, 15), lc(5, 17)),
        range: [70, 72],
        value: '',
        raw: "''",
      },
    }],
    kind: 'var',
  }],
}

/*
export const exportedConstantValue = {}
export let exportedLetValue = 1
export var exportedVarValue = ''
*/
export const exportedVariables = {
  errors: [],
  tokens: [],
  type: 'Program',
  loc: loc(lc(3, 0), lc(5, 30)),
  range: [13, 111],
  body: [{
    type: 'ExportNamedDeclaration',
    loc: loc(lc(3, 0), lc(3, 37)),
    range: [13, 50],
    declaration: {
      type: 'VariableDeclaration',
      loc: loc(lc(3, 7), lc(3, 37)),
      range: [20, 50],
      declarations: [{
        type: 'VariableDeclarator',
        loc: loc(lc(3, 13), lc(3, 37)),
        range: [26, 50],
        id: {
          type: 'Identifier',
          loc: loc(lc(3, 13), lc(3, 32)),
          range: [26, 45],
          name: 'exportedConstantValue',
          optional: false,
        },
        init: {
          type: 'ObjectExpression',
          loc: loc(lc(3, 35), lc(3, 37)),
          range: [48, 50],
          properties: [],
        },
      }],
      kind: 'const',
    },
    specifiers: [],
    exportKind: 'value',
  }, {
    type: 'ExportNamedDeclaration',
    loc: loc(lc(4, 0), lc(4, 29)),
    range: [51, 80],
    declaration: {
      type: 'VariableDeclaration',
      loc: loc(lc(4, 7), lc(4, 29)),
      range: [58, 80],
      declarations: [{
        type: 'VariableDeclarator',
        loc: loc(lc(4, 11), lc(4, 29)),
        range: [62, 80],
        id: {
          type: 'Identifier',
          loc: loc(lc(4, 11), lc(4, 25)),
          range: [62, 76],
          name: 'exportedLetValue',
          optional: false,
        },
        init: {
          type: 'Literal',
          loc: loc(lc(4, 28), lc(4, 29)),
          range: [79, 80],
          value: 1,
          raw: '1',
        },
      }],
      kind: 'let',
    },
    specifiers: [],
    exportKind: 'value',
  }, {
    type: 'ExportNamedDeclaration',
    loc: loc(lc(5, 0), lc(5, 30)),
    range: [81, 111],
    declaration: {
      type: 'VariableDeclaration',
      loc: loc(lc(5, 7), lc(5, 30)),
      range: [88, 111],
      declarations: [{
        type: 'VariableDeclarator',
        loc: loc(lc(5, 11), lc(5, 30)),
        range: [92, 111],
        id: {
          type: 'Identifier',
          loc: loc(lc(5, 11), lc(5, 25)),
          range: [92, 106],
          name: 'exportedVarValue',
          optional: false,
        },
        init: {
          type: 'Literal',
          loc: loc(lc(5, 28), lc(5, 30)),
          range: [109, 111],
          value: '',
          raw: "''",
        },
      }],
      kind: 'var',
    },
    specifiers: [],
    exportKind: 'value',
  }],
}

/*
export class Component extends React.Component {
  static propTypes = {
  }

  prop = 1

  constructor(props: any) {
  }

  handleChangeEvent = (ev: Event) => {
  }
}
*/
export const classDecl = {
  errors: [],
  tokens: [],
  type: 'Program',
  loc: loc(lc(3, 0), lc(14, 1)),
  range: [13, 172],
  body: [{
    type: 'ClassDeclaration',
    loc: loc(lc(3, 0), lc(14, 1)),
    range: [13, 172],
    id: {
      type: 'Identifier',
      loc: loc(lc(3, 6), lc(3, 15)),
      range: [19, 28],
      name: 'Component',
      optional: false,
    },
    body: {
      type: 'ClassBody',
      loc: loc(lc(3, 40), lc(14, 1)),
      range: [53, 172],
      body: [{
        type: 'ClassProperty',
        loc: loc(lc(4, 2), lc(5, 3)),
        range: [57, 81],
        key: {
          type: 'Identifier',
          loc: loc(lc(4, 9), lc(4, 18)),
          range: [64, 73],
          name: 'propTypes',
          optional: false,
        },
        value: {
          type: 'ObjectExpression',
          loc: loc(lc(4, 21), lc(5, 3)),
          range: [76, 81],
          properties: [],
        },
        computed: false,
        static: true,
      }, {
        type: 'ClassProperty',
        loc: loc(lc(7, 2), lc(7, 10)),
        range: [85, 93],
        key: {
          type: 'Identifier',
          loc: loc(lc(7, 2), lc(7, 6)),
          range: [85, 89],
          name: 'prop',
          optional: false,
        },
        value: {
          type: 'Literal',
          loc: loc(lc(7, 9), lc(7, 10)),
          range: [92, 93],
          value: 1,
          raw: '1',
        },
        computed: false,
        static: false,
      }, {
        type: 'MethodDefinition',
        loc: loc(lc(9, 2), lc(10, 3)),
        range: [97, 126],
        key: {
          type: 'Identifier',
          loc: loc(lc(9, 2), lc(9, 13)),
          range: [97, 108],
          name: 'constructor',
          optional: false,
        },
        value: {
          type: 'FunctionExpression',
          loc: loc(lc(9, 13), lc(10, 3)),
          range: [108, 126],
          params: [{
            type: 'Identifier',
            loc: loc(lc(9, 14), lc(9, 24)),
            range: [109, 119],
            name: 'props',
            typeAnnotation: {
              type: 'TypeAnnotation',
              loc: loc(lc(9, 19), lc(9, 24)),
              range: [114, 119],
              typeAnnotation: {
                type: 'AnyTypeAnnotation',
                loc: loc(lc(9, 21), lc(9, 24)),
                range: [116, 119],
              },
            },
            optional: false,
          }],
          body: {
            type: 'BlockStatement',
            loc: loc(lc(9, 26), lc(10, 3)),
            range: [121, 126],
            body: [],
          },
          async: false,
          generator: false,
          expression: false,
        },
        kind: 'constructor',
        static: false,
        computed: false,
        decorators: [],
      }, {
        type: 'ClassProperty',
        loc: loc(lc(12, 2), lc(13, 3)),
        range: [130, 170],
        key: {
          type: 'Identifier',
          loc: loc(lc(12, 2), lc(12, 19)),
          range: [130, 147],
          name: 'handleChangeEvent',
          optional: false,
        },
        value: {
          type: 'ArrowFunctionExpression',
          loc: loc(lc(12, 22), lc(13, 3)),
          range: [150, 170],
          params: [{
            type: 'Identifier',
            loc: loc(lc(12, 23), lc(12, 32)),
            range: [151, 160],
            name: 'ev',
            typeAnnotation: {
              type: 'TypeAnnotation',
              loc: loc(lc(12, 25), lc(12, 32)),
              range: [153, 160],
              typeAnnotation: {
                type: 'GenericTypeAnnotation',
                loc: loc(lc(12, 27), lc(12, 32)),
                range: [155, 160],
                id: {
                  type: 'Identifier',
                  loc: loc(lc(12, 27), lc(12, 32)),
                  range: [155, 160],
                  name: 'Event',
                  optional: false,
                },
              },
            },
            optional: false,
          }],
          body: {
            type: 'BlockStatement',
            loc: loc(lc(12, 37), lc(13, 3)),
            range: [165, 170],
            body: [],
          },
          async: false,
          generator: false,
          expression: false,
        },
        computed: false,
        static: false,
      }],
    },
    superClass: {
      type: 'MemberExpression',
      loc: loc(lc(3, 24), lc(3, 39)),
      range: [37, 52],
      object: {
        type: 'Identifier',
        loc: loc(lc(3, 24), lc(3, 29)),
        range: [37, 42],
        name: 'React',
        optional: false,
      },
      property: {
        type: 'Identifier',
        loc: loc(lc(3, 30), lc(3, 39)),
        range: [43, 52],
        name: 'Component',
        optional: false,
      },
      computed: false,
    },
    implements: [],
    decorators: [],
  }],
}

/*
export class ExportedComponent extends React.Component {
  static propTypes = {
  }

  prop = 1

  constructor(props: any) {
  }

  handleChangeEvent = (ev: Event) => {
  }
}
*/
export const exportedClassDecl = {
  type: 'Program',
  loc: loc(lc(3, 0), lc(14, 1)),
  range: [13, 179],
  body: [{
    type: 'ExportNamedDeclaration',
    loc: loc(lc(3, 0), lc(14, 1)),
    range: [13, 179],
    declaration: {
      type: 'ClassDeclaration',
      loc: loc(lc(3, 7), lc(14, 1)),
      range: [20, 179],
      id: {
        type: 'Identifier',
        loc: loc(lc(3, 13), lc(3, 22)),
        range: [26, 35],
        name: 'ExportedComponent',
        optional: false,
      },
      body: {
        type: 'ClassBody',
        loc: loc(lc(3, 47), lc(14, 1)),
        range: [60, 179],
        body: [{
          type: 'ClassProperty',
          loc: loc(lc(4, 2), lc(5, 3)),
          range: [64, 88],
          key: {
            type: 'Identifier',
            loc: loc(lc(4, 9), lc(4, 18)),
            range: [71, 80],
            name: 'propTypes',
            optional: false,
          },
          value: {
            type: 'ObjectExpression',
            loc: loc(lc(4, 21), lc(5, 3)),
            range: [83, 88],
            properties: [],
          },
          computed: false,
          static: true,
        }, {
          type: 'ClassProperty',
          loc: loc(lc(7, 2), lc(7, 10)),
          range: [92, 100],
          key: {
            type: 'Identifier',
            loc: loc(lc(7, 2), lc(7, 6)),
            range: [92, 96],
            name: 'prop',
            optional: false,
          },
          value: {
            type: 'Literal',
            loc: loc(lc(7, 9), lc(7, 10)),
            range: [99, 100],
            value: 1,
            raw: '1',
          },
          computed: false,
          static: false,
        }, {
          type: 'MethodDefinition',
          loc: loc(lc(9, 2), lc(10, 3)),
          range: [104, 133],
          key: {
            type: 'Identifier',
            loc: loc(lc(9, 2), lc(9, 13)),
            range: [104, 115],
            name: 'constructor',
            optional: false,
          },
          value: {
            type: 'FunctionExpression',
            loc: loc(lc(9, 13), lc(10, 3)),
            range: [115, 133],
            params: [{
              type: 'Identifier',
              loc: loc(lc(9, 14), lc(9, 24)),
              range: [116, 126],
              name: 'props',
              typeAnnotation: {
                type: 'TypeAnnotation',
                loc: loc(lc(9, 19), lc(9, 24)),
                range: [121, 126],
                typeAnnotation: {
                  type: 'AnyTypeAnnotation',
                  loc: loc(lc(9, 21), lc(9, 24)),
                  range: [123, 126],
                },
              },
              optional: false,
            }],
            body: {
              type: 'BlockStatement',
              loc: loc(lc(9, 26), lc(10, 3)),
              range: [128, 133],
              body: [],
            },
            async: false,
            generator: false,
            expression: false,
          },
          kind: 'constructor',
          static: false,
          computed: false,
          decorators: [],
        }, {
          type: 'ClassProperty',
          loc: loc(lc(12, 2), lc(13, 3)),
          range: [137, 177],
          key: {
            type: 'Identifier',
            loc: loc(lc(12, 2), lc(12, 19)),
            range: [137, 154],
            name: 'handleChangeEvent',
            optional: false,
          },
          value: {
            type: 'ArrowFunctionExpression',
            loc: loc(lc(12, 22), lc(13, 3)),
            range: [157, 177],
            params: [{
              type: 'Identifier',
              loc: loc(lc(12, 23), lc(12, 32)),
              range: [158, 167],
              name: 'ev',
              typeAnnotation: {
                type: 'TypeAnnotation',
                loc: loc(lc(12, 25), lc(12, 32)),
                range: [160, 167],
                typeAnnotation: {
                  type: 'GenericTypeAnnotation',
                  loc: loc(lc(12, 27), lc(12, 32)),
                  range: [162, 167],
                  id: {
                    type: 'Identifier',
                    loc: loc(lc(12, 27), lc(12, 32)),
                    range: [162, 167],
                    name: 'Event',
                    optional: false,
                  },
                },
              },
              optional: false,
            }],
            body: {
              type: 'BlockStatement',
              loc: loc(lc(12, 37), lc(13, 3)),
              range: [172, 177],
              body: [],
            },
            async: false,
            generator: false,
            expression: false,
          },
          computed: false,
          static: false,
        }],
      },
      superClass: {
        type: 'MemberExpression',
        loc: loc(lc(3, 31), lc(3, 46)),
        range: [44, 59],
        object: {
          type: 'Identifier',
          loc: loc(lc(3, 31), lc(3, 36)),
          range: [44, 49],
          name: 'React',
          optional: false,
        },
        property: {
          type: 'Identifier',
          loc: loc(lc(3, 37), lc(3, 46)),
          range: [50, 59],
          name: 'Component',
          optional: false,
        },
        computed: false,
      },
      implements: [],
      decorators: [],
    },
    specifiers: [],
    exportKind: 'value',
  }],
}

/*
export const restSpread = ({ ...rest }) => rest
 */
export const exportedFuncWithObjectRest = {
  errors: [],
  tokens: [],
  type: 'Program',
  loc: loc(lc(1, 0), lc(1, 52)),
  range: [0, 52],
  body: [
    {
      type: 'ExportNamedDeclaration',
      loc: loc(lc(1, 0), lc(1, 52)),
      range: [0, 52],
      declaration: {
        type: 'VariableDeclaration',
        loc: loc(lc(1, 7), lc(1, 52)),
        range: [7, 52],
        declarations: [
          {
            type: 'VariableDeclarator',
            loc: loc(lc(1, 13), lc(1, 52)),
            range: [13, 52],
            id: {
              type: 'Identifier',
              loc: loc(lc(1, 13), lc(1, 23)),
              range: [13, 23],
              name: 'restSpread',
              optional: false,
            },
            init: {
              type: 'ArrowFunctionExpression',
              loc: loc(lc(1, 26), lc(1, 52)),
              range: [26, 52],
              params: [
                {
                  type: 'AssignmentPattern',
                  loc: loc(lc(1, 27), lc(1, 43)),
                  range: [27, 43],
                  left: {
                    type: 'ObjectPattern',
                    loc: loc(lc(1, 27), lc(1, 38)),
                    range: [27, 38],
                    properties: [
                      {
                        type: 'RestProperty',
                        loc: loc(lc(1, 29), lc(1, 36)),
                        range: [29, 36],
                        argument: {
                          type: 'Identifier',
                          loc: loc(lc(1, 32), lc(1, 36)),
                          range: [32, 36],
                          name: 'rest',
                          optional: false,
                        },
                      },
                    ],
                  },
                  right: {
                    type: 'ObjectExpression',
                    loc: loc(lc(1, 41), lc(1, 43)),
                    range: [41, 43],
                    properties: [],
                  },
                },
              ],
              body: {
                type: 'Identifier',
                loc: loc(lc(1, 48), lc(1, 52)),
                range: [48, 52],
                name: 'rest',
                optional: false,
              },
              async: false,
              generator: false,
              expression: true,
            },
          },
        ],
        kind: 'const',
      },
      specifiers: [],
      exportKind: 'value',
    },
  ],
  comments: [],
}

/*
const { first, second: renamed, ...rest } = {}
 */
export const objectDestructuring = {
  errors: [],
  tokens: [],
  type: 'Program',
  loc: loc(lc(1, 0), lc(1, 46)),
  range: [0, 46],
  body: [
    {
      type: 'VariableDeclaration',
      loc: loc(lc(1, 0), lc(1, 46)),
      range: [0, 46],
      declarations: [
        {
          type: 'VariableDeclarator',
          loc: loc(lc(1, 6), lc(1, 46)),
          range: [6, 46],
          id: {
            type: 'ObjectPattern',
            loc: loc(lc(1, 6), lc(1, 41)),
            range: [6, 41],
            properties: [
              {
                type: 'Property',
                loc: loc(lc(1, 8), lc(1, 13)),
                range: [8, 13],
                key: {
                  type: 'Identifier',
                  loc: loc(lc(1, 8), lc(1, 13)),
                  range: [8, 13],
                  name: 'first',
                  optional: false,
                },
                value: {
                  type: 'Identifier',
                  loc: loc(lc(1, 8), lc(1, 13)),
                  range: [8, 13],
                  name: 'first',
                  optional: false,
                },
                kind: 'init',
                method: false,
                shorthand: true,
                computed: false,
              },
              {
                type: 'Property',
                loc: loc(lc(1, 15), lc(1, 30)),
                range: [15, 30],
                key: {
                  type: 'Identifier',
                  loc: loc(lc(1, 15), lc(1, 21)),
                  range: [15, 21],
                  name: 'second',
                  optional: false,
                },
                value: {
                  type: 'Identifier',
                  loc: loc(lc(1, 23), lc(1, 30)),
                  range: [23, 30],
                  name: 'renamed',
                  optional: false,
                },
                kind: 'init',
                method: false,
                shorthand: false,
                computed: false,
              },
              {
                type: 'RestProperty',
                loc: loc(lc(1, 32), lc(1, 39)),
                range: [32, 39],
                argument: {
                  type: 'Identifier',
                  loc: loc(lc(1, 35), lc(1, 39)),
                  range: [35, 39],
                  name: 'rest',
                  optional: false,
                },
              },
            ],
          },
          init: {
            type: 'ObjectExpression',
            loc: loc(lc(1, 44), lc(1, 46)),
            range: [44, 46],
            properties: [],
          },
        },
      ],
      kind: 'const',
    },
  ],
  comments: [],
}

/*
const [first, , ...rest] = []
 */
export const arrayDestructuring = {
  errors: [],
  tokens: [],
  type: 'Program',
  loc: loc(lc(1, 0), lc(1, 29)),
  range: [0, 29],
  body: [
    {
      type: 'VariableDeclaration',
      loc: loc(lc(1, 0), lc(1, 29)),
      range: [0, 29],
      declarations: [
        {
          type: 'VariableDeclarator',
          loc: loc(lc(1, 6), lc(1, 29)),
          range: [6, 29],
          id: {
            type: 'ArrayPattern',
            loc: loc(lc(1, 6), lc(1, 24)),
            range: [6, 24],
            elements: [
              {
                type: 'Identifier',
                loc: loc(lc(1, 7), lc(1, 12)),
                range: [7, 12],
                name: 'first',
                typeAnnotation: null,
                optional: false,
              },
              null,
              {
                type: 'RestElement',
                loc: loc(lc(1, 16), lc(1, 23)),
                range: [16, 23],
                argument: {
                  type: 'Identifier',
                  loc: loc(lc(1, 19), lc(1, 23)),
                  range: [19, 23],
                  name: 'rest',
                  typeAnnotation: null,
                  optional: false,
                },
              },
            ],
            typeAnnotation: null,
          },
          init: {
            type: 'ArrayExpression',
            loc: loc(lc(1, 27), lc(1, 29)),
            range: [27, 29],
            elements: [],
          },
        },
      ],
      kind: 'const',
    },
  ],
  comments: [],
}


================================================
FILE: spec/outline/parse-spec.js
================================================
/* @flow */
/* eslint-env jasmine */

import {
  typeDecl, exportedTypeDecl,
  func, exportedFunc,
  variables, exportedVariables,
  classDecl, exportedClassDecl,
  objectDestructuring, arrayDestructuring,
  exportedFuncWithObjectRest,
} from './parse-sample-ast'
import * as Parse from '../../lib/outline/parse'

function buildOptions(exported, args) {
  return {
    showKeywords: {
      export: exported,
      default: false,
      const: false,
      var: false,
      let: false,
      class: false,
      function: false,
      type: false,
    },
    showFunctionArgs: args,
  }
}

function toOutlineText(item, prefix) {
  const output = []
  const { tokenizedText } = item
  if (!tokenizedText) {
    return []
  }
  const text = tokenizedText.map(tt => tt.value).join('')
  output.push(prefix + text)

  return output.concat(
    toOutlineTexts(item.children, prefix + '  '), // eslint-disable-line
  )
}

function toOutlineTexts(tree, prefix = '') {
  return tree.reduce(
    (output, item) => output.concat(toOutlineText(item, prefix)),
    [],
  )
}

describe('outline/parse', function() {
  describe('types', function() {
    it('local', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), typeDecl).outlineTrees,
      )).toEqual(
        ['LocalType'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), typeDecl).outlineTrees,
      )).toEqual(
        ['LocalType'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), typeDecl).outlineTrees,
      )).toEqual(
        ['LocalType'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), typeDecl).outlineTrees,
      )).toEqual(
        ['LocalType'],
      )
    })

    it('exported', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), exportedTypeDecl).outlineTrees,
      )).toEqual(
        ['ExportedType'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), exportedTypeDecl).outlineTrees,
      )).toEqual(
        ['export ExportedType'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), exportedTypeDecl).outlineTrees,
      )).toEqual(
        ['ExportedType'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), exportedTypeDecl).outlineTrees,
      )).toEqual(
        ['export ExportedType'],
      )
    })
  })

  describe('functions', function() {
    it('local', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), func).outlineTrees,
      )).toEqual(
        ['func'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), func).outlineTrees,
      )).toEqual(
        ['func'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), func).outlineTrees,
      )).toEqual(
        ['func(foo, bar)'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), func).outlineTrees,
      )).toEqual(
        ['func(foo, bar)'],
      )
    })

    it('exported', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), exportedFunc).outlineTrees,
      )).toEqual(
        ['exportedFunc'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), exportedFunc).outlineTrees,
      )).toEqual(
        ['export exportedFunc'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), exportedFunc).outlineTrees,
      )).toEqual(
        ['exportedFunc(fooBar)'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), exportedFunc).outlineTrees,
      )).toEqual(
        ['export exportedFunc(fooBar)'],
      )
    })
  })

  describe('variables', function() {
    it('local', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), variables).outlineTrees,
      )).toEqual(
        ['constantValue', 'letValue', 'varValue'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), variables).outlineTrees,
      )).toEqual(
        ['constantValue', 'letValue', 'varValue'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), variables).outlineTrees,
      )).toEqual(
        ['constantValue', 'letValue', 'varValue'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), variables).outlineTrees,
      )).toEqual(
        ['constantValue', 'letValue', 'varValue'],
      )
    })

    it('exported', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), exportedVariables).outlineTrees,
      )).toEqual(
        ['exportedConstantValue', 'exportedLetValue', 'exportedVarValue'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), exportedVariables).outlineTrees,
      )).toEqual(
        ['export exportedConstantValue', 'export exportedLetValue', 'export exportedVarValue'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), exportedVariables).outlineTrees,
      )).toEqual(
        ['exportedConstantValue', 'exportedLetValue', 'exportedVarValue'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), exportedVariables).outlineTrees,
      )).toEqual(
        ['export exportedConstantValue', 'export exportedLetValue', 'export exportedVarValue'],
      )
    })

    it('object destructuring', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), objectDestructuring).outlineTrees,
      )).toEqual(
        ['{first, renamed, ...rest}'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), objectDestructuring).outlineTrees,
      )).toEqual(
        ['{first, renamed, ...rest}'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), objectDestructuring).outlineTrees,
      )).toEqual(
        ['{first, renamed, ...rest}'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), objectDestructuring).outlineTrees,
      )).toEqual(
        ['{first, renamed, ...rest}'],
      )
    })

    it('array destructuring', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), arrayDestructuring).outlineTrees,
      )).toEqual(
        ['[first, , ...rest]'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), arrayDestructuring).outlineTrees,
      )).toEqual(
        ['[first, , ...rest]'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), arrayDestructuring).outlineTrees,
      )).toEqual(
        ['[first, , ...rest]'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), arrayDestructuring).outlineTrees,
      )).toEqual(
        ['[first, , ...rest]'],
      )
    })

    it('exported func with object rest', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), exportedFuncWithObjectRest).outlineTrees,
      )).toEqual(
        ['restSpread'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), exportedFuncWithObjectRest).outlineTrees,
      )).toEqual(
        ['export restSpread'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), exportedFuncWithObjectRest).outlineTrees,
      )).toEqual(
        ['restSpread({...rest})'],
      )
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), exportedFuncWithObjectRest).outlineTrees,
      )).toEqual(
        ['export restSpread({...rest})'],
      )
    })
  })

  describe('class', function() {
    it('local', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), classDecl).outlineTrees,
      )).toEqual([
        'Component',
        '  propTypes',
        '  prop',
        '  constructor',
        '  handleChangeEvent',
      ])
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), classDecl).outlineTrees,
      )).toEqual([
        'Component',
        '  propTypes',
        '  prop',
        '  constructor',
        '  handleChangeEvent',
      ])
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), classDecl).outlineTrees,
      )).toEqual([
        'Component',
        '  propTypes',
        '  prop',
        '  constructor(props)',
        '  handleChangeEvent(ev)',
      ])
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), classDecl).outlineTrees,
      )).toEqual([
        'Component',
        '  propTypes',
        '  prop',
        '  constructor(props)',
        '  handleChangeEvent(ev)',
      ])
    })

    it('exported', function() {
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, false), exportedClassDecl).outlineTrees,
      )).toEqual([
        'ExportedComponent',
        '  propTypes',
        '  prop',
        '  constructor',
        '  handleChangeEvent',
      ])
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, false), exportedClassDecl).outlineTrees,
      )).toEqual([
        'export ExportedComponent',
        '  propTypes',
        '  prop',
        '  constructor',
        '  handleChangeEvent',
      ])
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(false, true), exportedClassDecl).outlineTrees,
      )).toEqual([
        'ExportedComponent',
        '  propTypes',
        '  prop',
        '  constructor(props)',
        '  handleChangeEvent(ev)',
      ])
      expect(toOutlineTexts(
        Parse.astToOutline(buildOptions(true, true), exportedClassDecl).outlineTrees,
      )).toEqual([
        'export ExportedComponent',
        '  propTypes',
        '  prop',
        '  constructor(props)',
        '  handleChangeEvent(ev)',
      ])
    })
  })
})


================================================
FILE: styles/flow-ide.less
================================================
.flow-ide-hide {
  display: none;
}


================================================
FILE: vendor/.flowconfig
================================================
[ignore]

[include]

[libs]

[options]
Download .txt
gitextract_6xu3tfy1/

├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.json
├── .flowconfig
├── .gitignore
├── CHANGELOG.md
├── LICENSE.md
├── README.md
├── decls/
│   ├── atom.js
│   ├── hyperclick.js
│   ├── jasmine-atom.js
│   ├── jasmine.js
│   ├── linter.js
│   └── outline.js
├── lib/
│   ├── coverage-view.js
│   ├── helpers.js
│   ├── index.js
│   ├── language-client.js
│   ├── linter/
│   │   ├── v1/
│   │   │   ├── index.js
│   │   │   ├── pretty.js
│   │   │   └── types.js
│   │   └── v2/
│   │       ├── index.js
│   │       └── types.js
│   ├── main.js
│   ├── outline/
│   │   ├── index.js
│   │   ├── parse.js
│   │   ├── text.js
│   │   └── types.js
│   └── types.js
├── package.json
├── spec/
│   ├── linter/
│   │   └── v2/
│   │       └── index-spec.js
│   └── outline/
│       ├── parse-sample-ast.js
│       └── parse-spec.js
├── styles/
│   └── flow-ide.less
└── vendor/
    └── .flowconfig
Download .txt
SYMBOL INDEX (64 symbols across 14 files)

FILE: decls/atom.js
  class atom$Model (line 66) | class atom$Model {
  method startCursorBlinking (line 1127) | startCursorBlinking: () => void,
  method pixelPositionForBufferPosition (line 1184) | pixelPositionForBufferPosition(

FILE: lib/coverage-view.js
  class CoverageView (line 5) | class CoverageView extends HTMLElement {
    method initialize (line 8) | initialize(): void {
    method update (line 16) | update(json: CoverageObject): void {
    method reset (line 34) | reset() {
    method destroy (line 42) | destroy(): void {

FILE: lib/helpers.js
  function getExecutablePathSync (line 23) | function getExecutablePathSync(fileDirectory: string): string {

FILE: lib/index.js
  constant INIT_MESSAGE (line 22) | const INIT_MESSAGE = 'flow server'
  constant RECHECKING_MESSAGE (line 23) | const RECHECKING_MESSAGE = 'flow is'
  method if (line 81) | if (this.coverageView) {
  method callback (line 441) | callback() {

FILE: lib/language-client.js
  method getGrammarScopes (line 11) | getGrammarScopes() { return Helpers.grammarScopes }
  method getLanguageName (line 12) | getLanguageName() { return 'Flowtype' }
  method getServerName (line 13) | getServerName() { return 'flow' }
  method startServerProcess (line 15) | async startServerProcess(projectPath: string) {

FILE: lib/linter/v1/index.js
  function locationToRange (line 10) | function locationToRange({ start, end }: Location): Range {
  function toLinterLocation (line 16) | function toLinterLocation(loc: Location) {
  function toLinterReference (line 23) | function toLinterReference(messages: StatusMessage[]) {
  function toLinterMessages (line 36) | function toLinterMessages(contents: string): Message[] {

FILE: lib/linter/v1/pretty.js
  function fileUrl (line 14) | function fileUrl({ source, start }: Location): string {
  function linterLink (line 26) | function linterLink(loc: Location, text: string) {
  function fileLink (line 29) | function fileLink(loc: Location): string {
  function lineLink (line 32) | function lineLink(loc: Location): string {
  function mainMessageOfError (line 36) | function mainMessageOfError(error: StatusError): StatusMessage {
  function getExtraMessages (line 44) | function getExtraMessages(extra: ?StatusExtra[]): StatusMessage[] {
  function getTraceReasons (line 59) | function getTraceReasons(trace: ?StatusMessage[]): StatusMessage[] {
  function mkComment (line 66) | function mkComment(descr: string): StatusMessage {
  function getOpReason (line 70) | function getOpReason(op: ?StatusMessage): StatusMessage[] {
  function getHeader (line 80) | function getHeader(mainLoc: ?Location, kind: string, level: string): Sta...
  function prettyPrintMessage (line 108) | function prettyPrintMessage(mainFile: string, { context, descr, loc, ind...
  function mergedMessagesOfError (line 157) | function mergedMessagesOfError(error: StatusError): StatusMessage[] {
  function prettyPrintError (line 180) | function prettyPrintError(error: StatusError): string {

FILE: lib/linter/v2/index.js
  function locationToRange (line 8) | function locationToRange({ start, end }: Location): Range {
  function locactionToLinterLocation (line 15) | function locactionToLinterLocation(loc: Location) {
  function plainText (line 22) | function plainText(messages: InlineMarkup[], references: ReferenceLocs):...
  function fileUrl (line 45) | function fileUrl(loc: Location, path: string): string {
  function markup (line 63) | function markup(messages: MessageMarkup, references: ReferenceLocs, path...
  function toLinterMessages (line 95) | function toLinterMessages(json: string, path: string): Message[] {

FILE: lib/main.js
  method onDidClick (line 9) | onDidClick () { return atom.restartApplication() }

FILE: lib/outline/index.js
  function toOutline (line 7) | function toOutline(result: string, options: OutlineOptions): Outline {

FILE: lib/outline/parse.js
  function itemToTree (line 13) | function exportDeclaration(
  method if (line 319) | if (fn == null) {
  method if (line 322) | if (
  method switch (line 374) | switch (expressionStatement.expression.type) {
  method if (line 390) | if (
  method if (line 430) | if (item == null) {
  method let (line 445) | let representativeName
  method if (line 455) | if (options.showKeywords.class) {

FILE: lib/outline/text.js
  function buildToken (line 5) | function buildToken(kind: TokenKind, value: string): TextToken {
  function keyword (line 9) | function keyword(value: string): TextToken {
  function className (line 13) | function className(value: string): TextToken {
  function constructor (line 17) | function constructor(value: string): TextToken {
  function method (line 21) | function method(value: string): TextToken {
  function param (line 25) | function param(value: string): TextToken {
  function string (line 29) | function string(value: string): TextToken {
  function whitespace (line 33) | function whitespace(value: string): TextToken {
  function plain (line 37) | function plain(value: string): TextToken {
  function type (line 41) | function type(value: string): TextToken {

FILE: spec/linter/v2/index-spec.js
  function fileUrl (line 7) | function fileUrl(file, row, column) {

FILE: spec/outline/parse-spec.js
  function buildOptions (line 14) | function buildOptions(exported, args) {
  function toOutlineText (line 30) | function toOutlineText(item, prefix) {
  function toOutlineTexts (line 44) | function toOutlineTexts(tree, prefix = '') {
Condensed preview — 36 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (189K chars).
[
  {
    "path": ".babelrc",
    "chars": 221,
    "preview": "{\n  \"presets\": [\n    [\"env\", { \"targets\": { \"node\": \"current\" } }]\n  ],\n  \"sourceMap\": \"inline\",\n  \"plugins\": [\n    \"tra"
  },
  {
    "path": ".editorconfig",
    "chars": 171,
    "preview": "root = true\n\n[*]\nindent_style = space\nend_of_line = lf\ncharset = utf-8\ntrim_trailing_whitespace = true\ninsert_final_newl"
  },
  {
    "path": ".eslintignore",
    "chars": 6,
    "preview": "decls\n"
  },
  {
    "path": ".eslintrc.json",
    "chars": 289,
    "preview": "{\n  \"extends\": \"steelbrain\",\n  \"plugins\": [\n    \"flowtype\"\n  ],\n  \"rules\": {\n    \"import/prefer-default-export\": \"off\",\n"
  },
  {
    "path": ".flowconfig",
    "chars": 245,
    "preview": "[ignore]\n\n[include]\n\n[libs]\ndecls\n\n[options]\nmodule.system=node\ninclude_warnings=true\nsuppress_comment= \\\\(.\\\\|\\n\\\\)*\\\\$"
  },
  {
    "path": ".gitignore",
    "chars": 35,
    "preview": ".idea\nnode_modules\n*.log\n.DS_Store\n"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 3659,
    "preview": "#### 1.13.0\n\n- Use the official [language server protocol](https://microsoft.github.io/language-server-protocol/) to com"
  },
  {
    "path": "LICENSE.md",
    "chars": 1054,
    "preview": "Copyright (c) 2016 steelbrain\n\nPermission is hereby granted, free of charge, to any person obtaining\na copy of this soft"
  },
  {
    "path": "README.md",
    "chars": 2116,
    "preview": "Flow-IDE\n=======\n\nFlow IDE is a lightweight package that provides IDE features for [FlowType][FlowType] for [Atom Editor"
  },
  {
    "path": "decls/atom.js",
    "chars": 69354,
    "preview": "/**\n * Copyright (c) 2015-present, Facebook, Inc.\n * All rights reserved.\n *\n * This source code is licensed under the l"
  },
  {
    "path": "decls/hyperclick.js",
    "chars": 1196,
    "preview": "/* @flow */\n\ndeclare module 'atom-ide-ui/hyperclick' {\n  import type { Point, Range, TextEditor } from 'atom'\n\n  declare"
  },
  {
    "path": "decls/jasmine-atom.js",
    "chars": 186,
    "preview": "/* @flow */\n\ndeclare function waitsForPromise(\n  optionsOrFunc: {timeout?: number, shouldReject?: boolean, label?: strin"
  },
  {
    "path": "decls/jasmine.js",
    "chars": 544,
    "preview": "/* @flow */\n\ndeclare var jasmine: Object;\ndeclare function it(name: string, callback: (() => void)): void;\ndeclare funct"
  },
  {
    "path": "decls/linter.js",
    "chars": 741,
    "preview": "/* @flow */\n\ndeclare module 'linter' {\n  import type { Point, Range } from 'atom'\n\n  declare type LinterProvider = {\n  }"
  },
  {
    "path": "decls/outline.js",
    "chars": 1686,
    "preview": "/* @flow */\n\ndeclare module 'atom-ide-ui/outline' {\n  import type { Point, TextEditor } from 'atom'\n\n  declare type Toke"
  },
  {
    "path": "lib/coverage-view.js",
    "chars": 1383,
    "preview": "/* @flow */\n\nimport type { CoverageObject } from './types'\n\nclass CoverageView extends HTMLElement {\n  tooltipDisposable"
  },
  {
    "path": "lib/helpers.js",
    "chars": 1044,
    "preview": "/* @flow */\n\nimport { findCached, findCachedAsync } from 'atom-linter'\nimport * as Path from 'path'\n\nconst executable = "
  },
  {
    "path": "lib/index.js",
    "chars": 19931,
    "preview": "/* @flow */\n\nimport { CompositeDisposable } from 'atom'\nimport type { TextEditor, Point, Range } from 'atom'\nimport { sh"
  },
  {
    "path": "lib/language-client.js",
    "chars": 945,
    "preview": "/* @flow */\n\nimport { TextEditor } from 'atom'\nimport { AutoLanguageClient } from '@lloiser/atom-languageclient'\nimport "
  },
  {
    "path": "lib/linter/v1/index.js",
    "chars": 1643,
    "preview": "/* @flow */\n\nimport { Range } from 'atom'\nimport type { Message } from 'linter'\n\nimport { mainMessageOfError, prettyPrin"
  },
  {
    "path": "lib/linter/v1/pretty.js",
    "chars": 5569,
    "preview": "/* @flow */\n\n// Note: the following code is based on\n// https://github.com/facebook/flow/blob/v0.47.0/tsrc/flowResult.js"
  },
  {
    "path": "lib/linter/v1/types.js",
    "chars": 622,
    "preview": "/* @flow */\n\nimport type { Location } from '../../types'\n\nexport type StatusMessage = {\n  context: ?string,\n  descr: str"
  },
  {
    "path": "lib/linter/v2/index.js",
    "chars": 3098,
    "preview": "/* @flow */\n\nimport { Range } from 'atom'\nimport type { Message } from 'linter'\n\nimport type { Location, MessageMarkup, "
  },
  {
    "path": "lib/linter/v2/types.js",
    "chars": 1965,
    "preview": "/* @flow */\n\nexport type Position = {\n  line: number,\n  column: number,\n  offset: number\n}\n\nexport type Location = {\n  s"
  },
  {
    "path": "lib/main.js",
    "chars": 734,
    "preview": "/* @flow */\n\nconst useLSP = atom.config.get('flow-ide.useLSP')\n\n// notify to restart if the value changes\natom.config.on"
  },
  {
    "path": "lib/outline/index.js",
    "chars": 305,
    "preview": "/* @flow */\n\nimport type { Outline } from 'atom-ide-ui/outline'\nimport { astToOutline } from './parse'\nimport type { Out"
  },
  {
    "path": "lib/outline/parse.js",
    "chars": 14324,
    "preview": "/* @flow */\n\n// NOTE: the following code is based on\n// https://github.com/flowtype/flow-language-server/blob/fbd1bc3/sr"
  },
  {
    "path": "lib/outline/text.js",
    "chars": 1008,
    "preview": "/* @flow */\n\nimport type { TokenKind, TextToken } from 'atom-ide-ui/outline'\n\nfunction buildToken(kind: TokenKind, value"
  },
  {
    "path": "lib/outline/types.js",
    "chars": 369,
    "preview": "/* @flow */\n\nimport type { Point } from 'atom'\n\nexport type OutlineOptions = {\n  showKeywords: {\n    export: boolean,\n  "
  },
  {
    "path": "lib/types.js",
    "chars": 654,
    "preview": "/* @flow */\n\nexport type Position = {\n  line: number,\n  column: number,\n  offset: number,\n  end: number,\n}\n\nexport type "
  },
  {
    "path": "package.json",
    "chars": 4634,
    "preview": "{\n  \"name\": \"flow-ide\",\n  \"private\": true,\n  \"version\": \"1.13.0\",\n  \"description\": \"Flowtype support in Atom without any"
  },
  {
    "path": "spec/linter/v2/index-spec.js",
    "chars": 2886,
    "preview": "/* @flow */\n/* eslint-env jasmine */\n\nimport { findCached, exec } from 'atom-linter'\nimport * as LinterV2 from '../../.."
  },
  {
    "path": "spec/outline/parse-sample-ast.js",
    "chars": 27680,
    "preview": "/* @flow */\n\nconst lc = (line, column) => ({ line, column })\nconst loc = (start, end, source = null) => ({ start, end, s"
  },
  {
    "path": "spec/outline/parse-spec.js",
    "chars": 10307,
    "preview": "/* @flow */\n/* eslint-env jasmine */\n\nimport {\n  typeDecl, exportedTypeDecl,\n  func, exportedFunc,\n  variables, exported"
  },
  {
    "path": "styles/flow-ide.less",
    "chars": 36,
    "preview": ".flow-ide-hide {\n  display: none;\n}\n"
  },
  {
    "path": "vendor/.flowconfig",
    "chars": 39,
    "preview": "[ignore]\n\n[include]\n\n[libs]\n\n[options]\n"
  }
]

About this extraction

This page contains the full source code of the steelbrain/flow-ide GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 36 files (176.4 KB), approximately 47.2k tokens, and a symbol index with 64 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!