@jupyterlab/fileeditor 4.0.0-alpha.9 → 4.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/fileeditorlspadapter.d.ts +96 -0
- package/lib/fileeditorlspadapter.js +175 -0
- package/lib/fileeditorlspadapter.js.map +1 -0
- package/lib/index.d.ts +4 -0
- package/lib/index.js +4 -0
- package/lib/index.js.map +1 -1
- package/lib/searchprovider.d.ts +60 -0
- package/lib/searchprovider.js +80 -0
- package/lib/searchprovider.js.map +1 -0
- package/lib/syntaxstatus.d.ts +74 -0
- package/lib/syntaxstatus.js +151 -0
- package/lib/syntaxstatus.js.map +1 -0
- package/lib/tabspacestatus.d.ts +8 -12
- package/lib/tabspacestatus.js +23 -27
- package/lib/tabspacestatus.js.map +1 -1
- package/lib/toc/factory.d.ts +25 -0
- package/lib/toc/factory.js +32 -0
- package/lib/toc/factory.js.map +1 -0
- package/lib/toc/index.d.ts +4 -0
- package/lib/toc/index.js +7 -0
- package/lib/toc/index.js.map +1 -0
- package/lib/toc/latex.d.ts +48 -0
- package/lib/toc/latex.js +117 -0
- package/lib/toc/latex.js.map +1 -0
- package/lib/toc/markdown.d.ts +44 -0
- package/lib/toc/markdown.js +68 -0
- package/lib/toc/markdown.js.map +1 -0
- package/lib/toc/python.d.ts +44 -0
- package/lib/toc/python.js +116 -0
- package/lib/toc/python.js.map +1 -0
- package/lib/widget.d.ts +4 -0
- package/lib/widget.js +21 -4
- package/lib/widget.js.map +1 -1
- package/package.json +26 -21
- package/src/fileeditorlspadapter.ts +225 -0
- package/src/index.ts +14 -0
- package/src/searchprovider.ts +115 -0
- package/src/syntaxstatus.tsx +217 -0
- package/src/tabspacestatus.tsx +166 -0
- package/src/toc/factory.ts +72 -0
- package/src/toc/index.ts +7 -0
- package/src/toc/latex.ts +145 -0
- package/src/toc/markdown.ts +90 -0
- package/src/toc/python.ts +143 -0
- package/src/tokens.ts +20 -0
- package/src/widget.ts +267 -0
- package/style/base.css +8 -0
- package/style/index.css +4 -0
- package/style/index.js +4 -0
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) Jupyter Development Team.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { CodeEditor, IEditorMimeTypeService } from '@jupyterlab/codeeditor';
|
|
7
|
+
import { IEditorLanguageRegistry } from '@jupyterlab/codemirror';
|
|
8
|
+
import { IChangedArgs } from '@jupyterlab/coreutils';
|
|
9
|
+
import { Popup, showPopup, TextItem } from '@jupyterlab/statusbar';
|
|
10
|
+
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
11
|
+
import { VDomModel, VDomRenderer } from '@jupyterlab/ui-components';
|
|
12
|
+
import { CommandRegistry } from '@lumino/commands';
|
|
13
|
+
import { JSONObject } from '@lumino/coreutils';
|
|
14
|
+
import { Menu } from '@lumino/widgets';
|
|
15
|
+
import React from 'react';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A namespace for `EditorSyntaxComponentStatics`.
|
|
19
|
+
*/
|
|
20
|
+
namespace EditorSyntaxComponent {
|
|
21
|
+
/**
|
|
22
|
+
* The props for the `EditorSyntaxComponent`.
|
|
23
|
+
*/
|
|
24
|
+
export interface IProps {
|
|
25
|
+
/**
|
|
26
|
+
* The current CodeMirror language for an editor.
|
|
27
|
+
*/
|
|
28
|
+
language: string;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A function to execute on clicking the component.
|
|
32
|
+
* By default we provide a function that opens a menu
|
|
33
|
+
* for CodeMirror language selection.
|
|
34
|
+
*/
|
|
35
|
+
handleClick: () => void;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A pure function that returns a tsx component for an editor syntax item.
|
|
41
|
+
*
|
|
42
|
+
* @param props: the props for the component.
|
|
43
|
+
*
|
|
44
|
+
* @returns an editor syntax component.
|
|
45
|
+
*/
|
|
46
|
+
function EditorSyntaxComponent(
|
|
47
|
+
props: EditorSyntaxComponent.IProps
|
|
48
|
+
): React.ReactElement<EditorSyntaxComponent.IProps> {
|
|
49
|
+
return <TextItem source={props.language} onClick={props.handleClick} />;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* StatusBar item to change the language syntax highlighting of the file editor.
|
|
54
|
+
*/
|
|
55
|
+
export class EditorSyntaxStatus extends VDomRenderer<EditorSyntaxStatus.Model> {
|
|
56
|
+
/**
|
|
57
|
+
* Construct a new VDomRenderer for the status item.
|
|
58
|
+
*/
|
|
59
|
+
constructor(options: EditorSyntaxStatus.IOptions) {
|
|
60
|
+
super(new EditorSyntaxStatus.Model(options.languages));
|
|
61
|
+
this._commands = options.commands;
|
|
62
|
+
this.translator = options.translator ?? nullTranslator;
|
|
63
|
+
const trans = this.translator.load('jupyterlab');
|
|
64
|
+
|
|
65
|
+
this.addClass('jp-mod-highlighted');
|
|
66
|
+
this.title.caption = trans.__('Change text editor syntax highlighting');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Render the status item.
|
|
71
|
+
*/
|
|
72
|
+
render(): JSX.Element | null {
|
|
73
|
+
if (!this.model) {
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
return (
|
|
77
|
+
<EditorSyntaxComponent
|
|
78
|
+
language={this.model.language}
|
|
79
|
+
handleClick={this._handleClick}
|
|
80
|
+
/>
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Create a menu for selecting the language of the editor.
|
|
86
|
+
*/
|
|
87
|
+
private _handleClick = () => {
|
|
88
|
+
const languageMenu = new Menu({ commands: this._commands });
|
|
89
|
+
const command = 'fileeditor:change-language';
|
|
90
|
+
if (this._popup) {
|
|
91
|
+
this._popup.dispose();
|
|
92
|
+
}
|
|
93
|
+
this.model.languages
|
|
94
|
+
.getLanguages()
|
|
95
|
+
.sort((a, b) => {
|
|
96
|
+
const aName = a.displayName ?? a.name;
|
|
97
|
+
const bName = b.displayName ?? b.name;
|
|
98
|
+
return aName.localeCompare(bName);
|
|
99
|
+
})
|
|
100
|
+
.forEach(spec => {
|
|
101
|
+
if (spec.name.toLowerCase().indexOf('brainf') === 0) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const args: JSONObject = {
|
|
106
|
+
name: spec.name,
|
|
107
|
+
displayName: spec.displayName ?? spec.name
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
languageMenu.addItem({
|
|
111
|
+
command,
|
|
112
|
+
args
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
this._popup = showPopup({
|
|
116
|
+
body: languageMenu,
|
|
117
|
+
anchor: this,
|
|
118
|
+
align: 'left'
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
protected translator: ITranslator;
|
|
123
|
+
private _commands: CommandRegistry;
|
|
124
|
+
private _popup: Popup | null = null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A namespace for EditorSyntax statics.
|
|
129
|
+
*/
|
|
130
|
+
export namespace EditorSyntaxStatus {
|
|
131
|
+
/**
|
|
132
|
+
* A VDomModel for the current editor/mode combination.
|
|
133
|
+
*/
|
|
134
|
+
export class Model extends VDomModel {
|
|
135
|
+
constructor(public languages: IEditorLanguageRegistry) {
|
|
136
|
+
super();
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* The current editor language. If no editor is present,
|
|
140
|
+
* returns the empty string.
|
|
141
|
+
*/
|
|
142
|
+
get language(): string {
|
|
143
|
+
return this._language;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* The current editor for the application editor tracker.
|
|
148
|
+
*/
|
|
149
|
+
get editor(): CodeEditor.IEditor | null {
|
|
150
|
+
return this._editor;
|
|
151
|
+
}
|
|
152
|
+
set editor(editor: CodeEditor.IEditor | null) {
|
|
153
|
+
const oldEditor = this._editor;
|
|
154
|
+
if (oldEditor !== null) {
|
|
155
|
+
oldEditor.model.mimeTypeChanged.disconnect(this._onMIMETypeChange);
|
|
156
|
+
}
|
|
157
|
+
const oldLanguage = this._language;
|
|
158
|
+
this._editor = editor;
|
|
159
|
+
if (this._editor === null) {
|
|
160
|
+
this._language = '';
|
|
161
|
+
} else {
|
|
162
|
+
const spec = this.languages.findByMIME(this._editor.model.mimeType);
|
|
163
|
+
this._language = spec?.name ?? IEditorMimeTypeService.defaultMimeType;
|
|
164
|
+
|
|
165
|
+
this._editor.model.mimeTypeChanged.connect(this._onMIMETypeChange);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
this._triggerChange(oldLanguage, this._language);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* If the editor mode changes, update the model.
|
|
173
|
+
*/
|
|
174
|
+
private _onMIMETypeChange = (
|
|
175
|
+
mode: CodeEditor.IModel,
|
|
176
|
+
change: IChangedArgs<string>
|
|
177
|
+
) => {
|
|
178
|
+
const oldLanguage = this._language;
|
|
179
|
+
const spec = this.languages.findByMIME(change.newValue);
|
|
180
|
+
this._language = spec?.name ?? IEditorMimeTypeService.defaultMimeType;
|
|
181
|
+
|
|
182
|
+
this._triggerChange(oldLanguage, this._language);
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Trigger a rerender of the model.
|
|
187
|
+
*/
|
|
188
|
+
private _triggerChange(oldState: string, newState: string) {
|
|
189
|
+
if (oldState !== newState) {
|
|
190
|
+
this.stateChanged.emit(void 0);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
private _language: string = '';
|
|
195
|
+
private _editor: CodeEditor.IEditor | null = null;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Options for the EditorSyntax status item.
|
|
200
|
+
*/
|
|
201
|
+
export interface IOptions {
|
|
202
|
+
/**
|
|
203
|
+
* The application command registry.
|
|
204
|
+
*/
|
|
205
|
+
commands: CommandRegistry;
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Editor languages.
|
|
209
|
+
*/
|
|
210
|
+
languages: IEditorLanguageRegistry;
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The language translator.
|
|
214
|
+
*/
|
|
215
|
+
translator?: ITranslator;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { Popup, showPopup, TextItem } from '@jupyterlab/statusbar';
|
|
5
|
+
import { ITranslator, nullTranslator } from '@jupyterlab/translation';
|
|
6
|
+
import { VDomModel, VDomRenderer } from '@jupyterlab/ui-components';
|
|
7
|
+
import { Menu } from '@lumino/widgets';
|
|
8
|
+
import React from 'react';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* A namespace for TabSpaceComponent statics.
|
|
12
|
+
*/
|
|
13
|
+
namespace TabSpaceComponent {
|
|
14
|
+
/**
|
|
15
|
+
* The props for TabSpaceComponent.
|
|
16
|
+
*/
|
|
17
|
+
export interface IProps {
|
|
18
|
+
/**
|
|
19
|
+
* The number of spaces for indentation.
|
|
20
|
+
*
|
|
21
|
+
* `null` means use tab character for indentation.
|
|
22
|
+
*/
|
|
23
|
+
tabSpace: number | null;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The application language translator.
|
|
27
|
+
*/
|
|
28
|
+
translator?: ITranslator;
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A click handler for the TabSpace component. By default
|
|
32
|
+
* opens a menu allowing the user to select tabs vs spaces.
|
|
33
|
+
*/
|
|
34
|
+
handleClick: () => void;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A pure functional component for rendering the TabSpace status.
|
|
40
|
+
*/
|
|
41
|
+
function TabSpaceComponent(
|
|
42
|
+
props: TabSpaceComponent.IProps
|
|
43
|
+
): React.ReactElement<TabSpaceComponent.IProps> {
|
|
44
|
+
const translator = props.translator || nullTranslator;
|
|
45
|
+
const trans = translator.load('jupyterlab');
|
|
46
|
+
const description =
|
|
47
|
+
typeof props.tabSpace === 'number'
|
|
48
|
+
? trans.__('Spaces')
|
|
49
|
+
: trans.__('Tab Indent');
|
|
50
|
+
return (
|
|
51
|
+
<TextItem
|
|
52
|
+
onClick={props.handleClick}
|
|
53
|
+
source={
|
|
54
|
+
typeof props.tabSpace === 'number'
|
|
55
|
+
? `${description}: ${props.tabSpace}`
|
|
56
|
+
: description
|
|
57
|
+
}
|
|
58
|
+
title={trans.__('Change the indentation…')}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* A VDomRenderer for a tabs vs. spaces status item.
|
|
65
|
+
*/
|
|
66
|
+
export class TabSpaceStatus extends VDomRenderer<TabSpaceStatus.Model> {
|
|
67
|
+
/**
|
|
68
|
+
* Create a new tab/space status item.
|
|
69
|
+
*/
|
|
70
|
+
constructor(options: TabSpaceStatus.IOptions) {
|
|
71
|
+
super(new TabSpaceStatus.Model());
|
|
72
|
+
this._menu = options.menu;
|
|
73
|
+
this.translator = options.translator || nullTranslator;
|
|
74
|
+
this.addClass('jp-mod-highlighted');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Render the TabSpace status item.
|
|
79
|
+
*/
|
|
80
|
+
render(): React.ReactElement<TabSpaceComponent.IProps> | null {
|
|
81
|
+
if (!this.model?.indentUnit) {
|
|
82
|
+
return null;
|
|
83
|
+
} else {
|
|
84
|
+
const tabSpace =
|
|
85
|
+
this.model.indentUnit === 'Tab'
|
|
86
|
+
? null
|
|
87
|
+
: parseInt(this.model.indentUnit, 10);
|
|
88
|
+
return (
|
|
89
|
+
<TabSpaceComponent
|
|
90
|
+
tabSpace={tabSpace}
|
|
91
|
+
handleClick={() => this._handleClick()}
|
|
92
|
+
translator={this.translator}
|
|
93
|
+
/>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Handle a click on the status item.
|
|
100
|
+
*/
|
|
101
|
+
private _handleClick(): void {
|
|
102
|
+
const menu = this._menu;
|
|
103
|
+
if (this._popup) {
|
|
104
|
+
this._popup.dispose();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
menu.aboutToClose.connect(this._menuClosed, this);
|
|
108
|
+
|
|
109
|
+
this._popup = showPopup({
|
|
110
|
+
body: menu,
|
|
111
|
+
anchor: this,
|
|
112
|
+
align: 'right'
|
|
113
|
+
});
|
|
114
|
+
// Update the menu items
|
|
115
|
+
menu.update();
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
private _menuClosed(): void {
|
|
119
|
+
this.removeClass('jp-mod-clicked');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
protected translator: ITranslator;
|
|
123
|
+
private _menu: Menu;
|
|
124
|
+
private _popup: Popup | null = null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* A namespace for TabSpace statics.
|
|
129
|
+
*/
|
|
130
|
+
export namespace TabSpaceStatus {
|
|
131
|
+
/**
|
|
132
|
+
* A VDomModel for the TabSpace status item.
|
|
133
|
+
*/
|
|
134
|
+
export class Model extends VDomModel {
|
|
135
|
+
/**
|
|
136
|
+
* Code editor indentation unit
|
|
137
|
+
*/
|
|
138
|
+
get indentUnit(): string | null {
|
|
139
|
+
return this._indentUnit;
|
|
140
|
+
}
|
|
141
|
+
set indentUnit(v: string | null) {
|
|
142
|
+
if (v !== this._indentUnit) {
|
|
143
|
+
this._indentUnit = v;
|
|
144
|
+
this.stateChanged.emit();
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
private _indentUnit: string | null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Options for creating a TabSpace status item.
|
|
153
|
+
*/
|
|
154
|
+
export interface IOptions {
|
|
155
|
+
/**
|
|
156
|
+
* A menu to open when clicking on the status item. This should allow
|
|
157
|
+
* the user to make a different selection about tabs/spaces.
|
|
158
|
+
*/
|
|
159
|
+
menu: Menu;
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Language translator.
|
|
163
|
+
*/
|
|
164
|
+
translator?: ITranslator;
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { DocumentRegistry, IDocumentWidget } from '@jupyterlab/docregistry';
|
|
5
|
+
import {
|
|
6
|
+
TableOfContents,
|
|
7
|
+
TableOfContentsFactory,
|
|
8
|
+
TableOfContentsModel
|
|
9
|
+
} from '@jupyterlab/toc';
|
|
10
|
+
import { FileEditor } from '../widget';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Interface describing a file editor heading.
|
|
14
|
+
*/
|
|
15
|
+
export interface IEditorHeading extends TableOfContents.IHeading {
|
|
16
|
+
/**
|
|
17
|
+
* Heading line number.
|
|
18
|
+
*/
|
|
19
|
+
line: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Base table of contents model factory for file editor
|
|
24
|
+
*/
|
|
25
|
+
export abstract class EditorTableOfContentsFactory extends TableOfContentsFactory<
|
|
26
|
+
IDocumentWidget<FileEditor>
|
|
27
|
+
> {
|
|
28
|
+
/**
|
|
29
|
+
* Create a new table of contents model for the widget
|
|
30
|
+
*
|
|
31
|
+
* @param widget - widget
|
|
32
|
+
* @param configuration - Table of contents configuration
|
|
33
|
+
* @returns The table of contents model
|
|
34
|
+
*/
|
|
35
|
+
createNew(
|
|
36
|
+
widget: IDocumentWidget<FileEditor, DocumentRegistry.IModel>,
|
|
37
|
+
configuration?: TableOfContents.IConfig
|
|
38
|
+
): TableOfContentsModel<
|
|
39
|
+
IEditorHeading,
|
|
40
|
+
IDocumentWidget<FileEditor, DocumentRegistry.IModel>
|
|
41
|
+
> {
|
|
42
|
+
const model = super.createNew(
|
|
43
|
+
widget,
|
|
44
|
+
configuration
|
|
45
|
+
) as TableOfContentsModel<
|
|
46
|
+
IEditorHeading,
|
|
47
|
+
IDocumentWidget<FileEditor, DocumentRegistry.IModel>
|
|
48
|
+
>;
|
|
49
|
+
|
|
50
|
+
const onActiveHeadingChanged = (
|
|
51
|
+
model: TableOfContentsModel<
|
|
52
|
+
IEditorHeading,
|
|
53
|
+
IDocumentWidget<FileEditor, DocumentRegistry.IModel>
|
|
54
|
+
>,
|
|
55
|
+
heading: IEditorHeading | null
|
|
56
|
+
) => {
|
|
57
|
+
if (heading) {
|
|
58
|
+
widget.content.editor.setCursorPosition({
|
|
59
|
+
line: heading.line,
|
|
60
|
+
column: 0
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
model.activeHeadingChanged.connect(onActiveHeadingChanged);
|
|
66
|
+
widget.disposed.connect(() => {
|
|
67
|
+
model.activeHeadingChanged.disconnect(onActiveHeadingChanged);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return model;
|
|
71
|
+
}
|
|
72
|
+
}
|
package/src/toc/index.ts
ADDED
package/src/toc/latex.ts
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { DocumentRegistry, IDocumentWidget } from '@jupyterlab/docregistry';
|
|
5
|
+
import {
|
|
6
|
+
TableOfContents,
|
|
7
|
+
TableOfContentsModel,
|
|
8
|
+
TableOfContentsUtils
|
|
9
|
+
} from '@jupyterlab/toc';
|
|
10
|
+
import { Widget } from '@lumino/widgets';
|
|
11
|
+
import { FileEditor } from '../widget';
|
|
12
|
+
import { EditorTableOfContentsFactory, IEditorHeading } from './factory';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Maps LaTeX section headings to HTML header levels.
|
|
16
|
+
*
|
|
17
|
+
* ## Notes
|
|
18
|
+
*
|
|
19
|
+
* - As `part` and `chapter` section headings appear to be less common, assign them to heading level 1.
|
|
20
|
+
*
|
|
21
|
+
* @private
|
|
22
|
+
*/
|
|
23
|
+
const LATEX_LEVELS: { [label: string]: number } = {
|
|
24
|
+
part: 1, // Only available for report and book classes
|
|
25
|
+
chapter: 1, // Only available for report and book classes
|
|
26
|
+
section: 1,
|
|
27
|
+
subsection: 2,
|
|
28
|
+
subsubsection: 3,
|
|
29
|
+
paragraph: 4,
|
|
30
|
+
subparagraph: 5
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Regular expression to create the outline
|
|
35
|
+
*/
|
|
36
|
+
const SECTIONS = /^\s*\\(section|subsection|subsubsection){(.+)}/;
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Table of content model for LaTeX files.
|
|
40
|
+
*/
|
|
41
|
+
export class LaTeXTableOfContentsModel extends TableOfContentsModel<
|
|
42
|
+
IEditorHeading,
|
|
43
|
+
IDocumentWidget<FileEditor, DocumentRegistry.IModel>
|
|
44
|
+
> {
|
|
45
|
+
/**
|
|
46
|
+
* Type of document supported by the model.
|
|
47
|
+
*
|
|
48
|
+
* #### Notes
|
|
49
|
+
* A `data-document-type` attribute with this value will be set
|
|
50
|
+
* on the tree view `.jp-TableOfContents-content[data-document-type="..."]`
|
|
51
|
+
*/
|
|
52
|
+
get documentType(): string {
|
|
53
|
+
return 'latex';
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* List of configuration options supported by the model.
|
|
58
|
+
*/
|
|
59
|
+
get supportedOptions(): (keyof TableOfContents.IConfig)[] {
|
|
60
|
+
return ['maximalDepth', 'numberHeaders'];
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Produce the headings for a document.
|
|
65
|
+
*
|
|
66
|
+
* @returns The list of new headings or `null` if nothing needs to be updated.
|
|
67
|
+
*/
|
|
68
|
+
protected getHeadings(): Promise<IEditorHeading[] | null> {
|
|
69
|
+
if (!this.isActive) {
|
|
70
|
+
return Promise.resolve(null);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Split the text into lines:
|
|
74
|
+
const lines = this.widget.content.model.sharedModel
|
|
75
|
+
.getSource()
|
|
76
|
+
.split('\n') as Array<string>;
|
|
77
|
+
|
|
78
|
+
const levels = new Array<number>();
|
|
79
|
+
let previousLevel = levels.length;
|
|
80
|
+
const headings = new Array<IEditorHeading>();
|
|
81
|
+
for (let i = 0; i < lines.length; i++) {
|
|
82
|
+
const match = lines[i].match(SECTIONS);
|
|
83
|
+
if (match) {
|
|
84
|
+
const level = LATEX_LEVELS[match[1]];
|
|
85
|
+
if (level <= this.configuration.maximalDepth) {
|
|
86
|
+
const prefix = TableOfContentsUtils.getPrefix(
|
|
87
|
+
level,
|
|
88
|
+
previousLevel,
|
|
89
|
+
levels,
|
|
90
|
+
{
|
|
91
|
+
...this.configuration,
|
|
92
|
+
// Force base numbering and numbering first level
|
|
93
|
+
baseNumbering: 1,
|
|
94
|
+
numberingH1: true
|
|
95
|
+
}
|
|
96
|
+
);
|
|
97
|
+
previousLevel = level;
|
|
98
|
+
|
|
99
|
+
headings.push({
|
|
100
|
+
text: match[2],
|
|
101
|
+
prefix: prefix,
|
|
102
|
+
level,
|
|
103
|
+
line: i
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return Promise.resolve(headings);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Table of content model factory for LaTeX files.
|
|
114
|
+
*/
|
|
115
|
+
export class LaTeXTableOfContentsFactory extends EditorTableOfContentsFactory {
|
|
116
|
+
/**
|
|
117
|
+
* Whether the factory can handle the widget or not.
|
|
118
|
+
*
|
|
119
|
+
* @param widget - widget
|
|
120
|
+
* @returns boolean indicating a ToC can be generated
|
|
121
|
+
*/
|
|
122
|
+
isApplicable(widget: Widget): boolean {
|
|
123
|
+
const isApplicable = super.isApplicable(widget);
|
|
124
|
+
|
|
125
|
+
if (isApplicable) {
|
|
126
|
+
let mime = (widget as any).content?.model?.mimeType;
|
|
127
|
+
return mime && (mime === 'text/x-latex' || mime === 'text/x-stex');
|
|
128
|
+
}
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Create a new table of contents model for the widget
|
|
134
|
+
*
|
|
135
|
+
* @param widget - widget
|
|
136
|
+
* @param configuration - Table of contents configuration
|
|
137
|
+
* @returns The table of contents model
|
|
138
|
+
*/
|
|
139
|
+
protected _createNew(
|
|
140
|
+
widget: IDocumentWidget<FileEditor, DocumentRegistry.IModel>,
|
|
141
|
+
configuration?: TableOfContents.IConfig
|
|
142
|
+
): LaTeXTableOfContentsModel {
|
|
143
|
+
return new LaTeXTableOfContentsModel(widget, configuration);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// Copyright (c) Jupyter Development Team.
|
|
2
|
+
// Distributed under the terms of the Modified BSD License.
|
|
3
|
+
|
|
4
|
+
import { DocumentRegistry, IDocumentWidget } from '@jupyterlab/docregistry';
|
|
5
|
+
import {
|
|
6
|
+
TableOfContents,
|
|
7
|
+
TableOfContentsModel,
|
|
8
|
+
TableOfContentsUtils
|
|
9
|
+
} from '@jupyterlab/toc';
|
|
10
|
+
import { Widget } from '@lumino/widgets';
|
|
11
|
+
import { FileEditor } from '../widget';
|
|
12
|
+
import { EditorTableOfContentsFactory, IEditorHeading } from './factory';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Table of content model for Markdown files.
|
|
16
|
+
*/
|
|
17
|
+
export class MarkdownTableOfContentsModel extends TableOfContentsModel<
|
|
18
|
+
IEditorHeading,
|
|
19
|
+
IDocumentWidget<FileEditor, DocumentRegistry.IModel>
|
|
20
|
+
> {
|
|
21
|
+
/**
|
|
22
|
+
* Type of document supported by the model.
|
|
23
|
+
*
|
|
24
|
+
* #### Notes
|
|
25
|
+
* A `data-document-type` attribute with this value will be set
|
|
26
|
+
* on the tree view `.jp-TableOfContents-content[data-document-type="..."]`
|
|
27
|
+
*/
|
|
28
|
+
get documentType(): string {
|
|
29
|
+
return 'markdown';
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Produce the headings for a document.
|
|
34
|
+
*
|
|
35
|
+
* @returns The list of new headings or `null` if nothing needs to be updated.
|
|
36
|
+
*/
|
|
37
|
+
protected getHeadings(): Promise<IEditorHeading[] | null> {
|
|
38
|
+
if (!this.isActive) {
|
|
39
|
+
return Promise.resolve(null);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const content = this.widget.content.model.sharedModel.getSource();
|
|
43
|
+
|
|
44
|
+
const headings = TableOfContentsUtils.filterHeadings(
|
|
45
|
+
TableOfContentsUtils.Markdown.getHeadings(content),
|
|
46
|
+
{
|
|
47
|
+
...this.configuration,
|
|
48
|
+
// Force removing numbering as they cannot be displayed
|
|
49
|
+
// in the document
|
|
50
|
+
numberHeaders: false
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
return Promise.resolve(headings);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Table of content model factory for Markdown files.
|
|
59
|
+
*/
|
|
60
|
+
export class MarkdownTableOfContentsFactory extends EditorTableOfContentsFactory {
|
|
61
|
+
/**
|
|
62
|
+
* Whether the factory can handle the widget or not.
|
|
63
|
+
*
|
|
64
|
+
* @param widget - widget
|
|
65
|
+
* @returns boolean indicating a ToC can be generated
|
|
66
|
+
*/
|
|
67
|
+
isApplicable(widget: Widget): boolean {
|
|
68
|
+
const isApplicable = super.isApplicable(widget);
|
|
69
|
+
|
|
70
|
+
if (isApplicable) {
|
|
71
|
+
let mime = (widget as any).content?.model?.mimeType;
|
|
72
|
+
return mime && TableOfContentsUtils.Markdown.isMarkdown(mime);
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Create a new table of contents model for the widget
|
|
79
|
+
*
|
|
80
|
+
* @param widget - widget
|
|
81
|
+
* @param configuration - Table of contents configuration
|
|
82
|
+
* @returns The table of contents model
|
|
83
|
+
*/
|
|
84
|
+
protected _createNew(
|
|
85
|
+
widget: IDocumentWidget<FileEditor, DocumentRegistry.IModel>,
|
|
86
|
+
configuration?: TableOfContents.IConfig
|
|
87
|
+
): MarkdownTableOfContentsModel {
|
|
88
|
+
return new MarkdownTableOfContentsModel(widget, configuration);
|
|
89
|
+
}
|
|
90
|
+
}
|