diff --git a/.babelrc b/.babelrc index 398b968a..fe4071a4 100644 --- a/.babelrc +++ b/.babelrc @@ -1,7 +1,6 @@ { "sourceMap": "inline", "plugins": [ - ["add-module-exports", {}], ["transform-class-properties", {}], ["transform-es2015-modules-commonjs", {"strictMode": false}], ["transform-object-rest-spread", {}], diff --git a/.travis.yml b/.travis.yml index aab23abd..59c077b7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,13 +10,10 @@ os: - linux - osx -osx_image: xcode8.3 - env: global: - GO111MODULE=off - APM_TEST_PACKAGES="go-signature-statusbar go-debug" - - FSEVENTS_BUILD_FROM_SOURCE=true matrix: - ATOM_CHANNEL=beta - ATOM_CHANNEL=stable diff --git a/flow-libs/atom.js.flow b/flow-libs/atom.js.flow index 0f4d5e66..04dcfa7f 100644 --- a/flow-libs/atom.js.flow +++ b/flow-libs/atom.js.flow @@ -274,6 +274,7 @@ declare class atom$Decoration { } declare class atom$DisplayMarkerLayer { + id: number, destroy(): void, clear(): void, isDestroyed(): boolean, @@ -789,6 +790,12 @@ type DecorateMarkerParams = { onlyHead?: boolean, onlyEmpty?: boolean, onlyNonEmpty?: boolean, +} | { + type: 'line-number', + class: string, + onlyHead?: boolean, + onlyEmpty?: boolean, + onlyNonEmpty?: boolean, } | { type: 'gutter', item?: HTMLElement, @@ -950,6 +957,7 @@ declare class atom$TextEditor extends atom$Model { // Markers addMarkerLayer(): atom$DisplayMarkerLayer, getDefaultMarkerLayer(): atom$DisplayMarkerLayer, + getMarkerLayer(id: number): 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, diff --git a/lib/ansi.js b/lib/ansi.js index 969ac56b..3cabda44 100644 --- a/lib/ansi.js +++ b/lib/ansi.js @@ -3,10 +3,10 @@ /* eslint-disable react/display-name */ import etch from 'etch' // eslint-disable-line -import EtchComponent from './etch-component' +import { EtchComponent } from './etch-component' import parser from 'ansi-style-parser' -export default class AnsiStyle extends EtchComponent { +export class AnsiStyle extends EtchComponent { props: { text?: string, mapText?: string => any } constructor(props: Object) { diff --git a/lib/bootstrap.js b/lib/bootstrap.js index ba8bdd61..c71a8dff 100644 --- a/lib/bootstrap.js +++ b/lib/bootstrap.js @@ -58,7 +58,6 @@ class Bootstrap { } subscribeToEvents() { - global.jasmineLog && global.jasmineLog('bootstrap - subscribeToEvents') const activationHook = (hookName, fn) => { const hooks: any = (atom.packages: any).triggeredActivationHooks if (hooks && hooks.has(hookName)) { @@ -88,19 +87,16 @@ class Bootstrap { } setEnvironmentLoaded() { - global.jasmineLog && global.jasmineLog('bootstrap - setEnvironmentLoaded') this.environmentLoaded = true this.check() } setGrammarUsed() { - global.jasmineLog && global.jasmineLog('bootstrap - setGrammarUsed') this.grammarUsed = true this.check() } setCommandUsed() { - global.jasmineLog && global.jasmineLog('bootstrap - setCommandUsed') this.commandUsed = true this.check() } @@ -111,7 +107,6 @@ class Bootstrap { } if (this.environmentLoaded && (this.grammarUsed || this.commandUsed)) { - global.jasmineLog && global.jasmineLog('bootstrap - activated') this.activated = true this.subscriptions.dispose() if (this.onActivated) { diff --git a/lib/config/environment.js b/lib/config/environment.js index 69befec5..ecb86cb4 100644 --- a/lib/config/environment.js +++ b/lib/config/environment.js @@ -1,6 +1,6 @@ // @flow -import pathhelper from './pathhelper' +import * as pathhelper from './pathhelper' import path from 'path' const getenvironment = (): { [string]: ?string } => { diff --git a/lib/config/locator.js b/lib/config/locator.js index d3b9ec89..ab453ded 100644 --- a/lib/config/locator.js +++ b/lib/config/locator.js @@ -2,7 +2,7 @@ import { CompositeDisposable } from 'atom' import { getenvironment, getgopath } from './environment' -import pathhelper from './pathhelper' +import * as pathhelper from './pathhelper' import fs from 'fs' import os from 'os' import path from 'path' diff --git a/lib/config/pathhelper.js b/lib/config/pathhelper.js index cbe69502..04c40505 100644 --- a/lib/config/pathhelper.js +++ b/lib/config/pathhelper.js @@ -3,7 +3,7 @@ import path from 'path' import os from 'os' -function expand(env: { [string]: ?string }, thepath: string): string { +export function expand(env: { [string]: ?string }, thepath: string): string { if (!thepath || thepath.trim() === '') { return '' } @@ -53,7 +53,7 @@ function expand(env: { [string]: ?string }, thepath: string): string { return result } -function resolveAndNormalize(pathitem: string): string { +export function resolveAndNormalize(pathitem: string): string { if (!pathitem || pathitem.trim() === '') { return '' } @@ -61,8 +61,6 @@ function resolveAndNormalize(pathitem: string): string { return result } -function home() { +export function home() { return os.homedir() } - -export default { expand, resolveAndNormalize, home } diff --git a/lib/doc/godoc-panel.js b/lib/doc/godoc-panel.js index 4971bc94..526554a9 100644 --- a/lib/doc/godoc-panel.js +++ b/lib/doc/godoc-panel.js @@ -2,9 +2,9 @@ import type { Tab, PanelModel } from './../panel/tab' import type { GogetdocResult } from './godoc' -import type GodocView from './godoc-view' +import type { GodocView } from './godoc-view' -export default class GodocPanel implements PanelModel { +export class GodocPanel implements PanelModel { key: string tab: Tab keymap: string @@ -16,6 +16,7 @@ export default class GodocPanel implements PanelModel { constructor() { this.key = 'reference' this.tab = { + key: 'reference', name: 'Reference', packageName: 'go-plus', icon: 'book', diff --git a/lib/doc/godoc-view.js b/lib/doc/godoc-view.js index ae19086e..8024e11f 100644 --- a/lib/doc/godoc-view.js +++ b/lib/doc/godoc-view.js @@ -1,13 +1,12 @@ // @flow -/** @babel */ /** @jsx etch.dom */ import etch from 'etch' -import EtchComponent from './../etch-component' +import { EtchComponent } from './../etch-component' -import type GodocPanel from './godoc-panel' +import type { GodocPanel } from './godoc-panel' -export default class GodocView extends EtchComponent { +export class GodocView extends EtchComponent { props: { model: GodocPanel } constructor(props: { model: GodocPanel }) { diff --git a/lib/doc/godoc.js b/lib/doc/godoc.js index dc031191..9ae0cfa2 100644 --- a/lib/doc/godoc.js +++ b/lib/doc/godoc.js @@ -1,7 +1,7 @@ // @flow import { CompositeDisposable, Point, Range } from 'atom' -import GodocPanel from './godoc-panel' +import { GodocPanel } from './godoc-panel' import { utf8OffsetForBufferPosition } from '../utils' import { buildGuruArchive } from '../guru-utils' import os from 'os' diff --git a/lib/etch-component.js b/lib/etch-component.js index 3046a152..84b95a55 100644 --- a/lib/etch-component.js +++ b/lib/etch-component.js @@ -3,11 +3,15 @@ import etch from 'etch' +export interface Renderable { + render(): any; +} + /* Public: Abstract class for handling the initialization boilerplate of an Etch component. */ -export default class EtchComponent { +export class EtchComponent implements Renderable { props: any refs: Object element: HTMLElement diff --git a/lib/get/get-manager.js b/lib/get/get-manager.js index 0d3226a4..12deb4b8 100644 --- a/lib/get/get-manager.js +++ b/lib/get/get-manager.js @@ -2,7 +2,7 @@ import os from 'os' import { CompositeDisposable, Disposable } from 'atom' -import SimpleDialog from './../simple-dialog' +import { SimpleDialog } from './../simple-dialog' import { promiseWaterfall } from './../promise' import type { GoConfig } from './../config/service' import type { ExecResult } from './../config/executor' @@ -31,7 +31,7 @@ export type InteractiveGetOptions = { class GetManager { goconfig: GoConfig outputFunc: () => OutputManager - busySignal: () => BusySignalService + busySignal: () => ?BusySignalService packages: Map onDidUpdateTools: Set<(MultiGetResult, Array) => void> subscriptions: CompositeDisposable @@ -39,7 +39,7 @@ class GetManager { constructor( goconfig: GoConfig, outputFunc: () => OutputManager, - busySignal: () => BusySignalService + busySignal: () => ?BusySignalService ) { this.goconfig = goconfig this.outputFunc = outputFunc diff --git a/lib/get/service.js b/lib/get/service.js index d14ad8b3..569478f9 100644 --- a/lib/get/service.js +++ b/lib/get/service.js @@ -18,7 +18,7 @@ class GetService { constructor( goconfig: GoConfig, getOutput: Function, - busySignal: () => BusySignalService + busySignal: () => ?BusySignalService ) { this.getmanager = new GetManager(goconfig, getOutput, busySignal) } diff --git a/lib/implements/implements-view.js b/lib/implements/implements-view.js index 8d96eacb..0e72b98d 100644 --- a/lib/implements/implements-view.js +++ b/lib/implements/implements-view.js @@ -1,11 +1,10 @@ // @flow -/** @babel */ /** @jsx etch.dom */ /* eslint-disable react/no-unknown-property */ /* eslint-disable react/jsx-key */ import etch from 'etch' -import EtchComponent from './../etch-component' +import { EtchComponent } from './../etch-component' import { parseGoPosition, openFile } from './../utils' import type { Implements } from './implements' diff --git a/lib/implements/implements.js b/lib/implements/implements.js index 0fc1361c..64e6779d 100644 --- a/lib/implements/implements.js +++ b/lib/implements/implements.js @@ -21,6 +21,7 @@ class Implements implements PanelModel { this.key = 'implements' this.tab = { + key: 'implements', name: 'Implements', packageName: 'go-plus', icon: 'tasklist', diff --git a/lib/import/importer-view.js b/lib/import/importer-view.js index 7f3ac3ab..1bbe547f 100644 --- a/lib/import/importer-view.js +++ b/lib/import/importer-view.js @@ -2,7 +2,7 @@ import SelectListView from 'atom-select-list' -export default class ImporterView { +export class ImporterView { modalPanel: any selectListView: SelectListView previouslyFocusedElement: ?HTMLElement diff --git a/lib/import/importer.js b/lib/import/importer.js index cb237da3..10d04e9f 100644 --- a/lib/import/importer.js +++ b/lib/import/importer.js @@ -2,7 +2,7 @@ import { CompositeDisposable } from 'atom' import path from 'path' -import ImporterView from './importer-view' +import { ImporterView } from './importer-view' import { getEditor } from './../utils' import { allPackages } from './../go' import * as gcph from './../autocomplete/gocodeprovider-helper' diff --git a/lib/info/information-view.js b/lib/info/information-view.js index 9004b2e5..a873424f 100644 --- a/lib/info/information-view.js +++ b/lib/info/information-view.js @@ -1,11 +1,10 @@ // @flow -/** @babel */ /** @jsx etch.dom */ /* eslint-disable react/no-unknown-property */ /* eslint-disable react/no-string-refs */ import etch from 'etch' -import EtchComponent from './../etch-component' +import { EtchComponent } from './../etch-component' import type { Information } from './information' @@ -15,7 +14,7 @@ type Props = { content: string } -export default class InformationView extends EtchComponent { +export class InformationView extends EtchComponent { props: Props constructor(props: Props) { diff --git a/lib/info/information.js b/lib/info/information.js index e371321d..2ce9bb6b 100644 --- a/lib/info/information.js +++ b/lib/info/information.js @@ -4,7 +4,7 @@ import os from 'os' import type { GoConfig } from './../config/service' import type { PanelModel, Tab } from './../panel/tab' -import type InformationView from './information-view' +import type { InformationView } from './information-view' class Information implements PanelModel { goconfig: GoConfig @@ -17,6 +17,7 @@ class Information implements PanelModel { this.goconfig = goconfig this.key = 'go' this.tab = { + key: 'go', name: 'Go', packageName: 'go-plus', icon: 'info', diff --git a/lib/main.js b/lib/main.js index 5345e019..1db52c96 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,41 +1,47 @@ // @flow import { CompositeDisposable, Disposable } from 'atom' - -global.jasmineLog && global.jasmineLog('main - loaded') - -export default { - bootstrap: null, - bootstrapped: null, - builder: null, - busySignal: null, - configservice: null, - console: null, - formatter: null, - getservice: null, - navigator: null, - godoc: null, - gomodifytags: null, - gorename: null, - information: null, - implements: null, - importer: null, - linter: null, - loaded: null, - orchestrator: null, - outputManager: null, - panelManager: null, - statusbar: null, - subscriptions: null, - tester: null, - references: null, - highlight: null, - outlineProvider: null, +import type { PanelModel } from './panel/tab' +import type { Renderable } from './etch-component' + +const minimumVersion = '1.12.7' + +class Main { + bootstrap = null + bootstrapped = null + builder = null + busySignal = null + configservice = null + console = null + formatter = null + getservice = null + navigator = null + godoc = null + gomodifytags = null + gorename = null + information = null + implements = null + importer = null + linter = null + loaded = null + orchestrator = null + outputManager = null + panelManager = null + statusbar = null + subscriptions: CompositeDisposable + tester = null + references = null + highlight = null + outlineProvider = null + autocompleteProvider = null + definitionProvider = null + packagemanager = null + buildLinter: any = null + gometalinterLinter: any = null activate() { - global.jasmineLog && global.jasmineLog('main - activated') this.subscriptions = new CompositeDisposable() - this.minimumVersion = '1.12.7' + this.validateAtomVersion() this.bootstrapped = false this.loaded = false @@ -52,18 +58,18 @@ export default { this.checkFormatOnSave() }) this.subscriptions.add(this.bootstrap) - }, + } deactivate() { this.dispose() - }, + } dispose() { this.bootstrapped = false this.loaded = false if (this.subscriptions) { this.subscriptions.dispose() - this.subscriptions = null + this.subscriptions = (null: any) } if (this.console) { this.console.dispose() @@ -92,7 +98,7 @@ export default { this.autocompleteProvider = null this.definitionProvider = null - }, + } load() { this.getPanelManager() @@ -112,47 +118,51 @@ export default { } this.getPanelManager().requestUpdate() - this.subscriptions.add( - this.orchestrator.register('builder', (editor, path) => { + const { subscriptions, orchestrator } = this + if (!subscriptions || !orchestrator) { + return + } + + subscriptions.add( + orchestrator.register('builder', (editor: TextEditor, path: string) => { if (this.builder) this.builder.build(editor, path) - return }) ) - this.subscriptions.add( - this.orchestrator.register('tester', (editor, path) => { - if (this.tester) this.tester.handleSaveEvent(editor, path) - return + subscriptions.add( + orchestrator.register('tester', (editor: TextEditor, path: string) => { + void path + if (this.tester) this.tester.handleSaveEvent(editor) }) ) - this.subscriptions.add( - this.orchestrator.register('linter', (editor, path) => { - if (this.golinter) this.golinter.lint(editor, path) - return + subscriptions.add( + orchestrator.register('linter', (editor: TextEditor, path: string) => { + void path + if (this.golinter) this.golinter.lint(editor) }) ) this.loaded = true - }, + } loadInformation() { if (this.information) { return this.information } - const InformationView = require('./info/information-view') + const { InformationView } = require('./info/information-view') const { Information } = require('./info/information') - this.information = new Information(this.provideGoConfig()) + const information = new Information(this.provideGoConfig()) + this.information = information const view = this.consumeViewProvider({ view: InformationView, - model: this.information + model: information }) if (this.subscriptions) { - this.subscriptions.add(this.information) - this.subscriptions.add(view) + this.subscriptions.add(information, view) } return this.information - }, + } loadImporter() { if (this.importer) { @@ -161,7 +171,7 @@ export default { const { Importer } = require('./import/importer') this.importer = new Importer(this.provideGoConfig()) return this.importer - }, + } loadDoc() { if (this.godoc) { @@ -169,38 +179,38 @@ export default { } const { Godoc } = require('./doc/godoc') - this.godoc = new Godoc(this.provideGoConfig()) + const godoc = new Godoc(this.provideGoConfig()) + this.godoc = godoc - const GodocView = require('./doc/godoc-view') + const { GodocView } = require('./doc/godoc-view') const view = this.consumeViewProvider({ view: GodocView, - model: this.godoc.getPanel() + model: godoc.getPanel() }) if (this.subscriptions) { - this.subscriptions.add(this.godoc) - this.subscriptions.add(view) + this.subscriptions.add(godoc, view) } - return this.godoc - }, + return godoc + } loadImplements() { if (this.implements) { return this.implements } const { Implements } = require('./implements/implements') - this.implements = new Implements(this.provideGoConfig()) + const impls = new Implements(this.provideGoConfig()) + this.implements = impls const { ImplementsView } = require('./implements/implements-view') const view = this.consumeViewProvider({ view: ImplementsView, - model: this.implements + model: impls }) if (this.subscriptions) { - this.subscriptions.add(this.implements) - this.subscriptions.add(view) + this.subscriptions.add(impls, view) } - return this.implements - }, + return impls + } provideOutlines() { if (this.outlineProvider) { @@ -209,7 +219,7 @@ export default { const { OutlineProvider } = require('./outline/outline-provider') this.outlineProvider = new OutlineProvider(this.provideGoConfig()) return this.outlineProvider - }, + } provideCodeHighlight() { if (this.highlight) { @@ -224,16 +234,17 @@ export default { } return this.highlight - }, + } loadOutput() { if (this.outputManager) { return this.outputManager } const { OutputManager } = require('./output-manager') - this.outputManager = new OutputManager() + const outputManager = new OutputManager() + this.outputManager = outputManager - const OutputPanel = require('./output-panel') + const { OutputPanel } = require('./output-panel') const view = this.consumeViewProvider({ view: OutputPanel, model: this.outputManager @@ -243,8 +254,8 @@ export default { this.subscriptions.add(view) } - return this.outputManager - }, + return outputManager + } provideCodeFormatter() { if (this.formatter) { @@ -256,7 +267,7 @@ export default { this.subscriptions.add(this.formatter) } return this.formatter - }, + } loadTester() { if (this.tester) { @@ -273,12 +284,8 @@ export default { this.subscriptions.add(this.tester) } - if (this.subscriptions) { - this.subscriptions.add(this.tester) - } - return this.tester - }, + } loadGorename() { if (this.gorename) { @@ -291,7 +298,7 @@ export default { } return this.gorename - }, + } loadGoModifyTags() { if (this.gomodifytags) { @@ -303,7 +310,7 @@ export default { this.subscriptions.add(this.gomodifytags) } return this.gomodifytags - }, + } loadBuilder() { if (this.builder) { @@ -322,7 +329,7 @@ export default { } return this.builder - }, + } loadLinter() { if (this.golinter) { @@ -340,7 +347,7 @@ export default { } return this.golinter - }, + } loadNavigator() { if (this.navigator) { @@ -354,7 +361,7 @@ export default { } return this.navigator - }, + } getPanelManager() { if (this.panelManager) { @@ -368,7 +375,7 @@ export default { } return this.panelManager - }, + } showPanel() { if (this.bootstrapped) { @@ -377,7 +384,7 @@ export default { } this.getPanelManager().togglePanel(true) } - }, + } loadPackageManager() { if (this.packagemanager) { @@ -395,15 +402,15 @@ export default { } return this.packagemanager - }, + } consumeStatusBar(service: any) { this.statusbar = service - }, + } consumeBusySignal(service: any) { this.busySignal = service - }, + } consumeConsole(createConsole: Function) { this.console = createConsole({ id: 'go-plus', name: 'go-plus' }) @@ -413,29 +420,35 @@ export default { this.console = null } }) - }, + } - consumeViewProvider(provider: any) { + consumeViewProvider(provider: { + view: Class, + model: PanelModel + }) { if (!provider) { - return + // for simplified type handling just assume + // that this never happens for our own code + return (null: any) } return this.getPanelManager().registerViewProvider( provider.view, provider.model ) - }, + } consumeLinter(registry: any) { this.buildLinter = registry({ name: 'go build' }) - this.subscriptions.add(this.buildLinter) this.linter = registry({ name: 'go linter' }) - this.subscriptions.add(this.linter) - }, + if (this.subscriptions) { + this.subscriptions.add(this.buildLinter, this.linter) + } + } consumeDatatipService(service: any) { service.addProvider(this.loadDoc()) - }, + } provideGoConfig() { if (this.configservice) { @@ -443,9 +456,11 @@ export default { } const { ConfigService } = require('./config/service') this.configservice = new ConfigService(() => this.console) - this.subscriptions.add(this.configservice) + if (this.subscriptions) { + this.subscriptions.add(this.configservice) + } return this.configservice.provide() - }, + } provideGoGet() { if (this.getservice) { @@ -458,7 +473,7 @@ export default { () => this.busySignal ) return this.getservice.provide() - }, + } provideAutocomplete() { if (this.autocompleteProvider) { @@ -472,7 +487,7 @@ export default { } return this.autocompleteProvider - }, + } provideReferences() { if (this.references) { @@ -482,7 +497,7 @@ export default { const { ReferencesProvider } = require('./references/references-provider') this.references = new ReferencesProvider(this.provideGoConfig()) return this.references - }, + } provideDefinitions() { if (this.definitionProvider) { @@ -496,7 +511,7 @@ export default { } return this.definitionProvider - }, + } checkFormatOnSave() { const skip = atom.config.get('go-plus.skipCodeFormatCheck') @@ -533,11 +548,11 @@ export default { "In order for go-plus to format code on save, `atom-ide-ui`'s " + 'format on save option must be enabled. Would you like to enable it now?' }) - }, + } validateAtomVersion() { const semver = require('semver') - if (semver.lt(atom.appVersion, this.minimumVersion)) { + if (semver.lt(atom.appVersion, minimumVersion)) { const os = require('os') const notification = atom.notifications.addError('go-plus', { dismissable: true, @@ -545,7 +560,7 @@ export default { detail: 'you are running an old version of Atom', description: '`go-plus` requires at least `v' + - this.minimumVersion + + minimumVersion + '` but you are running v`' + atom.appVersion + '`.' + @@ -563,3 +578,5 @@ export default { } } } + +module.exports = new Main() diff --git a/lib/orchestrator.js b/lib/orchestrator.js index 2174d09b..09cca037 100644 --- a/lib/orchestrator.js +++ b/lib/orchestrator.js @@ -3,8 +3,8 @@ import { CompositeDisposable, Disposable } from 'atom' import { isValidEditor } from './utils' -type WillSaveCallback = any => boolean -type DidSaveCallback = (any, string) => Promise +type WillSaveCallback = TextEditor => boolean +type DidSaveCallback = (TextEditor, string) => ?Promise export type CallbackKind = 'willSave' | 'didSave' export type Callback = WillSaveCallback | DidSaveCallback @@ -108,7 +108,7 @@ class Orchestrator { ) } - bufferWillSave(editor: any) { + bufferWillSave(editor: TextEditor) { for (const cb of this.willSaveCallbacks) { const succeeded = cb(editor) if (succeeded !== true) { @@ -117,9 +117,9 @@ class Orchestrator { } } - editorDidSave(editor: any, path: string): Promise { + editorDidSave(editor: TextEditor, path: string): Promise { return Array.from(this.didSaveCallbacks.entries()).reduce( - (p, [, func]) => p.then(() => func(editor, path)), + (p, [, func]) => p.then(() => func(editor, path) || Promise.resolve()), Promise.resolve() ) } diff --git a/lib/outline/outline-provider.js b/lib/outline/outline-provider.js index 97a484e7..c8306702 100644 --- a/lib/outline/outline-provider.js +++ b/lib/outline/outline-provider.js @@ -1,6 +1,26 @@ +// @flow + import { buildGuruArchive } from '../guru-utils' import type { GoConfig } from '../config/service' +type TokenKind = + | 'keyword' + | 'class-name' + | 'constructor' + | 'method' + | 'param' + | 'string' + | 'whitespace' + | 'plain' + | 'type' + +type TextToken = { + kind: TokenKind, + value: string +} + +type TokenizedText = Array + type Outline = { outlineTrees: Array } type OutlineTree = { @@ -66,20 +86,21 @@ const toOutline = ( ) => { goOutline.forEach(item => { let { label } = item + let kind = item.type // omit '-' variable assignments - if (label === '_' && item.type === 'variable') return + if (label === '_' && kind === 'variable') return // distinguish methods from ordinary functions if (item.receiverType) { label = `(${item.receiverType}).${label}` - item.type = 'method' + kind = 'method' } // there isn't an atom-ide-ui icon for "import", so we'll use // 'file' for our top-level container and 'package' for imports - if (item.type === 'package') item.type = 'file' - if (item.type === 'import') item.type = 'package' + if (kind === 'package') kind = 'file' + if (kind === 'import') kind = 'package' // TODO: this assumes a character index === byte index const start = editor.getBuffer().positionForCharacterIndex(item.start - 1) @@ -87,11 +108,11 @@ const toOutline = ( const line = editor.lineTextForBufferRow(start.row) - if (item.type === 'type') { + if (kind === 'type') { // distinguish between structs and interfaces if (line.includes('type')) { - if (line.includes('interface')) item.type = 'interface' - if (line.includes('struct')) item.type = 'class' // TODO: is there a better icon? + if (line.includes('interface')) kind = 'interface' + if (line.includes('struct')) kind = 'class' // TODO: is there a better icon? } // TODO: what about other type definitions? 'type' is not valid.. @@ -100,12 +121,12 @@ const toOutline = ( // go-outline doesn't distinguish constants from variables, // but we can get _some_ constants by looking for const in the line // (this won't catch multiple constants in a single GenDecl, though) - if (item.type === 'variable' && line.includes('const')) { - item.type = 'constant' + if (kind === 'variable' && line.includes('const')) { + kind = 'constant' } - const converted = { - kind: item.type, + const converted: OutlineTree = { + kind: (kind: any), plainText: label, representativeName: label, startPosition: start, diff --git a/lib/output-manager.js b/lib/output-manager.js index cdbd90ab..78c465cf 100644 --- a/lib/output-manager.js +++ b/lib/output-manager.js @@ -13,6 +13,7 @@ class OutputManager implements PanelModel { constructor() { this.key = 'output' this.tab = { + key: 'output', name: 'Output', packageName: 'go-plus', icon: 'check', diff --git a/lib/output-panel.js b/lib/output-panel.js index 8e729068..60e94959 100644 --- a/lib/output-panel.js +++ b/lib/output-panel.js @@ -5,13 +5,13 @@ import etch from 'etch' // eslint-disable-line no-unused-vars import path from 'path' -import EtchComponent from './etch-component' -import AnsiStyle from './ansi' +import { EtchComponent } from './etch-component' +import { AnsiStyle } from './ansi' import { openFile, parseGoPosition, projectPath } from './utils' const locationRegex = /([\w-/.\\:]*.go:\d+(:\d+)?)/g -export default class OutputPanel extends EtchComponent { +export class OutputPanel extends EtchComponent { scrollHeight: number constructor(props: Object = {}) { diff --git a/lib/panel/empty-tab-view.js b/lib/panel/empty-tab-view.js index 90e420dd..6282245e 100644 --- a/lib/panel/empty-tab-view.js +++ b/lib/panel/empty-tab-view.js @@ -1,12 +1,12 @@ -/** @babel */ +// @flow /** @jsx etch.dom */ /* eslint-disable react/no-unknown-property */ import etch from 'etch' -import EtchComponent from './../etch-component' +import { EtchComponent } from './../etch-component' import Octicon from 'etch-octicon' -export default class EmptyTabView extends EtchComponent { +export class EmptyTabView extends EtchComponent { render() { return (
diff --git a/lib/panel/go-plus-panel.js b/lib/panel/go-plus-panel.js index 8c14d94f..439ae36b 100644 --- a/lib/panel/go-plus-panel.js +++ b/lib/panel/go-plus-panel.js @@ -1,16 +1,23 @@ -/** @babel */ +// @flow /** @jsx etch.dom */ /* eslint-disable react/no-unknown-property */ /* eslint-disable react/no-string-refs */ import etch from 'etch' import ResizeObserver from 'resize-observer-polyfill' -import EtchComponent from './../etch-component' -import EmptyTabView from './empty-tab-view' +import { EtchComponent } from './../etch-component' +import { EmptyTabView } from './empty-tab-view' import Octicon from 'etch-octicon' +import type { PanelManager } from './panel-manager' +import type { Tab } from './tab' -export default class GoPlusPanel extends EtchComponent { - constructor(props) { +export class GoPlusPanel extends EtchComponent { + props: { model: PanelManager } + ro: ResizeObserver + isNarrow: boolean + scrollHeight: number + + constructor(props: { model: PanelManager }) { super(props) this.ro = new ResizeObserver(entries => { for (const entry of entries) { @@ -38,7 +45,7 @@ export default class GoPlusPanel extends EtchComponent { panelClass += ' is-narrow' } - let tabs = [] + let tabs: Tab[] = [] let ActiveView let activeModel let packageName = 'unknown' @@ -60,12 +67,13 @@ export default class GoPlusPanel extends EtchComponent { } tabs.push( Object.assign( - { + ({ key: model.key, order: 999, icon: 'question', - packageName: 'unknown' - }, + packageName: 'unknown', + name: '' + }: Tab), model.tab ) ) @@ -136,7 +144,7 @@ export default class GoPlusPanel extends EtchComponent { } } - handleTabClick(item) { + handleTabClick(item: Tab) { if ( item && item.key && @@ -154,7 +162,7 @@ export default class GoPlusPanel extends EtchComponent { destroy() { this.ro.unobserve(this.element) - this.ro = null + this.ro = (null: any) super.destroy(true) } } diff --git a/lib/panel/go-plus-status-bar.js b/lib/panel/go-plus-status-bar.js index 6b5a5936..2a9b39d1 100644 --- a/lib/panel/go-plus-status-bar.js +++ b/lib/panel/go-plus-status-bar.js @@ -1,12 +1,12 @@ -/** @babel */ +// @flow /** @jsx etch.dom */ /* eslint-disable react/no-unknown-property */ import etch from 'etch' -import EtchComponent from './../etch-component' +import { EtchComponent } from './../etch-component' -export default class GoPlusStatusBar extends EtchComponent { - constructor(props) { +export class GoPlusStatusBar extends EtchComponent { + constructor(props: { state: string, togglePanel?: () => void }) { if (!props) { props = { state: 'unknown' } } diff --git a/lib/panel/panel-manager.js b/lib/panel/panel-manager.js index f8357bf4..53e722a8 100644 --- a/lib/panel/panel-manager.js +++ b/lib/panel/panel-manager.js @@ -1,11 +1,12 @@ // @flow import { CompositeDisposable, Disposable } from 'atom' -import GoPlusPanel, { PANEL_URI } from './go-plus-panel' -import GoPlusStatusBar from './go-plus-status-bar' +import { GoPlusPanel, PANEL_URI } from './go-plus-panel' +import { GoPlusStatusBar } from './go-plus-status-bar' import type { PanelModel } from './tab' import type { GoConfig } from './../config/service' +import type { Renderable } from '../etch-component' type StatusBarTile = { getPriority: () => number, @@ -31,11 +32,11 @@ class PanelManager { getAllowedLocations: () => string[] } subscriptions: CompositeDisposable - viewProviders: Map - goPlusPanel: GoPlusPanel + viewProviders: Map, model: PanelModel }> + goPlusPanel: ?GoPlusPanel statusBar: () => StatusBar | false goconfig: GoConfig - goPlusStatusBar: GoPlusStatusBar + goPlusStatusBar: ?GoPlusStatusBar goPlusStatusBarTile: ?StatusBarTile constructor(statusBarFunc: () => StatusBar | false) { @@ -132,7 +133,7 @@ class PanelManager { this.goPlusStatusBarTile = null } - registerViewProvider(view: any, model: PanelModel): Disposable { + registerViewProvider(view: Class, model: PanelModel): Disposable { if (!view || !model || !model.key) { return new Disposable() } @@ -169,11 +170,9 @@ class PanelManager { if (!visible) { container.hide() - if (this.goPlusPanel.props && this.goPlusPanel.props.viewProviders) { - for (const { model } of this.goPlusPanel.props.viewProviders.values()) { - if (model.isActive) { - model.isActive(false) - } + for (const { model } of this.viewProviders.values()) { + if (model.isActive) { + model.isActive(false) } } return Promise.resolve() @@ -195,6 +194,7 @@ class PanelManager { } this.goPlusStatusBar = new GoPlusStatusBar({ + state: 'unknown', togglePanel: () => { this.togglePanel() } diff --git a/lib/panel/tab.js b/lib/panel/tab.js index 6c320fa8..9dfada4f 100644 --- a/lib/panel/tab.js +++ b/lib/panel/tab.js @@ -1,14 +1,18 @@ // @flow export type Tab = { + key: string, name: string, packageName: string, icon: string, - order: ?number + order: number, + className?: string, + suppressPadding?: boolean } export interface PanelModel { key: string; tab: Tab; requestFocus?: ?() => Promise; + isActive?: ?(active: boolean) => void; } diff --git a/lib/rename/gorename.js b/lib/rename/gorename.js index 7f9c8fda..9f0f6f09 100644 --- a/lib/rename/gorename.js +++ b/lib/rename/gorename.js @@ -2,7 +2,7 @@ import path from 'path' import { CompositeDisposable, Point } from 'atom' -import SimpleDialog from './../simple-dialog' +import { SimpleDialog } from './../simple-dialog' import { isValidEditor } from '../utils' import type { GoConfig } from './../config/service' diff --git a/lib/simple-dialog.js b/lib/simple-dialog.js index c0484a9a..9fb23759 100644 --- a/lib/simple-dialog.js +++ b/lib/simple-dialog.js @@ -3,7 +3,7 @@ import { CompositeDisposable, TextEditor } from 'atom' import etch from 'etch' -import EtchComponent from './etch-component' +import { EtchComponent } from './etch-component' type Props = { prompt: string, @@ -12,7 +12,7 @@ type Props = { onCancel?: () => void } -export default class SimpleDialog extends EtchComponent { +export class SimpleDialog extends EtchComponent { props: Props subscriptions: CompositeDisposable panel: any diff --git a/lib/tags/gomodifytags.js b/lib/tags/gomodifytags.js index e0638aaa..6fe84c60 100644 --- a/lib/tags/gomodifytags.js +++ b/lib/tags/gomodifytags.js @@ -3,7 +3,7 @@ import { CompositeDisposable, Point } from 'atom' import { isValidEditor } from '../utils' import { buildGuruArchive } from '../guru-utils' -import TagsDialog from './tags-dialog' +import { TagsDialog } from './tags-dialog' import type { GoConfig } from './../config/service' diff --git a/lib/tags/tags-dialog.js b/lib/tags/tags-dialog.js index 872e81a3..711c6a2b 100644 --- a/lib/tags/tags-dialog.js +++ b/lib/tags/tags-dialog.js @@ -4,7 +4,7 @@ import { CompositeDisposable, TextEditor } from 'atom' import etch from 'etch' -import EtchComponent from './../etch-component' +import { EtchComponent } from './../etch-component' import type { Tag } from './gomodifytags' @@ -14,7 +14,7 @@ export type AcceptedCallback = ({ sortTags: boolean }) => any -export default class TagsDialog extends EtchComponent { +export class TagsDialog extends EtchComponent { tags: Array case: 'snakecase' | 'camelcase' // | 'lispcase' tagRegex: RegExp diff --git a/lib/test/gocover-parser.js b/lib/test/gocover-parser.js index f914cf67..a12be6cb 100644 --- a/lib/test/gocover-parser.js +++ b/lib/test/gocover-parser.js @@ -9,7 +9,7 @@ export type CoverageRange = { file: string } -const ranges = (coverageFile: string): Array => { +export function ranges(coverageFile: string): Array { let data const ranges = [] try { @@ -41,5 +41,3 @@ const ranges = (coverageFile: string): Array => { return ranges } - -export default { ranges } diff --git a/lib/test/tester.js b/lib/test/tester.js index c2700c0c..6103d88e 100644 --- a/lib/test/tester.js +++ b/lib/test/tester.js @@ -6,12 +6,13 @@ import path from 'path' import temp from '@atom/temp' import argparser from 'yargs-parser/lib/tokenize-arg-string' import { CompositeDisposable } from 'atom' -import parser from './gocover-parser' +import * as gocover from './gocover-parser' import { getEditor, isValidEditor } from '../utils' import type { GoConfig } from './../config/service' import type { CoverageRange } from './gocover-parser' import type { OutputManager } from './../output-manager' +import type { TextEditor } from 'atom' class Tester { disposed: boolean @@ -20,7 +21,7 @@ class Tester { output: OutputManager busySignal: () => ?BusySignalService subscriptions: CompositeDisposable - markedEditors: Map + markedEditors: Map coverageHighlightMode: | 'covered-and-uncovered' | 'covered' @@ -121,7 +122,7 @@ class Tester { ) } - handleSaveEvent(editor: any): Promise { + handleSaveEvent(editor: TextEditor): Promise { if (atom.config.get('go-plus.test.runTestsOnSave')) { const bs = this.busySignal() const p = this.runTests(editor) @@ -144,7 +145,7 @@ class Tester { } } - addMarkersToEditor(editor: any) { + addMarkersToEditor(editor: TextEditor) { if (!isValidEditor(editor)) { return } @@ -165,12 +166,13 @@ class Tester { } const re = /[^/\\]+$/g + const fileMatch = (file.match(re) || [])[0] const editorRanges = this.ranges.filter(r => { - const fileMatches = r.file.match(re) + const rangeFileMatches = r.file.match(re) return ( - fileMatches && - fileMatches.length > 0 && - file.match(re)[0] === fileMatches[0] + rangeFileMatches && + rangeFileMatches.length > 0 && + fileMatch === rangeFileMatches[0] ) }) if (!editorRanges || editorRanges.length <= 0) { @@ -204,22 +206,28 @@ class Tester { const type = this.coverageDisplayMode === 'gutter' ? 'line-number' : 'highlight' - editor.decorateMarkerLayer(coveredLayer, { - type: type, - class: 'covered', - onlyNonEmpty: true - }) - editor.decorateMarkerLayer(uncoveredLayer, { - type: type, - class: 'uncovered', - onlyNonEmpty: true - }) + editor.decorateMarkerLayer( + coveredLayer, + ({ + type: type, + class: 'covered', + onlyNonEmpty: true + }: any) + ) + editor.decorateMarkerLayer( + uncoveredLayer, + ({ + type: type, + class: 'uncovered', + onlyNonEmpty: true + }: any) + ) } catch (e) { console.log(e) // eslint-disable-line no-console } } - clearMarkers(editor: any) { + clearMarkers(editor: TextEditor) { if ( !editor || !editor.hasOwnProperty('id') || @@ -240,7 +248,7 @@ class Tester { } for (const layerid of layersid.split(',')) { - const layer = editor.getMarkerLayer(layerid) + const layer = editor.getMarkerLayer(parseInt(layerid, 10)) if (layer) { layer.destroy() } @@ -322,7 +330,7 @@ class Tester { return args } - async runTests(editor: any = getEditor()): Promise { + async runTests(editor: ?TextEditor = getEditor()): Promise { if (!isValidEditor(editor)) { throw new Error('invalid editor') } @@ -390,7 +398,7 @@ class Tester { state = 'fail' } if (runTestsWithCoverage) { - this.ranges = parser.ranges(this.coverageFile) + this.ranges = gocover.ranges(this.coverageFile) this.addMarkersToEditors() } if (this.output) { diff --git a/lib/utils.js b/lib/utils.js index 4bd5a944..eb314ecb 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -1,19 +1,17 @@ // @flow import fs from 'fs' +import { TextEditor } from 'atom' -const isValidEditor = (e: any) => { - // TODO atom$TextEditor - if (!e || !e.getGrammar()) { - return false - } - const grammar = e.getGrammar() - return ( - grammar && (grammar.scopeName === 'source.go' || grammar.scopeName === 'go') - ) +const isValidEditor = (e: ?TextEditor): boolean %checks => { + return !!e && hasGoGrammar(e.getGrammar()) +} + +const hasGoGrammar = (g: ?atom$Grammar): boolean %checks => { + return !!g && (g.scopeName === 'source.go' || g.scopeName === 'go') } -const getEditor = () => { +const getEditor = (): ?TextEditor => { if (!atom || !atom.workspace) { return } @@ -25,7 +23,9 @@ const getEditor = () => { return editor } -const getWordPosition = (editor: any = getEditor()): ?atom$PointLike => { +const getWordPosition = ( + editor: ?TextEditor = getEditor() +): ?atom$PointLike => { if (!editor) { return undefined } diff --git a/package-lock.json b/package-lock.json index 6e0d86e4..934ea624 100644 --- a/package-lock.json +++ b/package-lock.json @@ -254,16 +254,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" }, - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "optional": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -273,21 +263,6 @@ "sprintf-js": "~1.0.2" } }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=" - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==" - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=" - }, "array-includes": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz", @@ -313,33 +288,12 @@ "integrity": "sha1-r2rId6Jcx/dOBYiUdThY39sk/bY=", "dev": true }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=" - }, "arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", "integrity": "sha1-iYUI2iIm84DfkEcoRWhJwVAaSw0=", "dev": true }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=" - }, - "async-each": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.1.tgz", - "integrity": "sha1-GdOGodntxufByF04iu28xW0zYC0=", - "optional": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==" - }, "atom-babel6-transpiler": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/atom-babel6-transpiler/-/atom-babel6-transpiler-1.2.0.tgz", @@ -477,14 +431,6 @@ "babel-runtime": "^6.22.0" } }, - "babel-plugin-add-module-exports": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-1.0.0.tgz", - "integrity": "sha512-m0sMxPL4FaN2K69GQgaRJa4Ny15qKSdoknIcpN+gz+NaJlAW9pge/povs13tPYsKDboflrEQC+/3kfIsONBTaw==", - "requires": { - "chokidar": "^2.0.4" - } - }, "babel-plugin-syntax-class-properties": { "version": "6.13.0", "resolved": "http://registry.npmjs.org/babel-plugin-syntax-class-properties/-/babel-plugin-syntax-class-properties-6.13.0.tgz", @@ -636,62 +582,6 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "binary-extensions": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.12.0.tgz", - "integrity": "sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==", - "optional": true - }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -701,49 +591,6 @@ "concat-map": "0.0.1" } }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, "caller-path": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/caller-path/-/caller-path-0.1.0.tgz", @@ -782,54 +629,12 @@ "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==", "dev": true }, - "chokidar": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz", - "integrity": "sha512-z9n7yt9rOvIJrMhvDtDictKrkFHeihkNl6uWMmZlmL6tJtX9Cs+87oK+teBx+JIgzvbX3yZHT3eF8vpbDxHJXQ==", - "optional": true, - "requires": { - "anymatch": "^2.0.0", - "async-each": "^1.0.0", - "braces": "^2.3.0", - "fsevents": "^1.2.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.1", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "lodash.debounce": "^4.0.8", - "normalize-path": "^2.1.1", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.0.0", - "upath": "^1.0.5" - } - }, "circular-json": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz", "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==", "dev": true }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "cli-cursor": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", @@ -845,15 +650,6 @@ "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", "dev": true }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, "color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -869,11 +665,6 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, - "component-emitter": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", - "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" - }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -887,22 +678,11 @@ "safe-buffer": "~5.1.1" } }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=" - }, "core-js": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.5.7.tgz", "integrity": "sha512-RszJCAxg/PP6uzXVXL6BsxSXx/B05oJAQ2vkJRjyjrEcNVycaqOmNb5OTxZPE3xa5gwZduqza6L9JOCenh/Ecw==" }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "optional": true - }, "cross-spawn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", @@ -924,11 +704,6 @@ "ms": "2.0.0" } }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" - }, "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", @@ -944,43 +719,6 @@ "object-keys": "^1.0.12" } }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "del": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/del/-/del-2.2.2.tgz", @@ -1286,57 +1024,6 @@ "octicons": "^7.0.1" } }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "external-editor": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", @@ -1348,65 +1035,6 @@ "tmp": "^0.0.33" } }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", @@ -1450,27 +1078,6 @@ "object-assign": "^4.0.1" } }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "flat-cache": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.0.tgz", @@ -1489,19 +1096,6 @@ "integrity": "sha512-ougBA2q6Rn9sZrjZQ9r5pTFxCotlGouySpD2yRIuq5AYwwfIT8HHhVMeSwrN5qJayjHINLJyrnsSkkPCZyfMrQ==", "dev": true }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=" - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "requires": { - "map-cache": "^0.2.2" - } - }, "fs-extra": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.0.tgz", @@ -1517,574 +1111,86 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, - "fsevents": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.4.tgz", - "integrity": "sha512-z8H8/diyk76B7q5wg+Ud0+CqzcAF3mBBI/bA5ne5zrRUUIvNkJY//D3BqyH571KuAC4Nr7Rw7CjWX4r0y9DvNg==", - "optional": true, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "fuzzaldrin": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", + "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=" + }, + "fuzzaldrin-plus": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/fuzzaldrin-plus/-/fuzzaldrin-plus-0.6.0.tgz", + "integrity": "sha1-gy9kifvodnaUWVmckUpnDsIpR+4=" + }, + "get-stdin": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", + "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" + }, + "globby": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", + "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", + "dev": true, + "requires": { + "array-union": "^1.0.1", + "arrify": "^1.0.0", + "glob": "^7.0.3", + "object-assign": "^4.0.1", + "pify": "^2.0.0", + "pinkie-promise": "^2.0.0" + } + }, + "graceful-fs": { + "version": "4.1.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", + "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", "requires": { - "nan": "^2.9.2", - "node-pre-gyp": "^0.10.0" - }, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "bundled": true, - "optional": true - }, - "ansi-regex": { - "version": "2.1.1", - "bundled": true - }, - "aproba": { - "version": "1.2.0", - "bundled": true, - "optional": true - }, - "are-we-there-yet": { - "version": "1.1.4", - "bundled": true, - "optional": true, - "requires": { - "delegates": "^1.0.0", - "readable-stream": "^2.0.6" - } - }, - "balanced-match": { - "version": "1.0.0", - "bundled": true - }, - "brace-expansion": { - "version": "1.1.11", - "bundled": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "chownr": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "code-point-at": { - "version": "1.1.0", - "bundled": true - }, - "concat-map": { - "version": "0.0.1", - "bundled": true - }, - "console-control-strings": { - "version": "1.1.0", - "bundled": true - }, - "core-util-is": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "debug": { - "version": "2.6.9", - "bundled": true, - "optional": true, - "requires": { - "ms": "2.0.0" - } - }, - "deep-extend": { - "version": "0.5.1", - "bundled": true, - "optional": true - }, - "delegates": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "detect-libc": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "fs-minipass": { - "version": "1.2.5", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "fs.realpath": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "gauge": { - "version": "2.7.4", - "bundled": true, - "optional": true, - "requires": { - "aproba": "^1.0.3", - "console-control-strings": "^1.0.0", - "has-unicode": "^2.0.0", - "object-assign": "^4.1.0", - "signal-exit": "^3.0.0", - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wide-align": "^1.1.0" - } - }, - "glob": { - "version": "7.1.2", - "bundled": true, - "optional": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "has-unicode": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "iconv-lite": { - "version": "0.4.21", - "bundled": true, - "optional": true, - "requires": { - "safer-buffer": "^2.1.0" - } - }, - "ignore-walk": { - "version": "3.0.1", - "bundled": true, - "optional": true, - "requires": { - "minimatch": "^3.0.4" - } - }, - "inflight": { - "version": "1.0.6", - "bundled": true, - "optional": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.3", - "bundled": true - }, - "ini": { - "version": "1.3.5", - "bundled": true, - "optional": true - }, - "is-fullwidth-code-point": { - "version": "1.0.0", - "bundled": true, - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "bundled": true, - "optional": true - }, - "minimatch": { - "version": "3.0.4", - "bundled": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "0.0.8", - "bundled": true - }, - "minipass": { - "version": "2.2.4", - "bundled": true, - "requires": { - "safe-buffer": "^5.1.1", - "yallist": "^3.0.0" - } - }, - "minizlib": { - "version": "1.1.0", - "bundled": true, - "optional": true, - "requires": { - "minipass": "^2.2.1" - } - }, - "mkdirp": { - "version": "0.5.1", - "bundled": true, - "requires": { - "minimist": "0.0.8" - } - }, - "ms": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "needle": { - "version": "2.2.0", - "bundled": true, - "optional": true, - "requires": { - "debug": "^2.1.2", - "iconv-lite": "^0.4.4", - "sax": "^1.2.4" - } - }, - "node-pre-gyp": { - "version": "0.10.0", - "bundled": true, - "optional": true, - "requires": { - "detect-libc": "^1.0.2", - "mkdirp": "^0.5.1", - "needle": "^2.2.0", - "nopt": "^4.0.1", - "npm-packlist": "^1.1.6", - "npmlog": "^4.0.2", - "rc": "^1.1.7", - "rimraf": "^2.6.1", - "semver": "^5.3.0", - "tar": "^4" - } - }, - "nopt": { - "version": "4.0.1", - "bundled": true, - "optional": true, - "requires": { - "abbrev": "1", - "osenv": "^0.1.4" - } - }, - "npm-bundled": { - "version": "1.0.3", - "bundled": true, - "optional": true - }, - "npm-packlist": { - "version": "1.1.10", - "bundled": true, - "optional": true, - "requires": { - "ignore-walk": "^3.0.1", - "npm-bundled": "^1.0.1" - } - }, - "npmlog": { - "version": "4.1.2", - "bundled": true, - "optional": true, - "requires": { - "are-we-there-yet": "~1.1.2", - "console-control-strings": "~1.1.0", - "gauge": "~2.7.3", - "set-blocking": "~2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "bundled": true - }, - "object-assign": { - "version": "4.1.1", - "bundled": true, - "optional": true - }, - "once": { - "version": "1.4.0", - "bundled": true, - "requires": { - "wrappy": "1" - } - }, - "os-homedir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "os-tmpdir": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "osenv": { - "version": "0.1.5", - "bundled": true, - "optional": true, - "requires": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, - "path-is-absolute": { - "version": "1.0.1", - "bundled": true, - "optional": true - }, - "process-nextick-args": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "rc": { - "version": "1.2.7", - "bundled": true, - "optional": true, - "requires": { - "deep-extend": "^0.5.1", - "ini": "~1.3.0", - "minimist": "^1.2.0", - "strip-json-comments": "~2.0.1" - }, - "dependencies": { - "minimist": { - "version": "1.2.0", - "bundled": true, - "optional": true - } - } - }, - "readable-stream": { - "version": "2.3.6", - "bundled": true, - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "rimraf": { - "version": "2.6.2", - "bundled": true, - "optional": true, - "requires": { - "glob": "^7.0.5" - } - }, - "safe-buffer": { - "version": "5.1.1", - "bundled": true - }, - "safer-buffer": { - "version": "2.1.2", - "bundled": true, - "optional": true - }, - "sax": { - "version": "1.2.4", - "bundled": true, - "optional": true - }, - "semver": { - "version": "5.5.0", - "bundled": true, - "optional": true - }, - "set-blocking": { - "version": "2.0.0", - "bundled": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "bundled": true, - "optional": true - }, - "string-width": { - "version": "1.0.2", - "bundled": true, - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - }, - "string_decoder": { - "version": "1.1.1", - "bundled": true, - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, - "strip-ansi": { - "version": "3.0.1", - "bundled": true, - "requires": { - "ansi-regex": "^2.0.0" - } - }, - "strip-json-comments": { - "version": "2.0.1", - "bundled": true, - "optional": true - }, - "tar": { - "version": "4.4.1", - "bundled": true, - "optional": true, - "requires": { - "chownr": "^1.0.1", - "fs-minipass": "^1.2.5", - "minipass": "^2.2.4", - "minizlib": "^1.1.0", - "mkdirp": "^0.5.0", - "safe-buffer": "^5.1.1", - "yallist": "^3.0.2" - } - }, - "util-deprecate": { - "version": "1.0.2", - "bundled": true, - "optional": true - }, - "wide-align": { - "version": "1.1.2", - "bundled": true, - "optional": true, - "requires": { - "string-width": "^1.0.2" - } - }, - "wrappy": { - "version": "1.0.2", - "bundled": true - }, - "yallist": { - "version": "3.0.2", - "bundled": true - } - } - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", - "dev": true - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "fuzzaldrin": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fuzzaldrin/-/fuzzaldrin-2.1.0.tgz", - "integrity": "sha1-kCBMPi/appQbso0WZF1BgGOpDps=" - }, - "fuzzaldrin-plus": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/fuzzaldrin-plus/-/fuzzaldrin-plus-0.6.0.tgz", - "integrity": "sha1-gy9kifvodnaUWVmckUpnDsIpR+4=" - }, - "get-stdin": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz", - "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==", - "dev": true - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=" - }, - "glob": { - "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", - "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", - "optional": true, - "requires": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - }, - "dependencies": { - "is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", - "optional": true, - "requires": { - "is-extglob": "^2.1.0" - } - } - } - }, - "globals": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", - "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" - }, - "globby": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-5.0.0.tgz", - "integrity": "sha1-69hGZ8oNuzMLmbz8aOrCvFQ3Dg0=", - "dev": true, - "requires": { - "array-union": "^1.0.1", - "arrify": "^1.0.0", - "glob": "^7.0.3", - "object-assign": "^4.0.1", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dev": true, - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", - "requires": { - "ansi-regex": "^2.0.0" + "ansi-regex": "^2.0.0" }, "dependencies": { "ansi-regex": { @@ -2106,35 +1212,6 @@ "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", "dev": true }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "home-or-tmp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", @@ -2248,95 +1325,18 @@ "loose-envify": "^1.0.0" } }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", - "optional": true, - "requires": { - "binary-extensions": "^1.0.0" - } - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==" - }, "is-callable": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", "integrity": "sha512-r5p9sxJjYnArLjObpjA4xu5EKI3CuKHkJXMhT7kwbpUyIFD1n5PMAsoPvWnvtZiNz7LjkYDRZhd7FlI0eMijEA==", "dev": true }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "is-date-object": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", "dev": true }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" - } - } - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=" - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=" - }, "is-finite": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.0.2.tgz", @@ -2351,33 +1351,6 @@ "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", "dev": true }, - "is-glob": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", - "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", - "optional": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "is-path-cwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-1.0.0.tgz", @@ -2402,14 +1375,6 @@ "path-is-inside": "^1.0.1" } }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "requires": { - "isobject": "^3.0.1" - } - }, "is-promise": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", @@ -2440,27 +1405,12 @@ "has-symbols": "^1.0.0" } }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=" - }, "js-tokens": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", @@ -2515,11 +1465,6 @@ "array-includes": "^3.0.3" } }, - "kind-of": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", - "integrity": "sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA==" - }, "levn": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", @@ -2535,12 +1480,6 @@ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" }, - "lodash.debounce": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", - "integrity": "sha1-gteb/zCmfEAF/9XiUVMArZyk168=", - "optional": true - }, "loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -2549,39 +1488,6 @@ "js-tokens": "^3.0.0 || ^4.0.0" } }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=" - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "requires": { - "object-visit": "^1.0.0" - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, "mimic-fn": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", @@ -2601,25 +1507,6 @@ "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, - "mixin-deep": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", - "integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==", - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, "mkdirp": { "version": "0.5.1", "resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", @@ -2639,30 +1526,6 @@ "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", "dev": true }, - "nan": { - "version": "2.11.1", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.1.tgz", - "integrity": "sha512-iji6k87OSXa0CcrLl9z+ZiYSuR2o+c0bGuNmXdrhTQTakxytAFsC56SArGYoiHlJlFoHSnvmhpceZJaXkVuOtA==", - "optional": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, "natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -2675,14 +1538,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, "number-is-nan": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", @@ -2693,56 +1548,12 @@ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "object-keys": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.0.12.tgz", "integrity": "sha512-FTMyFUm2wBcGHnH2eXmz7tC6IwlqQZ6mVZ+6dm6vZ4IQIHjs6FdNsQBuKGPuUUUY6NfJw2PshC08Tn6LzLDOag==", "dev": true }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "requires": { - "isobject": "^3.0.0" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "requires": { - "isobject": "^3.0.1" - } - }, "octicons": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/octicons/-/octicons-7.4.0.tgz", @@ -2792,17 +1603,6 @@ "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=" - }, - "path-dirname": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", - "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", - "optional": true - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", @@ -2847,11 +1647,6 @@ "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==", "dev": true }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=" - }, "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", @@ -2878,12 +1673,6 @@ "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" }, - "process-nextick-args": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", - "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", - "optional": true - }, "progress": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.1.tgz", @@ -2906,67 +1695,17 @@ "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", "dev": true }, - "readable-stream": { - "version": "2.3.6", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", - "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", - "optional": true, - "requires": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", - "optional": true, - "requires": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" - } - }, "regenerator-runtime": { "version": "0.11.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==", "dev": true }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=" - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==" - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=" - }, "repeating": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", @@ -2996,11 +1735,6 @@ "integrity": "sha1-Jsv+k10a7uq7Kbw/5a6wHpPUQiY=", "dev": true }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=" - }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", @@ -3011,11 +1745,6 @@ "signal-exit": "^3.0.2" } }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==" - }, "rimraf": { "version": "2.2.8", "resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", @@ -3045,14 +1774,6 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "requires": { - "ret": "~0.1.10" - } - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -3064,27 +1785,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg==" }, - "set-value": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", - "integrity": "sha512-hw0yxk9GT/Hr5yJEYnHNKYXkIA8mVJgd9ditYZCe16ZczcaELYYcfvaXesNACk2O8O0nTiPQcQhGUQj8JLzeeg==", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, "shebang-command": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", @@ -3120,120 +1820,11 @@ "is-fullwidth-code-point": "^2.0.0" } }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "requires": { - "kind-of": "^3.2.0" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, "source-map": { "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" }, - "source-map-resolve": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", - "integrity": "sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA==", - "requires": { - "atob": "^2.1.1", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, "source-map-support": { "version": "0.4.18", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", @@ -3242,44 +1833,12 @@ "source-map": "^0.5.6" } }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=" - }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "requires": { - "extend-shallow": "^3.0.0" - } - }, "sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", "dev": true }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, "string-width": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", @@ -3301,15 +1860,6 @@ } } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "optional": true, - "requires": { - "safe-buffer": "~5.1.0" - } - }, "strip-ansi": { "version": "3.0.1", "resolved": "http://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", @@ -3374,44 +1924,6 @@ "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - }, "trim-right": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", @@ -3432,85 +1944,11 @@ "prelude-ls": "~1.1.2" } }, - "union-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", - "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^0.4.3" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "requires": { - "is-extendable": "^0.1.0" - } - }, - "set-value": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", - "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.1", - "to-object-path": "^0.3.0" - } - } - } - }, "universalify": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=" - } - } - }, - "upath": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.0.tgz", - "integrity": "sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw==", - "optional": true - }, "uri-js": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", @@ -3520,22 +1958,6 @@ "punycode": "^2.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=" - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==" - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "optional": true - }, "which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", diff --git a/package.json b/package.json index 9fe408b1..3d4d0b6b 100644 --- a/package.json +++ b/package.json @@ -73,10 +73,10 @@ } ], "dependencies": { + "@atom/temp": "^0.8.4", "ansi-style-parser": "^2.0.0", - "atom-select-list": "^0.7.2", "atom-babel6-transpiler": "^1.2.0", - "babel-plugin-add-module-exports": "^1.0.0", + "atom-select-list": "^0.7.2", "babel-plugin-transform-class-properties": "^6.24.1", "babel-plugin-transform-es2015-modules-commonjs": "^6.26.2", "babel-plugin-transform-flow-strip-types": "^6.22.0", @@ -88,7 +88,6 @@ "fuzzaldrin-plus": "^0.6.0", "resize-observer-polyfill": "^1.5.0", "semver": "^5.6.0", - "@atom/temp": "^0.8.4", "yargs-parser": "^10.1.0" }, "devDependencies": { diff --git a/spec/autocomplete/gocodeprovider-helper-spec.js b/spec/autocomplete/gocodeprovider-helper-spec.js new file mode 100644 index 00000000..2e3193eb --- /dev/null +++ b/spec/autocomplete/gocodeprovider-helper-spec.js @@ -0,0 +1,94 @@ +'use babel' +/* eslint-env jasmine */ + +import { getPackage } from '../../lib/autocomplete/gocodeprovider-helper' +import * as path from 'path' + +describe('gocodeprovider-helper', () => { + describe('getPackage', () => { + function normalize(v) { + return (process.platform === 'win32' ? 'C:' : '') + path.normalize(v) + } + it('returns a non-vendored package if `useVendor` is false', () => { + const pkg = getPackage( + normalize('/Users/me/go/src/github.com/foo/server/main.go'), + normalize('/Users/me/go'), + [ + 'github.com/foo/server/vendor/github.com/bar/lib', + 'github.com/foo/lib' + ], + false // <-- vendor is not active + ) + expect(pkg).toBe('github.com/foo/lib') + }) + + it('returns a vendored package if `useVendor` is true', () => { + const pkg = getPackage( + normalize('/Users/me/go/src/github.com/foo/server/main.go'), + normalize('/Users/me/go'), + [ + 'github.com/foo/server/vendor/github.com/bar/lib', + 'github.com/foo/lib' + ], + true // <-- vendor is active + ) + expect(pkg).toBe('github.com/bar/lib') + }) + + it('gets vendored package if inside sub package', () => { + const pkg = getPackage( + normalize('/Users/me/go/src/github.com/foo/server/sub/sub.go'), // <-- inside sub package + normalize('/Users/me/go'), + [ + 'github.com/foo/server/vendor/github.com/bar/lib', + 'github.com/foo/lib' + ], + true + ) + expect(pkg).toBe('github.com/bar/lib') + }) + + it('ignores nested vendored packages', () => { + const pkg = getPackage( + normalize('/Users/me/go/src/github.com/foo/server/main.go'), + normalize('/Users/me/go'), + [ + 'github.com/foo/server/vendor/github.com/bar/lib/vendor/github.com/baz/other' + ], + true + ) + expect(pkg).toBeFalsy() + }) + + it('returns non-vendored package if vendor does not match', () => { + const pkg = getPackage( + normalize('/Users/me/go/src/github.com/foo/server/main.go'), + normalize('/Users/me/go'), + [ + // ignores this package because it is inside the "bar" package not "foo" + 'github.com/bar/server/vendor/github.com/baz/lib', + // returns this because no vendored package matches + 'github.com/qux/lib' + ], + true + ) + expect(pkg).toBe('github.com/qux/lib') + }) + + it('returns another vendored package inside a vendored package', () => { + const pkg = getPackage( + // a file inside a vendored package ... + normalize( + '/Users/me/go/src/github.com/foo/server/vendor/github.com/bar/lib/lib.go' + ), + normalize('/Users/me/go'), + [ + // ... is allowed to use another vendored package + 'github.com/foo/server/vendor/github.com/baz/other' + ], + true + ) + expect(pkg).toBe('github.com/baz/other') + }) + }) +}) diff --git a/spec/autocomplete/gocodeprovider-spec.js b/spec/autocomplete/gocodeprovider-spec.js index 9c3cd761..cae96819 100644 --- a/spec/autocomplete/gocodeprovider-spec.js +++ b/spec/autocomplete/gocodeprovider-spec.js @@ -98,7 +98,6 @@ describe('gocodeprovider', () => { }) }) - /* describe('when snippetMode is name', () => { beforeEach(() => { atom.config.set('go-plus.autocomplete.snippetMode', 'name') @@ -197,7 +196,6 @@ describe('gocodeprovider', () => { }) }) }) - */ }) // TODO revisit @@ -261,7 +259,6 @@ describe('gocodeprovider', () => { }) }) - /* describe('when the go-plus-issue-307 file is opened', () => { beforeEach(async () => { editor = await atom.workspace.open( @@ -417,5 +414,4 @@ describe('gocodeprovider', () => { }) }) }) - */ }) diff --git a/spec/autocomplete/suggestions-spec.js b/spec/autocomplete/suggestions-spec.js new file mode 100644 index 00000000..27c613c9 --- /dev/null +++ b/spec/autocomplete/suggestions-spec.js @@ -0,0 +1,898 @@ +'use babel' +/* eslint-env jasmine */ + +import * as Suggestions from '../../lib/autocomplete/suggestions' + +describe('gocodeprovider-suggestions', () => { + describe('matchFunc', () => { + let t = context => { + let match = Suggestions.matchFunc(context.input) + expect(match).toBeTruthy() + expect(match.length).toBe(3) + expect(match[0]).toBe(context.input) + expect(match[1]).toBe(context.args) + expect(match[2]).toBe(context.returns) + } + + it('identifies function arguments', () => { + t({ + input: 'func(name string, flag bool) bool', + args: 'name string, flag bool', + returns: 'bool' + }) + t({ + input: 'func(name string, flag bool) (bool)', + args: 'name string, flag bool', + returns: 'bool' + }) + t({ + input: 'func(name string, f func(t *testing.T)) bool', + args: 'name string, f func(t *testing.T)', + returns: 'bool' + }) + t({ + input: 'func(name string, f func(t *testing.T)) (bool)', + args: 'name string, f func(t *testing.T)', + returns: 'bool' + }) + t({ + input: 'func(name string, f func(t *testing.T) int) (bool)', + args: 'name string, f func(t *testing.T) int', + returns: 'bool' + }) + t({ + input: + 'func(pattern string, handler func(http.ResponseWriter, *http.Request))', + args: + 'pattern string, handler func(http.ResponseWriter, *http.Request)', + returns: undefined + }) + t({ + input: 'func(n int) func(p *T)', + args: 'n int', + returns: 'func(p *T)' + }) + }) + }) + + describe('parseType', () => { + let t = context => { + let result = Suggestions.parseType(context.input) + expect(result).toBeTruthy() + expect(result.isFunc).toBeTruthy() + expect(result.args).toEqual(context.args) + expect(result.returns).toEqual(context.returns) + } + + it('parses the function into args and returns arrays', () => { + t({ + input: 'func(name string, flag bool) bool', + args: [ + { + name: 'name string', + identifier: 'name', + type: { name: 'string', isFunc: false } + }, + { + name: 'flag bool', + identifier: 'flag', + type: { name: 'bool', isFunc: false } + } + ], + returns: [ + { + name: 'bool', + identifier: '', + type: { name: 'bool', isFunc: false } + } + ] + }) + + t({ + input: 'func(name string, flag bool) (bool)', + args: [ + { + name: 'name string', + identifier: 'name', + type: { name: 'string', isFunc: false } + }, + { + name: 'flag bool', + identifier: 'flag', + type: { name: 'bool', isFunc: false } + } + ], + returns: [ + { + name: 'bool', + identifier: '', + type: { name: 'bool', isFunc: false } + } + ] + }) + + t({ + input: 'func(name string, f func(t *testing.T)) bool', + args: [ + { + name: 'name string', + identifier: 'name', + type: { name: 'string', isFunc: false } + }, + { + name: 'f func(t *testing.T)', + identifier: 'f', + type: { + isFunc: true, + name: 'func(t *testing.T)', + args: [ + { + name: 't *testing.T', + identifier: 't', + type: { name: '*testing.T', isFunc: false } + } + ], + returns: [] + } + } + ], + returns: [ + { + name: 'bool', + identifier: '', + type: { name: 'bool', isFunc: false } + } + ] + }) + + t({ + input: 'func(name string, f func(t *testing.T)) (bool)', + args: [ + { + name: 'name string', + identifier: 'name', + type: { name: 'string', isFunc: false } + }, + { + name: 'f func(t *testing.T)', + identifier: 'f', + type: { + isFunc: true, + name: 'func(t *testing.T)', + args: [ + { + name: 't *testing.T', + identifier: 't', + type: { name: '*testing.T', isFunc: false } + } + ], + returns: [] + } + } + ], + returns: [ + { + name: 'bool', + identifier: '', + type: { name: 'bool', isFunc: false } + } + ] + }) + + t({ + input: + 'func(pattern string, handler func(http.ResponseWriter, *http.Request))', + args: [ + { + name: 'pattern string', + identifier: 'pattern', + type: { name: 'string', isFunc: false } + }, + { + name: 'handler func(http.ResponseWriter, *http.Request)', + identifier: 'handler', + type: { + isFunc: true, + name: 'func(http.ResponseWriter, *http.Request)', + args: [ + { + name: 'http.ResponseWriter', + identifier: '', + type: { name: 'http.ResponseWriter', isFunc: false } + }, + { + name: '*http.Request', + identifier: '', + type: { name: '*http.Request', isFunc: false } + } + ], + returns: [] + } + } + ], + returns: [] + }) + + t({ + input: + 'func(pattern string, handler func(http.ResponseWriter, *http.Request), otherhandler func(http.ResponseWriter, *http.Request))', + args: [ + { + name: 'pattern string', + identifier: 'pattern', + type: { name: 'string', isFunc: false } + }, + { + name: 'handler func(http.ResponseWriter, *http.Request)', + identifier: 'handler', + type: { + isFunc: true, + name: 'func(http.ResponseWriter, *http.Request)', + args: [ + { + name: 'http.ResponseWriter', + identifier: '', + type: { name: 'http.ResponseWriter', isFunc: false } + }, + { + name: '*http.Request', + identifier: '', + type: { name: '*http.Request', isFunc: false } + } + ], + returns: [] + } + }, + { + name: 'otherhandler func(http.ResponseWriter, *http.Request)', + identifier: 'otherhandler', + type: { + isFunc: true, + name: 'func(http.ResponseWriter, *http.Request)', + args: [ + { + name: 'http.ResponseWriter', + identifier: '', + type: { name: 'http.ResponseWriter', isFunc: false } + }, + { + name: '*http.Request', + identifier: '', + type: { name: '*http.Request', isFunc: false } + } + ], + returns: [] + } + } + ], + returns: [] + }) + + t({ + input: + 'func(pattern string, handler func(w http.ResponseWriter, r *http.Request), otherhandler func(w http.ResponseWriter, r *http.Request))', + args: [ + { + name: 'pattern string', + identifier: 'pattern', + type: { name: 'string', isFunc: false } + }, + { + name: 'handler func(w http.ResponseWriter, r *http.Request)', + identifier: 'handler', + type: { + isFunc: true, + name: 'func(w http.ResponseWriter, r *http.Request)', + args: [ + { + name: 'w http.ResponseWriter', + identifier: 'w', + type: { name: 'http.ResponseWriter', isFunc: false } + }, + { + name: 'r *http.Request', + identifier: 'r', + type: { name: '*http.Request', isFunc: false } + } + ], + returns: [] + } + }, + { + name: 'otherhandler func(w http.ResponseWriter, r *http.Request)', + identifier: 'otherhandler', + type: { + isFunc: true, + name: 'func(w http.ResponseWriter, r *http.Request)', + args: [ + { + name: 'w http.ResponseWriter', + identifier: 'w', + type: { name: 'http.ResponseWriter', isFunc: false } + }, + { + name: 'r *http.Request', + identifier: 'r', + type: { name: '*http.Request', isFunc: false } + } + ], + returns: [] + } + } + ], + returns: [] + }) + + t({ + input: 'func()', + args: [], + returns: [] + }) + + t({ + input: 'func(x int) int', + args: [ + { + name: 'x int', + identifier: 'x', + type: { name: 'int', isFunc: false } + } + ], + returns: [ + { + name: 'int', + identifier: '', + type: { name: 'int', isFunc: false } + } + ] + }) + + t({ + input: 'func(a, _ int, z float32) bool', + args: [ + { + name: 'a', + identifier: '', + type: { name: 'a', isFunc: false } + }, + { + name: '_ int', + identifier: '_', + type: { name: 'int', isFunc: false } + }, + { + name: 'z float32', + identifier: 'z', + type: { name: 'float32', isFunc: false } + } + ], + returns: [ + { + name: 'bool', + identifier: '', + type: { name: 'bool', isFunc: false } + } + ] + }) + + t({ + input: 'func(a, b int, z float32) (bool)', + args: [ + { + name: 'a', + identifier: '', + type: { name: 'a', isFunc: false } + }, + { + name: 'b int', + identifier: 'b', + type: { name: 'int', isFunc: false } + }, + { + name: 'z float32', + identifier: 'z', + type: { name: 'float32', isFunc: false } + } + ], + returns: [ + { + name: 'bool', + identifier: '', + type: { name: 'bool', isFunc: false } + } + ] + }) + + t({ + input: 'func(a, b int, z float64, opt ...interface{}) (success bool)', + args: [ + { + name: 'a', + identifier: '', + type: { name: 'a', isFunc: false } + }, + { + name: 'b int', + identifier: 'b', + type: { name: 'int', isFunc: false } + }, + { + name: 'z float64', + identifier: 'z', + type: { name: 'float64', isFunc: false } + }, + { + name: 'opt ...interface{}', + identifier: 'opt', + type: { name: '...interface{}', isFunc: false } + } + ], + returns: [ + { + name: 'success bool', + identifier: 'success', + type: { name: 'bool', isFunc: false } + } + ] + }) + + t({ + input: 'func(prefix string, values ...int)', + args: [ + { + name: 'prefix string', + identifier: 'prefix', + type: { name: 'string', isFunc: false } + }, + { + name: 'values ...int', + identifier: 'values', + type: { name: '...int', isFunc: false } + } + ], + returns: [] + }) + + t({ + input: 'func(int, int, float64) (float64, *[]int)', + args: [ + { + name: 'int', + identifier: '', + type: { name: 'int', isFunc: false } + }, + { + name: 'int', + identifier: '', + type: { name: 'int', isFunc: false } + }, + { + name: 'float64', + identifier: '', + type: { name: 'float64', isFunc: false } + } + ], + returns: [ + { + name: 'float64', + identifier: '', + type: { name: 'float64', isFunc: false } + }, + { + name: '*[]int', + identifier: '', + type: { name: '*[]int', isFunc: false } + } + ] + }) + + t({ + input: 'func(n int) func(p *T)', + args: [ + { + name: 'n int', + identifier: 'n', + type: { name: 'int', isFunc: false } + } + ], + returns: [ + { + name: 'func(p *T)', + identifier: '', + type: { + isFunc: true, + name: 'func(p *T)', + args: [ + { + name: 'p *T', + identifier: 'p', + type: { name: '*T', isFunc: false } + } + ], + returns: [] + } + } + ] + }) + }) + }) + + describe('generateSnippet', () => { + const t = context => { + const result = Suggestions.generateSnippet( + { snipCount: 0, argCount: 0, snippetMode: 'nameAndType' }, + context.input.name, + context.input.type + ) + expect(result).toBeTruthy() + expect(result.displayText).toEqual(context.result.displayText) + expect(result.snippet).toEqual(context.result.snippet) + } + + it('parses the function into args and returns arrays', () => { + t({ + input: { + name: 'Print', + type: { + isFunc: true, + name: 'func()', + args: [], + returns: [] + } + }, + result: { + snippet: 'Print()', + displayText: 'Print()' + } + }) + + t({ + input: { + name: 'Print', + type: { + isFunc: true, + name: 'func(x int) int', + args: [ + { + name: 'x int', + identifier: 'x', + type: { name: 'int', isFunc: false } + } + ], + returns: [ + { + name: 'int', + identifier: '', + type: { name: 'int', isFunc: false } + } + ] + } + }, + result: { + snippet: 'Print(${1:x int})', // eslint-disable-line no-template-curly-in-string + displayText: 'Print(x int)' + } + }) + + t({ + input: { + name: 'ServeFunc', + type: { + isFunc: true, + name: + 'func(pattern string, func(w http.ResponseWriter, r *http.Request))', + args: [ + { + name: 'pattern string', + identifier: 'pattern', + type: { name: 'string', isFunc: false } + }, + { + name: 'func(w http.ResponseWriter, r *http.Request)', + identifier: '', + type: { + isFunc: true, + name: 'func(w http.ResponseWriter, r *http.Request)', + args: [ + { + name: 'w http.ResponseWriter', + identifier: 'w', + type: { name: 'http.ResponseWriter', isFunc: false } + }, + { + name: 'r *http.Request', + identifier: 'r', + type: { name: '*http.Request', isFunc: false } + } + ], + returns: [] + } + } + ], + returns: [] + } + }, + result: { + snippet: + 'ServeFunc(${1:pattern string}, ${2:func(${3:w} http.ResponseWriter, ${4:r} *http.Request) {\n\t$5\n\\}})', // eslint-disable-line no-template-curly-in-string + displayText: + 'ServeFunc(pattern string, func(w http.ResponseWriter, r *http.Request))' + } + }) + + t({ + input: { + name: 'It', + type: { + isFunc: true, + name: + 'func(text string, body interface{}, timeout ...float64) bool', + args: [ + { + name: 'text string', + identifier: 'text', + type: { name: 'string', isFunc: false } + }, + { + name: 'body interface{}', + identifier: 'body', + type: { name: 'interface{}', isFunc: false } + }, + { + name: 'timeout ...float64', + identifier: 'timeout', + type: { name: '...float64', isFunc: false } + } + ], + returns: [ + { + name: 'bool', + identifier: '', + type: { name: 'bool', isFunc: false } + } + ] + } + }, + result: { + // snippet: 'It(${1:text string}, ${2:body interface{\\}}, ${3:timeout ...float64})', + snippet: 'It(${1:text string}, ${2:body interface{\\}})', // eslint-disable-line no-template-curly-in-string + displayText: 'It(text string, body interface{}, timeout ...float64)' + } + }) + + t({ + input: { + name: 'Bleh', + type: { + isFunc: true, + name: 'func(f func() interface{})', + args: [ + { + name: 'f func() interface{}', + identifier: 'f', + type: { + isFunc: true, + name: 'func() interface{}', + args: [], + returns: [ + { + name: 'interface{}', + identifier: '', + type: { name: 'interface{}', isFunc: false } + } + ] + } + } + ], + returns: [] + } + }, + result: { + snippet: 'Bleh(${1:func() interface{\\} {\n\t$2\n\\}})', // eslint-disable-line no-template-curly-in-string + displayText: 'Bleh(func() interface{})' + } + }) + + // this is just a ridiculous func to test the limits of the function... + t({ + input: { + name: 'Bleh', + type: { + isFunc: true, + name: 'func(f func(i interface{}) func(interface{}) interface{})', + args: [ + { + name: 'f func(i interface{}) func(interface{}) interface{}', + identifier: 'f', + type: { + isFunc: true, + name: 'func(i interface{}) func(interface{}) interface{}', + args: [ + { + name: 'i interface{}', + identifier: 'i', + type: { name: 'interface{}', isFunc: false } + } + ], + returns: [ + { + name: 'func(interface{}) interface{}', + identifier: '', + type: { + isFunc: true, + name: 'func(interface{}) interface{}', + args: [ + { + name: 'interface{}', + identifier: 'i', + type: { name: 'interface{}', isFunc: false } + } + ], + returns: [ + { + name: 'interface{}', + identifier: '', + type: { name: 'interface{}', isFunc: false } + } + ] + } + } + ] + } + } + ], + returns: [] + } + }, + result: { + snippet: + 'Bleh(${1:func(${2:i} interface{\\}) func(interface{\\}) interface{\\} {\n\t$3\n\\}})', // eslint-disable-line no-template-curly-in-string + displayText: 'Bleh(func(i interface{}) func(interface{}) interface{})' + } + }) + /* + func(x int) int + func(a, _ int, z float32) bool + func(a, b int, z float32) (bool) + func(prefix string, values ...int) + func(a, b int, z float64, opt ...interface{}) (success bool) + func(int, int, float64) (float64, *[]int) + func(n int) func(p *T) + */ + }) + }) + + describe('ensureNextArg', () => { + it('parses params', () => { + let result = Suggestions.ensureNextArg(['f func() int']) + expect(result).toEqual(['f func() int']) + result = Suggestions.ensureNextArg(['f func() int, s string']) + expect(result).toEqual(['f func() int', 's string']) + result = Suggestions.ensureNextArg([ + 'f func(s1 string, i1 int) int, s string' + ]) + expect(result).toEqual(['f func(s1 string, i1 int) int', 's string']) + }) + }) + + describe('toSuggestion', () => { + const toSuggestion = (candidate, o = {}) => { + return Suggestions.toSuggestion(candidate, { + prefix: '', + suffix: '', + snippetMode: 'nameAndType', + ...o + }) + } + + it('generates snippets', () => { + let result = toSuggestion({ + class: 'func', + name: 'Abc', + type: 'func(f func() int)' + }) + expect(result.displayText).toBe('Abc(func() int)') + expect(result.snippet).toBe('Abc(${1:func() int {\n\t$2\n\\}})$0') // eslint-disable-line no-template-curly-in-string + + result = toSuggestion({ + class: 'func', + name: 'Abc', + type: 'func(f func() interface{})' + }) + expect(result.displayText).toBe('Abc(func() interface{})') + expect(result.snippet).toBe( + 'Abc(${1:func() interface{\\} {\n\t$2\n\\}})$0' + ) // eslint-disable-line no-template-curly-in-string + + result = toSuggestion({ + class: 'func', + name: 'Abc', + type: 'func(f func(int, string, bool) interface{})' + }) + expect(result.displayText).toBe( + 'Abc(func(arg1 int, arg2 string, arg3 bool) interface{})' + ) + expect(result.snippet).toBe( + 'Abc(${1:func(${2:arg1} int, ${3:arg2} string, ${4:arg3} bool) interface{\\} {\n\t$5\n\\}})$0' // eslint-disable-line no-template-curly-in-string + ) + + result = toSuggestion({ + class: 'func', + name: 'Abc', + type: 'func(f func() (interface{}, interface{}))' + }) + expect(result.displayText).toBe('Abc(func() (interface{}, interface{}))') + expect(result.snippet).toBe( + 'Abc(${1:func() (interface{\\}, interface{\\}) {\n\t$2\n\\}})$0' + ) // eslint-disable-line no-template-curly-in-string + + result = toSuggestion({ + class: 'func', + name: 'Abc', + type: 'func(f interface{})' + }) + expect(result.displayText).toBe('Abc(f interface{})') + expect(result.snippet).toBe('Abc(${1:f interface{\\}})$0') // eslint-disable-line no-template-curly-in-string + + // type HandlerFunc func(http.ResponseWriter, *http.Request) + result = toSuggestion({ + class: 'type', + name: 'HandlerFunc', + type: 'func(http.ResponseWriter, *http.Request)' + }) + expect(result.snippet).toBe( + 'HandlerFunc(func(${1:arg1} http.ResponseWriter, ${2:arg2} *http.Request) {\n\t$3\n\\})$0' + ) // eslint-disable-line no-template-curly-in-string + expect(result.displayText).toBe('HandlerFunc') + + // type FooBar func(int, string) string + result = toSuggestion({ + class: 'type', + name: 'FooBar', + type: 'func(int, string) string' + }) + expect(result.snippet).toBe( + 'FooBar(func(${1:arg1} int, ${2:arg2} string) string {\n\t$3\n\\})$0' + ) // eslint-disable-line no-template-curly-in-string + expect(result.displayText).toBe('FooBar') + + // type FooBar func(int, ...string) string + result = toSuggestion({ + class: 'type', + name: 'FooBar', + type: 'func(int, ...string) string' + }) + expect(result.snippet).toBe( + 'FooBar(func(${1:arg1} int, ${2:arg2} ...string) string {\n\t$3\n\\})$0' + ) // eslint-disable-line no-template-curly-in-string + expect(result.displayText).toBe('FooBar') + }) + + it('does not add function arguments for ( suffix', () => { + let result = toSuggestion( + { + class: 'func', + name: 'Abc', + type: 'func(f func() int)' + }, + { suffix: '(' } + ) + expect(result.text).toBe('Abc') + expect(result.snippet).toBeFalsy() + expect(result.displayText).toBeFalsy() + + // type FooBar func(int, string) string + result = toSuggestion( + { + class: 'type', + name: 'FooBar', + type: 'func(int, string) string' + }, + { suffix: '(' } + ) + expect(result.text).toBe('FooBar') + expect(result.snippet).toBeFalsy() + expect(result.displayText).toBeFalsy() + }) + }) +}) diff --git a/spec/build/builder-spec.js b/spec/build/builder-spec.js new file mode 100644 index 00000000..817275b5 --- /dev/null +++ b/spec/build/builder-spec.js @@ -0,0 +1,166 @@ +'use babel' +/* eslint-env jasmine */ + +import path from 'path' +import { Builder } from '../../lib/build/builder' +import { ConfigService } from '../../lib/config/service' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('builder', () => { + let builder = null + let linter + + beforeEach(async () => { + lifecycle.setup() + + // mock the Linter V1 Indie API + linter = { + deleteMessages: () => {}, + setMessages: () => {}, + dispose: () => {} + } + const goconfig = new ConfigService().provide() + builder = new Builder(goconfig, linter, null) + }) + + describe('test executable', () => { + it('provides a standard set of flags for compilation', () => { + ;['', ' '].forEach(setting => { + const args = builder.testCompileArgs('output', setting) + expect(args[0]).toEqual('test') + expect(args).toContain('-c') + expect(args).toContain('-o') + expect(args).toContain('.') + expect(args.includes('output')).toEqual(true) + }) + }) + + it('includes additional args', () => { + const args = builder.testCompileArgs('output', '-foo -bar 5') + expect(args[0]).toEqual('test') + expect(args).toContain('-c') + expect(args).toContain('-o') + expect(args.includes('output')).toEqual(true) + expect(args).toContain('.') + expect(args).toContain('-foo') + expect(args).toContain('-bar') + expect(args).toContain('5') + }) + + it('puts additional args before the package path', () => { + const args = builder.testCompileArgs('output', '-foo') + const dot = args.indexOf('.') + const foo = args.indexOf('-foo') + expect(dot).not.toEqual(-1) + expect(foo).not.toEqual(-1) + expect(foo).toBeLessThan(dot) + }) + + it('does not duplicate args', () => { + const args = builder.testCompileArgs('output', '-c') + expect(args.filter(x => x === '-c').length).toEqual(1) + }) + + it('does not allow overriding the output file', () => { + const args = builder.testCompileArgs('output', '-o /root/dont_write_here') + const i = args.indexOf('-o') + expect(i).not.toEqual(-1) + expect(args[i + 1]).not.toEqual('/root/dont_write_here') + }) + }) + + describe('build command', () => { + it('runs go build for code outside gopath', () => { + ;[ + { + gopath: 'C:\\Users\\jsmith\\go', + cwd: 'C:\\projects\\go\\test', + sep: '\\' + }, + { + gopath: '/home/jsmith/go', + cwd: '/home/jsmith/go', + sep: '/' + }, + { + gopath: '/home/jsmith/go', + cwd: '/home/jsmith/code/', + sep: '/' + }, + { + gopath: '/Users/jsmith/go', + cwd: '/Users/jsmith/documents', + sep: '/' + } + ].forEach(({ gopath, cwd, sep }) => { + expect(builder.buildCommand(gopath, cwd, sep)).toBe('build', cwd) + }) + }) + + it('runs go install for code in gopath', () => { + ;[ + { + gopath: 'C:\\Users\\jsmith\\go', + cwd: 'C:\\Users\\jsmith\\go\\src\\github.com\\foo', + sep: '\\' + }, + { + gopath: '/home/jsmith/go', + cwd: '/home/jsmith/go/src/bar', + sep: '/' + }, + { + gopath: '/Users/jsmith/go', + cwd: '/Users/jsmith/go/src/github.com/foo/bar', + sep: '/' + }, + { + gopath: '/Users/jsmith/go/', + cwd: '/Users/jsmith/go/src/github.com/foo/bar', + sep: '/' + } + ].forEach(({ gopath, cwd, sep }) => { + expect(builder.buildCommand(gopath, cwd, sep)).toBe('install', cwd) + }) + }) + }) + + describe('getMessages', () => { + it('ignores duplicate errors', () => { + // GIVEN the same results from both 'go install' and 'go test' + let outputs = [ + { + output: + '# github.com/anonymous/sample-project\n.\\the-file.go:12: syntax error: unexpected semicolon or newline, expecting comma or }', + linterName: 'build' + }, + { + output: + '# github.com/anonymous/sample-project\n.\\the-file.go:12: syntax error: unexpected semicolon or newline, expecting comma or }', + linterName: 'test' + } + ] + + // WHEN I get the messages for these outputs + const messages = builder.getMessages( + outputs, + path.join('src', 'github.com', 'anonymous', 'sample-project') + ) + + // THEN I expect only one message to be returned because they are the same + expect(messages.length).toEqual(1) + + const message = messages[0] + expect(message.name).toEqual('build') + expect( + message.excerpt.indexOf( + 'syntax error: unexpected semicolon or newline, expecting comma or }' + ) === 0 + ).toBeTruthy() + expect(message.location.file.indexOf('the-file.go') > 0).toBeTruthy() // file is in the path + expect(message.location.file.indexOf('sample-project') > 0).toBeTruthy() // cwd is in the path + expect(message.location.position.start.row).toEqual(11) + }) + }) +}) diff --git a/spec/config/environment-spec.js b/spec/config/environment-spec.js new file mode 100644 index 00000000..f2714e0c --- /dev/null +++ b/spec/config/environment-spec.js @@ -0,0 +1,57 @@ +'use babel' +/* eslint-env jasmine */ + +import { getgopath } from './../../lib/config/environment' +import * as pathhelper from './../../lib/config/pathhelper' +import path from 'path' +import { lifecycle } from './../spec-helpers' +import temp from '@atom/temp' + +describe('executor', () => { + let [envDir, configDir] = [] + beforeEach(() => { + lifecycle.setup() + envDir = temp.path('gopathenv') + configDir = temp.path('gopathconfig') + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('there is a gopath in the environment', () => { + beforeEach(() => { + process.env.GOPATH = envDir + atom.config.set('go-plus.config.gopath', configDir) + }) + + it("uses the environment's gopath", () => { + expect(getgopath()).toBeTruthy() + expect(getgopath()).toBe(envDir) + }) + }) + + describe('there is no gopath in the environment or config', () => { + beforeEach(() => { + delete process.env.GOPATH + atom.config.set('go-plus.config.gopath', '') + }) + + it('uses the default gopath', () => { + expect(getgopath()).toBeTruthy() + expect(getgopath()).toBe(path.join(pathhelper.home(), 'go')) + }) + }) + + describe('there is a gopath in config and not in the environment', () => { + beforeEach(() => { + delete process.env.GOPATH + atom.config.set('go-plus.config.gopath', configDir) + }) + + it("uses the config's gopath", () => { + expect(getgopath()).toBeTruthy() + expect(getgopath()).toBe(configDir) + }) + }) +}) diff --git a/spec/config/executor-spec.js b/spec/config/executor-spec.js new file mode 100644 index 00000000..ca7515c0 --- /dev/null +++ b/spec/config/executor-spec.js @@ -0,0 +1,159 @@ +'use babel' +/* eslint-env jasmine */ + +import { Executor } from './../../lib/config/executor' +import * as pathhelper from './../../lib/config/pathhelper' +import os from 'os' +import path from 'path' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('executor', () => { + let executor = null + let prefix = null + + beforeEach(() => { + lifecycle.setup() + prefix = '/' + if (os.platform() === 'win32') { + prefix = 'C:\\' + } + executor = new Executor() + }) + + describe('when asynchronously executing a command', () => { + it('succeeds', async () => { + let command = 'env' + if (os.platform() === 'win32') { + command = path.resolve( + __dirname, + 'tools', + 'env', + 'env_windows_amd64.exe' + ) + } + + const result = await executor.exec(command, [], { cwd: prefix }) + expect(result).toBeDefined() + expect(result.exitcode).toBeDefined() + expect(result.exitcode).toBe(0) + expect(result.stdout).toBeDefined() + expect(result.stdout).not.toBe('') + expect(result.stderr).toBeDefined() + expect(result.stderr).toBe('') + expect(result.error).toBeFalsy() + }) + + it('sets the working directory correctly', async () => { + let command = 'pwd' + if (os.platform() === 'win32') { + command = path.resolve( + __dirname, + 'tools', + 'pwd', + 'pwd_windows_amd64.exe' + ) + } + + const result = await executor.exec(command, [], { + cwd: pathhelper.home() + }) + expect(result).toBeDefined() + expect(result.exitcode).toBeDefined() + expect(result.exitcode).toBe(0) + expect(result.stdout).toBeDefined() + expect(result.stdout).toBe(pathhelper.home() + '\n') + expect(result.stderr).toBeDefined() + expect(result.stderr).toBe('') + expect(result.error).toBeFalsy() + }) + + it('sets the environment correctly', async () => { + let command = 'env' + if (os.platform() === 'win32') { + command = path.resolve( + __dirname, + 'tools', + 'env', + 'env_windows_amd64.exe' + ) + } + let env = { testenv: 'testing' } + const result = await executor.exec(command, [], { env }) + + expect(result).toBeDefined() + expect(result.exitcode).toBeDefined() + expect(result.exitcode).toBe(0) + expect(result.stdout).toBeDefined() + expect(result.stdout).toContain('testenv=testing\n') + expect(result.stderr).toBeDefined() + expect(result.stderr).toBe('') + expect(result.error).toBeFalsy() + }) + + it('handles and returns an ENOENT error if the command was not found', async () => { + const result = await executor.exec( + 'nonexistentcommand', + [], + executor.getOptions() + ) + expect(result).toBeTruthy() + expect(result.error).toBeTruthy() + expect(result.error.errno).toBe('ENOENT') + expect(result.error.message).toBe('spawn nonexistentcommand ENOENT') + expect(result.error.path).toBe('nonexistentcommand') + expect(result.exitcode).toBe(127) + expect(result.stdout).toBe('') + expect(result.stderr).toBeDefined() + if (os.platform() === 'win32') { + expect(result.stderr).toBe( + "'nonexistentcommand' is not recognized as an internal or external command,\r\noperable program or batch file.\r\n" + ) + } else { + expect(result.stderr).toBe('') + } + }) + }) + + describe('when synchronously executing a command', () => { + it('succeeds', () => { + let command = 'env' + if (os.platform() === 'win32') { + command = path.resolve( + __dirname, + 'tools', + 'env', + 'env_windows_amd64.exe' + ) + } + + let result = executor.execSync(command, [], executor.getOptions()) + expect(result.exitcode).toBeDefined() + expect(result.exitcode).toBe(0) + expect(result.stdout).toBeDefined() + expect(result.stdout).not.toBe('') + expect(result.stderr).toBeDefined() + expect(result.stderr).toBe('') + expect(result.error).toBeFalsy() + }) + + it('returns a message if the command was not found', () => { + let result = executor.execSync( + 'nonexistentcommand', + [], + executor.getOptions() + ) + expect(result.exitcode).toBeDefined() + expect(result.exitcode).toBe(127) + expect(result.stdout).toBeDefined() + expect(result.stdout).toBe('') + expect(result.stderr).toBeDefined() + expect(result.stderr).toBe('') + expect(result.error).toBeTruthy() + expect(result.error.code).toBe('ENOENT') + expect(result.error.errno).toBe('ENOENT') + expect(result.error.message).toBe('spawnSync nonexistentcommand ENOENT') + expect(result.error.path).toBe('nonexistentcommand') + }) + }) +}) diff --git a/spec/config/locator-spec.js b/spec/config/locator-spec.js new file mode 100644 index 00000000..fa861a95 --- /dev/null +++ b/spec/config/locator-spec.js @@ -0,0 +1,433 @@ +/* eslint-env jasmine */ +// @flow + +import { Executor } from './../../lib/config/executor' +import * as pathhelper from './../../lib/config/pathhelper' +import { Locator } from './../../lib/config/locator' +import fs from 'fs-extra' +import os from 'os' +import path from 'path' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('Locator', () => { + let executor = null + let platform = '' + let arch = '' + let executableSuffix = '' + let pathkey = '' + let locator: Locator + + beforeEach(() => { + lifecycle.setup() + if (process.env.GOROOT) { + delete process.env.GOROOT + } + platform = process.platform + if (process.arch === 'arm') { + arch = 'arm' + } else if (process.arch === 'ia32') { + // Ugh, Atom is 32-bit on Windows... for now. + if (platform === 'win32') { + arch = 'amd64' + } else { + arch = '386' + } + } else { + arch = 'amd64' + } + executor = new Executor(() => null) + executableSuffix = '' + pathkey = 'PATH' + if (process.platform === 'win32') { + platform = 'windows' + executableSuffix = '.exe' + pathkey = 'Path' + } + + locator = new Locator({ executor }) + }) + + afterEach(() => { + lifecycle.teardown() + if (executor !== null) { + executor.dispose() + executor = null + } + + if (locator) { + locator.dispose() + } + }) + + describe('when the environment is process.env', () => { + it('findExecutablesInPath returns an array with elements if its arguments are valid', () => { + expect(locator.findExecutablesInPath).toBeDefined() + if (os.platform() === 'win32') { + const r = locator.findExecutablesInPath('c:\\windows\\system32', [ + 'cmd.exe' + ]) + expect(r.length).toBe(1) + expect(r[0]).toBe('c:\\windows\\system32\\cmd.exe') + } else { + const r = locator.findExecutablesInPath('/bin', ['sh']) + expect(r.length).toBe(1) + expect(r[0]).toBe('/bin/sh') + } + }) + }) + + describe('when the environment has a GOPATH that includes a tilde', () => { + beforeEach(() => { + process.env.GOPATH = path.join('~', 'go') + }) + + it('gopath() returns a path with the home directory expanded', () => { + expect(locator.gopath).toBeDefined() + expect(locator.gopath()).toBe(path.join(pathhelper.home(), 'go')) + }) + + describe('when there is atom config for go-plus.config.gopath', () => { + beforeEach(() => { + atom.config.set('go-plus.config.gopath', '~/go2') + }) + + it('gopath() prioritizes the environment over the config', () => { + expect(locator.gopath).toBeDefined() + expect(locator.gopath()).toBe(path.join(pathhelper.home(), 'go')) + }) + }) + }) + + describe('when the environment has an empty GOPATH', () => { + beforeEach(() => { + if (process.env.GOPATH) { + delete process.env.GOPATH + } + }) + + it('gopath() returns the default GOPATH', () => { + expect(locator.gopath).toBeDefined() + expect(locator.gopath()).toBe(path.join(os.homedir(), 'go')) + }) + + describe('when there is atom config for go-plus.config.gopath', () => { + beforeEach(() => { + atom.config.set('go-plus.config.gopath', '~/go') + }) + + it('gopath() returns the expanded value for ~/go', () => { + expect(locator.gopath).toBeDefined() + expect(locator.gopath()).toBe(path.join(pathhelper.home(), 'go')) + }) + }) + + describe('when there is atom config for go-plus.config.gopath', () => { + beforeEach(() => { + atom.config.set('go-plus.config.gopath', '~/go') + }) + + it('gopath() returns the expanded value for ~/go', () => { + expect(locator.gopath).toBeDefined() + expect(locator.gopath()).toBe(path.join(pathhelper.home(), 'go')) + }) + }) + }) + + describe('when the environment has a GOPATH that is whitespace', () => { + beforeEach(() => { + process.env.GOPATH = ' ' + }) + + it('gopath() returns the default GOPATH', () => { + expect(locator.gopath).toBeDefined() + expect(locator.gopath()).toBe(path.join(os.homedir(), 'go')) + }) + + describe('when there is atom config for go-plus.config.gopath', () => { + beforeEach(() => { + atom.config.set('go-plus.config.gopath', '~/go') + }) + + it('gopath() returns the expanded value for ~/go', () => { + expect(locator.gopath).toBeDefined() + expect(locator.gopath()).toBe(path.join(pathhelper.home(), 'go')) + }) + }) + + describe('when there is atom config for go-plus.config.gopath', () => { + beforeEach(() => { + atom.config.set('go-plus.config.gopath', '~/go') + }) + + it('gopath() returns the expanded value for ~/go', () => { + expect(locator.gopath).toBeDefined() + expect(locator.gopath()).toBe(path.join(pathhelper.home(), 'go')) + }) + }) + }) + + describe('when the PATH has a single directory with a go executable in it', () => { + let godir = null + let go = null + beforeEach(() => { + godir = lifecycle.temp.mkdirSync('go-') + go = path.join(godir, 'go' + executableSuffix) + fs.writeFileSync(go, '.', { encoding: 'utf8', mode: 511 }) + process.env[pathkey] = godir + process.env.GOPATH = path.join('~', 'go') + }) + + it('runtimeCandidates() finds the runtime', () => { + expect(locator.runtimeCandidates).toBeDefined() + const candidates = locator.runtimeCandidates() + expect(candidates).toBeTruthy() + expect(candidates.length).toBeGreaterThan(0) + expect(candidates[0].path).toBe(go) + }) + }) + + describe('when GOROOT is set and the go tool is available within $GOROOT/bin', () => { + // a temporary $PATH where we place a fake go executable + let godir = null + let go = null + + // a temporary $GOROOT/bin directory where we place a fake go executable + let gorootgo = null + let gorootdir = null + let gorootbindir = null + + beforeEach(() => { + gorootdir = lifecycle.temp.mkdirSync('goroot-') + gorootbindir = path.join(gorootdir, 'bin') + fs.mkdirSync(gorootbindir) + gorootgo = path.join(gorootbindir, 'go' + executableSuffix) + + godir = lifecycle.temp.mkdirSync('go-') + go = path.join(godir, 'go' + executableSuffix) + + fs.writeFileSync(gorootgo, '.', { encoding: 'utf8', mode: 511 }) + fs.writeFileSync(go, '.', { encoding: 'utf8', mode: 511 }) + + process.env[pathkey] = godir + process.env.GOROOT = gorootdir + process.env.GOPATH = path.join('~', 'go') + }) + + afterEach(() => { + process.env.GOROOT = '' + }) + + it('runtimeCandidates() finds the runtime and orders the go in $GOROOT/bin before the go in PATH', () => { + expect(locator.runtimeCandidates).toBeDefined() + let candidates = locator.runtimeCandidates() + expect(candidates).toBeTruthy() + expect(candidates.length).toBeGreaterThan(1) + expect(candidates[0].path).toBe(gorootgo) + expect(candidates[1].path).toBe(go) + }) + }) + + describe('when the PATH has multiple directories with a go executable in it', () => { + let godir = '' + let go1dir = '' + let go = '' + let go1 = '' + + beforeEach(() => { + godir = lifecycle.temp.mkdirSync('go-') + go1dir = lifecycle.temp.mkdirSync('go1-') + go = path.join(godir, 'go' + executableSuffix) + go1 = path.join(go1dir, 'go' + executableSuffix) + fs.writeFileSync(go, '.', { encoding: 'utf8', mode: 511 }) + fs.writeFileSync(go1, '.', { encoding: 'utf8', mode: 511 }) + process.env[pathkey] = godir + path.delimiter + go1dir + }) + + it('runtimeCandidates() returns the candidates in the correct order', () => { + expect(locator.runtimeCandidates).toBeDefined() + let candidates = locator.runtimeCandidates() + expect(candidates).toBeTruthy() + expect(candidates.length).toBeGreaterThan(1) + expect(candidates[0].path).toBe(go) + expect(candidates[1].path).toBe(go1) + }) + + it('runtimeCandidates() returns candidates in the correct order when a candidate occurs multiple times in the path', () => { + process.env[pathkey] = + godir + path.delimiter + go1dir + path.delimiter + godir + expect(locator.runtimeCandidates).toBeDefined() + let candidates = locator.runtimeCandidates() + expect(candidates).toBeTruthy() + expect(candidates.length).toBeGreaterThan(1) + expect(candidates[0].path).toBe(go) + expect(candidates[1].path).toBe(go1) + if (candidates.length > 2) { + expect(candidates[2]).not.toBe(go) + } + }) + }) + + describe('when a go executable exists in $PATH and not in $GOROOT/bin', () => { + let tempPath = '' + let tempGoroot = '' + let tempGopath = '' + + let gorootbindir = '' + let gotooldir = '' + let goInPath = '' + + const gorootbintools = ['go', 'godoc', 'gofmt'] + const gotooldirtools = ['addr2line', 'cgo', 'cover', 'doc', 'vet'] + + beforeEach(() => { + tempPath = lifecycle.temp.mkdirSync('path-') + tempGoroot = lifecycle.temp.mkdirSync('goroot-') + tempGopath = lifecycle.temp.mkdirSync('gopath-') + + gorootbindir = path.join(tempGoroot, 'bin') + gotooldir = path.join(tempGoroot, 'pkg', 'tool', platform + '_' + arch) + + fs.mkdirSync(gorootbindir) + fs.mkdirsSync(gotooldir) + + // copy our fake go binary into our temporary $PATH dir + const fakeexecutable = 'go_' + platform + '_' + arch + executableSuffix + const fakego = path.join(__dirname, 'tools', 'go', fakeexecutable) + goInPath = path.join(tempPath, 'go' + executableSuffix) + fs.copySync(fakego, goInPath) + + process.env[pathkey] = tempPath + process.env['GOROOT'] = tempGoroot + process.env['GOPATH'] = tempGopath + + // write dummy tools to $GOROOT/bin + for (const tool of gorootbintools) { + if (tool !== 'go') { + const toolpath = path.join(gorootbindir, tool + executableSuffix) + fs.writeFileSync(toolpath, '.', { encoding: 'utf8', mode: 511 }) + } + } + // write dummy tools to $GOROOT/pkg/tool + for (const tool of gotooldirtools) { + const toolpath = path.join(gotooldir, tool + executableSuffix) + fs.writeFileSync(toolpath, '.', { encoding: 'utf8', mode: 511 }) + } + }) + + it('runtimeCandidates() finds go', () => { + expect(locator.runtimeCandidates).toBeDefined() + let candidates = locator.runtimeCandidates() + expect(candidates).toBeTruthy() + expect(candidates.length).toBeGreaterThan(0) + expect(candidates[0].path).toBe(goInPath) + }) + + it('runtimes() returns the runtime', async () => { + const runtimes = await locator.runtimes() + expect(runtimes).toBeTruthy() + expect(runtimes.length).toBeGreaterThan(0) + + const rt: any = runtimes[0] + expect(rt.locator).toBe('path-locator') + expect(rt.name).toBe('go1.99.1') + expect(rt.semver).toBe('1.99.1') + expect(rt.version).toBe('go version go1.99.1 ' + platform + '/' + arch) + expect(rt.path).toBe(goInPath) + expect(rt.GOARCH).toBe(arch) + expect(rt.GOBIN).toBe('') + expect(rt.GOEXE).toBe(platform === 'windows' ? '.exe' : '') + expect(rt.GOHOSTARCH).toBe(arch) + expect(rt.GOHOSTOS).toBe(platform) + expect(rt.GOOS).toBe(platform) + expect(rt.GOROOT).toBe(tempGoroot) + expect(rt.GOPATH).toBe(tempGopath) + expect(rt.GOTOOLDIR).toBe(gotooldir) + }) + + it('findTool() finds the go tool', async () => { + expect(locator.findTool).toBeDefined() + const tool = await locator.findTool('go') + expect(tool).toBe(path.join(tempPath, 'go' + executableSuffix)) + }) + + it('findTool() finds tools in GOROOT', async () => { + const tools = ['godoc', 'gofmt'] + const runtime = await locator.runtime() + expect(runtime).toBeTruthy() + if (runtime) { + for (const toolItem of tools) { + const toolPath = path.join( + runtime.GOROOT, + 'bin', + toolItem + runtime.GOEXE + ) + const tool = await locator.findTool(toolItem) + expect(tool).toBe(toolPath) + } + } + }) + + it('findTool() finds tools in GOTOOLDIR', async () => { + const tools = ['addr2line', 'cgo', 'cover', 'doc', 'vet'] + const runtime = await locator.runtime() + expect(runtime).toBeTruthy() + if (runtime) { + for (const toolItem of tools) { + const toolPath = path.join( + runtime.GOTOOLDIR, + toolItem + runtime.GOEXE + ) + const tool = await locator.findTool(toolItem) + expect(tool).toBe(toolPath) + } + } + }) + }) + + describe('when the path includes a directory with the gometalinter tool in it', () => { + let gopathdir = '' + let gopathbindir = '' + let pathdir = '' + const pathtools = ['gometalinter', 'gb'] + const gopathbintools = ['somerandomtool', 'gb'] + + beforeEach(() => { + pathdir = lifecycle.temp.mkdirSync('path-') + gopathdir = lifecycle.temp.mkdirSync('gopath-') + gopathbindir = path.join(gopathdir, 'bin') + fs.mkdirSync(gopathbindir) + process.env['GOPATH'] = gopathdir + process.env[pathkey] = + pathdir + path.delimiter + (process.env[pathkey] || '') + + const opts = { encoding: 'utf8', mode: 511 } + for (const tool of pathtools) { + const fp = path.join(pathdir, tool + executableSuffix) + fs.writeFileSync(fp, '.', opts) + } + for (const tool of gopathbintools) { + const fp = path.join(gopathbindir, tool + executableSuffix) + fs.writeFileSync(fp, '.', opts) + } + }) + + it('findTool() finds tools in PATH', async () => { + for (const toolItem of pathtools) { + const toolPath = gopathbintools.includes(toolItem) + ? path.join(gopathbindir, toolItem + executableSuffix) + : path.join(pathdir, toolItem + executableSuffix) + const tool = await locator.findTool(toolItem) + expect(tool).toBe(toolPath) + } + }) + + it("findTool() finds tools in GOPATH's bin directory", async () => { + for (const toolItem of gopathbintools) { + const toolPath = path.join(gopathbindir, toolItem + executableSuffix) + const tool = await locator.findTool(toolItem) + expect(tool).toBe(toolPath) + } + }) + }) +}) diff --git a/spec/config/pathhelper-spec.js b/spec/config/pathhelper-spec.js new file mode 100644 index 00000000..0624344f --- /dev/null +++ b/spec/config/pathhelper-spec.js @@ -0,0 +1,90 @@ +'use babel' +/* eslint-env jasmine */ + +import * as pathhelper from './../../lib/config/pathhelper' +import os from 'os' +import path from 'path' +import { lifecycle } from './../spec-helpers' + +describe('pathhelper', () => { + let gopathToken = '' + + beforeEach(() => { + lifecycle.setup() + runs(() => { + gopathToken = '$GOPATH' + if (os.platform() === 'win32') { + gopathToken = '%GOPATH%' + } + }) + }) + + describe('when working with a single-item path', () => { + it('expands the path', () => { + let env = Object.assign({}, process.env) + env.GOPATH = '~' + path.sep + 'go' + + let result = pathhelper.expand( + env, + path.join('~', 'go', 'go', '..', 'bin', 'goimports') + ) + expect(result).toBeDefined() + expect(result).toBeTruthy() + expect(result).toBe( + path.join(pathhelper.home(), 'go', 'bin', 'goimports') + ) + + result = pathhelper.expand( + env, + path.join(gopathToken, 'go', '..', 'bin', 'goimports') + ) + expect(result).toBeDefined() + expect(result).toBeTruthy() + expect(result).toBe( + path.join(pathhelper.home(), 'go', 'bin', 'goimports') + ) + + let root = path.sep + let nonexistentKey = '$NONEXISTENT' + if (os.platform() === 'win32') { + root = 'c:' + path.sep + nonexistentKey = '%NONEXISTENT%' + } + result = pathhelper.expand( + env, + path.join(root, nonexistentKey, 'go', '..', 'bin', 'goimports') + ) + expect(result).toBeDefined() + expect(result).toBeTruthy() + expect(result).toBe(path.join(root, nonexistentKey, 'bin', 'goimports')) + }) + }) + + describe('when working with a multi-item path', () => { + it('expands the path', () => { + let env = Object.assign({}, process.env) + env.GOPATH = + '~' + path.sep + 'go' + path.delimiter + '~' + path.sep + 'othergo' + + let result = pathhelper.expand( + env, + path.join('~', 'go', 'go', '..', 'bin', 'goimports') + ) + expect(result).toBeDefined() + expect(result).toBeTruthy() + expect(result).toBe( + path.join(pathhelper.home(), 'go', 'bin', 'goimports') + ) + + result = pathhelper.expand( + env, + path.join(gopathToken, 'go', '..', 'bin', 'goimports') + ) + expect(result).toBeDefined() + expect(result).toBeTruthy() + expect(result).toBe( + path.join(pathhelper.home(), 'go', 'bin', 'goimports') + ) + }) + }) +}) diff --git a/spec/config/tools/env/env_darwin_amd64 b/spec/config/tools/env/env_darwin_amd64 new file mode 100755 index 00000000..418cb98b Binary files /dev/null and b/spec/config/tools/env/env_darwin_amd64 differ diff --git a/spec/config/tools/env/env_linux_amd64 b/spec/config/tools/env/env_linux_amd64 new file mode 100755 index 00000000..da6f482b Binary files /dev/null and b/spec/config/tools/env/env_linux_amd64 differ diff --git a/spec/config/tools/env/env_windows_amd64.exe b/spec/config/tools/env/env_windows_amd64.exe new file mode 100755 index 00000000..b4ac83e6 Binary files /dev/null and b/spec/config/tools/env/env_windows_amd64.exe differ diff --git a/spec/config/tools/env/main.go b/spec/config/tools/env/main.go new file mode 100644 index 00000000..21fda90d --- /dev/null +++ b/spec/config/tools/env/main.go @@ -0,0 +1,13 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + env := os.Environ() + for _, item := range env { + fmt.Println(item) + } +} diff --git a/spec/config/tools/go/env_darwin.go b/spec/config/tools/go/env_darwin.go new file mode 100644 index 00000000..f3c77b01 --- /dev/null +++ b/spec/config/tools/go/env_darwin.go @@ -0,0 +1,31 @@ +package main + +const env = `{ + "CC": "clang", + "CGO_CFLAGS": "-g -O2", + "CGO_CPPFLAGS": "", + "CGO_CXXFLAGS": "-g -O2", + "CGO_ENABLED": "1", + "CGO_FFLAGS": "-g -O2", + "CGO_LDFLAGS": "-g -O2", + "CXX": "clang++", + "GCCGO": "gccgo", + "GOARCH": "amd64", + "GOBIN": "", + "GOCACHE": "/Users/foo/Library/Caches/go-build", + "GOEXE": "", + "GOFLAGS": "", + "GOGCCFLAGS": "-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/cn/99kczpg14fg5bmchk2kk8wyh0000gn/T/go-build813444343=/tmp/go-build -gno-record-gcc-switches -fno-common", + "GOHOSTARCH": "amd64", + "GOHOSTOS": "darwin", + "GOMOD": "", + "GOOS": "darwin", + "GOPATH": "/Users/foo/go", + "GOPROXY": "", + "GORACE": "", + "GOROOT": "/usr/local/Cellar/go/1.11.1/libexec", + "GOTMPDIR": "", + "GOTOOLDIR": "/usr/local/Cellar/go/1.11.1/libexec/pkg/tool/darwin_amd64", + "PKG_CONFIG": "pkg-config" +} +` diff --git a/spec/config/tools/go/env_linux.go b/spec/config/tools/go/env_linux.go new file mode 100644 index 00000000..6f3b0755 --- /dev/null +++ b/spec/config/tools/go/env_linux.go @@ -0,0 +1,31 @@ +package main + +const env = `{ + "CC": "clang", + "CGO_CFLAGS": "-g -O2", + "CGO_CPPFLAGS": "", + "CGO_CXXFLAGS": "-g -O2", + "CGO_ENABLED": "1", + "CGO_FFLAGS": "-g -O2", + "CGO_LDFLAGS": "-g -O2", + "CXX": "clang++", + "GCCGO": "gccgo", + "GOARCH": "amd64", + "GOBIN": "", + "GOCACHE": "/home/foo/.cache/go-build", + "GOEXE": "", + "GOFLAGS": "", + "GOGCCFLAGS": "-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=/var/folders/cn/99kczpg14fg5bmchk2kk8wyh0000gn/T/go-build813444343=/tmp/go-build -gno-record-gcc-switches -fno-common", + "GOHOSTARCH": "amd64", + "GOHOSTOS": "linux", + "GOMOD": "", + "GOOS": "linux", + "GOPATH": "/home/foo/go", + "GOPROXY": "", + "GORACE": "", + "GOROOT": "/usr/local/go", + "GOTMPDIR": "", + "GOTOOLDIR": "/usr/local/go/pkg/tool/linux_amd64", + "PKG_CONFIG": "pkg-config" +} +` diff --git a/spec/config/tools/go/env_windows.go b/spec/config/tools/go/env_windows.go new file mode 100644 index 00000000..6f5ee5b1 --- /dev/null +++ b/spec/config/tools/go/env_windows.go @@ -0,0 +1,31 @@ +package main + +const env = `{ + "CC": "clang", + "CGO_CFLAGS": "-g -O2", + "CGO_CPPFLAGS": "", + "CGO_CXXFLAGS": "-g -O2", + "CGO_ENABLED": "1", + "CGO_FFLAGS": "-g -O2", + "CGO_LDFLAGS": "-g -O2", + "CXX": "clang++", + "GCCGO": "gccgo", + "GOARCH": "amd64", + "GOBIN": "", + "GOCACHE": "C:\\Users\\foo\\AppData\\local\\go-build", + "GOEXE": ".exe", + "GOFLAGS": "", + "GOGCCFLAGS": "-fPIC -m64 -pthread -fno-caret-diagnostics -Qunused-arguments -fmessage-length=0 -fdebug-prefix-map=C:\\Users\\foo\\AppData\\local\\go-build\\asdfasdfa -gno-record-gcc-switches -fno-common", + "GOHOSTARCH": "amd64", + "GOHOSTOS": "windows", + "GOMOD": "", + "GOOS": "windows", + "GOPATH": "C:\\Users\\foo\\go", + "GOPROXY": "", + "GORACE": "", + "GOROOT": "C:\\Go", + "GOTMPDIR": "", + "GOTOOLDIR": "C:\\Go\\pkg\\tool\\windows_amd64", + "PKG_CONFIG": "pkg-config" +} +` diff --git a/spec/config/tools/go/go_darwin_amd64 b/spec/config/tools/go/go_darwin_amd64 new file mode 100755 index 00000000..45d2a39b Binary files /dev/null and b/spec/config/tools/go/go_darwin_amd64 differ diff --git a/spec/config/tools/go/go_linux_amd64 b/spec/config/tools/go/go_linux_amd64 new file mode 100755 index 00000000..c8900d06 Binary files /dev/null and b/spec/config/tools/go/go_linux_amd64 differ diff --git a/spec/config/tools/go/go_windows_amd64.exe b/spec/config/tools/go/go_windows_amd64.exe new file mode 100755 index 00000000..6bd56a30 Binary files /dev/null and b/spec/config/tools/go/go_windows_amd64.exe differ diff --git a/spec/config/tools/go/main.go b/spec/config/tools/go/main.go new file mode 100644 index 00000000..ff1fff93 --- /dev/null +++ b/spec/config/tools/go/main.go @@ -0,0 +1,95 @@ +package main + +import ( + "encoding/json" + "fmt" + "go/build" + "io/ioutil" + "os" + "path" + "path/filepath" + "runtime" + "strings" +) + +func main() { + if len(os.Args) <= 1 { + os.Exit(1) + } + + command := os.Args[1] + switch command { + case "version": + fmt.Printf("go version go1.99.1 %s/%s\n", runtime.GOOS, runtime.GOARCH) + return + case "get": + if os.Args[2] == "-u" { + get(os.Args[3]) + } else { + get(os.Args[2]) + } + return + case "env": + j := len(os.Args) == 3 && os.Args[2] == "-json" + printEnv(j) + return + default: + fmt.Println("unknown command", command) + os.Exit(1) + } +} + +// get simulates `go get` for a command line tool. +// it writes a dummy file go $GOPATH/bin +func get(packagePath string) { + if packagePath == "" { + fmt.Println("no package path was supplied to go get") + os.Exit(1) + } + + paths := strings.Split(packagePath, "/") + if len(paths) <= 1 { + fmt.Println("invalid package path:" + packagePath) + os.Exit(1) + } + p := paths[len(paths)-1] + suffix := "" + if runtime.GOOS == "windows" { + suffix = ".exe" + } + + gopath := os.Getenv("GOPATH") + if gopath == "" { + gopath = build.Default.GOPATH + } + + bin := path.Join(gopath, "bin", p+suffix) + if err := ioutil.WriteFile(bin, []byte("dummy file"), 0755); err != nil { + fmt.Printf("couldnt write to %s: %v", bin, err) + os.Exit(1) + } +} + +func printEnv(inJSON bool) { + var m map[string]string + json.Unmarshal([]byte(env), &m) + + m["GOPATH"] = os.Getenv("GOPATH") + m["GORACE"] = os.Getenv("GORACE") + if gr := os.Getenv("GOROOT"); gr != "" { + m["GOROOT"] = gr + } + m["GOTOOLDIR"] = filepath.Join(m["GOROOT"], "pkg", "tool", runtime.GOOS+"_"+runtime.GOARCH) + + if inJSON { + json.NewEncoder(os.Stdout).Encode(m) + return + } + prefix := "" + if m["GOOS"] == "windows" { + prefix = "set " + } + for k, v := range m { + fmt.Printf("%s%s=%s\n", prefix, k, v) + } +} diff --git a/spec/config/tools/pwd/main.go b/spec/config/tools/pwd/main.go new file mode 100644 index 00000000..8cecd14a --- /dev/null +++ b/spec/config/tools/pwd/main.go @@ -0,0 +1,15 @@ +package main + +import ( + "fmt" + "os" +) + +func main() { + pwd, err := os.Getwd() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + fmt.Println(pwd) +} diff --git a/spec/config/tools/pwd/pwd_darwin_amd64 b/spec/config/tools/pwd/pwd_darwin_amd64 new file mode 100755 index 00000000..64effe3f Binary files /dev/null and b/spec/config/tools/pwd/pwd_darwin_amd64 differ diff --git a/spec/config/tools/pwd/pwd_linux_amd64 b/spec/config/tools/pwd/pwd_linux_amd64 new file mode 100755 index 00000000..18feb767 Binary files /dev/null and b/spec/config/tools/pwd/pwd_linux_amd64 differ diff --git a/spec/config/tools/pwd/pwd_windows_amd64.exe b/spec/config/tools/pwd/pwd_windows_amd64.exe new file mode 100755 index 00000000..c5f99da7 Binary files /dev/null and b/spec/config/tools/pwd/pwd_windows_amd64.exe differ diff --git a/spec/doc/godoc-spec.js b/spec/doc/godoc-spec.js new file mode 100644 index 00000000..c75f1de1 --- /dev/null +++ b/spec/doc/godoc-spec.js @@ -0,0 +1,119 @@ +/* eslint-env jasmine */ +// @flow + +import path from 'path' +import fs from 'fs-extra' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('godoc', () => { + let godoc + let editor + let gopath + let source = null + let target = null + + beforeEach(async () => { + lifecycle.setup() + gopath = fs.realpathSync(lifecycle.temp.mkdirSync('gopath-')) + process.env.GOPATH = gopath + await lifecycle.activatePackage() + const { mainModule } = lifecycle + mainModule.provideGoConfig() + mainModule.provideGoGet() + godoc = mainModule.loadDoc() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('when determining if the decl is a method', () => { + it('returns the type of the method receiver (non-pointer)', () => { + const receiverType = godoc.declIsMethod('func (a Auth) Foo() error') + expect(receiverType).toBeDefined() + expect(receiverType).toBe('Auth') + }) + + it('returns the type of the method receiver (pointer)', () => { + const receiverType = godoc.declIsMethod('func (a *Auth) Foo() error') + expect(receiverType).toBeDefined() + expect(receiverType).toBe('Auth') + }) + + it('returns undefined for non-methods', () => { + for (const decl of [ + 'func Foo() error', + 'var w io.Writer', + 'const Foo = "Bar"' + ]) { + const result = godoc.declIsMethod(decl) + expect(result).not.toBeDefined() + } + }) + }) + + describe('when the godoc command is invoked on a valid go file', () => { + let result + beforeEach(async () => { + source = path.join(__dirname, '..', 'fixtures') + target = path.join(gopath, 'src', 'godoctest') + fs.copySync(source, target) + atom.project.setPaths([target]) + editor = await atom.workspace.open(path.join(target, 'doc.go')) + editor.setCursorBufferPosition([24, 10]) + }) + + it('provides tooltips', async () => { + const pos = editor.getCursorBufferPosition() + const tip = await godoc.datatip(editor, pos) + expect(tip).toBeTruthy() + expect(tip.range.start).toBe(pos) + expect(tip.range.end).toBe(pos) + expect(tip.markedStrings.length).toEqual(1) + }) + + it('executes gogetdoc successfully', () => { + runs(async () => { + result = await godoc.commandInvoked() + expect(result).toBeTruthy() + expect(result.success).toBe(true) + }) + }) + + it('returns a godoc.org link', () => { + expect(result.doc.gddo).toBe( + 'https://godoc.org/godoctest#Foo.ChangeMessage' + ) + }) + }) + + describe('when the godoc command is invoked on an unsaved go file', () => { + beforeEach(async () => { + source = path.join(__dirname, '..', 'fixtures') + target = path.join(gopath, 'src', 'godoctest') + fs.copySync(source, target) + atom.project.setPaths([target]) + editor = await atom.workspace.open(path.join(target, 'doc.go')) + expect(editor).toBeDefined() + editor.setCursorBufferPosition([24, 35]) + editor.selectLinesContainingCursors() + editor.insertText('fmt.Printf("this line has been modified\\n")\n') + expect(editor.isModified()).toBe(true) + }) + + it('gets the correct documentation', async () => { + let result = false + editor.setCursorBufferPosition([24, 7]) + result = await godoc.commandInvoked() + expect(result).toBeTruthy() + expect(result.success).toBe(true) + expect(result.doc).toBeTruthy() + expect(result.doc.import).toBe('fmt') + expect(result.doc.decl).toBe( + 'func Printf(format string, a ...interface{}) (n int, err error)' + ) + expect(result.doc.gddo).toBe('https://godoc.org/fmt#Printf') + }) + }) +}) diff --git a/spec/format/formatter-spec.js b/spec/format/formatter-spec.js new file mode 100644 index 00000000..ff9d7468 --- /dev/null +++ b/spec/format/formatter-spec.js @@ -0,0 +1,58 @@ +'use babel' +/* eslint-env jasmine */ + +import path from 'path' +import { ConfigService } from '../../lib/config/service' +import { Formatter } from '../../lib/format/formatter' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +const nl = '\n' +const formattedText = 'package main' + nl + nl + 'func main() {' + nl + '}' + nl + +describe('formatter', () => { + let formatter = null + + beforeEach(async () => { + await atom.packages.activatePackage('language-go') + atom.config.set('editor.defaultLineEnding', 'LF') + atom.config.set('go-plus.test.runTestsOnSave', false) + formatter = new Formatter(new ConfigService().provide()) + }) + + afterEach(() => { + formatter.dispose() + }) + + describe('when a simple file is opened', () => { + let editor + + beforeEach(async () => { + const filePath = path.join( + __dirname, + '..', + 'fixtures', + 'format', + 'gofmt.go' + ) + editor = await atom.workspace.open(filePath) + }) + + describe('for each tool', () => { + for (const tool of ['gofmt', 'goimports', 'goreturns']) { + it('formats on save using ' + tool, () => { + runs(() => { + atom.config.set('go-plus.format.tool', tool) + }) + waitsFor(() => { + return formatter.tool === tool + }) + runs(async () => { + const result = await formatter.formatEntireFile(editor, null) + expect(result).toBeTruthy() + expect(result.formatted).toEqual(formattedText) + }) + }) + } + }) + }) +}) diff --git a/spec/get/get-manager-spec.js b/spec/get/get-manager-spec.js new file mode 100644 index 00000000..e6d7c3a8 --- /dev/null +++ b/spec/get/get-manager-spec.js @@ -0,0 +1,108 @@ +'use babel' +/* eslint-env jasmine */ + +import fs from 'fs-extra' +import path from 'path' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('go-get', () => { + let manager = null + let gopath + let platform + let arch + let executableSuffix = '' + let pathkey = 'PATH' + let go + + beforeEach(async () => { + lifecycle.setup() + gopath = fs.realpathSync(lifecycle.temp.mkdirSync('gopath-')) + const goroot = fs.realpathSync(lifecycle.temp.mkdirSync('goroot-')) + const gorootbin = path.join(goroot, 'bin') + fs.mkdirSync(gorootbin) + platform = process.platform + if (process.arch === 'arm') { + arch = 'arm' + } else if (process.arch === 'ia32') { + // Ugh, Atom is 32-bit on Windows... for now. + if (platform === 'win32') { + arch = 'amd64' + } else { + arch = '386' + } + } else { + arch = 'amd64' + } + + if (process.platform === 'win32') { + platform = 'windows' + executableSuffix = '.exe' + pathkey = 'Path' + } + const fakeexecutable = 'go_' + platform + '_' + arch + executableSuffix + const configPath = path.join(__dirname, '..', 'config') + const fakego = path.join(configPath, 'tools', 'go', fakeexecutable) + go = path.join(gorootbin, 'go' + executableSuffix) + fs.copySync(fakego, go) + process.env[pathkey] = gorootbin + process.env['GOPATH'] = gopath + process.env['GOROOT'] = goroot + + await lifecycle.activatePackage() + const { mainModule } = lifecycle + mainModule.provideGoGet() + manager = mainModule.getservice.getmanager + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('manager', () => { + let gocodebinary + let goimportsbinary + beforeEach(() => { + fs.mkdirSync(path.join(gopath, 'bin')) + gocodebinary = path.join(gopath, 'bin', 'gocode' + executableSuffix) + fs.writeFileSync(gocodebinary, '', { encoding: 'utf8', mode: 511 }) + goimportsbinary = path.join(gopath, 'bin', 'goimports' + executableSuffix) + fs.writeFileSync(goimportsbinary, '', { encoding: 'utf8', mode: 511 }) + }) + + it('updates packages', async () => { + let stat = fs.statSync(gocodebinary) + expect(stat.size).toBe(0) + + manager.register('github.com/mdempsky/gocode') + manager.register('golang.org/x/tools/cmd/goimports') + const outcome = await manager.updateTools() + expect(outcome.success).toEqual(true, 'outcome is ', outcome) + expect(outcome.results.length).toBe(2) + + stat = fs.statSync(gocodebinary) + expect(stat.size).toBeGreaterThan(0) + stat = fs.statSync(goimportsbinary) + expect(stat.size).toBeGreaterThan(0) + }) + + it('calls the callback after updating packages, if provided', async () => { + let callbackOutcome + let callbackCalled + let stat = fs.statSync(gocodebinary) + expect(stat.size).toBe(0) + manager.register('golang.org/x/tools/cmd/goimports', o => { + callbackCalled = true + callbackOutcome = o + }) + let outcome = await manager.updateTools() + expect(callbackCalled).toBe(true) + expect(outcome).toBeTruthy() + expect(outcome.success).toBe(true) + expect(outcome.results).toBeTruthy() + expect(outcome.results.length).toBe(1) + expect(outcome.results[0].pack).toBe('golang.org/x/tools/cmd/goimports') + expect(callbackOutcome).toBe(outcome) + }) + }) +}) diff --git a/spec/get/provider-spec.js b/spec/get/provider-spec.js new file mode 100644 index 00000000..4def0e27 --- /dev/null +++ b/spec/get/provider-spec.js @@ -0,0 +1,94 @@ +'use babel' +/* eslint-env jasmine */ + +import { lifecycle } from './../spec-helpers' +import mainModule from './../../lib/main' + +describe('go-get service provider', () => { + beforeEach(() => { + lifecycle.setup() + mainModule.activate() + }) + + afterEach(() => { + lifecycle.teardown() + mainModule.deactivate() + }) + + describe('the provider', () => { + it('is truthy', () => { + expect(mainModule.provideGoGet).toBeDefined() + expect(mainModule.provideGoGet()).toBeTruthy() + }) + }) + + describe('the 2.0.0 provider', () => { + it('is truthy', () => { + expect(mainModule.provideGoGet).toBeDefined() + expect(mainModule.provideGoGet()).toBeTruthy() + }) + + it('has a get function', () => { + expect(mainModule.provideGoGet().get).toBeDefined() + }) + + it('has a register function', () => { + expect(mainModule.provideGoGet().register).toBeDefined() + }) + + describe('register()', () => { + let manager + let provider + beforeEach(() => { + provider = mainModule.provideGoGet() + manager = mainModule.getservice.getmanager + expect(manager).toBeTruthy() + expect(manager.packages).toBeTruthy() + expect(manager.packages.size).toBe(0) + }) + + it('registers a package', () => { + provider.register('github.com/mdempsky/gocode') + expect(manager.packages.size).toBe(1) + provider.register('github.com/mdempsky/gocode') + expect(manager.packages.size).toBe(1) + }) + + it('registers the same package multiple times', () => { + provider.register('github.com/mdempsky/gocode') + expect(manager.packages.size).toBe(1) + provider.register('github.com/mdempsky/gocode') + expect(manager.packages.size).toBe(1) + provider.register('github.com/mdempsky/gocode') + expect(manager.packages.size).toBe(1) + }) + + it('allows a package registration to be disposed', () => { + let registration = provider.register('github.com/mdempsky/gocode') + expect(registration).toBeTruthy() + expect(registration.dispose).toBeDefined() + expect(manager.packages.size).toBe(1) + registration.dispose() + expect(manager.packages.size).toBe(0) + }) + + it('dispose is aware of the number of package registrations', () => { + let registration1 = provider.register('github.com/mdempsky/gocode') + expect(registration1).toBeTruthy() + expect(registration1.dispose).toBeDefined() + expect(manager.packages.size).toBe(1) + let registration2 = provider.register('github.com/mdempsky/gocode') + expect(registration2).toBeTruthy() + expect(registration2.dispose).toBeDefined() + expect(manager.packages.size).toBe(1) + registration1.dispose() + expect(manager.packages.size).toBe(1) + registration2.dispose() + expect(manager.packages.size).toBe(0) + registration1.dispose() + registration2.dispose() + expect(manager.packages.size).toBe(0) + }) + }) + }) +}) diff --git a/spec/highlight/highlight-provider-spec.js b/spec/highlight/highlight-provider-spec.js new file mode 100644 index 00000000..7fe1afa7 --- /dev/null +++ b/spec/highlight/highlight-provider-spec.js @@ -0,0 +1,73 @@ +'use babel' +/* eslint-env jasmine */ + +import fs from 'fs-extra' +import path from 'path' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('Highlight Provider', () => { + let highlight + + beforeEach(async () => { + lifecycle.setup() + await lifecycle.activatePackage() + + const { mainModule } = lifecycle + mainModule.provideGoConfig() + highlight = mainModule.provideCodeHighlight() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + it('monitors the config', () => { + atom.config.set('go-plus.guru.highlightIdentifiers', false) + expect(highlight.shouldDecorate).toBe(false) + atom.config.set('go-plus.guru.highlightIdentifiers', true) + expect(highlight.shouldDecorate).toBe(true) + atom.config.set('go-plus.guru.highlightIdentifiers', false) + expect(highlight.shouldDecorate).toBe(false) + }) + + describe('when run on a valid go file', () => { + let editor + let gopath = null + let source = null + let target = null + + beforeEach(async () => { + gopath = fs.realpathSync(lifecycle.temp.mkdirSync('gopath-')) + process.env.GOPATH = gopath + + source = path.join(__dirname, '..', 'fixtures') + target = path.join(gopath, 'src', 'what') + fs.copySync(source, target) + + atom.config.set('go-plus.guru.highlightIdentifiers', true) + + editor = await atom.workspace.open(path.join(target || '.', 'doc.go')) + }) + + it('returns the appropriate ranges', async () => { + editor.setCursorBufferPosition([22, 2]) + + const ranges = await highlight.highlight( + editor, + editor.getCursorBufferPosition() + ) + expect(ranges.length).toEqual(3) + expect(ranges[0].start.row).toEqual(22) + expect(ranges[0].start.column).toEqual(1) + expect(ranges[1].start.row).toEqual(23) + expect(ranges[1].start.column).toEqual(38) + expect(ranges[2].start.row).toEqual(24) + expect(ranges[2].start.column).toEqual(1) + + ranges.forEach(r => { + expect(editor.getTextInBufferRange(r)).toEqual('f') + }) + }) + }) +}) diff --git a/spec/implements/implements-spec.js b/spec/implements/implements-spec.js new file mode 100644 index 00000000..173141f5 --- /dev/null +++ b/spec/implements/implements-spec.js @@ -0,0 +1,75 @@ +'use babel' +/* eslint-env jasmine */ + +import fs from 'fs-extra' +import path from 'path' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('implements', () => { + let impl + let editor + let gopath + let source + let target + + beforeEach(async () => { + lifecycle.setup() + + gopath = fs.realpathSync(lifecycle.temp.mkdirSync('gopath-')) + process.env.GOPATH = gopath + + source = path.join(__dirname, '..', 'fixtures', 'implements') + target = path.join(gopath, 'src', 'implements') + fs.copySync(source, target) + await lifecycle.activatePackage() + const { mainModule } = lifecycle + mainModule.provideGoConfig() + impl = mainModule.loadImplements() + impl.view = {} + impl.view.update = jasmine.createSpy('update') + impl.requestFocus = jasmine + .createSpy('requestFocus') + .andReturn(Promise.resolve()) + + editor = await atom.workspace.open(path.join(target, 'main.go')) + }) + + afterEach(() => { + lifecycle.teardown() + }) + + it('updates the view when invoking guru', async () => { + await impl.handleCommand() + expect(impl.view.update).toHaveBeenCalled() + }) + + it('updates the view when guru fails', async () => { + await impl.handleCommand() + expect(impl.view.update).toHaveBeenCalled() + expect(impl.view.update.calls.length).toBe(2) + + const args0 = impl.view.update.calls[0].args[0] + const args1 = impl.view.update.calls[1].args[0] + expect(args0.startsWith('running guru')).toBe(true) + expect(args1.startsWith('guru failed')).toBe(true) + }) + + it('updates the view when guru succeeds', async () => { + editor.setCursorBufferPosition([4, 9]) + await impl.handleCommand() + expect(impl.view.update).toHaveBeenCalled() + expect(impl.view.update.calls.length).toBe(2) + + const args0 = impl.view.update.calls[0].args[0] + const args1 = impl.view.update.calls[1].args[0] + expect(args0.startsWith('running guru')).toBe(true) + + expect(typeof args1).toBe('object') + expect(args1.type.kind).toBe('struct') + expect(args1.type.name).toBe('implements.Impl') + expect(args1.from.length).toBe(2) + expect(args1.from[0].name).toBe('implements.Fooer') + expect(args1.from[1].name).toBe('io.Reader') + }) +}) diff --git a/spec/import/importer-spec.js b/spec/import/importer-spec.js new file mode 100644 index 00000000..76f1a92d --- /dev/null +++ b/spec/import/importer-spec.js @@ -0,0 +1,72 @@ +/* eslint-env jasmine */ + +import path from 'path' + +import { importablePackages } from './../../lib/import/importer' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('importablePackages', () => { + const all = [ + 'github.com/user1/project1/', + 'github.com/user1/project1/subpackage', + 'github.com/user1/project1/internal/privatelib', + 'github.com/user2/project1/vendor/github.com/author/lib/subpackage', + 'github.com/user2/project1/nested/package', + 'github.com/user2/project2/subpackage/vendor/github.com/author/lib/subpackage' + ] + + it('does not present vendor or internal directories from other projects', () => { + const importable = importablePackages('github.com/user3/newproject', all) + expect(importable).toEqual([all[0], all[1], all[4]]) + }) + + it('presents internal packages for the same project', () => { + const importable = importablePackages('github.com/user1/project1/foo', all) + expect(importable).toContain( + 'github.com/user1/project1/internal/privatelib' + ) + }) + + it('presents vendor packages for the same project', () => { + const importable = importablePackages( + 'github.com/user2/project1/foo/bar', + all + ) + expect(importable).toContain('github.com/author/lib/subpackage') + }) + + it('handles nested vendor packages correctly', () => { + const nestedVendor = 'github.com/author/lib/subpackage' + expect( + importablePackages('github.com/user2/project2/foo/bar', all) + ).not.toContain(nestedVendor) + expect( + importablePackages('github.com/user2/project2/subpackage/a/b', all) + ).toContain(nestedVendor) + }) +}) + +describe('importer', () => { + let importer = null + let editor = null + + beforeEach(async () => { + lifecycle.setup() + await lifecycle.activatePackage() + const { mainModule } = lifecycle + mainModule.provideGoConfig() + importer = mainModule.loadImporter() + editor = await atom.workspace.open( + path.join(__dirname, '..', 'fixtures', 'doc.go') + ) + }) + + afterEach(() => lifecycle.teardown()) + + it('adds imports', async () => { + const result = await importer.addImport('bufio') + expect(result.success).toBe(true) + expect(editor.lineTextForBufferRow(3).trim()).toEqual('"bufio"') + }) +}) diff --git a/spec/main/main-spec.js b/spec/main/main-spec.js new file mode 100644 index 00000000..7ee0fa96 --- /dev/null +++ b/spec/main/main-spec.js @@ -0,0 +1,26 @@ +'use babel' +/* eslint-env jasmine */ + +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('go-plus', () => { + beforeEach(async () => { + lifecycle.setup() + await lifecycle.activatePackage() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('when the go-plus package is activated', () => { + it('activates successfully', () => { + const { mainModule } = lifecycle + expect(mainModule).toBeDefined() + expect(mainModule).toBeTruthy() + expect(mainModule.activate).toBeDefined() + expect(mainModule.deactivate).toBeDefined() + }) + }) +}) diff --git a/spec/navigator/navigator-spec.js b/spec/navigator/navigator-spec.js new file mode 100644 index 00000000..310796d7 --- /dev/null +++ b/spec/navigator/navigator-spec.js @@ -0,0 +1,89 @@ +'use babel' +/* eslint-env jasmine */ + +import path from 'path' +import fs from 'fs-extra' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('go to definition', () => { + let navigator = null + let gopath = null + + beforeEach(async () => { + lifecycle.setup() + gopath = fs.realpathSync(lifecycle.temp.mkdirSync('gopath-')) + process.env.GOPATH = gopath + await lifecycle.activatePackage() + const { mainModule } = lifecycle + mainModule.provideGoConfig() + mainModule.provideGoGet() + navigator = mainModule.loadNavigator() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('when invoked on a valid project file', () => { + let sourceDir + let targetDir + let editor + + beforeEach(async () => { + sourceDir = path.join(__dirname, '..', 'fixtures', 'navigator') + targetDir = path.join(gopath, 'src', 'godeftest') + fs.copySync(sourceDir, targetDir) + editor = await atom.workspace.open(path.join(targetDir, 'foo.go')) + }) + + describe('when using the godef navigator mode', () => { + beforeEach(() => { + atom.config.set('go-plus.navigator.mode', 'godef') + }) + + it('navigates to the correct location', async () => { + editor.setCursorBufferPosition([3, 17]) + await navigator.gotoDefinitionForWordAtCursor() + const activeEditor = atom.workspace.getActiveTextEditor() + expect(activeEditor.getTitle()).toBe('bar.go') + + const pos = activeEditor.getCursorBufferPosition() + expect(pos.row).toBe(2) + expect(pos.column).toBe(5) + expect(navigator.navigationStack.isEmpty()).toBe(false) + }) + }) + + describe('when using the guru navigator mode', () => { + beforeEach(() => { + atom.config.set('go-plus.navigator.mode', 'guru') + }) + + it('navigates to the correct location', async () => { + editor.setCursorBufferPosition([3, 17]) // at the beginning of -> `Bar()` + await navigator.gotoDefinitionForWordAtCursor() + const activeEditor = atom.workspace.getActiveTextEditor() + expect(activeEditor.getTitle()).toBe('bar.go') + const pos = activeEditor.getCursorBufferPosition() + expect(pos.row).toBe(2) + expect(pos.column).toBe(5) + expect(navigator.navigationStack.isEmpty()).toBe(false) + }) + + it('navigates to the correct location if at the end of a word', async () => { + editor.setCursorBufferPosition([3, 20]) // at the end of `Bar()` <- + await navigator.gotoDefinitionForWordAtCursor() + + const activeEditor = atom.workspace.getActiveTextEditor() + expect(activeEditor.getTitle()).toBe('bar.go') + + const pos = activeEditor.getCursorBufferPosition() + expect(pos.row).toBe(2) + expect(pos.column).toBe(5) + + expect(navigator.navigationStack.isEmpty()).toBe(false) + }) + }) + }) +}) diff --git a/spec/orchestrator-spec.js b/spec/orchestrator-spec.js new file mode 100644 index 00000000..e5f22e48 --- /dev/null +++ b/spec/orchestrator-spec.js @@ -0,0 +1,191 @@ +'use babel' +/* eslint-env jasmine */ + +import path from 'path' +import { Orchestrator } from './../lib/orchestrator' +import { it, fit, ffit, beforeEach, runs } from './async-spec-helpers' // eslint-disable-line + +describe('orchestrator', () => { + let orchestrator = null + + beforeEach(async () => { + orchestrator = new Orchestrator() + await atom.packages.activatePackage('language-go') + }) + + afterEach(() => { + orchestrator.dispose() + }) + + describe('register', () => { + it('throws an error if a callback is not provided', () => { + expect(() => { + orchestrator.register('test') + }).toThrow() + }) + + it('throws an error if the callback is not a function', () => { + expect(() => { + orchestrator.register('test', 42) + }).toThrow() + }) + + it('registers a callback that can be unregistered', () => { + const callback = () => {} + const disp = orchestrator.register('test', callback) + expect(orchestrator.didSaveCallbacks.size).toBe(1) + + disp.dispose() + expect(orchestrator.didSaveCallbacks.size).toBe(0) + }) + + it('runs a single callback', async () => { + let called = false + const callback = () => { + called = true + return Promise.resolve(true) + } + orchestrator.register('test', callback) + + const filepath = path.join(__dirname, 'fixtures', 'main.go') + const editor = await atom.workspace.open(filepath) + await editor.save() + expect(called).toBe(true) + }) + + it('runs multiple callbacks', () => { + const called = [false, false] + + runs(async () => { + const callback0 = () => { + called[0] = true + return Promise.resolve(true) + } + const callback1 = () => { + called[1] = true + return Promise.resolve(true) + } + orchestrator.register('callback0', callback0) + orchestrator.register('callback1', callback1) + + const filepath = path.join(__dirname, 'fixtures', 'main.go') + const editor = await atom.workspace.open(filepath) + await editor.save() + }) + + waitsFor( + () => called[0] === true && called[1] === true, + 'Both callbacks should be called', + 1000 + ) + + runs(() => { + expect(called[0]).toBe(true) + expect(called[1]).toBe(true) + }) + }) + + it('stops invoking callbacks when a promise is rejected', async () => { + let called = [false, false] + const callback0 = () => { + called[0] = true + return Promise.reject(new Error()) + } + const callback1 = () => { + called[1] = true + return Promise.resolve(true) + } + orchestrator.register('callback0', callback0) + orchestrator.register('callback1', callback1) + + const filepath = path.join(__dirname, 'fixtures', 'main.go') + const editor = await atom.workspace.open(filepath) + await editor.save() + + expect(called[0]).toBe(true) + expect(called[1]).toBe(false) + }) + }) + + describe('register / onWillSave', () => { + it('throws an error if a callback is not provided', () => { + expect(() => { + orchestrator.register('test', undefined, 'willSave') + }).toThrow() + }) + + it('throws an error if the callback is not a function', () => { + expect(() => { + orchestrator.register('test', 42, 'willSave') + }).toThrow() + }) + + it('registers a callback that can be unregistered', () => { + const callback = () => {} + const disp = orchestrator.register('test', callback, 'willSave') + expect(orchestrator.willSaveCallbacks.size).toBe(1) + + disp.dispose() + expect(orchestrator.willSaveCallbacks.size).toBe(0) + }) + + it('runs a single callback', async () => { + let called = false + + const callback = () => { + called = true + return true + } + orchestrator.register('test', callback, 'willSave') + + const filepath = path.join(__dirname, 'fixtures', 'main.go') + const editor = await atom.workspace.open(filepath) + await editor.save() + expect(called).toBe(true) + }) + + it('runs multiple callbacks', async () => { + let called = [false, false] + + const callback0 = () => { + called[0] = true + return true + } + const callback1 = () => { + called[1] = true + return true + } + orchestrator.register('test', callback0, 'willSave') + orchestrator.register('test', callback1, 'willSave') + + const filepath = path.join(__dirname, 'fixtures', 'main.go') + const editor = await atom.workspace.open(filepath) + await editor.save() + + expect(called[0]).toBe(true) + expect(called[1]).toBe(true) + }) + + it('stops invoking callbacks when encountering a non-true return value', async () => { + let called = [false, false] + + const callback0 = () => { + called[0] = true + return false + } + const callback1 = () => { + called[1] = true + return true + } + orchestrator.register('test', callback0, 'willSave') + orchestrator.register('test', callback1, 'willSave') + + const filepath = path.join(__dirname, 'fixtures', 'main.go') + const editor = await atom.workspace.open(filepath) + await editor.save() + + expect(called[0]).toBe(true) + expect(called[1]).toBe(false) + }) + }) +}) diff --git a/spec/outline/outline-provider-spec.js b/spec/outline/outline-provider-spec.js new file mode 100644 index 00000000..7034c77b --- /dev/null +++ b/spec/outline/outline-provider-spec.js @@ -0,0 +1,126 @@ +'use babel' +/* eslint-env jasmine */ + +import path from 'path' +import { OutlineProvider } from '../../lib/outline/outline-provider' +import { ConfigService } from '../../lib/config/service' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('Outline Provider', () => { + let editor + let provider + let outline + + beforeEach(async () => { + provider = new OutlineProvider(new ConfigService().provide()) + + const p = path.join(__dirname, '..', 'fixtures', 'outline', 'outline.go') + editor = await atom.workspace.open(p) + + outline = await provider.getOutline(editor) + }) + + it('returns an outline', () => { + expect(outline).toBeDefined() + expect(outline.outlineTrees).toBeDefined() + expect(outline.outlineTrees.length).toEqual(1) + }) + + it('returns the file at the root of the outline', () => { + const f = outline.outlineTrees[0] + expect(f.kind).toEqual('file') + expect(f.plainText).toEqual('main') + expect(f.representativeName).toEqual('main') + expect(f.startPosition.row).toEqual(0) + expect(f.startPosition.column).toEqual(0) + expect(f.children.length).toEqual(12) + }) + + it('returns packages for imports', () => { + const f = outline.outlineTrees[0] + const packages = f.children.filter(o => o.kind === 'package') + expect(packages.length).toEqual(2) + + expect(packages[0].plainText).toEqual('"fmt"') + expect(packages[0].startPosition.row).toEqual(3) + expect(packages[0].startPosition.column).toEqual(1) + expect(packages[0].endPosition.row).toEqual(3) + expect(packages[0].endPosition.column).toEqual(6) + + expect(packages[1].plainText).toEqual('"io"') + expect(packages[1].startPosition.row).toEqual(4) + expect(packages[1].startPosition.column).toEqual(1) + expect(packages[1].endPosition.row).toEqual(4) + expect(packages[1].endPosition.column).toEqual(5) + }) + + it('identifies single-line constants', () => { + const f = outline.outlineTrees[0] + const consts = f.children.filter(o => o.plainText === 'Answer') + expect(consts.length).toEqual(1) + expect(consts[0].kind).toEqual('constant') + }) + + it('identifies interfaces', () => { + const f = outline.outlineTrees[0] + const ifaces = f.children.filter(o => o.kind === 'interface') + expect(ifaces.length).toEqual(1) + expect(ifaces[0].plainText).toEqual('Fooer') + expect(ifaces[0].startPosition.row).toEqual(19) + expect(ifaces[0].startPosition.column).toEqual(5) + expect(ifaces[0].endPosition.row).toEqual(21) + expect(ifaces[0].endPosition.column).toEqual(1) + }) + + it('identifies methods', () => { + const f = outline.outlineTrees[0] + const methods = f.children.filter(o => o.kind === 'method') + expect(methods.length).toEqual(1) + expect(methods[0].plainText).toEqual('(Number).ToInt') + }) + + it('identifies functions', () => { + const f = outline.outlineTrees[0] + const funcs = f.children.filter(o => o.kind === 'function') + expect(funcs.length).toEqual(1) + expect(funcs[0].plainText).toEqual('Hello') + }) + + it('identifies structs', () => { + const f = outline.outlineTrees[0] + const ss = f.children.filter(o => o.plainText === 'S') + expect(ss.length).toEqual(1) + const s = ss[0] + expect(s.kind).toEqual('class') + }) + + it('identifies type definitions', () => { + const f = outline.outlineTrees[0] + const nums = f.children.filter(o => o.plainText === 'Number') + expect(nums.length).toEqual(1) + + // TODO: there's no icon for type, so provide a custom icon here.. + expect(nums[0].kind).toEqual('type') // there's no icon for type + }) + + it('identifies variables', () => { + const f = outline.outlineTrees[0] + const rs = f.children.filter(o => o.plainText === 'r') + expect(rs.length).toEqual(1) + expect(rs[0].kind).toEqual('variable') + }) + + it('identifies constants/enums', () => { + // go-outline doesn't provide this for us + const f = outline.outlineTrees[0] + const items = f.children.filter(o => ['A', 'B', 'C'].includes(o.plainText)) + expect(items.length).toEqual(3) + + // TODO: expect kind to be constant or enum instead + items.forEach(i => expect(i.kind).toEqual('variable')) + }) + + it('handles multi-byte characters in the input file', () => { + // TODO ... + }) +}) diff --git a/spec/output-panel-spec.js b/spec/output-panel-spec.js new file mode 100644 index 00000000..d8b90cd9 --- /dev/null +++ b/spec/output-panel-spec.js @@ -0,0 +1,121 @@ +/** @babel */ +/* eslint-env jasmine */ + +import { OutputPanel } from './../lib/output-panel' + +describe('test panel', () => { + let outputPanel + + beforeEach(() => { + outputPanel = new OutputPanel() + }) + + describe('make link', () => { + it('renders text without links', () => { + const elements = outputPanel.makeLink('test') + expect(elements.length).toBe(1) + }) + + it('renders a link', () => { + const elements = outputPanel.makeLink('/Users/user/go/src/foo/bar.go:23') + expect(elements).toBeTruthy() + expect(elements.length).toBe(2) + expect(elements[0].tag).toBe('span') + expect(elements[1].tag).toBe('a') + expect(elements[0].children[0].text).toBe('') + expect(elements[1].children[0].text).toBe( + '/Users/user/go/src/foo/bar.go:23' + ) + }) + + it('renders a link to a go file (relative path)', () => { + const elements = outputPanel.makeLink('failure at foo/bar.go:23') + expect(elements.length).toBe(2) + expect(elements[0].tag).toBe('span') + expect(elements[1].tag).toBe('a') + expect(elements[1].children[0].text).toBe('foo/bar.go:23') + }) + + it('renders a link to a go file (absolute path)', () => { + const elements = outputPanel.makeLink( + 'failure at /home/user/go/src/foo/bar.go:23' + ) + expect(elements.length).toBe(2) + expect(elements[0].tag).toBe('span') + expect(elements[1].tag).toBe('a') + expect(elements[1].children[0].text).toBe( + '/home/user/go/src/foo/bar.go:23' + ) + }) + + it('renders a link to a test go file (relative path)', () => { + const elements = outputPanel.makeLink('failure at foo/bar_test.go:23') + expect(elements.length).toBe(2) + expect(elements[0].tag).toBe('span') + expect(elements[1].tag).toBe('a') + expect(elements[1].children[0].text).toBe('foo/bar_test.go:23') + }) + + it('renders a link to a test go file (absolute path)', () => { + const elements = outputPanel.makeLink( + 'failure at /home/user/go/src/foo/bar_test.go:23' + ) + expect(elements.length).toBe(2) + expect(elements[0].tag).toBe('span') + expect(elements[1].tag).toBe('a') + expect(elements[1].children[0].text).toBe( + '/home/user/go/src/foo/bar_test.go:23' + ) + }) + + it('renders links to Windows paths', () => { + const elements = outputPanel.makeLink( + 'failure at C:\\Users\\Me\\go\\src\\foo\\bar_test.go:23' + ) + expect(elements.length).toBe(2) + expect(elements[0].tag).toBe('span') + expect(elements[1].tag).toBe('a') + expect(elements[1].children[0].text).toBe( + 'C:\\Users\\Me\\go\\src\\foo\\bar_test.go:23' + ) + }) + + it('renders multiple links', () => { + const elements = outputPanel.makeLink( + 'failures at foo/bar.go:12 and baz/quux.go:34' + ) + expect(elements.length).toBe(4) + expect(elements[0].tag).toBe('span') + expect(elements[1].tag).toBe('a') + expect(elements[2].tag).toBe('span') + expect(elements[3].tag).toBe('a') + + expect(elements[1].children[0].text).toBe('foo/bar.go:12') + expect(elements[3].children[0].text).toBe('baz/quux.go:34') + }) + + it('renders a link with prefix and suffix', () => { + const elements = outputPanel.makeLink('prefix foo/bar.go:23 suffix') + expect(elements.length).toBe(3) + expect(elements[0].tag).toBe('span') + expect(elements[1].tag).toBe('a') + expect(elements[2].tag).toBe('span') + + expect(elements[0].children[0].text).toBe('prefix ') + expect(elements[1].children[0].text).toBe('foo/bar.go:23') + expect(elements[2].children[0].text).toBe(' suffix') + }) + + it('renders links in multi-line text', () => { + const elements = outputPanel.makeLink( + '--- FAIL: TestFail (0.00s)\n\tbar_test.go:23: Error!\nFAIL' + ) + expect(elements.length).toBe(3) + expect(elements[0].children[0].text).toBe( + '--- FAIL: TestFail (0.00s)\n\t' + ) + expect(elements[1].children[0].text).toBe('bar_test.go:23') + expect(elements[2].children[0].text).toBe(': Error!\nFAIL') + }) + }) +}) diff --git a/spec/panel-manager-spec.js b/spec/panel-manager-spec.js new file mode 100644 index 00000000..15af84ad --- /dev/null +++ b/spec/panel-manager-spec.js @@ -0,0 +1,45 @@ +/** @babel */ +/* eslint-env jasmine */ + +import { lifecycle } from './spec-helpers' +import { EmptyTabView } from './../lib/panel/empty-tab-view' +import { it, fit, ffit, beforeEach, runs } from './async-spec-helpers' // eslint-disable-line + +describe('panel manager', () => { + let pm = null + + beforeEach(async () => { + lifecycle.setup() + + await lifecycle.activatePackage() + + const { mainModule } = lifecycle + pm = mainModule.getPanelManager() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('registerViewProvider', () => { + let view + let model + let disp + + beforeEach(() => { + view = new EmptyTabView() + model = { key: 'foo', tab: { name: 'dummy' } } + disp = pm.registerViewProvider(view, model) + }) + + afterEach(() => { + disp.dispose() + }) + + it('records the view provider by key', () => { + const { view: v, model: m } = pm.viewProviders.get(model.key) + expect(v).toBe(view) + expect(m).toBe(model) + }) + }) +}) diff --git a/spec/references/references-provider-spec.js b/spec/references/references-provider-spec.js new file mode 100644 index 00000000..511a5f5c --- /dev/null +++ b/spec/references/references-provider-spec.js @@ -0,0 +1,63 @@ +'use babel' +/* eslint-env jasmine */ + +import { lifecycle } from './../spec-helpers' +import { ReferencesProvider } from './../../lib/references/references-provider' +import path from 'path' +import fs from 'fs' + +describe('References Provider', () => { + let references + + beforeEach(() => { + lifecycle.setup() + references = new ReferencesProvider() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('parseStream', () => { + it('is able to handle a json object stream correctly', () => { + // A JSON object stream is another name for malformed JSON + const file = fs.readFileSync( + path.join(__dirname, '..', 'fixtures', 'usage', 'referrers-1.json'), + 'utf8' + ) + const result = references.parseStream(file) + expect(result).toBeTruthy() + expect(result.length).toBe(2) + expect(result[0].objpos).toBe( + '/Users/joe/go/src/github.com/kelseyhightower/envconfig/envconfig.go:33:6' + ) + expect(result[0].desc).toBe( + 'type github.com/kelseyhightower/envconfig.Decoder interface{Decode(value string) error}' + ) + expect(result[1].package).toBe('github.com/kelseyhightower/envconfig') + expect(result[1].refs).toBeTruthy() + expect(result[1].refs.length).toBe(3) + }) + }) + + describe('referrers', () => { + it('parses output correctly', () => { + const file = fs.readFileSync( + path.join(__dirname, '..', 'fixtures', 'usage', 'referrers-1.json'), + 'utf8' + ) + const j = references.parseStream(file) + const result = references.parse(j) + expect(result).toBeTruthy() + expect(result.length).toEqual(3) + expect(result[0].uri).toEqual( + '/Users/joe/go/src/github.com/kelseyhightower/envconfig/envconfig.go' + ) + expect(result[0].range.start.row).toBe(306) + expect(result[0].range.start.column).toBe(42) + expect(result[0].name).toBe( + 'func decoderFrom(field reflect.Value) (d Decoder) {' + ) + }) + }) +}) diff --git a/spec/rename/gorename-spec.js b/spec/rename/gorename-spec.js new file mode 100644 index 00000000..d433dcb2 --- /dev/null +++ b/spec/rename/gorename-spec.js @@ -0,0 +1,77 @@ +'use babel' +/* eslint-env jasmine */ + +import path from 'path' +import fs from 'fs-extra' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('gorename', () => { + let gorename = null + let editor = null + let gopath = null + let source = null + let target = null + + beforeEach(async () => { + lifecycle.setup() + gopath = fs.realpathSync(lifecycle.temp.mkdirSync('gopath-')) + process.env.GOPATH = gopath + await lifecycle.activatePackage() + const { mainModule } = lifecycle + mainModule.provideGoConfig() + mainModule.provideGoGet() + gorename = mainModule.loadGorename() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('when a simple file is open', () => { + beforeEach(async () => { + source = path.join(__dirname, '..', 'fixtures', 'gorename') + target = path.join(gopath, 'src', 'basic') + fs.copySync(source, target) + editor = await atom.workspace.open(path.join(target, 'main.go')) + }) + + it('renames a single token', async () => { + editor.setCursorBufferPosition([4, 5]) + const info = gorename.wordAndOffset(editor) + expect(info.word).toBe('foo') + expect(info.offset).toBe(33) + + const file = editor.getBuffer().getPath() + const cwd = path.dirname(file) + + const cmd = await lifecycle.mainModule + .provideGoConfig() + .locator.findTool('gorename') + expect(cmd).toBeTruthy() + + const result = await gorename.runGorename( + file, + info.offset, + cwd, + 'bar', + cmd + ) + expect(result).toBeTruthy() + expect(result.success).toBe(true) + expect(result.result.stdout.trim()).toBe( + 'Renamed 2 occurrences in 1 file in 1 package.' + ) + + editor.destroy() + editor = await atom.workspace.open(path.join(target, 'main.go')) + + const expected = fs.readFileSync( + path.join(__dirname, '..', 'fixtures', 'gorename', 'expected'), + 'utf8' + ) + const actual = editor.getText() + expect(actual).toBe(expected) + }) + }) +}) diff --git a/spec/spec-helpers.js b/spec/spec-helpers.js index c7662b3b..b178cc62 100644 --- a/spec/spec-helpers.js +++ b/spec/spec-helpers.js @@ -4,13 +4,6 @@ import temp from '@atom/temp' -global.jasmineLog = msg => { - global.jasmine - .getEnv() - .currentSpec.fail(new Date().toISOString() + ' | ' + msg) -} -const log = global.jasmineLog - class Lifecycle { env: Object temp: temp @@ -40,56 +33,24 @@ class Lifecycle { atom.packages.triggerActivationHook('language-go:grammar-used') atom.packages.triggerActivationHook('core:loaded-shell-environment') - const transpiler = require('atom-babel6-transpiler') - const spy = spyOn(transpiler, 'transpile').andCallThrough() - - log('spec-helper - activatePackage') return Promise.all([ atom.packages.activatePackage('language-go').catch(e => { // eslint-disable-next-line no-console - log(e) + jasmine.getEnv().currentSpec.fail(e) throw e }), atom.packages.activatePackage('go-plus').then( pkg => { - log('spec-helper - loaded go-plus') - log('> mainModule? ' + !!pkg.mainModule) - log('> required=' + !!pkg.mainModuleRequired) - log('> path=' + pkg.mainModulePath) this.mainModule = pkg.mainModule - if (!pkg.mainModule) { - pkg.activateNow() - } - return pkg.activate().then( // eslint-disable-line - () => { - log('spec-helper - activated go-plus') - log('> mainModule? ' + !!pkg.mainModule) - log('> required=' + !!pkg.mainModuleRequired) - log('> transpile calls=' + spy.calls.length) - log('> isCompatible=' + pkg.isCompatible().toString()) - log('> build failures=' + (pkg.getBuildFailureOutput() || '')) - log( - '> incompatible native modules=' + - JSON.stringify(pkg.getIncompatibleNativeModules()) - ) - this.mainModule = pkg.mainModule - return pkg - }, - e => { - log(e) - throw e - } - ) + return pkg }, e => { - // eslint-disable-next-line no-console - log(e) + jasmine.getEnv().currentSpec.fail(e) throw e } ) ]).catch(e => { - // eslint-disable-next-line no-console - log(e) + jasmine.getEnv().currentSpec.fail(e) throw e }) } diff --git a/spec/tags/gomodifytags-spec.js b/spec/tags/gomodifytags-spec.js new file mode 100644 index 00000000..ffe4efc9 --- /dev/null +++ b/spec/tags/gomodifytags-spec.js @@ -0,0 +1,202 @@ +'use babel' +/* eslint-env jasmine */ + +import path from 'path' +import fs from 'fs-extra' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('gomodifytags', () => { + let gopath = null + let editor = null + let gomodifytags = null + let source = null + let target = null + + beforeEach(async () => { + lifecycle.setup() + gopath = fs.realpathSync(lifecycle.temp.mkdirSync('gopath-')) + process.env.GOPATH = gopath + await lifecycle.activatePackage() + const { mainModule } = lifecycle + mainModule.provideGoConfig() + gomodifytags = mainModule.loadGoModifyTags() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('when a file is open', () => { + let tempfile + beforeEach(async () => { + source = path.join(__dirname, '..', 'fixtures', 'gomodifytags') + target = path.join(gopath, 'src', 'gomodifytags') + fs.copySync(source, target) + tempfile = path.join(target, 'foo.go') + editor = await atom.workspace.open(tempfile) + }) + + describe('argument builder', () => { + let options + + beforeEach(() => { + options = { + tags: [{ tag: 'xml', option: null }, { tag: 'bson', option: null }], + transform: 'snakecase', + sortTags: false + } + }) + + it('includes the -file option', () => { + const args = gomodifytags.buildArgs(editor, options, 'Add') + expect(args.length).toBeGreaterThan(1) + expect(args[0]).toBe('-file') + expect(args[1]).toBe(tempfile) + }) + + it('defaults to json if no tags are specified', () => { + editor.setCursorBufferPosition([4, 6]) + options.tags = [] + const args = gomodifytags.buildArgs(editor, options, 'Add') + const i = args.indexOf('-add-tags') + expect(i).not.toBe(-1) + expect(args[i + 1]).toBe('json') + expect(args.includes('-add-options')).toBe(false) + }) + + it('specifies tags correctly', () => { + editor.setCursorBufferPosition([4, 6]) + const args = gomodifytags.buildArgs(editor, options, 'Add') + const i = args.indexOf('-add-tags') + expect(i).not.toBe(-1) + expect(args[i + 1]).toBe('xml,bson') + }) + + it('uses the -offset flag if there is no selection', () => { + editor.setCursorBufferPosition([4, 6]) + const args = gomodifytags.buildArgs(editor, options, 'Add') + expect(args.length).toBeGreaterThan(3) + expect(args[2]).toBe('-offset') + expect(args[3]).toBe('54') + }) + + it('uses the -line flag when there is a selection', () => { + editor.setSelectedBufferRange([[3, 2], [4, 6]]) + const args = gomodifytags.buildArgs(editor, options, 'Add') + expect(args.length).toBeGreaterThan(3) + expect(args[2]).toBe('-line') + expect(args[3]).toBe('4,5') + }) + + it('uses the -modified flag when the buffer is modified', () => { + editor.setCursorBufferPosition([4, 6]) + editor.insertNewlineBelow() + expect(editor.isModified()).toBe(true) + const args = gomodifytags.buildArgs(editor, options, 'Add') + expect(args.includes('-modified')).toBe(true) + }) + + it('uses the -transform flag when camel case is specified', () => { + options.transform = 'camelcase' + editor.setCursorBufferPosition([4, 6]) + const args = gomodifytags.buildArgs(editor, options, 'Add') + const i = args.indexOf('-transform') + expect(i).not.toBe(-1) + expect(args[i + 1]).toBe('camelcase') + }) + + it('uses the -sort flag when the sort option is enabled', () => { + options.sortTags = true + editor.setCursorBufferPosition([4, 6]) + const args = gomodifytags.buildArgs(editor, options, 'Add') + expect(args.includes('-sort')).toBe(true) + }) + + it('includes the -add-options flag if options were specified for add', () => { + editor.setCursorBufferPosition([4, 6]) + options.tags = [ + { tag: 'bson', option: 'omitempty' }, + { tag: 'xml', option: 'foo' } + ] + const args = gomodifytags.buildArgs(editor, options, 'Add') + let i = args.indexOf('-add-tags') + expect(i).not.toBe(-1) + expect(args[i + 1]).toBe('bson,xml') + + i = args.indexOf('-add-options') + expect(i).not.toBe(-1) + expect(args[i + 1]).toBe('bson=omitempty,xml=foo') + }) + + it('uses the -clear-tags flag if no tags are specified for remove', () => { + editor.setCursorBufferPosition([4, 6]) + options.tags = [] + const args = gomodifytags.buildArgs(editor, options, 'Remove') + expect(args.includes('-clear-tags')).toBe(true) + }) + + it('includes the -remove-tags flag if no options are specified for remove', () => { + editor.setCursorBufferPosition([4, 6]) + options.tags = [{ tag: 'json', option: null }] + const args = gomodifytags.buildArgs(editor, options, 'Remove') + expect(args.includes('-remove-options')).toBe(false) + const i = args.indexOf('-remove-tags') + expect(i).not.toBe(-1) + expect(args[i + 1]).toBe('json') + }) + + it('includes the -remove-options flag if options are specified for remove', () => { + editor.setCursorBufferPosition([4, 6]) + options.tags = [{ tag: 'json', option: 'omitempty' }] + const args = gomodifytags.buildArgs(editor, options, 'Remove') + expect(args.includes('-remove-tags')).toBe(false) + const i = args.indexOf('-remove-options') + expect(i).not.toBe(-1) + expect(args[i + 1]).toBe('json=omitempty') + }) + }) + + describe('when modifying tags', () => { + it('adds json tags with options', async () => { + editor.setCursorBufferPosition([4, 6]) + const command = await lifecycle.mainModule + .provideGoConfig() + .locator.findTool('gomodifytags') + expect(command).toBeTruthy() + + const result = await gomodifytags.modifyTags( + editor, + { + tags: [{ tag: 'json', option: 'omitempty' }], + transform: 'snakecase', + sortTags: false + }, + 'Add', + command + ) + + expect(result).toBeTruthy() + expect(result.success).toBe(true) + expect(result.result.stdout).toBe( + 'package foo\n\ntype Bar struct {\n\tQuickBrownFox int `json:"quick_brown_fox,omitempty"`\n\tLazyDog string `json:"lazy_dog,omitempty"`\n}\n\n' + ) + }) + + it('returns an error if the cursor is not inside a struct declaration', async () => { + editor.setCursorBufferPosition([0, 2]) + const command = await lifecycle.mainModule + .provideGoConfig() + .locator.findTool('gomodifytags') + const result = await gomodifytags.modifyTags( + editor, + { tags: [] }, + 'Add', + command + ) + expect(result).toBeTruthy() + expect(result.success).toBe(false) + }) + }) + }) +}) diff --git a/spec/test/gocover-parser-spec.js b/spec/test/gocover-parser-spec.js new file mode 100644 index 00000000..22c43801 --- /dev/null +++ b/spec/test/gocover-parser-spec.js @@ -0,0 +1,30 @@ +/* eslint-env jasmine */ + +import { ranges } from './../../lib/test/gocover-parser' +import path from 'path' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('gocover-parser', () => { + it('parses the file for a single package', async () => { + const testDir = path.join(__dirname, '..', 'fixtures', 'test') + const file = path.join(testDir, 'coverage.out') + const src = + '/Users/zmb3/github/go-plus/spec/fixtures/test/src/github.com/testuser/example/go-plus.go' + + const result = ranges(file) + expect(result.length).toBe(2) + expect(result[0].range.start.column).toBe(12) + expect(result[0].range.start.row).toBe(4) + expect(result[0].range.end.column).toBe(1) + expect(result[0].range.end.row).toBe(6) + expect(result[0].count).toBe(0) + expect(result[0].file).toBe(src) + + expect(result[1].range.start.column).toBe(20) + expect(result[1].range.start.row).toBe(8) + expect(result[1].range.end.column).toBe(1) + expect(result[1].range.end.row).toBe(10) + expect(result[1].count).toBe(1) + expect(result[1].file).toBe(src) + }) +}) diff --git a/spec/test/tester-spec.js b/spec/test/tester-spec.js new file mode 100644 index 00000000..862c0870 --- /dev/null +++ b/spec/test/tester-spec.js @@ -0,0 +1,223 @@ +'use babel' +/* eslint-env jasmine */ + +import path from 'path' +import { lifecycle } from './../spec-helpers' +import { it, fit, ffit, beforeEach, runs } from '../async-spec-helpers' // eslint-disable-line + +describe('tester', () => { + let tester = null + + beforeEach(async () => { + lifecycle.setup() + atom.config.set( + 'go-plus.test.coverageHighlightMode', + 'covered-and-uncovered' + ) + await lifecycle.activatePackage() + const { mainModule } = lifecycle + tester = mainModule.loadTester() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('go test args', () => { + beforeEach(() => { + atom.config.unset('go-plus.config.additionalTestArgs') + }) + + afterEach(() => { + atom.config.set('go-plus.test.runTestsWithShortFlag', true) + atom.config.set('go-plus.test.runTestsWithVerboseFlag', false) + }) + + it('uses the specified timeout', () => { + const args = tester.buildGoTestArgs(10000, false) + let foundTimeout = false + for (const arg of args) { + if (arg.startsWith('-timeout')) { + foundTimeout = true + expect(arg).toBe('-timeout=10000ms') + } + } + expect(foundTimeout).toBe(true) + }) + + it('invokes the go test command with a coverprofile', () => { + const args = tester.buildGoTestArgs(10000, true) + expect(args[0]).toBe('test') + expect(args[1].startsWith('-coverprofile=')).toBe(true) + }) + + describe('when specifying custom args', () => { + it('prefers timeout from the custom args (if specified)', () => { + atom.config.set('go-plus.config.additionalTestArgs', '-timeout=4000ms') + const args = tester.buildGoTestArgs(8000, false) + let foundTimeout = false + for (const arg of args) { + if (arg.startsWith('-timeout')) { + foundTimeout = true + expect(arg).toBe('-timeout=4000ms') + } + } + expect(foundTimeout).toBe(true) + }) + + it('does not duplicate the -short or -verbose flags', () => { + atom.config.set('go-plus.test.runTestsWithShortFlag', true) + atom.config.set('go-plus.test.runTestsWithVerboseFlag', true) + atom.config.set('go-plus.config.additionalTestArgs', '-short -verbose') + + const args = tester.buildGoTestArgs() + const shortFlags = args.filter(a => a === '-short').length + const verboseFlags = args.filter(a => a === '-v').length + + expect(shortFlags).toBe(1) + expect(verboseFlags).toBe(1) + }) + + it('handles args with spaces', () => { + atom.config.set( + 'go-plus.config.additionalTestArgs', + '-myarg="hello world" -arg2 3' + ) + const args = tester.buildGoTestArgs() + expect(args.length).toBeGreaterThan(3) + expect(args[1]).toEqual('-myarg=hello world') + expect(args[2]).toEqual('-arg2') + expect(args[3]).toEqual('3') + }) + }) + }) + + describe('with a gopath setup', () => { + let gopath = null + let filePath + let editor + + beforeEach(async () => { + gopath = path.join(__dirname, '..', 'fixtures', 'test') + process.env.GOPATH = gopath + atom.project.setPaths([gopath]) + + filePath = path.join( + gopath, + 'src', + 'github.com', + 'testuser', + 'example', + 'go-plus.go' + ) + + editor = await atom.workspace.open(filePath) + }) + + describe('when run tests on save is enabled, but compile on save is disabled', () => { + beforeEach(() => { + atom.config.set('go-plus.config.compileOnSave', false) + atom.config.set('go-plus.test.runTestsOnSave', true) + atom.config.set('go-plus.test.runTestsWithCoverage', false) + }) + + it('runs tests', async () => { + spyOn(tester, 'runTests').andCallThrough() + await tester.handleSaveEvent() + expect(tester.runTests).toHaveBeenCalled() + }) + + it('updates the busy signal', async () => { + const fake = (title, promiseFunc) => promiseFunc() + const reportBusySpy = jasmine + .createSpy('reportBusyWhile') + .andCallFake(fake) + spyOn(tester, 'busySignal').andReturn({ + reportBusyWhile: reportBusySpy + }) + await tester.handleSaveEvent(editor) + expect(reportBusySpy.calls.length).toEqual(1) + }) + }) + + describe('when run tests on save is disabled', () => { + beforeEach(() => { + atom.config.set('go-plus.test.runTestsOnSave', false) + }) + + it('does not run tests automatically on save', async () => { + spyOn(tester, 'runTests') + await tester.handleSaveEvent() + expect(tester.runTests).not.toHaveBeenCalled() + }) + + it('displays coverage for go source', async () => { + await tester.runTests(editor) + + const layers = tester.markedEditors.get(editor.id) + expect(layers).toBeTruthy() + let layerids = layers.split(',') + let coveredLayer = editor.getMarkerLayer(layerids[0]) + let uncoveredLayer = editor.getMarkerLayer(layerids[1]) + expect(coveredLayer).toBeTruthy() + expect(uncoveredLayer).toBeTruthy() + + let coveredmarkers = coveredLayer.getMarkers() + expect(coveredmarkers).toBeDefined() + expect(coveredmarkers.length).toBe(1) + expect(coveredmarkers[0]).toBeDefined() + let range = coveredmarkers[0].getBufferRange() + expect(range.start.row).toBe(8) + expect(range.start.column).toBe(20) + expect(range.end.row).toBe(10) + expect(range.end.column).toBe(1) + + let uncoveredmarkers = uncoveredLayer.getMarkers() + expect(uncoveredmarkers).toBeDefined() + expect(uncoveredmarkers.length).toBe(1) + expect(uncoveredmarkers[0]).toBeDefined() + range = uncoveredmarkers[0].getBufferRange() + expect(range).toBeDefined() + expect(range.start.row).toBe(4) + expect(range.start.column).toBe(12) + expect(range.end.row).toBe(6) + expect(range.end.column).toBe(1) + }) + + it('clears coverage for go source', async () => { + await tester.runTests(editor) + + let layerids = tester.markedEditors.get(editor.id).split(',') + let coveredLayer = editor.getMarkerLayer(layerids[0]) + let uncoveredLayer = editor.getMarkerLayer(layerids[1]) + expect(coveredLayer).toBeTruthy() + expect(uncoveredLayer).toBeTruthy() + + let coveredmarkers = coveredLayer.getMarkers() + expect(coveredmarkers).toBeDefined() + expect(coveredmarkers.length).toBe(1) + expect(coveredmarkers[0]).toBeDefined() + let range = coveredmarkers[0].getBufferRange() + expect(range.start.row).toBe(8) + expect(range.start.column).toBe(20) + expect(range.end.row).toBe(10) + expect(range.end.column).toBe(1) + + let uncoveredmarkers = uncoveredLayer.getMarkers() + expect(uncoveredmarkers).toBeDefined() + expect(uncoveredmarkers.length).toBe(1) + expect(uncoveredmarkers[0]).toBeDefined() + range = uncoveredmarkers[0].getBufferRange() + expect(range).toBeDefined() + expect(range.start.row).toBe(4) + expect(range.start.column).toBe(12) + expect(range.end.row).toBe(6) + expect(range.end.column).toBe(1) + + tester.clearMarkers(editor) + expect(coveredLayer.getMarkers().length).toBe(0) + expect(uncoveredLayer.getMarkers().length).toBe(0) + }) + }) + }) +}) diff --git a/spec/utils-spec.js b/spec/utils-spec.js new file mode 100644 index 00000000..70f20660 --- /dev/null +++ b/spec/utils-spec.js @@ -0,0 +1,58 @@ +'use babel' +/* eslint-env jasmine */ + +import { lifecycle } from './spec-helpers' +import { parseGoPosition, stat } from './../lib/utils' + +describe('utils', () => { + beforeEach(() => { + lifecycle.setup() + }) + + afterEach(() => { + lifecycle.teardown() + }) + + describe('parseGoPosition(identifier)', () => { + it('parses unix paths', () => { + const parsed = parseGoPosition( + '/private/temp/src/gopath-11726-3832-1xl0vhg.4128uayvi/src/what/doc.go:23:2' + ) + expect(parsed).toBeTruthy() + expect(parsed.file).toBe( + '/private/temp/src/gopath-11726-3832-1xl0vhg.4128uayvi/src/what/doc.go' + ) + expect(parsed.line).toBe(23) + expect(parsed.column).toBe(2) + }) + + it('parses windows paths', () => { + const parsed = parseGoPosition( + 'C:\\Users\\vagrant\\AppData\\Local\\Temp\\2\\gopath-11726-3832-1xl0vhg.4128uayvi\\src\\what\\doc.go:23:2' + ) + expect(parsed).toBeTruthy() + expect(parsed.file).toBe( + 'C:\\Users\\vagrant\\AppData\\Local\\Temp\\2\\gopath-11726-3832-1xl0vhg.4128uayvi\\src\\what\\doc.go' + ) + expect(parsed.line).toBe(23) + expect(parsed.column).toBe(2) + }) + }) + + describe('stat', () => { + it('is rejected for nonexistent files', () => { + let result, err + + waitsForPromise(() => { + return stat('nonexistentthing') + .then(r => (result = r)) + .catch(e => (err = e)) + }) + + runs(() => { + expect(result).toBeFalsy() + expect(err).toBeTruthy() + }) + }) + }) +})