/
searchprovider.ts
146 lines (132 loc) · 3.45 KB
/
searchprovider.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
// Copyright (c) Jupyter Development Team.
// Distributed under the terms of the Modified BSD License.
import { MainAreaWidget } from '@jupyterlab/apputils';
import { CodeMirrorEditor, EditorSearchProvider } from '@jupyterlab/codemirror';
import { CodeEditor } from '@jupyterlab/codeeditor';
import {
IFilters,
IReplaceOptionsSupport,
ISearchProvider
} from '@jupyterlab/documentsearch';
import { ITranslator } from '@jupyterlab/translation';
import { Widget } from '@lumino/widgets';
import { FileEditor } from './widget';
import { ISharedText, SourceChange } from '@jupyter/ydoc';
/**
* Helper type
*/
export type FileEditorPanel = MainAreaWidget<FileEditor>;
/**
* File editor search provider
*/
export class FileEditorSearchProvider
extends EditorSearchProvider<CodeEditor.IModel>
implements ISearchProvider
{
/**
* Constructor
* @param widget File editor panel
*/
constructor(protected widget: FileEditorPanel) {
super();
}
get isReadOnly(): boolean {
return this.editor.getOption('readOnly') as boolean;
}
/**
* Support for options adjusting replacement behavior.
*/
get replaceOptionsSupport(): IReplaceOptionsSupport {
return {
preserveCase: true
};
}
/**
* Text editor
*/
get editor() {
return this.widget.content.editor as CodeMirrorEditor;
}
/**
* Editor content model
*/
get model(): CodeEditor.IModel {
return this.widget.content.model;
}
async startQuery(
query: RegExp,
filters: IFilters | undefined
): Promise<void> {
this._searchActive = true;
await super.startQuery(query, filters);
await this.highlightNext(true, {
from: 'selection-start',
scroll: false,
select: false
});
}
/**
* Stop the search and clean any UI elements.
*/
async endQuery(): Promise<void> {
this._searchActive = false;
await super.endQuery();
}
/**
* Callback on source change
*
* @param emitter Source of the change
* @param changes Source change
*/
protected async onSharedModelChanged(
emitter: ISharedText,
changes: SourceChange
): Promise<void> {
if (this._searchActive) {
return super.onSharedModelChanged(emitter, changes);
}
}
/**
* Instantiate a search provider for the widget.
*
* #### Notes
* The widget provided is always checked using `isApplicable` before calling
* this factory.
*
* @param widget The widget to search on
* @param translator [optional] The translator object
*
* @returns The search provider on the widget
*/
static createNew(
widget: FileEditorPanel,
translator?: ITranslator
): ISearchProvider {
return new FileEditorSearchProvider(widget);
}
/**
* Report whether or not this provider has the ability to search on the given object
*/
static isApplicable(domain: Widget): domain is FileEditorPanel {
return (
domain instanceof MainAreaWidget &&
domain.content instanceof FileEditor &&
domain.content.editor instanceof CodeMirrorEditor
);
}
/**
* Get an initial query value if applicable so that it can be entered
* into the search box as an initial query
*
* @returns Initial value used to populate the search box.
*/
getInitialQuery(): string {
const cm = this.editor as CodeMirrorEditor;
const selection = cm.state.sliceDoc(
cm.state.selection.main.from,
cm.state.selection.main.to
);
return selection;
}
private _searchActive = false;
}