Ver Fonte

Merge branch 'master' into keyboard-shortcuts

Afshin Darian há 6 anos atrás
pai
commit
935da45339

+ 99 - 0
docs/source/developer/extension_dev.rst

@@ -74,6 +74,15 @@ The default plugins in the JupyterLab application include:
 A dependency graph for the core JupyterLab plugins (along with links to
 their source) is shown here: |dependencies|
 
+.. danger::
+
+    Installing an extension allows for arbitrary code execution on the
+    server, kernel, and in the client's browser. You should therefore
+    take steps to protect against malicious changes to your extension's
+    code. This includes ensuring strong authentication for your npm
+    account.
+
+
 Application Object
 ~~~~~~~~~~~~~~~~~~
 
@@ -126,6 +135,9 @@ meets the following criteria:
    ``"extension"`` metadata. The value can be ``true`` to use the main
    module of the package, or a string path to a specific module (e.g.
    ``"lib/foo"``).
+-  It is also recommended to include the keyword `jupyterlab-extension`
+   in the package.json, to aid with discovery (e.g. by the extension
+   manager).
 
 While authoring the extension, you can use the command:
 
@@ -477,3 +489,90 @@ This would look something like the following in a ``Widget`` subclass:
       event.stopPropagation();
 
 .. |dependencies| image:: dependency-graph.svg
+
+
+
+.. _ext-author-companion-packages:
+
+Companion Packages
+^^^^^^^^^^^^^^^^^^
+
+If your extensions depends on the presence of one or more packages in the
+kernel, or on a notebook server extension, you can add metadata to indicate
+this to the extension manager by adding metadata to your package.json file.
+The full options available are::
+
+    "jupyterlab": {
+      "discovery": {
+        "kernel": [
+          {
+            "kernel_spec": {
+              "language": "<regexp for matching kernel language>",
+              "display_name": "<regexp for matching kernel display name>"   // optional
+            },
+            "base": {
+              "name": "<the name of the kernel package>"
+            },
+            "overrides": {   // optional
+              "<manager name, e.g. 'pip'>": {
+                "name": "<name of kernel package on pip, if it differs from base name>"
+              }
+            },
+            "managers": [   // list of package managers that have your kernel package
+                "pip",
+                "conda"
+            ]
+          }
+        ],
+        "server": {
+          "base": {
+            "name": "<the name of the server extension package>"
+          },
+          "overrides": {   // optional
+            "<manager name, e.g. 'pip'>": {
+              "name": "<name of server extension package on pip, if it differs from base name>"
+            }
+          },
+          "managers": [   // list of package managers that have your server extension package
+              "pip",
+              "conda"
+          ]
+        }
+      }
+    }
+
+
+A typical setup for e.g. a jupyter-widget based package will then be::
+
+    "keywords": [
+        "jupyterlab-extension",
+        "jupyter",
+        "widgets",
+        "jupyterlab"
+    ],
+    "jupyterlab": {
+      "extension": true,
+      "discovery": {
+        "kernel": [
+          {
+            "kernel_spec": {
+              "language": "^python",
+            },
+            "base": {
+              "name": "myipywidgetspackage"
+            },
+            "managers": [
+                "pip",
+                "conda"
+            ]
+          }
+        ]
+      }
+    }
+
+
+Currently supported package managers are:
+
+- pip
+- conda
+

+ 139 - 12
docs/source/user/extensions.rst

@@ -13,12 +13,12 @@ JupyterLab itself is simply a collection of extensions that are no more powerful
 or privileged than any custom extension.
 
 JupyterLab extensions are `npm <https://www.npmjs.com/>`__ packages (the
-standard package format in Javascript development). There are many
-community-developed extensions being built on GitHub. You can search for the
-GitHub topic `jupyterlab-extension
-<https://github.com/topics/jupyterlab-extension>`__ to find extensions. For
-information about developing extensions, see the :ref:`developer documentation
-<developer_extensions>`.
+standard package format in Javascript development). You can search for the
+keyword `jupyterlab-extension
+<https://www.npmjs.com/search?q=keywords%3Ajupyterlab-extension`__ on the
+npm registry to find extensions. For information about developing extensions,
+see the :ref:`developer documentation <developer_extensions>`.
+
 
 .. note::
 
