diff --git a/.eslintignore b/.eslintignore new file mode 100644 index 0000000..18fd55a --- /dev/null +++ b/.eslintignore @@ -0,0 +1 @@ +decls diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..39b031d --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,8 @@ +{ + "extends": "steelbrain", + "rules": { + "no-param-reassign": "off", + "no-duplicate-imports": "off", + "import/prefer-default-export": "off" + } +} diff --git a/.flowconfig b/.flowconfig index c1450b9..6c667b9 100644 --- a/.flowconfig +++ b/.flowconfig @@ -1,21 +1,9 @@ [ignore] -node_modules/**/.* [include] [libs] -interface -node_modules/flow-atom-api/sig/v1.7.4 -node_modules/flow-atom-api/node_modules/flow-json/sig -node_modules/flow-atom-api/node_modules/flow-json-schema/sig -node_modules/flow-atom-api/node_modules/flow-atom-event-kit/sig/v1.5.0 -node_modules/flow-atom-api/node_modules/flow-atom-first-mate/sig/v5.1.1 -node_modules/flow-atom-api/node_modules/flow-atom-keymap/sig/v6.3.2 -node_modules/flow-atom-api/node_modules/flow-atom-node/sig -node_modules/flow-atom-api/node_modules/flow-atom-oniguruma/sig/v6.1.0 -node_modules/flow-atom-api/node_modules/flow-atom-pathwatcher/sig/v6.2.4 -node_modules/flow-atom-api/node_modules/flow-atom-text-buffer/sig/v8.5.0 -node_modules/flow-atom-api/node_modules/flow-electron-api/sig/v1.1.1 -node_modules/flow-atom-api/node_modules/iflow-jquery/index.js.flow +decls [options] +module.system=node diff --git a/.gitignore b/.gitignore index 3c3629e..fd4f2b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ node_modules +.DS_Store diff --git a/LICENSE b/LICENSE index c44cd17..6eddd75 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2016 Philipp von Radziewsky +Copyright (c) 2016 AtomDebugger Team (Philipp von Radziewsky & Steel Brain) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/circle.yml b/circle.yml new file mode 100644 index 0000000..18cdf84 --- /dev/null +++ b/circle.yml @@ -0,0 +1,20 @@ +dependencies: + override: + - curl -L https://atom.io/download/deb -o atom-amd64.deb + - sudo dpkg --install atom-amd64.deb || true + - sudo apt-get update + - sudo apt-get -f install + - node --version + - npm --version + - atom --version + - npm prune + - npm install + - apm rebuild + +test: + override: + - npm test + +machine: + node: + version: 6 diff --git a/decls/atom.js b/decls/atom.js new file mode 100644 index 0000000..92d2910 --- /dev/null +++ b/decls/atom.js @@ -0,0 +1,16 @@ +/* @flow */ + +declare var atom: Object; +declare module 'atom' { + declare var Point: any; + declare var Range: any; + declare var Panel: any; + declare var TextEditor: any; + declare var TextBuffer: any; + declare var BufferMarker: any; + declare var TextEditorGutter: any; + declare var TextEditorMarker: any; + declare var CompositeDisposable: any; + declare var Disposable: any; + declare var Emitter: any; +} diff --git a/decls/jasmine.js b/decls/jasmine.js new file mode 100644 index 0000000..7d563f1 --- /dev/null +++ b/decls/jasmine.js @@ -0,0 +1,9 @@ +/* @flow */ + +declare function it(name: string, callback: (() => void)): void; +declare function fit(name: string, callback: (() => void)): void; +declare function expect(value: any): Object; +declare function describe(name: string, callback: (() => void)): void; +declare function fdescribe(name: string, callback: (() => void)): void; +declare function beforeEach(callback: (() => void)): void; +declare function afterEach(callback: (() => void)): void; diff --git a/interface/atom.js b/interface/atom.js deleted file mode 100644 index d9d0dea..0000000 --- a/interface/atom.js +++ /dev/null @@ -1,12 +0,0 @@ -import { File } from 'sig/1.7.4/File' -import { CompositeDisposable, Disposable, Emitter } from 'event-kit' - -declare module atom { - - declare var exports: { - File: File, - CompositeDisposable: CompositeDisposable, - Disposable: Disposable, - Emitter: Emitter - }; -} diff --git a/lib/breakpoint-event.js b/lib-old/breakpoint-event.js similarity index 100% rename from lib/breakpoint-event.js rename to lib-old/breakpoint-event.js diff --git a/lib/breakpoint.js b/lib-old/breakpoint.js similarity index 100% rename from lib/breakpoint.js rename to lib-old/breakpoint.js diff --git a/lib/debugger-controller.js b/lib-old/debugger-controller.js similarity index 100% rename from lib/debugger-controller.js rename to lib-old/debugger-controller.js diff --git a/lib/debugger-proxy.js b/lib-old/debugger-proxy.js similarity index 100% rename from lib/debugger-proxy.js rename to lib-old/debugger-proxy.js diff --git a/lib/debugger-registry.js b/lib-old/debugger-registry.js similarity index 100% rename from lib/debugger-registry.js rename to lib-old/debugger-registry.js diff --git a/lib-old/index.js b/lib-old/index.js new file mode 100644 index 0000000..3545654 --- /dev/null +++ b/lib-old/index.js @@ -0,0 +1,44 @@ +'use babel' + +/* @flow */ + +import Breakpoint from './breakpoint' +import BreakpointEvent from './breakpoint-event' +import DebuggerController from './debugger-controller' +import SessionEvent from './session-event' +import TargetEvent from './target-event' +import type { Debugger, DebuggerView } from './types' +import { Disposable } from 'atom' + +module.exports = { + instance: DebuggerController, + + activate(): void { + this.instance = new DebuggerController() + require('atom-package-deps').install('debugger') // eslint-disable-line + }, + + provideEventDefs(): Object { + return { + 'BreakpointEvent': BreakpointEvent, + 'SessionEvent': SessionEvent, + 'TargetEvent': TargetEvent + } + }, + + consumeView(view: DebuggerView): Disposable { + this.instance.addView(view) + + return new Disposable(() => { + this.instance.deleteView(view) + }) + }, + + consumeDebugger(debug: Debugger): Disposable { + this.instance.addDebugger(debug) + + return new Disposable(() => { + this.instance.deleteDebugger(debug) + }) + } +} diff --git a/lib/project-config.js b/lib-old/project-config.js similarity index 100% rename from lib/project-config.js rename to lib-old/project-config.js diff --git a/lib/session-event.js b/lib-old/session-event.js similarity index 100% rename from lib/session-event.js rename to lib-old/session-event.js diff --git a/lib/stack-frame.js b/lib-old/stack-frame.js similarity index 100% rename from lib/stack-frame.js rename to lib-old/stack-frame.js diff --git a/lib/target-event.js b/lib-old/target-event.js similarity index 100% rename from lib/target-event.js rename to lib-old/target-event.js diff --git a/lib-old/types.js b/lib-old/types.js new file mode 100644 index 0000000..73096a8 --- /dev/null +++ b/lib-old/types.js @@ -0,0 +1,54 @@ +'use babel' + +/* @flow */ + +import DebuggerController from './debugger-controller' +import Breakpoint from './breakpoint' + +import type { StackFrame } from './stack-frame' +import type { Variable } from './variable' + +export type DebuggerTarget = { + filePath: string, + arguments: ?string +} + +export type DebuggerView = { + + activate(controller: DebuggerController): void, + + dispose(): void +} + +export type Debugger = { + + name(): string, + + onSessionEvent(callback: Function): void, + + onBreakpointEvent(callback: Function): void, + + onTargetEvent(callback: Function): void, + + start(target: DebuggerTarget): void, + + stop(): void, + + resume(): void, + + pause(): void, + + stepInto(): void, + + stepOver(): void, + + insertBreakpoint(breakpoint: Breakpoint): void, + + getCallStack(): Promise>, + + getSelectedFrame(): Promise, + + setSelectedFrame(level: number): void, + + getVariableList(): Promise> +} diff --git a/lib/variable.js b/lib-old/variable.js similarity index 100% rename from lib/variable.js rename to lib-old/variable.js diff --git a/lib/view-registry.js b/lib-old/view-registry.js similarity index 100% rename from lib/view-registry.js rename to lib-old/view-registry.js diff --git a/lib/commands.js b/lib/commands.js new file mode 100644 index 0000000..dbb3295 --- /dev/null +++ b/lib/commands.js @@ -0,0 +1,64 @@ +/* @flow */ + +import { CompositeDisposable, Emitter } from 'atom' +import type DebuggerDelegate from './delegate' + +export default class Commands { + active: ?DebuggerDelegate; + emitter: Emitter; + subscriptions: CompositeDisposable; + + constructor() { + this.active = null + this.emitter = new Emitter() + this.subscriptions = new CompositeDisposable() + + this.subscriptions.add(this.emitter) + } + activate() { + this.subscriptions.add(atom.commands.add('atom-text-editor:not([mini])', { + 'debugger:start': () => this.start(), + 'debugger:stop': () => this.stop(), + })) + } + start() { + if (this.active) { + return + } + const delegate = this.requestDelegate() + if (!delegate) { + return + } + this.active = delegate + this.active.start() + } + stop() { + if (this.active) { + this.active.stop() + this.emitter.emit('did-stop', this.active) + this.active = null + } + } + requestDelegate(): ?DebuggerDelegate { + const event = { delegate: null } + this.emitter.emit('request-delegate', event) + return event.delegate + } + onShouldProvideDelegate(callback: (() => ?DebuggerDelegate)): void { + this.emitter.on('request-delegate', function(event) { + event.delegate = event.delegate || callback() + }) + } + onDidStart(callback: ((delegate: DebuggerDelegate) => any)): void { + this.emitter.on('did-start', callback) + } + onDidStop(callback: ((delegate: DebuggerDelegate) => any)): void { + this.emitter.on('did-stop', callback) + } + dispose() { + if (this.active) { + this.stop() + } + this.subscriptions.dispose() + } +} diff --git a/lib/delegate-registry.js b/lib/delegate-registry.js new file mode 100644 index 0000000..14b4cba --- /dev/null +++ b/lib/delegate-registry.js @@ -0,0 +1,34 @@ +/* @flow */ + +import { CompositeDisposable } from 'atom' + +import DebuggerDelegate from './delegate' +import validateDebugger from './validate/debugger' +import type { Debugger } from './types' + +export default class DelegateRegistry { + delegates: Set; + subscriptions: CompositeDisposable; + + constructor() { + this.delegates = new Set() + this.subscriptions = new CompositeDisposable() + } + register(debugProvider: Debugger): DebuggerDelegate { + if (validateDebugger(debugProvider)) { + throw new Error('Invalid debugger provided') + } + const delegate = new DebuggerDelegate(debugProvider) + this.delegates.add(delegate) + delegate.onDidDestroy(() => { + this.delegates.delete(delegate) + }) + return delegate + } + dispose() { + for (const delegate of this.delegates) { + delegate.dispose() + } + this.subscriptions.dispose() + } +} diff --git a/lib/delegate.js b/lib/delegate.js new file mode 100644 index 0000000..0c375cc --- /dev/null +++ b/lib/delegate.js @@ -0,0 +1,39 @@ +/* @flow */ + +import { Emitter, CompositeDisposable } from 'atom' +import type { Debugger } from './types' + +export default class DebuggerDelegate { + emitter: Emitter; + provider: Debugger; + subscriptions: CompositeDisposable; + + constructor(provider: Debugger) { + this.provider = provider + this.emitter = new Emitter() + this.subscriptions = new CompositeDisposable() + + this.subscriptions.add(this.emitter) + } + get(): Debugger { + return this.provider + } + // Real stuff -- starts + start() { + console.log('start debugger') + } + stop() { + console.log('stop debugger') + } + // Real stuff -- ends + onDidDestroy(callback: (() => any)) { + return this.emitter.on('did-destroy', callback) + } + dispose() { + if (this.subscriptions.isDisposed()) { + return + } + this.emitter.emit('did-destroy') + this.subscriptions.dispose() + } +} diff --git a/lib/editor.js b/lib/editor.js new file mode 100644 index 0000000..8765926 --- /dev/null +++ b/lib/editor.js @@ -0,0 +1,26 @@ +/* @flow */ + +import { CompositeDisposable, Disposable } from 'atom' +import type { TextEditor } from 'atom' + +export default class Editor { + textEditor: TextEditor; + subscriptions: CompositeDisposable; + + constructor(textEditor: TextEditor) { + this.textEditor = textEditor + this.subscriptions = new CompositeDisposable() + } + onDidDestroy(callback: (() => any)): Disposable { + const subscription = this.textEditor.onDidDestroy(callback) + const disposable = new Disposable(() => { + subscription.dispose() + this.subscriptions.remove(disposable) + }) + this.subscriptions.add(disposable) + return disposable + } + dispose() { + this.subscriptions.dispose() + } +} diff --git a/lib/editors.js b/lib/editors.js new file mode 100644 index 0000000..39fa030 --- /dev/null +++ b/lib/editors.js @@ -0,0 +1,43 @@ +/* @flow */ + +import { CompositeDisposable, Emitter } from 'atom' +import type { Disposable, TextEditor } from 'atom' + +import Editor from './editor' + +export default class Editors { + emitter: Emitter; + editors: Map; + subscriptions: CompositeDisposable; + + constructor() { + this.emitter = new Emitter() + this.editors = new Map() + this.subscriptions = new CompositeDisposable() + + this.subscriptions.add(this.emitter) + } + activate() { + this.subscriptions.add(atom.workspace.observeTextEditors(textEditor => { + const editor = new Editor(textEditor) + this.editors.set(textEditor, editor) + this.emitter.emit('observe', editor) + editor.onDidDestroy(() => { + this.editors.delete(textEditor) + }) + })) + } + get(textEditor: TextEditor): ?Editor { + return this.editors.get(textEditor) + } + observe(callback: ((editor: Editor) => any)): Disposable { + this.editors.forEach(callback) + return this.emitter.on('observe', callback) + } + dispose() { + for (const editor of this.editors.values()) { + editor.dispose() + } + this.subscriptions.dispose() + } +} diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..a36a732 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,31 @@ +/* @flow */ + +import { Disposable } from 'atom' +import AtomDebugger from './main' +import type { UI } from './types' + +export default { + instance: null, + activate() { + require('atom-package-deps').install('debugger') // eslint-disable-line + this.instance = new AtomDebugger() + this.instance.activate() + }, + provideDebuggerRegistry() { + return this.instance.delegateRegistry + }, + consumeUI(ui: UI | Array): Disposable { + const uis = [].concat(ui) + uis.forEach(entry => { + this.instance.uiRegistry.add(entry) + }) + return new Disposable(() => { + uis.forEach(entry => { + this.instance.uiRegistry.delete(entry) + }) + }) + }, + deactivate() { + this.instance.dispose() + }, +} diff --git a/lib/main.js b/lib/main.js index 9311078..ccbe52b 100644 --- a/lib/main.js +++ b/lib/main.js @@ -1,43 +1,50 @@ -'use babel' - /* @flow */ -import Breakpoint from './breakpoint' -import BreakpointEvent from './breakpoint-event' -import DebuggerController from './debugger-controller' -import SessionEvent from './session-event' -import TargetEvent from './target-event' -import type { Debugger, DebuggerView } from './types' -import { Disposable } from 'atom' - -module.exports = { - instance: DebuggerController, - - activate(): void { - this.instance = new DebuggerController() - }, - - provideEventDefs(): Object { - return { - 'BreakpointEvent': BreakpointEvent, - 'SessionEvent': SessionEvent, - 'TargetEvent': TargetEvent - } - }, - - consumeView(view: DebuggerView): Disposable { - this.instance.addView(view) - - return new Disposable(() => { - this.instance.deleteView(view) +import { CompositeDisposable } from 'atom' + +import Editors from './editors' +import Commands from './commands' +import UIRegistry from './ui-registry' +import DelegateRegistry from './delegate-registry' + +export default class AtomDebugger { + editors: Editors; + commands: Commands; + uiRegistry: UIRegistry; + subscriptions: CompositeDisposable; + delegateRegistry: DelegateRegistry; + + constructor() { + this.editors = new Editors() + this.commands = new Commands() + this.uiRegistry = new UIRegistry() + this.subscriptions = new CompositeDisposable() + this.delegateRegistry = new DelegateRegistry() + + this.subscriptions.add(this.editors) + this.subscriptions.add(this.commands) + this.subscriptions.add(this.uiRegistry) + this.subscriptions.add(this.delegateRegistry) + + this.commands.onShouldProvideDelegate(() => { + // TODO: Have a good way to choose between the delegate + for (const delegate of this.delegateRegistry.delegates) { + return delegate + } + return null }) - }, - - consumeDebugger(debug: Debugger): Disposable { - this.instance.addDebugger(debug) - - return new Disposable(() => { - this.instance.deleteDebugger(debug) + this.commands.onDidStart(delegate => { + this.uiRegistry.didStart(delegate) + }) + this.commands.onDidStop(delegate => { + this.uiRegistry.didStop(delegate) }) } + activate() { + this.editors.activate() + this.commands.activate() + } + dispose() { + this.subscriptions.dispose() + } } diff --git a/lib/types.js b/lib/types.js index 73096a8..1662357 100644 --- a/lib/types.js +++ b/lib/types.js @@ -1,54 +1,17 @@ -'use babel' - /* @flow */ -import DebuggerController from './debugger-controller' -import Breakpoint from './breakpoint' - -import type { StackFrame } from './stack-frame' -import type { Variable } from './variable' - -export type DebuggerTarget = { - filePath: string, - arguments: ?string -} - -export type DebuggerView = { - - activate(controller: DebuggerController): void, - - dispose(): void -} +import type Delegate from './delegate' export type Debugger = { - - name(): string, - - onSessionEvent(callback: Function): void, - - onBreakpointEvent(callback: Function): void, - - onTargetEvent(callback: Function): void, - - start(target: DebuggerTarget): void, - + name: string, + grammarScopes: Array, + start(): void, stop(): void, +} - resume(): void, - - pause(): void, - - stepInto(): void, - - stepOver(): void, - - insertBreakpoint(breakpoint: Breakpoint): void, - - getCallStack(): Promise>, - - getSelectedFrame(): Promise, - - setSelectedFrame(level: number): void, - - getVariableList(): Promise> +export type UI = { + activate(): void, + didStart(delegate: Delegate): void, + didStop(delegate: Delegate): void, + dispose(): void, } diff --git a/lib/ui-registry.js b/lib/ui-registry.js new file mode 100644 index 0000000..c352e0e --- /dev/null +++ b/lib/ui-registry.js @@ -0,0 +1,39 @@ +/* @flow */ + +import type { UI } from './types' +import type Delegate from './delegate' + +export default class UIRegistry { + interfaces: Set; + + constructor() { + this.interfaces = new Set() + } + add(ui: UI): void { + if (!this.interfaces.has(ui)) { + this.interfaces.add(ui) + ui.activate() + } + } + delete(ui: UI): void { + if (this.interfaces.has(ui)) { + this.interfaces.delete(ui) + ui.dispose() + } + } + didStart(delegate: Delegate): void { + for (const entry of this.interfaces) { + entry.didStart(delegate) + } + } + didStop(delegate: Delegate): void { + for (const entry of this.interfaces) { + entry.didStop(delegate) + } + } + dispose() { + for (const entry of this.interfaces) { + entry.dispose() + } + } +} diff --git a/lib/validate/debugger.js b/lib/validate/debugger.js new file mode 100644 index 0000000..c5c8c75 --- /dev/null +++ b/lib/validate/debugger.js @@ -0,0 +1,5 @@ +/* @flow */ + +export default function validateDebugger() { + +} diff --git a/package.json b/package.json index 32704dd..da2ceea 100644 --- a/package.json +++ b/package.json @@ -1,39 +1,42 @@ { "name": "debugger", - "main": "./lib/main", - "author": "philippvr", + "main": "./lib/index", + "author": "AtomDebugger", "version": "0.0.0", "license": "MIT", + "private": true, "engines": { - "atom": ">=1.0.0 <2.0.0" + "atom": ">=1.9.0 <2.0.0" }, + "scripts": { + "test": "(apm test) && (flow check) && (eslint . )" + }, + "keywords": [ + "debugger" + ], + "description": "Base debugger with Cow Powers", "dependencies": { - "atom-package-deps": "^4.0.0", - "season": "^5.2.0", - "underscore-plus": "^1.0.0" + "atom-package-deps": "^4.0.0" }, "devDependencies": { - "flow-atom-api": "https://github.com/flow-atom/flow-atom-api.git" + "eslint-config-steelbrain": "^1.0.4", + "flow-bin": "^0.31.1", + "jasmine-fix": "^1.0.1" }, "package-deps": [ "debugger-ui-default" ], "providedServices": { - "debugger-event-defs": { + "debugger": { "versions": { - "0.1.0": "provideEventDefs" + "1.0.0": "provideDebuggerRegistry" } } }, "consumedServices": { "debugger-ui": { "versions": { - "^0.1.0": "consumeView" - } - }, - "debugger": { - "versions": { - "^0.1.0": "consumeDebugger" + "^1.0.0": "consumeUI" } } }