registry.ts 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import * as utils
  4. from 'jupyter-js-utils';
  5. import {
  6. IDisposable, DisposableDelegate
  7. } from 'phosphor-disposable';
  8. import {
  9. Widget
  10. } from 'phosphor-widget';
  11. import {
  12. IModelFactory, IWidgetFactory, IWidgetFactoryOptions,
  13. IFileType, IKernelPreference, IFileCreator, IWidgetExtension
  14. } from './interfaces';
  15. /**
  16. * The document registery.
  17. */
  18. export
  19. class DocumentRegistry implements IDisposable {
  20. /**
  21. * The name of the default widget factory.
  22. *
  23. * #### Notes
  24. * This is a read-only property.
  25. */
  26. get defaultWidgetFactory(): string {
  27. return this._defaultWidgetFactory;
  28. }
  29. /**
  30. * Get whether the document registry has been disposed.
  31. */
  32. get isDisposed(): boolean {
  33. return this._widgetFactories === null;
  34. }
  35. /**
  36. * Dispose of the resources held by the document registery.
  37. */
  38. dispose(): void {
  39. if (this.isDisposed) {
  40. return;
  41. }
  42. for (let modelName in this._modelFactories) {
  43. this._modelFactories[modelName].dispose();
  44. }
  45. this._modelFactories = null;
  46. for (let widgetName in this._widgetFactories) {
  47. this._widgetFactories[widgetName].factory.dispose();
  48. }
  49. this._widgetFactories = null;
  50. }
  51. /**
  52. * Add a widget factory to the registry.
  53. *
  54. * @param factory - The factory instance to register.
  55. *
  56. * @param options - The options used to register the factory.
  57. *
  58. * @returns A disposable which will unregister the factory.
  59. *
  60. * #### Notes
  61. * If a factory with the given `displayName` is already registered,
  62. * an error will be thrown.
  63. * If `'.*'` is given as a default extension, the factory will be registered
  64. * as the global default.
  65. * If a factory is already registered as a default for a given extension or
  66. * as the global default, this factory will override the existing default.
  67. */
  68. addWidgetFactory(factory: IWidgetFactory<Widget>, options: IWidgetFactoryOptions): IDisposable {
  69. let name = options.displayName;
  70. let exOpt = utils.copy(options) as Private.IWidgetFactoryEx;
  71. exOpt.factory = factory;
  72. if (this._widgetFactories[name]) {
  73. throw new Error(`Duplicate registered factory ${name}`);
  74. }
  75. this._widgetFactories[name] = exOpt;
  76. if (options.defaultFor) {
  77. for (let option of options.defaultFor) {
  78. if (option === '.*') {
  79. this._defaultWidgetFactory = name;
  80. } else if (options.fileExtensions.indexOf(option) !== -1) {
  81. this._defaultWidgetFactories[option] = name;
  82. }
  83. }
  84. }
  85. return new DisposableDelegate(() => {
  86. delete this._widgetFactories[name];
  87. if (this._defaultWidgetFactory === name) {
  88. this._defaultWidgetFactory = '';
  89. }
  90. for (let opt of Object.keys(this._defaultWidgetFactories)) {
  91. let n = this._defaultWidgetFactories[opt];
  92. if (n === name) {
  93. delete this._defaultWidgetFactories[opt];
  94. }
  95. }
  96. });
  97. }
  98. /**
  99. * Add a model factory to the registry.
  100. *
  101. * @param factory - The factory instance.
  102. *
  103. * @returns A disposable which will unregister the factory.
  104. *
  105. * #### Notes
  106. * If a factory with the given `name` is already registered, an error
  107. * will be thrown.
  108. */
  109. addModelFactory(factory: IModelFactory): IDisposable {
  110. let name = factory.name;
  111. if (this._modelFactories[name]) {
  112. throw new Error(`Duplicate registered factory ${name}`);
  113. }
  114. this._modelFactories[name] = factory;
  115. return new DisposableDelegate(() => {
  116. delete this._modelFactories[name];
  117. });
  118. }
  119. /**
  120. * Add a widget extension to the registry.
  121. *
  122. * @param widgetName - The name of the widget factory.
  123. *
  124. * @param extension - A widget extension.
  125. *
  126. * @returns A disposable which will unregister the extension.
  127. */
  128. addWidgetExtension(widgetName: string, extension: IWidgetExtension<Widget>): IDisposable {
  129. if (!(widgetName in this._extenders)) {
  130. this._extenders[widgetName] = [];
  131. }
  132. this._extenders[widgetName].push(extension);
  133. return new DisposableDelegate(() => {
  134. let index = this._extenders[widgetName].indexOf(extension);
  135. this._extenders[widgetName].splice(index, 1);
  136. });
  137. }
  138. /**
  139. * Add a file type to the document registry.
  140. *
  141. * @params fileType - The file type object to register.
  142. *
  143. * @returns A disposable which will unregister the command.
  144. *
  145. * #### Notes
  146. * These are used to populate the "Create New" dialog.
  147. */
  148. addFileType(fileType: IFileType): IDisposable {
  149. this._fileTypes.push(fileType);
  150. this._fileTypes.sort((a, b) => a.name.localeCompare(b.name));
  151. return new DisposableDelegate(() => {
  152. let index = this._fileTypes.indexOf(fileType);
  153. this._fileTypes.splice(index, 1);
  154. });
  155. }
  156. /**
  157. * Add a creator to the registry.
  158. *
  159. * @params creator - The file creator object to register.
  160. *
  161. * @params after - The optional item name to insert after.
  162. *
  163. * @returns A disposable which will unregister the creator.
  164. *
  165. * #### Notes
  166. * If `after` is not given or not already registered, it will be moved
  167. * to the end.
  168. */
  169. addCreator(creator: IFileCreator, after?: string): IDisposable {
  170. let added = false;
  171. if (after) {
  172. for (let existing of this._creators) {
  173. if (existing.name === after) {
  174. let index = this._creators.indexOf(existing);
  175. this._creators.splice(index, 0, creator);
  176. added = true;
  177. }
  178. }
  179. }
  180. if (!added) {
  181. this._creators.push(creator);
  182. }
  183. return new DisposableDelegate(() => {
  184. let index = this._creators.indexOf(creator);
  185. this._creators.splice(index, 1);
  186. });
  187. }
  188. /**
  189. * List the names of the registered widget factories.
  190. *
  191. * @param ext - An optional file extension to filter the results.
  192. *
  193. * @returns A new array of registered widget factory names.
  194. *
  195. * #### Notes
  196. * The first item in the list is considered the default.
  197. */
  198. listWidgetFactories(ext?: string): string[] {
  199. ext = ext || '';
  200. let factories: string[] = [];
  201. let options: Private.IWidgetFactoryEx;
  202. let name = '';
  203. // If an extension was given, filter by extension.
  204. // Make sure the modelFactory is registered.
  205. if (ext.length > 1) {
  206. if (ext in this._defaultWidgetFactories) {
  207. name = this._defaultWidgetFactories[ext];
  208. options = this._widgetFactories[name];
  209. if (options.modelName in this._modelFactories) {
  210. factories.push(name);
  211. }
  212. }
  213. }
  214. // Add the rest of the valid widgetFactories that can open the path.
  215. for (name in this._widgetFactories) {
  216. if (factories.indexOf(name) !== -1) {
  217. continue;
  218. }
  219. options = this._widgetFactories[name];
  220. if (!(options.modelName in this._modelFactories)) {
  221. continue;
  222. }
  223. let exts = options.fileExtensions;
  224. if ((exts.indexOf(ext) !== -1) || (exts.indexOf('.*') !== -1)) {
  225. factories.push(name);
  226. }
  227. }
  228. // Add the default widget if it was not already added.
  229. name = this._defaultWidgetFactory;
  230. if (name && factories.indexOf(name) === -1) {
  231. options = this._widgetFactories[name];
  232. if (options.modelName in this._modelFactories) {
  233. factories.push(name);
  234. }
  235. }
  236. return factories;
  237. }
  238. /**
  239. * List the names of the registered model factories.
  240. *
  241. * @returns A new array of registered model factory names.
  242. */
  243. listModelFactories(): string[] {
  244. return Object.keys(this._modelFactories);
  245. }
  246. /**
  247. * Get a list of file types that have been registered.
  248. *
  249. * @returns A new array of registered file type objects.
  250. */
  251. listFileTypes(): IFileType[] {
  252. return this._fileTypes.slice();
  253. }
  254. /**
  255. * Get an ordered list of the file creators that have been registered.
  256. *
  257. * @returns A new array of registered file creator objects.
  258. */
  259. listCreators(): IFileCreator[] {
  260. return this._creators.slice();
  261. }
  262. /**
  263. * Get a kernel preference.
  264. *
  265. * @param ext - The file extension.
  266. *
  267. * @param widgetName - The name of the widget factory.
  268. *
  269. * @returns A kernel preference.
  270. */
  271. getKernelPreference(ext: string, widgetName: string): IKernelPreference {
  272. let widgetFactoryEx = this._getWidgetFactoryEx(widgetName);
  273. let modelFactory = this.getModelFactory(widgetName);
  274. let language = modelFactory.preferredLanguage(ext);
  275. return {
  276. language,
  277. preferKernel: widgetFactoryEx.preferKernel,
  278. canStartKernel: widgetFactoryEx.canStartKernel
  279. };
  280. }
  281. /**
  282. * Get the model factory registered for a given widget factory.
  283. *
  284. * @param widgetName - The name of the widget factory.
  285. *
  286. * @returns A model factory instance.
  287. */
  288. getModelFactory(widgetName: string): IModelFactory {
  289. let wFactoryEx = this._getWidgetFactoryEx(widgetName);
  290. if (!wFactoryEx) {
  291. return;
  292. }
  293. return this._modelFactories[wFactoryEx.modelName];
  294. }
  295. /**
  296. * Get a widget factory by name.
  297. *
  298. * @param widgetName - The name of the widget factory.
  299. *
  300. * @returns A widget factory instance.
  301. */
  302. getWidgetFactory(widgetName: string): IWidgetFactory<Widget> {
  303. return this._getWidgetFactoryEx(widgetName).factory;
  304. }
  305. /**
  306. * Get the registered extensions for a given widget.
  307. *
  308. * @param widgetName - The name of the widget factory.
  309. *
  310. * @returns A new array of widget extensions.
  311. */
  312. getWidgetExtensions(widgetName: string): IWidgetExtension<Widget>[] {
  313. if (!(widgetName in this._extenders)) {
  314. return [];
  315. }
  316. return this._extenders[widgetName].slice();
  317. }
  318. /**
  319. * Get the appropriate widget factory by name.
  320. */
  321. private _getWidgetFactoryEx(widgetName: string): Private.IWidgetFactoryEx {
  322. let options: Private.IWidgetFactoryEx;
  323. if (widgetName === 'default') {
  324. options = this._widgetFactories[this._defaultWidgetFactory];
  325. } else {
  326. options = this._widgetFactories[widgetName];
  327. }
  328. return options;
  329. }
  330. private _modelFactories: { [key: string]: IModelFactory } = Object.create(null);
  331. private _widgetFactories: { [key: string]: Private.IWidgetFactoryEx } = Object.create(null);
  332. private _defaultWidgetFactory = '';
  333. private _defaultWidgetFactories: { [key: string]: string } = Object.create(null);
  334. private _fileTypes: IFileType[] = [];
  335. private _creators: IFileCreator[] = [];
  336. private _extenders: { [key: string] : IWidgetExtension<Widget>[] } = Object.create(null);
  337. }
  338. /**
  339. * A private namespace for DocumentRegistry data.
  340. */
  341. namespace Private {
  342. /**
  343. * An extended interface for a widget factory and its options.
  344. */
  345. export
  346. interface IWidgetFactoryEx extends IWidgetFactoryOptions {
  347. factory: IWidgetFactory<Widget>;
  348. }
  349. }