inspector.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. /*-----------------------------------------------------------------------------
  2. | Copyright (c) Jupyter Development Team.
  3. | Distributed under the terms of the Modified BSD License.
  4. |----------------------------------------------------------------------------*/
  5. import { DataConnector, ISchemaValidator } from '@jupyterlab/coreutils';
  6. import { InspectionHandler, InspectorPanel } from '@jupyterlab/inspector';
  7. import {
  8. IRenderMimeRegistry,
  9. RenderMimeRegistry,
  10. standardRendererFactories
  11. } from '@jupyterlab/rendermime';
  12. import { ReadonlyJSONObject } from '@phosphor/coreutils';
  13. import { RawEditor } from './raweditor';
  14. /**
  15. * Create a raw editor inspector.
  16. */
  17. export function createInspector(
  18. editor: RawEditor,
  19. rendermime?: IRenderMimeRegistry
  20. ): InspectorPanel {
  21. const connector = new InspectorConnector(editor);
  22. const inspector = new InspectorPanel({
  23. initialContent: 'Any errors will be listed here'
  24. });
  25. const handler = new InspectionHandler({
  26. connector,
  27. rendermime:
  28. rendermime ||
  29. new RenderMimeRegistry({
  30. initialFactories: standardRendererFactories
  31. })
  32. });
  33. inspector.addClass('jp-SettingsDebug');
  34. inspector.source = handler;
  35. handler.editor = editor.source;
  36. return inspector;
  37. }
  38. /**
  39. * The data connector used to populate a code inspector.
  40. *
  41. * #### Notes
  42. * This data connector debounces fetch requests to throttle them at no more than
  43. * one request per 100ms. This means that using the connector to populate
  44. * multiple client objects can lead to missed fetch responses.
  45. */
  46. class InspectorConnector extends DataConnector<
  47. InspectionHandler.IReply,
  48. void,
  49. InspectionHandler.IRequest
  50. > {
  51. constructor(editor: RawEditor) {
  52. super();
  53. this._editor = editor;
  54. }
  55. /**
  56. * Fetch inspection requests.
  57. */
  58. fetch(
  59. request: InspectionHandler.IRequest
  60. ): Promise<InspectionHandler.IReply> {
  61. return new Promise<InspectionHandler.IReply>(resolve => {
  62. // Debounce requests at a rate of 100ms.
  63. const current = (this._current = window.setTimeout(() => {
  64. if (current !== this._current) {
  65. return resolve(null);
  66. }
  67. const errors = this._validate(request.text);
  68. if (!errors) {
  69. return resolve({
  70. data: { 'text/markdown': 'No errors found' },
  71. metadata: {}
  72. });
  73. }
  74. resolve({ data: Private.render(errors), metadata: {} });
  75. }, 100));
  76. });
  77. }
  78. private _validate(raw: string): ISchemaValidator.IError[] | null {
  79. const editor = this._editor;
  80. if (!editor.settings) {
  81. return null;
  82. }
  83. const { id, schema, version } = editor.settings;
  84. const data = { composite: {}, user: {} };
  85. const validator = editor.registry.validator;
  86. return validator.validateData({ data, id, raw, schema, version }, false);
  87. }
  88. private _current = 0;
  89. private _editor: RawEditor;
  90. }
  91. /**
  92. * A namespace for private module data.
  93. */
  94. namespace Private {
  95. /**
  96. * Render validation errors as an HTML string.
  97. */
  98. export function render(
  99. errors: ISchemaValidator.IError[]
  100. ): ReadonlyJSONObject {
  101. return { 'text/markdown': errors.map(renderError).join('') };
  102. }
  103. /**
  104. * Render an individual validation error as a markdown string.
  105. */
  106. function renderError(error: ISchemaValidator.IError): string {
  107. switch (error.keyword) {
  108. case 'additionalProperties':
  109. return `**\`[additional property error]\`**
  110. \`${error.params.additionalProperty}\` is not a valid property`;
  111. case 'syntax':
  112. return `**\`[syntax error]\`** *${error.message}*`;
  113. case 'type':
  114. return `**\`[type error]\`**
  115. \`${error.dataPath}\` ${error.message}`;
  116. default:
  117. return `**\`[error]\`** *${error.message}*`;
  118. }
  119. }
  120. }