registry.ts 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. ArrayExt,
  5. ArrayIterator,
  6. IIterator,
  7. each,
  8. empty,
  9. find,
  10. map
  11. } from '@lumino/algorithm';
  12. import * as models from '@jupyterlab/shared-models';
  13. import { PartialJSONValue, ReadonlyPartialJSONValue } from '@lumino/coreutils';
  14. import { IDisposable, DisposableDelegate } from '@lumino/disposable';
  15. import { ISignal, Signal } from '@lumino/signaling';
  16. import { DockLayout, Widget } from '@lumino/widgets';
  17. import { ISessionContext, Toolbar } from '@jupyterlab/apputils';
  18. import { CodeEditor } from '@jupyterlab/codeeditor';
  19. import {
  20. IChangedArgs as IChangedArgsGeneric,
  21. PathExt
  22. } from '@jupyterlab/coreutils';
  23. import { IModelDB } from '@jupyterlab/observables';
  24. import { IRenderMime } from '@jupyterlab/rendermime-interfaces';
  25. import { Contents, Kernel } from '@jupyterlab/services';
  26. import { nullTranslator, ITranslator } from '@jupyterlab/translation';
  27. import {
  28. fileIcon,
  29. folderIcon,
  30. imageIcon,
  31. LabIcon,
  32. jsonIcon,
  33. markdownIcon,
  34. notebookIcon,
  35. pdfIcon,
  36. pythonIcon,
  37. rKernelIcon,
  38. spreadsheetIcon,
  39. yamlIcon
  40. } from '@jupyterlab/ui-components';
  41. import { TextModelFactory } from './default';
  42. /**
  43. * The document registry.
  44. */
  45. export class DocumentRegistry implements IDisposable {
  46. /**
  47. * Construct a new document registry.
  48. */
  49. constructor(options: DocumentRegistry.IOptions = {}) {
  50. const factory = options.textModelFactory;
  51. this.translator = options.translator || nullTranslator;
  52. if (factory && factory.name !== 'text') {
  53. throw new Error('Text model factory must have the name `text`');
  54. }
  55. this._modelFactories['text'] = factory || new TextModelFactory();
  56. const fts =
  57. options.initialFileTypes ||
  58. DocumentRegistry.getDefaultFileTypes(this.translator);
  59. fts.forEach(ft => {
  60. const value: DocumentRegistry.IFileType = {
  61. ...DocumentRegistry.getFileTypeDefaults(this.translator),
  62. ...ft
  63. };
  64. this._fileTypes.push(value);
  65. });
  66. }
  67. /**
  68. * A signal emitted when the registry has changed.
  69. */
  70. get changed(): ISignal<this, DocumentRegistry.IChangedArgs> {
  71. return this._changed;
  72. }
  73. /**
  74. * Get whether the document registry has been disposed.
  75. */
  76. get isDisposed(): boolean {
  77. return this._isDisposed;
  78. }
  79. /**
  80. * Dispose of the resources held by the document registry.
  81. */
  82. dispose(): void {
  83. if (this.isDisposed) {
  84. return;
  85. }
  86. this._isDisposed = true;
  87. for (const modelName in this._modelFactories) {
  88. this._modelFactories[modelName].dispose();
  89. }
  90. for (const widgetName in this._widgetFactories) {
  91. this._widgetFactories[widgetName].dispose();
  92. }
  93. for (const widgetName in this._extenders) {
  94. this._extenders[widgetName].length = 0;
  95. }
  96. this._fileTypes.length = 0;
  97. Signal.clearData(this);
  98. }
  99. /**
  100. * Add a widget factory to the registry.
  101. *
  102. * @param factory - The factory instance to register.
  103. *
  104. * @returns A disposable which will unregister the factory.
  105. *
  106. * #### Notes
  107. * If a factory with the given `'name'` is already registered,
  108. * a warning will be logged, and this will be a no-op.
  109. * If `'*'` is given as a default extension, the factory will be registered
  110. * as the global default.
  111. * If an extension or global default is already registered, this factory
  112. * will override the existing default.
  113. * The factory cannot be named an empty string or the string `'default'`.
  114. */
  115. addWidgetFactory(factory: DocumentRegistry.WidgetFactory): IDisposable {
  116. const name = factory.name.toLowerCase();
  117. if (!name || name === 'default') {
  118. throw Error('Invalid factory name');
  119. }
  120. if (this._widgetFactories[name]) {
  121. console.warn(`Duplicate registered factory ${name}`);
  122. return new DisposableDelegate(Private.noOp);
  123. }
  124. this._widgetFactories[name] = factory;
  125. for (const ft of factory.defaultFor || []) {
  126. if (factory.fileTypes.indexOf(ft) === -1) {
  127. continue;
  128. }
  129. if (ft === '*') {
  130. this._defaultWidgetFactory = name;
  131. } else {
  132. this._defaultWidgetFactories[ft] = name;
  133. }
  134. }
  135. for (const ft of factory.defaultRendered || []) {
  136. if (factory.fileTypes.indexOf(ft) === -1) {
  137. continue;
  138. }
  139. this._defaultRenderedWidgetFactories[ft] = name;
  140. }
  141. // For convenience, store a mapping of file type name -> name
  142. for (const ft of factory.fileTypes) {
  143. if (!this._widgetFactoriesForFileType[ft]) {
  144. this._widgetFactoriesForFileType[ft] = [];
  145. }
  146. this._widgetFactoriesForFileType[ft].push(name);
  147. }
  148. this._changed.emit({
  149. type: 'widgetFactory',
  150. name,
  151. change: 'added'
  152. });
  153. return new DisposableDelegate(() => {
  154. delete this._widgetFactories[name];
  155. if (this._defaultWidgetFactory === name) {
  156. this._defaultWidgetFactory = '';
  157. }
  158. for (const ext of Object.keys(this._defaultWidgetFactories)) {
  159. if (this._defaultWidgetFactories[ext] === name) {
  160. delete this._defaultWidgetFactories[ext];
  161. }
  162. }
  163. for (const ext of Object.keys(this._defaultRenderedWidgetFactories)) {
  164. if (this._defaultRenderedWidgetFactories[ext] === name) {
  165. delete this._defaultRenderedWidgetFactories[ext];
  166. }
  167. }
  168. for (const ext of Object.keys(this._widgetFactoriesForFileType)) {
  169. ArrayExt.removeFirstOf(this._widgetFactoriesForFileType[ext], name);
  170. if (this._widgetFactoriesForFileType[ext].length === 0) {
  171. delete this._widgetFactoriesForFileType[ext];
  172. }
  173. }
  174. for (const ext of Object.keys(this._defaultWidgetFactoryOverrides)) {
  175. if (this._defaultWidgetFactoryOverrides[ext] === name) {
  176. delete this._defaultWidgetFactoryOverrides[ext];
  177. }
  178. }
  179. this._changed.emit({
  180. type: 'widgetFactory',
  181. name,
  182. change: 'removed'
  183. });
  184. });
  185. }
  186. /**
  187. * Add a model factory to the registry.
  188. *
  189. * @param factory - The factory instance.
  190. *
  191. * @returns A disposable which will unregister the factory.
  192. *
  193. * #### Notes
  194. * If a factory with the given `name` is already registered, or
  195. * the given factory is already registered, a warning will be logged
  196. * and this will be a no-op.
  197. */
  198. addModelFactory(factory: DocumentRegistry.ModelFactory): IDisposable {
  199. const name = factory.name.toLowerCase();
  200. if (this._modelFactories[name]) {
  201. console.warn(`Duplicate registered factory ${name}`);
  202. return new DisposableDelegate(Private.noOp);
  203. }
  204. this._modelFactories[name] = factory;
  205. this._changed.emit({
  206. type: 'modelFactory',
  207. name,
  208. change: 'added'
  209. });
  210. return new DisposableDelegate(() => {
  211. delete this._modelFactories[name];
  212. this._changed.emit({
  213. type: 'modelFactory',
  214. name,
  215. change: 'removed'
  216. });
  217. });
  218. }
  219. /**
  220. * Add a widget extension to the registry.
  221. *
  222. * @param widgetName - The name of the widget factory.
  223. *
  224. * @param extension - A widget extension.
  225. *
  226. * @returns A disposable which will unregister the extension.
  227. *
  228. * #### Notes
  229. * If the extension is already registered for the given
  230. * widget name, a warning will be logged and this will be a no-op.
  231. */
  232. addWidgetExtension(
  233. widgetName: string,
  234. extension: DocumentRegistry.WidgetExtension
  235. ): IDisposable {
  236. widgetName = widgetName.toLowerCase();
  237. if (!(widgetName in this._extenders)) {
  238. this._extenders[widgetName] = [];
  239. }
  240. const extenders = this._extenders[widgetName];
  241. const index = ArrayExt.firstIndexOf(extenders, extension);
  242. if (index !== -1) {
  243. console.warn(`Duplicate registered extension for ${widgetName}`);
  244. return new DisposableDelegate(Private.noOp);
  245. }
  246. this._extenders[widgetName].push(extension);
  247. this._changed.emit({
  248. type: 'widgetExtension',
  249. name: widgetName,
  250. change: 'added'
  251. });
  252. return new DisposableDelegate(() => {
  253. ArrayExt.removeFirstOf(this._extenders[widgetName], extension);
  254. this._changed.emit({
  255. type: 'widgetExtension',
  256. name: widgetName,
  257. change: 'removed'
  258. });
  259. });
  260. }
  261. /**
  262. * Add a file type to the document registry.
  263. *
  264. * @params fileType - The file type object to register.
  265. *
  266. * @returns A disposable which will unregister the command.
  267. *
  268. * #### Notes
  269. * These are used to populate the "Create New" dialog.
  270. */
  271. addFileType(fileType: Partial<DocumentRegistry.IFileType>): IDisposable {
  272. const value: DocumentRegistry.IFileType = {
  273. ...DocumentRegistry.getFileTypeDefaults(this.translator),
  274. ...fileType,
  275. // fall back to fileIcon if needed
  276. ...(!(fileType.icon || fileType.iconClass) && { icon: fileIcon })
  277. };
  278. this._fileTypes.push(value);
  279. this._changed.emit({
  280. type: 'fileType',
  281. name: value.name,
  282. change: 'added'
  283. });
  284. return new DisposableDelegate(() => {
  285. ArrayExt.removeFirstOf(this._fileTypes, value);
  286. this._changed.emit({
  287. type: 'fileType',
  288. name: fileType.name,
  289. change: 'removed'
  290. });
  291. });
  292. }
  293. /**
  294. * Get a list of the preferred widget factories.
  295. *
  296. * @param path - The file path to filter the results.
  297. *
  298. * @returns A new array of widget factories.
  299. *
  300. * #### Notes
  301. * Only the widget factories whose associated model factory have
  302. * been registered will be returned.
  303. * The first item is considered the default. The returned array
  304. * has widget factories in the following order:
  305. * - path-specific default factory
  306. * - path-specific default rendered factory
  307. * - global default factory
  308. * - all other path-specific factories
  309. * - all other global factories
  310. */
  311. preferredWidgetFactories(path: string): DocumentRegistry.WidgetFactory[] {
  312. const factories = new Set<string>();
  313. // Get the ordered matching file types.
  314. const fts = this.getFileTypesForPath(PathExt.basename(path));
  315. // Start with any user overrides for the defaults.
  316. fts.forEach(ft => {
  317. if (ft.name in this._defaultWidgetFactoryOverrides) {
  318. factories.add(this._defaultWidgetFactoryOverrides[ft.name]);
  319. }
  320. });
  321. // Next add the file type default factories.
  322. fts.forEach(ft => {
  323. if (ft.name in this._defaultWidgetFactories) {
  324. factories.add(this._defaultWidgetFactories[ft.name]);
  325. }
  326. });
  327. // Add the file type default rendered factories.
  328. fts.forEach(ft => {
  329. if (ft.name in this._defaultRenderedWidgetFactories) {
  330. factories.add(this._defaultRenderedWidgetFactories[ft.name]);
  331. }
  332. });
  333. // Add the global default factory.
  334. if (this._defaultWidgetFactory) {
  335. factories.add(this._defaultWidgetFactory);
  336. }
  337. // Add the file type factories in registration order.
  338. fts.forEach(ft => {
  339. if (ft.name in this._widgetFactoriesForFileType) {
  340. each(this._widgetFactoriesForFileType[ft.name], n => {
  341. factories.add(n);
  342. });
  343. }
  344. });
  345. // Add the rest of the global factories, in registration order.
  346. if ('*' in this._widgetFactoriesForFileType) {
  347. each(this._widgetFactoriesForFileType['*'], n => {
  348. factories.add(n);
  349. });
  350. }
  351. // Construct the return list, checking to make sure the corresponding
  352. // model factories are registered.
  353. const factoryList: DocumentRegistry.WidgetFactory[] = [];
  354. factories.forEach(name => {
  355. const factory = this._widgetFactories[name];
  356. if (!factory) {
  357. return;
  358. }
  359. const modelName = factory.modelName || 'text';
  360. if (modelName in this._modelFactories) {
  361. factoryList.push(factory);
  362. }
  363. });
  364. return factoryList;
  365. }
  366. /**
  367. * Get the default rendered widget factory for a path.
  368. *
  369. * @param path - The path to for which to find a widget factory.
  370. *
  371. * @returns The default rendered widget factory for the path.
  372. *
  373. * ### Notes
  374. * If the widget factory has registered a separate set of `defaultRendered`
  375. * file types and there is a match in that set, this returns that.
  376. * Otherwise, this returns the same widget factory as
  377. * [[defaultWidgetFactory]].
  378. */
  379. defaultRenderedWidgetFactory(path: string): DocumentRegistry.WidgetFactory {
  380. // Get the matching file types.
  381. const fts = this.getFileTypesForPath(PathExt.basename(path));
  382. let factory: DocumentRegistry.WidgetFactory | undefined = undefined;
  383. // Find if a there is a default rendered factory for this type.
  384. for (const ft of fts) {
  385. if (ft.name in this._defaultRenderedWidgetFactories) {
  386. factory = this._widgetFactories[
  387. this._defaultRenderedWidgetFactories[ft.name]
  388. ];
  389. break;
  390. }
  391. }
  392. return factory || this.defaultWidgetFactory(path);
  393. }
  394. /**
  395. * Get the default widget factory for a path.
  396. *
  397. * @param path - An optional file path to filter the results.
  398. *
  399. * @returns The default widget factory for an path.
  400. *
  401. * #### Notes
  402. * This is equivalent to the first value in [[preferredWidgetFactories]].
  403. */
  404. defaultWidgetFactory(path?: string): DocumentRegistry.WidgetFactory {
  405. if (!path) {
  406. return this._widgetFactories[this._defaultWidgetFactory];
  407. }
  408. return this.preferredWidgetFactories(path)[0];
  409. }
  410. /**
  411. * Set overrides for the default widget factory for a file type.
  412. *
  413. * Normally, a widget factory informs the document registry which file types
  414. * it should be the default for using the `defaultFor` option in the
  415. * IWidgetFactoryOptions. This function can be used to override that after
  416. * the fact.
  417. *
  418. * @param fileType: The name of the file type.
  419. *
  420. * @param factory: The name of the factory.
  421. *
  422. * #### Notes
  423. * If `factory` is undefined, then any override will be unset, and the
  424. * default factory will revert to the original value.
  425. *
  426. * If `factory` or `fileType` are not known to the docregistry, or
  427. * if `factory` cannot open files of type `fileType`, this will throw
  428. * an error.
  429. */
  430. setDefaultWidgetFactory(fileType: string, factory: string | undefined): void {
  431. fileType = fileType.toLowerCase();
  432. if (!this.getFileType(fileType)) {
  433. throw Error(`Cannot find file type ${fileType}`);
  434. }
  435. if (!factory) {
  436. if (this._defaultWidgetFactoryOverrides[fileType]) {
  437. delete this._defaultWidgetFactoryOverrides[fileType];
  438. }
  439. return;
  440. }
  441. if (!this.getWidgetFactory(factory)) {
  442. throw Error(`Cannot find widget factory ${factory}`);
  443. }
  444. factory = factory.toLowerCase();
  445. const factories = this._widgetFactoriesForFileType[fileType];
  446. if (
  447. factory !== this._defaultWidgetFactory &&
  448. !(factories && factories.includes(factory))
  449. ) {
  450. throw Error(`Factory ${factory} cannot view file type ${fileType}`);
  451. }
  452. this._defaultWidgetFactoryOverrides[fileType] = factory;
  453. }
  454. /**
  455. * Create an iterator over the widget factories that have been registered.
  456. *
  457. * @returns A new iterator of widget factories.
  458. */
  459. widgetFactories(): IIterator<DocumentRegistry.WidgetFactory> {
  460. return map(Object.keys(this._widgetFactories), name => {
  461. return this._widgetFactories[name];
  462. });
  463. }
  464. /**
  465. * Create an iterator over the model factories that have been registered.
  466. *
  467. * @returns A new iterator of model factories.
  468. */
  469. modelFactories(): IIterator<DocumentRegistry.ModelFactory> {
  470. return map(Object.keys(this._modelFactories), name => {
  471. return this._modelFactories[name];
  472. });
  473. }
  474. /**
  475. * Create an iterator over the registered extensions for a given widget.
  476. *
  477. * @param widgetName - The name of the widget factory.
  478. *
  479. * @returns A new iterator over the widget extensions.
  480. */
  481. widgetExtensions(
  482. widgetName: string
  483. ): IIterator<DocumentRegistry.WidgetExtension> {
  484. widgetName = widgetName.toLowerCase();
  485. if (!(widgetName in this._extenders)) {
  486. return empty<DocumentRegistry.WidgetExtension>();
  487. }
  488. return new ArrayIterator(this._extenders[widgetName]);
  489. }
  490. /**
  491. * Create an iterator over the file types that have been registered.
  492. *
  493. * @returns A new iterator of file types.
  494. */
  495. fileTypes(): IIterator<DocumentRegistry.IFileType> {
  496. return new ArrayIterator(this._fileTypes);
  497. }
  498. /**
  499. * Get a widget factory by name.
  500. *
  501. * @param widgetName - The name of the widget factory.
  502. *
  503. * @returns A widget factory instance.
  504. */
  505. getWidgetFactory(
  506. widgetName: string
  507. ): DocumentRegistry.WidgetFactory | undefined {
  508. return this._widgetFactories[widgetName.toLowerCase()];
  509. }
  510. /**
  511. * Get a model factory by name.
  512. *
  513. * @param name - The name of the model factory.
  514. *
  515. * @returns A model factory instance.
  516. */
  517. getModelFactory(name: string): DocumentRegistry.ModelFactory | undefined {
  518. return this._modelFactories[name.toLowerCase()];
  519. }
  520. /**
  521. * Get a file type by name.
  522. */
  523. getFileType(name: string): DocumentRegistry.IFileType | undefined {
  524. name = name.toLowerCase();
  525. return find(this._fileTypes, fileType => {
  526. return fileType.name.toLowerCase() === name;
  527. });
  528. }
  529. /**
  530. * Get a kernel preference.
  531. *
  532. * @param path - The file path.
  533. *
  534. * @param widgetName - The name of the widget factory.
  535. *
  536. * @param kernel - An optional existing kernel model.
  537. *
  538. * @returns A kernel preference.
  539. */
  540. getKernelPreference(
  541. path: string,
  542. widgetName: string,
  543. kernel?: Partial<Kernel.IModel>
  544. ): ISessionContext.IKernelPreference | undefined {
  545. widgetName = widgetName.toLowerCase();
  546. const widgetFactory = this._widgetFactories[widgetName];
  547. if (!widgetFactory) {
  548. return void 0;
  549. }
  550. const modelFactory = this.getModelFactory(
  551. widgetFactory.modelName || 'text'
  552. );
  553. if (!modelFactory) {
  554. return void 0;
  555. }
  556. const language = modelFactory.preferredLanguage(PathExt.basename(path));
  557. const name = kernel && kernel.name;
  558. const id = kernel && kernel.id;
  559. return {
  560. id,
  561. name,
  562. language,
  563. shouldStart: widgetFactory.preferKernel,
  564. canStart: widgetFactory.canStartKernel,
  565. shutdownOnDispose: widgetFactory.shutdownOnClose
  566. };
  567. }
  568. /**
  569. * Get the best file type given a contents model.
  570. *
  571. * @param model - The contents model of interest.
  572. *
  573. * @returns The best matching file type.
  574. */
  575. getFileTypeForModel(
  576. model: Partial<Contents.IModel>
  577. ): DocumentRegistry.IFileType {
  578. switch (model.type) {
  579. case 'directory':
  580. return (
  581. find(this._fileTypes, ft => ft.contentType === 'directory') ||
  582. DocumentRegistry.getDefaultDirectoryFileType(this.translator)
  583. );
  584. case 'notebook':
  585. return (
  586. find(this._fileTypes, ft => ft.contentType === 'notebook') ||
  587. DocumentRegistry.getDefaultNotebookFileType(this.translator)
  588. );
  589. default:
  590. // Find the best matching extension.
  591. if (model.name || model.path) {
  592. const name = model.name || PathExt.basename(model.path!);
  593. const fts = this.getFileTypesForPath(name);
  594. if (fts.length > 0) {
  595. return fts[0];
  596. }
  597. }
  598. return (
  599. this.getFileType('text') ||
  600. DocumentRegistry.getDefaultTextFileType(this.translator)
  601. );
  602. }
  603. }
  604. /**
  605. * Get the file types that match a file name.
  606. *
  607. * @param path - The path of the file.
  608. *
  609. * @returns An ordered list of matching file types.
  610. */
  611. getFileTypesForPath(path: string): DocumentRegistry.IFileType[] {
  612. const fts: DocumentRegistry.IFileType[] = [];
  613. const name = PathExt.basename(path);
  614. // Look for a pattern match first.
  615. let ft = find(this._fileTypes, ft => {
  616. return !!(ft.pattern && name.match(ft.pattern) !== null);
  617. });
  618. if (ft) {
  619. fts.push(ft);
  620. }
  621. // Then look by extension name, starting with the longest
  622. let ext = Private.extname(name);
  623. while (ext.length > 1) {
  624. ft = find(this._fileTypes, ft => ft.extensions.indexOf(ext) !== -1);
  625. if (ft) {
  626. fts.push(ft);
  627. }
  628. ext = '.' + ext.split('.').slice(2).join('.');
  629. }
  630. return fts;
  631. }
  632. protected translator: ITranslator;
  633. private _modelFactories: {
  634. [key: string]: DocumentRegistry.ModelFactory;
  635. } = Object.create(null);
  636. private _widgetFactories: {
  637. [key: string]: DocumentRegistry.WidgetFactory;
  638. } = Object.create(null);
  639. private _defaultWidgetFactory = '';
  640. private _defaultWidgetFactoryOverrides: {
  641. [key: string]: string;
  642. } = Object.create(null);
  643. private _defaultWidgetFactories: { [key: string]: string } = Object.create(
  644. null
  645. );
  646. private _defaultRenderedWidgetFactories: {
  647. [key: string]: string;
  648. } = Object.create(null);
  649. private _widgetFactoriesForFileType: {
  650. [key: string]: string[];
  651. } = Object.create(null);
  652. private _fileTypes: DocumentRegistry.IFileType[] = [];
  653. private _extenders: {
  654. [key: string]: DocumentRegistry.WidgetExtension[];
  655. } = Object.create(null);
  656. private _changed = new Signal<this, DocumentRegistry.IChangedArgs>(this);
  657. private _isDisposed = false;
  658. }
  659. /**
  660. * The namespace for the `DocumentRegistry` class statics.
  661. */
  662. export namespace DocumentRegistry {
  663. /**
  664. * The item to be added to document toolbar.
  665. */
  666. export interface IToolbarItem {
  667. name: string;
  668. widget: Widget;
  669. }
  670. /**
  671. * The options used to create a document registry.
  672. */
  673. export interface IOptions {
  674. /**
  675. * The text model factory for the registry. A default instance will
  676. * be used if not given.
  677. */
  678. textModelFactory?: ModelFactory;
  679. /**
  680. * The initial file types for the registry.
  681. * The [[DocumentRegistry.defaultFileTypes]] will be used if not given.
  682. */
  683. initialFileTypes?: DocumentRegistry.IFileType[];
  684. /**
  685. * The application language translator.
  686. */
  687. translator?: ITranslator;
  688. }
  689. /**
  690. * The interface for a document model.
  691. */
  692. export interface IModel extends IDisposable {
  693. /**
  694. * A signal emitted when the document content changes.
  695. */
  696. contentChanged: ISignal<this, void>;
  697. /**
  698. * A signal emitted when the model state changes.
  699. */
  700. stateChanged: ISignal<this, IChangedArgsGeneric<any>>;
  701. /**
  702. * The dirty state of the model.
  703. *
  704. * #### Notes
  705. * This should be cleared when the document is loaded from
  706. * or saved to disk.
  707. */
  708. dirty: boolean;
  709. /**
  710. * The read-only state of the model.
  711. */
  712. readOnly: boolean;
  713. /**
  714. * The default kernel name of the document.
  715. */
  716. readonly defaultKernelName: string;
  717. /**
  718. * The default kernel language of the document.
  719. */
  720. readonly defaultKernelLanguage: string;
  721. /**
  722. * The underlying `IModelDB` instance in which model
  723. * data is stored.
  724. *
  725. * ### Notes
  726. * Making direct edits to the values stored in the`IModelDB`
  727. * is not recommended, and may produce unpredictable results.
  728. */
  729. readonly modelDB: IModelDB;
  730. /**
  731. * The shared notebook model.
  732. */
  733. readonly sharedModel: models.ISharedDocument;
  734. /**
  735. * Serialize the model to a string.
  736. */
  737. toString(): string;
  738. /**
  739. * Deserialize the model from a string.
  740. *
  741. * #### Notes
  742. * Should emit a [contentChanged] signal.
  743. */
  744. fromString(value: string): void;
  745. /**
  746. * Serialize the model to JSON.
  747. */
  748. toJSON(): PartialJSONValue;
  749. /**
  750. * Deserialize the model from JSON.
  751. *
  752. * #### Notes
  753. * Should emit a [contentChanged] signal.
  754. */
  755. fromJSON(value: ReadonlyPartialJSONValue): void;
  756. /**
  757. * Initialize model state after initial data load.
  758. *
  759. * #### Notes
  760. * This function must be called after the initial data is loaded to set up
  761. * initial model state, such as an initial undo stack, etc.
  762. */
  763. initialize(): void;
  764. }
  765. /**
  766. * The interface for a document model that represents code.
  767. */
  768. export interface ICodeModel extends IModel, CodeEditor.IModel {
  769. sharedModel: models.ISharedFile;
  770. }
  771. /**
  772. * The document context object.
  773. */
  774. export interface IContext<T extends IModel> extends IDisposable {
  775. /**
  776. * A signal emitted when the path changes.
  777. */
  778. pathChanged: ISignal<this, string>;
  779. /**
  780. * A signal emitted when the contentsModel changes.
  781. */
  782. fileChanged: ISignal<this, Contents.IModel>;
  783. /**
  784. * A signal emitted on the start and end of a saving operation.
  785. */
  786. saveState: ISignal<this, SaveState>;
  787. /**
  788. * A signal emitted when the context is disposed.
  789. */
  790. disposed: ISignal<this, void>;
  791. /**
  792. * The data model for the document.
  793. */
  794. readonly model: T;
  795. /**
  796. * The session context object associated with the context.
  797. */
  798. readonly sessionContext: ISessionContext;
  799. /**
  800. * The current path associated with the document.
  801. */
  802. readonly path: string;
  803. /**
  804. * The current local path associated with the document.
  805. * If the document is in the default notebook file browser,
  806. * this is the same as the path.
  807. */
  808. readonly localPath: string;
  809. /**
  810. * The document metadata, stored as a services contents model.
  811. *
  812. * #### Notes
  813. * This will be null until the context is 'ready'. Since we only store
  814. * metadata here, the `.contents` attribute will always be empty.
  815. */
  816. readonly contentsModel: Contents.IModel | null;
  817. /**
  818. * The url resolver for the context.
  819. */
  820. readonly urlResolver: IRenderMime.IResolver;
  821. /**
  822. * Whether the context is ready.
  823. */
  824. readonly isReady: boolean;
  825. /**
  826. * A promise that is fulfilled when the context is ready.
  827. */
  828. readonly ready: Promise<void>;
  829. /**
  830. * Rename the document.
  831. */
  832. rename(newName: string): Promise<void>;
  833. /**
  834. * Save the document contents to disk.
  835. */
  836. save(manual?: boolean): Promise<void>;
  837. /**
  838. * Save the document to a different path chosen by the user.
  839. */
  840. saveAs(): Promise<void>;
  841. /**
  842. * Save the document to a different path chosen by the user.
  843. */
  844. download(): Promise<void>;
  845. /**
  846. * Revert the document contents to disk contents.
  847. */
  848. revert(): Promise<void>;
  849. /**
  850. * Create a checkpoint for the file.
  851. *
  852. * @returns A promise which resolves with the new checkpoint model when the
  853. * checkpoint is created.
  854. */
  855. createCheckpoint(): Promise<Contents.ICheckpointModel>;
  856. /**
  857. * Delete a checkpoint for the file.
  858. *
  859. * @param checkpointID - The id of the checkpoint to delete.
  860. *
  861. * @returns A promise which resolves when the checkpoint is deleted.
  862. */
  863. deleteCheckpoint(checkpointID: string): Promise<void>;
  864. /**
  865. * Restore the file to a known checkpoint state.
  866. *
  867. * @param checkpointID - The optional id of the checkpoint to restore,
  868. * defaults to the most recent checkpoint.
  869. *
  870. * @returns A promise which resolves when the checkpoint is restored.
  871. */
  872. restoreCheckpoint(checkpointID?: string): Promise<void>;
  873. /**
  874. * List available checkpoints for the file.
  875. *
  876. * @returns A promise which resolves with a list of checkpoint models for
  877. * the file.
  878. */
  879. listCheckpoints(): Promise<Contents.ICheckpointModel[]>;
  880. /**
  881. * Add a sibling widget to the document manager.
  882. *
  883. * @param widget - The widget to add to the document manager.
  884. *
  885. * @param options - The desired options for adding the sibling.
  886. *
  887. * @returns A disposable used to remove the sibling if desired.
  888. *
  889. * #### Notes
  890. * It is assumed that the widget has the same model and context
  891. * as the original widget.
  892. */
  893. addSibling(widget: Widget, options?: IOpenOptions): IDisposable;
  894. }
  895. export type SaveState =
  896. | 'started'
  897. | 'failed'
  898. | 'completed'
  899. | 'completed-manual';
  900. /**
  901. * A type alias for a context.
  902. */
  903. export type Context = IContext<IModel>;
  904. /**
  905. * A type alias for a code context.
  906. */
  907. export type CodeContext = IContext<ICodeModel>;
  908. /**
  909. * The options used to initialize a widget factory.
  910. */
  911. export interface IWidgetFactoryOptions<T extends Widget = Widget> {
  912. /**
  913. * The name of the widget to display in dialogs.
  914. */
  915. readonly name: string;
  916. /**
  917. * The file types the widget can view.
  918. */
  919. readonly fileTypes: ReadonlyArray<string>;
  920. /**
  921. * The file types for which the factory should be the default.
  922. */
  923. readonly defaultFor?: ReadonlyArray<string>;
  924. /**
  925. * The file types for which the factory should be the default for rendering,
  926. * if that is different than the default factory (which may be for editing).
  927. * If undefined, then it will fall back on the default file type.
  928. */
  929. readonly defaultRendered?: ReadonlyArray<string>;
  930. /**
  931. * Whether the widget factory is read only.
  932. */
  933. readonly readOnly?: boolean;
  934. /**
  935. * The registered name of the model type used to create the widgets.
  936. */
  937. readonly modelName?: string;
  938. /**
  939. * Whether the widgets prefer having a kernel started.
  940. */
  941. readonly preferKernel?: boolean;
  942. /**
  943. * Whether the widgets can start a kernel when opened.
  944. */
  945. readonly canStartKernel?: boolean;
  946. /**
  947. * Whether the kernel should be shutdown when the widget is closed.
  948. */
  949. readonly shutdownOnClose?: boolean;
  950. /**
  951. * The application language translator.
  952. */
  953. readonly translator?: ITranslator;
  954. /**
  955. * A function producing toolbar widgets, overriding the default toolbar widgets.
  956. */
  957. readonly toolbarFactory?: (widget: T) => DocumentRegistry.IToolbarItem[];
  958. }
  959. /**
  960. * The options used to open a widget.
  961. */
  962. export interface IOpenOptions {
  963. /**
  964. * The reference widget id for the insert location.
  965. *
  966. * The default is `null`.
  967. */
  968. ref?: string | null;
  969. /**
  970. * The supported insertion modes.
  971. *
  972. * An insert mode is used to specify how a widget should be added
  973. * to the main area relative to a reference widget.
  974. */
  975. mode?: DockLayout.InsertMode;
  976. /**
  977. * Whether to activate the widget. Defaults to `true`.
  978. */
  979. activate?: boolean;
  980. /**
  981. * The rank order of the widget among its siblings.
  982. *
  983. * #### Notes
  984. * This field may be used or ignored depending on shell implementation.
  985. */
  986. rank?: number;
  987. }
  988. /**
  989. * The interface for a widget factory.
  990. */
  991. export interface IWidgetFactory<T extends IDocumentWidget, U extends IModel>
  992. extends IDisposable,
  993. IWidgetFactoryOptions {
  994. /**
  995. * A signal emitted when a new widget is created.
  996. */
  997. widgetCreated: ISignal<IWidgetFactory<T, U>, T>;
  998. /**
  999. * Create a new widget given a context.
  1000. *
  1001. * @param source - A widget to clone
  1002. *
  1003. * #### Notes
  1004. * It should emit the [widgetCreated] signal with the new widget.
  1005. */
  1006. createNew(context: IContext<U>, source?: T): T;
  1007. }
  1008. /**
  1009. * A type alias for a standard widget factory.
  1010. */
  1011. export type WidgetFactory = IWidgetFactory<IDocumentWidget, IModel>;
  1012. /**
  1013. * An interface for a widget extension.
  1014. */
  1015. export interface IWidgetExtension<T extends Widget, U extends IModel> {
  1016. /**
  1017. * Create a new extension for a given widget.
  1018. */
  1019. createNew(widget: T, context: IContext<U>): IDisposable | void;
  1020. }
  1021. /**
  1022. * A type alias for a standard widget extension.
  1023. */
  1024. export type WidgetExtension = IWidgetExtension<Widget, IModel>;
  1025. /**
  1026. * The interface for a model factory.
  1027. */
  1028. export interface IModelFactory<T extends IModel> extends IDisposable {
  1029. /**
  1030. * The name of the model.
  1031. */
  1032. readonly name: string;
  1033. /**
  1034. * The content type of the file (defaults to `"file"`).
  1035. */
  1036. readonly contentType: Contents.ContentType;
  1037. /**
  1038. * The format of the file (defaults to `"text"`).
  1039. */
  1040. readonly fileFormat: Contents.FileFormat;
  1041. /**
  1042. * Create a new model for a given path.
  1043. *
  1044. * @param languagePreference - An optional kernel language preference.
  1045. * @param modelDB - An optional modelDB.
  1046. * @param isInitialized - An optional flag to check if the model is initialized.
  1047. *
  1048. * @returns A new document model.
  1049. */
  1050. createNew(
  1051. languagePreference?: string,
  1052. modelDB?: IModelDB,
  1053. isInitialized?: boolean
  1054. ): T;
  1055. /**
  1056. * Get the preferred kernel language given a file path.
  1057. */
  1058. preferredLanguage(path: string): string;
  1059. }
  1060. /**
  1061. * A type alias for a standard model factory.
  1062. */
  1063. export type ModelFactory = IModelFactory<IModel>;
  1064. /**
  1065. * A type alias for a code model factory.
  1066. */
  1067. export type CodeModelFactory = IModelFactory<ICodeModel>;
  1068. /**
  1069. * An interface for a file type.
  1070. */
  1071. export interface IFileType {
  1072. /**
  1073. * The name of the file type.
  1074. */
  1075. readonly name: string;
  1076. /**
  1077. * The mime types associated the file type.
  1078. */
  1079. readonly mimeTypes: ReadonlyArray<string>;
  1080. /**
  1081. * The extensions of the file type (e.g. `".txt"`). Can be a compound
  1082. * extension (e.g. `".table.json`).
  1083. */
  1084. readonly extensions: ReadonlyArray<string>;
  1085. /**
  1086. * An optional display name for the file type.
  1087. */
  1088. readonly displayName?: string;
  1089. /**
  1090. * An optional pattern for a file name (e.g. `^Dockerfile$`).
  1091. */
  1092. readonly pattern?: string;
  1093. /**
  1094. * The icon for the file type.
  1095. */
  1096. readonly icon?: LabIcon;
  1097. /**
  1098. * The icon class name for the file type.
  1099. */
  1100. readonly iconClass?: string;
  1101. /**
  1102. * The icon label for the file type.
  1103. */
  1104. readonly iconLabel?: string;
  1105. /**
  1106. * The content type of the new file.
  1107. */
  1108. readonly contentType: Contents.ContentType;
  1109. /**
  1110. * The format of the new file.
  1111. */
  1112. readonly fileFormat: Contents.FileFormat;
  1113. }
  1114. /**
  1115. * An arguments object for the `changed` signal.
  1116. */
  1117. export interface IChangedArgs {
  1118. /**
  1119. * The type of the changed item.
  1120. */
  1121. readonly type:
  1122. | 'widgetFactory'
  1123. | 'modelFactory'
  1124. | 'widgetExtension'
  1125. | 'fileType';
  1126. /**
  1127. * The name of the item or the widget factory being extended.
  1128. */
  1129. readonly name?: string;
  1130. /**
  1131. * Whether the item was added or removed.
  1132. */
  1133. readonly change: 'added' | 'removed';
  1134. }
  1135. /**
  1136. * The defaults used for a file type.
  1137. *
  1138. * @param translator - The application language translator.
  1139. *
  1140. * @returns The default file type.
  1141. */
  1142. export function getFileTypeDefaults(translator?: ITranslator): IFileType {
  1143. translator = translator || nullTranslator;
  1144. const trans = translator?.load('jupyterlab');
  1145. return {
  1146. name: 'default',
  1147. displayName: trans.__('default'),
  1148. extensions: [],
  1149. mimeTypes: [],
  1150. contentType: 'file',
  1151. fileFormat: 'text'
  1152. };
  1153. }
  1154. /**
  1155. * The default text file type used by the document registry.
  1156. *
  1157. * @param translator - The application language translator.
  1158. *
  1159. * @returns The default text file type.
  1160. */
  1161. export function getDefaultTextFileType(translator?: ITranslator): IFileType {
  1162. translator = translator || nullTranslator;
  1163. const trans = translator?.load('jupyterlab');
  1164. const fileTypeDefaults = getFileTypeDefaults(translator);
  1165. return {
  1166. ...fileTypeDefaults,
  1167. name: 'text',
  1168. displayName: trans.__('Text'),
  1169. mimeTypes: ['text/plain'],
  1170. extensions: ['.txt'],
  1171. icon: fileIcon
  1172. };
  1173. }
  1174. /**
  1175. * The default notebook file type used by the document registry.
  1176. *
  1177. * @param translator - The application language translator.
  1178. *
  1179. * @returns The default notebook file type.
  1180. */
  1181. export function getDefaultNotebookFileType(
  1182. translator?: ITranslator
  1183. ): IFileType {
  1184. translator = translator || nullTranslator;
  1185. const trans = translator?.load('jupyterlab');
  1186. return {
  1187. ...getFileTypeDefaults(translator),
  1188. name: 'notebook',
  1189. displayName: trans.__('Notebook'),
  1190. mimeTypes: ['application/x-ipynb+json'],
  1191. extensions: ['.ipynb'],
  1192. contentType: 'notebook',
  1193. fileFormat: 'json',
  1194. icon: notebookIcon
  1195. };
  1196. }
  1197. /**
  1198. * The default directory file type used by the document registry.
  1199. *
  1200. * @param translator - The application language translator.
  1201. *
  1202. * @returns The default directory file type.
  1203. */
  1204. export function getDefaultDirectoryFileType(
  1205. translator?: ITranslator
  1206. ): IFileType {
  1207. translator = translator || nullTranslator;
  1208. const trans = translator?.load('jupyterlab');
  1209. return {
  1210. ...getFileTypeDefaults(translator),
  1211. name: 'directory',
  1212. displayName: trans.__('Directory'),
  1213. extensions: [],
  1214. mimeTypes: ['text/directory'],
  1215. contentType: 'directory',
  1216. icon: folderIcon
  1217. };
  1218. }
  1219. /**
  1220. * The default file types used by the document registry.
  1221. *
  1222. * @param translator - The application language translator.
  1223. *
  1224. * @returns The default directory file types.
  1225. */
  1226. export function getDefaultFileTypes(
  1227. translator?: ITranslator
  1228. ): ReadonlyArray<Partial<IFileType>> {
  1229. translator = translator || nullTranslator;
  1230. const trans = translator?.load('jupyterlab');
  1231. return [
  1232. getDefaultTextFileType(translator),
  1233. getDefaultNotebookFileType(translator),
  1234. getDefaultDirectoryFileType(translator),
  1235. {
  1236. name: 'markdown',
  1237. displayName: trans.__('Markdown File'),
  1238. extensions: ['.md'],
  1239. mimeTypes: ['text/markdown'],
  1240. icon: markdownIcon
  1241. },
  1242. {
  1243. name: 'PDF',
  1244. displayName: trans.__('PDF File'),
  1245. extensions: ['.pdf'],
  1246. mimeTypes: ['application/pdf'],
  1247. icon: pdfIcon
  1248. },
  1249. {
  1250. name: 'python',
  1251. displayName: trans.__('Python File'),
  1252. extensions: ['.py'],
  1253. mimeTypes: ['text/x-python'],
  1254. icon: pythonIcon
  1255. },
  1256. {
  1257. name: 'json',
  1258. displayName: trans.__('JSON File'),
  1259. extensions: ['.json'],
  1260. mimeTypes: ['application/json'],
  1261. icon: jsonIcon
  1262. },
  1263. {
  1264. name: 'csv',
  1265. displayName: trans.__('CSV File'),
  1266. extensions: ['.csv'],
  1267. mimeTypes: ['text/csv'],
  1268. icon: spreadsheetIcon
  1269. },
  1270. {
  1271. name: 'tsv',
  1272. displayName: trans.__('TSV File'),
  1273. extensions: ['.tsv'],
  1274. mimeTypes: ['text/csv'],
  1275. icon: spreadsheetIcon
  1276. },
  1277. {
  1278. name: 'r',
  1279. displayName: trans.__('R File'),
  1280. mimeTypes: ['text/x-rsrc'],
  1281. extensions: ['.r'],
  1282. icon: rKernelIcon
  1283. },
  1284. {
  1285. name: 'yaml',
  1286. displayName: trans.__('YAML File'),
  1287. mimeTypes: ['text/x-yaml', 'text/yaml'],
  1288. extensions: ['.yaml', '.yml'],
  1289. icon: yamlIcon
  1290. },
  1291. {
  1292. name: 'svg',
  1293. displayName: trans.__('Image'),
  1294. mimeTypes: ['image/svg+xml'],
  1295. extensions: ['.svg'],
  1296. icon: imageIcon,
  1297. fileFormat: 'base64'
  1298. },
  1299. {
  1300. name: 'tiff',
  1301. displayName: trans.__('Image'),
  1302. mimeTypes: ['image/tiff'],
  1303. extensions: ['.tif', '.tiff'],
  1304. icon: imageIcon,
  1305. fileFormat: 'base64'
  1306. },
  1307. {
  1308. name: 'jpeg',
  1309. displayName: trans.__('Image'),
  1310. mimeTypes: ['image/jpeg'],
  1311. extensions: ['.jpg', '.jpeg'],
  1312. icon: imageIcon,
  1313. fileFormat: 'base64'
  1314. },
  1315. {
  1316. name: 'gif',
  1317. displayName: trans.__('Image'),
  1318. mimeTypes: ['image/gif'],
  1319. extensions: ['.gif'],
  1320. icon: imageIcon,
  1321. fileFormat: 'base64'
  1322. },
  1323. {
  1324. name: 'png',
  1325. displayName: trans.__('Image'),
  1326. mimeTypes: ['image/png'],
  1327. extensions: ['.png'],
  1328. icon: imageIcon,
  1329. fileFormat: 'base64'
  1330. },
  1331. {
  1332. name: 'bmp',
  1333. displayName: trans.__('Image'),
  1334. mimeTypes: ['image/bmp'],
  1335. extensions: ['.bmp'],
  1336. icon: imageIcon,
  1337. fileFormat: 'base64'
  1338. }
  1339. ];
  1340. }
  1341. }
  1342. /**
  1343. * An interface for a document widget.
  1344. */
  1345. export interface IDocumentWidget<
  1346. T extends Widget = Widget,
  1347. U extends DocumentRegistry.IModel = DocumentRegistry.IModel
  1348. > extends Widget {
  1349. /**
  1350. * The content widget.
  1351. */
  1352. readonly content: T;
  1353. /**
  1354. * A promise resolving after the content widget is revealed.
  1355. */
  1356. readonly revealed: Promise<void>;
  1357. /**
  1358. * The context associated with the document.
  1359. */
  1360. readonly context: DocumentRegistry.IContext<U>;
  1361. /**
  1362. * The toolbar for the widget.
  1363. */
  1364. readonly toolbar: Toolbar<Widget>;
  1365. /**
  1366. * Set URI fragment identifier.
  1367. */
  1368. setFragment(fragment: string): void;
  1369. shouldNameFile?: Signal<this, void>;
  1370. }
  1371. /**
  1372. * A private namespace for DocumentRegistry data.
  1373. */
  1374. namespace Private {
  1375. /**
  1376. * Get the extension name of a path.
  1377. *
  1378. * @param file - string.
  1379. *
  1380. * #### Notes
  1381. * Dotted filenames (e.g. `".table.json"` are allowed).
  1382. */
  1383. export function extname(path: string): string {
  1384. const parts = PathExt.basename(path).split('.');
  1385. parts.shift();
  1386. const ext = '.' + parts.join('.');
  1387. return ext.toLowerCase();
  1388. }
  1389. /**
  1390. * A no-op function.
  1391. */
  1392. export function noOp(): void {
  1393. /* no-op */
  1394. }
  1395. }