plugin.ts 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. AttachedProperty
  5. } from 'phosphor/lib/core/properties';
  6. import {
  7. JupyterLab, JupyterLabPlugin
  8. } from '../application';
  9. import {
  10. InstanceTracker
  11. } from '../common/instancetracker';
  12. import {
  13. IDocumentRegistry
  14. } from '../docregistry';
  15. import {
  16. ILayoutRestorer
  17. } from '../layoutrestorer';
  18. import {
  19. IEditorTracker, EditorWidget, EditorWidgetFactory
  20. } from './widget';
  21. import {
  22. IEditorServices
  23. } from '../codeeditor';
  24. /**
  25. * The class name for all main area portrait tab icons.
  26. */
  27. const PORTRAIT_ICON_CLASS = 'jp-MainAreaPortraitIcon';
  28. /**
  29. * The class name for the text editor icon from the default theme.
  30. */
  31. const EDITOR_ICON_CLASS = 'jp-ImageTextEditor';
  32. /**
  33. * The name of the factory that creates editor widgets.
  34. */
  35. const FACTORY = 'Editor';
  36. /**
  37. * The map of command ids used by the editor.
  38. */
  39. const cmdIds = {
  40. lineNumbers: 'editor:line-numbers',
  41. lineWrap: 'editor:line-wrap',
  42. createConsole: 'editor:create-console',
  43. runCode: 'editor:run-code'
  44. };
  45. /**
  46. * The editor handler extension.
  47. */
  48. const plugin: JupyterLabPlugin<IEditorTracker> = {
  49. id: 'jupyter.services.editor-handler',
  50. requires: [IDocumentRegistry, ILayoutRestorer, IEditorServices],
  51. provides: IEditorTracker,
  52. activate: activateEditorHandler,
  53. autoStart: true
  54. };
  55. /**
  56. * Export the plugin as default.
  57. */
  58. export default plugin;
  59. /**
  60. * Sets up the editor widget
  61. */
  62. function activateEditorHandler(app: JupyterLab, registry: IDocumentRegistry, layout: ILayoutRestorer, editorServices: IEditorServices): IEditorTracker {
  63. const factory = new EditorWidgetFactory({
  64. editorServices,
  65. factoryOptions: {
  66. name: FACTORY,
  67. fileExtensions: ['*'],
  68. defaultFor: ['*']
  69. }
  70. });
  71. const tracker = new InstanceTracker<EditorWidget>({ namespace: 'editor' });
  72. // Handle state restoration.
  73. layout.restore(tracker, {
  74. command: 'file-operations:open',
  75. args: widget => ({ path: widget.context.path, factory: FACTORY }),
  76. name: widget => widget.context.path
  77. });
  78. // Sync tracker with currently focused widget.
  79. app.shell.currentChanged.connect((sender, args) => {
  80. tracker.sync(args.newValue);
  81. });
  82. factory.widgetCreated.connect((sender, widget) => {
  83. widget.title.icon = `${PORTRAIT_ICON_CLASS} ${EDITOR_ICON_CLASS}`;
  84. // Notify the instance tracker if restore data needs to update.
  85. widget.context.pathChanged.connect(() => { tracker.save(widget); });
  86. tracker.add(widget);
  87. });
  88. registry.addWidgetFactory(factory);
  89. /**
  90. * Toggle editor line numbers
  91. */
  92. function toggleLineNums() {
  93. if (tracker.currentWidget) {
  94. let editor = tracker.currentWidget.editor;
  95. editor.lineNumbers = !editor.lineNumbers;
  96. }
  97. }
  98. /**
  99. * Toggle editor line wrap
  100. */
  101. function toggleLineWrap() {
  102. if (tracker.currentWidget) {
  103. let editor = tracker.currentWidget.editor;
  104. editor.wordWrap = !editor.wordWrap;
  105. }
  106. }
  107. /**
  108. * An attached property for the session id associated with an editor widget.
  109. */
  110. const sessionIdProperty = new AttachedProperty<EditorWidget, string>({
  111. name: 'sessionId'
  112. });
  113. let commands = app.commands;
  114. commands.addCommand(cmdIds.lineNumbers, {
  115. execute: () => { toggleLineNums(); },
  116. label: 'Toggle Line Numbers'
  117. });
  118. commands.addCommand(cmdIds.lineWrap, {
  119. execute: () => { toggleLineWrap(); },
  120. label: 'Toggle Line Wrap'
  121. });
  122. commands.addCommand(cmdIds.createConsole, {
  123. execute: () => {
  124. let widget = tracker.currentWidget;
  125. if (!widget) {
  126. return;
  127. }
  128. let options: any = {
  129. path: widget.context.path,
  130. preferredLanguage: widget.context.model.defaultKernelLanguage
  131. };
  132. return commands.execute('console:create', options)
  133. .then(id => { sessionIdProperty.set(widget, id); });
  134. },
  135. label: 'Create Console for Editor'
  136. });
  137. commands.addCommand(cmdIds.runCode, {
  138. execute: () => {
  139. let widget = tracker.currentWidget;
  140. if (!widget) {
  141. return;
  142. }
  143. // Get the session id.
  144. let id = sessionIdProperty.get(widget);
  145. if (!id) {
  146. return;
  147. }
  148. // Get the selected code from the editor.
  149. const editorModel = widget.editor.model;
  150. const selection = widget.editor.getSelection();
  151. const start = editorModel.getOffsetAt(selection.start);
  152. const end = editorModel.getOffsetAt(selection.end);
  153. const code = editorModel.value.text.substring(start, end);
  154. return commands.execute('console:inject', { id, code });
  155. },
  156. label: 'Run Code'
  157. });
  158. return tracker;
  159. }