handler.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. IKernel, KernelMessage
  5. } from '@jupyterlab/services';
  6. import {
  7. IDisposable
  8. } from 'phosphor/lib/core/disposable';
  9. import {
  10. ICellEditorWidget, ITextChange, ICompletionRequest
  11. } from '../notebook/cells/editor';
  12. import {
  13. BaseCellWidget
  14. } from '../notebook/cells/widget';
  15. import {
  16. CompleterWidget
  17. } from './widget';
  18. /**
  19. * A completer handler for cell widgets.
  20. */
  21. export
  22. class CellCompleterHandler implements IDisposable {
  23. /**
  24. * Construct a new completer handler for a widget.
  25. */
  26. constructor(completer: CompleterWidget) {
  27. this._completer = completer;
  28. this._completer.selected.connect(this.onCompletionSelected, this);
  29. this._completer.visibilityChanged.connect(this.onVisibilityChanged, this);
  30. }
  31. /**
  32. * The kernel used by the completer handler.
  33. */
  34. get kernel(): IKernel {
  35. return this._kernel;
  36. }
  37. set kernel(value: IKernel) {
  38. this._kernel = value;
  39. }
  40. /**
  41. * The cell widget used by the completer handler.
  42. */
  43. get activeCell(): BaseCellWidget {
  44. return this._activeCell;
  45. }
  46. set activeCell(newValue: BaseCellWidget) {
  47. if (newValue === this._activeCell) {
  48. return;
  49. }
  50. if (this._activeCell && !this._activeCell.isDisposed) {
  51. const editor = this._activeCell.editor;
  52. editor.textChanged.disconnect(this.onTextChanged, this);
  53. editor.completionRequested.disconnect(this.onCompletionRequested, this);
  54. }
  55. this._activeCell = newValue;
  56. if (this._activeCell) {
  57. const editor = this._activeCell.editor as ICellEditorWidget;
  58. editor.textChanged.connect(this.onTextChanged, this);
  59. editor.completionRequested.connect(this.onCompletionRequested, this);
  60. }
  61. }
  62. /**
  63. * Get whether the completer handler is disposed.
  64. *
  65. * #### Notes
  66. * This is a read-only property.
  67. */
  68. get isDisposed(): boolean {
  69. return this._completer === null;
  70. }
  71. /**
  72. * Dispose of the resources used by the handler.
  73. */
  74. dispose(): void {
  75. if (this.isDisposed) {
  76. return;
  77. }
  78. this._completer = null;
  79. this._kernel = null;
  80. this._activeCell = null;
  81. }
  82. /**
  83. * Make a complete request using the kernel.
  84. */
  85. protected makeRequest(request: ICompletionRequest): Promise<void> {
  86. if (!this._kernel) {
  87. return Promise.reject(new Error('no kernel for completion request'));
  88. }
  89. let content: KernelMessage.ICompleteRequest = {
  90. code: request.currentValue,
  91. cursor_pos: request.position
  92. };
  93. let pending = ++this._pending;
  94. return this._kernel.complete(content).then(msg => {
  95. this.onReply(pending, request, msg);
  96. });
  97. }
  98. /**
  99. * Receive a completion reply from the kernel.
  100. */
  101. protected onReply(pending: number, request: ICompletionRequest, msg: KernelMessage.ICompleteReplyMsg): void {
  102. // If we have been disposed, bail.
  103. if (this.isDisposed) {
  104. return;
  105. }
  106. // If a newer completion request has created a pending request, bail.
  107. if (pending !== this._pending) {
  108. return;
  109. }
  110. let value = msg.content;
  111. let model = this._completer.model;
  112. if (!model) {
  113. return;
  114. }
  115. // Completion request failures or negative results fail silently.
  116. if (value.status !== 'ok') {
  117. model.reset();
  118. return;
  119. }
  120. // Update the original request.
  121. model.original = request;
  122. // Update the options.
  123. model.options = value.matches;
  124. // Update the cursor.
  125. model.cursor = { start: value.cursor_start, end: value.cursor_end };
  126. }
  127. /**
  128. * Handle a text changed signal from an editor.
  129. */
  130. protected onTextChanged(editor: ICellEditorWidget, change: ITextChange): void {
  131. if (!this._completer.model) {
  132. return;
  133. }
  134. this._completer.model.handleTextChange(change);
  135. }
  136. /**
  137. * Handle a completion requested signal from an editor.
  138. */
  139. protected onCompletionRequested(editor: ICellEditorWidget, request: ICompletionRequest): void {
  140. if (!this._kernel || !this._completer.model) {
  141. return;
  142. }
  143. this.makeRequest(request);
  144. }
  145. /**
  146. * Handle a visiblity change signal from a completer widget.
  147. */
  148. protected onVisibilityChanged(completer: CompleterWidget): void {
  149. if (completer.isDisposed || completer.isHidden) {
  150. if (this._activeCell) {
  151. this._activeCell.activate();
  152. }
  153. }
  154. }
  155. /**
  156. * Handle a completion selected signal from the completion widget.
  157. */
  158. protected onCompletionSelected(widget: CompleterWidget, value: string): void {
  159. if (!this._activeCell || !this._completer.model) {
  160. return;
  161. }
  162. let patch = this._completer.model.createPatch(value);
  163. if (!patch) {
  164. return;
  165. }
  166. let cell = this._activeCell;
  167. cell.model.source = patch.text;
  168. cell.editor.setCursorPosition(patch.position);
  169. }
  170. private _activeCell: BaseCellWidget = null;
  171. private _completer: CompleterWidget = null;
  172. private _kernel: IKernel = null;
  173. private _pending = 0;
  174. }