Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,9 @@ over the underlined code to see a more detailed message

![inspections](https://raw.githubusercontent.com/jupyter-lsp/jupyterlab-lsp/master/examples/screenshots/inspections.png)

### Jump to Definition
### Jump to Definition and References

Use the context menu entry, or <kbd>Alt</kbd> + :computer_mouse: to jump to definitions (you can change it to <kbd>Ctrl</kbd>/<kbd>⌘</kbd> in settings); use <kbd>Alt</kbd> + <kbd>o</kbd> to jump back
Use the context menu entry, or <kbd>Alt</kbd> + :computer_mouse: to jump to definitions/references (you can change it to <kbd>Ctrl</kbd>/<kbd>⌘</kbd> in settings); use <kbd>Alt</kbd> + <kbd>o</kbd> to jump back.

![jump](https://raw.githubusercontent.com/jupyter-lsp/jupyterlab-lsp/master/examples/screenshots/jump_to_definition.png)

Expand Down
23 changes: 0 additions & 23 deletions atest/04_Interface/DiagnosticsPanel.robot
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ ${DIAGNOSTIC MESSAGE} trailing whitespace
${DIAGNOSTIC MESSAGE R} Closing curly-braces should always be on their own line
${R CELL} %%R\n{}
${MENU COLUMNS} xpath://div[contains(@class, 'lm-Menu-itemLabel')][contains(text(), "columns")]
${LAB MENU} css:.lm-Menu

*** Test Cases ***
Diagnostics Panel Opens
Expand Down Expand Up @@ -119,28 +118,6 @@ Open Context Menu Over W291
Table Cell Should Equal W291 row=-1 column=2
Open Context Menu Over css:.lsp-diagnostics-listing tbody > tr:last-child

Expand Menu Entry
[Arguments] ${label}
${entry} = Set Variable xpath://div[contains(@class, 'lm-Menu-itemLabel')][contains(text(), "${label}")]
Wait Until Page Contains Element ${entry} timeout=10s
${menus before} = Get Element Count ${LAB MENU}
Mouse Over ${entry}
${expected menus} = Evaluate ${menus before} + 1
Wait Until Keyword Succeeds 10 x 1s Menus Count Equal ${expected menus}

Menus Count Equal
[Arguments] ${count}
${menus count} = Get Element Count ${LAB MENU}
Should Be Equal ${menus count} ${count}

Select Menu Entry
[Arguments] ${label}
${entry} Set Variable xpath://div[contains(@class, 'lm-Menu-itemLabel')][contains(text(), '${label}')]
Wait Until Page Contains Element ${entry} timeout=10s
Mouse Over ${entry}
Click Element ${entry}
Wait Until Page Does Not Contain Element ${entry} timeout=10s

Open Notebook And Panel
[Arguments] ${notebook}
Setup Notebook Python ${notebook}
Expand Down
58 changes: 50 additions & 8 deletions atest/05_Features/Jump.robot
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,60 @@ Force Tags feature:jump-to-definition gh:403
Resource ../Keywords.robot

*** Variables ***
${FOLDER WITH SPACE} a földer
${FOLDER WITH SPACE} a föl@der

*** Test Cases ***
Python Jumps between Files
Python Jumps Between Files
Copy Files to Folder With Spaces jump_a.py jump_b.py
${def} = Set Variable a_function_definition
Open ${FOLDER WITH SPACE}/jump_b.py in ${MENU EDITOR}
Wait Until Fully Initialized
${sel} = Set Variable xpath:(//span[contains(@class, 'cm-variable')][contains(text(), '${def}')])[last()]
${sel} = Select Token Occurrence a_function_definition
Jump To Definition ${sel}
Wait Until Page Contains ANOTHER_CONSTANT
Capture Page Screenshot 10-jumped.png
Clean Up After Working With File jump_b.py

Jumps To References With Modifier Click
[Setup] Prepare File for Editing Python editor jump_references.py
Configure JupyterLab Plugin {"modifierKey": "Accel"} plugin id=${JUMP PLUGIN ID}
Wait Until Fully Initialized
${token} = Select Token Occurrence func type=def
Click Element ${token}
${original} = Measure Cursor Position
Ctrl Click Element ${token}
Wait Until Page Contains Choose the jump target
${references_count} = Get Element Count css:.jp-Dialog select option
Should Be True ${references_count} == ${3}
Select From List By Index css:.jp-Dialog select 2
Click Element css:.jp-Dialog-button.jp-mod-accept
Wait Until Keyword Succeeds 10 x 1 s Cursor Should Jump ${original}
Clean Up After Working With File jump_references.py

Jumps To References From Context Menu
[Setup] Prepare File for Editing Python editor jump_references.py
Wait Until Fully Initialized
${token} = Select Token Occurrence func type=def
Click Element ${token}
${original} = Measure Cursor Position
Open Context Menu Over ${token}
Select Menu Entry Jump to references
Wait Until Page Contains Choose the jump target
${references_count} = Get Element Count css:.jp-Dialog select option
Should Be True ${references_count} == ${3}
Select From List By Index css:.jp-Dialog select 2
Click Element css:.jp-Dialog-button.jp-mod-accept
Wait Until Keyword Succeeds 10 x 1 s Cursor Should Jump ${original}
Clean Up After Working With File jump_references.py

Ctrl Click And Jumping Back Works
[Setup] Prepare File for Editing Python editor jump.py
Configure JupyterLab Plugin {"modifierKey": "Accel"} plugin id=${JUMP PLUGIN ID}
Wait Until Fully Initialized
${usage} = Set Variable a_variable
${sel} = Set Variable xpath:(//span[contains(@class, 'cm-variable')][contains(text(), '${usage}')])[last()]
${sel} = Select Token Occurrence a_variable
Click Element ${sel}
${original} = Measure Cursor Position
Capture Page Screenshot 01-ready-to-jump.png
${key} = Evaluate 'COMMAND' if platform.system() == 'Darwin' else 'CTRL' platform
Click Element ${sel} modifier=${key}
Ctrl Click Element ${sel}
Capture Page Screenshot 02-jumped.png
Wait Until Keyword Succeeds 10 x 1 s Cursor Should Jump ${original}
${new} = Measure Cursor Position
Expand All @@ -47,3 +76,16 @@ Copy Files to Folder With Spaces
FOR ${file} IN @{files}
Copy File examples${/}${file} ${NOTEBOOK DIR}${/}${FOLDER WITH SPACE}${/}${file}
END

Select Token Occurrence
[Arguments] ${token} ${type}=variable ${which}=last
[Return] xpath:(//span[contains(@class, 'cm-${type}')][contains(text(), '${token}')])[${which}()]

Ctrl Click Element
[Arguments] ${element}
${key} = Evaluate 'COMMAND' if platform.system() == 'Darwin' else 'CTRL' platform
Click Element ${element} modifier=${key}

Should Have Expected Count
[Arguments] ${expected_count}
${count} = Count Diagnostics In Panel
22 changes: 22 additions & 0 deletions atest/Keywords.robot
Original file line number Diff line number Diff line change
Expand Up @@ -434,3 +434,25 @@ Restart Kernel
Lab Command Restart Kernel…
Wait For Dialog
Accept Default Dialog Option

Expand Menu Entry
[Arguments] ${label}
${entry} = Set Variable xpath://div[contains(@class, 'lm-Menu-itemLabel')][contains(text(), "${label}")]
Wait Until Page Contains Element ${entry} timeout=10s
${menus before} = Get Element Count ${LAB MENU}
Mouse Over ${entry}
${expected menus} = Evaluate ${menus before} + 1
Wait Until Keyword Succeeds 10 x 1s Menus Count Equal ${expected menus}

Menus Count Equal
[Arguments] ${count}
${menus count} = Get Element Count ${LAB MENU}
Should Be Equal ${menus count} ${count}

Select Menu Entry
[Arguments] ${label}
${entry} Set Variable xpath://div[contains(@class, 'lm-Menu-itemLabel')][contains(text(), '${label}')]
Wait Until Page Contains Element ${entry} timeout=10s
Mouse Over ${entry}
Click Element ${entry}
Wait Until Page Does Not Contain Element ${entry} timeout=10s
1 change: 1 addition & 0 deletions atest/Variables.robot
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ ${MENU EDITOR} xpath://div[contains(@class, 'lm-Menu-itemLabel')][contains(.,
${MENU JUMP} xpath://div[contains(@class, 'lm-Menu-itemLabel')][contains(text(), "Jump to definition")]
${MENU SETTINGS} xpath://div[contains(@class, 'lm-MenuBar-itemLabel')][contains(text(), "Settings")]
${MENU EDITOR THEME} xpath://div[contains(@class, 'lm-Menu-itemLabel')][contains(text(), "Text Editor Theme")]
${LAB MENU} css:.lm-Menu
${CM CURSOR} css:.CodeMirror-cursor
${CM CURSORS} css:.jp-MainAreaWidget:not(.lm-mod-hidden) .CodeMirror-cursors:not([style='visibility: hidden'])
# settings
Expand Down
6 changes: 6 additions & 0 deletions atest/examples/jump_references.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
def func():
pass


x = func()
y = func()
Binary file modified examples/screenshots/jump_to_definition.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions packages/jupyterlab-lsp/src/connection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,10 +129,10 @@ export interface IClientResult {
[Method.ClientRequest.DEFINITION]: AnyLocation;
[Method.ClientRequest.DOCUMENT_HIGHLIGHT]: lsp.DocumentHighlight[];
[Method.ClientRequest.DOCUMENT_SYMBOL]: lsp.DocumentSymbol[];
[Method.ClientRequest.HOVER]: lsp.Hover;
[Method.ClientRequest.HOVER]: lsp.Hover | null;
[Method.ClientRequest.IMPLEMENTATION]: AnyLocation;
[Method.ClientRequest.INITIALIZE]: lsp.InitializeResult;
[Method.ClientRequest.REFERENCES]: Location[];
[Method.ClientRequest.REFERENCES]: lsp.Location[] | null;
[Method.ClientRequest.RENAME]: lsp.WorkspaceEdit;
[Method.ClientRequest.SIGNATURE_HELP]: lsp.SignatureHelp;
[Method.ClientRequest.TYPE_DEFINITION]: AnyLocation;
Expand Down
12 changes: 12 additions & 0 deletions packages/jupyterlab-lsp/src/editor_integration/codemirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ export abstract class CodeMirrorIntegration
protected virtual_document: VirtualDocument;
protected connection: LSPConnection;

/** @deprecated: use `setStatusMessage()` instead */
protected status_message: StatusMessage;
protected adapter: WidgetAdapter<IDocumentWidget>;
protected console: ILSPLogConsole;
Expand Down Expand Up @@ -128,6 +129,17 @@ export abstract class CodeMirrorIntegration
this.is_registered = false;
}

/**
* Set the text message and (optionally) the timeout to remove it.
* @param message
* @param timeout - number of ms to until the message is cleaned;
* -1 if the message should stay up indefinitely;
* defaults to 3000ms (3 seconds)
*/
setStatusMessage(message: string, timeout?: number): void {
this.status_message.set(message, timeout);
}

register(): void {
// register editor handlers
for (let [event_name, handler] of this.editor_handlers) {
Expand Down
39 changes: 4 additions & 35 deletions packages/jupyterlab-lsp/src/features/highlights.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,15 @@ import {
} from '@jupyterlab/application';
import { CodeEditor } from '@jupyterlab/codeeditor';
import { ISettingRegistry } from '@jupyterlab/settingregistry';
import { ITranslator, TranslationBundle } from '@jupyterlab/translation';
import { LabIcon } from '@jupyterlab/ui-components';
import { Debouncer } from '@lumino/polling';
import type * as CodeMirror from 'codemirror';
import type * as lsProtocol from 'vscode-languageserver-protocol';

import highlightTypeSvg from '../../style/icons/highlight-type.svg';
import highlightSvg from '../../style/icons/highlight.svg';
import { CodeHighlights as LSPHighlightsSettings } from '../_highlights';
import { CodeMirrorIntegration } from '../editor_integration/codemirror';
import { FeatureSettings, IFeatureCommand } from '../feature';
import { FeatureSettings } from '../feature';
import { DocumentHighlightKind } from '../lsp';
import { IRootPosition, IVirtualPosition } from '../positioning';
import { ILSPFeatureManager, PLUGIN_ID } from '../tokens';
Expand All @@ -25,32 +23,6 @@ export const highlightIcon = new LabIcon({
svgstr: highlightSvg
});

export const highlightTypeIcon = new LabIcon({
name: 'lsp:highlight-type',
svgstr: highlightTypeSvg
});

const COMMANDS = (trans: TranslationBundle): IFeatureCommand[] => [
{
id: 'highlight-references',
execute: ({ connection, virtual_position, document }) =>
connection?.getReferences(virtual_position, document.document_info),
is_enabled: ({ connection }) =>
connection ? connection.isReferencesSupported() : false,
label: trans.__('Highlight references'),
icon: highlightIcon
},
{
id: 'highlight-type-definition',
execute: ({ connection, virtual_position, document }) =>
connection?.getTypeDefinition(virtual_position, document.document_info),
is_enabled: ({ connection }) =>
connection ? connection.isTypeDefinitionSupported() : false,
label: trans.__('Highlight type definition'),
icon: highlightTypeIcon
}
];

export class HighlightsCM extends CodeMirrorIntegration {
protected highlight_markers: CodeMirror.TextMarker[] = [];
private debounced_get_highlight: Debouncer<
Expand Down Expand Up @@ -240,24 +212,21 @@ const FEATURE_ID = PLUGIN_ID + ':highlights';

export const HIGHLIGHTS_PLUGIN: JupyterFrontEndPlugin<void> = {
id: FEATURE_ID,
requires: [ILSPFeatureManager, ISettingRegistry, ITranslator],
requires: [ILSPFeatureManager, ISettingRegistry],
autoStart: true,
activate: (
app: JupyterFrontEnd,
featureManager: ILSPFeatureManager,
settingRegistry: ISettingRegistry,
translator: ITranslator
settingRegistry: ISettingRegistry
) => {
const settings = new FeatureSettings(settingRegistry, FEATURE_ID);
const trans = translator.load('jupyterlab_lsp');

featureManager.register({
feature: {
editorIntegrationFactory: new Map([['CodeMirrorEditor', HighlightsCM]]),
id: FEATURE_ID,
name: 'LSP Highlights',
settings: settings,
commands: COMMANDS(trans)
settings: settings
}
});
}
Expand Down
32 changes: 18 additions & 14 deletions packages/jupyterlab-lsp/src/features/hover.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,12 @@ import {
IEditorIntegrationOptions,
IFeatureLabIntegration
} from '../feature';
import { IRootPosition, IVirtualPosition, is_equal } from '../positioning';
import {
IRootPosition,
IVirtualPosition,
ProtocolCoordinates,
is_equal
} from '../positioning';
import { ILSPFeatureManager, PLUGIN_ID } from '../tokens';
import { getModifierState } from '../utils';
import { VirtualDocument } from '../virtual/document';
Expand Down Expand Up @@ -122,10 +127,12 @@ export class HoverCM extends CodeMirrorIntegration {
private virtual_position: IVirtualPosition;
protected cache: ResponseCache;

private debounced_get_hover: Throttler<Promise<lsProtocol.Hover | undefined>>;
private debounced_get_hover: Throttler<
Promise<lsProtocol.Hover | undefined | null>
>;
private tooltip: FreeTooltip;
private _previousHoverRequest: Promise<
Promise<lsProtocol.Hover | undefined>
Promise<lsProtocol.Hover | undefined | null>
> | null = null;

constructor(options: IEditorIntegrationOptions) {
Expand Down Expand Up @@ -154,13 +161,7 @@ export class HoverCM extends CodeMirrorIntegration {
return false;
}
let range = cache_item.response.range!;
return (
line >= range.start.line &&
line <= range.end.line &&
// need to be non-overlapping see https://github.com/jupyter-lsp/jupyterlab-lsp/issues/628
(line != range.start.line || ch > range.start.character) &&
(line != range.end.line || ch <= range.end.character)
);
return ProtocolCoordinates.isWithinRange({ line, character: ch }, range);
});
if (matching_items.length > 1) {
this.console.warn(
Expand Down Expand Up @@ -250,10 +251,13 @@ export class HoverCM extends CodeMirrorIntegration {
}

protected create_throttler() {
return new Throttler<Promise<lsProtocol.Hover | undefined>>(this.on_hover, {
limit: this.settings.composite.throttlerDelay,
edge: 'trailing'
});
return new Throttler<Promise<lsProtocol.Hover | undefined | null>>(
this.on_hover,
{
limit: this.settings.composite.throttlerDelay,
edge: 'trailing'
}
);
}

afterChange(change: IEditorChange, root_position: IRootPosition) {
Expand Down
Loading