session.ts 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  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. * The client session to connect to a debugger.
  21. */
  22. client: IClientSession;
  23. /**
  24. * The code editors in a debugger session.
  25. */
  26. editors: CodeEditor.IEditor[];
  27. /**
  28. * Dispose the debug session.
  29. */
  30. dispose(): void {
  31. if (this.isDisposed) {
  32. return;
  33. }
  34. this._isDisposed = true;
  35. this._disposed.emit();
  36. Signal.clearData(this);
  37. }
  38. /**
  39. * A signal emitted when the debug session is disposed.
  40. */
  41. get disposed(): ISignal<this, void> {
  42. return this._disposed;
  43. }
  44. /**
  45. * Whether the debug session is disposed.
  46. */
  47. get isDisposed(): boolean {
  48. return this._isDisposed;
  49. }
  50. /**
  51. * Start a new debug session
  52. */
  53. async start(): Promise<void> {
  54. await this.sendRequest('initialize', {
  55. clientID: 'jupyterlab',
  56. clientName: 'JupyterLab',
  57. adapterID: 'python',
  58. pathFormat: 'path',
  59. linesStartAt1: true,
  60. columnsStartAt1: true,
  61. supportsVariableType: true,
  62. supportsVariablePaging: true,
  63. supportsRunInTerminalRequest: true,
  64. locale: 'en-us'
  65. });
  66. await this.sendRequest('attach', {});
  67. }
  68. /**
  69. * Stop the running debug session.
  70. */
  71. async stop(): Promise<void> {
  72. await this.sendRequest('disconnect', {
  73. restart: false,
  74. terminateDebuggee: true
  75. });
  76. }
  77. /**
  78. * Send a custom debug request to the kernel.
  79. * @param command debug command.
  80. * @param args arguments for the debug command.
  81. */
  82. async sendRequest<K extends keyof IDebugger.ISession.Request>(
  83. command: K,
  84. args: IDebugger.ISession.Request[K]
  85. ): Promise<IDebugger.ISession.Response[K]> {
  86. const message = await this._sendDebugMessage({
  87. type: 'request',
  88. seq: this._seq++,
  89. command,
  90. arguments: args
  91. });
  92. return message.content as IDebugger.ISession.Response[K];
  93. }
  94. /**
  95. * Signal emitted for debug event messages.
  96. */
  97. get eventMessage(): ISignal<DebugSession, IDebugger.ISession.Event> {
  98. return this._eventMessage;
  99. }
  100. /**
  101. * Handle debug events sent on the 'iopub' channel.
  102. */
  103. private _handleEvent(
  104. sender: IClientSession,
  105. message: KernelMessage.IIOPubMessage
  106. ): void {
  107. const msgType = message.header.msg_type;
  108. if (msgType !== 'debug_event') {
  109. return;
  110. }
  111. const event = message.content as IDebugger.ISession.Event;
  112. this._eventMessage.emit(event);
  113. }
  114. /**
  115. * Send a debug request message to the kernel.
  116. * @param msg debug request message to send to the kernel.
  117. */
  118. private async _sendDebugMessage(
  119. msg: KernelMessage.IDebugRequestMsg['content']
  120. ): Promise<KernelMessage.IDebugReplyMsg> {
  121. const reply = new PromiseDelegate<KernelMessage.IDebugReplyMsg>();
  122. const kernel = this.client.kernel;
  123. const future = kernel.requestDebug(msg);
  124. future.onReply = (msg: KernelMessage.IDebugReplyMsg) => {
  125. return reply.resolve(msg);
  126. };
  127. await future.done;
  128. return reply.promise;
  129. }
  130. private _disposed = new Signal<this, void>(this);
  131. private _isDisposed: boolean = false;
  132. private _eventMessage = new Signal<DebugSession, IDebugger.ISession.Event>(
  133. this
  134. );
  135. private _seq: number = 0;
  136. }
  137. /**
  138. * A namespace for `DebugSession` statics.
  139. */
  140. export namespace DebugSession {
  141. export interface IOptions {
  142. /**
  143. * The client session used by the debug session.
  144. */
  145. client: IClientSession;
  146. }
  147. }