@@ -44,12 +44,138 @@ If you use `Homebrew <https://brew.sh/>`__ on Mac OS X:
 You can also download Node.js from the `Node.js website <https://nodejs.org/>`__ and
 install it directly.
 
-Installing Extensions
+
+Using the Extension Manager
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+To manage your extensions, you can use the extension manager. By default, the
+manager is disabled, but you can enable it with the following steps:
+
+
+   - Go into advanced settings editor.
+   - Open the Extension Manager section.
+   - Add the entry "enabled": true.
+   - Save the settings.
+   - If prompted whether you are sure, read the warning, and click "Enable"
+     if you are still sure.
+
+Once enabled, you should see a new tab appear in the :ref:`left sidebar <left-sidebar>`.
+
+
+.. figure:: images/extension_manager_default.png
+   :align: center
+   :class: jp-screenshot
+
+   **Figure:** The default view has three components: a search bar, an "Installed"
+   section, and a "Discover" section.
+
+
+Finding Extensions
+^^^^^^^^^^^^^^^^^^
+
+You can use the extension manager to find extensions for JupyterLab. To discovery
+freely among the currently available extensions, expand the "Discovery" section.
+This triggers a search for all JupyterLab extensions on the NPM registry, and
+the results are listed according to the `registry's sort order
+<https://docs.npmjs.com/searching-for-and-choosing-packages-to-download#package-search-rank-criteria>`__.
+An exception to this sort order is that extensions released by the Jupyter
+organization are always placed first. These extensions are distinguished by
+a small Jupyter icon next to their name.
+
+
+.. image:: images/extension_manager_discover.png
+   :align: center
+   :class: jp-screenshot
+   :alt: Screenshot showing the discovery extension listing.
+
+
+Alternatively, you can limit your discovery by using the search bar. This
+performs a free-text search of JupyterLab extensions on the NPM registry.
+
+.. image:: images/extension_manager_search.png
+   :align: center
+   :class: jp-screenshot
+   :alt: Screenshot showing an example search result
+
+
+Installing an Extension
+^^^^^^^^^^^^^^^^^^^^^^^
+
+Once you have found an extension that you think is interesting, install
+it by clicking the "Install" button of the extension list entry.
+
+
+.. danger::
+
+    Installing an extension allows it to execute arbitrary code on the
+    server, kernel, and in the client's browser. You should therefore
+    avoid installing extensions you do not trust, and watch out for
+    any extensions trying to masquerade as a trusted extension.
+
+
+A short while after starting the install of an extension, a drop-down should
+appear under the search bar indicating that the extension has been
+downloaded, but that a rebuild is needed to complete the installation.
+
+
+.. image:: images/extension_manager_rebuild.png
+   :align: center
+   :class: jp-screenshot
+   :alt: Screenshot showing the rebuild indicator
+
+
+If you want to install/uninstall other extensions as well, you can ignore
+the rebuild notice until you have made all the changes you want. Once satisfied,
+click the 'Rebuild' button to start a rebuild in the background.
+Once the rebuild completes, a dialog will pop up, indicating that a reload of
+the page is needed in order to load the latest build into the browser.
+
+If you ignore the rebuild notice by mistake, simply refresh your browser
+window to trigger a new rebuild check.
+
+
+Managing Installed Extensions
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+When there are some installed extensions, they will be shown in the "Installed"
+section. These can then be uninstalled or disabled. Disabling an extension will
+prevent it from being activated, but without rebuilding the application.
+
+
+Companion packages
+^^^^^^^^^^^^^^^^^^
+
+During installation of an extension, JupyterLab will inspect the package
+metadata for any
+:ref:`instructions on companion packages<ext-author-companion-packages>`.
+Companion packages can be:
+
+   - Notebook server extensions (or any other packages that need to be
+     installed on the Notebook server).
+   - Kernel packages. An example of companion packages for the
+     kernel are Jupyter Widget packages, like the `ipywidgets`_
+     Python package for the
+     `@jupyter-widgets/jupyterlab-manager package`_.
+
+If JupyterLab finds instructions for companion packages, it will present
+a dialog to notify you about these. These are informational only, and it
+will be up to you to take these into account or not.
+
+
+
+Using the Terminal
 ~~~~~~~~~~~~~~~~~~~~~
 
