session.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { IClientSession } from '@jupyterlab/apputils';
  4. import { CodeEditor } from '@jupyterlab/codeeditor';
  5. import { KernelMessage } from '@jupyterlab/services';
  6. import { PromiseDelegate } from '@phosphor/coreutils';
  7. import { ISignal, Signal } from '@phosphor/signaling';
  8. import { IDebugger } from './tokens';
  9. export class DebugSession implements IDebugger.ISession {
  10. /**
  11. * Instantiate a new debug session
  12. *
  13. * @param options - The debug session instantiation options.
  14. */
  15. constructor(options: DebugSession.IOptions) {
  16. this.client = options.client;
  17. this.client.iopubMessage.connect(this._handleEvent, this);
  18. }
  19. /**
  20. * Dispose the debug session.
  21. */
  22. dispose(): void {
  23. if (this.isDisposed) {
  24. return;
  25. }
  26. this.client.iopubMessage.disconnect(this._handleEvent, this);
  27. this._isDisposed = true;
  28. this._disposed.emit();
  29. }
  30. /**
  31. * A signal emitted when the debug session is disposed.
  32. */
  33. get disposed(): ISignal<this, void> {
  34. return this._disposed;
  35. }
  36. /**
  37. * Whether the debug session is disposed.
  38. */
  39. get isDisposed(): boolean {
  40. return this._isDisposed;
  41. }
  42. /**
  43. * Start a new debug session
  44. */
  45. public async start(): Promise<void> {
  46. await this.sendRequest('initialize', {
  47. clientID: 'jupyterlab',
  48. clientName: 'JupyterLab',
  49. adapterID: 'python',
  50. pathFormat: 'path',
  51. linesStartAt1: true,
  52. columnsStartAt1: true,
  53. supportsVariableType: true,
  54. supportsVariablePaging: true,
  55. supportsRunInTerminalRequest: true,
  56. locale: 'en-us'
  57. });
  58. await this.sendRequest('attach', {});
  59. }
  60. /**
  61. * Stop the running debug session.
  62. */
  63. public async stop(): Promise<void> {
  64. await this.sendRequest('disconnect', {
  65. restart: false,
  66. terminateDebuggee: true
  67. });
  68. }
  69. /**
  70. * Send a custom debug request to the kernel.
  71. * @param command debug command.
  72. * @param args arguments for the debug command.
  73. */
  74. async sendRequest<K extends keyof IDebugger.ISession.IRequest>(
  75. command: K,
  76. args: IDebugger.ISession.IRequest[K]
  77. ): Promise<IDebugger.ISession.IResponse[K]> {
  78. const message = await this._sendDebugMessage({
  79. type: 'request',
  80. seq: this._seq++,
  81. command,
  82. arguments: args
  83. });
  84. return message.content as IDebugger.ISession.IResponse[K];
  85. }
  86. /**
  87. * Signal emitted for debug event messages.
  88. */
  89. get eventMessage(): ISignal<DebugSession, IDebugger.ISession.IEvent> {
  90. return this._eventMessage;
  91. }
  92. /**
  93. * Handle debug events sent on the 'iopub' channel.
  94. */
  95. private _handleEvent(
  96. sender: IClientSession,
  97. message: KernelMessage.IIOPubMessage
  98. ): void {
  99. const msgType = message.header.msg_type;
  100. if (msgType !== 'debug_event') {
  101. return;
  102. }
  103. const event = message.content as IDebugger.ISession.IEvent;
  104. this._eventMessage.emit(event);
  105. }
  106. /**
  107. * Send a debug request message to the kernel.
  108. * @param msg debug request message to send to the kernel.
  109. */
  110. private async _sendDebugMessage(
  111. msg: KernelMessage.IDebugRequestMsg['content']
  112. ): Promise<KernelMessage.IDebugReplyMsg> {
  113. const reply = new PromiseDelegate<KernelMessage.IDebugReplyMsg>();
  114. const kernel = this.client.kernel;
  115. const future = kernel.requestDebug(msg);
  116. future.onReply = (msg: KernelMessage.IDebugReplyMsg) => {
  117. return reply.resolve(msg);
  118. };
  119. await future.done;
  120. return reply.promise;
  121. }
  122. client: IClientSession;
  123. editors: CodeEditor.IEditor[];
  124. private _disposed = new Signal<this, void>(this);
  125. private _isDisposed: boolean = false;
  126. private _eventMessage = new Signal<DebugSession, IDebugger.ISession.IEvent>(
  127. this
  128. );
  129. /**
  130. * Debug protocol sequence number.
  131. */
  132. private _seq: number = 0;
  133. }
  134. /**
  135. * A namespace for `DebugSession` statics.
  136. */
  137. export namespace DebugSession {
  138. export interface IOptions {
  139. /**
  140. * The client session used by the debug session.
  141. */
  142. client: IClientSession;
  143. }
  144. }