Skip to content

Commit

Permalink
Merge pull request #115 from krassowski/rename_for_notebooks
Browse files Browse the repository at this point in the history
Add shadow filesystem, rename in notebooks
  • Loading branch information
krassowski authored Jan 5, 2020
2 parents 0b5c9fd + aa464cc commit 48f9a4e
Show file tree
Hide file tree
Showing 49 changed files with 1,980 additions and 255 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
- generates types for server data responses from JSON schema (
[#110](https://github.com/krassowski/jupyterlab-lsp/pull/110)
)
- add 'rename' function for notebooks, using shadow filesystem (
[#115](https://github.com/krassowski/jupyterlab-lsp/pull/115)
)

## `lsp-ws-connection` (unreleased)

Expand Down
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,16 @@ extension name, like this:
jupyter labextension install @krassowski/[email protected]
```

### Troubleshooting

#### Rename fails with `IndexError`

Question: using rename feature for Python does not work, the statusbar displays `Rename failed: Error: IndexError: list index out of range`

Answer: this is a pyls-specific error which happens when there is a syntax error and rope (the package used by pyls to perform edits) cannot parse the Python file.

Solution: Fix (or comment out) the fragments with syntax errors as indicated by diagnostics feature first, and try again.

## Acknowledgements

This would not be possible without the fantastic initial work at
Expand Down
52 changes: 46 additions & 6 deletions atest/01_Editor.robot
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,11 @@ Resource Keywords.robot
${MENU EDITOR} xpath://div[contains(@class, 'p-Menu-itemLabel')][contains(., "Editor")]
${MENU OPEN WITH} xpath://div[contains(@class, 'p-Menu-itemLabel')][contains(text(), "Open With")]
${MENU JUMP} xpath://div[contains(@class, 'p-Menu-itemLabel')][contains(text(), "Jump to definition")]
${MENU RENAME} xpath://div[contains(@class, 'p-Menu-itemLabel')][contains(text(), "Rename")]
${CM CURSOR} css:.CodeMirror-cursor
${CM CURSORS} css:.CodeMirror-cursors:not([style='visibility: hidden'])
${DIALOG WINDOW} css:.jp-Dialog
${DIALOG INPUT} css:.jp-Input-Dialog input

*** Test Cases ***
Bash
Expand All @@ -19,6 +22,7 @@ Bash
CSS
${def} = Set Variable xpath:(//span[contains(@class, 'cm-variable-2')][contains(text(), '--some-var')])[last()]
Editor Shows Features for Language CSS example.css Diagnostics=Do not use empty rulesets Jump to Definition=${def}
... Rename=${def}

Docker
${def} = Set Variable xpath://span[contains(@class, 'cm-string')][contains(text(), 'PLANET')]
Expand All @@ -27,6 +31,7 @@ Docker
JS
${def} = Set Variable xpath:(//span[contains(@class, 'cm-variable')][contains(text(), 'fib')])[last()]
Editor Shows Features for Language JS example.js Diagnostics=Expression expected Jump to Definition=${def}
... Rename=${def}

JSON
Editor Shows Features for Language JSON example.json Diagnostics=Duplicate object key
Expand All @@ -41,7 +46,7 @@ Less

Python
${def} = Set Variable xpath:(//span[contains(@class, 'cm-variable')][contains(text(), 'fib')])[last()]
Editor Shows Features for Language Python example.py Diagnostics=multiple spaces after keyword Jump to Definition=${def}
Editor Shows Features for Language Python example.py Diagnostics=multiple spaces after keyword Jump to Definition=${def} Rename=${def}

R
${def} = Set Variable xpath:(//span[contains(@class, 'cm-variable')][contains(text(), 'fib')])[last()]
Expand Down Expand Up @@ -74,6 +79,7 @@ Editor Shows Features for Language
FOR ${f} IN @{features}
Run Keyword If "${f}" == "Diagnostics" Editor Should Show Diagnostics ${features["${f}"]}
... ELSE IF "${f}" == "Jump to Definition" Editor Should Jump To Definition ${features["${f}"]}
... ELSE IF "${f}" == "Rename" Editor Should Rename ${features["${f}"]}
END
Capture Page Screenshot 99-done.png
[Teardown] Clean Up After Working With File ${file}
Expand Down Expand Up @@ -107,11 +113,7 @@ Editor Should Jump To Definition
[Arguments] ${symbol}
Set Tags feature:jump-to-definition
${sel} = Set Variable If "${symbol}".startswith(("xpath", "css")) ${symbol} xpath:(//span[@role="presentation"][contains(., "${symbol}")])[last()]
Mouse Over ${sel}
Sleep 10s
Mouse Over ${sel}
Wait Until Keyword Succeeds 10 x 0.1 s Click Element ${sel}
Wait Until Keyword Succeeds 10 x 0.1 s Open Context Menu ${sel}
Open Context Menu Over ${sel}
${cursor} = Measure Cursor Position
Capture Page Screenshot 02-jump-to-definition-0.png
Mouse Over ${MENU JUMP}
Expand All @@ -129,3 +131,41 @@ Measure Cursor Position
Wait Until Page Contains Element ${CM CURSORS}
${position} = Wait Until Keyword Succeeds 20 x 0.05s Get Vertical Position ${CM CURSOR}
[Return] ${position}

Wait For Dialog
Wait Until Page Contains Element ${DIALOG WINDOW} timeout=180s

Open Context Menu Over
[Arguments] ${sel}
Mouse Over ${sel}
Sleep 10s
Mouse Over ${sel}
Wait Until Keyword Succeeds 10 x 0.1 s Click Element ${sel}
Wait Until Keyword Succeeds 10 x 0.1 s Open Context Menu ${sel}

Editor Content Changed
[Arguments] ${old_content}
${new_content} Execute JavaScript return document.querySelector('.CodeMirror').CodeMirror.getValue()
Should Not Be Equal ${old_content} ${new_content}
[Return] ${new_content}

Editor Should Rename
[Arguments] ${symbol}
Set Tags feature:rename
${sel} = Set Variable If "${symbol}".startswith(("xpath", "css")) ${symbol} xpath:(//span[@role="presentation"][contains(., "${symbol}")])[last()]
Open Context Menu Over ${sel}
${old_content} Execute JavaScript return document.querySelector('.CodeMirror').CodeMirror.getValue()
Capture Page Screenshot 03-rename-0.png
Mouse Over ${MENU RENAME}
Capture Page Screenshot 03-rename-1.png
Click Element ${MENU RENAME}
Wait For Dialog
Click Element ${DIALOG INPUT}
Capture Page Screenshot 03-rename-3.png
Input Text ${DIALOG INPUT} new_name
Capture Page Screenshot 03-rename-4.png
Click Element css:button.jp-Dialog-button.jp-mod-accept
Sleep 3s
Capture Page Screenshot 03-rename-5.png
${new_content} Wait Until Keyword Succeeds 10 x 0.1 s Editor Content Changed ${old_content}
Should Be True "new_name" in """${new_content}"""
6 changes: 6 additions & 0 deletions atest/Keywords.robot
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,17 @@ Close JupyterLab

Reset Application State
Lab Command Close All Tabs
Accept Default Dialog Option
Ensure All Kernels Are Shut Down
Lab Command Reset Application State
Wait For Splash
Lab Command Close All Tabs

Accept Default Dialog Option
[Documentation] Accept a dialog, if it exists
${el} = Get WebElements ${CSS DIALOG OK}
Run Keyword If ${el.__len__()} Click Element ${CSS DIALOG OK}

Ensure All Kernels Are Shut Down
Enter Command Name Shut Down All Kernels
${els} = Get WebElements ${CMD PALETTE ITEM ACTIVE}
Expand Down
1 change: 1 addition & 0 deletions atest/Variables.robot
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ ${JLAB XP TOP} //div[@id='jp-top-panel']
${JLAB XP MENU ITEM LABEL} //div[@class='p-Menu-itemLabel']
${JLAB XP MENU LABEL} //div[@class='p-MenuBar-itemLabel']
${JLAB CSS VERSION} css:.jp-About-version
${CSS DIALOG OK} css:.jp-Dialog .jp-mod-accept
11 changes: 6 additions & 5 deletions ci/job.test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ jobs:
- script: conda info && conda list -n jupyterlab-lsp
displayName: list conda packages and info

- task: CacheBeta@0
inputs:
key: yarn | $(Agent.OS) | yarn.lock
path: $(YARN_CACHE_FOLDER)
displayName: restore cached yarn packages
# TODO: determine how can bring this back to get more robust installs (see #115)
# - task: CacheBeta@0
# inputs:
# key: yarn | $(Agent.OS) | yarn.lock
# path: $(YARN_CACHE_FOLDER)
# displayName: restore cached yarn packages

- script: ${{ platform.activate }} jupyterlab-lsp && jlpm || jlpm || jlpm
displayName: install npm dependencies
Expand Down
4 changes: 2 additions & 2 deletions packages/jupyterlab-lsp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
"@jupyterlab/rendermime": "^1.1",
"@jupyterlab/services": "^4.1",
"@jupyterlab/statusbar": "^1.1",
"@jupyterlab/testutils": "^1.0.0-alpha.6",
"@jupyterlab/testutils": "^1.2.2",
"@jupyterlab/tooltip": "^1.1",
"@phosphor/algorithm": "*",
"@types/chai": "^4.1.7",
Expand Down Expand Up @@ -90,7 +90,7 @@
"@jupyterlab/rendermime": "^1.1",
"@jupyterlab/services": "^4.1",
"@jupyterlab/statusbar": "^1.1",
"@jupyterlab/testutils": "^1.0.0-alpha.6",
"@jupyterlab/testutils": "^1.2.2",
"@jupyterlab/tooltip": "^1.1",
"@phosphor/algorithm": "*",
"codemirror": "*",
Expand Down
10 changes: 10 additions & 0 deletions packages/jupyterlab-lsp/schema/plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
"command": "lsp:jump-to-definition-file_editor",
"keys": ["Accel B"],
"selector": ".jp-FileEditor"
},
{
"command": "lsp:rename-symbol-notebook",
"keys": ["F2"],
"selector": ".jp-Notebook .jp-CodeCell"
},
{
"command": "lsp:rename-symbol-file_editor'",
"keys": ["F2"],
"selector": ".jp-FileEditor"
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ import {
import { IRootPosition } from '../../positioning';
import * as CodeMirror from 'codemirror';
import { CodeMirrorLSPFeature } from './feature';
import { FeatureTestEnvironment } from './testutils';
import { FileEditorFeatureTestEnvironment } from './testutils';

describe('CodeMirrorAdapter', () => {
let env: FeatureTestEnvironment;
let env: FileEditorFeatureTestEnvironment;

beforeEach(() => (env = new FeatureTestEnvironment()));
beforeEach(() => (env = new FileEditorFeatureTestEnvironment()));
afterEach(() => env.dispose());

describe('Works with VirtualFileEditor', () => {
Expand Down
10 changes: 5 additions & 5 deletions packages/jupyterlab-lsp/src/adapters/codemirror/cm_adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { ILSPFeature } from './feature';
import { IJupyterLabComponentsManager } from '../jupyterlab/jl_adapter';

export class CodeMirrorAdapter {
protected features: Array<ILSPFeature>;
features: Map<string, ILSPFeature>;

private last_change: CodeMirror.EditorChange;
private doc_change_handler: CodeMirrorHandler;
Expand All @@ -21,14 +21,14 @@ export class CodeMirrorAdapter {
this.doc_change_handler = this.saveChange.bind(this);
this.editor.on('change', this.doc_change_handler);

this.features = [];
this.features = new Map();

for (let feature of features) {
feature.register();
if (!feature.is_registered) {
console.warn('The feature ', feature, 'was not registered properly');
}
this.features.push(feature);
this.features.set(feature.name, feature);
}
}

Expand Down Expand Up @@ -58,7 +58,7 @@ export class CodeMirrorAdapter {
return true;
}

for (let feature of this.features) {
for (let feature of this.features.values()) {
feature.afterChange(change, root_position);
}
return true;
Expand All @@ -78,7 +78,7 @@ export class CodeMirrorAdapter {
}

public remove() {
for (let feature of this.features) {
for (let feature of this.features.values()) {
feature.remove();
}
this.editor.off('change', this.doc_change_handler);
Expand Down
Loading

0 comments on commit 48f9a4e

Please sign in to comment.