-The base JupyterLab application includes a core set of extensions, which
-provide the features described in this user guide (notebook, terminal,
-text editor, etc.) You can install new extensions into the application
+Another way of managing your extensions is from the terminal on the server,
+using the ``jupyter labextension`` entry point. In general, a simple help text
+is available by typing ``jupyter labextension --help``.
+
+
+Installing Extensions
+^^^^^^^^^^^^^^^^^^^^^
+
+
+You can install new extensions into the application
 using the command:
 
 .. code:: bash
@@ -105,7 +231,7 @@ rebuild, you can run the command:
     jupyter lab build
 
 Disabling Extensions
-~~~~~~~~~~~~~~~~~~~~
+^^^^^^^^^^^^^^^^^^^^
 
 You can disable specific JupyterLab extensions (including core
 extensions) without rebuilding the application by running the command:
@@ -156,7 +282,8 @@ Building consists of:
 Note that building will always use the latest JavaScript packages that meet
 the dependency requirements of JupyterLab itself and any installed extensions.
 If you wish to run JupyterLab with the set of pinned requirements that was
-shipped with the Python package, you can launch as `jupyter lab --core-mode`.
+shipped with the Python package, you can launch as
+``jupyter lab --core-mode``.
 
 JupyterLab Application Directory
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

BIN
docs/source/user/images/extension_manager_default.png


BIN
docs/source/user/images/extension_manager_discover.png


BIN
docs/source/user/images/extension_manager_rebuild.png


BIN
docs/source/user/images/extension_manager_search.png


+ 24 - 6
packages/codemirror-extension/schema/commands.json

@@ -5,22 +5,40 @@
   "description": "Text editor settings for all CodeMirror editors.",
   "properties": {
     "keyMap": {
-      "default": "default",
-      "description": "The keymap CodeMirror uses.",
+      "type": "string",
       "title": "Key Map",
-      "type": "string"
+      "description": "Configures the keymap to use",
+      "default": "default"
     },
     "theme": {
-      "default": "default",
-      "description": "The CodeMirror theme.",
+      "type": "string",
       "title": "Theme",
-      "type": "string"
+      "description": "CSS file defining the corresponding\n.cm-s-[name] styles is loaded",
+      "default": "default"
     },
     "scrollPastEnd": {
       "type": "boolean",
       "title": "Scroll behavior",
       "description": "Whether to scroll past the end of text document",
       "default": true
+    },
+    "styleActiveLine": {
+      "type": ["boolean", "object"],
+      "title": "Style Active Line",
+      "description": "Value is boolean, or { nonEmpty: boolean }",
+      "default": false
+    },
+    "styleSelectedText": {
+      "type": "boolean",
+      "title": "Style Selected Text",
+      "description": "Value is boolean",
+      "default": false
+    },
+    "selectionPointer": {
+      "type": ["boolean", "string"],
+      "title": "Selection Pointer",
+      "description": "Value is boolean or string, e.g. 'pointer'",
+      "default": false
     }
   },
   "type": "object"

+ 23 - 1
packages/codemirror-extension/src/index.ts

