registry.ts 25 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. Contents, Kernel
  5. } from '@jupyterlab/services';
  6. import {
  7. IIterator, each, empty, map
  8. } from 'phosphor/lib/algorithm/iteration';
  9. import {
  10. find, findIndex, indexOf
  11. } from 'phosphor/lib/algorithm/searching';
  12. import {
  13. Vector
  14. } from 'phosphor/lib/collections/vector';
  15. import {
  16. IDisposable, DisposableDelegate
  17. } from 'phosphor/lib/core/disposable';
  18. import {
  19. ISignal, defineSignal
  20. } from 'phosphor/lib/core/signaling';
  21. import {
  22. Token
  23. } from 'phosphor/lib/core/token';
  24. import {
  25. Widget
  26. } from 'phosphor/lib/ui/widget';
  27. import {
  28. CodeEditor
  29. } from '../codeeditor';
  30. import {
  31. IChangedArgs as IChangedArgsGeneric
  32. } from '../common/interfaces';
  33. /* tslint:disable */
  34. /**
  35. * The document registry token.
  36. */
  37. export
  38. const IDocumentRegistry = new Token<IDocumentRegistry>('jupyter.services.document-registry');
  39. /* tslint:enable */
  40. /**
  41. * The interface for a document registry.
  42. */
  43. export
  44. interface IDocumentRegistry extends DocumentRegistry {}
  45. /**
  46. * The document registry.
  47. */
  48. export
  49. class DocumentRegistry {
  50. /**
  51. * A signal emitted when the registry has changed.
  52. */
  53. readonly changed: ISignal<this, DocumentRegistry.IChangedArgs>;
  54. /**
  55. * Get whether the document registry has been disposed.
  56. */
  57. get isDisposed(): boolean {
  58. return this._widgetFactories === null;
  59. }
  60. /**
  61. * Dispose of the resources held by the document registery.
  62. */
  63. dispose(): void {
  64. if (this.isDisposed) {
  65. return;
  66. }
  67. for (let modelName in this._modelFactories) {
  68. this._modelFactories[modelName].dispose();
  69. }
  70. this._modelFactories = null;
  71. for (let widgetName in this._widgetFactories) {
  72. this._widgetFactories[widgetName].dispose();
  73. }
  74. this._widgetFactories = null;
  75. this._fileTypes.clear();
  76. this._creators.clear();
  77. for (let widgetName in this._extenders) {
  78. this._extenders[widgetName].clear();
  79. }
  80. }
  81. /**
  82. * Add a widget factory to the registry.
  83. *
  84. * @param factory - The factory instance to register.
  85. *
  86. * @returns A disposable which will unregister the factory.
  87. *
  88. * #### Notes
  89. * If a factory with the given `'displayName'` is already registered,
  90. * a warning will be logged, and this will be a no-op.
  91. * If `'*'` is given as a default extension, the factory will be registered
  92. * as the global default.
  93. * If an extension or global default is already registered, this factory
  94. * will override the existing default.
  95. */
  96. addWidgetFactory(factory: DocumentRegistry.WidgetFactory): IDisposable {
  97. let name = factory.name.toLowerCase();
  98. if (this._widgetFactories[name]) {
  99. console.warn(`Duplicate registered factory ${name}`);
  100. return new DisposableDelegate(null);
  101. }
  102. this._widgetFactories[name] = factory;
  103. for (let ext of factory.defaultFor) {
  104. if (factory.fileExtensions.indexOf(ext) === -1) {
  105. continue;
  106. }
  107. if (ext === '*') {
  108. this._defaultWidgetFactory = name;
  109. } else {
  110. this._defaultWidgetFactories[ext] = name;
  111. }
  112. }
  113. // For convenience, store a mapping of ext -> name
  114. for (let ext of factory.fileExtensions) {
  115. if (!this._widgetFactoryExtensions[ext]) {
  116. this._widgetFactoryExtensions[ext] = new Vector<string>();
  117. }
  118. this._widgetFactoryExtensions[ext].pushBack(name);
  119. }
  120. this.changed.emit({
  121. type: 'widgetFactory',
  122. name,
  123. change: 'added'
  124. });
  125. return new DisposableDelegate(() => {
  126. delete this._widgetFactories[name];
  127. if (this._defaultWidgetFactory === name) {
  128. this._defaultWidgetFactory = '';
  129. }
  130. for (let ext of Object.keys(this._defaultWidgetFactories)) {
  131. if (this._defaultWidgetFactories[ext] === name) {
  132. delete this._defaultWidgetFactories[ext];
  133. }
  134. }
  135. for (let ext of Object.keys(this._widgetFactoryExtensions)) {
  136. this._widgetFactoryExtensions[ext].remove(name);
  137. if (this._widgetFactoryExtensions[ext].length === 0) {
  138. delete this._widgetFactoryExtensions[ext];
  139. }
  140. }
  141. this.changed.emit({
  142. type: 'widgetFactory',
  143. name,
  144. change: 'removed'
  145. });
  146. });
  147. }
  148. /**
  149. * Add a model factory to the registry.
  150. *
  151. * @param factory - The factory instance.
  152. *
  153. * @returns A disposable which will unregister the factory.
  154. *
  155. * #### Notes
  156. * If a factory with the given `name` is already registered, or
  157. * the given factory is already registered, a warning will be logged
  158. * and this will be a no-op.
  159. */
  160. addModelFactory(factory: DocumentRegistry.ModelFactory): IDisposable {
  161. let name = factory.name.toLowerCase();
  162. if (this._modelFactories[name]) {
  163. console.warn(`Duplicate registered factory ${name}`);
  164. return new DisposableDelegate(null);
  165. }
  166. this._modelFactories[name] = factory;
  167. this.changed.emit({
  168. type: 'modelFactory',
  169. name,
  170. change: 'added'
  171. });
  172. return new DisposableDelegate(() => {
  173. delete this._modelFactories[name];
  174. this.changed.emit({
  175. type: 'modelFactory',
  176. name,
  177. change: 'removed'
  178. });
  179. });
  180. }
  181. /**
  182. * Add a widget extension to the registry.
  183. *
  184. * @param widgetName - The name of the widget factory.
  185. *
  186. * @param extension - A widget extension.
  187. *
  188. * @returns A disposable which will unregister the extension.
  189. *
  190. * #### Notes
  191. * If the extension is already registered for the given
  192. * widget name, a warning will be logged and this will be a no-op.
  193. */
  194. addWidgetExtension(widgetName: string, extension: DocumentRegistry.WidgetExtension): IDisposable {
  195. widgetName = widgetName.toLowerCase();
  196. if (!(widgetName in this._extenders)) {
  197. this._extenders[widgetName] = new Vector<DocumentRegistry.WidgetExtension>();
  198. }
  199. let extenders = this._extenders[widgetName];
  200. let index = indexOf(extenders, extension);
  201. if (index !== -1) {
  202. console.warn(`Duplicate registered extension for ${widgetName}`);
  203. return new DisposableDelegate(null);
  204. }
  205. this._extenders[widgetName].pushBack(extension);
  206. this.changed.emit({
  207. type: 'widgetExtension',
  208. name: null,
  209. change: 'added'
  210. });
  211. return new DisposableDelegate(() => {
  212. this._extenders[widgetName].remove(extension);
  213. this.changed.emit({
  214. type: 'widgetExtension',
  215. name: null,
  216. change: 'removed'
  217. });
  218. });
  219. }
  220. /**
  221. * Add a file type to the document registry.
  222. *
  223. * @params fileType - The file type object to register.
  224. *
  225. * @returns A disposable which will unregister the command.
  226. *
  227. * #### Notes
  228. * These are used to populate the "Create New" dialog.
  229. */
  230. addFileType(fileType: DocumentRegistry.IFileType): IDisposable {
  231. this._fileTypes.pushBack(fileType);
  232. this.changed.emit({
  233. type: 'fileType',
  234. name: fileType.name,
  235. change: 'added'
  236. });
  237. return new DisposableDelegate(() => {
  238. this._fileTypes.remove(fileType);
  239. this.changed.emit({
  240. type: 'fileType',
  241. name: fileType.name,
  242. change: 'removed'
  243. });
  244. });
  245. }
  246. /**
  247. * Add a creator to the registry.
  248. *
  249. * @params creator - The file creator object to register.
  250. *
  251. * @returns A disposable which will unregister the creator.
  252. */
  253. addCreator(creator: DocumentRegistry.IFileCreator): IDisposable {
  254. let index = findIndex(this._creators, (value) => {
  255. return value.name.localeCompare(creator.name) > 0;
  256. });
  257. if (index !== -1) {
  258. this._creators.insert(index, creator);
  259. } else {
  260. this._creators.pushBack(creator);
  261. }
  262. this.changed.emit({
  263. type: 'fileCreator',
  264. name: creator.name,
  265. change: 'added'
  266. });
  267. return new DisposableDelegate(() => {
  268. this._creators.remove(creator);
  269. this.changed.emit({
  270. type: 'fileCreator',
  271. name: creator.name,
  272. change: 'removed'
  273. });
  274. });
  275. }
  276. /**
  277. * Get a list of the preferred widget factories.
  278. *
  279. * @param ext - An optional file extension to filter the results.
  280. *
  281. * @returns A new array of widget factories.
  282. *
  283. * #### Notes
  284. * Only the widget factories whose associated model factory have
  285. * been registered will be returned.
  286. * The first item is considered the default. The returned iterator
  287. * has widget factories in the following order:
  288. * - extension-specific default factory
  289. * - global default factory
  290. * - all other extension-specific factories
  291. * - all other global factories
  292. */
  293. preferredWidgetFactories(ext: string = '*'): DocumentRegistry.WidgetFactory[] {
  294. let factories = new Set<string>();
  295. ext = Private.normalizeExtension(ext);
  296. // Start with the extension-specific default factory.
  297. if (ext.length > 1) {
  298. if (ext in this._defaultWidgetFactories) {
  299. factories.add(this._defaultWidgetFactories[ext]);
  300. }
  301. }
  302. // Add the global default factory.
  303. if (this._defaultWidgetFactory) {
  304. factories.add(this._defaultWidgetFactory);
  305. }
  306. // Add the extension-specific factories in registration order.
  307. if (ext.length > 1) {
  308. if (ext in this._widgetFactoryExtensions) {
  309. each(this._widgetFactoryExtensions[ext], n => {
  310. factories.add(n);
  311. });
  312. }
  313. }
  314. // Add the rest of the global factories, in registration order.
  315. if ('*' in this._widgetFactoryExtensions) {
  316. each(this._widgetFactoryExtensions['*'], n => {
  317. factories.add(n);
  318. });
  319. }
  320. // Construct the return list, checking to make sure the corresponding
  321. // model factories are registered.
  322. let factoryList: DocumentRegistry.WidgetFactory[] = [];
  323. factories.forEach(name => {
  324. if (this._widgetFactories[name].modelName in this._modelFactories) {
  325. factoryList.push(this._widgetFactories[name]);
  326. }
  327. });
  328. return factoryList;
  329. }
  330. /**
  331. * Get the default widget factory for an extension.
  332. *
  333. * @param ext - An optional file extension to filter the results.
  334. *
  335. * @returns The default widget factory for an extension.
  336. *
  337. * #### Notes
  338. * This is equivalent to the first value in [[preferredWidgetFactories]].
  339. */
  340. defaultWidgetFactory(ext: string = '*'): DocumentRegistry.WidgetFactory {
  341. return this.preferredWidgetFactories(ext)[0];
  342. }
  343. /**
  344. * Create an iterator over the widget factories that have been registered.
  345. *
  346. * @returns A new iterator of widget factories.
  347. */
  348. widgetFactories(): IIterator<DocumentRegistry.WidgetFactory> {
  349. return map(Object.keys(this._widgetFactories), name => {
  350. return this._widgetFactories[name];
  351. });
  352. }
  353. /**
  354. * Create an iterator over the model factories that have been registered.
  355. *
  356. * @returns A new iterator of model factories.
  357. */
  358. modelFactories(): IIterator<DocumentRegistry.ModelFactory> {
  359. return map(Object.keys(this._modelFactories), name => {
  360. return this._modelFactories[name];
  361. });
  362. }
  363. /**
  364. * Create an iterator over the registered extensions for a given widget.
  365. *
  366. * @param widgetName - The name of the widget factory.
  367. *
  368. * @returns A new iterator over the widget extensions.
  369. */
  370. widgetExtensions(widgetName: string): IIterator<DocumentRegistry.WidgetExtension> {
  371. widgetName = widgetName.toLowerCase();
  372. if (!(widgetName in this._extenders)) {
  373. return empty<DocumentRegistry.WidgetExtension>();
  374. }
  375. return this._extenders[widgetName].iter();
  376. }
  377. /**
  378. * Create an iterator over the file types that have been registered.
  379. *
  380. * @returns A new iterator of file types.
  381. */
  382. fileTypes(): IIterator<DocumentRegistry.IFileType> {
  383. return this._fileTypes.iter();
  384. }
  385. /**
  386. * Create an iterator over the file creators that have been registered.
  387. *
  388. * @returns A new iterator of file creatores.
  389. */
  390. creators(): IIterator<DocumentRegistry.IFileCreator> {
  391. return this._creators.iter();
  392. }
  393. /**
  394. * Get a widget factory by name.
  395. *
  396. * @param widgetName - The name of the widget factory.
  397. *
  398. * @returns A widget factory instance.
  399. */
  400. getWidgetFactory(widgetName: string): DocumentRegistry.WidgetFactory {
  401. return this._widgetFactories[widgetName.toLowerCase()];
  402. }
  403. /**
  404. * Get a model factory by name.
  405. *
  406. * @param name - The name of the model factory.
  407. *
  408. * @returns A model factory instance.
  409. */
  410. getModelFactory(name: string): DocumentRegistry.ModelFactory {
  411. return this._modelFactories[name.toLowerCase()];
  412. }
  413. /**
  414. * Get a file type by name.
  415. */
  416. getFileType(name: string): DocumentRegistry.IFileType {
  417. name = name.toLowerCase();
  418. return find(this._fileTypes, fileType => {
  419. return fileType.name.toLowerCase() === name;
  420. });
  421. }
  422. /**
  423. * Get a creator by name.
  424. */
  425. getCreator(name: string): DocumentRegistry.IFileCreator {
  426. name = name.toLowerCase();
  427. return find(this._creators, creator => {
  428. return creator.name.toLowerCase() === name;
  429. });
  430. }
  431. /**
  432. * Get a kernel preference.
  433. *
  434. * @param ext - The file extension.
  435. *
  436. * @param widgetName - The name of the widget factory.
  437. *
  438. * @returns A kernel preference.
  439. */
  440. getKernelPreference(ext: string, widgetName: string): DocumentRegistry.IKernelPreference {
  441. ext = Private.normalizeExtension(ext);
  442. widgetName = widgetName.toLowerCase();
  443. let widgetFactory = this._widgetFactories[widgetName];
  444. if (!widgetFactory) {
  445. return void 0;
  446. }
  447. let modelFactory = this.getModelFactory(widgetFactory.modelName);
  448. if (!modelFactory) {
  449. return void 0;
  450. }
  451. let language = modelFactory.preferredLanguage(ext);
  452. return {
  453. language,
  454. preferKernel: widgetFactory.preferKernel,
  455. canStartKernel: widgetFactory.canStartKernel
  456. };
  457. }
  458. private _modelFactories: { [key: string]: DocumentRegistry.ModelFactory } = Object.create(null);
  459. private _widgetFactories: { [key: string]: DocumentRegistry.WidgetFactory } = Object.create(null);
  460. private _defaultWidgetFactory = '';
  461. private _defaultWidgetFactories: { [key: string]: string } = Object.create(null);
  462. private _widgetFactoryExtensions: {[key: string]: Vector<string> } = Object.create(null);
  463. private _fileTypes = new Vector<DocumentRegistry.IFileType>();
  464. private _creators = new Vector<DocumentRegistry.IFileCreator>();
  465. private _extenders: { [key: string] : Vector<DocumentRegistry.WidgetExtension> } = Object.create(null);
  466. }
  467. /**
  468. * The namespace for the `DocumentRegistry` class statics.
  469. */
  470. export
  471. namespace DocumentRegistry {
  472. /**
  473. * The interface for a document model.
  474. */
  475. export
  476. interface IModel extends IDisposable {
  477. /**
  478. * A signal emitted when the document content changes.
  479. */
  480. contentChanged: ISignal<this, void>;
  481. /**
  482. * A signal emitted when the model state changes.
  483. */
  484. stateChanged: ISignal<this, IChangedArgsGeneric<any>>;
  485. /**
  486. * The dirty state of the model.
  487. *
  488. * #### Notes
  489. * This should be cleared when the document is loaded from
  490. * or saved to disk.
  491. */
  492. dirty: boolean;
  493. /**
  494. * The read-only state of the model.
  495. */
  496. readOnly: boolean;
  497. /**
  498. * The default kernel name of the document.
  499. */
  500. readonly defaultKernelName: string;
  501. /**
  502. * The default kernel language of the document.
  503. */
  504. readonly defaultKernelLanguage: string;
  505. /**
  506. * Serialize the model to a string.
  507. */
  508. toString(): string;
  509. /**
  510. * Deserialize the model from a string.
  511. *
  512. * #### Notes
  513. * Should emit a [contentChanged] signal.
  514. */
  515. fromString(value: string): void;
  516. /**
  517. * Serialize the model to JSON.
  518. */
  519. toJSON(): any;
  520. /**
  521. * Deserialize the model from JSON.
  522. *
  523. * #### Notes
  524. * Should emit a [contentChanged] signal.
  525. */
  526. fromJSON(value: any): void;
  527. }
  528. /**
  529. * The interface for a document model that represents code.
  530. */
  531. export
  532. interface ICodeModel extends IModel, CodeEditor.IModel { }
  533. /**
  534. * The document context object.
  535. */
  536. export
  537. interface IContext<T extends IModel> extends IDisposable {
  538. /**
  539. * A signal emitted when the kernel changes.
  540. */
  541. kernelChanged: ISignal<this, Kernel.IKernel>;
  542. /**
  543. * A signal emitted when the path changes.
  544. */
  545. pathChanged: ISignal<this, string>;
  546. /**
  547. * A signal emitted when the contentsModel changes.
  548. */
  549. fileChanged: ISignal<this, Contents.IModel>;
  550. /**
  551. * A signal emitted when the context is disposed.
  552. */
  553. disposed: ISignal<this, void>;
  554. /**
  555. * Get the model associated with the document.
  556. */
  557. readonly model: T;
  558. /**
  559. * The current kernel associated with the document.
  560. */
  561. readonly kernel: Kernel.IKernel;
  562. /**
  563. * The current path associated with the document.
  564. */
  565. readonly path: string;
  566. /**
  567. * The current contents model associated with the document
  568. *
  569. * #### Notes
  570. * The model will have an empty `contents` field.
  571. * It will be `null` until the context is ready.
  572. */
  573. readonly contentsModel: Contents.IModel;
  574. /**
  575. * Whether the context is ready.
  576. */
  577. readonly isReady: boolean;
  578. /**
  579. * A promise that is fulfilled when the context is ready.
  580. */
  581. readonly ready: Promise<void>;
  582. /**
  583. * Start the default kernel for the context.
  584. *
  585. * @returns A promise that resolves with the new kernel.
  586. */
  587. startDefaultKernel(): Promise<Kernel.IKernel>;
  588. /**
  589. * Change the current kernel associated with the document.
  590. *
  591. * #### Notes
  592. * If no options are given, the session is shut down.
  593. */
  594. changeKernel(options?: Kernel.IModel): Promise<Kernel.IKernel>;
  595. /**
  596. * Save the document contents to disk.
  597. */
  598. save(): Promise<void>;
  599. /**
  600. * Save the document to a different path chosen by the user.
  601. */
  602. saveAs(): Promise<void>;
  603. /**
  604. * Revert the document contents to disk contents.
  605. */
  606. revert(): Promise<void>;
  607. /**
  608. * Create a checkpoint for the file.
  609. *
  610. * @returns A promise which resolves with the new checkpoint model when the
  611. * checkpoint is created.
  612. */
  613. createCheckpoint(): Promise<Contents.ICheckpointModel>;
  614. /**
  615. * Delete a checkpoint for the file.
  616. *
  617. * @param checkpointID - The id of the checkpoint to delete.
  618. *
  619. * @returns A promise which resolves when the checkpoint is deleted.
  620. */
  621. deleteCheckpoint(checkpointID: string): Promise<void>;
  622. /**
  623. * Restore the file to a known checkpoint state.
  624. *
  625. * @param checkpointID - The optional id of the checkpoint to restore,
  626. * defaults to the most recent checkpoint.
  627. *
  628. * @returns A promise which resolves when the checkpoint is restored.
  629. */
  630. restoreCheckpoint(checkpointID?: string): Promise<void>;
  631. /**
  632. * List available checkpoints for the file.
  633. *
  634. * @returns A promise which resolves with a list of checkpoint models for
  635. * the file.
  636. */
  637. listCheckpoints(): Promise<Contents.ICheckpointModel[]>;
  638. /**
  639. * Resolve a url to a correct server path.
  640. */
  641. resolveUrl(url: string): string;
  642. /**
  643. * Add a sibling widget to the document manager.
  644. *
  645. * @param widget - The widget to add to the document manager.
  646. *
  647. * @returns A disposable used to remove the sibling if desired.
  648. *
  649. * #### Notes
  650. * It is assumed that the widget has the same model and context
  651. * as the original widget.
  652. */
  653. addSibling(widget: Widget): IDisposable;
  654. }
  655. /**
  656. * A type alias for a context.
  657. */
  658. export
  659. type Context = IContext<IModel>;
  660. /**
  661. * A type alias for a code context.
  662. */
  663. export
  664. type CodeContext = IContext<ICodeModel>;
  665. /**
  666. * The options used to initialize a widget factory.
  667. */
  668. export
  669. interface IWidgetFactoryOptions {
  670. /**
  671. * The file extensions the widget can view.
  672. *
  673. * #### Notes
  674. * Use "*" to denote all files. Specific file extensions must be preceded
  675. * with '.', like '.png', '.txt', etc.
  676. */
  677. readonly fileExtensions: string[];
  678. /**
  679. * The name of the widget to display in dialogs.
  680. */
  681. readonly name: string;
  682. /**
  683. * The file extensions for which the factory should be the default.
  684. *
  685. * #### Notes
  686. * Use "*" to denote all files. Specific file extensions must be preceded
  687. * with '.', like '.png', '.txt', etc. Entries in this attribute must also
  688. * be included in the fileExtensions attribute.
  689. * The default is an empty array.
  690. *
  691. * **See also:** [[fileExtensions]].
  692. */
  693. readonly defaultFor?: string[];
  694. /**
  695. * The registered name of the model type used to create the widgets.
  696. */
  697. readonly modelName?: string;
  698. /**
  699. * Whether the widgets prefer having a kernel started.
  700. */
  701. readonly preferKernel?: boolean;
  702. /**
  703. * Whether the widgets can start a kernel when opened.
  704. */
  705. readonly canStartKernel?: boolean;
  706. }
  707. /**
  708. * The interface for a widget factory.
  709. */
  710. export
  711. interface IWidgetFactory<T extends Widget, U extends IModel> extends IDisposable, IWidgetFactoryOptions {
  712. /**
  713. * A signal emitted when a widget is created.
  714. */
  715. widgetCreated: ISignal<IWidgetFactory<T, U>, T>;
  716. /**
  717. * Create a new widget given a context.
  718. *
  719. * #### Notes
  720. * It should emit the [widgetCreated] signal with the new widget.
  721. */
  722. createNew(context: IContext<U>): T;
  723. }
  724. /**
  725. * A type alias for a standard widget factory.
  726. */
  727. export
  728. type WidgetFactory = IWidgetFactory<Widget, IModel>;
  729. /**
  730. * An interface for a widget extension.
  731. */
  732. export
  733. interface IWidgetExtension<T extends Widget, U extends IModel> {
  734. /**
  735. * Create a new extension for a given widget.
  736. */
  737. createNew(widget: T, context: IContext<U>): IDisposable;
  738. }
  739. /**
  740. * A type alias for a standard widget extension.
  741. */
  742. export
  743. type WidgetExtension = IWidgetExtension<Widget, IModel>;
  744. /**
  745. * The interface for a model factory.
  746. */
  747. export
  748. interface IModelFactory<T extends IModel> extends IDisposable {
  749. /**
  750. * The name of the model.
  751. */
  752. readonly name: string;
  753. /**
  754. * The content type of the file (defaults to `"file"`).
  755. */
  756. readonly contentType: Contents.ContentType;
  757. /**
  758. * The format of the file (defaults to `"text"`).
  759. */
  760. readonly fileFormat: Contents.FileFormat;
  761. /**
  762. * Create a new model for a given path.
  763. *
  764. * @param languagePreference - An optional kernel language preference.
  765. *
  766. * @returns A new document model.
  767. */
  768. createNew(languagePreference?: string): T;
  769. /**
  770. * Get the preferred kernel language given an extension.
  771. */
  772. preferredLanguage(ext: string): string;
  773. }
  774. /**
  775. * A type alias for a standard model factory.
  776. */
  777. export
  778. type ModelFactory = IModelFactory<IModel>;
  779. /**
  780. * A type alias for a code model factory.
  781. */
  782. export
  783. type CodeModelFactory = IModelFactory<ICodeModel>;
  784. /**
  785. * A kernel preference for a given file path and widget.
  786. */
  787. export
  788. interface IKernelPreference {
  789. /**
  790. * The preferred kernel language.
  791. */
  792. readonly language: string;
  793. /**
  794. * Whether to prefer having a kernel started when opening.
  795. */
  796. readonly preferKernel: boolean;
  797. /**
  798. * Whether a kernel when can be started when opening.
  799. */
  800. readonly canStartKernel: boolean;
  801. }
  802. /**
  803. * An interface for a file type.
  804. */
  805. export
  806. interface IFileType {
  807. /**
  808. * The name of the file type.
  809. */
  810. readonly name: string;
  811. /**
  812. * The extension of the file type (e.g. `".txt"`).
  813. */
  814. readonly extension: string;
  815. /**
  816. * The optional mimetype of the file type.
  817. */
  818. readonly mimetype?: string;
  819. /**
  820. * The optional icon class to use for the file type.
  821. */
  822. readonly icon?: string;
  823. /**
  824. * The content type of the new file (defaults to `"file"`).
  825. */
  826. readonly contentType?: Contents.ContentType;
  827. /**
  828. * The format of the new file (default to `"text"`).
  829. */
  830. readonly fileFormat?: Contents.FileFormat;
  831. }
  832. /**
  833. * An interface for a "Create New" item.
  834. */
  835. export
  836. interface IFileCreator {
  837. /**
  838. * The name of the file creator.
  839. */
  840. readonly name: string;
  841. /**
  842. * The filetype name associated with the creator.
  843. */
  844. readonly fileType: string;
  845. /**
  846. * The optional widget name.
  847. */
  848. readonly widgetName?: string;
  849. /**
  850. * The optional kernel name.
  851. */
  852. readonly kernelName?: string;
  853. }
  854. /**
  855. * An arguments object for the `changed` signal.
  856. */
  857. export
  858. interface IChangedArgs {
  859. /**
  860. * The type of the changed item.
  861. */
  862. readonly type: 'widgetFactory' | 'modelFactory' | 'widgetExtension' | 'fileCreator' | 'fileType';
  863. /**
  864. * The name of the item.
  865. */
  866. readonly name: string;
  867. /**
  868. * Whether the item was added or removed.
  869. */
  870. readonly change: 'added' | 'removed';
  871. }
  872. }
  873. // Define the signals for the `DocumentRegistry` class.
  874. defineSignal(DocumentRegistry.prototype, 'changed');
  875. /**
  876. * A private namespace for DocumentRegistry data.
  877. */
  878. namespace Private {
  879. /**
  880. * Normalize a file extension to be of the type `'.foo'`.
  881. *
  882. * Adds a leading dot if not present and converts to lower case.
  883. */
  884. export
  885. function normalizeExtension(extension: string): string {
  886. if (extension === '*') {
  887. return extension;
  888. }
  889. if (extension === '.*') {
  890. return '*';
  891. }
  892. if (extension.indexOf('.') !== 0) {
  893. extension = `.${extension}`;
  894. }
  895. return extension.toLowerCase();
  896. }
  897. }