contextconnector.ts 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import { CodeEditor } from '@jupyterlab/codeeditor';
  4. import { DataConnector } from '@jupyterlab/statedb';
  5. import { CompletionHandler } from './handler';
  6. /**
  7. * A context connector for completion handlers.
  8. */
  9. export class ContextConnector extends DataConnector<
  10. CompletionHandler.IReply,
  11. void,
  12. CompletionHandler.IRequest
  13. > {
  14. /**
  15. * Create a new context connector for completion requests.
  16. *
  17. * @param options - The instatiation options for the context connector.
  18. */
  19. constructor(options: ContextConnector.IOptions) {
  20. super();
  21. this._editor = options.editor;
  22. }
  23. /**
  24. * Fetch completion requests.
  25. *
  26. * @param request - The completion request text and details.
  27. */
  28. fetch(
  29. request: CompletionHandler.IRequest
  30. ): Promise<CompletionHandler.IReply> {
  31. if (!this._editor) {
  32. return Promise.reject('No editor');
  33. }
  34. return new Promise<CompletionHandler.IReply>(resolve => {
  35. resolve(Private.contextHint(this._editor!));
  36. });
  37. }
  38. private _editor: CodeEditor.IEditor | null;
  39. }
  40. /**
  41. * A namespace for context connector statics.
  42. */
  43. export namespace ContextConnector {
  44. /**
  45. * The instantiation options for cell completion handlers.
  46. */
  47. export interface IOptions {
  48. /**
  49. * The session used by the context connector.
  50. */
  51. editor: CodeEditor.IEditor | null;
  52. }
  53. }
  54. /**
  55. * A namespace for Private functionality.
  56. */
  57. namespace Private {
  58. /**
  59. * Get a list of completion hints from a tokenization
  60. * of the editor.
  61. */
  62. export function contextHint(
  63. editor: CodeEditor.IEditor
  64. ): CompletionHandler.IReply {
  65. // Find the token at the cursor
  66. const cursor = editor.getCursorPosition();
  67. const token = editor.getTokenForPosition(cursor);
  68. // Get the list of matching tokens.
  69. const tokenList = getCompletionTokens(token, editor);
  70. // Only choose the ones that have a non-empty type
  71. // field, which are likely to be of interest.
  72. const completionList = tokenList.filter(t => t.type).map(t => t.value);
  73. // Remove duplicate completsions from the list
  74. const matches = Array.from(new Set<string>(completionList));
  75. return {
  76. start: token.offset,
  77. end: token.offset + token.value.length,
  78. matches,
  79. metadata: {}
  80. };
  81. }
  82. /**
  83. * Get a list of tokens that match the completion request,
  84. * but are not identical to the completion request.
  85. */
  86. function getCompletionTokens(
  87. token: CodeEditor.IToken,
  88. editor: CodeEditor.IEditor
  89. ): CodeEditor.IToken[] {
  90. const candidates = editor.getTokens();
  91. // Only get the tokens that have a common start, but
  92. // are not identical.
  93. return candidates.filter(
  94. t => t.value.indexOf(token.value) === 0 && t.value !== token.value
  95. );
  96. }
  97. }