@@ -134,7 +134,14 @@ function activateEditorCommands(
   settingRegistry: ISettingRegistry
 ): void {
   const { commands, restored } = app;
-  let { theme, keyMap, scrollPastEnd } = CodeMirrorEditor.defaultConfig;
+  let {
+    theme,
+    keyMap,
+    scrollPastEnd,
+    styleActiveLine,
+    styleSelectedText,
+    selectionPointer
+  } = CodeMirrorEditor.defaultConfig;
 
   /**
    * Update the setting values.
@@ -143,6 +150,15 @@ function activateEditorCommands(
     keyMap = (settings.get('keyMap').composite as string | null) || keyMap;
     theme = (settings.get('theme').composite as string | null) || theme;
     scrollPastEnd = settings.get('scrollPastEnd').composite as boolean | null;
+    styleActiveLine =
+      (settings.get('styleActiveLine').composite as boolean | object) ||
+      styleActiveLine;
+    styleSelectedText =
+      (settings.get('styleSelectedText').composite as boolean) ||
+      styleSelectedText;
+    selectionPointer =
+      (settings.get('selectionPointer').composite as boolean | string) ||
+      selectionPointer;
   }
 
   /**
@@ -155,6 +171,9 @@ function activateEditorCommands(
         cm.setOption('keyMap', keyMap);
         cm.setOption('theme', theme);
         cm.setOption('scrollPastEnd', scrollPastEnd);
+        cm.setOption('styleActiveLine', styleActiveLine);
+        cm.setOption('styleSelectedText', styleSelectedText);
+        cm.setOption('selectionPointer', selectionPointer);
       }
     });
   }
@@ -183,6 +202,9 @@ function activateEditorCommands(
       cm.setOption('keyMap', keyMap);
       cm.setOption('theme', theme);
       cm.setOption('scrollPastEnd', scrollPastEnd);
+      cm.setOption('styleActiveLine', styleActiveLine);
+      cm.setOption('styleSelectedText', styleSelectedText);
+      cm.setOption('selectionPointer', selectionPointer);
     }
   });
 

+ 29 - 1
packages/codemirror/src/editor.ts

@@ -32,6 +32,9 @@ import 'codemirror/addon/scroll/scrollpastend.js';
 import 'codemirror/addon/search/searchcursor';
 import 'codemirror/addon/search/search';
 import 'codemirror/addon/search/jump-to-line';
+import 'codemirror/addon/selection/active-line';
+import 'codemirror/addon/selection/mark-selection';
+import 'codemirror/addon/selection/selection-pointer';
 import 'codemirror/keymap/emacs.js';
 import 'codemirror/keymap/sublime.js';
 import 'codemirror/keymap/vim.js';
@@ -1127,6 +1130,28 @@ export namespace CodeMirrorEditor {
      * Whether to scroll past the end of the buffer.
      */
     scrollPastEnd?: boolean;
+
+    /**
+     * Whether to give the wrapper of the line that contains the cursor the class
+     * CodeMirror-activeline, adds a background with the class
+     * CodeMirror-activeline-background, and adds the class
+     * CodeMirror-activeline-gutter to the line's gutter space is enabled.
+     */
+    styleActiveLine: boolean | object;
+
+    /**
+     * Whether to causes the selected text to be marked with the CSS class
+     * CodeMirror-selectedtext. Useful to change the colour of the selection
+     * (in addition to the background).
+     */
+    styleSelectedText: boolean;
+
+    /**
+     * Defines the mouse cursor appearance when hovering over the selection.
+     * It can be set to a string, like "pointer", or to true,
+     * in which case the "default" (arrow) cursor will be used.
+     */
+    selectionPointer: boolean | string;
   }
 
   /**
@@ -1148,7 +1173,10 @@ export namespace CodeMirrorEditor {
     lineSeparator: null,
     scrollbarStyle: 'native',
     lineWiseCopyCut: true,
-    scrollPastEnd: false
+    scrollPastEnd: false,
+    styleActiveLine: false,
+    styleSelectedText: false,
+    selectionPointer: false
   };
 
   /**

+ 2 - 1
packages/extensionmanager/src/model.ts

@@ -738,7 +738,8 @@ namespace Private {
     let testB = isJupyterOrg(b.name);
 
     if (testA === testB) {
-      return a.name.localeCompare(b.name);
+      // Retain sort-order from API
+      return 0;
     } else if (testA && !testB) {
       return -1;
     } else {