Skip to content

Commit ded7c46

Browse files
committed
Extractors: split defaults, implement ipython-sql, fix html --isolated
1 parent ea45f0f commit ded7c46

File tree

15 files changed

+490
-190
lines changed

15 files changed

+490
-190
lines changed

packages/jupyterlab-lsp/src/editor_integration/feature.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
import * as lsProtocol from 'vscode-languageserver-protocol';
1111
import * as nbformat from '@jupyterlab/nbformat';
1212
import { language_specific_overrides } from '../magics/defaults';
13-
import { foreign_code_extractors } from '../extractors/defaults';
13+
import { foreign_code_extractors } from '../extractors/ipython';
1414
import { NotebookModel } from '@jupyterlab/notebook';
1515
import { PageConfig } from '@jupyterlab/coreutils';
1616
import { CodeMirrorIntegration } from './codemirror';

packages/jupyterlab-lsp/src/editor_integration/testutils.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,7 @@ import { ServiceManager } from '@jupyterlab/services';
4444
import { FeatureManager, ILSPExtension } from '../index';
4545
import { JupyterFrontEnd } from '@jupyterlab/application';
4646
import { IFeatureSettings } from '../feature';
47-
import { IForeignCodeExtractorsRegistry } from "../extractors/types";
48-
import { foreign_code_extractors } from "../extractors/defaults";
47+
import { IForeignCodeExtractorsRegistry } from '../extractors/types';
4948

5049
export interface ITestEnvironment {
5150
document_options: VirtualDocument.IOptions;
@@ -105,7 +104,7 @@ export class MockExtension implements ILSPExtension {
105104
name: 'CodeMirrorEditor',
106105
supports: CodeMirrorEditor
107106
});
108-
this.foreign_code_extractors = foreign_code_extractors;
107+
this.foreign_code_extractors = {};
109108
}
110109
}
111110

