index.ts 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355
  1. // Copyright (c) Jupyter Development Team.
  2. // Distributed under the terms of the Modified BSD License.
  3. import {
  4. ILabShell,
  5. JupyterFrontEnd,
  6. JupyterFrontEndPlugin
  7. } from '@jupyterlab/application';
  8. import {
  9. ISessionContext,
  10. ICommandPalette,
  11. ISessionContextDialogs,
  12. sessionContextDialogs
  13. } from '@jupyterlab/apputils';
  14. import { Cell, CodeCell } from '@jupyterlab/cells';
  15. import {
  16. CodeConsole,
  17. ConsolePanel,
  18. IConsoleTracker
  19. } from '@jupyterlab/console';
  20. import { IDocumentWidget } from '@jupyterlab/docregistry';
  21. import { FileEditor, IEditorTracker } from '@jupyterlab/fileeditor';
  22. import {
  23. INotebookTracker,
  24. Notebook,
  25. NotebookPanel
  26. } from '@jupyterlab/notebook';
  27. import {
  28. IStatusBar,
  29. KernelStatus,
  30. LineCol,
  31. MemoryUsage,
  32. RunningSessions,
  33. StatusBar
  34. } from '@jupyterlab/statusbar';
  35. import { IMainMenu } from '@jupyterlab/mainmenu';
  36. import { ISettingRegistry } from '@jupyterlab/settingregistry';
  37. import { Title, Widget } from '@lumino/widgets';
  38. export const STATUSBAR_PLUGIN_ID = '@jupyterlab/statusbar-extension:plugin';
  39. /**
  40. * Initialization data for the statusbar extension.
  41. */
  42. const statusBar: JupyterFrontEndPlugin<IStatusBar> = {
  43. id: STATUSBAR_PLUGIN_ID,
  44. provides: IStatusBar,
  45. autoStart: true,
  46. activate: (
  47. app: JupyterFrontEnd,
  48. labShell: ILabShell | null,
  49. settingRegistry: ISettingRegistry | null,
  50. mainMenu: IMainMenu | null,
  51. palette: ICommandPalette | null
  52. ) => {
  53. const statusBar = new StatusBar();
  54. statusBar.id = 'jp-main-statusbar';
  55. app.shell.add(statusBar, 'bottom');
  56. // If available, connect to the shell's layout modified signal.
  57. if (labShell) {
  58. labShell.layoutModified.connect(() => {
  59. statusBar.update();
  60. });
  61. }
  62. const category: string = 'Main Area';
  63. const command: string = 'statusbar:toggle';
  64. app.commands.addCommand(command, {
  65. label: 'Show Status Bar',
  66. execute: (args: any) => {
  67. statusBar.setHidden(statusBar.isVisible);
  68. if (settingRegistry) {
  69. void settingRegistry.set(
  70. STATUSBAR_PLUGIN_ID,
  71. 'visible',
  72. statusBar.isVisible
  73. );
  74. }
  75. },
  76. isToggled: () => statusBar.isVisible
  77. });
  78. if (palette) {
  79. palette.addItem({ command, category });
  80. }
  81. if (mainMenu) {
  82. mainMenu.viewMenu.addGroup([{ command }], 1);
  83. }
  84. if (settingRegistry) {
  85. const updateSettings = (settings: ISettingRegistry.ISettings): void => {
  86. const visible = settings.get('visible').composite as boolean;
  87. statusBar.setHidden(!visible);
  88. };
  89. Promise.all([settingRegistry.load(STATUSBAR_PLUGIN_ID), app.restored])
  90. .then(([settings]) => {
  91. updateSettings(settings);
  92. settings.changed.connect(settings => {
  93. updateSettings(settings);
  94. });
  95. })
  96. .catch((reason: Error) => {
  97. console.error(reason.message);
  98. });
  99. }
  100. return statusBar;
  101. },
  102. optional: [ILabShell, ISettingRegistry, IMainMenu, ICommandPalette]
  103. };
  104. /**
  105. * A plugin that provides a kernel status item to the status bar.
  106. */
  107. export const kernelStatus: JupyterFrontEndPlugin<void> = {
  108. id: '@jupyterlab/statusbar-extension:kernel-status',
  109. autoStart: true,
  110. requires: [IStatusBar, INotebookTracker, IConsoleTracker, ILabShell],
  111. optional: [ISessionContextDialogs],
  112. activate: (
  113. app: JupyterFrontEnd,
  114. statusBar: IStatusBar,
  115. notebookTracker: INotebookTracker,
  116. consoleTracker: IConsoleTracker,
  117. labShell: ILabShell,
  118. sessionDialogs: ISessionContextDialogs | null
  119. ) => {
  120. // When the status item is clicked, launch the kernel
  121. // selection dialog for the current session.
  122. let currentSession: ISessionContext | null = null;
  123. const changeKernel = async () => {
  124. if (!currentSession) {
  125. return;
  126. }
  127. await (sessionDialogs || sessionContextDialogs).selectKernel(
  128. currentSession
  129. );
  130. };
  131. // Create the status item.
  132. const item = new KernelStatus({
  133. onClick: changeKernel
  134. });
  135. // When the title of the active widget changes, update the label
  136. // of the hover text.
  137. const onTitleChanged = (title: Title<Widget>) => {
  138. item.model!.activityName = title.label;
  139. };
  140. // Keep the session object on the status item up-to-date.
  141. labShell.currentChanged.connect((_, change) => {
  142. const { oldValue, newValue } = change;
  143. // Clean up after the old value if it exists,
  144. // listen for changes to the title of the activity
  145. if (oldValue) {
  146. oldValue.title.changed.disconnect(onTitleChanged);
  147. }
  148. if (newValue) {
  149. newValue.title.changed.connect(onTitleChanged);
  150. }
  151. // Grab the session off of the current widget, if it exists.
  152. if (newValue && consoleTracker.has(newValue)) {
  153. currentSession = (newValue as ConsolePanel).sessionContext;
  154. } else if (newValue && notebookTracker.has(newValue)) {
  155. currentSession = (newValue as NotebookPanel).sessionContext;
  156. } else {
  157. currentSession = null;
  158. }
  159. item.model!.sessionContext = currentSession;
  160. });
  161. statusBar.registerStatusItem(
  162. '@jupyterlab/statusbar-extension:kernel-status',
  163. {
  164. item,
  165. align: 'left',
  166. rank: 1,
  167. isActive: () => {
  168. const current = labShell.currentWidget;
  169. return (
  170. !!current &&
  171. (notebookTracker.has(current) || consoleTracker.has(current))
  172. );
  173. }
  174. }
  175. );
  176. }
  177. };
  178. /**
  179. * A plugin providing a line/column status item to the application.
  180. */
  181. export const lineColItem: JupyterFrontEndPlugin<void> = {
  182. id: '@jupyterlab/statusbar-extension:line-col-status',
  183. autoStart: true,
  184. requires: [
  185. IStatusBar,
  186. INotebookTracker,
  187. IEditorTracker,
  188. IConsoleTracker,
  189. ILabShell
  190. ],
  191. activate: (
  192. _: JupyterFrontEnd,
  193. statusBar: IStatusBar,
  194. notebookTracker: INotebookTracker,
  195. editorTracker: IEditorTracker,
  196. consoleTracker: IConsoleTracker,
  197. labShell: ILabShell
  198. ) => {
  199. const item = new LineCol();
  200. const onActiveCellChanged = (notebook: Notebook, cell: Cell) => {
  201. item.model!.editor = cell && cell.editor;
  202. };
  203. const onPromptCreated = (console: CodeConsole, prompt: CodeCell) => {
  204. item.model!.editor = prompt && prompt.editor;
  205. };
  206. labShell.currentChanged.connect((_, change) => {
  207. const { oldValue, newValue } = change;
  208. // Check if we need to disconnect the console listener
  209. // or the notebook active cell listener
  210. if (oldValue && consoleTracker.has(oldValue)) {
  211. (oldValue as ConsolePanel).console.promptCellCreated.disconnect(
  212. onPromptCreated
  213. );
  214. } else if (oldValue && notebookTracker.has(oldValue)) {
  215. (oldValue as NotebookPanel).content.activeCellChanged.disconnect(
  216. onActiveCellChanged
  217. );
  218. }
  219. // Wire up the new editor to the model if it exists
  220. if (newValue && consoleTracker.has(newValue)) {
  221. (newValue as ConsolePanel).console.promptCellCreated.connect(
  222. onPromptCreated
  223. );
  224. const prompt = (newValue as ConsolePanel).console.promptCell;
  225. item.model!.editor = prompt && prompt.editor;
  226. } else if (newValue && notebookTracker.has(newValue)) {
  227. (newValue as NotebookPanel).content.activeCellChanged.connect(
  228. onActiveCellChanged
  229. );
  230. const cell = (newValue as NotebookPanel).content.activeCell;
  231. item.model!.editor = cell && cell.editor;
  232. } else if (newValue && editorTracker.has(newValue)) {
  233. item.model!.editor = (newValue as IDocumentWidget<
  234. FileEditor
  235. >).content.editor;
  236. } else {
  237. item.model!.editor = null;
  238. }
  239. });
  240. // Add the status item to the status bar.
  241. statusBar.registerStatusItem(
  242. '@jupyterlab/statusbar-extension:line-col-status',
  243. {
  244. item,
  245. align: 'right',
  246. rank: 2,
  247. isActive: () => {
  248. const current = labShell.currentWidget;
  249. return (
  250. !!current &&
  251. (notebookTracker.has(current) ||
  252. editorTracker.has(current) ||
  253. consoleTracker.has(current))
  254. );
  255. }
  256. }
  257. );
  258. }
  259. };
  260. /**
  261. * A plugin providing memory usage statistics to the application.
  262. *
  263. * #### Notes
  264. * This plugin will not work unless the memory usage server extension
  265. * is installed.
  266. */
  267. export const memoryUsageItem: JupyterFrontEndPlugin<void> = {
  268. id: '@jupyterlab/statusbar-extension:memory-usage-status',
  269. autoStart: true,
  270. requires: [IStatusBar],
  271. activate: (app: JupyterFrontEnd, statusBar: IStatusBar) => {
  272. let item = new MemoryUsage();
  273. statusBar.registerStatusItem(
  274. '@jupyterlab/statusbar-extension:memory-usage-status',
  275. {
  276. item,
  277. align: 'left',
  278. rank: 2,
  279. isActive: () => item.model!.metricsAvailable,
  280. activeStateChanged: item.model!.stateChanged
  281. }
  282. );
  283. }
  284. };
  285. /*
  286. * A plugin providing running terminals and sessions information
  287. * to the status bar.
  288. */
  289. export const runningSessionsItem: JupyterFrontEndPlugin<void> = {
  290. id: '@jupyterlab/statusbar-extension:running-sessions-status',
  291. autoStart: true,
  292. requires: [IStatusBar],
  293. activate: (app: JupyterFrontEnd, statusBar: IStatusBar) => {
  294. const item = new RunningSessions({
  295. onClick: () => app.shell.activateById('jp-running-sessions'),
  296. serviceManager: app.serviceManager
  297. });
  298. statusBar.registerStatusItem(
  299. '@jupyterlab/statusbar-extension:running-sessions-status',
  300. {
  301. item,
  302. align: 'left',
  303. rank: 0
  304. }
  305. );
  306. }
  307. };
  308. const plugins: JupyterFrontEndPlugin<any>[] = [
  309. statusBar,
  310. lineColItem,
  311. kernelStatus,
  312. runningSessionsItem,
  313. memoryUsageItem
  314. ];
  315. export default plugins;