Lines changed: 10 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -1,140 +1,10 @@
1-
import { IForeignCodeExtractorsRegistry } from './types';
2-
import { RegExpForeignCodeExtractor } from './regexp';
3-
import {
4-
extract_r_args,
5-
rpy2_args_pattern,
6-
RPY2_MAX_ARGS
7-
} from '../magics/rpy2';
8-
import { JupyterFrontEndPlugin } from "@jupyterlab/application";
9-
import { ILSPCodeExtractorsManager, PLUGIN_ID } from "../tokens";
10-
11-
function rpy2_code_extractor(match: string, ...args: string[]) {
12-
let r = extract_r_args(args, -3);
13-
let code: string;
14-
if (r.rest == null) {
15-
code = '';
16-
} else {
17-
code = r.rest.startsWith(' ') ? r.rest.slice(1) : r.rest;
18-
}
19-
return code;
20-
}
21-
22-
function rpy2_args(match: string, ...args: string[]) {
23-
let r = extract_r_args(args, -3);
24-
// define dummy input variables using empty data frames
25-
let inputs = r.inputs.map(i => i + ' <- data.frame();').join(' ');
26-
let code = rpy2_code_extractor(match, ...args);
27-
if (inputs !== '' && code) {
28-
inputs += ' ';
29-
}
30-
return inputs;
31-
}
32-
33-
// TODO: make the regex code extractors configurable
34-
export let foreign_code_extractors: IForeignCodeExtractorsRegistry = {
35-
// general note: to match new lines use [^] instead of dot, unless the target is ES2018, then use /s
36-
python: [
37-
//
38-
// R magics (non-standalone: the R code will always be in the same, single R-namespace)
39-
//
40-
new RegExpForeignCodeExtractor({
41-
language: 'r',
42-
pattern: '^%%R' + rpy2_args_pattern(RPY2_MAX_ARGS) + '\n([^]*)',
43-
extract_to_foreign: rpy2_code_extractor,
44-
extract_arguments: rpy2_args,
45-
is_standalone: false,
46-
file_extension: 'R'
47-
}),
48-
new RegExpForeignCodeExtractor({
49-
language: 'r',
50-
pattern: '(?:^|\n)%R' + rpy2_args_pattern(RPY2_MAX_ARGS) + '( .*)?\n?',
51-
extract_to_foreign: rpy2_code_extractor,
52-
extract_arguments: rpy2_args,
53-
is_standalone: false,
54-
file_extension: 'R'
55-
}),
56-
//
57-
// Standalone IPython magics
58-
// (script magics are standalone, i.e. consecutive code cells with the same magic create two different namespaces)
59-
//
60-
new RegExpForeignCodeExtractor({
61-
language: 'python',
62-
pattern: '^%%(python|python2|python3|pypy)( .*?)?\n([^]*)',
63-
extract_to_foreign: '$3',
64-
is_standalone: true,
65-
file_extension: 'py'
66-
}),
67-
new RegExpForeignCodeExtractor({
68-
language: 'perl',
69-
pattern: '^%%(perl)( .*?)?\n([^]*)',
70-
extract_to_foreign: '$3',
71-
is_standalone: true,
72-
file_extension: 'pl'
73-
}),
74-
new RegExpForeignCodeExtractor({
75-
language: 'ruby',
76-
pattern: '^%%(ruby)( .*?)?\n([^]*)',
77-
extract_to_foreign: '$3',
78-
is_standalone: true,
79-
file_extension: 'rb'
80-
}),
81-
new RegExpForeignCodeExtractor({
82-
language: 'sh',
83-
pattern: '^%%(sh|bash)( .*?)?\n([^]*)',
84-
extract_to_foreign: '$3',
85-
is_standalone: true,
86-
file_extension: 'sh'
87-
}),
88-
new RegExpForeignCodeExtractor({
89-
language: 'html',
90-
pattern: '^%%(html --isolated)( .*?)?\n([^]*)',
91-
extract_to_foreign: '$3',
92-
is_standalone: true,
93-
file_extension: 'html'
94-
}),
95-
//
96-
// IPython magics producing continuous documents (non-standalone):
97-
//
98-
new RegExpForeignCodeExtractor({
99-
language: 'javascript',
100-
pattern: '^%%(js|javascript)( .*?)?\n([^]*)',
101-
extract_to_foreign: '$3',
102-
is_standalone: false,
103-
file_extension: 'js'
104-
}),
105-
new RegExpForeignCodeExtractor({
106-
language: 'html',
107-
pattern: '^%%(html)( .*?)?\n([^]*)',
108-
extract_to_foreign: '$3',
109-
is_standalone: false,
110-
file_extension: 'html'
111-
}),
112-
new RegExpForeignCodeExtractor({
113-
language: 'latex',
114-
pattern: '^%%(latex)( .*?)?\n([^]*)',
115-
extract_to_foreign: '$3',
116-
is_standalone: false,
117-
file_extension: 'tex'
118-
}),
119-
new RegExpForeignCodeExtractor({
120-
language: 'markdown',
121-
pattern: '^%%(markdown)( .*?)?\n([^]*)',
122-
extract_to_foreign: '$3',
123-
is_standalone: false,
124-
file_extension: 'md'
125-
})
126-
]
127-
};
128-
129-
export const DEFAULT_CODE_EXTRACTORS: JupyterFrontEndPlugin<void> = {
130-
id: PLUGIN_ID + ':default-extractors',
131-
requires: [ILSPCodeExtractorsManager],
132-
activate: (app, extractors_manager: ILSPCodeExtractorsManager) => {
133-
for (let language of Object.keys(foreign_code_extractors)) {
134-
for (let extractor of foreign_code_extractors[language]) {
135-
extractors_manager.register(extractor, language);
136-
}
137-
}
138-
},
139-
autoStart: true
140-
};
1+
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
2+
import { IPYTHON_CODE_EXTRACTORS } from './ipython';
3+
import { IPYTHON_RPY2_CODE_EXTRACTORS } from './ipython-rpy2';
4+
import { IPYTHON_SQL_CODE_EXTRACTORS } from './ipython-sql';
5+
6+
export const DEFAULT_CODE_EXTRACTORS: JupyterFrontEndPlugin<void>[] = [
7+
IPYTHON_CODE_EXTRACTORS,
8+
IPYTHON_RPY2_CODE_EXTRACTORS,
9+
IPYTHON_SQL_CODE_EXTRACTORS
10+
];

packages/jupyterlab-lsp/src/extractors/defaults.spec.ts renamed to packages/jupyterlab-lsp/src/extractors/ipython-rpy2.spec.ts

Lines changed: 11 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,18 @@
1-
import { IVirtualDocumentBlock, VirtualDocument } from '../virtual/document';
1+
import { VirtualDocument } from '../virtual/document';
22
import { expect } from 'chai';
3-
import { foreign_code_extractors } from './defaults';
4-
import { CodeEditor } from '@jupyterlab/codeeditor';
5-
6-
function wrap_in_python_lines(line: string) {
7-
return 'print("some code before")\n' + line + '\n' + 'print("and after")\n';
8-
}
9-
10-
describe('Default extractors', () => {
3+
import { foreign_code_extractors } from './ipython-rpy2';
4+
import {
5+
extract_code,
6+
get_the_only_pair,
7+
get_the_only_virtual,
8+
wrap_in_python_lines
9+
} from './testutils';
10+
11+
describe('IPython rpy2 extractors', () => {
1112
let document: VirtualDocument;
1213

1314
function extract(code: string) {
14-
return document.extract_foreign_code(code, null, {
15-
line: 0,
16-
column: 0
17-
});
18-
}
19-
20-
function get_the_only_pair(
21-
foreign_document_map: Map<CodeEditor.IRange, IVirtualDocumentBlock>
22-
) {
23-
expect(foreign_document_map.size).to.equal(1);
24-
25-
let range = foreign_document_map.keys().next().value;
26-
let { virtual_document } = foreign_document_map.get(range);
27-
28-
return { range, virtual_document };
29-
}
30-
31-
function get_the_only_virtual(
32-
foreign_document_map: Map<CodeEditor.IRange, IVirtualDocumentBlock>
33-
) {
34-
let { virtual_document } = get_the_only_pair(foreign_document_map);
35-
return virtual_document;
15+
return extract_code(document, code);
3616
}
3717

3818
beforeEach(() => {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { IForeignCodeExtractorsRegistry } from './types';
2+
import { RegExpForeignCodeExtractor } from './regexp';
3+
import {
4+
extract_r_args,
5+
rpy2_args_pattern,
6+
RPY2_MAX_ARGS
7+
} from '../magics/rpy2';
8+
import { JupyterFrontEndPlugin } from '@jupyterlab/application';
9+
import { ILSPCodeExtractorsManager, PLUGIN_ID } from '../tokens';
10+
11+
function rpy2_code_extractor(match: string, ...args: string[]) {
12+
let r = extract_r_args(args, -3);
13+
let code: string;
14+
if (r.rest == null) {
15+
code = '';
16+
} else {
17+
code = r.rest.startsWith(' ') ? r.rest.slice(1) : r.rest;
18+
}
19+
return code;
20+
}
21+
22+
function rpy2_args(match: string, ...args: string[]) {
23+
let r = extract_r_args(args, -3);
24+
// define dummy input variables using empty data frames
25+
let inputs = r.inputs.map(i => i + ' <- data.frame();').join(' ');
26+
let code = rpy2_code_extractor(match, ...args);
27+
if (inputs !== '' && code) {
28+
inputs += ' ';
29+
}
30+
return inputs;
31+
}
32+
33+
export let foreign_code_extractors: IForeignCodeExtractorsRegistry = {
34+
// general note: to match new lines use [^] instead of dot, unless the target is ES2018, then use /s
35+
python: [
36+
//
37+
// R magics (non-standalone: the R code will always be in the same, single R-namespace)
38+
//
39+
new RegExpForeignCodeExtractor({
40+
language: 'r',
41+
pattern: '^%%R' + rpy2_args_pattern(RPY2_MAX_ARGS) + '\n([^]*)',
42+
extract_to_foreign: rpy2_code_extractor,
43+
extract_arguments: rpy2_args,
44+
is_standalone: false,
45+
file_extension: 'R'
46+
}),
47+
new RegExpForeignCodeExtractor({
48+
language: 'r',
49+
pattern: '(?:^|\n)%R' + rpy2_args_pattern(RPY2_MAX_ARGS) + '( .*)?\n?',
50+
extract_to_foreign: rpy2_code_extractor,
51+
extract_arguments: rpy2_args,
52+
is_standalone: false,
53+
file_extension: 'R'
54+
})
55+
]
56+
};
57+
58+
export const IPYTHON_RPY2_CODE_EXTRACTORS: JupyterFrontEndPlugin<void> = {
59+
id: PLUGIN_ID + ':extractors-ipython-rpy2',
60+
requires: [ILSPCodeExtractorsManager],
61+
activate: (app, extractors_manager: ILSPCodeExtractorsManager) => {
62+
for (let language of Object.keys(foreign_code_extractors)) {
63+
for (let extractor of foreign_code_extractors[language]) {
64+
extractors_manager.register(extractor, language);
65+
}
66+
}
67+
},
68+
autoStart: true
69+
};

0 commit comments

Comments
 (0)